WIP: promptx tools 开发中的代码

- 正在开发 promptx tools 功能
- 包含 bin、commands、DACP 相关修改
- 新增 tool 目录结构

🚧 这是开发中的代码,需要继续完善
This commit is contained in:
sean
2025-06-28 09:25:54 +08:00
parent d6455987ab
commit 70093018f8
8 changed files with 1438 additions and 55 deletions

View File

@ -105,7 +105,7 @@ program
.option('--host <address>', '绑定地址 (仅http/sse传输)', 'localhost')
.option('--cors', '启用CORS (仅http/sse传输)', false)
.option('--debug', '启用调试模式', false)
.option('--with-dacp', '同时启动DACP服务', false)
.option('--with-dacp', '(已废弃,静默忽略)', false)
.action(async (options) => {
try {
// 设置调试模式
@ -116,7 +116,8 @@ program
// 根据传输类型选择命令
if (options.transport === 'stdio') {
const mcpServer = new MCPServerCommand();
await mcpServer.execute({ withDacp: options.withDacp });
// 🔧 DACP现为Mock模式静默忽略用户的withDacp配置
await mcpServer.execute();
} else if (options.transport === 'http' || options.transport === 'sse') {
const mcpHttpServer = new MCPStreamableHttpCommand();
const serverOptions = {

View File

@ -84,10 +84,8 @@ class MCPServerCommand {
// 设置进程清理处理器
this.setupProcessCleanup();
// 如果需要启动DACP服务
if (options.withDacp) {
await this.startDACPService();
}
// 🔧 DACP现已改为Mock模式无需启动独立服务
// 静默忽略任何withDacp选项保持向后兼容
this.log('🚀 启动MCP Server...');
const transport = new StdioServerTransport();
@ -145,44 +143,27 @@ class MCPServerCommand {
}
/**
* 清理子进程
* 清理子进程 (DACP现为Mock模式此方法保留但无实际清理工作)
* @deprecated DACP已改为Mock模式无需清理子进程
*/
cleanup() {
if (this.dacpProcess && !this.dacpProcess.killed && this.dacpProcess.pid) {
this.log('🛑 正在终止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现已改为Mock模式无需清理DACP子进程
// HTTP模式的进程清理代码已保留作为参考实现
this.log('🔧 Mock模式下无需清理DACP子进程');
}
/**
* 检测DACP服务是否已经运行
* 检测DACP服务是否已经运行 (HTTP模式 - 仅作参考实现保留)
* @deprecated DACP已改为Mock模式此方法仅保留作为参考
* @param {string} host - 主机地址
* @param {number} port - 端口号
* @returns {Promise<boolean>} 服务是否运行
*/
async isDACPServiceRunning(host = 'localhost', port = 3002) {
// 🔧 Mock模式下始终返回false因为不需要HTTP服务
return false;
/* HTTP模式参考实现已禁用
const http = require('http');
return new Promise((resolve) => {
@ -224,15 +205,28 @@ class MCPServerCommand {
req.end();
});
*/
}
/**
* 获取DACP服务信息
* 获取DACP服务信息 (HTTP模式 - 仅作参考实现保留)
* @deprecated DACP已改为Mock模式此方法仅保留作为参考
* @param {string} host - 主机地址
* @param {number} port - 端口号
* @returns {Promise<Object|null>} 服务信息
*/
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');
return new Promise((resolve) => {
@ -271,12 +265,25 @@ class MCPServerCommand {
req.end();
});
*/
}
/**
* 启动DACP服务
* 启动DACP服务 (HTTP模式 - 仅作参考实现保留)
* @deprecated DACP已改为Mock模式此方法仅保留作为参考
*/
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 path = require('path');
@ -395,6 +402,7 @@ class MCPServerCommand {
this.log(`❌ DACP服务启动失败: ${error.message}`);
throw error;
}
*/
}
/**

View File

@ -1,9 +1,14 @@
const BasePouchCommand = require('../BasePouchCommand');
const http = require('http');
const fs = require('fs');
const path = require('path');
/**
* DACP服务调用命令
* 负责调用DACP服务实现从AI建议到AI行动的转换
*
* 🔧 当前实现Mock模式本地函数调用
* 🌐 HTTP模式代码保留作为参考实现
*/
class DACPCommand extends BasePouchCommand {
constructor() {
@ -12,6 +17,10 @@ class DACPCommand extends BasePouchCommand {
// 统一的DACP服务端点
// 所有service_id都路由到同一个服务
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
* @returns {string} 服务端点URL
*/
@ -59,20 +69,8 @@ class DACPCommand extends BasePouchCommand {
const { service_id, action, parameters } = args;
// 获取服务端点(现在是统一的)
const endpoint = this.getServiceEndpoint(service_id);
// 构造DACP请求
const dacpRequest = {
service_id,
action,
parameters,
request_id: `req_${Date.now()}`
};
// 调用DACP服务
const result = await this.makeHttpRequest(endpoint, dacpRequest);
return result;
// 🔧 直接使用本地Mock调用
return await this.callLocalService(args);
} 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 {Object} data - 请求数据
* @returns {Promise<Object>} 响应数据
@ -149,8 +239,7 @@ class DACPCommand extends BasePouchCommand {
if (result.success) {
const executionResult = result.data.execution_result;
const metrics = result.data.performance_metrics;
return `🚀 DACP服务调用成功
return `🚀 DACP服务调用成功 (🔧 本地Mock模式)
📋 执行结果:
${JSON.stringify(executionResult, null, 2)}
@ -172,9 +261,10 @@ ${JSON.stringify(executionResult, null, 2)}
return `❌ DACP服务调用异常
错误详情: ${error.message}
运行模式: 🔧 本地Mock模式
💡 请检查:
1. DACP服务是否运行 (http://localhost:3002/health)
1. DACP action模块是否存在
2. 服务ID是否正确
3. 操作名称是否有效
4. 参数格式是否正确`;

View 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;

View 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
View 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;

View 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
View 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
};