/** * SSE 连接管理 * * 封装 EventSource 的创建、事件注册和 done 事件处理,避免重复代码。 */ import { App } from './state.js'; import { removeTypingIndicator, setFileProcessing, setFileDone, setFileCached, setFileError, handleLLMStream, addChatMessage, showStatus, } from './chat.js'; import { handleAgentEvent } from './agent.js'; /** * 创建 SSE 连接并返回 EventSource 实例 * * @param {string} sessionId - 会话 ID * @param {Object} options - 选项配置 * @param {Function} options.onDone - done 事件处理器 * @param {boolean} options.handleFileProgress - 是否处理文件进度事件 (默认 true) * @param {boolean} options.handleLLMStream - 是否处理 LLM 流式事件 (默认 true) * @param {boolean} options.handleAgentEvents - 是否处理 Agent 事件 (默认 true) * @returns {EventSource} */ export function createSSEConnection(sessionId, options = {}) { const { onDone, handleFileProgress = true, handleLLMStream: handleStream = true, handleAgentEvents = true, } = options; const es = new EventSource(`/api/logs/${sessionId}`); es.addEventListener('message', e => { try { const msg = JSON.parse(e.data); // 文件进度事件 if (handleFileProgress && msg.type === 'file_progress') { const filename = msg.file; switch (msg.status) { case 'processing': setFileProcessing(filename); break; case 'done': setFileDone(filename, msg.summary || {}); break; case 'cached': setFileCached(filename); break; case 'error': setFileError(filename, msg.error); break; } return; } // LLM 流式事件 if (handleStream && msg.type === 'llm_stream') { handleLLMStream(msg); return; } // Agent 事件 if (handleAgentEvents && msg.type && msg.type.startsWith('agent_')) { handleAgentEvent(msg); return; } // 完成事件 if (msg.type === 'done') { es.close(); if (onDone) { onDone(msg.result); } return; } } catch (err) { // 普通日志行,忽略 } }); es.onerror = () => { es.close(); removeTypingIndicator(); App.isProcessing = false; App.processState = 'done'; addChatMessage('连接中断,请刷新页面重试', 'error'); }; return es; } /** * 处理 done 事件的通用逻辑 (根据 result 内容更新 UI 状态) * * @param {Object} result - done 事件的结果对象 */ export function handleDoneResult(result) { App.isProcessing = false; if (App.forceSubmitting) { App.forceSubmitting = false; App.lastProcessedFileCount = App.allFiles.length; if (result.ok) { addChatMessage('提交完成!', 'done'); } else { addChatMessage(`提交失败:${result.error || '未知错误'}`, 'error'); } return; } if (result.waiting_for_supplement) { App.processState = 'awaiting_supplement'; showStatus('信息不完整,请补充上传相关材料'); addChatMessage( '您可通过上传区补充文件,或直接输入文字说明补充信息,或输入"直接提交"跳过校验', 'system' ); } else { App.processState = 'done'; App.lastProcessedFileCount = App.allFiles.length; if (result.ok) { if (result.agent_ready) { if (result.submit_ok !== false) { addChatMessage('信息完整,已自动提交到财务系统', 'done'); } else { addChatMessage(`校验通过但提交失败:${result.submit_error || '未知错误'}`, 'error'); } } else if (result.invoice_count) { addChatMessage(`处理完成!共提取 ${result.invoice_count} 张发票数据`, 'done'); } } else { addChatMessage(`处理失败:${result.error || '未知错误'}`, 'error'); } } }