import { ChatMessage, MessageBlock, FetchMessagesRequest, SendMessageRequest, ChatConfig, ApiResponse, RealApiMessage, ApiMessageContent, MessagesResponse, FunctionName, ProjectInit, ScriptSummary, CharacterGeneration, SketchGeneration, ShotSketchGeneration, ShotVideoGeneration } from "./types"; import { post } from "@/api/request"; // Mock 数据 const MOCK_MESSAGES: RealApiMessage[] = [ // 用户发送剧本 { id: 1, role: 'user', content: JSON.stringify([{ type: 'text', content: '我想拍一个关于一个小女孩和她的机器人朋友的故事,故事发生在未来世界。' }]), created_at: '2024-03-20T10:00:00Z', function_name: undefined, custom_data: undefined, status: 'success', intent_type: 'chat' }, // 项目初始化 { id: 2, role: 'system', content: '我会帮您创建一个温馨感人的科幻短片,讲述人工智能与人类情感的故事。', created_at: '2024-03-20T10:00:10Z', function_name: 'create_project', custom_data: { project_data: { script: '小女孩和机器人朋友的故事' } }, status: 'success', intent_type: 'procedure' }, // 剧本总结 { id: 3, role: 'system', content: '故事概要:在2045年的未来城市,10岁的小女孩艾米丽收到了一个特别的生日礼物——一个具有高度情感智能的机器人伙伴"小星"。随着时间推移,他们建立了深厚的友谊。当小星因能源耗尽即将永久关闭时,艾米丽想尽办法寻找解决方案,最终通过她的坚持和创意,成功为小星找到了新的能源,让这段跨越人机界限的友谊得以延续。', created_at: '2024-03-20T10:01:00Z', function_name: 'generate_script_summary', custom_data: { summary: '一个关于友谊和希望的温暖故事' }, status: 'success', intent_type: 'procedure' }, // 角色生成 - 艾米丽 { id: 4, role: 'system', content: '主角艾米丽的形象已生成', created_at: '2024-03-20T10:02:00Z', function_name: 'generate_character', custom_data: { character_name: '艾米丽', image_path: 'https://picsum.photos/seed/emily/300/400', completed_count: 1, total_count: 2 }, status: 'success', intent_type: 'procedure' }, // 角色生成 - 小星 { id: 5, role: 'system', content: '机器人小星的形象已生成', created_at: '2024-03-20T10:03:00Z', function_name: 'generate_character', custom_data: { character_name: '小星', image_path: 'https://picsum.photos/seed/robot/300/400', completed_count: 2, total_count: 2 }, status: 'success', intent_type: 'procedure' }, // 场景生成 - 未来城市 { id: 6, role: 'system', content: '未来城市场景设计完成', created_at: '2024-03-20T10:04:00Z', function_name: 'generate_sketch', custom_data: { sketch_name: '未来城市街景', image_path: 'https://picsum.photos/seed/city/600/400', completed_count: 1, total_count: 3 }, status: 'success', intent_type: 'procedure' }, // 场景生成 - 艾米丽的房间 { id: 7, role: 'system', content: '艾米丽的未来风格卧室设计完成', created_at: '2024-03-20T10:05:00Z', function_name: 'generate_sketch', custom_data: { sketch_name: '艾米丽的卧室', image_path: 'https://picsum.photos/seed/room/600/400', completed_count: 2, total_count: 3 }, status: 'success', intent_type: 'procedure' }, // 场景生成 - 实验室 { id: 8, role: 'system', content: '高科技实验室场景设计完成', created_at: '2024-03-20T10:06:00Z', function_name: 'generate_sketch', custom_data: { sketch_name: '未来实验室', image_path: 'https://picsum.photos/seed/lab/600/400', completed_count: 3, total_count: 3 }, status: 'success', intent_type: 'procedure' }, // 分镜生成 - 相遇 { id: 9, role: 'system', content: '第一个分镜:艾米丽收到礼物时的场景', created_at: '2024-03-20T10:07:00Z', function_name: 'generate_shot_sketch', custom_data: { shot_type: '中景', atmosphere: '温馨、期待', key_action: '艾米丽惊喜地打开礼物盒,小星缓缓启动', url: 'https://picsum.photos/seed/shot1/600/400', completed_count: 1, total_count: 3 }, status: 'success', intent_type: 'procedure' }, // 分镜生成 - 危机 { id: 10, role: 'system', content: '第二个分镜:小星能源耗尽的场景', created_at: '2024-03-20T10:08:00Z', function_name: 'generate_shot_sketch', custom_data: { shot_type: '特写', atmosphere: '紧张、担忧', key_action: '小星的能源指示灯闪烁微弱,艾米丽神情焦急', url: 'https://picsum.photos/seed/shot2/600/400', completed_count: 2, total_count: 3 }, status: 'success', intent_type: 'procedure' }, // 分镜生成 - 解决 { id: 11, role: 'system', content: '第三个分镜:找到新能源解决方案的场景', created_at: '2024-03-20T10:09:00Z', function_name: 'generate_shot_sketch', custom_data: { shot_type: '全景', atmosphere: '欢欣、胜利', key_action: '实验室中艾米丽成功激活新能源,小星重新焕发活力', url: 'https://picsum.photos/seed/shot3/600/400', completed_count: 3, total_count: 3 }, status: 'success', intent_type: 'procedure' }, // 分镜视频生成 { id: 11.1, role: 'system', content: '分镜视频生成完成', created_at: '2024-03-20T10:10:00Z', function_name: 'generate_video', custom_data: { prompt_json: { core_atmosphere: '欢欣、胜利', }, urls: ['https://cdn.qikongjian.com/faces/1755798635_facefusion_output_1755798635.mp4'], completed_count: 1, total_count: 1 }, status: 'success', intent_type: 'procedure' }, // 用户反馈 { id: 12, role: 'user', content: JSON.stringify([{ type: 'text', content: '这个故事设计太棒了!特别喜欢艾米丽和小星的互动场景。' }]), created_at: '2024-03-20T10:10:00Z', function_name: undefined, custom_data: undefined, status: 'success', intent_type: 'function_call' }, // 助手回复 { id: 13, role: 'assistant', content: JSON.stringify([{ type: 'text', content: '谢谢您的肯定!我们可以继续优化任何场景或角色设计,您觉得有什么地方需要调整吗?' }]), created_at: '2024-03-20T10:10:10Z', function_name: undefined, custom_data: undefined, status: 'success', intent_type: 'function_call' } ]; /** * 类型守卫函数 */ function isProjectInit(data: any): data is ProjectInit { return data && 'project_data' in data; } function isScriptSummary(data: any): data is ScriptSummary { return data && 'summary' in data; } function isCharacterGeneration(data: any): data is CharacterGeneration { return data && 'character_name' in data && 'image_path' in data && 'completed_count' in data && 'total_count' in data; } function isSketchGeneration(data: any): data is SketchGeneration { return data && 'sketch_name' in data && 'image_path' in data && 'completed_count' in data && 'total_count' in data; } function isShotSketchGeneration(data: any): data is ShotSketchGeneration { return data && 'shot_type' in data && 'atmosphere' in data && 'key_action' in data && 'url' in data && 'completed_count' in data && 'total_count' in data; } function isShotVideoGeneration(data: any): data is ShotVideoGeneration { return data && 'prompt_json' in data && 'urls' in data && 'completed_count' in data && 'total_count' in data; } /** * 系统消息转换为blocks数组 */ function transformSystemMessage( functionName: FunctionName, content: string, customData: ProjectInit | ScriptSummary | CharacterGeneration | SketchGeneration | ShotSketchGeneration | ShotVideoGeneration ): MessageBlock[] { let blocks: MessageBlock[] = []; switch (functionName) { case 'create_project': if (isProjectInit(customData)) { blocks = [{ type: 'text', text: `🎬 根据您输入的 "${customData.project_data.script}",我已完成项目的初始化。\n\n${content}` }]; } break; case 'generate_script_summary': if (isScriptSummary(customData)) { blocks = [ { type: 'text', text: `🎬 剧本摘要生成完成\n\n${customData.summary}\n\n${content}` } ]; } break; case 'generate_character': if (isCharacterGeneration(customData)) { blocks = [{ type: 'text', text: `🎭 演员 "${customData.character_name}" 已就位` }, { type: 'image', url: customData.image_path }, { type: 'text', text: '图片中演员形象仅供参考,后续可根据视频生成后进行调整。' }, { type: 'progress', value: customData.completed_count, total: customData.total_count, label: `已完成 ${customData.completed_count} 个演员,共有 ${customData.total_count} 个` }, { type: 'text', text: `\n\n${content}` }]; } break; case 'generate_sketch': if (isSketchGeneration(customData)) { blocks = [{ type: 'text', text: `🎨 场景 "${customData.sketch_name}" 参考图片已生成 \n` }, { type: 'image', url: customData.image_path }, { type: 'text', text: '图片中场景仅供参考,后续可根据视频生成后进行调整。' }, { type: 'progress', value: customData.completed_count, total: customData.total_count, label: `已完成 ${customData.completed_count} 个场景,共有 ${customData.total_count} 个` }, { type: 'text', text: `\n\n${content}` }]; } break; case 'generate_shot_sketch': if (isShotSketchGeneration(customData)) { blocks = [{ type: 'text', text: `🎬 故事板静帧生成 \n镜头类型:${customData.shot_type}\n氛围:${customData.atmosphere}\n关键动作:${customData.key_action}` }, { type: 'image', url: customData.url }, { type: 'text', text: '图片中故事板静帧仅供参考,后续可根据视频生成后进行调整。' }, { type: 'progress', value: customData.completed_count, total: customData.total_count, label: `已完成 ${customData.completed_count} 个故事板静帧,共有 ${customData.total_count} 个` }, { type: 'text', text: `\n\n${content}` }]; } break; case 'generate_video': if (isShotVideoGeneration(customData)) { blocks.push({ type: 'text', text: `🎬 该分镜下包含${customData.urls.length} 个视频。 \n核心氛围:${customData.prompt_json.core_atmosphere}` }); customData.urls.forEach((url: string) => { blocks.push({ type: 'video', url: url }); }); blocks.push({ type: 'text', text: '后续可在剪辑线上进行编辑。' }, { type: 'progress', value: customData.completed_count, total: customData.total_count, label: `已完成 ${customData.completed_count} 个分镜,共有 ${customData.total_count} 个分镜` }, { type: 'text', text: `\n\n${content}` }) } break; } return blocks; } /** * 将API响应转换为ChatMessage格式 */ function transformMessage(apiMessage: RealApiMessage): ChatMessage { try { const { id, role, content, created_at, function_name, custom_data, status, intent_type } = apiMessage; let message: ChatMessage = { id: id ? id.toString() : Date.now().toString(), role: role, createdAt: new Date(created_at).getTime(), blocks: [], chatType: intent_type, status: status || 'success', }; if (role === 'assistant' || role === 'user') { try { const contentObj = JSON.parse(content); const contentArray = Array.isArray(contentObj) ? contentObj : [contentObj]; contentArray.forEach((c: ApiMessageContent) => { if (c.type === "text") { message.blocks.push({ type: "text", text: c.content }); } else if (c.type === "image") { message.blocks.push({ type: "image", url: c.content }); } else if (c.type === "video") { message.blocks.push({ type: "video", url: c.content }); } else if (c.type === "audio") { message.blocks.push({ type: "audio", url: c.content }); } }); } catch (error) { // 如果 JSON 解析失败,将整个 content 作为文本内容 message.blocks.push({ type: "text", text: content }); } } else if (role === 'system' && function_name && custom_data) { // 处理系统消息 message.blocks = transformSystemMessage(function_name, content, custom_data); } else { message.blocks.push({ type: "text", text: content }); } // 如果没有有效的 blocks,至少添加一个文本块 if (message.blocks.length === 0) { message.blocks.push({ type: "text", text: "无内容" }); } return message; } catch (error) { console.error("转换消息格式失败:", error, apiMessage); // 返回一个带有错误信息的消息 return { id: new Date().getTime().toString(), role: apiMessage.role, createdAt: new Date(apiMessage.created_at).getTime(), blocks: [{ type: "text", text: "消息格式错误" }], chatType: 'chat', status: 'error', }; } } /** * 获取消息列表 */ export async function fetchMessages( config: ChatConfig, offset: number = 0, limit: number = 50 ): Promise<{ messages: ChatMessage[], hasMore: boolean, totalCount: number, }> { const request: FetchMessagesRequest = { session_id: `project_${config.projectId}_user_${config.userId}`, limit, offset, }; try { console.log('发送历史消息请求:', request); const response = await post>("/intelligent/history", request); console.log('收到历史消息响应:', response); // 确保 response.data 和 messages 存在 if (!response.data || !response.data.messages) { console.error('历史消息响应格式错误:', response); return { messages: [], hasMore: false, totalCount: 0 }; } // 转换消息并按时间排序 // if (response.data.messages.length === 0) { // return { // messages: MOCK_MESSAGES.map(transformMessage), // hasMore: false, // totalCount: 0 // }; // } return { messages: response.data.messages .map(transformMessage) .sort((a, b) => Number(a.id) - Number(b.id)), hasMore: response.data.has_more, totalCount: response.data.total_count }; } catch (error) { console.error("获取消息历史失败:", error); throw error; } } /** * 发送新消息 */ export async function sendMessage( blocks: MessageBlock[], config: ChatConfig, videoId?: string ): Promise { // 提取文本、图片和视频 const textBlocks = blocks.filter(b => b.type === "text"); const imageBlocks = blocks.filter(b => b.type === "image"); const videoBlocks = blocks.filter(b => b.type === "video"); const request: SendMessageRequest = { session_id: `project_${config.projectId}_user_${config.userId}`, user_input: textBlocks.map(b => (b as { text: string }).text).join("\n"), project_id: config.projectId, user_id: config.userId.toString(), }; // 如果有图片,添加第一张图片的URL if (imageBlocks.length > 0) { request.image_url = (imageBlocks[0] as { url: string }).url; } // 如果有视频,添加视频URL if (videoBlocks.length > 0) { request.video_url = (videoBlocks[0] as { url: string }).url; } // 如果有视频ID,添加到请求中 if (videoId) { request.video_id = videoId; } try { console.log('发送消息请求:', request); await post>("/intelligent/chat", request); } catch (error) { console.error("发送消息失败:", error); throw error; } } /** * 重试发送消息 */ export async function retryMessage( messageId: string, config: ChatConfig ): Promise { // TODO: 实现实际的重试逻辑,可能需要保存原始消息内容 // 这里简单重用发送消息的接口 return sendMessage([{ type: "text", text: "重试消息" }], config); }