/** * Base Agent Class * * Reusable base class for all AI agents in the platform. * Provides common functionality: chat, sessions, status, lifecycle management. * * Each specific agent (ExpenseAnalyzer, MarketMaker, etc.) extends this class * and adds domain-specific methods and tools. */ import { StandardAgent, AgentMessage, AgentResponse, createTradingAgent } from './index.js'; /** * Logger interface abstraction * Decouples from specific logging libraries */ export interface ILogger { info(message: string | object, ...args: any[]): void; error(message: string | object, ...args: any[]): void; warn(message: string | object, ...args: any[]): void; debug(message: string | object, ...args: any[]): void; } /** * Chat message interface */ export interface ChatMessage { role: 'user' | 'assistant' | 'system'; content: string; timestamp: string; } /** * Chat session interface */ export interface ChatSession { sessionId: string; messages: ChatMessage[]; metadata?: Record; } /** * Base agent configuration */ export interface BaseAgentConfig { name: string; description: string; systemPrompt: string; logger: ILogger; llm: { model: string; temperature: number; maxTokens: number; openAIApiKey: string; }; memory: { enabled: boolean; persistentThreadId: boolean; maxMessages: number; }; capabilities?: string[]; enableRAG?: boolean; } /** * Base Agent Class * * All agents extend this class and inherit common functionality. * Specific agents can override methods or add domain-specific ones. */ export abstract class BaseAgent { protected agent: StandardAgent; protected logger: ILogger; protected sessions: Map; protected startTime: number; protected config: BaseAgentConfig; constructor(config: BaseAgentConfig) { this.config = config; this.logger = config.logger; this.sessions = new Map(); this.startTime = Date.now(); // Initialize StandardAgent from agent-core this.agent = createTradingAgent({ name: config.name, description: config.description, llm: { model: config.llm.model, temperature: config.llm.temperature, maxTokens: config.llm.maxTokens, openAIApiKey: config.llm.openAIApiKey }, memory: { enabled: config.memory.enabled, persistentThreadId: config.memory.persistentThreadId, maxMessages: config.memory.maxMessages }, prompt: { systemMessage: config.systemPrompt, capabilities: (config.capabilities || []) as any, // Allow any string capabilities enableConversationMemory: config.memory.enabled }, services: { messageService: true, ragService: config.enableRAG || false, loggingEnabled: true } }); this.logger.info(`✅ ${config.name} initialized`); } /** * Start the agent */ async start(): Promise { try { await this.agent.start(); this.logger.info(`🚀 ${this.config.name} started successfully`); } catch (error) { this.logger.error(`❌ Failed to start ${this.config.name}:`, error); throw error; } } /** * Process a user message and return AI response */ async chat( userMessage: string, sessionId: string, contextMetadata?: Record ): Promise { try { const startTime = Date.now(); this.logger.info( { sessionId, contextMetadata }, `💬 Processing chat message: ${userMessage.substring(0, 100)}...` ); // Get or create session let session = this.sessions.get(sessionId); if (!session) { session = { sessionId, messages: [], metadata: { createdAt: new Date().toISOString(), ...contextMetadata } }; this.sessions.set(sessionId, session); } // Add user message to session history session.messages.push({ role: 'user', content: userMessage, timestamp: new Date().toISOString() }); // Process through LangGraph agent const agentMessage: AgentMessage = { content: userMessage, timestamp: new Date().toISOString(), role: 'user', metadata: { sessionId, messageCount: session.messages.length, ...contextMetadata } }; const response = await this.agent.process(agentMessage); // Add assistant response to session history session.messages.push({ role: 'assistant', content: response.content, timestamp: new Date().toISOString() }); // Trim history if too long if (session.messages.length > this.config.memory.maxMessages) { session.messages = session.messages.slice(-this.config.memory.maxMessages); } const processingTime = Date.now() - startTime; this.logger.info( { sessionId, processingTime, tokensUsed: response.metrics?.tokensUsed }, `✅ Chat response generated in ${processingTime}ms` ); return response; } catch (error) { this.logger.error({ sessionId, error }, '❌ Error processing chat message'); throw error; } } /** * Get session history */ getSession(sessionId: string): ChatSession | undefined { return this.sessions.get(sessionId); } /** * Clear session history */ clearSession(sessionId: string): void { this.sessions.delete(sessionId); this.logger.info({ sessionId }, 'Session cleared'); } /** * Get all active sessions */ getActiveSessions(): string[] { return Array.from(this.sessions.keys()); } /** * Get agent status */ getStatus() { return { name: this.config.name, status: 'running', uptime: Date.now() - this.startTime, activeSessions: this.sessions.size, model: this.config.llm.model, capabilities: this.config.capabilities || [] }; } /** * Stop the agent */ async stop(): Promise { try { await this.agent.stop(); this.logger.info(`🛑 ${this.config.name} stopped`); } catch (error) { this.logger.error('Error stopping agent:', error); throw error; } } /** * Get underlying StandardAgent (for advanced use) */ protected getUnderlyingAgent(): StandardAgent { return this.agent; } }