This commit is contained in:
锦恢 2025-05-11 00:17:45 +08:00
parent e20bbf2b42
commit 295c45fa00
4 changed files with 85 additions and 28 deletions

View File

@ -24,11 +24,9 @@ export class MessageBridge {
private handlers = new Map<string, Set<CommandHandler>>();
private isConnected: Promise<boolean> | 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;
}

View File

@ -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<string>,
private readonly streamingToolCalls: Ref<ToolCall[]>,
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) {

View File

@ -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),
};
}
}
}
export class EventAdapter {
public emitter: EventEmitter;
private messageHandlers: Set<MessageHandler>;
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),
};
}
}

View File

@ -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';