crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
201 lines (200 loc) • 7.31 kB
JavaScript
/**
* 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');
}
}