/** * useConversation — Active conversation management * * The core business logic composable: * - Manages the currently active conversation * - Handles sending messages and receiving mock AI responses * - Auto-generates workspace items for substantial responses * - Coordinates between useConversations, useWorkflow, and useWorkspace */ import type { Message, WorkflowId, Conversation, WorkspaceItem } from '@/types/index' import { STORAGE_KEYS, generateId, MOCK_AI_DELAY_MS } from '@/constants/index' import { useStorage } from './useStorage' // Mock AI response templates per workflow const AI_RESPONSES: Record = { general: [ '这是一个很好的问题!让我来为你详细解答。\n\n首先,我们需要理解核心概念。人工智能正在快速发展,已经渗透到我们生活的方方面面。\n\n**关键要点:**\n- 保持好奇心,不断学习新知识\n- 实践是检验真理的唯一标准\n- 多角度思考问题能带来更好的解决方案\n\n希望这些对你有所帮助!还有其他问题吗?', '感谢你的提问!关于这个问题,我有以下几点建议:\n\n1. **明确目标**:先确定你想要达到的具体结果\n2. **制定计划**:把大目标拆分成小步骤\n3. **持续迭代**:在执行过程中不断优化调整\n\n如果你能提供更多具体信息,我可以给出更具针对性的建议。', '让我从几个角度来分析这个问题:\n\n从技术层面看,这涉及到系统的整体架构设计。从用户体验角度来说,简洁直观的交互至关重要。\n\n综合考虑,我建议采用渐进式的方法,先验证核心功能,再逐步扩展。你觉得这个思路如何?', ], code: [ '好的,让我来帮你解决这个编程问题。\n\n```\nfunction solve(arr: number[]): number {\n const n = arr.length\n let result = 0\n for (let i = 0; i < n; i++) {\n result += arr[i]\n }\n return result\n}\n```\n\n这段代码的时间复杂度是 O(n),空间复杂度是 O(1)。\n\n**注意事项:**\n- 记得处理边界情况(空数组)\n- 可以考虑使用 reduce 方法简化代码\n- 如果需要处理大数,注意整数溢出问题\n\n还有什么需要我帮忙的吗?', '这是一个经典的设计模式问题。我推荐使用以下架构:\n\n```\nclass EventBus {\n private handlers: Map\n \n on(event: string, handler: Function) {\n // 注册事件处理器\n }\n \n emit(event: string, data: any) {\n // 触发事件\n }\n}\n```\n\n这种发布-订阅模式可以有效解耦模块间的依赖,让代码更具可维护性。', '让我看看你的代码逻辑...\n\n问题在于异步操作的处理。你需要使用 `await` 来等待 Promise 完成:\n\n```\nasync function fetchData(url: string) {\n try {\n const response = await fetch(url)\n const data = await response.json()\n return data\n } catch (error) {\n console.error(\'Failed to fetch:\', error)\n throw error\n }\n}\n```\n\n另外建议添加错误处理和重试机制,提高代码的健壮性。', ], document: [ '好的,我来帮你撰写这篇文档。\n\n---\n\n**引言**\n\n在当今快速发展的数字化时代,企业面临着前所未有的机遇与挑战。如何有效利用技术手段提升业务效率,已成为每个管理者必须思考的问题。\n\n**核心观点**\n\n- 数字化转型不仅仅是技术升级,更是思维方式的转变\n- 数据驱动决策能够显著提升业务精准度\n- 用户体验应始终放在产品设计的首位\n\n**总结**\n\n通过系统化的方法推进数字化转型,企业能够在竞争激烈的市场中保持领先地位。\n\n---\n\n以上是初稿,你觉得需要调整什么方向吗?', '帮你润色了一下这段文字:\n\n**原文**:这个产品很好用,功能很多,界面也好看。\n\n**润色后**:这款产品表现优秀,不仅功能丰富全面,界面设计也简洁美观,为用户带来了出色的使用体验。\n\n**修改要点:**\n- 用词更加正式专业\n- 句式更加丰富多变\n- 增强了文字的感染力\n\n需要继续优化其他内容吗?', ], data: [ '根据你提供的数据,我进行了详细分析:\n\n📊 **数据概览**\n\n| 指标 | 当前值 | 环比变化 |\n|------|--------|----------|\n| 用户数 | 12,580 | +8.3% |\n| 活跃率 | 67.2% | +2.1% |\n| 转化率 | 3.8% | -0.5% |\n\n🔍 **关键洞察**\n\n1. 用户增长保持良好势头,增长率超过行业平均水平\n2. 活跃率稳步提升,说明产品粘性增强\n3. 转化率略有下降,需要关注转化漏斗的优化\n\n💡 **建议措施**\n\n- 针对转化率问题,优化注册流程\n- 分析高活跃用户的行为特征并复制推广\n- 关注用户留存曲线的变化趋势\n\n需要我深入分析某个具体指标吗?', '让我从数据角度来分析这个趋势:\n\n**相关性分析结果:**\n- 变量A与变量B的相关系数:0.87(强正相关)\n- 变量C与结果的相关系数:-0.42(中等负相关)\n\n这意味着当A增加时,B也倾向于增加。而C的增加反而会抑制结果的提升。\n\n**统计显著性**:p值 < 0.01,结果具有统计意义。\n\n建议重点关注A和B的关系,这可能是一个关键的驱动因素。', ], creative: [ '好的,让我来一场头脑风暴!🎯\n\n**方案一:社交裂变**\n利用用户的社交网络,通过分享激励机制实现病毒式传播。核心是设计一个让用户"忍不住想分享"的体验。\n\n**方案二:内容IP化**\n打造独特的品牌IP形象,通过故事化内容与用户建立情感连接。IP可以出现在产品各个触点。\n\n**方案三:场景化营销**\n深入用户真实使用场景,在不同场景下提供定制化的解决方案。让产品"恰好"出现在用户需要的时候。\n\n💡 我个人最看好方案二的长期价值,但方案一能在短期内带来快速增长。你觉得哪个方向更符合你的目标?', '让我们跳出常规思维,重新构想这个问题的解决方案!\n\n想象一下,如果完全没有技术和资源的限制,理想的解决方案会是什么样?\n\n从这个"理想态"往回推导:\n1. 理想体验的核心要素是什么?\n2. 哪些要素可以通过现有技术实现?\n3. 哪些需要创新的替代方案?\n\n这种"反向思维"往往能激发出意想不到的创意。比如Airbnb就是通过"让陌生人住进家里"这个看似疯狂的创意颠覆了酒店行业。\n\n你有什么初步的想法吗?我们一起碰撞出更多灵感!', ], } /** * @param conversationsApi — functions from useConversations(): getConversation, saveConversation, createConversation * @param getActiveWorkflowId — getter function that returns the current active workflow ID from useWorkflow() * @param addWorkspaceItemFn — function from useWorkspace() to add workspace items. * All are passed in as dependencies so the same state is shared with the page. */ export function useConversation( conversationsApi: { getConversation: (id: string) => Conversation | undefined saveConversation: (conv: Conversation) => void createConversation: (workflowId: WorkflowId, title?: string) => Conversation }, getActiveWorkflowId: () => WorkflowId, addWorkspaceItemFn?: (item: WorkspaceItem) => void ) { const { loadValue, saveValue } = useStorage() const { getConversation, saveConversation, createConversation } = conversationsApi // Fallback no-op if not provided const addWorkspaceItem = addWorkspaceItemFn ?? (() => {}) // Active conversation ID const activeConversationId = ref( loadValue(STORAGE_KEYS.ACTIVE_CONVERSATION_ID, null) ) // Whether the AI is currently "typing" const isLoading = ref(false) /** * Messages of the currently active conversation (computed). */ const messages = computed(() => { if (!activeConversationId.value) return [] const conv = getConversation(activeConversationId.value) return conv ? conv.messages : [] }) /** * Set the active conversation ID and persist. */ function setActiveConversationId(id: string | null): void { activeConversationId.value = id if (id) { saveValue(STORAGE_KEYS.ACTIVE_CONVERSATION_ID, id) } else { saveValue(STORAGE_KEYS.ACTIVE_CONVERSATION_ID, null) } } /** * Start a new conversation with the given workflow. */ function startNewConversation(workflowId: WorkflowId): void { const conv = createConversation(workflowId) setActiveConversationId(conv.id) isLoading.value = false } /** * Load an existing conversation by ID. */ function loadConversation(id: string): void { const conv = getConversation(id) if (conv) { setActiveConversationId(id) isLoading.value = false } } /** * Send a message from the user. * Adds the user message, then triggers a mock AI response after a delay. */ function sendMessage(content: string): void { if (!content.trim() || isLoading.value) return // Ensure we have an active conversation if (!activeConversationId.value) { startNewConversation(getActiveWorkflowId()) } const convId: string = activeConversationId.value ?? '' if (!convId) return const conv = getConversation(convId) if (!conv) { startNewConversation(getActiveWorkflowId()) // Retry after creating sendMessage(content) return } // Add user message const userMessage: Message = { id: generateId(), role: 'user', content: content.trim(), timestamp: Date.now(), status: 'sent', } conv.messages.push(userMessage) conv.updatedAt = Date.now() // Auto-title: use first user message as conversation title if (conv.messages.filter((m) => m.role === 'user').length === 1) { let title = content.trim() if (title.length > 20) { title = title.substring(0, 20) + '...' } conv.title = title } saveConversation(conv) // Trigger mock AI response isLoading.value = true setTimeout(() => { simulateAiResponse(conv) }, MOCK_AI_DELAY_MS) } /** * Retry sending a message (for error recovery). */ function retryMessage(messageId: string): void { const conv = getConversation(activeConversationId.value ?? '') if (!conv) return const msg = conv.messages.find((m) => m.id === messageId) if (msg) { msg.status = 'sent' saveConversation(conv) } } /** * Simulate an AI response. * In production, replace this with a real API call. */ function simulateAiResponse(conv: Conversation): void { const responses = AI_RESPONSES[conv.workflowId] ?? AI_RESPONSES['general'] const responseContent = responses[Math.floor(Math.random() * responses.length)] const aiMessage: Message = { id: generateId(), role: 'assistant', content: responseContent, timestamp: Date.now(), status: 'sent', } // Get fresh reference in case conversations array changed const freshConv = getConversation(conv.id) if (!freshConv) { isLoading.value = false return } freshConv.messages.push(aiMessage) freshConv.updatedAt = Date.now() saveConversation(freshConv) // Auto-generate workspace item for substantial responses if (conv.workflowId !== 'general') { generateWorkspaceItem(freshConv, aiMessage) } isLoading.value = false } /** * Generate a workspace item from an AI response. */ function generateWorkspaceItem(conv: Conversation, message: Message): void { const typeMap: Record = { code: 'code_snippet', document: 'document', data: 'analysis_report', creative: 'creative_output', } const titles: Record = { code: '代码片段', document: '文档草稿', data: '分析报告', creative: '创意方案', } const itemType = (typeMap[conv.workflowId] ?? 'document') as import('@/types/index').WorkspaceItemType const autoTitle = titles[conv.workflowId] ?? '工作结果' // Only create if response is long enough to be "substantial" if (message.content.length < 50) return addWorkspaceItem({ id: generateId(), conversationId: conv.id, workflowId: conv.workflowId, type: itemType, title: `${autoTitle} - ${conv.title}`, content: message.content, summary: message.content.substring(0, 120) + '...', createdAt: Date.now(), }) } return { activeConversationId, messages, isLoading, sendMessage, retryMessage, loadConversation, startNewConversation, setActiveConversationId, } }