task loop
This commit is contained in:
parent
be641197b5
commit
4a8385d7ad
@ -39,22 +39,22 @@ export class MessageBridge {
|
||||
switch (platform) {
|
||||
case 'vscode':
|
||||
this.setupVsCodeListener();
|
||||
pinkLog('当前模式: vscode');
|
||||
pinkLog('current platform: vscode');
|
||||
break;
|
||||
|
||||
case 'electron':
|
||||
this.setupElectronListener();
|
||||
pinkLog('当前模式: electron');
|
||||
pinkLog('current platform: electron');
|
||||
break;
|
||||
|
||||
case 'nodejs':
|
||||
this.setupNodejsListener();
|
||||
pinkLog('当前模式: nodejs');
|
||||
pinkLog('current platform: nodejs');
|
||||
break;
|
||||
|
||||
case 'web':
|
||||
this.setupWebSocket();
|
||||
pinkLog('当前模式: web');
|
||||
pinkLog('current platform: web');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ export class TaskLoop {
|
||||
};
|
||||
|
||||
constructor(
|
||||
private readonly taskOptions: TaskLoopOptions = {
|
||||
private taskOptions: TaskLoopOptions = {
|
||||
maxEpochs: 50,
|
||||
maxJsonParseRetry: 3,
|
||||
adapter: undefined,
|
||||
@ -99,6 +99,25 @@ export class TaskLoop {
|
||||
mcpClientAdapter.addConnectRefreshListener();
|
||||
}
|
||||
|
||||
public async waitConnection() {
|
||||
await this.nodejsStatus.connectionFut;
|
||||
}
|
||||
|
||||
public setTaskLoopOptions(taskOptions: TaskLoopOptions) {
|
||||
const {
|
||||
maxEpochs = 50,
|
||||
maxJsonParseRetry = 3,
|
||||
verbose = 1,
|
||||
} = taskOptions;
|
||||
|
||||
this.taskOptions = {
|
||||
maxEpochs,
|
||||
maxJsonParseRetry,
|
||||
verbose,
|
||||
...this.taskOptions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 处理 streaming 输出的每一个分块的 content 部分
|
||||
* @param chunk
|
||||
@ -784,9 +803,11 @@ export class TaskLoop {
|
||||
}
|
||||
}
|
||||
|
||||
public async getPrompt(promptId: string, args: Record<string, any>) {
|
||||
public async getPrompt(promptId: string, args?: Record<string, any>) {
|
||||
const prompt = await mcpClientAdapter.readPromptTemplate(promptId, args);
|
||||
return prompt;
|
||||
// transform prompt to string
|
||||
const promptString = prompt.messages.map(m => m.content.text).join('\n');
|
||||
return promptString;
|
||||
}
|
||||
|
||||
public async getResource(resourceUri: string) {
|
||||
|
@ -677,7 +677,7 @@ class McpClientAdapter {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public async readPromptTemplate(promptId: string, args: Record<string, any>) {
|
||||
public async readPromptTemplate(promptId: string, args?: Record<string, any>) {
|
||||
// TODO: 如果遇到不同服务器的同名 tool,请拓展解决方案
|
||||
// 目前只找到第一个匹配 toolName 的工具进行调用
|
||||
let clientId = this.clients[0].clientId;
|
||||
@ -691,6 +691,8 @@ class McpClientAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(args);
|
||||
|
||||
const bridge = useMessageBridge();
|
||||
const { code, msg } = await bridge.commandRequest<PromptsGetResponse>('prompts/get', { clientId, promptId, args });
|
||||
return msg;
|
||||
|
13
resources/openmcp-sdk-release/task-loop.d.ts
vendored
13
resources/openmcp-sdk-release/task-loop.d.ts
vendored
@ -189,6 +189,17 @@ interface ChatSetting {
|
||||
export class TaskLoop {
|
||||
constructor(taskOptions?: TaskLoopOptions);
|
||||
|
||||
/**
|
||||
* @description wait for connection
|
||||
*/
|
||||
waitConnection(): Promise<void>;
|
||||
|
||||
/**
|
||||
* @description Set the task loop options
|
||||
* @param taskOptions
|
||||
*/
|
||||
setTaskLoopOptions(taskOptions: TaskLoopOptions): void;
|
||||
|
||||
/**
|
||||
* @description make chat data
|
||||
* @param tabStorage
|
||||
@ -294,7 +305,7 @@ export class TaskLoop {
|
||||
/**
|
||||
* @description Get prompt template from mcp server
|
||||
*/
|
||||
getPrompt(promptId: string, args: Record<string, any>): Promise<string>;
|
||||
getPrompt(promptId: string, args?: Record<string, any>): Promise<string>;
|
||||
|
||||
/**
|
||||
* @description Get resource template from mcp server
|
||||
|
@ -1,8 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { WebSocket } from 'ws';
|
||||
import { EventEmitter } from 'events';
|
||||
import { routeMessage } from '../common/router.js';
|
||||
import { ConnectionType } from '../mcp/client.dto.js';
|
||||
|
||||
// WebSocket 消息格式
|
||||
@ -80,318 +76,3 @@ export class VSCodeWebViewLike {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class TaskLoopAdapter {
|
||||
public emitter: EventEmitter;
|
||||
private messageHandlers: Set<MessageHandler>;
|
||||
private connectionOptions: IConnectionArgs[] = [];
|
||||
|
||||
constructor(option?: any) {
|
||||
this.emitter = new EventEmitter(option);
|
||||
this.messageHandlers = new Set();
|
||||
|
||||
this.emitter.on('message/renderer', (message: WebSocketMessage) => {
|
||||
this.messageHandlers.forEach((handler) => handler(message));
|
||||
});
|
||||
|
||||
// 默认需要将监听的消息导入到 routeMessage 中
|
||||
this.onDidReceiveMessage((message) => {
|
||||
const { command, data } = message;
|
||||
|
||||
switch (command) {
|
||||
case 'nodejs/launch-signature':
|
||||
this.postMessage({
|
||||
command: 'nodejs/launch-signature',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: this.connectionOptions
|
||||
}
|
||||
})
|
||||
break;
|
||||
|
||||
case 'nodejs/update-connection-signature':
|
||||
// sdk 模式下不需要自动保存连接参数
|
||||
break;
|
||||
|
||||
default:
|
||||
routeMessage(command, data, this);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 发送消息
|
||||
* @param message - 包含 command 和 args 的消息
|
||||
*/
|
||||
public postMessage(message: WebSocketMessage): void {
|
||||
this.emitter.emit('message/service', message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 注册接受消息的句柄
|
||||
* @param callback - 消息回调
|
||||
* @returns {{ dispose: () => void }} - 可销毁的监听器
|
||||
*/
|
||||
public onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } {
|
||||
this.messageHandlers.add(callback);
|
||||
return {
|
||||
dispose: () => this.messageHandlers.delete(callback),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 连接到 mcp 服务端
|
||||
* @param mcpOption
|
||||
*/
|
||||
public addMcp(mcpOption: IConnectionArgs) {
|
||||
|
||||
// 0.1.4 新版本开始,此处修改为懒加载连接
|
||||
// 实际的连接移交给前端 mcpAdapter 中进行统一的调度
|
||||
// 调度步骤如下:
|
||||
// getLaunchSignature 先获取访问签名,访问签名通过当前函数 push 到 class 中
|
||||
|
||||
this.connectionOptions.push(mcpOption);
|
||||
}
|
||||
}
|
||||
|
||||
interface StdioMCPConfig {
|
||||
command: string;
|
||||
args: string[];
|
||||
env?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
description?: string;
|
||||
prompts?: string[];
|
||||
resources?: string[];
|
||||
}
|
||||
|
||||
interface HttpMCPConfig {
|
||||
url: string;
|
||||
type?: string;
|
||||
env?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
description?: string;
|
||||
prompts?: string[];
|
||||
resources?: string[];
|
||||
}
|
||||
|
||||
export interface OmAgentConfiguration {
|
||||
version: string;
|
||||
mcpServers: {
|
||||
[key: string]: StdioMCPConfig | HttpMCPConfig;
|
||||
};
|
||||
defaultLLM: {
|
||||
baseURL: string;
|
||||
apiToken: string;
|
||||
model: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface DefaultLLM {
|
||||
baseURL: string;
|
||||
apiToken?: string;
|
||||
model: string;
|
||||
}
|
||||
|
||||
import {
|
||||
MessageState,
|
||||
type TaskLoopOptions,
|
||||
type ChatMessage,
|
||||
type ChatSetting,
|
||||
type TaskLoop,
|
||||
type TextMessage
|
||||
} from '../../task-loop.js';
|
||||
|
||||
export function UserMessage(content: string): TextMessage {
|
||||
return {
|
||||
role: 'user',
|
||||
content,
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.None,
|
||||
serverName: '',
|
||||
enableXmlWrapper: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function AssistantMessage(content: string): TextMessage {
|
||||
return {
|
||||
role: 'assistant',
|
||||
content,
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.None,
|
||||
serverName: '',
|
||||
enableXmlWrapper: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OmAgent {
|
||||
private _adapter: TaskLoopAdapter;
|
||||
private _loop?: TaskLoop;
|
||||
private _defaultLLM?: DefaultLLM;
|
||||
|
||||
constructor() {
|
||||
this._adapter = new TaskLoopAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Load MCP configuration from file.
|
||||
* Supports multiple MCP backends and a default LLM model configuration.
|
||||
*
|
||||
* @example
|
||||
* Example configuration:
|
||||
* {
|
||||
* "version": "1.0.0",
|
||||
* "mcpServers": {
|
||||
* "openmemory": {
|
||||
* "command": "npx",
|
||||
* "args": ["-y", "openmemory"],
|
||||
* "env": {
|
||||
* "OPENMEMORY_API_KEY": "YOUR_API_KEY",
|
||||
* "CLIENT_NAME": "openmemory"
|
||||
* },
|
||||
* "description": "A MCP for long-term memory support"
|
||||
* }
|
||||
* },
|
||||
* "defaultLLM": {
|
||||
* "baseURL": "https://api.openmemory.ai",
|
||||
* "apiToken": "YOUR_API_KEY",
|
||||
* "model": "deepseek-chat"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param configPath - Path to the configuration file
|
||||
*/
|
||||
public loadMcpConfig(configPath: string) {
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as OmAgentConfiguration;
|
||||
const { mcpServers, defaultLLM } = config;
|
||||
|
||||
// set default llm
|
||||
this.setDefaultLLM(defaultLLM);
|
||||
|
||||
for (const key in mcpServers) {
|
||||
const mcpConfig = mcpServers[key];
|
||||
if ('command' in mcpConfig) {
|
||||
const commandString = (
|
||||
mcpConfig.command + ' ' + mcpConfig.args.join(' ')
|
||||
).trim();
|
||||
|
||||
this._adapter.addMcp({
|
||||
commandString,
|
||||
connectionType: 'STDIO',
|
||||
env: mcpConfig.env,
|
||||
description: mcpConfig.description,
|
||||
});
|
||||
} else {
|
||||
const connectionType: ConnectionType = mcpConfig.type === 'http' ? 'STREAMABLE_HTTP' : 'SSE';
|
||||
this._adapter.addMcp({
|
||||
url: mcpConfig.url,
|
||||
env: mcpConfig.env,
|
||||
connectionType,
|
||||
description: mcpConfig.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Add MCP server
|
||||
*/
|
||||
public addMcpServer(connectionArgs: IConnectionArgs) {
|
||||
this._adapter.addMcp(connectionArgs);
|
||||
}
|
||||
|
||||
private async getLoop(loopOption?: TaskLoopOptions) {
|
||||
if (this._loop) {
|
||||
return this._loop;
|
||||
}
|
||||
|
||||
const {
|
||||
verbose = 1,
|
||||
maxEpochs = 50,
|
||||
maxJsonParseRetry = 3,
|
||||
} = loopOption || {}
|
||||
|
||||
const adapter = this._adapter;
|
||||
const { TaskLoop } = await import('../../task-loop.js');
|
||||
this._loop = new TaskLoop({ adapter, verbose, maxEpochs, maxJsonParseRetry });
|
||||
|
||||
return this._loop;
|
||||
}
|
||||
|
||||
public setDefaultLLM(option: DefaultLLM) {
|
||||
this._defaultLLM = option;
|
||||
}
|
||||
|
||||
public async getPrompt(promptId: string, args: Record<string, any>) {
|
||||
const loop = await this.getLoop();
|
||||
const prompt = await loop.getPrompt(promptId, args);
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Asynchronous invoking agent by string or messages
|
||||
* @param messages Chat message or string
|
||||
* @param settings Chat setting and task loop options
|
||||
* @returns
|
||||
*/
|
||||
public async ainvoke(
|
||||
{ messages, settings }: { messages: ChatMessage[] | string; settings?: ChatSetting & Partial<TaskLoopOptions>; }
|
||||
) {
|
||||
if (messages.length === 0) {
|
||||
throw new Error('messages is empty');
|
||||
}
|
||||
|
||||
// detach taskloop option from settings and set default value
|
||||
const {
|
||||
maxEpochs = 50,
|
||||
maxJsonParseRetry = 3,
|
||||
verbose = 1
|
||||
} = settings || {};
|
||||
|
||||
const loop = await this.getLoop({ maxEpochs, maxJsonParseRetry, verbose });
|
||||
const storage = await loop.createStorage(settings);
|
||||
|
||||
// set input message
|
||||
// user can invoke [UserMessage("CONTENT")] to make messages quickly
|
||||
// or use string directly
|
||||
let userMessage: string;
|
||||
if (typeof messages === 'string') {
|
||||
userMessage = messages;
|
||||
} else {
|
||||
const lastMessageContent = messages.at(-1)?.content;
|
||||
if (typeof lastMessageContent === 'string') {
|
||||
userMessage = lastMessageContent;
|
||||
} else {
|
||||
throw new Error('last message content is undefined');
|
||||
}
|
||||
}
|
||||
|
||||
// select correct llm config
|
||||
// user can set llm config via omagent.setDefaultLLM()
|
||||
// or write "defaultLLM" in mcpconfig.json to specify
|
||||
if (this._defaultLLM) {
|
||||
loop.setLlmConfig({
|
||||
baseUrl: this._defaultLLM.baseURL,
|
||||
userToken: this._defaultLLM.apiToken,
|
||||
userModel: this._defaultLLM.model,
|
||||
});
|
||||
} else {
|
||||
// throw error to user and give the suggestion
|
||||
throw new Error('default LLM is not set, please set it via omagent.setDefaultLLM() or write "defaultLLM" in mcpconfig.json');
|
||||
}
|
||||
|
||||
await loop.start(storage, userMessage);
|
||||
|
||||
// get response from last message in message list
|
||||
const lastMessage = storage.messages.at(-1)?.content;
|
||||
return lastMessage;
|
||||
}
|
||||
}
|
||||
|
327
service/src/hook/sdk.ts
Normal file
327
service/src/hook/sdk.ts
Normal file
@ -0,0 +1,327 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { routeMessage } from '../common/router.js';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export class TaskLoopAdapter {
|
||||
public emitter: EventEmitter;
|
||||
private messageHandlers: Set<MessageHandler>;
|
||||
private connectionOptions: IConnectionArgs[] = [];
|
||||
|
||||
constructor(option?: any) {
|
||||
this.emitter = new EventEmitter(option);
|
||||
this.messageHandlers = new Set();
|
||||
|
||||
this.emitter.on('message/renderer', (message: WebSocketMessage) => {
|
||||
this.messageHandlers.forEach((handler) => handler(message));
|
||||
});
|
||||
|
||||
// 默认需要将监听的消息导入到 routeMessage 中
|
||||
this.onDidReceiveMessage((message) => {
|
||||
const { command, data } = message;
|
||||
|
||||
switch (command) {
|
||||
case 'nodejs/launch-signature':
|
||||
this.postMessage({
|
||||
command: 'nodejs/launch-signature',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: this.connectionOptions
|
||||
}
|
||||
})
|
||||
break;
|
||||
|
||||
case 'nodejs/update-connection-signature':
|
||||
// sdk 模式下不需要自动保存连接参数
|
||||
break;
|
||||
|
||||
default:
|
||||
routeMessage(command, data, this);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 发送消息
|
||||
* @param message - 包含 command 和 args 的消息
|
||||
*/
|
||||
public postMessage(message: WebSocketMessage): void {
|
||||
this.emitter.emit('message/service', message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 注册接受消息的句柄
|
||||
* @param callback - 消息回调
|
||||
* @returns {{ dispose: () => void }} - 可销毁的监听器
|
||||
*/
|
||||
public onDidReceiveMessage(callback: MessageHandler): { dispose: () => void } {
|
||||
this.messageHandlers.add(callback);
|
||||
return {
|
||||
dispose: () => this.messageHandlers.delete(callback),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 连接到 mcp 服务端
|
||||
* @param mcpOption
|
||||
*/
|
||||
public addMcp(mcpOption: IConnectionArgs) {
|
||||
|
||||
// 0.1.4 新版本开始,此处修改为懒加载连接
|
||||
// 实际的连接移交给前端 mcpAdapter 中进行统一的调度
|
||||
// 调度步骤如下:
|
||||
// getLaunchSignature 先获取访问签名,访问签名通过当前函数 push 到 class 中
|
||||
|
||||
this.connectionOptions.push(mcpOption);
|
||||
}
|
||||
}
|
||||
|
||||
interface StdioMCPConfig {
|
||||
command: string;
|
||||
args: string[];
|
||||
env?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
description?: string;
|
||||
prompts?: string[];
|
||||
resources?: string[];
|
||||
}
|
||||
|
||||
interface HttpMCPConfig {
|
||||
url: string;
|
||||
type?: string;
|
||||
env?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
description?: string;
|
||||
prompts?: string[];
|
||||
resources?: string[];
|
||||
}
|
||||
|
||||
export interface OmAgentConfiguration {
|
||||
version: string;
|
||||
mcpServers: {
|
||||
[key: string]: StdioMCPConfig | HttpMCPConfig;
|
||||
};
|
||||
defaultLLM: {
|
||||
baseURL: string;
|
||||
apiToken: string;
|
||||
model: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface DefaultLLM {
|
||||
baseURL: string;
|
||||
apiToken?: string;
|
||||
model: string;
|
||||
}
|
||||
|
||||
import {
|
||||
MessageState,
|
||||
type TaskLoopOptions,
|
||||
type ChatMessage,
|
||||
type ChatSetting,
|
||||
type TaskLoop,
|
||||
type TextMessage
|
||||
} from '../../task-loop.js';
|
||||
import { IConnectionArgs, MessageHandler, WebSocketMessage } from './adapter.js';
|
||||
import { ConnectionType } from 'src/mcp/client.dto.js';
|
||||
|
||||
export function UserMessage(content: string): TextMessage {
|
||||
return {
|
||||
role: 'user',
|
||||
content,
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.None,
|
||||
serverName: '',
|
||||
enableXmlWrapper: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function AssistantMessage(content: string): TextMessage {
|
||||
return {
|
||||
role: 'assistant',
|
||||
content,
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.None,
|
||||
serverName: '',
|
||||
enableXmlWrapper: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OmAgent {
|
||||
private _adapter: TaskLoopAdapter;
|
||||
private _loop?: TaskLoop;
|
||||
private _defaultLLM?: DefaultLLM;
|
||||
|
||||
constructor() {
|
||||
this._adapter = new TaskLoopAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Load MCP configuration from file.
|
||||
* Supports multiple MCP backends and a default LLM model configuration.
|
||||
*
|
||||
* @example
|
||||
* Example configuration:
|
||||
* {
|
||||
* "version": "1.0.0",
|
||||
* "mcpServers": {
|
||||
* "openmemory": {
|
||||
* "command": "npx",
|
||||
* "args": ["-y", "openmemory"],
|
||||
* "env": {
|
||||
* "OPENMEMORY_API_KEY": "YOUR_API_KEY",
|
||||
* "CLIENT_NAME": "openmemory"
|
||||
* },
|
||||
* "description": "A MCP for long-term memory support"
|
||||
* }
|
||||
* },
|
||||
* "defaultLLM": {
|
||||
* "baseURL": "https://api.openmemory.ai",
|
||||
* "apiToken": "YOUR_API_KEY",
|
||||
* "model": "deepseek-chat"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param configPath - Path to the configuration file
|
||||
*/
|
||||
public loadMcpConfig(configPath: string) {
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as OmAgentConfiguration;
|
||||
const { mcpServers, defaultLLM } = config;
|
||||
|
||||
// set default llm
|
||||
this.setDefaultLLM(defaultLLM);
|
||||
|
||||
for (const key in mcpServers) {
|
||||
const mcpConfig = mcpServers[key];
|
||||
if ('command' in mcpConfig) {
|
||||
const commandString = (
|
||||
mcpConfig.command + ' ' + mcpConfig.args.join(' ')
|
||||
).trim();
|
||||
|
||||
this._adapter.addMcp({
|
||||
commandString,
|
||||
connectionType: 'STDIO',
|
||||
env: mcpConfig.env,
|
||||
description: mcpConfig.description,
|
||||
});
|
||||
} else {
|
||||
const connectionType: ConnectionType = mcpConfig.type === 'http' ? 'STREAMABLE_HTTP' : 'SSE';
|
||||
this._adapter.addMcp({
|
||||
url: mcpConfig.url,
|
||||
env: mcpConfig.env,
|
||||
connectionType,
|
||||
description: mcpConfig.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Add MCP server
|
||||
*/
|
||||
public addMcpServer(connectionArgs: IConnectionArgs) {
|
||||
this._adapter.addMcp(connectionArgs);
|
||||
}
|
||||
|
||||
private async getLoop(loopOption?: TaskLoopOptions) {
|
||||
|
||||
if (this._loop) {
|
||||
if (loopOption) {
|
||||
this._loop.setTaskLoopOptions(loopOption);
|
||||
}
|
||||
return this._loop;
|
||||
}
|
||||
|
||||
const {
|
||||
verbose = 1,
|
||||
maxEpochs = 50,
|
||||
maxJsonParseRetry = 3,
|
||||
} = loopOption || {}
|
||||
|
||||
const adapter = this._adapter;
|
||||
const { TaskLoop } = await import('../../task-loop.js');
|
||||
|
||||
this._loop = new TaskLoop({ adapter, verbose, maxEpochs, maxJsonParseRetry });
|
||||
await this._loop.waitConnection();
|
||||
|
||||
return this._loop;
|
||||
}
|
||||
|
||||
public setDefaultLLM(option: DefaultLLM) {
|
||||
this._defaultLLM = option;
|
||||
}
|
||||
|
||||
public async getPrompt(promptId: string, args: Record<string, any>) {
|
||||
const loop = await this.getLoop();
|
||||
|
||||
const prompt = await loop.getPrompt(promptId, args);
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Asynchronous invoking agent by string or messages
|
||||
* @param messages Chat message or string
|
||||
* @param settings Chat setting and task loop options
|
||||
* @returns
|
||||
*/
|
||||
public async ainvoke(
|
||||
{ messages, settings }: { messages: ChatMessage[] | string; settings?: ChatSetting & Partial<TaskLoopOptions>; }
|
||||
) {
|
||||
if (messages.length === 0) {
|
||||
throw new Error('messages is empty');
|
||||
}
|
||||
|
||||
// detach taskloop option from settings and set default value
|
||||
const {
|
||||
maxEpochs = 50,
|
||||
maxJsonParseRetry = 3,
|
||||
verbose = 1
|
||||
} = settings || {};
|
||||
|
||||
const loop = await this.getLoop({ maxEpochs, maxJsonParseRetry, verbose });
|
||||
const storage = await loop.createStorage(settings);
|
||||
|
||||
// set input message
|
||||
// user can invoke [UserMessage("CONTENT")] to make messages quickly
|
||||
// or use string directly
|
||||
let userMessage: string;
|
||||
if (typeof messages === 'string') {
|
||||
userMessage = messages;
|
||||
} else {
|
||||
const lastMessageContent = messages.at(-1)?.content;
|
||||
if (typeof lastMessageContent === 'string') {
|
||||
userMessage = lastMessageContent;
|
||||
} else {
|
||||
throw new Error('last message content is undefined');
|
||||
}
|
||||
}
|
||||
|
||||
// select correct llm config
|
||||
// user can set llm config via omagent.setDefaultLLM()
|
||||
// or write "defaultLLM" in mcpconfig.json to specify
|
||||
if (this._defaultLLM) {
|
||||
loop.setLlmConfig({
|
||||
baseUrl: this._defaultLLM.baseURL,
|
||||
userToken: this._defaultLLM.apiToken,
|
||||
userModel: this._defaultLLM.model,
|
||||
});
|
||||
} else {
|
||||
// throw error to user and give the suggestion
|
||||
throw new Error('default LLM is not set, please set it via omagent.setDefaultLLM() or write "defaultLLM" in mcpconfig.json');
|
||||
}
|
||||
|
||||
await loop.start(storage, userMessage);
|
||||
|
||||
// get response from last message in message list
|
||||
const lastMessage = storage.messages.at(-1)?.content;
|
||||
return lastMessage;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export { routeMessage } from './common/router.js';
|
||||
export { VSCodeWebViewLike, TaskLoopAdapter, OmAgent, OmAgentConfiguration, UserMessage, AssistantMessage } from './hook/adapter.js';
|
||||
export { VSCodeWebViewLike } from './hook/adapter.js';
|
||||
export { setVscodeWorkspace, setRunningCWD } from './hook/setting.js';
|
||||
export { clientMap } from './mcp/connect.service.js';
|
@ -268,24 +268,24 @@ export async function connectService(
|
||||
): Promise<RestfulResponse> {
|
||||
try {
|
||||
// 使用cli-table3创建美观的表格
|
||||
const table = new Table({
|
||||
head: ['Property', 'Value'],
|
||||
colWidths: [20, 60],
|
||||
style: {
|
||||
head: ['green'],
|
||||
border: ['grey']
|
||||
}
|
||||
});
|
||||
// const table = new Table({
|
||||
// head: ['Property', 'Value'],
|
||||
// colWidths: [20, 60],
|
||||
// style: {
|
||||
// head: ['green'],
|
||||
// border: ['grey']
|
||||
// }
|
||||
// });
|
||||
|
||||
table.push(
|
||||
['Connection Type', option.connectionType],
|
||||
['Command', option.command || 'N/A'],
|
||||
['Arguments', option.args?.join(' ') || 'N/A'],
|
||||
['Working Directory', option.cwd || 'N/A'],
|
||||
['URL', option.url || 'N/A']
|
||||
);
|
||||
// table.push(
|
||||
// ['Connection Type', option.connectionType],
|
||||
// ['Command', option.command || 'N/A'],
|
||||
// ['Arguments', option.args?.join(' ') || 'N/A'],
|
||||
// ['Working Directory', option.cwd || 'N/A'],
|
||||
// ['URL', option.url || 'N/A']
|
||||
// );
|
||||
|
||||
console.log(table.toString());
|
||||
// console.log(table.toString());
|
||||
|
||||
// 预处理字符串
|
||||
await preprocessCommand(option, webview);
|
||||
|
1
service/src/sdk.ts
Normal file
1
service/src/sdk.ts
Normal file
@ -0,0 +1 @@
|
||||
export { TaskLoopAdapter, OmAgent, OmAgentConfiguration, UserMessage, AssistantMessage } from './hook/sdk.js';
|
39
service/task-loop.d.ts
vendored
39
service/task-loop.d.ts
vendored
@ -5,25 +5,25 @@ export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
|
||||
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string };
|
||||
|
||||
interface SchemaProperty {
|
||||
title: string;
|
||||
type: string;
|
||||
description?: string;
|
||||
title: string;
|
||||
type: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface InputSchema {
|
||||
type: string;
|
||||
properties: Record<string, SchemaProperty>;
|
||||
required?: string[];
|
||||
title?: string;
|
||||
$defs?: any;
|
||||
type: string;
|
||||
properties: Record<string, SchemaProperty>;
|
||||
required?: string[];
|
||||
title?: string;
|
||||
$defs?: any;
|
||||
}
|
||||
|
||||
interface ToolItem {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: InputSchema;
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: InputSchema;
|
||||
enabled: boolean;
|
||||
anyOf?: any;
|
||||
anyOf?: any;
|
||||
}
|
||||
|
||||
interface IExtraInfo {
|
||||
@ -87,7 +87,7 @@ export interface ToolCall {
|
||||
export interface ToolCallContent {
|
||||
type: string;
|
||||
text: string;
|
||||
[key: string]: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ToolCallResult {
|
||||
@ -189,6 +189,17 @@ interface ChatSetting {
|
||||
export class TaskLoop {
|
||||
constructor(taskOptions?: TaskLoopOptions);
|
||||
|
||||
/**
|
||||
* @description wait for connection
|
||||
*/
|
||||
waitConnection(): Promise<void>;
|
||||
|
||||
/**
|
||||
* @description Set the task loop options
|
||||
* @param taskOptions
|
||||
*/
|
||||
setTaskLoopOptions(taskOptions: TaskLoopOptions): void;
|
||||
|
||||
/**
|
||||
* @description make chat data
|
||||
* @param tabStorage
|
||||
@ -294,7 +305,7 @@ export class TaskLoop {
|
||||
/**
|
||||
* @description Get prompt template from mcp server
|
||||
*/
|
||||
getPrompt(promptId: string, args: Record<string, any>): Promise<string>;
|
||||
getPrompt(promptId: string, args?: Record<string, any>): Promise<string>;
|
||||
|
||||
/**
|
||||
* @description Get resource template from mcp server
|
||||
|
@ -10,7 +10,7 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"outDir": "./dist",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"declarationMap": false,
|
||||
"experimentalDecorators": true,
|
||||
},
|
||||
"paths": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user