From 295c45fa00554296c9130c81b31d7e687070128f Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Sun, 11 May 2025 00:17:45 +0800 Subject: [PATCH] update --- renderer/src/api/message-bridge.ts | 43 +++++++++++-------- .../main-panel/chat/core/task-loop.ts | 31 +++++++++---- service/src/hook/adapter.ts | 37 +++++++++++++++- service/src/index.ts | 2 +- 4 files changed, 85 insertions(+), 28 deletions(-) diff --git a/renderer/src/api/message-bridge.ts b/renderer/src/api/message-bridge.ts index fbc1ccf..d9bdf76 100644 --- a/renderer/src/api/message-bridge.ts +++ b/renderer/src/api/message-bridge.ts @@ -24,11 +24,9 @@ export class MessageBridge { private handlers = new Map>(); private isConnected: Promise | null = null; - constructor(private wsUrl: string = 'ws://localhost:8080') { - - // 环境检测优先级: - // 1. VS Code WebView 环境 - // 2. 浏览器 WebSocket 环境 + constructor( + private setupSignature: any + ) { const platform = getPlatform(); @@ -68,7 +66,14 @@ export class MessageBridge { // WebSocket 环境连接 private setupWebSocket() { - this.ws = new WebSocket(this.wsUrl); + + const wsUrl = this.setupSignature; + + if (typeof wsUrl !== 'string') { + throw new Error('setupSignature must be a string'); + } + + this.ws = new WebSocket(wsUrl); this.ws.onmessage = (event) => { try { @@ -119,13 +124,19 @@ export class MessageBridge { } private setupNodejsListener() { - const EventEmitter = require('events'); + const { EventEmitter } = require('events'); - const eventEmitter = new EventEmitter(); - + const emitter = this.setupSignature; + if (!(emitter instanceof EventEmitter)) { + throw new Error('setupSignature must be an EventEmitter'); + } + + emitter.on('message/service', (message: VSCodeMessage) => { + this.dispatchMessage(message); + }); this.postMessage = (message) => { - eventEmitter.emit('server', message); + emitter.emit('message/renderer', message); }; } @@ -209,16 +220,14 @@ export class MessageBridge { } // 单例实例 -const messageBridge = new MessageBridge(); +let messageBridge: MessageBridge; // 向外暴露一个独立函数,保证 MessageBridge 是单例的 export function useMessageBridge() { + if (!messageBridge) { + messageBridge = new MessageBridge('ws://localhost:8080'); + } const bridge = messageBridge; - return { - postMessage: bridge.postMessage.bind(bridge), - addCommandListener: bridge.addCommandListener.bind(bridge), - commandRequest: bridge.commandRequest.bind(bridge), - awaitForWebsockt: bridge.awaitForWebsockt.bind(bridge) - }; + return bridge; } \ No newline at end of file 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 1666e2f..e0c8a56 100644 --- a/renderer/src/components/main-panel/chat/core/task-loop.ts +++ b/renderer/src/components/main-panel/chat/core/task-loop.ts @@ -1,18 +1,20 @@ /* eslint-disable */ import type { Ref } from "vue"; import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat"; -import { useMessageBridge } from "@/api/message-bridge"; +import { useMessageBridge, MessageBridge } from "@/api/message-bridge"; import type { OpenAI } from 'openai'; import { llmManager, llms } from "@/views/setting/llm"; import { pinkLog, redLog } from "@/views/setting/util"; import { ElMessage } from "element-plus"; import { handleToolCalls } from "./handle-tool-calls"; +import { getPlatform } from "@/api/platform"; export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk; export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string }; export interface TaskLoopOptions { maxEpochs: number; maxJsonParseRetry: number; + adapter: any; } export interface IErrorMssage { @@ -29,21 +31,34 @@ export interface IDoConversationResult { * @description 对任务循环进行的抽象封装 */ export class TaskLoop { - private bridge = useMessageBridge(); + private bridge: MessageBridge; private currentChatId = ''; + private onError: (error: IErrorMssage) => void = (msg) => {}; + private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => {}; + private onDone: () => void = () => {}; + private onEpoch: () => void = () => {}; private completionUsage: ChatCompletionChunk['usage'] | undefined; private llmConfig: any; constructor( private readonly streamingContent: Ref, private readonly streamingToolCalls: Ref, - private onError: (error: IErrorMssage) => void = (msg) => {}, - private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => {}, - private onDone: () => void = () => {}, - private onEpoch: () => void = () => {}, - private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3 }, + private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3, adapter: undefined }, ) { - + // 根据当前环境决定是否要开启 messageBridge + const platform = getPlatform(); + if (platform === 'nodejs') { + + const adapter = taskOptions.adapter; + + if (!adapter) { + throw new Error('adapter is required'); + } + + this.bridge = new MessageBridge(adapter.emitter); + } else { + this.bridge = useMessageBridge(); + } } private handleChunkDeltaContent(chunk: ChatCompletionChunk) { diff --git a/service/src/hook/adapter.ts b/service/src/hook/adapter.ts index 50f715b..781a267 100644 --- a/service/src/hook/adapter.ts +++ b/service/src/hook/adapter.ts @@ -1,9 +1,10 @@ import { WebSocket } from 'ws'; +import { EventEmitter } from 'events'; // WebSocket 消息格式 export interface WebSocketMessage { command: string; - data: any; + data: any; } // 服务器返回的消息格式 @@ -62,4 +63,36 @@ export class VSCodeWebViewLike { dispose: () => this.messageHandlers.delete(callback), }; } -} \ No newline at end of file +} + + +export class EventAdapter { + public emitter: EventEmitter; + private messageHandlers: Set; + + constructor(option: any) { + this.emitter = new EventEmitter(option); + this.messageHandlers = new Set(); + } + + /** + * 发送消息 + * @param message - 包含 command 和 args 的消息 + */ + postMessage(message: WebSocketMessage): void { + this.emitter.emit('message/service', message); + } + + /** + * 接收消息 + * @param callback - 消息回调 + * @returns {{ dispose: () => void }} - 可销毁的监听器 + */ + onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } { + this.messageHandlers.add(callback); + return { + dispose: () => this.messageHandlers.delete(callback), + }; + } +} + diff --git a/service/src/index.ts b/service/src/index.ts index 53374a1..137af6a 100644 --- a/service/src/index.ts +++ b/service/src/index.ts @@ -1,5 +1,5 @@ export { routeMessage } from './common/router'; -export { VSCodeWebViewLike } from './hook/adapter'; +export { VSCodeWebViewLike, EventAdapter } from './hook/adapter'; export { setVscodeWorkspace, setRunningCWD } from './hook/setting'; // TODO: 更加规范 export { client } from './mcp/connect.service'; \ No newline at end of file