crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
248 lines (247 loc) • 8.24 kB
JavaScript
/**
* Base Agent Builder
* Abstract base class for agent builders that provides a foundation for creating agents
* with optimized performance and memory usage
*/
import { v4 as uuidv4 } from 'uuid';
import { createHash } from 'crypto';
// These will be implemented later, for now create placeholder types
class CacheHandler {
getOrCompute(key, producer) {
return producer();
}
}
class ToolsHandler {
setCache(cache) { }
addTool(tool) { }
addTools(tools) { }
}
/**
* Base Agent Builder class that provides core functionality for building agents
* Optimized for performance and memory efficiency
*/
export class BaseAgentBuilder {
id;
originalRole;
originalGoal;
originalBackstory;
// Implementing getters/setters for mutable properties that are readonly in the interface
_role;
_goal;
_backstory;
get role() {
return this._role;
}
get goal() {
return this._goal;
}
get backstory() {
return this._backstory;
}
// Configuration properties
_verbose;
_allowDelegation;
_maxIterations;
_maxRpm;
_memory;
_cache;
_tools;
_llm;
_functionCallingLlm;
_knowledgeSources;
_knowledgeStorage; // Will be properly typed later
_systemPrompt;
_useSystemPrompt;
// Original values are now defined as readonly properties in the class signature
// Handlers and utilities
_cacheHandler;
_toolsHandler;
_logger; // Will be properly typed later
_rpmController; // Will be properly typed later
// Weakmap for storing agent executors to avoid memory leaks
static _agentExecutors = new WeakMap();
/**
* Constructor for the BaseAgentBuilder
* @param config Configuration for the agent
*/
constructor(config) {
// Generate a unique ID for the agent
this.id = uuidv4();
// Set original immutable properties
this.originalRole = config.role;
this.originalGoal = config.goal;
this.originalBackstory = config.backstory;
// Set mutable properties
this._role = config.role;
this._goal = config.goal;
this._backstory = config.backstory;
this._llm = config.llm;
this._functionCallingLlm = config.functionCallingLlm;
this._verbose = config.verbose ?? false;
this._allowDelegation = config.allowDelegation ?? true;
this._maxIterations = config.maxIterations ?? 15;
this._maxRpm = config.maxRpm;
this._memory = config.memory ?? true;
this._cache = config.memory ?? false;
this._systemPrompt = config.systemPrompt;
this._useSystemPrompt = config.useSystemPrompt ?? true;
// Initialize handlers
this._toolsHandler = new ToolsHandler();
this._tools = this.validateTools(config.tools || []);
// Set up cache if needed
if (this._cache) {
this._cacheHandler = new CacheHandler();
this._toolsHandler.setCache(this._cacheHandler);
}
}
/**
* Set knowledge for the agent
* @param knowledge Knowledge sources or embeddings
*/
async setKnowledge(knowledge) {
// Implementation for setting agent knowledge would go here
// This would connect to a vector store or other knowledge base
}
/**
* Generate a unique key for this agent based on its configuration
* Used for caching purposes
*/
get key() {
const hashContent = `${this.role}:${this.goal}:${this.backstory || ''}`;
return createHash('md5').update(hashContent).digest('hex');
}
/**
* Create a deep copy of the Agent
* Optimized to avoid unnecessary duplication of resources
*/
copy() {
// This is a placeholder - subclasses will implement proper copying
throw new Error('copy() must be implemented by subclasses');
}
/**
* Interpolate inputs into the agent description and backstory
* @param inputs Values to interpolate into the agent template strings
*/
interpolateInputs(inputs) {
if (inputs && Object.keys(inputs).length > 0) {
this._role = this.interpolateString(this.originalRole, inputs);
this._goal = this.interpolateString(this.originalGoal, inputs);
if (this.originalBackstory) {
this._backstory = this.interpolateString(this.originalBackstory, inputs);
}
}
}
/**
* Set the cache handler for the agent
* @param cacheHandler An instance of the CacheHandler class
*/
setCacheHandler(cacheHandler) {
this._toolsHandler = new ToolsHandler();
if (this._cache) {
this._cacheHandler = cacheHandler;
this._toolsHandler.setCache(cacheHandler);
}
this.createAgentExecutor();
}
/**
* Set the RPM controller for the agent
* @param rpmController An instance of the RPMController class
*/
setRpmController(rpmController) {
if (!this._rpmController) {
this._rpmController = rpmController;
this.createAgentExecutor();
}
}
/**
* Validate and process the tools provided to the agent
* Ensures each tool meets required criteria and converts non-BaseTool to BaseTool
*
* @param tools List of tools to validate
* @returns List of validated BaseTool instances
*/
validateTools(tools) {
const validatedTools = [];
for (const tool of tools) {
// Fast path: tool is already a BaseTool
if (this.isBaseTool(tool)) {
validatedTools.push(tool);
continue;
}
// Check if it has the necessary properties to be converted to a BaseTool
if (this.canBeConvertedToTool(tool)) {
validatedTools.push(this.convertToBaseTool(tool));
continue;
}
throw new Error(`Tool validation failed: ${JSON.stringify(tool)}. ` +
`Tools must be an instance of BaseTool or have 'name', 'func', and 'description' properties.`);
}
return validatedTools;
}
/**
* Check if an object is a BaseTool instance
* @param obj Object to check
* @returns True if the object is a BaseTool instance
*/
isBaseTool(obj) {
return obj &&
typeof obj === 'object' &&
'name' in obj &&
'description' in obj &&
typeof obj.execute === 'function';
}
/**
* Check if an object can be converted to a BaseTool
* @param obj Object to check
* @returns True if the object can be converted to a BaseTool
*/
canBeConvertedToTool(obj) {
return obj &&
typeof obj === 'object' &&
'name' in obj &&
'description' in obj &&
'func' in obj &&
typeof obj.func === 'function';
}
/**
* Convert an object to a BaseTool instance
* @param obj Object to convert
* @returns BaseTool instance
*/
convertToBaseTool(obj) {
// This is a simplified implementation
// In practice, you would create a proper BaseTool implementation
const customTool = {
name: obj.name,
description: obj.description,
verbose: false,
cacheResults: false,
execute: async (input) => {
return await obj.func(input);
},
getMetadata: () => ({
name: obj.name,
description: obj.description
})
};
return customTool;
}
/**
* Interpolate a string with values from an object
* @param str String to interpolate
* @param values Values to interpolate
* @returns Interpolated string
*/
interpolateString(str, values) {
return str.replace(/\{([^}]+)\}/g, (match, key) => {
const value = values[key.trim()];
return value !== undefined ? String(value) : match;
});
}
/**
* String representation of the agent
*/
toString() {
return `Agent(id=${this.id}, role=${this.role})`;
}
}