UNPKG

@xynehq/jaf

Version:

Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

277 lines 10.2 kB
/** * JAF ADK Layer - Agent System * * Functional agent creation and management utilities */ import { throwAgentError, createAgentError, Model } from '../types'; // ========== ID Generation ========== export const generateAgentId = () => { // Use crypto-based ID generation for pure functional approach return `agent_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }; // ========== Agent Creation ========== export const createAgent = (config) => { validateAgentConfig(config); const metadata = { created: new Date(), version: '1.0.0', tags: [] }; return { id: generateAgentId(), config: { ...config }, metadata }; }; export const createSimpleAgent = (name, model, instruction, tools = []) => { return createAgent({ name, model, instruction, tools }); }; export const createMultiAgent = (name, model, instruction, subAgents, delegationStrategy = 'conditional') => { const config = { name, model, instruction, tools: [], subAgents, delegationStrategy }; return createAgent(config); }; // ========== Agent Validation ========== export const validateAgentConfig = (config) => { const errors = []; if (!config.name || config.name.trim().length === 0) { errors.push('Agent name is required'); } if (!config.model || config.model.trim().length === 0) { errors.push('Agent model is required'); } if (!config.instruction || config.instruction.trim().length === 0) { errors.push('Agent instruction is required'); } if (!Array.isArray(config.tools)) { errors.push('Agent tools must be an array'); } // Validate tools for (const tool of config.tools) { const toolValidation = validateTool(tool); if (!toolValidation.success) { errors.push(`Tool '${tool.name}': ${toolValidation.errors?.join(', ')}`); } } // Validate sub-agents if present if (config.subAgents) { for (const subAgent of config.subAgents) { const subAgentValidation = validateAgentConfig(subAgent); if (!subAgentValidation.success) { errors.push(`Sub-agent '${subAgent.name}': ${subAgentValidation.errors?.join(', ')}`); } } } if (errors.length > 0) { return { success: false, errors }; } return { success: true, data: config }; }; const validateTool = (tool) => { const errors = []; if (!tool.name || tool.name.trim().length === 0) { errors.push('Tool name is required'); } if (!tool.description || tool.description.trim().length === 0) { errors.push('Tool description is required'); } if (!Array.isArray(tool.parameters)) { errors.push('Tool parameters must be an array'); } if (typeof tool.execute !== 'function') { errors.push('Tool execute must be a function'); } if (errors.length > 0) { return { success: false, errors }; } return { success: true, data: tool }; }; export const validateAgent = (agent) => { if (!agent.id) { return { success: false, errors: ['Agent ID is required'] }; } if (!agent.config) { return { success: false, errors: ['Agent config is required'] }; } if (!agent.metadata) { return { success: false, errors: ['Agent metadata is required'] }; } const configValidation = validateAgentConfig(agent.config); if (!configValidation.success) { return { success: false, errors: configValidation.errors }; } return { success: true, data: agent }; }; // ========== Agent Manipulation ========== export const cloneAgent = (agent, overrides = {}) => { const newConfig = { ...agent.config, ...overrides, tools: [...(overrides.tools || agent.config.tools)], subAgents: overrides.subAgents ? [...overrides.subAgents] : agent.config.subAgents ? [...agent.config.subAgents] : undefined }; const newAgent = createAgent(newConfig); // Preserve some metadata newAgent.metadata.tags = agent.metadata.tags ? [...agent.metadata.tags] : []; return newAgent; }; export const updateAgent = (agent, updates) => { const updatedConfig = { ...agent.config, ...updates }; return { ...agent, config: updatedConfig, metadata: { ...agent.metadata, lastModified: new Date() } }; }; export const addToolToAgent = (agent, tool) => { const newTools = [...agent.config.tools, tool]; return updateAgent(agent, { tools: newTools }); }; export const removeToolFromAgent = (agent, toolName) => { const newTools = agent.config.tools.filter(tool => tool.name !== toolName); return updateAgent(agent, { tools: newTools }); }; export const addSubAgent = (agent, subAgent) => { const subAgents = agent.config.subAgents || []; const newSubAgents = [...subAgents, subAgent]; return updateAgent(agent, { subAgents: newSubAgents }); }; export const removeSubAgent = (agent, subAgentName) => { if (!agent.config.subAgents) { return agent; } const newSubAgents = agent.config.subAgents.filter(sub => sub.name !== subAgentName); return updateAgent(agent, { subAgents: newSubAgents }); }; // ========== Agent Query Functions ========== export const getAgentTool = (agent, toolName) => { return agent.config.tools.find(tool => tool.name === toolName) || null; }; export const hasAgentTool = (agent, toolName) => { return getAgentTool(agent, toolName) !== null; }; export const getAgentSubAgent = (agent, subAgentName) => { if (!agent.config.subAgents) { return null; } return agent.config.subAgents.find(sub => sub.name === subAgentName) || null; }; export const hasSubAgent = (agent, subAgentName) => { return getAgentSubAgent(agent, subAgentName) !== null; }; export const isMultiAgent = (agent) => { return agent.config.subAgents !== undefined && agent.config.subAgents.length > 0; }; export const getAgentToolNames = (agent) => { return agent.config.tools.map(tool => tool.name); }; export const getSubAgentNames = (agent) => { if (!agent.config.subAgents) { return []; } return agent.config.subAgents.map(sub => sub.name); }; // ========== Agent Statistics ========== export const getAgentStats = (agent) => { const toolCount = agent.config.tools.length; const subAgentCount = agent.config.subAgents?.length || 0; const hasGuardrails = (agent.config.guardrails?.length || 0) > 0; const hasInputSchema = agent.config.inputSchema !== undefined; const hasOutputSchema = agent.config.outputSchema !== undefined; const hasExamples = (agent.config.examples?.length || 0) > 0; return { id: agent.id, name: agent.config.name, model: agent.config.model, toolCount, subAgentCount, hasGuardrails, hasInputSchema, hasOutputSchema, hasExamples, isMultiAgent: isMultiAgent(agent), created: agent.metadata.created, lastModified: agent.metadata.lastModified }; }; // ========== Agent Templates ========== export const createWeatherAgent = () => { return createSimpleAgent('weather_agent', Model.GEMINI_2_0_FLASH, 'You are a helpful weather assistant. Use the available tools to provide accurate weather information.', []); }; export const createChatAgent = () => { return createSimpleAgent('chat_agent', Model.GEMINI_2_0_FLASH, 'You are a friendly and helpful conversational assistant. Engage naturally with users and be helpful.', []); }; export const createCodeAgent = () => { return createSimpleAgent('code_agent', Model.GEMINI_2_0_FLASH, 'You are a programming assistant. Help users with code, debugging, and software development questions.', []); }; // ========== Agent Utilities ========== export const agentToJSON = (agent) => { const replacer = (key, value) => { // Skip function values during serialization if (typeof value === 'function') { return undefined; } return value; }; return JSON.stringify(agent, replacer, 2); }; export const agentFromJSON = (json) => { try { const parsed = JSON.parse(json); const validation = validateAgent(parsed); if (!validation.success) { throwAgentError(`Invalid agent JSON: ${validation.errors?.join(', ')}`, parsed.id); } return parsed; } catch (error) { if (error && typeof error === 'object' && error.name === 'AgentError') { throw error; } throwAgentError('Failed to parse agent JSON', undefined, { error: error instanceof Error ? error.message : String(error) }); } // TypeScript doesn't detect that all paths above either return or throw, so this is unreachable throw new Error('Unreachable code'); }; export const compareAgents = (agent1, agent2) => { return (agent1.config.name === agent2.config.name && agent1.config.model === agent2.config.model && agent1.config.instruction === agent2.config.instruction && agent1.config.tools.length === agent2.config.tools.length && agent1.config.tools.every((tool, index) => tool.name === agent2.config.tools[index]?.name)); }; // ========== Agent Error Handling ========== // Re-export createAgentError from types for convenience export { createAgentError }; export const withAgentErrorHandling = (fn, agentId) => { return (...args) => { try { return fn(...args); } catch (error) { if (error && typeof error === 'object' && error.name === 'AgentError') { throw error; } throwAgentError(`Agent operation failed: ${error instanceof Error ? error.message : String(error)}`, agentId, { originalError: error }); } // TypeScript doesn't detect that all paths above either return or throw, so this is unreachable throw new Error('Unreachable code'); }; }; //# sourceMappingURL=index.js.map