UNPKG

@robota-sdk/tools

Version:

Tools and utilities package for Robota SDK - providing AI function calling and tool integration capabilities

1,814 lines (1,798 loc) 73.7 kB
'use strict'; var zod = require('zod'); // src/factories/function-factory.ts function zodToJsonSchema(schema) { const jsonSchema = { type: "object", properties: {}, required: [] }; const shape = schema._def.shape(); const entries = Object.entries(shape); for (const [key, value] of entries) { const fieldSchema = convertZodTypeToJsonSchema(value, key); jsonSchema.properties[key] = fieldSchema; if (!isOptionalType(value) && !isNullableType(value)) { if (!jsonSchema.required) { jsonSchema.required = []; } jsonSchema.required.push(key); } } return jsonSchema; } function convertZodTypeToJsonSchema(zodType, fieldName) { const jsonSchema = {}; const description = getZodDescription(zodType); if (description) { jsonSchema.description = description; } if (zodType instanceof zod.z.ZodString) { return convertZodString(zodType, jsonSchema); } else if (zodType instanceof zod.z.ZodNumber) { return convertZodNumber(zodType, jsonSchema); } else if (zodType instanceof zod.z.ZodBoolean) { jsonSchema.type = "boolean"; } else if (zodType instanceof zod.z.ZodArray) { return convertZodArray(zodType, jsonSchema, fieldName); } else if (zodType instanceof zod.z.ZodEnum) { return convertZodEnum(zodType, jsonSchema); } else if (zodType instanceof zod.z.ZodObject) { return convertZodObject(zodType, jsonSchema); } else if (zodType instanceof zod.z.ZodUnion) { return convertZodUnion(zodType, jsonSchema, fieldName); } else if (zodType instanceof zod.z.ZodOptional) { return convertZodTypeToJsonSchema(zodType._def.innerType, fieldName); } else if (zodType instanceof zod.z.ZodNullable) { return convertZodNullable(zodType, jsonSchema, fieldName); } else if (zodType instanceof zod.z.ZodDefault) { return convertZodDefault(zodType, jsonSchema, fieldName); } else { jsonSchema.type = "string"; console.warn(`Unsupported zod type for field ${fieldName}, using string as fallback`); } return jsonSchema; } function convertZodString(zodType, jsonSchema) { jsonSchema.type = "string"; if (zodType._def.checks) { for (const check of zodType._def.checks) { switch (check.kind) { case "min": jsonSchema.minLength = check.value; break; case "max": jsonSchema.maxLength = check.value; break; case "regex": jsonSchema.pattern = check.regex.source; break; case "email": jsonSchema.format = "email"; break; case "url": jsonSchema.format = "uri"; break; } } } return jsonSchema; } function convertZodNumber(zodType, jsonSchema) { jsonSchema.type = "number"; if (zodType._def.checks) { for (const check of zodType._def.checks) { switch (check.kind) { case "min": jsonSchema.minimum = check.value; break; case "max": jsonSchema.maximum = check.value; break; case "int": jsonSchema.type = "integer"; break; } } } return jsonSchema; } function convertZodArray(zodType, jsonSchema, fieldName) { jsonSchema.type = "array"; jsonSchema.items = convertZodTypeToJsonSchema(zodType._def.type, `${fieldName}[]`); if (zodType._def.minLength !== null) { jsonSchema.minItems = zodType._def.minLength.value; } if (zodType._def.maxLength !== null) { jsonSchema.maxItems = zodType._def.maxLength.value; } return jsonSchema; } function convertZodEnum(zodType, jsonSchema) { jsonSchema.type = "string"; jsonSchema.enum = zodType._def.values; return jsonSchema; } function convertZodObject(zodType, jsonSchema) { jsonSchema.type = "object"; const nestedSchema = zodToJsonSchema(zodType); jsonSchema.properties = nestedSchema.properties; jsonSchema.required = nestedSchema.required; return jsonSchema; } function convertZodUnion(zodType, jsonSchema, fieldName) { jsonSchema.oneOf = zodType._def.options.map( (option) => convertZodTypeToJsonSchema(option, fieldName) ); return jsonSchema; } function convertZodNullable(zodType, jsonSchema, fieldName) { const innerSchema = convertZodTypeToJsonSchema(zodType._def.innerType, fieldName); jsonSchema.type = [innerSchema.type, "null"]; Object.assign(jsonSchema, innerSchema); return jsonSchema; } function convertZodDefault(zodType, jsonSchema, fieldName) { const innerSchema = convertZodTypeToJsonSchema(zodType._def.innerType, fieldName); Object.assign(jsonSchema, innerSchema); jsonSchema.default = zodType._def.defaultValue(); return jsonSchema; } function getZodDescription(zodType) { const description = zodType._def.description; if (description) return description; if (zodType instanceof zod.z.ZodOptional || zodType instanceof zod.z.ZodNullable) { return getZodDescription(zodType._def.innerType); } return void 0; } function isOptionalType(zodType) { return zodType instanceof zod.z.ZodOptional || zodType instanceof zod.z.ZodDefault; } function isNullableType(zodType) { return zodType instanceof zod.z.ZodNullable; } // src/factories/function-factory.ts function createFunction(options) { const { name, description, parameters, execute } = options; const schema = { name, description, parameters: parameters instanceof zod.z.ZodObject ? zodToJsonSchema(parameters) : parameters }; const wrappedExecute = async (params) => { try { if (parameters instanceof zod.z.ZodObject) { parameters.parse(params); } return await Promise.resolve(execute(params)); } catch (error) { if (error instanceof zod.z.ZodError) { const errorMessage = formatZodError(error); throw new Error(`Parameter validation failed: ${errorMessage}`); } throw error; } }; return { name, description, schema, execute: wrappedExecute }; } function functionFromCallback(name, fn, description) { const paramInfo = extractParameterInfo(fn); const paramSchema = createParameterSchema(paramInfo.argNames); const execute = async (params) => { const args = paramInfo.argNames.map((name2) => params[name2]); return await Promise.resolve(fn(...args)); }; return { name, description, schema: { name, description, parameters: paramSchema }, execute }; } function createValidatedFunction(options) { const baseFunction = createFunction(options); if (!options.validateResult) { return baseFunction; } const wrappedExecute = async (params) => { const result = await baseFunction.execute(params); if (!options.validateResult(result)) { throw new Error( options.resultErrorMessage || "Function result validation failed" ); } return result; }; return { ...baseFunction, execute: wrappedExecute }; } function formatZodError(error) { return error.errors.map((e) => { const path = e.path.length > 0 ? e.path.join(".") : "root"; return `${path}: ${e.message}`; }).join(", "); } function extractParameterInfo(fn) { const fnStr = fn.toString(); const argsMatch = fnStr.match(/\(([^)]*)\)/); const argNames = argsMatch ? argsMatch[1].split(",").map((arg) => arg.trim()).filter(Boolean) : []; return { argNames }; } function createParameterSchema(argNames) { return { type: "object", properties: Object.fromEntries( argNames.map((name) => [name, { type: "string" }]) ), required: argNames }; } // src/registry/function-registry.ts var FunctionRegistry = class { /** @internal Map of function names to handlers */ functions = /* @__PURE__ */ new Map(); /** @internal Map of function names to definitions */ definitions = /* @__PURE__ */ new Map(); /** * Register a function with its definition and handler * * @param definition - Function definition with schema * @param handler - Function execution handler */ register(definition, handler) { this.functions.set(definition.name, handler); this.definitions.set(definition.name, definition); } /** * Unregister a function by name * * @param name - Function name to unregister * @returns True if function was found and removed */ unregister(name) { const hadFunction = this.functions.delete(name); const hadDefinition = this.definitions.delete(name); return hadFunction || hadDefinition; } /** * Check if a function is registered * * @param name - Function name to check * @returns True if function is registered */ has(name) { return this.functions.has(name); } /** * Get all registered function definitions * * @returns Array of all function definitions */ getAllDefinitions() { return Array.from(this.definitions.values()); } /** * Get function definition by name * * @param name - Function name * @returns Function definition if found */ getDefinition(name) { return this.definitions.get(name); } /** * Get all registered function names * * @returns Array of function names */ getFunctionNames() { return Array.from(this.functions.keys()); } /** * Get total number of registered functions * * @returns Number of registered functions */ getCount() { return this.functions.size; } /** * Clear all registered functions */ clear() { this.functions.clear(); this.definitions.clear(); } /** * Execute function call * * @param functionCall - Function call with name and arguments * @param context - Optional execution context * @returns Promise resolving to function call result */ async execute(functionCall, context) { const { name, arguments: args } = functionCall; const handler = this.functions.get(name); if (!handler) { return { name, error: `Function '${name}' is not registered` }; } try { const parsedArgs = this.parseArguments(args); const result = await handler(parsedArgs, context); return { name, result }; } catch (error) { return { name, error: error instanceof Error ? error.message : String(error) }; } } /** * Parse function arguments safely * * @param args - Arguments to parse (string or object) * @returns Parsed arguments object */ parseArguments(args) { if (typeof args === "string") { try { return JSON.parse(args); } catch (error) { throw new Error(`Failed to parse function arguments: ${error instanceof Error ? error.message : String(error)}`); } } return args || {}; } }; function createFunctionSchema(definition) { const propertySchemas = {}; if (definition.parameters && definition.parameters.properties) { for (const [key, prop] of Object.entries(definition.parameters.properties)) { propertySchemas[key] = convertJsonSchemaToZod(prop, key); } } return zod.z.object(propertySchemas); } function convertJsonSchemaToZod(property, fieldName) { if (!property.type) { console.warn(`No type specified for field ${fieldName}, using z.any()`); return zod.z.any(); } switch (property.type) { case "string": return createZodString(property); case "number": return createZodNumber(property); case "integer": return createZodInteger(property); case "boolean": return zod.z.boolean(); case "array": return createZodArray(property, fieldName); case "object": return createZodObject(); default: console.warn(`Unsupported type ${property.type} for field ${fieldName}, using z.any()`); return zod.z.any(); } } function createZodString(property) { let schema = zod.z.string(); if (property.minLength !== void 0) { schema = schema.min(property.minLength); } if (property.maxLength !== void 0) { schema = schema.max(property.maxLength); } if (property.pattern) { schema = schema.regex(new RegExp(property.pattern)); } if (property.format === "email") { schema = schema.email(); } if (property.format === "uri" || property.format === "url") { schema = schema.url(); } return schema; } function createZodNumber(property) { let schema = zod.z.number(); if (property.minimum !== void 0) { schema = schema.min(property.minimum); } if (property.maximum !== void 0) { schema = schema.max(property.maximum); } return schema; } function createZodInteger(property) { let schema = zod.z.number().int(); if (property.minimum !== void 0) { schema = schema.min(property.minimum); } if (property.maximum !== void 0) { schema = schema.max(property.maximum); } return schema; } function createZodArray(property, fieldName) { const itemSchema = property.items ? convertJsonSchemaToZod(property.items, `${fieldName}[]`) : zod.z.any(); let schema = zod.z.array(itemSchema); if (property.minItems !== void 0) { schema = schema.min(property.minItems); } if (property.maxItems !== void 0) { schema = schema.max(property.maxItems); } return schema; } function createZodObject(_property) { return zod.z.record(zod.z.any()); } function zodToJsonSchema2(schema) { const shape = schema._def.shape(); const properties = {}; const required = []; Object.entries(shape).forEach(([key, zodType]) => { const typeObj = zodType; let property = {}; if (typeObj instanceof zod.z.ZodNumber) { property.type = "number"; } else if (typeObj instanceof zod.z.ZodString) { property.type = "string"; } else if (typeObj instanceof zod.z.ZodBoolean) { property.type = "boolean"; } else if (typeObj instanceof zod.z.ZodEnum) { property.type = "string"; property.enum = typeObj._def.values; } else if (typeObj instanceof zod.z.ZodArray) { property.type = "array"; if (typeObj._def.type instanceof zod.z.ZodObject) { property.items = zodToJsonSchema2(typeObj._def.type); } } else if (typeObj instanceof zod.z.ZodObject) { property = zodToJsonSchema2(typeObj); } const description = typeObj._def.description; if (description) { property.description = description; } const isOptional = typeObj instanceof zod.z.ZodOptional; if (!isOptional) { required.push(key); } properties[key] = property; }); return { type: "object", properties, required: required.length > 0 ? required : void 0 }; } function zodFunctionToSchema(tool) { return { name: tool.name, description: tool.description, parameters: zodToJsonSchema2(tool.parameters) }; } // src/performance/performance-monitor.ts var PerformanceMonitor = class { callRecords = []; memorySnapshots = []; maxRecords; monitoringInterval; eventListeners = []; isMonitoring = false; // External statistics sources cacheStatsProvider; lazyLoadStatsProvider; resourceStatsProvider; constructor(options = {}) { this.maxRecords = options.maxRecords || 1e4; if (options.monitoringIntervalMs) { this.startMonitoring(options.monitoringIntervalMs); } } /** * Start monitoring */ startMonitoring(intervalMs = 5e3) { if (this.isMonitoring) { return; } this.isMonitoring = true; this.monitoringInterval = setInterval(() => { this.collectMetrics(); }, intervalMs); } /** * Stop monitoring */ stopMonitoring() { if (!this.isMonitoring) { return; } this.isMonitoring = false; if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = void 0; } } /** * Record tool call */ recordToolCall(record) { this.callRecords.push(record); if (this.callRecords.length > this.maxRecords) { const removeCount = Math.floor(this.maxRecords * 0.1); this.callRecords.splice(0, removeCount); } this.collectMemorySnapshot(); } /** * Helper for recording tool call start time */ startToolCall(toolName, parameters) { const callId = `${toolName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const parameterSize = this.estimateObjectSize(parameters); this._pendingCalls = this._pendingCalls || /* @__PURE__ */ new Map(); this._pendingCalls.set(callId, { toolName, startTime: performance.now(), parameterSize }); return callId; } /** * Helper for recording tool call completion */ endToolCall(callId, success, response, error) { const pendingCalls = this._pendingCalls; if (!pendingCalls || !pendingCalls.has(callId)) { return; } const pending = pendingCalls.get(callId); pendingCalls.delete(callId); const endTime = performance.now(); const responseSize = response ? this.estimateObjectSize(response) : 0; this.recordToolCall({ toolName: pending.toolName, startTime: pending.startTime, endTime, duration: endTime - pending.startTime, success, error, parameterSize: pending.parameterSize, responseSize }); } /** * Register external statistics providers */ setCacheStatsProvider(provider) { this.cacheStatsProvider = provider; } setLazyLoadStatsProvider(provider) { this.lazyLoadStatsProvider = provider; } setResourceStatsProvider(provider) { this.resourceStatsProvider = provider; } /** * Register event listener */ addEventListener(listener) { this.eventListeners.push(listener); } /** * Remove event listener */ removeEventListener(listener) { const index = this.eventListeners.indexOf(listener); if (index > -1) { this.eventListeners.splice(index, 1); } } /** * Get current performance metrics */ getMetrics() { const now = performance.now(); const recentRecords = this.callRecords.filter( (record) => now - record.endTime < 6e4 // last 1 minute ); const totalCalls = this.callRecords.length; const successfulCalls = this.callRecords.filter((r) => r.success).length; const failedCalls = totalCalls - successfulCalls; const successRate = totalCalls > 0 ? successfulCalls / totalCalls : 0; const durations = this.callRecords.map((r) => r.duration); const averageCallTime = durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / durations.length : 0; const maxCallTime = durations.length > 0 ? Math.max(...durations) : 0; const minCallTime = durations.length > 0 ? Math.min(...durations) : 0; const throughput = recentRecords.length / 60; const memoryUsage = this.calculateMemoryMetrics(); return { toolCallCount: totalCalls, averageCallTime, maxCallTime, minCallTime, successfulCalls, failedCalls, successRate, throughput, memoryUsage, cacheMetrics: this.cacheStatsProvider ? this.cacheStatsProvider() : null, lazyLoadMetrics: this.lazyLoadStatsProvider ? this.lazyLoadStatsProvider() : null, resourceMetrics: this.resourceStatsProvider ? this.resourceStatsProvider() : null }; } /** * Get performance metrics for specific tool */ getToolMetrics(toolName) { const toolRecords = this.callRecords.filter((r) => r.toolName === toolName); if (toolRecords.length === 0) { return { toolCallCount: 0, averageCallTime: 0, maxCallTime: 0, minCallTime: 0, successfulCalls: 0, failedCalls: 0, successRate: 0, throughput: 0 }; } const successfulCalls = toolRecords.filter((r) => r.success).length; const failedCalls = toolRecords.length - successfulCalls; const durations = toolRecords.map((r) => r.duration); return { toolCallCount: toolRecords.length, averageCallTime: durations.reduce((sum, d) => sum + d, 0) / durations.length, maxCallTime: Math.max(...durations), minCallTime: Math.min(...durations), successfulCalls, failedCalls, successRate: successfulCalls / toolRecords.length, throughput: toolRecords.filter( (r) => performance.now() - r.endTime < 6e4 ).length / 60 }; } /** * Reset performance metrics */ reset() { this.callRecords = []; this.memorySnapshots = []; } /** * Generate performance report */ generateReport() { const metrics = this.getMetrics(); return ` === Tool Performance Report === Total Calls: ${metrics.toolCallCount} Success Rate: ${(metrics.successRate * 100).toFixed(2)}% Average Response Time: ${metrics.averageCallTime.toFixed(2)}ms Max Response Time: ${metrics.maxCallTime.toFixed(2)}ms Min Response Time: ${metrics.minCallTime.toFixed(2)}ms Throughput: ${metrics.throughput.toFixed(2)} TPS Memory Usage: - Current Heap: ${(metrics.memoryUsage.currentHeapUsed / 1024 / 1024).toFixed(2)}MB - Max Heap: ${(metrics.memoryUsage.maxHeapUsed / 1024 / 1024).toFixed(2)}MB - Average Heap: ${(metrics.memoryUsage.averageHeapUsed / 1024 / 1024).toFixed(2)}MB ${metrics.cacheMetrics ? ` Cache Performance: - Hit Rate: ${(metrics.cacheMetrics.hitRate * 100).toFixed(2)}% - Cache Items: ${metrics.cacheMetrics.totalItems} - Memory Usage: ${(metrics.cacheMetrics.estimatedMemoryUsage / 1024).toFixed(2)}KB ` : ""} ${metrics.resourceMetrics ? ` Resource Management: - Total Resources: ${metrics.resourceMetrics.totalResources} - Memory Usage: ${(metrics.resourceMetrics.estimatedMemoryUsage / 1024 / 1024).toFixed(2)}MB ` : ""} `.trim(); } /** * Collect memory snapshot */ collectMemorySnapshot() { const memUsage = process.memoryUsage(); this.memorySnapshots.push(memUsage.heapUsed); if (this.memorySnapshots.length > 1e3) { this.memorySnapshots.splice(0, 100); } } /** * Calculate memory metrics */ calculateMemoryMetrics() { const memUsage = process.memoryUsage(); return { currentHeapUsed: memUsage.heapUsed, maxHeapUsed: this.memorySnapshots.length > 0 ? Math.max(...this.memorySnapshots) : memUsage.heapUsed, averageHeapUsed: this.memorySnapshots.length > 0 ? this.memorySnapshots.reduce((sum, snap) => sum + snap, 0) / this.memorySnapshots.length : memUsage.heapUsed, external: memUsage.external, rss: memUsage.rss }; } /** * Collect metrics and trigger events */ collectMetrics() { try { const metrics = this.getMetrics(); for (const listener of this.eventListeners) { try { listener(metrics); } catch (error) { console.error("Performance event listener error:", error); } } } catch (error) { console.error("Failed to collect performance metrics:", error); } } /** * Estimate object size */ estimateObjectSize(obj) { if (obj === null || obj === void 0) { return 8; } switch (typeof obj) { case "string": return obj.length * 2; case "number": return 8; case "boolean": return 4; case "object": if (Array.isArray(obj)) { return obj.reduce((acc, item) => acc + this.estimateObjectSize(item), 0); } else { let size = 0; for (const key in obj) { size += key.length * 2; size += this.estimateObjectSize(obj[key]); } return size; } default: return 16; } } }; var globalPerformanceMonitor = new PerformanceMonitor({ maxRecords: 1e4, monitoringIntervalMs: 5e3 }); // src/tool-provider.ts var ToolProviderError = class extends Error { code; context; constructor(message, code = "TOOL_PROVIDER_ERROR", context) { super(message); this.name = "ToolProviderError"; this.code = code; this.context = context; } }; var ToolNotFoundError = class extends ToolProviderError { constructor(toolName, availableTools) { super( `Tool '${toolName}' not found.${availableTools ? ` Available tools: ${availableTools.join(", ")}` : ""}`, "TOOL_NOT_FOUND", { toolName, availableTools } ); } }; var ToolExecutionError = class extends ToolProviderError { constructor(toolName, originalError) { const errorMessage = originalError instanceof Error ? originalError.message : String(originalError); super( `Tool '${toolName}' call failed: ${errorMessage}`, "TOOL_EXECUTION_ERROR", { toolName, originalError: errorMessage } ); } }; var BaseToolProvider = class { logger; constructor(options) { this.logger = options?.logger; } /** * Get available tool names from functions list */ getAvailableTools() { return this.functions?.map((f) => f.name) || []; } /** * Check if a tool exists */ hasTool(toolName) { return this.getAvailableTools().includes(toolName); } /** * Validate tool existence before calling */ validateToolExists(toolName) { if (!this.hasTool(toolName)) { throw new ToolNotFoundError(toolName, this.getAvailableTools()); } } /** * Log error with context */ logError(message, context) { if (this.logger) { this.logger(message, context); } else { console.error(message, context); } } /** * Handle tool execution with common error handling and performance monitoring */ async executeToolSafely(toolName, parameters, executor) { const callId = globalPerformanceMonitor.startToolCall(toolName, parameters); try { this.validateToolExists(toolName); this.logError(`Tool '${toolName}' execution started`, { toolName, parameters }); const result = await executor(); this.logError(`Tool '${toolName}' execution succeeded`, { toolName, result }); globalPerformanceMonitor.endToolCall(callId, true, result); return result; } catch (error) { this.logError(`Error occurred while calling tool '${toolName}'`, { toolName, parameters, error }); const errorMessage = error instanceof Error ? error.message : String(error); globalPerformanceMonitor.endToolCall(callId, false, void 0, errorMessage); if (error instanceof ToolProviderError) { throw error; } throw new ToolExecutionError(toolName, error); } } }; // src/performance/cache-manager.ts var CacheManager = class { cache = /* @__PURE__ */ new Map(); maxSize; defaultTTL; hits = 0; misses = 0; expired = 0; constructor(options = {}) { this.maxSize = options.maxSize || 1e3; this.defaultTTL = options.defaultTTL; } /** * Get value from cache */ get(key) { const item = this.cache.get(key); if (!item) { this.misses++; return void 0; } if (this.isExpired(item)) { this.cache.delete(key); this.expired++; this.misses++; return void 0; } item.accessCount++; item.lastAccessed = Date.now(); this.hits++; return item.data; } /** * Set value in cache */ set(key, value, ttl) { if (this.cache.size >= this.maxSize && !this.cache.has(key)) { this.evictLRU(); } const now = Date.now(); const item = { data: value, timestamp: now, ttl: ttl || this.defaultTTL, accessCount: 1, lastAccessed: now }; this.cache.set(key, item); } /** * Delete item from cache */ delete(key) { return this.cache.delete(key); } /** * Check if specific key exists in cache */ has(key) { const item = this.cache.get(key); if (!item) return false; if (this.isExpired(item)) { this.cache.delete(key); this.expired++; return false; } return true; } /** * Clear entire cache */ clear() { this.cache.clear(); this.hits = 0; this.misses = 0; this.expired = 0; } /** * Clean up expired items */ cleanup() { let cleanedCount = 0; const now = Date.now(); for (const [key, item] of this.cache.entries()) { if (this.isExpired(item, now)) { this.cache.delete(key); cleanedCount++; this.expired++; } } return cleanedCount; } /** * Get cache statistics */ getStats() { const totalRequests = this.hits + this.misses; const hitRate = totalRequests > 0 ? this.hits / totalRequests : 0; let estimatedMemoryUsage = 0; for (const [key, item] of this.cache.entries()) { estimatedMemoryUsage += key.length * 2; estimatedMemoryUsage += this.estimateObjectSize(item); } return { totalItems: this.cache.size, hits: this.hits, misses: this.misses, hitRate, expired: this.expired, estimatedMemoryUsage }; } /** * Get all cache keys */ keys() { return Array.from(this.cache.keys()); } /** * Get all cache values */ values() { return Array.from(this.cache.values()).map((item) => item.data); } /** * Get cache size */ size() { return this.cache.size; } /** * Check if item has expired */ isExpired(item, now) { if (!item.ttl) return false; const currentTime = now || Date.now(); return currentTime - item.timestamp > item.ttl; } /** * Evict least recently used item using LRU algorithm */ evictLRU() { let lruKey; let lruTime = Infinity; for (const [key, item] of this.cache.entries()) { if (item.lastAccessed < lruTime) { lruTime = item.lastAccessed; lruKey = key; } } if (lruKey) { this.cache.delete(lruKey); } } /** * Estimate object size (approximate) */ estimateObjectSize(obj) { let size = 0; if (obj === null || obj === void 0) { return 8; } switch (typeof obj) { case "string": return obj.length * 2; // UTF-16 case "number": return 8; case "boolean": return 4; case "object": if (Array.isArray(obj)) { return obj.reduce((acc, item) => acc + this.estimateObjectSize(item), 0); } else { for (const key in obj) { size += key.length * 2; size += this.estimateObjectSize(obj[key]); } return size; } default: return 16; } } }; var FunctionSchemaCacheManager = class extends CacheManager { constructor() { super({ maxSize: 500, // smaller number as function schemas can be large defaultTTL: 30 * 60 * 1e3 // 30 minutes }); } /** * Generate cache key from tool definitions */ generateKey(toolDefinitions) { const keys = Object.keys(toolDefinitions).sort(); const signature = keys.map((key) => { const tool = toolDefinitions[key]; return `${key}:${tool.name}:${tool.description}`; }).join("|"); return this.simpleHash(signature); } /** * Simple hash function */ simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return Math.abs(hash).toString(36); } }; var globalFunctionSchemaCache = new FunctionSchemaCacheManager(); var globalToolCache = new CacheManager({ maxSize: 1e3, defaultTTL: 60 * 60 * 1e3 // 1 hour }); var CacheCleanupScheduler = class { intervals = []; /** * Start periodic cache cleanup */ start(cacheManagers, intervalMs = 5 * 60 * 1e3) { for (const cache of cacheManagers) { const interval = setInterval(() => { const cleaned = cache.cleanup(); if (cleaned > 0) { console.log(`Cache cleanup: removed ${cleaned} expired items`); } }, intervalMs); this.intervals.push(interval); } } /** * Stop periodic cache cleanup */ stop() { for (const interval of this.intervals) { clearInterval(interval); } this.intervals = []; } }; var globalCacheCleanupScheduler = new CacheCleanupScheduler(); // src/function-tool-provider.ts var ZodFunctionToolProvider = class extends BaseToolProvider { tools; _functions = null; enableCache; cacheManager; cacheKey; constructor(options) { super({ logger: options.logger }); this.tools = options.tools; this.enableCache = options.enableCache !== false; this.cacheManager = options.cacheManager || globalFunctionSchemaCache; this.cacheKey = this.enableCache ? this.cacheManager.generateKey(this.tools) : ""; } /** * Function schema list (lazy loading + caching) */ get functions() { if (this._functions) { return this._functions; } if (this.enableCache) { const cached = this.cacheManager.get(this.cacheKey); if (cached) { this._functions = cached; this.logDebug("Loaded function schemas from cache.", { toolCount: cached.length, cacheKey: this.cacheKey }); return this._functions; } } this._functions = this.convertToolsToFunctions(); if (this.enableCache) { this.cacheManager.set(this.cacheKey, this._functions); this.logDebug("Saved function schemas to cache.", { toolCount: this._functions.length, cacheKey: this.cacheKey }); } return this._functions; } /** * Convert tool definitions to JSON schema */ convertToolsToFunctions() { const startTime = this.enableCache ? performance.now() : 0; const functions = Object.values(this.tools).map((tool) => { const properties = {}; const required = []; const shape = tool.parameters.shape || {}; for (const propName in shape) { const prop = shape[propName]; let type = "string"; if (prop._def.typeName === "ZodNumber") type = "number"; if (prop._def.typeName === "ZodBoolean") type = "boolean"; if (prop._def.typeName === "ZodArray") type = "array"; if (prop._def.typeName === "ZodObject") type = "object"; properties[propName] = { type, description: prop._def.description || `${propName} parameter` }; if (prop._def.typeName === "ZodEnum") { properties[propName].enum = prop._def.values; } if (!prop._def.isOptional) { required.push(propName); } } return { name: tool.name, description: tool.description, parameters: { type: "object", properties, required: required.length > 0 ? required : void 0 } }; }); if (this.enableCache) { const endTime = performance.now(); this.logDebug("Function schema conversion completed", { toolCount: functions.length, processingTime: `${(endTime - startTime).toFixed(2)}ms` }); } return functions; } /** * Tool call implementation */ async callTool(toolName, parameters) { return this.executeToolSafely(toolName, parameters, async () => { const tool = this.tools[toolName]; if (!tool) { throw new Error(`Tool definition not found.`); } return await tool.handler(parameters); }); } /** * Check if specific tool exists (override) */ hasTool(toolName) { return toolName in this.tools; } /** * Get cache statistics */ getCacheStats() { if (!this.enableCache) { return null; } return this.cacheManager.getStats(); } /** * Clear cache */ clearCache() { if (this.enableCache) { this.cacheManager.delete(this.cacheKey); this._functions = null; this.logDebug("Deleted function schema cache.", { cacheKey: this.cacheKey }); } } /** * Output debug log */ logDebug(message, context) { this.logError(`[ZodFunctionToolProvider] ${message}`, context); } }; function createZodFunctionToolProvider(options) { return new ZodFunctionToolProvider(options); } // src/mcp-tool-provider.ts var MCPToolProvider = class extends BaseToolProvider { mcpClient; functions; constructor(options) { super({ logger: options.logger }); this.mcpClient = options.mcpClient; this.initializeFunctions(); } /** * Get tool list from MCP client and convert to function schema */ async initializeFunctions() { try { if (this.mcpClient.listTools) { const result = await this.mcpClient.listTools(); this.functions = result.tools.map((tool) => ({ name: tool.name, description: tool.description || `MCP tool: ${tool.name}`, parameters: tool.inputSchema || { type: "object", properties: {}, required: [] } })); } } catch (error) { this.logError("MCP tool list initialization failed", { error }); this.functions = []; } } /** * Tool call implementation */ async callTool(toolName, parameters) { return this.executeToolSafely(toolName, parameters, async () => { return await this.mcpClient.callTool(toolName, parameters); }); } /** * Return available tool list (override) * For MCP, function list may not exist, so handle dynamically */ getAvailableTools() { if (!this.functions || this.functions.length === 0) { return []; } return super.getAvailableTools(); } /** * Check if specific tool exists (override) * For MCP, tool may exist even without function list, so always return true */ hasTool(toolName) { if (!this.functions || this.functions.length === 0) { return true; } return super.hasTool(toolName); } }; function createMcpToolProvider(mcpClient) { return new MCPToolProvider({ mcpClient }); } // src/openapi-tool-provider.ts var OpenAPIToolProvider = class extends BaseToolProvider { openApiSpec; baseUrl; functions; constructor(options) { super({ logger: options.logger }); this.openApiSpec = options.openApiSpec; this.baseUrl = options.baseUrl; } /** * Convert OpenAPI spec to function list */ convertOpenAPIToFunctions(spec) { const functions = []; for (const path in spec.paths) { for (const method in spec.paths[path]) { const operation = spec.paths[path][method]; const functionName = operation.operationId || `${method}${path.replace(/\//g, "_")}`; const parameters = {}; const required = []; if (operation.parameters) { for (const param of operation.parameters) { parameters[param.name] = { type: param.schema?.type || "string", description: param.description || `${param.name} parameter` }; if (param.required) { required.push(param.name); } } } if (operation.requestBody?.content?.["application/json"]?.schema) { const schema = operation.requestBody.content["application/json"].schema; if (schema.properties) { for (const propName in schema.properties) { parameters[propName] = { type: schema.properties[propName].type || "string", description: schema.properties[propName].description || `${propName} property` }; } if (schema.required) { required.push(...schema.required); } } } functions.push({ name: functionName, description: operation.summary || operation.description || `${method.toUpperCase()} ${path}`, parameters: { type: "object", properties: parameters, required: required.length > 0 ? required : void 0 } }); } } return functions; } /** * Initialize OpenAPI spec (lazy loading) */ async initializeSpec() { if (this.functions) return; try { if (typeof this.openApiSpec === "string") { const response = await fetch(this.openApiSpec); if (!response.ok) { throw new Error(`Cannot fetch OpenAPI spec: ${response.status} ${response.statusText}`); } this.openApiSpec = await response.json(); } this.functions = this.convertOpenAPIToFunctions(this.openApiSpec); } catch (error) { throw new Error(`OpenAPI spec initialization failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Tool call implementation */ async callTool(toolName, parameters) { await this.initializeSpec(); return this.executeToolSafely(toolName, parameters, async () => { const baseUrl = this.baseUrl || ""; const url = `${baseUrl}/${toolName}`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(parameters) }); if (!response.ok) { throw new Error(`API call failed: ${response.status} ${response.statusText}`); } return await response.json(); }); } /** * Return available tool list (override) */ getAvailableTools() { if (!this.functions) { return []; } return super.getAvailableTools(); } /** * Check if specific tool exists (override) */ hasTool(toolName) { if (!this.functions) { return false; } return super.hasTool(toolName); } }; function createOpenAPIToolProvider(openApiSpec, options) { return new OpenAPIToolProvider({ openApiSpec, baseUrl: options?.baseUrl }); } // src/tool-provider-factory.ts var ToolProviderFactory = class { providers = /* @__PURE__ */ new Map(); logger; constructor(options) { this.logger = options?.logger; } /** * Create Zod Function Tool Provider */ createZodFunctionProvider(name, tools) { const provider = new ZodFunctionToolProvider({ tools, logger: this.logger }); this.providers.set(name, provider); return provider; } /** * Create OpenAPI Tool Provider */ createOpenAPIProvider(name, openApiSpec, options) { const provider = new OpenAPIToolProvider({ openApiSpec, baseUrl: options?.baseUrl, logger: this.logger }); this.providers.set(name, provider); return provider; } /** * Create MCP Tool Provider */ createMCPProvider(name, mcpClient) { const provider = new MCPToolProvider({ mcpClient, logger: this.logger }); this.providers.set(name, provider); return provider; } /** * Generic Tool Provider creation method */ createProvider(type, name, config) { let provider; switch (type) { case "zod-function": provider = new ZodFunctionToolProvider(config); break; case "openapi": provider = new OpenAPIToolProvider(config); break; case "mcp": provider = new MCPToolProvider(config); break; default: throw new Error(`Unsupported tool provider type: ${type}`); } this.providers.set(name, provider); return provider; } /** * Get registered Provider */ getProvider(name) { return this.providers.get(name); } /** * Get all registered Provider list */ getAllProviders() { return Object.fromEntries(this.providers.entries()); } /** * Remove Provider */ removeProvider(name) { return this.providers.delete(name); } /** * Get integrated list of available tools from all Providers */ getAllAvailableTools() { const result = {}; for (const [name, provider] of this.providers.entries()) { result[name] = provider.getAvailableTools?.() || []; } return result; } /** * Find Provider that provides specific tool */ findProviderForTool(toolName) { for (const [name, provider] of this.providers.entries()) { if (provider.hasTool?.(toolName)) { return { providerName: name, provider }; } } return void 0; } /** * Tool call (automatically find appropriate Provider) */ async callTool(toolName, parameters) { const providerInfo = this.findProviderForTool(toolName); if (!providerInfo) { throw new Error(`Cannot find provider that provides tool '${toolName}'.`); } return await providerInfo.provider.callTool(toolName, parameters); } }; var globalFactory; function getGlobalToolProviderFactory() { if (!globalFactory) { globalFactory = new ToolProviderFactory(); } return globalFactory; } function createZodFunctionProvider(tools, options) { return new ZodFunctionToolProvider({ tools, logger: options?.logger }); } function createOpenAPIProvider(openApiSpec, options) { return new OpenAPIToolProvider({ openApiSpec, baseUrl: options?.baseUrl, logger: options?.logger }); } function createMCPProvider(mcpClient, options) { return new MCPToolProvider({ mcpClient, logger: options?.logger }); } // src/tool/base-tool.ts var BaseTool = class { /** * Tool name */ name; /** * Tool description */ description; /** * Tool category */ category; /** * Tool version */ version; /** * Whether to validate parameters */ validateParams; /** * Tool execution function */ _execute; /** * Pre-execution hook */ beforeExecute; /** * Post-execution hook */ afterExecute; /** * Constructor * * @param options - Base tool options */ constructor(options) { this.name = options.name; this.description = options.description; this.category = options.category; this.version = options.version; this.validateParams = options.validateParams ?? true; this._execute = options.execute; this.beforeExecute = options.beforeExecute; this.afterExecute = options.afterExecute; } /** * Execute tool * * @param params - Tool parameters * @returns Tool execution result */ async execute(params) { try { let validatedParams = params; if (this.validateParams) { validatedParams = this.validateParameters(params); } if (this.beforeExecute) { validatedParams = await Promise.resolve(this.beforeExecute(validatedParams)); } let result = await Promise.resolve(this._execute(validatedParams)); if (this.afterExecute) { result = await Promise.resolve(this.afterExecute(result)); } return result; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { status: "error", error: errorMessage }; } } /** * Convert to function schema * * @returns Function schema */ toFunctionSchema() { return { name: this.name, description: this.description, parameters: this.toJsonSchema() }; } /** * Convert to function definition * * @returns Function definition */ toFunctionDefinition() { return { name: this.name, description: this.description, parameters: this.toJsonSchema() }; } /** * Generate string representation * * @returns String representation of the tool */ toString() { return `${this.constructor.name}(name=${this.name}, category=${this.category || "none"}, version=${this.version || "none"})`; } }; var ZodTool = class _ZodTool extends BaseTool { /** * Zod schema */ parameters; /** * Constructor * * @param options - Zod tool options */ constructor(options) { super(options); this.parameters = options.parameters; } /** * Tool schema (returns Zod schema) * * @returns Zod schema */ get schema() { return this.parameters; } /** * Convert Zod schema to JSON schema * * @returns JSON schema format parameter definition */ toJsonSchema() { const jsonSchema = { type: "object", properties: {} }; const shape = this.parameters.shape; for (const [key, zodType] of Object.entries(shape)) { jsonSchema.properties[key] = { type: this.getSchemaType(zodType), description: this.getZodDescription(zodType) || void 0 }; if (zodType._def.values) { jsonSchema.properties[key].enum = zodType._def.values; } } jsonSchema.required = Object.entries(shape).filter(([_key, zodType]) => !this.isOptionalType(zodType)).map(([key]) => key); if (jsonSchema.required?.length === 0) { delete jsonSchema.required; } return jsonSchema; } /** * Validate parameters using Zod schema * * @param params - Parameters to validate * @returns Validated parameters */ validateParameters(params) { return this.parameters.parse(params); } /** * Extract description from Zod type * * @param zodType - Zod type * @returns Description string */ getZodDescription(zodType) { const description = zodType._def.description; if (description) return description; if (zodType instanceof zod.z.ZodOptional || zodType instanceof zod.z.ZodNullable) { return this.getZodDescription(zodType._def.innerType); } return void 0; } /** * Convert Zod schema type to JSON schema type * * @param schema - Zod schema * @returns JSON schema type string */ getSchemaType(schema) { if (schema instanceof zod.z.ZodString) { return "string"; } else if (schema instanceof zod.z.ZodNumber) { return "number"; } else if (schema instanceof zod.z.ZodBoolean) { return "boolean"; } else if (schema instanceof zod.z.ZodArray) { return "array"; } else if (schema instanceof zod.z.ZodObject) { return "object"; } else if (schema instanceof zod.z.ZodEnum) { return "string"; } else if (schema instanceof zod.z.ZodOptional) { return this.getSchemaType(schema._def.innerType); } else { return "string"; } } /** * Check if Zod type is optional * * @param zodType - Zod type * @returns Whether the type is optional */