UNPKG

crewai-ts

Version:

TypeScript port of crewAI for agent-based workflows

201 lines (200 loc) 7.31 kB
/** * Tools Handler * Manages agent tools with efficient access patterns and caching capabilities * Optimized for memory efficiency and performance */ /** * Tool category enum for organizing tools */ export var ToolCategory; (function (ToolCategory) { ToolCategory["GENERAL"] = "general"; ToolCategory["DELEGATION"] = "delegation"; ToolCategory["KNOWLEDGE"] = "knowledge"; ToolCategory["CUSTOM"] = "custom"; })(ToolCategory || (ToolCategory = {})); /** * Tools Handler for managing agent tools with efficient access patterns * and optional caching capabilities * * Optimized with: * - Fast tool lookup with Map * - Category-based filtering * - Tool result caching * - Memory efficient tool storage */ export class ToolsHandler { tools = new Map(); cache; defaultKeyGenerator; /** * Create a new ToolsHandler * Optimized for memory efficiency and type safety */ constructor() { // Default cache key generator function with improved memory handling // Uses explicit typing for parameters and optimizes string handling this.defaultKeyGenerator = (tool, args) => { try { // Type validation to ensure proper serialization if (!tool || typeof tool.name !== 'string') { return `unknown-tool:${Date.now()}`; } // Memory-optimized argument handling - filter undefined/null values const filteredArgs = args.filter(arg => arg !== undefined && arg !== null); // Use compact string representation with hash to minimize memory usage const argsStr = JSON.stringify(filteredArgs); // Use substring to limit key size for large argument objects return `${tool.name}:${argsStr.substring(0, 100)}`; } catch (error) { // Fallback with error-safe implementation const toolName = tool && typeof tool.name === 'string' ? tool.name : 'unknown'; return `${toolName}:${Date.now()}`; } }; } /** * Set the cache handler for tool results * @param cache Cache handler instance */ setCache(cache) { this.cache = cache; } /** * Add a tool to the handler * @param tool Tool to add * @param metadata Optional metadata for the tool */ addTool(tool, metadata) { const enhancedTool = { ...tool }; if (metadata) { enhancedTool.metadata = metadata; } this.tools.set(tool.name, enhancedTool); } /** * Add multiple tools to the handler * @param tools Array of tools to add * @param metadata Optional metadata to apply to all tools */ addTools(tools, metadata) { for (const tool of tools) { this.addTool(tool, metadata); } } /** * Get a tool by name * @param name Name of the tool * @returns The tool or undefined if not found */ getTool(name) { return this.tools.get(name); } /** * Get all tools * @returns Array of all tools */ getAllTools() { return Array.from(this.tools.values()); } /** * Get tools by category * @param category Category to filter by * @returns Array of tools in the category */ getToolsByCategory(category) { // Memory-optimized filtering with explicit null/undefined checking for improved type safety return this.getAllTools().filter(tool => { // First check if metadata exists to avoid unnecessary property access if (!tool.metadata) return false; // Then safely compare the category values return tool.metadata.category === category; }); } /** * Remove a tool by name * @param name Name of the tool to remove * @returns True if the tool was removed, false otherwise */ removeTool(name) { return this.tools.delete(name); } /** * Remove all tools */ clearTools() { this.tools.clear(); } /** * Check if a tool exists * @param name Name of the tool * @returns True if the tool exists, false otherwise */ hasTool(name) { return this.tools.has(name); } /** * Execute a tool with caching if enabled * @param name Name of the tool to execute * @param args Arguments to pass to the tool * @param keyGenerator Optional custom cache key generator * @returns The result of the tool execution */ async executeTool(name, args, keyGenerator) { // Validate name is non-empty string with proper type safety if (typeof name !== 'string' || !name.trim()) { throw new TypeError('Tool name must be a non-empty string'); } const tool = this.getTool(name); if (!tool) { throw new Error(`Tool not found: ${name}`); } // Ensure args is an array for type safety and memory optimization // This implementation is more robust with undefined/null handling const safeArgs = Array.isArray(args) ? args : (args ? [args] : []); // If caching is enabled, try to get from cache or compute with memory optimization if (this.cache) { // Use provided key generator or default, with proper null handling const keygen = keyGenerator || this.defaultKeyGenerator; // Generate cache key with proper type handling const cacheKey = keygen(tool, safeArgs); // Use generic type parameter for better type safety return this.cache.getOrCompute(cacheKey, async () => { // Extract the first argument with proper type safety const firstArg = safeArgs.length > 0 ? safeArgs[0] : undefined; // Cast the result to the expected type for memory-efficient handling return await tool.execute(firstArg); }); } // If no cache, execute the tool with memory-optimized parameter handling const firstArg = safeArgs.length > 0 ? safeArgs[0] : undefined; return await tool.execute(firstArg); } /** * Create a categorized string representation of tools for agent prompts * @returns Formatted string of tools grouped by category */ formatToolsForPrompt() { if (this.tools.size === 0) { return 'No tools available.'; } // Group tools by category const toolsByCategory = {}; for (const tool of this.getAllTools()) { const category = tool.metadata?.category || ToolCategory.GENERAL; if (!toolsByCategory[category]) { toolsByCategory[category] = []; } toolsByCategory[category].push(tool); } // Format each category const formattedCategories = []; for (const [category, categoryTools] of Object.entries(toolsByCategory)) { formattedCategories.push(`${category.toUpperCase()} TOOLS:\n` + categoryTools.map(tool => `- ${tool.name}: ${tool.description}`).join('\n')); } return formattedCategories.join('\n\n'); } }