/** * 对话服务 * 整合 SSE 通信、工具执行、用户交互 */ import * as vscode from 'vscode'; import { startStreamDialog, generateTaskId, SSEController, SSECallbacks } from './sseHandler'; import { executeToolCall, createToolExecutorContext, ToolExecutorContext } from './toolExecutor'; import { userInteractionManager } from './userInteraction'; import { getConfig } from '../config/settings'; import type { DialogRequest, ToolCallRequest, AskUserEvent } from '../types/api'; /** * 对话回调接口 */ export interface DialogCallbacks { /** 收到文本(可能多次调用,流式) */ onText?: (text: string, isStreaming: boolean) => void; /** 工具开始执行 */ onToolStart?: (toolName: string) => void; /** 工具执行完成 */ onToolComplete?: (toolName: string, result: string) => void; /** 工具执行错误 */ onToolError?: (toolName: string, error: string) => void; /** 显示问题(ask_user) */ onQuestion?: (askId: string, question: string, options: string[]) => void; /** 对话完成 */ onComplete?: () => void; /** 错误 */ onError?: (message: string) => void; /** 通知消息 */ onNotification?: (message: string) => void; } /** * 对话会话 */ export class DialogSession { private taskId: string; private sseController: SSEController | null = null; private toolContext: ToolExecutorContext; private accumulatedText = ''; private isActive = false; constructor(extensionPath: string) { this.taskId = generateTaskId(); this.toolContext = createToolExecutorContext(extensionPath); } /** * 获取任务ID */ getTaskId(): string { return this.taskId; } /** * 是否活跃 */ get active(): boolean { return this.isActive; } /** * 发送消息并开始流式对话 */ async sendMessage( message: string, callbacks: DialogCallbacks ): Promise { if (this.isActive) { callbacks.onError?.('当前有对话正在进行中'); return; } this.isActive = true; this.accumulatedText = ''; const config = getConfig(); const request: DialogRequest = { taskId: this.taskId, message, userId: config.userId, toolMode: 'AGENT' }; const sseCallbacks: SSECallbacks = { onTextDelta: (data) => { this.accumulatedText += data.text; console.log('[DialogSession] onTextDelta, 累积文本长度:', this.accumulatedText.length); callbacks.onText?.(this.accumulatedText, true); }, onToolCall: async (data: ToolCallRequest) => { callbacks.onToolStart?.(data.params.name); try { await executeToolCall(data, this.toolContext); callbacks.onToolComplete?.(data.params.name, '执行完成'); } catch (error) { const errorMsg = error instanceof Error ? error.message : '未知错误'; callbacks.onToolError?.(data.params.name, errorMsg); } }, onToolStart: (data) => { callbacks.onToolStart?.(data.tool_name); }, onToolComplete: (data) => { callbacks.onToolComplete?.(data.tool_name, data.result); }, onToolError: (data) => { callbacks.onToolError?.(data.tool_name, data.error); }, onAskUser: async (data: AskUserEvent) => { callbacks.onQuestion?.(data.askId, data.question, data.options); try { await userInteractionManager.handleAskUser(data, this.taskId); } catch (error) { console.error('[DialogSession] 处理用户问题失败:', error); } }, onComplete: (data) => { this.isActive = false; // 发送最终文本(非流式) if (this.accumulatedText) { callbacks.onText?.(this.accumulatedText, false); } callbacks.onComplete?.(); }, onError: (data) => { this.isActive = false; callbacks.onError?.(data.message); }, onWarning: (data) => { callbacks.onNotification?.(`⚠️ ${data.message}`); }, onNotification: (data) => { callbacks.onNotification?.(data.message); }, onOpen: () => { console.log('[DialogSession] SSE 连接已建立'); }, onClose: () => { console.log('[DialogSession] SSE 连接已关闭'); this.isActive = false; } }; try { this.sseController = await startStreamDialog(request, sseCallbacks); } catch (error) { this.isActive = false; const errorMsg = error instanceof Error ? error.message : '连接失败'; callbacks.onError?.(errorMsg); throw error; } } /** * 中止当前对话 */ abort(): void { if (this.sseController) { this.sseController.abort(); this.sseController = null; } this.isActive = false; userInteractionManager.cancelAll(); } /** * 提交用户回答 */ async submitAnswer( askId: string, selected?: string[], customInput?: string ): Promise { await userInteractionManager.receiveAnswer(askId, selected, customInput); } } /** * 全局对话会话管理 */ class DialogManager { private currentSession: DialogSession | null = null; /** * 创建新会话 */ createSession(extensionPath: string): DialogSession { // 如果有活跃会话,先中止 if (this.currentSession?.active) { this.currentSession.abort(); } this.currentSession = new DialogSession(extensionPath); return this.currentSession; } /** * 获取当前会话 */ getCurrentSession(): DialogSession | null { return this.currentSession; } /** * 中止当前会话 */ abortCurrentSession(): void { this.currentSession?.abort(); } } export const dialogManager = new DialogManager();