144 lines
3.9 KiB
JavaScript
144 lines
3.9 KiB
JavaScript
/**
|
|
* 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');
|
|
}
|
|
}
|
|
} |