From 1f55126c82966762ae9bbf7e32a857c818d9c8be Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Sun, 11 May 2025 18:26:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20streaming=20=E6=9E=B6?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main-panel/chat/chat-box/index.vue | 3 +- .../main-panel/chat/core/task-loop.ts | 19 +++++++--- renderer/src/views/setting/api.ts | 4 +- service/src/common/router.ts | 2 +- service/src/hook/adapter.ts | 37 +++++++++++++++---- service/src/index.ts | 2 +- webpack/webpack.task-loop.js | 1 + 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/renderer/src/components/main-panel/chat/chat-box/index.vue b/renderer/src/components/main-panel/chat/chat-box/index.vue index a5d7f13..6dd6f9e 100644 --- a/renderer/src/components/main-panel/chat/chat-box/index.vue +++ b/renderer/src/components/main-panel/chat/chat-box/index.vue @@ -93,7 +93,8 @@ function handleSend(newMessage?: string) { isLoading.value = true; autoScroll.value = true; - loop = new TaskLoop(streamingContent, streamingToolCalls); + loop = new TaskLoop(); + loop.bindStreaming(streamingContent, streamingToolCalls); loop.registerOnError((error) => { console.log('error.msg'); diff --git a/renderer/src/components/main-panel/chat/core/task-loop.ts b/renderer/src/components/main-panel/chat/core/task-loop.ts index 65e8ce3..082d556 100644 --- a/renderer/src/components/main-panel/chat/core/task-loop.ts +++ b/renderer/src/components/main-panel/chat/core/task-loop.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -import type { Ref } from "vue"; +import { ref, type Ref } from "vue"; import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat"; import { useMessageBridge, MessageBridge } from "@/api/message-bridge"; import type { OpenAI } from 'openai'; @@ -32,6 +32,9 @@ export interface IDoConversationResult { */ export class TaskLoop { private bridge: MessageBridge; + private streamingContent: Ref; + private streamingToolCalls: Ref; + private currentChatId = ''; private onError: (error: IErrorMssage) => void = (msg) => {}; private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => {}; @@ -41,10 +44,11 @@ export class TaskLoop { private llmConfig: any; constructor( - private readonly streamingContent: Ref, - private readonly streamingToolCalls: Ref, private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3, adapter: undefined }, ) { + this.streamingContent = ref(''); + this.streamingToolCalls = ref([]); + // 根据当前环境决定是否要开启 messageBridge const platform = getPlatform(); if (platform === 'nodejs') { @@ -150,7 +154,7 @@ export class TaskLoop { }, { once: true }); - console.log(chatData); + // console.log(chatData); this.bridge.postMessage({ command: 'llm/chat/completions', @@ -250,6 +254,11 @@ export class TaskLoop { this.llmConfig = config; } + public bindStreaming(content: Ref, toolCalls: Ref) { + this.streamingContent = content; + this.streamingToolCalls = toolCalls; + } + public getLlmConfig() { if (this.llmConfig) { return this.llmConfig; @@ -301,8 +310,8 @@ export class TaskLoop { // 发送请求 const doConverationResult = await this.doConversation(chatData); + console.log('[doConverationResult] Response'); console.log(doConverationResult); - // 如果存在需要调度的工具 if (this.streamingToolCalls.value.length > 0) { diff --git a/renderer/src/views/setting/api.ts b/renderer/src/views/setting/api.ts index a417010..1b2f4cb 100644 --- a/renderer/src/views/setting/api.ts +++ b/renderer/src/views/setting/api.ts @@ -26,9 +26,7 @@ export async function makeSimpleTalk() { // 使用最简单的 hello 来测试 const testMessage = 'hello'; - const s1 = ref(''); - const s2 = ref([]); - const loop = new TaskLoop(s1, s2); + const loop = new TaskLoop(); const chatStorage: ChatStorage = { messages: [], diff --git a/service/src/common/router.ts b/service/src/common/router.ts index 884e0a5..297ebd1 100644 --- a/service/src/common/router.ts +++ b/service/src/common/router.ts @@ -1,5 +1,5 @@ import { requestHandlerStorage } from "."; -import { PostMessageble } from "../hook/adapter"; +import type { PostMessageble } from "../hook/adapter"; import { LlmController } from "../llm/llm.controller"; import { ClientController } from "../mcp/client.controller"; import { ConnectController } from "../mcp/connect.controller"; diff --git a/service/src/hook/adapter.ts b/service/src/hook/adapter.ts index 2102d01..59e513a 100644 --- a/service/src/hook/adapter.ts +++ b/service/src/hook/adapter.ts @@ -1,5 +1,8 @@ import { WebSocket } from 'ws'; import { EventEmitter } from 'events'; +import { routeMessage } from '../common/router'; +import { McpOptions } from '../mcp/client.dto'; +import { client, connectService } from '../mcp/connect.service'; // WebSocket 消息格式 export interface WebSocketMessage { @@ -66,7 +69,7 @@ export class VSCodeWebViewLike { } -export class EventAdapter { +export class TaskLoopAdapter { public emitter: EventEmitter; private messageHandlers: Set; @@ -77,28 +80,48 @@ export class EventAdapter { this.emitter.on('message/renderer', (message: WebSocketMessage) => { this.messageHandlers.forEach((handler) => handler(message)); }); + + // 默认需要将监听的消息导入到 routeMessage 中 + this.onDidReceiveMessage((message) => { + const { command, data } = message; + routeMessage(command, data, this); + }); + } /** - * 发送消息 + * @description 发送消息 * @param message - 包含 command 和 args 的消息 */ - postMessage(message: WebSocketMessage): void { - console.log('message/renderer', message); - + public postMessage(message: WebSocketMessage): void { this.emitter.emit('message/service', message); } /** - * 接收消息 + * @description 注册接受消息的句柄 * @param callback - 消息回调 * @returns {{ dispose: () => void }} - 可销毁的监听器 */ - onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } { + public onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } { this.messageHandlers.add(callback); return { dispose: () => this.messageHandlers.delete(callback), }; } + + /** + * @description 连接到 mcp 服务端 + * @param mcpOption + */ + public async connectMcpServer(mcpOption: McpOptions) { + const res = await connectService(undefined, mcpOption); + if (res.code === 200) { + console.log('✅ 成功连接 mcp 服务器: ' + res.msg); + const version = client?.getServerVersion(); + console.log(version); + } else { + console.error('❌ 连接 mcp 服务器失败:' + res.msg); + } + } } diff --git a/service/src/index.ts b/service/src/index.ts index 137af6a..7e20481 100644 --- a/service/src/index.ts +++ b/service/src/index.ts @@ -1,5 +1,5 @@ export { routeMessage } from './common/router'; -export { VSCodeWebViewLike, EventAdapter } from './hook/adapter'; +export { VSCodeWebViewLike, TaskLoopAdapter } from './hook/adapter'; export { setVscodeWorkspace, setRunningCWD } from './hook/setting'; // TODO: 更加规范 export { client } from './mcp/connect.service'; \ No newline at end of file diff --git a/webpack/webpack.task-loop.js b/webpack/webpack.task-loop.js index 67d0ffb..1394e8a 100644 --- a/webpack/webpack.task-loop.js +++ b/webpack/webpack.task-loop.js @@ -45,6 +45,7 @@ module.exports = { plugins: [ new webpack.DefinePlugin({ window: { + nodejs: true, navigator: { userAgent: 2 },