// @ts-nocheck import { debugLog } from './common'; /** * 任务轮询事件处理器 * 将从 REST API 轮询获取的事件转换为前端状态更新 */ export const taskPollingMethods = { /** * 处理任务事件(从轮询获取) */ handleTaskEvent(event: any) { if (!event || !event.type) { return; } const eventType = event.type; const eventData = event.data || {}; const eventIdx = event.idx; // 事件去重检查 if (typeof eventIdx === 'number') { if (!this._processedEventIndices) { this._processedEventIndices = new Set(); } if (this._processedEventIndices.has(eventIdx)) { debugLog(`[TaskPolling] 跳过重复事件 #${eventIdx}`); return; } this._processedEventIndices.add(eventIdx); // 限制 Set 大小(保留最近 1000 个) if (this._processedEventIndices.size > 1000) { const firstIdx = Math.min(...this._processedEventIndices); this._processedEventIndices.delete(firstIdx); } } // 检查事件的 conversation_id 是否匹配当前对话 // 如果不匹配,忽略该事件(避免切换对话后旧任务的事件显示到新对话中) if (eventData.conversation_id && this.currentConversationId) { if (eventData.conversation_id !== this.currentConversationId) { console.log(`[DEBUG_AWAITING] 忽略不匹配的事件`, { eventType, eventIdx, eventConversationId: eventData.conversation_id, currentConversationId: this.currentConversationId }); debugLog(`[TaskPolling] 忽略不匹配的事件 #${eventIdx}: ${eventType}, 事件对话=${eventData.conversation_id}, 当前对话=${this.currentConversationId}`); return; } } debugLog(`[TaskPolling] 处理事件 #${eventIdx}: ${eventType}`, eventData); console.log(`[DEBUG_AWAITING] 处理事件`, { eventType, eventIdx, eventConversationId: eventData.conversation_id, currentConversationId: this.currentConversationId }); // 根据事件类型调用对应的处理方法 switch (eventType) { case 'ai_message_start': this.handleAiMessageStart(eventData, eventIdx); break; case 'thinking_start': this.handleThinkingStart(eventData, eventIdx); break; case 'thinking_chunk': this.handleThinkingChunk(eventData, eventIdx); break; case 'thinking_end': this.handleThinkingEnd(eventData, eventIdx); break; case 'text_start': this.handleTextStart(eventData, eventIdx); break; case 'text_chunk': this.handleTextChunk(eventData, eventIdx); break; case 'text_end': this.handleTextEnd(eventData, eventIdx); break; case 'tool_preparing': this.handleToolPreparing(eventData, eventIdx); break; case 'tool_start': this.handleToolStart(eventData, eventIdx); break; case 'tool_intent': this.handleToolIntent(eventData, eventIdx); break; case 'tool_update_action': case 'update_action': this.handleToolUpdateAction(eventData, eventIdx); break; case 'append_payload': this.handleAppendPayload(eventData, eventIdx); break; case 'modify_payload': this.handleModifyPayload(eventData, eventIdx); break; case 'task_complete': this.handleTaskComplete(eventData, eventIdx); break; case 'task_stopped': this.handleTaskStopped(eventData, eventIdx); break; case 'error': this.handleTaskError(eventData, eventIdx); break; case 'token_update': this.handleTokenUpdate(eventData, eventIdx); break; case 'conversation_changed': this.handleConversationChanged(eventData, eventIdx); break; case 'conversation_resolved': this.handleConversationResolved(eventData, eventIdx); break; case 'user_message': this.handleUserMessage(eventData, eventIdx); break; default: debugLog(`[TaskPolling] 未知事件类型: ${eventType}`); } // 注意:不要在这里清除 _rebuildingFromScratch 标记 // 该标记应该在恢复完所有历史事件后才清除 }, handleAiMessageStart(data: any, eventIdx: number) { const lastMessage = this.messages[this.messages.length - 1]; console.log('[DEBUG_AWAITING] ===== handleAiMessageStart =====', { eventIdx, lastMessageRole: lastMessage?.role, lastMessageAwaitingFirstContent: lastMessage?.awaitingFirstContent, lastMessageGeneratingLabel: lastMessage?.generatingLabel }); debugLog('[TaskPolling] AI消息开始, idx:', eventIdx); console.log('[AiMessageStart] 开始处理', { eventIdx, messagesLength: this.messages.length, lastMessageRole: this.messages[this.messages.length - 1]?.role, streamingMessage: this.streamingMessage, taskInProgress: this.taskInProgress, currentConversationId: this.currentConversationId }); if (this.waitingForSubAgent) { this.waitingForSubAgent = false; } // 检查是否已经有 assistant 消息 const hasAssistantMessage = lastMessage && lastMessage.role === 'assistant'; console.log('[AiMessageStart] 最后一条消息状态', { hasAssistantMessage, lastMessageActionsLength: lastMessage?.actions?.length || 0, lastMessageAwaitingFirstContent: lastMessage?.awaitingFirstContent, lastMessageGeneratingLabel: lastMessage?.generatingLabel }); // 判断是否是刷新恢复的情况: // 1. 有assistant消息 // 2. 且该消息正在streaming或者没有任何内容(说明是刚创建的) // 3. 且不是历史加载完成的消息(历史消息的awaitingFirstContent应该是false) const isRefreshRestore = hasAssistantMessage && (this.streamingMessage || !lastMessage.actions || lastMessage.actions.length === 0) && (lastMessage.awaitingFirstContent === true || this.taskInProgress); console.log('[AiMessageStart] 场景判断', { isRefreshRestore, streamingMessage: this.streamingMessage, hasActions: !!(lastMessage?.actions && lastMessage.actions.length > 0), awaitingFirstContent: lastMessage?.awaitingFirstContent, taskInProgress: this.taskInProgress }); if (isRefreshRestore) { console.log('[AiMessageStart] 场景=刷新恢复,复用现有assistant消息'); debugLog('[TaskPolling] 刷新恢复场景,复用现有 assistant 消息'); // 只更新状态,不创建新消息 this.taskInProgress = true; this.stopRequested = false; this.streamingMessage = true; // 确保 awaitingFirstContent 被正确设置 const hasContent = lastMessage.actions && lastMessage.actions.length > 0; if (!hasContent) { lastMessage.awaitingFirstContent = true; lastMessage.generatingLabel = lastMessage.generatingLabel || '思考中...'; console.log('[AiMessageStart] 设置等待动画', { awaitingFirstContent: true, generatingLabel: lastMessage.generatingLabel }); } else { // 如果已有内容,确保等待动画不显示 lastMessage.awaitingFirstContent = false; lastMessage.generatingLabel = ''; console.log('[AiMessageStart] 关闭等待动画(已有内容)'); } return; } // 其他情况:创建新的 assistant 消息 console.log('[AiMessageStart] 场景=创建新消息'); debugLog('[TaskPolling] 创建新的 assistant 消息'); this.monitorResetSpeech(); this.cleanupStaleToolActions(); this.taskInProgress = true; this.chatStartAssistantMessage(); this.stopRequested = false; this.streamingMessage = true; const newMessage = this.messages[this.messages.length - 1]; console.log('[AiMessageStart] 新消息创建完成', { role: newMessage?.role, awaitingFirstContent: newMessage?.awaitingFirstContent, generatingLabel: newMessage?.generatingLabel, actionsLength: newMessage?.actions?.length || 0, messagesLength: this.messages.length }); // 如果是从头重建,标记消息为静默恢复 if (this._rebuildingFromScratch) { if (newMessage && newMessage.role === 'assistant') { debugLog('[TaskPolling] 标记消息为静默恢复(从头重建)'); newMessage.awaitingFirstContent = false; newMessage.generatingLabel = ''; console.log('[AiMessageStart] 从头重建,关闭等待动画'); } } // 强制触发Vue响应式更新 this.$forceUpdate(); console.log('[AiMessageStart] 已调用$forceUpdate'); this.$nextTick(() => { console.log('[AiMessageStart] nextTick回调执行'); this.scrollToBottom(); }); }, handleThinkingStart(data: any, eventIdx: number) { console.log('[ThinkingStart] 开始处理', { eventIdx }); debugLog('[TaskPolling] 思考开始, idx:', eventIdx); const ignoreThinking = this.runMode === 'fast' || this.thinkingMode === false; if (ignoreThinking) { this.monitorEndModelOutput(); console.log('[ThinkingStart] 忽略思考(fast模式或关闭思考)'); return; } // 防御性检查:如果没有assistant消息,先创建一个 const lastMessage = this.messages[this.messages.length - 1]; if (!lastMessage || lastMessage.role !== 'assistant') { console.log('[DEBUG_AWAITING] ThinkingStart 检测到缺少assistant消息,自动创建'); this.chatStartAssistantMessage(); } this.monitorShowThinking(); const result = this.chatStartThinkingAction(); if (result && result.blockId) { const blockId = result.blockId; const lastMessage = this.messages[this.messages.length - 1]; // 有内容了,关闭等待动画 if (lastMessage && lastMessage.role === 'assistant') { console.log('[ThinkingStart] 关闭等待动画', { beforeAwaitingFirstContent: lastMessage.awaitingFirstContent, beforeGeneratingLabel: lastMessage.generatingLabel }); lastMessage.awaitingFirstContent = false; lastMessage.generatingLabel = ''; } // 只在非历史恢复时展开思考块 if (!this._rebuildingFromScratch) { this.chatExpandBlock(blockId); } this.scrollToBottom(); this.chatSetThinkingLock(blockId, true); // 强制触发 actions 数组的响应式更新 if (lastMessage && lastMessage.actions) { lastMessage.actions = [...lastMessage.actions]; } this.$forceUpdate(); } }, handleThinkingChunk(data: any, eventIdx: number) { if (this.runMode === 'fast' || this.thinkingMode === false) { return; } const thinkingAction = this.chatAppendThinkingChunk(data.content); if (thinkingAction) { this.$forceUpdate(); this.$nextTick(() => { if (thinkingAction && thinkingAction.blockId) { this.scrollThinkingToBottom(thinkingAction.blockId); } this.conditionalScrollToBottom(); }); } this.monitorShowThinking(); }, handleThinkingEnd(data: any) { debugLog('[TaskPolling] 思考结束'); if (this.runMode === 'fast' || this.thinkingMode === false) { return; } const blockId = this.chatCompleteThinkingAction(data.full_content); if (blockId) { // 解锁思考块 this.chatSetThinkingLock(blockId, false); // 只在非历史恢复时延迟折叠思考块 if (!this._rebuildingFromScratch) { // 延迟折叠思考块(给用户一点时间看到思考完成) setTimeout(() => { this.chatCollapseBlock(blockId); this.$forceUpdate(); }, 1000); } else { // 历史恢复时立即折叠,不需要动画 this.chatCollapseBlock(blockId); } this.$nextTick(() => this.scrollThinkingToBottom(blockId)); } this.$forceUpdate(); this.monitorEndModelOutput(); }, handleTextStart(data: any) { console.log('[TextStart] 开始处理'); debugLog('[TaskPolling] 文本开始'); // 防御性检查:如果没有assistant消息,先创建一个 const lastMessage = this.messages[this.messages.length - 1]; if (!lastMessage || lastMessage.role !== 'assistant') { console.log('[DEBUG_AWAITING] TextStart 检测到缺少assistant消息,自动创建'); this.chatStartAssistantMessage(); } this.chatStartTextAction(); // 有内容了,关闭等待动画 const currentMessage = this.messages[this.messages.length - 1]; if (currentMessage && currentMessage.role === 'assistant') { console.log('[TextStart] 关闭等待动画', { beforeAwaitingFirstContent: currentMessage.awaitingFirstContent, beforeGeneratingLabel: currentMessage.generatingLabel }); currentMessage.awaitingFirstContent = false; currentMessage.generatingLabel = ''; } this.$forceUpdate(); }, handleTextChunk(data: any) { if (data && typeof data.content === 'string' && data.content.length) { this.chatAppendTextChunk(data.content); this.$forceUpdate(); this.conditionalScrollToBottom(); const speech = data.content.replace(/\r/g, ''); if (speech) { this.monitorShowSpeech(speech); } } }, handleTextEnd(data: any) { debugLog('[TaskPolling] 文本结束'); const full = data?.full_content || ''; this.chatCompleteTextAction(full); this.$forceUpdate(); this.monitorEndModelOutput(); }, handleToolPreparing(data: any) { console.log('[ToolPreparing] 开始处理', { name: data.name, id: data.id }); debugLog('[TaskPolling] 工具准备中:', data.name); if (this.dropToolEvents) { console.log('[ToolPreparing] dropToolEvents=true,跳过'); return; } const msg = this.chatEnsureAssistantMessage(); if (!msg) { console.log('[ToolPreparing] 无法获取assistant消息'); return; } if (msg.awaitingFirstContent) { console.log('[ToolPreparing] 关闭等待动画', { beforeAwaitingFirstContent: msg.awaitingFirstContent, beforeGeneratingLabel: msg.generatingLabel }); msg.awaitingFirstContent = false; msg.generatingLabel = ''; } const action = { id: data.id, type: 'tool', tool: { id: data.id, name: data.name, arguments: {}, argumentSnapshot: null, argumentLabel: '', status: 'preparing', result: null, message: data.message || `准备调用 ${data.name}...`, intent_full: data.intent || '', intent_rendered: data.intent || '' }, timestamp: Date.now() }; msg.actions.push(action); this.preparingTools.set(data.id, action); this.toolRegisterAction(action, data.id); this.toolTrackAction(data.name, action); this.$forceUpdate(); this.conditionalScrollToBottom(); if (this.monitorPreviewTool) { this.monitorPreviewTool(data); } }, handleToolStart(data: any) { debugLog('[TaskPolling] 工具开始:', data.name); if (this.dropToolEvents) { return; } let action = null; if (data.preparing_id && this.preparingTools.has(data.preparing_id)) { action = this.preparingTools.get(data.preparing_id); this.preparingTools.delete(data.preparing_id); } else { action = this.toolFindAction(data.id, data.preparing_id, data.execution_id); } if (!action) { const msg = this.chatEnsureAssistantMessage(); if (!msg) { return; } action = { id: data.id, type: 'tool', tool: { id: data.id, name: data.name, arguments: {}, argumentSnapshot: null, argumentLabel: '', status: 'running', result: null }, timestamp: Date.now() }; msg.actions.push(action); } action.tool.status = 'running'; action.tool.arguments = data.arguments; action.tool.argumentSnapshot = this.cloneToolArguments(data.arguments); action.tool.argumentLabel = this.buildToolLabel(action.tool.argumentSnapshot); action.tool.message = null; action.tool.id = data.id; action.tool.executionId = data.id; this.toolRegisterAction(action, data.id); this.toolTrackAction(data.name, action); this.$forceUpdate(); this.conditionalScrollToBottom(); if (this.monitorQueueTool) { this.monitorQueueTool(data); } }, handleToolIntent(data: any) { debugLog('[TaskPolling] 工具意图:', data.name, data.intent); if (this.dropToolEvents) { return; } // 查找对应的工具 action const action = this.toolFindAction(data.id, data.preparing_id, data.execution_id); if (action && action.tool) { const newIntent = data.intent || ''; // 如果 intent 没有变化,跳过 if (action.tool.intent_full === newIntent) { return; } // 停止之前的打字机效果 if (action.tool._intentTyping) { action.tool._intentTyping = false; if (action.tool._intentTimer) { clearTimeout(action.tool._intentTimer); action.tool._intentTimer = null; } } // 更新完整 intent action.tool.intent_full = newIntent; // 判断是否是历史恢复:只有从头重建时才直接显示 const isHistoryRestore = this._rebuildingFromScratch; if (isHistoryRestore) { // 历史恢复,直接显示完整 intent action.tool.intent_rendered = newIntent; debugLog('[TaskPolling] 历史恢复,直接显示 intent'); } else { // 新工具块,逐字符显示 debugLog('[TaskPolling] 新工具块,开始打字机效果'); action.tool.intent_rendered = ''; action.tool._intentTyping = true; // 计算每个字符的间隔时间 // 总时长1秒,但最少0.5秒 const totalDuration = Math.max(500, Math.min(1000, newIntent.length * 50)); const charInterval = totalDuration / newIntent.length; let charIndex = 0; const typeNextChar = () => { if (charIndex < newIntent.length && action.tool._intentTyping) { action.tool.intent_rendered += newIntent[charIndex]; charIndex++; this.$forceUpdate(); action.tool._intentTimer = setTimeout(typeNextChar, charInterval); } else { action.tool._intentTyping = false; action.tool.intent_rendered = newIntent; // 确保完整 action.tool._intentTimer = null; this.$forceUpdate(); } }; action.tool._intentTimer = setTimeout(typeNextChar, 50); // 延迟50ms开始 } // 更新 arguments 和 label if (action.tool.arguments) { action.tool.arguments.intent = newIntent; action.tool.argumentSnapshot = this.cloneToolArguments(action.tool.arguments); action.tool.argumentLabel = this.buildToolLabel(action.tool.argumentSnapshot); } this.$forceUpdate(); debugLog('[TaskPolling] 已更新工具意图:', data.name); } else { debugLog('[TaskPolling] 未找到对应的工具 action:', data.id); } }, handleToolUpdateAction(data: any) { if (this.dropToolEvents) { return; } debugLog('[TaskPolling] 更新action:', data.id, 'status:', data.status); let targetAction = this.toolFindAction(data.id, data.preparing_id, data.execution_id); if (!targetAction && data.preparing_id && this.preparingTools.has(data.preparing_id)) { targetAction = this.preparingTools.get(data.preparing_id); } if (!targetAction) { return; } if (data.status) { targetAction.tool.status = data.status; } if (data.result !== undefined) { targetAction.tool.result = data.result; } if (data.message !== undefined) { targetAction.tool.message = data.message; } if (data.content !== undefined) { targetAction.tool.content = data.content; } this.$forceUpdate(); this.conditionalScrollToBottom(); if (this.monitorResolveTool && data.status === 'completed') { this.monitorResolveTool(data); } }, handleAppendPayload(data: any) { debugLog('[TaskPolling] 文件追加:', data.path); this.chatAddAppendPayloadAction(data); this.$forceUpdate(); this.conditionalScrollToBottom(); }, handleModifyPayload(data: any) { debugLog('[TaskPolling] 文件修改:', data.path); this.chatAddModifyPayloadAction(data); this.$forceUpdate(); this.conditionalScrollToBottom(); }, markLatestUserWorkCompleted() { if (!Array.isArray(this.messages) || this.messages.length === 0) { return; } for (let i = this.messages.length - 1; i >= 0; i -= 1) { const msg = this.messages[i]; if (!msg || msg.role !== 'user') { continue; } msg.metadata = msg.metadata || {}; msg.metadata.work_timer = msg.metadata.work_timer || {}; const timer = msg.metadata.work_timer; if (timer.status === 'completed' && typeof timer.duration_ms === 'number') { return; } const nowIso = new Date().toISOString(); const startedAt = timer.started_at || msg.timestamp || nowIso; const startMs = Date.parse(startedAt); const endMs = Date.now(); const durationMs = Number.isFinite(startMs) ? Math.max(0, endMs - startMs) : 0; timer.status = 'completed'; timer.started_at = startedAt; timer.finished_at = nowIso; timer.duration_ms = durationMs; return; } }, handleTaskComplete(data: any) { const hasRunningSubAgents = !!data?.has_running_sub_agents; if (hasRunningSubAgents) { debugLog('[TaskPolling] 任务完成,但仍有后台子智能体运行'); } else { debugLog('[TaskPolling] 任务完成'); } // 同步处理状态更新 this.streamingMessage = false; this.stopRequested = false; if (!hasRunningSubAgents) { this.markLatestUserWorkCompleted(); } if (hasRunningSubAgents) { this.taskInProgress = true; this.waitingForSubAgent = true; } else { this.taskInProgress = false; this.waitingForSubAgent = false; this.clearTaskState(); // 清理任务状态 } this.$forceUpdate(); // 只更新统计,不重新加载历史 if (this.currentConversationId) { setTimeout(() => { this.fetchConversationTokenStatistics(); this.updateCurrentContextTokens(); }, 500); } }, handleTaskStopped(data: any, eventIdx: number) { debugLog('[TaskPolling] 任务已停止, idx:', eventIdx, data); this.markLatestUserWorkCompleted(); this.streamingMessage = false; this.taskInProgress = false; this.stopRequested = false; this.waitingForSubAgent = false; if (typeof this.clearPendingTools === 'function') { this.clearPendingTools('task_stopped'); } this.$forceUpdate(); this.clearTaskState(); }, // 新增统一清理方法 clearTaskState() { (async () => { const { useTaskStore } = await import('../../stores/task'); const taskStore = useTaskStore(); taskStore.clearTask(); // 使用 clearTask 而不是 stopPolling })(); if (typeof this.clearProcessedEvents === 'function') { this.clearProcessedEvents(); } }, handleUserMessage(data: any) { const message = (data?.message || data?.content || '').trim(); if (!message) { return; } debugLog('[TaskPolling] 收到用户消息事件'); this.chatAddUserMessage(message, data?.images || [], data?.videos || []); this.taskInProgress = true; this.streamingMessage = false; this.stopRequested = false; if (data?.sub_agent_notice) { if (typeof data?.has_running_sub_agents === 'boolean') { this.waitingForSubAgent = data.has_running_sub_agents; } else if (typeof data?.remaining_count === 'number') { this.waitingForSubAgent = data.remaining_count > 0; } } this.$forceUpdate(); this.conditionalScrollToBottom(); }, handleTaskError(data: any) { const shouldRetry = Boolean(data?.retry); if (shouldRetry) { const retryIn = Number(data?.retry_in) || 5; const attempt = Number(data?.attempt) || 1; const maxAttempts = Number(data?.max_attempts) || attempt; debugLog('[TaskPolling] API错误,等待自动重试', { retryIn, attempt, maxAttempts, message: data?.message }); this.stopRequested = false; this.taskInProgress = true; this.streamingMessage = true; this.$forceUpdate(); this.uiPushToast({ title: '即将重试', message: `将在 ${retryIn} 秒后重试(第 ${attempt}/${maxAttempts} 次)`, type: 'info', duration: Math.max(retryIn, 1) * 1000 }); return; } const errorMessage = data.message || '未知错误'; const errorType = data.error_type || 'unknown'; let title = '任务执行失败'; let message = errorMessage; // 根据错误类型提供友好提示 if (errorType === 'api_error') { title = 'API 调用失败'; message = `模型服务异常:${errorMessage}`; } else if (errorType === 'timeout') { title = '任务超时'; message = '任务执行时间过长,已自动停止'; } else if (errorType === 'quota_exceeded') { title = '配额不足'; message = '您的使用配额已用尽'; } console.error('[TaskPolling] 任务错误:', data.message); this.uiPushToast({ title, message, type: 'error', duration: 8000 }); // 清理状态 this.markLatestUserWorkCompleted(); this.streamingMessage = false; this.taskInProgress = false; this.stopRequested = false; this.$forceUpdate(); // 停止轮询 (async () => { const { useTaskStore } = await import('../../stores/task'); const taskStore = useTaskStore(); taskStore.stopPolling(); })(); }, handleTokenUpdate(data: any) { if (data.conversation_id === this.currentConversationId) { this.currentConversationTokens.cumulative_input_tokens = data.cumulative_input_tokens || 0; this.currentConversationTokens.cumulative_output_tokens = data.cumulative_output_tokens || 0; this.currentConversationTokens.cumulative_total_tokens = data.cumulative_total_tokens || 0; if (typeof data.current_context_tokens === 'number') { this.resourceSetCurrentContextTokens(data.current_context_tokens); } else { this.updateCurrentContextTokens(); } this.$forceUpdate(); } }, handleConversationChanged(data: any, eventIdx: number) { debugLog('[TaskPolling] 对话标题已更新, idx:', eventIdx, data); if (data && data.conversation_id === this.currentConversationId) { // 更新当前对话标题 if (data.title) { this.currentConversationTitle = data.title; debugLog('[TaskPolling] 更新当前对话标题:', data.title); } // 更新对话列表中的标题 if (data.title && Array.isArray(this.conversations)) { const conv = this.conversations.find(c => c && c.id === data.conversation_id); if (conv) { conv.title = data.title; debugLog('[TaskPolling] 更新对话列表中的标题:', data.title); } } this.$forceUpdate(); } }, handleConversationResolved(data: any) { if (data && data.conversation_id) { this.currentConversationId = data.conversation_id; if (data.title) { this.currentConversationTitle = data.title; } this.promoteConversationToTop(data.conversation_id); const pathFragment = this.stripConversationPrefix(data.conversation_id); const currentPath = window.location.pathname.replace(/^\/+/, ''); if (data.created) { history.pushState({ conversationId: data.conversation_id }, '', `/${pathFragment}`); } else if (currentPath !== pathFragment) { history.replaceState({ conversationId: data.conversation_id }, '', `/${pathFragment}`); } } }, /** * 清理已处理的事件索引 */ clearProcessedEvents() { if (this._processedEventIndices) { this._processedEventIndices.clear(); } }, /** * 恢复任务状态(页面刷新后调用) */ async restoreTaskState() { // 清理已处理的事件索引 this.clearProcessedEvents(); try { const { useTaskStore } = await import('../../stores/task'); const taskStore = useTaskStore(); // 如果已经在流式输出中,不重复恢复 if (this.streamingMessage || this.taskInProgress) { debugLog('[TaskPolling] 任务已在进行中,跳过恢复', { streamingMessage: this.streamingMessage, taskInProgress: this.taskInProgress, currentConversationId: this.currentConversationId }); return; } // 查找运行中的任务 const runningTask = await taskStore.loadRunningTask(this.currentConversationId); if (!runningTask) { debugLog('[TaskPolling] 没有运行中的任务', { currentConversationId: this.currentConversationId }); await this.restoreSubAgentWaitingState(); return; } debugLog('[TaskPolling] 发现运行中的任务,开始恢复状态', { taskId: runningTask?.task_id, status: runningTask?.status, conversationId: runningTask?.conversation_id }); // 检查历史是否已加载 const hasMessages = Array.isArray(this.messages) && this.messages.length > 0; if (!hasMessages) { debugLog('[TaskPolling] 历史未加载,等待历史加载完成'); setTimeout(() => { this.restoreTaskState(); }, 500); return; } debugLog('[TaskPolling] 历史已加载,开始精细恢复'); // 获取任务的所有事件 const detailResponse = await fetch(`/api/tasks/${taskStore.currentTaskId}`); if (!detailResponse.ok) { debugLog('[TaskPolling] 获取任务详情失败'); return; } const detailResult = await detailResponse.json(); if (!detailResult.success || !detailResult.data.events) { debugLog('[TaskPolling] 任务详情无效'); return; } const allEvents = detailResult.data.events; debugLog(`[TaskPolling] 获取到 ${allEvents.length} 个事件`); // 找到最后一条消息 const lastMessage = this.messages[this.messages.length - 1]; const isAssistantMessage = lastMessage && lastMessage.role === 'assistant'; // console.log('[TaskPolling] 最后一条消息:', { // exists: !!lastMessage, // role: lastMessage?.role, // actionsCount: lastMessage?.actions?.length || 0, // isAssistant: isAssistantMessage // }); // 检查是否需要从头重建 // 1. 最后一条不是 assistant 消息 // 2. 最后一条是空的 assistant 消息 // 3. 事件数量远大于历史中的 actions 数量(说明历史不完整) const historyActionsCount = lastMessage?.actions?.length || 0; const eventCount = allEvents.length; const historyIncomplete = eventCount > historyActionsCount + 5; // 允许5个事件的误差 const needsRebuild = !isAssistantMessage || (isAssistantMessage && (!lastMessage.actions || lastMessage.actions.length === 0)) || historyIncomplete; if (needsRebuild) { // if (historyIncomplete) { // console.log('[TaskPolling] 历史不完整,从头重建:', { // historyActionsCount, // eventCount, // diff: eventCount - historyActionsCount // }); // } debugLog('[TaskPolling] 需要从头重建 assistant 响应'); // 清空所有消息,准备从头重建 // 保留用户消息,只删除最后的 assistant 消息 if (isAssistantMessage) { debugLog('[TaskPolling] 删除不完整的 assistant 消息'); this.messages.pop(); } this.streamingMessage = true; this.taskInProgress = true; this.$forceUpdate(); // 重置偏移量为 0,从头获取所有事件来重建 assistant 消息 taskStore.lastEventIndex = 0; debugLog('[TaskPolling] 重置偏移量为 0,从头开始轮询'); // 标记正在从头重建,用于后续处理 this._rebuildingFromScratch = true; this._rebuildingEventCount = allEvents.length; // 记录当前事件总数 (window as any).__taskEventHandler = (event: any) => { this.handleTaskEvent(event); }; taskStore.startPolling((event: any) => { this.handleTaskEvent(event); }); // 延迟清除重建标记,确保所有历史事件都处理完毕 setTimeout(() => { debugLog('[TaskPolling] 历史事件处理完毕,清除重建标记'); this._rebuildingFromScratch = false; this._rebuildingEventCount = 0; }, 2000); return; } // 分析事件,找到当前正在进行的操作 let inThinking = false; let inText = false; for (let i = 0; i < allEvents.length; i++) { const event = allEvents[i]; if (event.type === 'thinking_start') { inThinking = true; } else if (event.type === 'thinking_end') { inThinking = false; } if (event.type === 'text_start') { inText = true; } else if (event.type === 'text_end') { inText = false; } } // console.log('[TaskPolling] 分析结果:', { // inThinking, // inText, // totalEvents: allEvents.length, // lastEventType: allEvents[allEvents.length - 1]?.type, // lastEventIdx: allEvents[allEvents.length - 1]?.idx // }); // 恢复思考块状态 if (lastMessage.actions) { // console.log('[TaskPolling] 历史中的 actions 详情:', lastMessage.actions.map((a, idx) => ({ // index: idx, // type: a.type, // id: a.id, // hasContent: !!a.content, // contentLength: a.content?.length || 0, // toolName: a.tool?.name, // hasBlockId: !!a.blockId, // blockId: a.blockId, // collapsed: a.collapsed, // streaming: a.streaming // }))); const thinkingActions = lastMessage.actions.filter(a => a.type === 'thinking'); // console.log('[TaskPolling] 思考块数量:', thinkingActions.length); if (inThinking && thinkingActions.length > 0) { // 正在思考中,检查最后一个思考块是否正在流式输出 const lastThinking = thinkingActions[thinkingActions.length - 1]; // 只有当思考块正在流式输出时才设置锁定状态(但不展开) if (lastThinking.streaming && lastThinking.blockId) { // console.log('[TaskPolling] 找到正在流式输出的思考块,设置锁定状态:', lastThinking.blockId); this.$nextTick(() => { this.chatSetThinkingLock(lastThinking.blockId, true); }); } } // 确保所有思考块都是折叠状态 for (const thinking of thinkingActions) { if (thinking.blockId) { thinking.collapsed = true; } } // 检查思考块状态(在设置之后)(已禁用) // thinkingActions.forEach((thinking, idx) => { // console.log(`[TaskPolling] 思考块 ${idx} (设置后):`, { // hasBlockId: !!thinking.blockId, // blockId: thinking.blockId, // collapsed: thinking.collapsed, // contentLength: thinking.content?.length || 0 // }); // }); // 恢复文本块状态 const textActions = lastMessage.actions.filter(a => a.type === 'text'); // console.log('[TaskPolling] 文本块数量:', textActions.length); if (inText && textActions.length > 0) { const lastText = textActions[textActions.length - 1]; // console.log('[TaskPolling] 标记文本块为流式状态'); lastText.streaming = true; } // 注册历史中的工具块到 toolActionIndex // 这样后续的 update_action 事件可以找到对应的块进行状态更新 const toolActions = lastMessage.actions.filter(a => a.type === 'tool'); // console.log('[TaskPolling] 工具块数量:', toolActions.length); for (const toolAction of toolActions) { if (toolAction.tool && toolAction.tool.id) { // console.log('[TaskPolling] 注册工具块:', { // id: toolAction.tool.id, // name: toolAction.tool.name, // status: toolAction.tool.status // }); // 注册到 toolActionIndex this.toolRegisterAction(toolAction, toolAction.tool.id); // 如果有 executionId,也注册 if (toolAction.tool.executionId) { this.toolRegisterAction(toolAction, toolAction.tool.executionId); } // 追踪工具调用 if (toolAction.tool.name) { this.toolTrackAction(toolAction.tool.name, toolAction); } } } } // 标记状态为进行中 this.streamingMessage = true; this.taskInProgress = true; // 设置 currentMessageIndex 指向最后一条 assistant 消息 // 这样后续添加的 action 会添加到正确的消息中 const lastMessageIndex = this.messages.length - 1; if (lastMessage && lastMessage.role === 'assistant') { this.currentMessageIndex = lastMessageIndex; // console.log('[TaskPolling] 设置 currentMessageIndex 为:', lastMessageIndex); } // 强制更新界面 this.$forceUpdate(); // 滚动到底部 this.$nextTick(() => { this.conditionalScrollToBottom(); }); // 注册事件处理器到全局 (window as any).__taskEventHandler = (event: any) => { this.handleTaskEvent(event); }; // 启动轮询(从当前偏移量开始,只处理新事件) debugLog('[TaskPolling] 启动轮询,起始偏移量:', taskStore.lastEventIndex); taskStore.startPolling((event: any) => { this.handleTaskEvent(event); }); this.uiPushToast({ title: '任务恢复', message: '检测到进行中的任务,已恢复连接', type: 'info', duration: 3000 }); } catch (error) { console.error('[TaskPolling] 恢复任务状态失败:', error); } }, /** * 恢复子智能体等待状态(页面刷新后调用) */ async restoreSubAgentWaitingState(retry = 0) { try { if (!this.currentConversationId) { if (retry < 5) { setTimeout(() => { this.restoreSubAgentWaitingState(retry + 1); }, 300); } return; } const response = await fetch('/api/sub_agents'); if (!response.ok) { debugLog('[TaskPolling] 获取子智能体状态失败'); return; } const result = await response.json(); if (!result.success) { debugLog('[TaskPolling] 子智能体状态响应无效'); return; } const tasks = Array.isArray(result.data) ? result.data : []; debugLog('[TaskPolling] 子智能体状态响应', { total: tasks.length, currentConversationId: this.currentConversationId, tasks: tasks.map((task: any) => ({ task_id: task?.task_id, status: task?.status, run_in_background: task?.run_in_background, notice_pending: task?.notice_pending, conversation_id: task?.conversation_id })) }); const terminalStatuses = new Set(['completed', 'failed', 'timeout', 'terminated']); const relevant = tasks.filter((task: any) => { if (task && task.conversation_id && task.conversation_id !== this.currentConversationId) { return false; } return true; }); const running = relevant.filter((task: any) => task?.run_in_background && !terminalStatuses.has(task?.status)); const pendingNotice = relevant.filter((task: any) => task?.notice_pending); if (running.length || pendingNotice.length) { debugLog('[TaskPolling] 恢复子智能体等待状态', { running: running.length, pendingNotice: pendingNotice.length, runningTasks: running.map((task: any) => ({ task_id: task?.task_id, status: task?.status })), pendingTasks: pendingNotice.map((task: any) => ({ task_id: task?.task_id, status: task?.status })) }); this.waitingForSubAgent = true; this.taskInProgress = true; this.streamingMessage = false; this.stopRequested = false; this.$forceUpdate(); } } catch (error) { console.error('[TaskPolling] 恢复子智能体等待状态失败:', error); } }, };