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 handlers = new Map<string, Set<CommandHandler>>();
private isConnected: Promise<boolean> | null = null; private isConnected: Promise<boolean> | null = null;
constructor(private wsUrl: string = 'ws://localhost:8080') { constructor(
private setupSignature: any
// 环境检测优先级: ) {
// 1. VS Code WebView 环境
// 2. 浏览器 WebSocket 环境
const platform = getPlatform(); const platform = getPlatform();
@ -68,7 +66,14 @@ export class MessageBridge {
// WebSocket 环境连接 // WebSocket 环境连接
private setupWebSocket() { 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) => { this.ws.onmessage = (event) => {
try { try {
@ -119,13 +124,19 @@ export class MessageBridge {
} }
private setupNodejsListener() { 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) => { 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 是单例的 // 向外暴露一个独立函数,保证 MessageBridge 是单例的
export function useMessageBridge() { export function useMessageBridge() {
if (!messageBridge) {
messageBridge = new MessageBridge('ws://localhost:8080');
}
const bridge = messageBridge; const bridge = messageBridge;
return { return bridge;
postMessage: bridge.postMessage.bind(bridge),
addCommandListener: bridge.addCommandListener.bind(bridge),
commandRequest: bridge.commandRequest.bind(bridge),
awaitForWebsockt: bridge.awaitForWebsockt.bind(bridge)
};
} }

View File

@ -1,18 +1,20 @@
/* eslint-disable */ /* eslint-disable */
import type { Ref } from "vue"; import type { Ref } from "vue";
import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat"; 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 type { OpenAI } from 'openai';
import { llmManager, llms } from "@/views/setting/llm"; import { llmManager, llms } from "@/views/setting/llm";
import { pinkLog, redLog } from "@/views/setting/util"; import { pinkLog, redLog } from "@/views/setting/util";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { handleToolCalls } from "./handle-tool-calls"; import { handleToolCalls } from "./handle-tool-calls";
import { getPlatform } from "@/api/platform";
export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk; export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string }; export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string };
export interface TaskLoopOptions { export interface TaskLoopOptions {
maxEpochs: number; maxEpochs: number;
maxJsonParseRetry: number; maxJsonParseRetry: number;
adapter: any;
} }
export interface IErrorMssage { export interface IErrorMssage {
@ -29,21 +31,34 @@ export interface IDoConversationResult {
* @description * @description
*/ */
export class TaskLoop { export class TaskLoop {
private bridge = useMessageBridge(); private bridge: MessageBridge;
private currentChatId = ''; 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 completionUsage: ChatCompletionChunk['usage'] | undefined;
private llmConfig: any; private llmConfig: any;
constructor( constructor(
private readonly streamingContent: Ref<string>, private readonly streamingContent: Ref<string>,
private readonly streamingToolCalls: Ref<ToolCall[]>, private readonly streamingToolCalls: Ref<ToolCall[]>,
private onError: (error: IErrorMssage) => void = (msg) => {}, private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3, adapter: undefined },
private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => {},
private onDone: () => void = () => {},
private onEpoch: () => void = () => {},
private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3 },
) { ) {
// 根据当前环境决定是否要开启 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) { private handleChunkDeltaContent(chunk: ChatCompletionChunk) {

View File

@ -1,4 +1,5 @@
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
import { EventEmitter } from 'events';
// WebSocket 消息格式 // WebSocket 消息格式
export interface WebSocketMessage { export interface WebSocketMessage {
@ -63,3 +64,35 @@ export class VSCodeWebViewLike {
}; };
} }
} }
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 { routeMessage } from './common/router';
export { VSCodeWebViewLike } from './hook/adapter'; export { VSCodeWebViewLike, EventAdapter } from './hook/adapter';
export { setVscodeWorkspace, setRunningCWD } from './hook/setting'; export { setVscodeWorkspace, setRunningCWD } from './hook/setting';
// TODO: 更加规范 // TODO: 更加规范
export { client } from './mcp/connect.service'; export { client } from './mcp/connect.service';