upgrade taskloop

This commit is contained in:
锦恢 2025-06-20 17:04:26 +08:00
parent 50510ae647
commit 51ceacbc10
9 changed files with 66 additions and 26 deletions

View File

@ -3,11 +3,11 @@ import * as fs from 'node:fs';
const targetFile = './openmcp-sdk/task-loop.js'; const targetFile = './openmcp-sdk/task-loop.js';
if (fs.existsSync(targetFile)) { if (fs.existsSync(targetFile)) {
let content = fs.readFileSync(targetFile, 'utf8'); let content = fs.readFileSync(targetFile, 'utf-8');
// Replace element-plus with ./tools.js // Replace element-plus with ./tools.js
content = content.replace(/'element-plus'/g, "'./tools.js'"); content = content.replace(/'element-plus'/g, "'./tools.mjs'");
content = content.replace(/"element-plus"/g, "\"./tools.js\""); content = content.replace(/"element-plus"/g, "\"./tools.mjs\"");
// content = content.replace(/const chalk = require\("chalk"\);/g, 'const chalk = require("chalk").default;'); // content = content.replace(/const chalk = require\("chalk"\);/g, 'const chalk = require("chalk").default;');

View File

@ -34,6 +34,8 @@ export interface IErrorMssage {
msg: string msg: string
} }
export { MessageState };
export interface IDoConversationResult { export interface IDoConversationResult {
stop: boolean; stop: boolean;
} }
@ -83,12 +85,18 @@ export class TaskLoop {
throw new Error('adapter is required'); throw new Error('adapter is required');
} }
// 根据 adapter 创建 nodejs 下特殊的、基于 event 的 message bridge (不占用任何端口)
createMessageBridge(adapter.emitter); createMessageBridge(adapter.emitter);
// 用于进行连接同步
this.nodejsStatus.connectionFut = mcpClientAdapter.launch(); this.nodejsStatus.connectionFut = mcpClientAdapter.launch();
} }
// web 环境下 bridge 会自动加载完成 // web 环境下 bridge 会自动加载完成
this.bridge = useMessageBridge(); this.bridge = useMessageBridge();
// 注册 HMR
mcpClientAdapter.addConnectRefreshListener();
} }
/** /**
@ -381,7 +389,7 @@ export class TaskLoop {
if (verbose > 0) { if (verbose > 0) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.blueBright('🔧 calling tool'), chalk.blueBright('🔧 using tool'),
chalk.blueBright(toolCall.function!.name) chalk.blueBright(toolCall.function!.name)
); );
} }
@ -395,13 +403,13 @@ export class TaskLoop {
if (result.state === 'success') { if (result.state === 'success') {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.green('✓ call tools okey dockey'), chalk.green('✓ use tools'),
chalk.green(result.state) chalk.green(result.state)
); );
} else { } else {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.red('× fail to call tools'), chalk.red('× use tools'),
chalk.red(result.content.map(item => item.text).join(', ')) chalk.red(result.content.map(item => item.text).join(', '))
); );
} }
@ -413,7 +421,12 @@ export class TaskLoop {
const { verbose = 0 } = this.taskOptions; const { verbose = 0 } = this.taskOptions;
if (verbose > 0) { if (verbose > 0) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`)
);
}
if (verbose > 1) {
console.log(
chalk.blue('task loop enters a new epoch') chalk.blue('task loop enters a new epoch')
); );
} }
@ -425,9 +438,15 @@ export class TaskLoop {
if (verbose > 0) { if (verbose > 0) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
);
}
if (verbose > 1) {
console.log(
chalk.green('task loop finish a epoch') chalk.green('task loop finish a epoch')
); );
} }
return this.onDone(); return this.onDone();
} }
@ -559,7 +578,7 @@ export class TaskLoop {
if (verbose > 0) { if (verbose > 0) {
console.log( console.log(
chalk.gray(`[${new Date().toLocaleString()}]`), chalk.gray(`[${new Date().toLocaleString()}]`),
chalk.yellow('🤖 llm wants to call these tools'), chalk.yellow('🤖 Agent wants to use these tools'),
chalk.yellow(this.streamingToolCalls.value.map(tool => tool.function!.name || '').join(', ')) chalk.yellow(this.streamingToolCalls.value.map(tool => tool.function!.name || '').join(', '))
); );
} }

View File

@ -506,9 +506,7 @@ class McpClientAdapter {
constructor( constructor(
public platform: string public platform: string
) { ) {}
this.addConnectRefreshListener();
}
/** /**
* @description * @description
@ -562,7 +560,9 @@ class McpClientAdapter {
return index; return index;
} }
/**
* @description register HMR
*/
public addConnectRefreshListener() { public addConnectRefreshListener() {
// 创建对于 connect/refresh 的监听 // 创建对于 connect/refresh 的监听
if (!this.connectrefreshListener) { if (!this.connectrefreshListener) {

View File

@ -32,8 +32,8 @@ export default defineConfig({
lib: { lib: {
entry: resolve(__dirname, '..', 'renderer/src/components/main-panel/chat/core/task-loop.ts'), entry: resolve(__dirname, '..', 'renderer/src/components/main-panel/chat/core/task-loop.ts'),
name: 'TaskLoop', name: 'TaskLoop',
fileName: 'task-loop', fileName: (format) => `task-loop.js`,
formats: ['cjs'] formats: ['es']
}, },
outDir: resolve(__dirname, '..', 'openmcp-sdk'), outDir: resolve(__dirname, '..', 'openmcp-sdk'),
emptyOutDir: false, emptyOutDir: false,

View File

@ -1,6 +1,7 @@
{ {
"name": "openmcp-sdk", "name": "openmcp-sdk",
"version": "0.0.8", "version": "0.0.8",
"type": "module",
"description": "openmcp-sdk", "description": "openmcp-sdk",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"

View File

@ -195,7 +195,14 @@ export interface DefaultLLM {
model: string; model: string;
} }
import { MessageState, TaskLoopOptions, type ChatMessage, type ChatSetting, type TaskLoop, type TextMessage } from '../../task-loop.js'; import {
MessageState,
type TaskLoopOptions,
type ChatMessage,
type ChatSetting,
type TaskLoop,
type TextMessage
} from '../../task-loop.js';
export function UserMessage(content: string): TextMessage { export function UserMessage(content: string): TextMessage {
return { return {
@ -309,6 +316,7 @@ export class OmAgent {
const adapter = this._adapter; const adapter = this._adapter;
const { TaskLoop } = await import('../../task-loop.js'); const { TaskLoop } = await import('../../task-loop.js');
this._loop = new TaskLoop({ adapter, verbose, maxEpochs, maxJsonParseRetry }); this._loop = new TaskLoop({ adapter, verbose, maxEpochs, maxJsonParseRetry });
return this._loop; return this._loop;
} }
@ -316,9 +324,14 @@ export class OmAgent {
this._defaultLLM = option; this._defaultLLM = option;
} }
public async start( /**
messages: ChatMessage[] | string, * @description Asynchronous invoking agent by string or messages
settings?: ChatSetting & Partial<TaskLoopOptions> * @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) { if (messages.length === 0) {
throw new Error('messages is empty'); throw new Error('messages is empty');
@ -363,6 +376,10 @@ export class OmAgent {
throw new Error('default LLM is not set, please set it via omagent.setDefaultLLM() or write "defaultLLM" in mcpconfig.json'); throw new Error('default LLM is not set, please set it via omagent.setDefaultLLM() or write "defaultLLM" in mcpconfig.json');
} }
return await loop.start(storage, userMessage); await loop.start(storage, userMessage);
// get response from last message in message list
const lastMessage = storage.messages.at(-1)?.content;
return lastMessage;
} }
} }

View File

@ -38,7 +38,7 @@ export class McpServerConnectMonitor {
this.filePath = getFilePath(options); this.filePath = getFilePath(options);
// 记录实例创建 // 记录实例创建
logger.info({ uuid, connectionType: options.connectionType }, 'Created new connection monitor instance'); // logger.info({ uuid, connectionType: options.connectionType }, 'Created new connection monitor instance');
switch (options.connectionType) { switch (options.connectionType) {
case 'STDIO': case 'STDIO':
@ -94,7 +94,7 @@ export class McpServerConnectMonitor {
}, },
onStart: () => { onStart: () => {
// 使用 info 级别记录监控开始 // 使用 info 级别记录监控开始
logger.info({ uuid: this.uuid, filePath: path.resolve(fileConfig.filePath) }, 'Started monitoring file'); // logger.info({ uuid: this.uuid, filePath: path.resolve(fileConfig.filePath) }, 'Started monitoring file');
try { try {
const stats = fs.statSync(fileConfig.filePath); const stats = fs.statSync(fileConfig.filePath);

View File

@ -71,7 +71,7 @@ class SingleFileMonitor {
this.handleFileChange(true); this.handleFileChange(true);
} }
}); });
console.log(`正在监控文件: ${this.filePath}`); // console.log(`正在监控文件: ${this.filePath}`);
} catch (error) { } catch (error) {
this.onError(error as Error); this.onError(error as Error);
} }
@ -171,7 +171,7 @@ class SingleFileMonitor {
if (this.watcher) { if (this.watcher) {
// 明确指定close方法的类型解决TS2554错误 // 明确指定close方法的类型解决TS2554错误
(this.watcher.close as (callback?: () => void) => void)(() => { (this.watcher.close as (callback?: () => void) => void)(() => {
console.log(`已停止监控文件: ${this.filePath}`); // console.log(`已停止监控文件: ${this.filePath}`);
}); });
this.watcher = null; this.watcher = null;
} }

View File

@ -14,6 +14,9 @@
"noEmitHelpers": false, "noEmitHelpers": false,
"experimentalDecorators": true, "experimentalDecorators": true,
"sourceMap": true, "sourceMap": true,
"types": [
"./service/task-loop.d.ts"
],
// 访 openmcp-sdk // 访 openmcp-sdk
"paths": { "paths": {
"@openmcp-sdk/*": [ "@openmcp-sdk/*": [