WIP: promptx tools 开发中的代码
- 正在开发 promptx tools 功能
- 包含 bin、commands、DACP 相关修改
- 新增 tool 目录结构
🚧 这是开发中的代码,需要继续完善
This commit is contained in:
@ -105,7 +105,7 @@ program
|
|||||||
.option('--host <address>', '绑定地址 (仅http/sse传输)', 'localhost')
|
.option('--host <address>', '绑定地址 (仅http/sse传输)', 'localhost')
|
||||||
.option('--cors', '启用CORS (仅http/sse传输)', false)
|
.option('--cors', '启用CORS (仅http/sse传输)', false)
|
||||||
.option('--debug', '启用调试模式', false)
|
.option('--debug', '启用调试模式', false)
|
||||||
.option('--with-dacp', '同时启动DACP服务', false)
|
.option('--with-dacp', '(已废弃,静默忽略)', false)
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
try {
|
try {
|
||||||
// 设置调试模式
|
// 设置调试模式
|
||||||
@ -116,7 +116,8 @@ program
|
|||||||
// 根据传输类型选择命令
|
// 根据传输类型选择命令
|
||||||
if (options.transport === 'stdio') {
|
if (options.transport === 'stdio') {
|
||||||
const mcpServer = new MCPServerCommand();
|
const mcpServer = new MCPServerCommand();
|
||||||
await mcpServer.execute({ withDacp: options.withDacp });
|
// 🔧 DACP现为Mock模式,静默忽略用户的withDacp配置
|
||||||
|
await mcpServer.execute();
|
||||||
} else if (options.transport === 'http' || options.transport === 'sse') {
|
} else if (options.transport === 'http' || options.transport === 'sse') {
|
||||||
const mcpHttpServer = new MCPStreamableHttpCommand();
|
const mcpHttpServer = new MCPStreamableHttpCommand();
|
||||||
const serverOptions = {
|
const serverOptions = {
|
||||||
|
|||||||
@ -84,10 +84,8 @@ class MCPServerCommand {
|
|||||||
// 设置进程清理处理器
|
// 设置进程清理处理器
|
||||||
this.setupProcessCleanup();
|
this.setupProcessCleanup();
|
||||||
|
|
||||||
// 如果需要启动DACP服务
|
// 🔧 DACP现已改为Mock模式,无需启动独立服务
|
||||||
if (options.withDacp) {
|
// 静默忽略任何withDacp选项,保持向后兼容
|
||||||
await this.startDACPService();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log('🚀 启动MCP Server...');
|
this.log('🚀 启动MCP Server...');
|
||||||
const transport = new StdioServerTransport();
|
const transport = new StdioServerTransport();
|
||||||
@ -145,44 +143,27 @@ class MCPServerCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清理子进程
|
* 清理子进程 (DACP现为Mock模式,此方法保留但无实际清理工作)
|
||||||
|
* @deprecated DACP已改为Mock模式,无需清理子进程
|
||||||
*/
|
*/
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if (this.dacpProcess && !this.dacpProcess.killed && this.dacpProcess.pid) {
|
// 🔧 DACP现已改为Mock模式,无需清理DACP子进程
|
||||||
this.log('🛑 正在终止DACP服务及其所有子进程...');
|
// HTTP模式的进程清理代码已保留作为参考实现
|
||||||
|
this.log('🔧 Mock模式下无需清理DACP子进程');
|
||||||
// 使用 tree-kill 终止整个进程树
|
|
||||||
treeKill(this.dacpProcess.pid, 'SIGTERM', (err) => {
|
|
||||||
if (err) {
|
|
||||||
this.log(`⚠️ 优雅终止失败: ${err.message}`);
|
|
||||||
|
|
||||||
// 3秒后强制终止
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.dacpProcess && !this.dacpProcess.killed && this.dacpProcess.pid) {
|
|
||||||
this.log('⚠️ DACP服务未响应SIGTERM,强制终止整个进程树...');
|
|
||||||
treeKill(this.dacpProcess.pid, 'SIGKILL', (killErr) => {
|
|
||||||
if (killErr) {
|
|
||||||
this.log(`❌ 强制终止失败: ${killErr.message}`);
|
|
||||||
} else {
|
|
||||||
this.log('✅ DACP服务进程树已强制终止');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
} else {
|
|
||||||
this.log('✅ DACP服务进程树已优雅终止');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测DACP服务是否已经运行
|
* 检测DACP服务是否已经运行 (HTTP模式 - 仅作参考实现保留)
|
||||||
|
* @deprecated DACP已改为Mock模式,此方法仅保留作为参考
|
||||||
* @param {string} host - 主机地址
|
* @param {string} host - 主机地址
|
||||||
* @param {number} port - 端口号
|
* @param {number} port - 端口号
|
||||||
* @returns {Promise<boolean>} 服务是否运行
|
* @returns {Promise<boolean>} 服务是否运行
|
||||||
*/
|
*/
|
||||||
async isDACPServiceRunning(host = 'localhost', port = 3002) {
|
async isDACPServiceRunning(host = 'localhost', port = 3002) {
|
||||||
|
// 🔧 Mock模式下始终返回false,因为不需要HTTP服务
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* HTTP模式参考实现(已禁用)
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@ -224,15 +205,28 @@ class MCPServerCommand {
|
|||||||
|
|
||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取DACP服务信息
|
* 获取DACP服务信息 (HTTP模式 - 仅作参考实现保留)
|
||||||
|
* @deprecated DACP已改为Mock模式,此方法仅保留作为参考
|
||||||
* @param {string} host - 主机地址
|
* @param {string} host - 主机地址
|
||||||
* @param {number} port - 端口号
|
* @param {number} port - 端口号
|
||||||
* @returns {Promise<Object|null>} 服务信息
|
* @returns {Promise<Object|null>} 服务信息
|
||||||
*/
|
*/
|
||||||
async getDACPServiceInfo(host = 'localhost', port = 3002) {
|
async getDACPServiceInfo(host = 'localhost', port = 3002) {
|
||||||
|
// 🔧 Mock模式下返回模拟的服务信息
|
||||||
|
return {
|
||||||
|
service: {
|
||||||
|
name: 'PromptX DACP Mock Service',
|
||||||
|
version: '1.0.0-mock'
|
||||||
|
},
|
||||||
|
available_actions: ['calculate', 'send_email'],
|
||||||
|
mode: 'local_mock'
|
||||||
|
};
|
||||||
|
|
||||||
|
/* HTTP模式参考实现(已禁用)
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@ -271,12 +265,25 @@ class MCPServerCommand {
|
|||||||
|
|
||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动DACP服务
|
* 启动DACP服务 (HTTP模式 - 仅作参考实现保留)
|
||||||
|
* @deprecated DACP已改为Mock模式,此方法仅保留作为参考
|
||||||
*/
|
*/
|
||||||
async startDACPService() {
|
async startDACPService() {
|
||||||
|
// 🔧 Mock模式下输出提示信息即可
|
||||||
|
console.error('');
|
||||||
|
console.error('=====================================');
|
||||||
|
console.error('🔧 DACP Mock模式已启用');
|
||||||
|
console.error('📦 本地函数调用模式:无需HTTP服务');
|
||||||
|
console.error('🔧 支持的Actions: send_email, calculate');
|
||||||
|
console.error('✅ Mock模式启动成功');
|
||||||
|
console.error('=====================================');
|
||||||
|
console.error('');
|
||||||
|
|
||||||
|
/* HTTP模式参考实现(已禁用)
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
@ -395,6 +402,7 @@ class MCPServerCommand {
|
|||||||
this.log(`❌ DACP服务启动失败: ${error.message}`);
|
this.log(`❌ DACP服务启动失败: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
const BasePouchCommand = require('../BasePouchCommand');
|
const BasePouchCommand = require('../BasePouchCommand');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DACP服务调用命令
|
* DACP服务调用命令
|
||||||
* 负责调用DACP服务,实现从AI建议到AI行动的转换
|
* 负责调用DACP服务,实现从AI建议到AI行动的转换
|
||||||
|
*
|
||||||
|
* 🔧 当前实现:Mock模式(本地函数调用)
|
||||||
|
* 🌐 HTTP模式代码保留作为参考实现
|
||||||
*/
|
*/
|
||||||
class DACPCommand extends BasePouchCommand {
|
class DACPCommand extends BasePouchCommand {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -12,6 +17,10 @@ class DACPCommand extends BasePouchCommand {
|
|||||||
// 统一的DACP服务端点
|
// 统一的DACP服务端点
|
||||||
// 所有service_id都路由到同一个服务
|
// 所有service_id都路由到同一个服务
|
||||||
this.defaultEndpoint = 'http://localhost:3002/dacp';
|
this.defaultEndpoint = 'http://localhost:3002/dacp';
|
||||||
|
|
||||||
|
// 🔧 永久使用Mock模式(本地函数调用)
|
||||||
|
// 不再支持HTTP模式,简化架构复杂度
|
||||||
|
this.useMockMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +46,8 @@ class DACPCommand extends BasePouchCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务端点
|
* 获取服务端点(HTTP模式 - 仅作参考实现保留)
|
||||||
|
* @deprecated 当前使用Mock模式,此方法仅保留作为参考
|
||||||
* @param {string} serviceId - 服务ID
|
* @param {string} serviceId - 服务ID
|
||||||
* @returns {string} 服务端点URL
|
* @returns {string} 服务端点URL
|
||||||
*/
|
*/
|
||||||
@ -59,20 +69,8 @@ class DACPCommand extends BasePouchCommand {
|
|||||||
|
|
||||||
const { service_id, action, parameters } = args;
|
const { service_id, action, parameters } = args;
|
||||||
|
|
||||||
// 获取服务端点(现在是统一的)
|
// 🔧 直接使用本地Mock调用
|
||||||
const endpoint = this.getServiceEndpoint(service_id);
|
return await this.callLocalService(args);
|
||||||
|
|
||||||
// 构造DACP请求
|
|
||||||
const dacpRequest = {
|
|
||||||
service_id,
|
|
||||||
action,
|
|
||||||
parameters,
|
|
||||||
request_id: `req_${Date.now()}`
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调用DACP服务
|
|
||||||
const result = await this.makeHttpRequest(endpoint, dacpRequest);
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 统一错误处理
|
// 统一错误处理
|
||||||
@ -87,7 +85,99 @@ class DACPCommand extends BasePouchCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送HTTP请求
|
* 本地服务调用(Mock模式)
|
||||||
|
* @param {Object} args - 调用参数
|
||||||
|
* @returns {Promise<Object>} DACP标准响应
|
||||||
|
*/
|
||||||
|
async callLocalService(args) {
|
||||||
|
const startTime = Date.now();
|
||||||
|
const { service_id, action, parameters } = args;
|
||||||
|
const request_id = `req_${Date.now()}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 读取DACP配置
|
||||||
|
const configPath = path.join(__dirname, '../../../dacp/dacp-promptx-service/dacp.config.json');
|
||||||
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
|
||||||
|
// 2. 验证service_id
|
||||||
|
if (service_id !== config.service.id) {
|
||||||
|
throw new Error(`Service ${service_id} not found. This is ${config.service.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 动态加载actions
|
||||||
|
const actionsDir = path.join(__dirname, '../../../dacp/dacp-promptx-service/actions');
|
||||||
|
const actions = {};
|
||||||
|
|
||||||
|
if (fs.existsSync(actionsDir)) {
|
||||||
|
fs.readdirSync(actionsDir).forEach(file => {
|
||||||
|
if (file.endsWith('.js')) {
|
||||||
|
const actionName = file.replace('.js', '');
|
||||||
|
actions[actionName] = require(path.join(actionsDir, file));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 查找action处理器
|
||||||
|
let handler = null;
|
||||||
|
|
||||||
|
// 先按模块名查找
|
||||||
|
for (const [moduleName, module] of Object.entries(actions)) {
|
||||||
|
if (module[action] && typeof module[action] === 'function') {
|
||||||
|
handler = module[action];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找不到则按精确匹配查找
|
||||||
|
if (!handler && actions[action]) {
|
||||||
|
handler = actions[action];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error(`Action ${action} is not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 执行action
|
||||||
|
const result = await handler(parameters);
|
||||||
|
|
||||||
|
// 6. 返回DACP标准格式响应
|
||||||
|
return {
|
||||||
|
request_id: request_id,
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
execution_result: result,
|
||||||
|
evaluation: {
|
||||||
|
constraint_compliance: true,
|
||||||
|
rule_adherence: true,
|
||||||
|
guideline_alignment: true
|
||||||
|
},
|
||||||
|
applied_guidelines: [
|
||||||
|
'DACP protocol standard',
|
||||||
|
'Local mock execution'
|
||||||
|
],
|
||||||
|
performance_metrics: {
|
||||||
|
execution_time: `${Date.now() - startTime}ms`,
|
||||||
|
resource_usage: 'minimal'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
request_id: request_id,
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: error.message.includes('not found') ? 'INVALID_SERVICE' :
|
||||||
|
error.message.includes('not supported') ? 'UNKNOWN_ACTION' : 'EXECUTION_ERROR',
|
||||||
|
message: error.message
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送HTTP请求(HTTP模式 - 仅作参考实现保留)
|
||||||
|
* @deprecated 当前使用Mock模式,此方法仅保留作为参考
|
||||||
* @param {string} url - 请求URL
|
* @param {string} url - 请求URL
|
||||||
* @param {Object} data - 请求数据
|
* @param {Object} data - 请求数据
|
||||||
* @returns {Promise<Object>} 响应数据
|
* @returns {Promise<Object>} 响应数据
|
||||||
@ -149,8 +239,7 @@ class DACPCommand extends BasePouchCommand {
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
const executionResult = result.data.execution_result;
|
const executionResult = result.data.execution_result;
|
||||||
const metrics = result.data.performance_metrics;
|
const metrics = result.data.performance_metrics;
|
||||||
|
return `🚀 DACP服务调用成功 (🔧 本地Mock模式)
|
||||||
return `🚀 DACP服务调用成功
|
|
||||||
|
|
||||||
📋 执行结果:
|
📋 执行结果:
|
||||||
${JSON.stringify(executionResult, null, 2)}
|
${JSON.stringify(executionResult, null, 2)}
|
||||||
@ -172,9 +261,10 @@ ${JSON.stringify(executionResult, null, 2)}
|
|||||||
return `❌ DACP服务调用异常
|
return `❌ DACP服务调用异常
|
||||||
|
|
||||||
错误详情: ${error.message}
|
错误详情: ${error.message}
|
||||||
|
运行模式: 🔧 本地Mock模式
|
||||||
|
|
||||||
💡 请检查:
|
💡 请检查:
|
||||||
1. DACP服务是否运行 (http://localhost:3002/health)
|
1. DACP action模块是否存在
|
||||||
2. 服务ID是否正确
|
2. 服务ID是否正确
|
||||||
3. 操作名称是否有效
|
3. 操作名称是否有效
|
||||||
4. 参数格式是否正确`;
|
4. 参数格式是否正确`;
|
||||||
|
|||||||
292
src/lib/tool/ToolExecutor.js
Normal file
292
src/lib/tool/ToolExecutor.js
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
const ToolValidator = require('./ToolValidator');
|
||||||
|
const { TOOL_ERROR_CODES } = require('./ToolInterface');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToolExecutor 工具执行器
|
||||||
|
* 负责工具的加载、验证、执行和结果处理
|
||||||
|
*/
|
||||||
|
class ToolExecutor {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.options = {
|
||||||
|
timeout: 30000, // 默认30秒超时
|
||||||
|
maxConcurrency: 10, // 最大并发数
|
||||||
|
enableCache: true, // 启用工具缓存
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toolCache = new Map(); // 工具实例缓存
|
||||||
|
this.runningTasks = new Set(); // 正在执行的任务
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行工具(从代码内容)
|
||||||
|
* @param {string} toolContent - 工具JavaScript代码内容
|
||||||
|
* @param {Object} parameters - 工具参数
|
||||||
|
* @param {Object} context - 执行上下文
|
||||||
|
* @returns {Promise<Object>} 执行结果
|
||||||
|
*/
|
||||||
|
async execute(toolContent, parameters = {}, context = {}) {
|
||||||
|
const executionId = this.generateExecutionId();
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 并发控制
|
||||||
|
if (this.runningTasks.size >= this.options.maxConcurrency) {
|
||||||
|
throw new Error(`超出最大并发限制: ${this.options.maxConcurrency}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.runningTasks.add(executionId);
|
||||||
|
|
||||||
|
// 2. 执行工具代码并创建实例
|
||||||
|
const tool = this.executeToolContent(toolContent, context.toolName || 'unknown');
|
||||||
|
|
||||||
|
// 3. 参数验证
|
||||||
|
const validation = this.validateParameters(tool, parameters);
|
||||||
|
if (!validation.valid) {
|
||||||
|
return this.formatError(TOOL_ERROR_CODES.VALIDATION_ERROR, '参数验证失败', {
|
||||||
|
errors: validation.errors,
|
||||||
|
parameters: parameters
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 执行工具(带超时控制)
|
||||||
|
const result = await this.executeWithTimeout(tool, parameters);
|
||||||
|
const executionTime = Date.now() - startTime;
|
||||||
|
|
||||||
|
// 5. 格式化成功结果
|
||||||
|
return this.formatSuccess(result, {
|
||||||
|
executionId,
|
||||||
|
executionTime: `${executionTime}ms`,
|
||||||
|
tool: tool.getMetadata ? tool.getMetadata() : { name: context.toolName || 'unknown' }
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const executionTime = Date.now() - startTime;
|
||||||
|
return this.formatError(
|
||||||
|
this.getErrorCode(error),
|
||||||
|
error.message,
|
||||||
|
{
|
||||||
|
executionId,
|
||||||
|
executionTime: `${executionTime}ms`,
|
||||||
|
stack: error.stack
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.runningTasks.delete(executionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行工具内容并返回实例
|
||||||
|
* @param {string} toolContent - 工具代码内容
|
||||||
|
* @param {string} toolName - 工具名称
|
||||||
|
* @returns {Object} 工具实例
|
||||||
|
*/
|
||||||
|
executeToolContent(toolContent, toolName) {
|
||||||
|
try {
|
||||||
|
// 创建安全的执行环境
|
||||||
|
const sandbox = this.createSandbox();
|
||||||
|
|
||||||
|
// 执行工具代码
|
||||||
|
const vm = require('vm');
|
||||||
|
const script = new vm.Script(toolContent, { filename: `${toolName}.js` });
|
||||||
|
const context = vm.createContext(sandbox);
|
||||||
|
|
||||||
|
script.runInContext(context);
|
||||||
|
|
||||||
|
// 获取导出的工具类
|
||||||
|
const ToolClass = context.module.exports;
|
||||||
|
|
||||||
|
if (!ToolClass || typeof ToolClass !== 'function') {
|
||||||
|
throw new Error(`工具未正确导出类: ${toolName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建工具实例
|
||||||
|
return new ToolClass();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`工具代码执行失败 ${toolName}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建安全的执行沙箱
|
||||||
|
* @returns {Object} 沙箱环境
|
||||||
|
*/
|
||||||
|
createSandbox() {
|
||||||
|
return {
|
||||||
|
require: require,
|
||||||
|
module: { exports: {} },
|
||||||
|
exports: {},
|
||||||
|
console: console,
|
||||||
|
Buffer: Buffer,
|
||||||
|
process: {
|
||||||
|
env: process.env,
|
||||||
|
hrtime: process.hrtime
|
||||||
|
},
|
||||||
|
setTimeout: setTimeout,
|
||||||
|
clearTimeout: clearTimeout,
|
||||||
|
setInterval: setInterval,
|
||||||
|
clearInterval: clearInterval,
|
||||||
|
// 基础全局对象
|
||||||
|
Object: Object,
|
||||||
|
Array: Array,
|
||||||
|
String: String,
|
||||||
|
Number: Number,
|
||||||
|
Boolean: Boolean,
|
||||||
|
Date: Date,
|
||||||
|
JSON: JSON,
|
||||||
|
Math: Math,
|
||||||
|
RegExp: RegExp,
|
||||||
|
Error: Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带超时的工具执行
|
||||||
|
* @param {BaseTool} tool - 工具实例
|
||||||
|
* @param {Object} parameters - 参数
|
||||||
|
* @returns {Promise<*>} 执行结果
|
||||||
|
*/
|
||||||
|
async executeWithTimeout(tool, parameters) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
reject(new Error(`工具执行超时: ${this.options.timeout}ms`));
|
||||||
|
}, this.options.timeout);
|
||||||
|
|
||||||
|
tool.execute(parameters)
|
||||||
|
.then(result => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
resolve(result);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成执行ID
|
||||||
|
* @returns {string} 唯一执行ID
|
||||||
|
*/
|
||||||
|
generateExecutionId() {
|
||||||
|
return `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数验证
|
||||||
|
* @param {Object} tool - 工具实例
|
||||||
|
* @param {Object} parameters - 参数
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
validateParameters(tool, parameters) {
|
||||||
|
// 如果工具有自定义validate方法,使用它
|
||||||
|
if (typeof tool.validate === 'function') {
|
||||||
|
return tool.validate(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则使用默认验证
|
||||||
|
return ToolValidator.defaultValidate(tool, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为工具增强默认实现
|
||||||
|
* @param {Object} tool - 工具实例
|
||||||
|
*/
|
||||||
|
enhanceToolWithDefaults(tool) {
|
||||||
|
// 如果没有validate方法,提供默认实现
|
||||||
|
if (!tool.validate) {
|
||||||
|
tool.validate = (parameters) => ToolValidator.defaultValidate(tool, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有cleanup方法,提供空实现
|
||||||
|
if (!tool.cleanup) {
|
||||||
|
tool.cleanup = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有init方法,提供空实现
|
||||||
|
if (!tool.init) {
|
||||||
|
tool.init = () => {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取错误代码
|
||||||
|
* @param {Error} error - 错误对象
|
||||||
|
* @returns {string} 错误代码
|
||||||
|
*/
|
||||||
|
getErrorCode(error) {
|
||||||
|
if (error.message.includes('超时')) return TOOL_ERROR_CODES.TIMEOUT_ERROR;
|
||||||
|
if (error.message.includes('不存在')) return 'TOOL_NOT_FOUND';
|
||||||
|
if (error.message.includes('验证失败')) return TOOL_ERROR_CODES.VALIDATION_ERROR;
|
||||||
|
if (error.message.includes('并发限制')) return 'CONCURRENCY_ERROR';
|
||||||
|
if (error.message.includes('接口不符合规范')) return 'INTERFACE_ERROR';
|
||||||
|
return TOOL_ERROR_CODES.EXECUTION_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化成功结果
|
||||||
|
* @param {*} data - 结果数据
|
||||||
|
* @param {Object} metadata - 元信息
|
||||||
|
* @returns {Object} 标准化结果
|
||||||
|
*/
|
||||||
|
formatSuccess(data, metadata = {}) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data,
|
||||||
|
metadata: {
|
||||||
|
executor: 'ToolExecutor',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
...metadata
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化错误结果
|
||||||
|
* @param {string} code - 错误代码
|
||||||
|
* @param {string} message - 错误消息
|
||||||
|
* @param {Object} details - 错误详情
|
||||||
|
* @returns {Object} 标准化错误
|
||||||
|
*/
|
||||||
|
formatError(code, message, details = {}) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
details: details
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
executor: 'ToolExecutor',
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取执行统计信息
|
||||||
|
* @returns {Object} 统计信息
|
||||||
|
*/
|
||||||
|
getStats() {
|
||||||
|
return {
|
||||||
|
runningTasks: this.runningTasks.size,
|
||||||
|
cachedTools: this.toolCache.size,
|
||||||
|
maxConcurrency: this.options.maxConcurrency,
|
||||||
|
timeout: this.options.timeout
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理资源
|
||||||
|
*/
|
||||||
|
cleanup() {
|
||||||
|
this.toolCache.clear();
|
||||||
|
this.runningTasks.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ToolExecutor;
|
||||||
183
src/lib/tool/ToolInterface.js
Normal file
183
src/lib/tool/ToolInterface.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* ToolInterface - PromptX工具接口规范
|
||||||
|
* 定义鸭子类型的工具接口,外部工具无需继承任何类
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tool接口规范定义
|
||||||
|
*/
|
||||||
|
const TOOL_INTERFACE = {
|
||||||
|
// 必须实现的方法
|
||||||
|
required: [
|
||||||
|
{
|
||||||
|
name: 'getMetadata',
|
||||||
|
signature: '() => Object',
|
||||||
|
description: '获取工具元信息',
|
||||||
|
returns: {
|
||||||
|
name: 'string - 工具名称',
|
||||||
|
description: 'string - 工具描述',
|
||||||
|
version: 'string - 版本号',
|
||||||
|
category: 'string - 分类(可选)',
|
||||||
|
author: 'string - 作者(可选)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'getSchema',
|
||||||
|
signature: '() => Object',
|
||||||
|
description: '获取参数JSON Schema',
|
||||||
|
returns: {
|
||||||
|
type: 'string - 参数类型,通常为object',
|
||||||
|
properties: 'Object - 参数属性定义',
|
||||||
|
required: 'Array - 必需参数列表(可选)',
|
||||||
|
additionalProperties: 'boolean - 是否允许额外参数(可选)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'execute',
|
||||||
|
signature: '(parameters: Object) => Promise<any>',
|
||||||
|
description: '执行工具主逻辑',
|
||||||
|
parameters: {
|
||||||
|
parameters: 'Object - 工具参数,符合getSchema定义'
|
||||||
|
},
|
||||||
|
returns: 'Promise<any> - 工具执行结果'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// 可选实现的方法
|
||||||
|
optional: [
|
||||||
|
{
|
||||||
|
name: 'validate',
|
||||||
|
signature: '(parameters: Object) => Object',
|
||||||
|
description: '验证参数(可选,有默认实现)',
|
||||||
|
parameters: {
|
||||||
|
parameters: 'Object - 待验证参数'
|
||||||
|
},
|
||||||
|
returns: {
|
||||||
|
valid: 'boolean - 验证是否通过',
|
||||||
|
errors: 'Array<string> - 错误信息列表'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cleanup',
|
||||||
|
signature: '() => void | Promise<void>',
|
||||||
|
description: '清理资源(可选)',
|
||||||
|
returns: 'void | Promise<void>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'init',
|
||||||
|
signature: '(config?: Object) => void | Promise<void>',
|
||||||
|
description: '初始化工具(可选)',
|
||||||
|
parameters: {
|
||||||
|
config: 'Object - 初始化配置(可选)'
|
||||||
|
},
|
||||||
|
returns: 'void | Promise<void>'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具错误类型定义
|
||||||
|
*/
|
||||||
|
const TOOL_ERROR_CODES = {
|
||||||
|
VALIDATION_ERROR: 'VALIDATION_ERROR', // 参数验证失败
|
||||||
|
EXECUTION_ERROR: 'EXECUTION_ERROR', // 执行错误
|
||||||
|
TIMEOUT_ERROR: 'TIMEOUT_ERROR', // 超时错误
|
||||||
|
PERMISSION_ERROR: 'PERMISSION_ERROR', // 权限错误
|
||||||
|
RESOURCE_ERROR: 'RESOURCE_ERROR', // 资源错误
|
||||||
|
CONFIGURATION_ERROR: 'CONFIGURATION_ERROR' // 配置错误
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准结果格式定义
|
||||||
|
*/
|
||||||
|
const TOOL_RESULT_FORMAT = {
|
||||||
|
success: {
|
||||||
|
success: true,
|
||||||
|
data: 'any - 工具返回的实际数据',
|
||||||
|
metadata: {
|
||||||
|
tool: 'string - 工具名称',
|
||||||
|
executionTime: 'string - 执行时间',
|
||||||
|
timestamp: 'string - 时间戳',
|
||||||
|
// ...其他元信息
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
error: {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'string - 错误代码(见TOOL_ERROR_CODES)',
|
||||||
|
message: 'string - 错误消息',
|
||||||
|
details: 'Object - 错误详情(可选)'
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
tool: 'string - 工具名称',
|
||||||
|
timestamp: 'string - 时间戳',
|
||||||
|
// ...其他元信息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例工具实现
|
||||||
|
*/
|
||||||
|
const EXAMPLE_TOOL = `
|
||||||
|
class ExampleTool {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
name: 'example-tool',
|
||||||
|
description: '示例工具',
|
||||||
|
version: '1.0.0',
|
||||||
|
category: 'example',
|
||||||
|
author: 'PromptX Team'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchema() {
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
input: {
|
||||||
|
type: 'string',
|
||||||
|
description: '输入参数'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['input'],
|
||||||
|
additionalProperties: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(parameters) {
|
||||||
|
const { input } = parameters;
|
||||||
|
|
||||||
|
// 工具逻辑
|
||||||
|
const result = \`处理结果: \${input}\`;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:自定义参数验证
|
||||||
|
validate(parameters) {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (!parameters.input || parameters.input.trim() === '') {
|
||||||
|
errors.push('input不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:清理资源
|
||||||
|
cleanup() {
|
||||||
|
console.log('清理资源');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ExampleTool;
|
||||||
|
`;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
TOOL_INTERFACE,
|
||||||
|
TOOL_ERROR_CODES,
|
||||||
|
TOOL_RESULT_FORMAT,
|
||||||
|
EXAMPLE_TOOL
|
||||||
|
};
|
||||||
325
src/lib/tool/ToolUtils.js
Normal file
325
src/lib/tool/ToolUtils.js
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
const ToolValidator = require('./ToolValidator');
|
||||||
|
const { TOOL_ERROR_CODES, TOOL_RESULT_FORMAT } = require('./ToolInterface');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToolUtils - 工具实用函数集合
|
||||||
|
* 提供工具开发和使用的辅助函数
|
||||||
|
*/
|
||||||
|
class ToolUtils {
|
||||||
|
/**
|
||||||
|
* 创建标准化的成功结果
|
||||||
|
* @param {*} data - 结果数据
|
||||||
|
* @param {Object} options - 选项
|
||||||
|
* @returns {Object} 标准化结果
|
||||||
|
*/
|
||||||
|
static createSuccessResult(data, options = {}) {
|
||||||
|
const {
|
||||||
|
tool = 'unknown',
|
||||||
|
executionTime = null,
|
||||||
|
metadata = {}
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data,
|
||||||
|
metadata: {
|
||||||
|
tool: tool,
|
||||||
|
executionTime: executionTime,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
...metadata
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建标准化的错误结果
|
||||||
|
* @param {string} code - 错误代码
|
||||||
|
* @param {string} message - 错误消息
|
||||||
|
* @param {Object} options - 选项
|
||||||
|
* @returns {Object} 标准化错误
|
||||||
|
*/
|
||||||
|
static createErrorResult(code, message, options = {}) {
|
||||||
|
const {
|
||||||
|
tool = 'unknown',
|
||||||
|
details = {},
|
||||||
|
metadata = {}
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: code,
|
||||||
|
message: message,
|
||||||
|
details: details
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
tool: tool,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
...metadata
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证工具结果格式
|
||||||
|
* @param {Object} result - 工具结果
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static validateResult(result) {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (!result || typeof result !== 'object') {
|
||||||
|
errors.push('结果必须是对象类型');
|
||||||
|
return { valid: false, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof result.success !== 'boolean') {
|
||||||
|
errors.push('结果必须包含success(boolean)字段');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// 成功结果验证
|
||||||
|
if (!('data' in result)) {
|
||||||
|
errors.push('成功结果必须包含data字段');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 错误结果验证
|
||||||
|
if (!result.error || typeof result.error !== 'object') {
|
||||||
|
errors.push('错误结果必须包含error(object)字段');
|
||||||
|
} else {
|
||||||
|
if (!result.error.code || typeof result.error.code !== 'string') {
|
||||||
|
errors.push('错误结果必须包含error.code(string)字段');
|
||||||
|
}
|
||||||
|
if (!result.error.message || typeof result.error.message !== 'string') {
|
||||||
|
errors.push('错误结果必须包含error.message(string)字段');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全地执行工具方法
|
||||||
|
* @param {Object} tool - 工具实例
|
||||||
|
* @param {string} methodName - 方法名
|
||||||
|
* @param {...any} args - 方法参数
|
||||||
|
* @returns {Promise<*>} 执行结果
|
||||||
|
*/
|
||||||
|
static async safeExecute(tool, methodName, ...args) {
|
||||||
|
try {
|
||||||
|
if (!tool || typeof tool[methodName] !== 'function') {
|
||||||
|
throw new Error(`工具不存在方法: ${methodName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await tool[methodName](...args);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`方法执行失败 ${methodName}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具性能分析
|
||||||
|
* @param {Object} tool - 工具实例
|
||||||
|
* @param {Object} parameters - 测试参数
|
||||||
|
* @param {Object} options - 选项
|
||||||
|
* @returns {Promise<Object>} 性能分析结果
|
||||||
|
*/
|
||||||
|
static async benchmarkTool(tool, parameters = {}, options = {}) {
|
||||||
|
const {
|
||||||
|
iterations = 10,
|
||||||
|
warmup = 3
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
toolName: 'unknown',
|
||||||
|
iterations: iterations,
|
||||||
|
warmup: warmup,
|
||||||
|
times: [],
|
||||||
|
stats: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取工具名称
|
||||||
|
if (tool.getMetadata) {
|
||||||
|
const metadata = tool.getMetadata();
|
||||||
|
results.toolName = metadata.name || 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证工具接口
|
||||||
|
const validation = ToolValidator.validateTool(tool);
|
||||||
|
if (!validation.valid) {
|
||||||
|
throw new Error(`工具接口验证失败: ${validation.errors.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预热运行
|
||||||
|
for (let i = 0; i < warmup; i++) {
|
||||||
|
await tool.execute(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 性能测试
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
const startTime = process.hrtime.bigint();
|
||||||
|
await tool.execute(parameters);
|
||||||
|
const endTime = process.hrtime.bigint();
|
||||||
|
|
||||||
|
const executionTime = Number(endTime - startTime) / 1000000; // 转换为毫秒
|
||||||
|
results.times.push(executionTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算统计信息
|
||||||
|
results.stats = this.calculateStats(results.times);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
results.error = error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算统计信息
|
||||||
|
* @param {Array<number>} times - 时间数组
|
||||||
|
* @returns {Object} 统计信息
|
||||||
|
*/
|
||||||
|
static calculateStats(times) {
|
||||||
|
if (times.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = [...times].sort((a, b) => a - b);
|
||||||
|
const sum = times.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
count: times.length,
|
||||||
|
min: Math.min(...times),
|
||||||
|
max: Math.max(...times),
|
||||||
|
mean: sum / times.length,
|
||||||
|
median: sorted[Math.floor(sorted.length / 2)],
|
||||||
|
p95: sorted[Math.floor(sorted.length * 0.95)],
|
||||||
|
p99: sorted[Math.floor(sorted.length * 0.99)]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成工具模板代码
|
||||||
|
* @param {Object} options - 工具选项
|
||||||
|
* @returns {string} 工具模板代码
|
||||||
|
*/
|
||||||
|
static generateToolTemplate(options = {}) {
|
||||||
|
const {
|
||||||
|
toolName = 'ExampleTool',
|
||||||
|
className = 'ExampleTool',
|
||||||
|
description = '示例工具',
|
||||||
|
category = 'utility',
|
||||||
|
author = 'PromptX Developer'
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
return `/**
|
||||||
|
* ${className} - ${description}
|
||||||
|
* 使用PromptX鸭子类型接口,无需继承任何基类
|
||||||
|
*/
|
||||||
|
class ${className} {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
name: '${toolName}',
|
||||||
|
description: '${description}',
|
||||||
|
version: '1.0.0',
|
||||||
|
category: '${category}',
|
||||||
|
author: '${author}'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchema() {
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
input: {
|
||||||
|
type: 'string',
|
||||||
|
description: '输入参数'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['input'],
|
||||||
|
additionalProperties: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(parameters) {
|
||||||
|
const { input } = parameters;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: 实现工具逻辑
|
||||||
|
const result = \`处理结果: \${input}\`;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(\`执行失败: \${error.message}\`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:自定义参数验证
|
||||||
|
validate(parameters) {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (!parameters.input || parameters.input.trim() === '') {
|
||||||
|
errors.push('input不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:清理资源
|
||||||
|
cleanup() {
|
||||||
|
// 清理逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ${className};
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建工具开发指南
|
||||||
|
* @returns {string} 开发指南
|
||||||
|
*/
|
||||||
|
static getDevGuide() {
|
||||||
|
return `
|
||||||
|
# PromptX Tool 开发指南
|
||||||
|
|
||||||
|
## 鸭子类型接口
|
||||||
|
PromptX工具使用鸭子类型设计,无需继承任何基类。只需实现以下接口:
|
||||||
|
|
||||||
|
### 必需方法
|
||||||
|
1. \`getMetadata()\` - 返回工具元信息
|
||||||
|
2. \`getSchema()\` - 返回参数JSON Schema
|
||||||
|
3. \`execute(parameters)\` - 执行工具逻辑
|
||||||
|
|
||||||
|
### 可选方法
|
||||||
|
1. \`validate(parameters)\` - 自定义参数验证
|
||||||
|
2. \`cleanup()\` - 清理资源
|
||||||
|
3. \`init(config)\` - 初始化工具
|
||||||
|
|
||||||
|
## 开发步骤
|
||||||
|
1. 使用 ToolUtils.generateToolTemplate() 生成模板
|
||||||
|
2. 实现必需的接口方法
|
||||||
|
3. 使用 ToolValidator.validateTool() 验证接口
|
||||||
|
4. 使用 ToolUtils.benchmarkTool() 性能测试
|
||||||
|
5. 注册到工具注册表
|
||||||
|
|
||||||
|
## 示例代码
|
||||||
|
\`\`\`javascript
|
||||||
|
${this.generateToolTemplate()}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
- 保持execute方法的幂等性
|
||||||
|
- 提供清晰的错误消息
|
||||||
|
- 使用合适的JSON Schema验证
|
||||||
|
- 实现适当的资源清理
|
||||||
|
- 遵循统一的结果格式
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ToolUtils;
|
||||||
374
src/lib/tool/ToolValidator.js
Normal file
374
src/lib/tool/ToolValidator.js
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
const { TOOL_INTERFACE, TOOL_ERROR_CODES } = require('./ToolInterface');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToolValidator - 工具接口验证器
|
||||||
|
* 使用鸭子类型验证工具是否符合PromptX接口规范
|
||||||
|
*/
|
||||||
|
class ToolValidator {
|
||||||
|
/**
|
||||||
|
* 验证工具是否符合接口规范
|
||||||
|
* @param {any} tool - 待验证的工具对象
|
||||||
|
* @returns {Object} 验证结果 {valid: boolean, errors: [], warnings: []}
|
||||||
|
*/
|
||||||
|
static validateTool(tool) {
|
||||||
|
const errors = [];
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
// 基础类型检查
|
||||||
|
if (!tool || typeof tool !== 'object') {
|
||||||
|
errors.push('工具必须是对象类型');
|
||||||
|
return { valid: false, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证必需方法
|
||||||
|
for (const methodSpec of TOOL_INTERFACE.required) {
|
||||||
|
const methodName = methodSpec.name;
|
||||||
|
|
||||||
|
if (!(methodName in tool)) {
|
||||||
|
errors.push(`缺少必需方法: ${methodName}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof tool[methodName] !== 'function') {
|
||||||
|
errors.push(`${methodName} 必须是函数类型`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法签名验证
|
||||||
|
try {
|
||||||
|
const validationResult = this.validateMethod(tool, methodSpec);
|
||||||
|
if (!validationResult.valid) {
|
||||||
|
errors.push(...validationResult.errors);
|
||||||
|
warnings.push(...validationResult.warnings);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
warnings.push(`${methodName} 方法验证时出错: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证可选方法
|
||||||
|
for (const methodSpec of TOOL_INTERFACE.optional) {
|
||||||
|
const methodName = methodSpec.name;
|
||||||
|
|
||||||
|
if (methodName in tool) {
|
||||||
|
if (typeof tool[methodName] !== 'function') {
|
||||||
|
warnings.push(`${methodName} 应该是函数类型`);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const validationResult = this.validateMethod(tool, methodSpec);
|
||||||
|
if (!validationResult.valid) {
|
||||||
|
warnings.push(...validationResult.errors);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
warnings.push(`${methodName} 方法验证时出错: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: errors.length === 0,
|
||||||
|
errors,
|
||||||
|
warnings
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证特定方法
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @param {Object} methodSpec - 方法规范
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static validateMethod(tool, methodSpec) {
|
||||||
|
const errors = [];
|
||||||
|
const warnings = [];
|
||||||
|
const methodName = methodSpec.name;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (methodName) {
|
||||||
|
case 'getMetadata':
|
||||||
|
return this.validateGetMetadata(tool);
|
||||||
|
case 'getSchema':
|
||||||
|
return this.validateGetSchema(tool);
|
||||||
|
case 'execute':
|
||||||
|
return this.validateExecute(tool);
|
||||||
|
case 'validate':
|
||||||
|
return this.validateValidateMethod(tool);
|
||||||
|
default:
|
||||||
|
return { valid: true, errors: [], warnings: [] };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(`${methodName} 方法调用失败: ${error.message}`);
|
||||||
|
return { valid: false, errors, warnings };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证getMetadata方法
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static validateGetMetadata(tool) {
|
||||||
|
const errors = [];
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const metadata = tool.getMetadata();
|
||||||
|
|
||||||
|
if (!metadata || typeof metadata !== 'object') {
|
||||||
|
errors.push('getMetadata() 必须返回对象');
|
||||||
|
return { valid: false, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证必需字段
|
||||||
|
if (!metadata.name || typeof metadata.name !== 'string') {
|
||||||
|
errors.push('metadata.name 必须是非空字符串');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.description || typeof metadata.description !== 'string') {
|
||||||
|
errors.push('metadata.description 必须是非空字符串');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.version || typeof metadata.version !== 'string') {
|
||||||
|
errors.push('metadata.version 必须是非空字符串');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证可选字段
|
||||||
|
if (metadata.category && typeof metadata.category !== 'string') {
|
||||||
|
warnings.push('metadata.category 应该是字符串类型');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.author && typeof metadata.author !== 'string') {
|
||||||
|
warnings.push('metadata.author 应该是字符串类型');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(`getMetadata() 执行失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证getSchema方法
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static validateGetSchema(tool) {
|
||||||
|
const errors = [];
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const schema = tool.getSchema();
|
||||||
|
|
||||||
|
if (!schema || typeof schema !== 'object') {
|
||||||
|
errors.push('getSchema() 必须返回对象');
|
||||||
|
return { valid: false, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础JSON Schema验证
|
||||||
|
if (!schema.type) {
|
||||||
|
warnings.push('schema.type 建议定义');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.type && typeof schema.type !== 'string') {
|
||||||
|
errors.push('schema.type 必须是字符串');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.properties && typeof schema.properties !== 'object') {
|
||||||
|
errors.push('schema.properties 必须是对象');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schema.required && !Array.isArray(schema.required)) {
|
||||||
|
errors.push('schema.required 必须是数组');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(`getSchema() 执行失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证execute方法
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static validateExecute(tool) {
|
||||||
|
const errors = [];
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
// 检查方法签名
|
||||||
|
const executeMethod = tool.execute;
|
||||||
|
if (executeMethod.length === 0) {
|
||||||
|
warnings.push('execute() 方法建议接受parameters参数');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:这里不实际调用execute方法,因为可能有副作用
|
||||||
|
// 只进行静态检查
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证validate方法(可选)
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static validateValidateMethod(tool) {
|
||||||
|
const errors = [];
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 测试validate方法的返回格式
|
||||||
|
const testParams = {};
|
||||||
|
const result = tool.validate(testParams);
|
||||||
|
|
||||||
|
if (!result || typeof result !== 'object') {
|
||||||
|
errors.push('validate() 必须返回对象');
|
||||||
|
return { valid: false, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof result.valid !== 'boolean') {
|
||||||
|
errors.push('validate() 返回值必须包含valid(boolean)字段');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.errors && !Array.isArray(result.errors)) {
|
||||||
|
errors.push('validate() 返回值的errors字段必须是数组');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
warnings.push(`validate() 方法测试失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors, warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为工具提供默认的validate方法实现
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @param {Object} parameters - 待验证参数
|
||||||
|
* @returns {Object} 验证结果
|
||||||
|
*/
|
||||||
|
static defaultValidate(tool, parameters) {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取schema
|
||||||
|
const schema = tool.getSchema();
|
||||||
|
|
||||||
|
// 基础类型检查
|
||||||
|
if (!parameters || typeof parameters !== 'object') {
|
||||||
|
errors.push('参数必须是对象类型');
|
||||||
|
return { valid: false, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 必需参数检查
|
||||||
|
if (schema.required && Array.isArray(schema.required)) {
|
||||||
|
for (const field of schema.required) {
|
||||||
|
if (!(field in parameters)) {
|
||||||
|
errors.push(`缺少必需参数: ${field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础字段类型检查
|
||||||
|
if (schema.properties && typeof schema.properties === 'object') {
|
||||||
|
for (const [field, fieldSchema] of Object.entries(schema.properties)) {
|
||||||
|
if (field in parameters) {
|
||||||
|
const value = parameters[field];
|
||||||
|
const expectedType = fieldSchema.type;
|
||||||
|
|
||||||
|
if (expectedType && !this.validateType(value, expectedType)) {
|
||||||
|
errors.push(`参数 ${field} 类型错误,期望 ${expectedType},实际 ${typeof value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(`参数验证失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型验证辅助方法
|
||||||
|
* @param {*} value - 待验证值
|
||||||
|
* @param {string} expectedType - 期望类型
|
||||||
|
* @returns {boolean} 是否匹配
|
||||||
|
*/
|
||||||
|
static validateType(value, expectedType) {
|
||||||
|
switch (expectedType) {
|
||||||
|
case 'string':
|
||||||
|
return typeof value === 'string';
|
||||||
|
case 'number':
|
||||||
|
return typeof value === 'number';
|
||||||
|
case 'boolean':
|
||||||
|
return typeof value === 'boolean';
|
||||||
|
case 'object':
|
||||||
|
return typeof value === 'object' && value !== null;
|
||||||
|
case 'array':
|
||||||
|
return Array.isArray(value);
|
||||||
|
default:
|
||||||
|
return true; // 未知类型,跳过验证
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成工具接口报告
|
||||||
|
* @param {Object} tool - 工具对象
|
||||||
|
* @returns {Object} 接口报告
|
||||||
|
*/
|
||||||
|
static generateInterfaceReport(tool) {
|
||||||
|
const validation = this.validateTool(tool);
|
||||||
|
const report = {
|
||||||
|
toolName: 'unknown',
|
||||||
|
valid: validation.valid,
|
||||||
|
errors: validation.errors,
|
||||||
|
warnings: validation.warnings,
|
||||||
|
implementedMethods: {
|
||||||
|
required: [],
|
||||||
|
optional: []
|
||||||
|
},
|
||||||
|
metadata: null,
|
||||||
|
schema: null
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取工具名称
|
||||||
|
if (tool.getMetadata) {
|
||||||
|
const metadata = tool.getMetadata();
|
||||||
|
report.toolName = metadata.name || 'unknown';
|
||||||
|
report.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取schema
|
||||||
|
if (tool.getSchema) {
|
||||||
|
report.schema = tool.getSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查已实现的方法
|
||||||
|
for (const methodSpec of TOOL_INTERFACE.required) {
|
||||||
|
if (typeof tool[methodSpec.name] === 'function') {
|
||||||
|
report.implementedMethods.required.push(methodSpec.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const methodSpec of TOOL_INTERFACE.optional) {
|
||||||
|
if (typeof tool[methodSpec.name] === 'function') {
|
||||||
|
report.implementedMethods.optional.push(methodSpec.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
report.warnings.push(`生成报告时出错: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ToolValidator;
|
||||||
110
src/lib/tool/index.js
Normal file
110
src/lib/tool/index.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* PromptX Tool Framework
|
||||||
|
* 统一的工具框架入口文件
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ToolExecutor = require('./ToolExecutor');
|
||||||
|
const ToolValidator = require('./ToolValidator');
|
||||||
|
const ToolUtils = require('./ToolUtils');
|
||||||
|
const { TOOL_INTERFACE, TOOL_ERROR_CODES, TOOL_RESULT_FORMAT, EXAMPLE_TOOL } = require('./ToolInterface');
|
||||||
|
|
||||||
|
// 创建全局工具实例
|
||||||
|
let globalExecutor = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局工具执行器
|
||||||
|
* @param {Object} options - 配置选项
|
||||||
|
* @returns {ToolExecutor} 工具执行器实例
|
||||||
|
*/
|
||||||
|
function getGlobalToolExecutor(options = {}) {
|
||||||
|
if (!globalExecutor) {
|
||||||
|
globalExecutor = new ToolExecutor(options);
|
||||||
|
}
|
||||||
|
return globalExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化工具框架
|
||||||
|
* @param {Object} options - 配置选项
|
||||||
|
* @returns {Object} 初始化结果
|
||||||
|
*/
|
||||||
|
function initialize(options = {}) {
|
||||||
|
try {
|
||||||
|
const executor = getGlobalToolExecutor(options.executor);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: '工具框架初始化成功',
|
||||||
|
executor: {
|
||||||
|
maxConcurrency: executor.options.maxConcurrency,
|
||||||
|
timeout: executor.options.timeout
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `工具框架初始化失败: ${error.message}`,
|
||||||
|
error: error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行工具的便捷方法
|
||||||
|
* @param {string} toolContent - 工具JavaScript代码内容
|
||||||
|
* @param {Object} parameters - 工具参数
|
||||||
|
* @param {Object} context - 执行上下文
|
||||||
|
* @returns {Promise<Object>} 执行结果
|
||||||
|
*/
|
||||||
|
async function executeTool(toolContent, parameters = {}, context = {}) {
|
||||||
|
const executor = getGlobalToolExecutor();
|
||||||
|
return await executor.execute(toolContent, parameters, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置工具框架
|
||||||
|
*/
|
||||||
|
function reset() {
|
||||||
|
if (globalExecutor) {
|
||||||
|
globalExecutor.cleanup();
|
||||||
|
globalExecutor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取工具框架统计信息
|
||||||
|
* @returns {Object} 统计信息
|
||||||
|
*/
|
||||||
|
function getStats() {
|
||||||
|
const executorStats = globalExecutor ? globalExecutor.getStats() : {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
executor: executorStats,
|
||||||
|
framework: {
|
||||||
|
initialized: !!globalExecutor,
|
||||||
|
version: '1.0.0'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// 核心类
|
||||||
|
ToolExecutor,
|
||||||
|
ToolValidator,
|
||||||
|
ToolUtils,
|
||||||
|
|
||||||
|
// 接口规范
|
||||||
|
TOOL_INTERFACE,
|
||||||
|
TOOL_ERROR_CODES,
|
||||||
|
TOOL_RESULT_FORMAT,
|
||||||
|
EXAMPLE_TOOL,
|
||||||
|
|
||||||
|
// 全局实例获取器
|
||||||
|
getGlobalToolExecutor,
|
||||||
|
|
||||||
|
// 便捷方法
|
||||||
|
initialize,
|
||||||
|
executeTool,
|
||||||
|
reset,
|
||||||
|
getStats
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user