UNPKG

contextual-agent-sdk

Version:

SDK for building AI agents with seamless voice-text context switching

471 lines • 20.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContextualAgent = void 0; const SessionStateManager_1 = require("./core/SessionStateManager"); const ContextBridge_1 = require("./core/ContextBridge"); const ModalityRouter_1 = require("./core/ModalityRouter"); const LLMManager_1 = require("./core/LLMManager"); const ContextManager_1 = require("./core/ContextManager"); const ToolManager_1 = require("./core/ToolManager"); class ContextualAgent { config; sessionManager; contextBridge; modalityRouter; llmManager; contextManager; toolManager; eventListeners = new Map(); lastLLMResponse; constructor(config, legacyOpenAIKey) { this.config = config; this.sessionManager = new SessionStateManager_1.SessionStateManager(config.storage); this.contextBridge = new ContextBridge_1.ContextBridge(); this.modalityRouter = new ModalityRouter_1.ModalityRouter(); this.initializeContextManager(); this.initializeLLMManager(legacyOpenAIKey); this.initializeToolManager().catch(error => { console.error('[ContextualAgent] Tool manager initialization failed:', error); }); this.initializeEventListeners(); } async processMessage(input, targetModality, sessionId, userId) { const startTime = Date.now(); try { let session = await this.sessionManager.getSession(sessionId); if (!session) { session = await this.sessionManager.createSession(sessionId, userId); this.emitEvent('session_started', sessionId, { userId }); } const inputModality = this.modalityRouter.detectModality(input); const userMessage = await this.modalityRouter.processMessage(input, inputModality, sessionId); this.emitEvent('message_received', sessionId, { messageId: userMessage.id, modality: inputModality }); session = await this.sessionManager.updateSession(sessionId, userMessage, inputModality); let contextualPrompt = ''; if (session.currentModality !== targetModality) { contextualPrompt = await this.sessionManager.bridgeContextForModality(sessionId, targetModality); this.emitEvent('modality_switched', sessionId, { from: session.currentModality, to: targetModality }); this.emitEvent('context_bridged', sessionId, { bridgeType: `${session.currentModality}_to_${targetModality}` }); } else { contextualPrompt = await this.sessionManager.getConversationSummary(sessionId); } let externalContext = ''; if (this.contextManager) { try { const contextResults = await this.contextManager.getContext({ query: userMessage.content, sessionId: sessionId, userId: userId, modality: targetModality }); if (contextResults && contextResults.length > 0) { externalContext = this.contextManager.formatContext(contextResults); this.emitEvent('external_context_retrieved', sessionId, { contextSources: contextResults.length, hasExternalKnowledge: true }); } } catch (error) { console.error('Failed to retrieve external context:', error); } } const enhancedPrompt = this.combineContextSources(contextualPrompt, externalContext); let responseContent = await this.generateResponseWithToolSupport(userMessage.content, enhancedPrompt, targetModality); if (this.toolManager) { try { const toolExecution = await this.toolManager.detectAndExecuteTools(responseContent, { sessionId, userId, modality: targetModality }); if (toolExecution.toolCalls.length > 0) { responseContent = toolExecution.enhancedResponse; this.emitEvent('tools_executed', sessionId, { toolCalls: toolExecution.toolCalls.length, toolResults: toolExecution.toolResults, success: toolExecution.toolResults.every(r => r.success) }); } } catch (toolError) { console.error('[ContextualAgent] Tool execution failed:', toolError); } } const assistantMessage = await this.modalityRouter.prepareResponse(responseContent, targetModality, sessionId); await this.sessionManager.updateSession(sessionId, assistantMessage, targetModality); this.emitEvent('message_sent', sessionId, { messageId: assistantMessage.id, modality: targetModality }); const currentSession = await this.sessionManager.getSession(sessionId); if (!currentSession) { throw new Error('Session lost during processing'); } const responseData = { message: assistantMessage, sessionState: currentSession }; const responseMetadata = { responseTime: Date.now() - startTime, tokensUsed: this.lastLLMResponse?.usage || { promptTokens: 0, completionTokens: 0, totalTokens: 0 }, modalityUsed: targetModality, contextBridgeTriggered: session.currentModality !== targetModality, processingSteps: [ { step: 'message_processing', duration: 50, success: true }, { step: 'context_bridging', duration: 10, success: true }, { step: 'ai_generation', duration: Date.now() - startTime - 60, success: true } ] }; return { success: true, data: responseData, metadata: responseMetadata }; } catch (error) { this.emitEvent('error_occurred', sessionId, { error: error }); return { success: false, error: { code: 'PROCESSING_ERROR', message: `Failed to process message: ${error}`, recoverable: true }, metadata: { responseTime: Date.now() - startTime, tokensUsed: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }, modalityUsed: targetModality, contextBridgeTriggered: false, processingSteps: [ { step: 'error', duration: Date.now() - startTime, success: false, data: { error } } ] } }; } } async switchModality(newModality, sessionId) { const session = await this.sessionManager.getSession(sessionId); if (!session) { throw new Error(`Session ${sessionId} not found`); } const switchMessage = `Switching to ${newModality} mode. How can I help you?`; return this.processMessage(switchMessage, newModality, sessionId); } initializeContextManager() { if (!this.config.contextProviders || this.config.contextProviders.length === 0) { return; } try { this.contextManager = new ContextManager_1.ContextManager({ providers: this.config.contextProviders, maxTokens: this.config.contextSettings?.contextWindowSize || 4000, defaultFormatter: (ctx) => typeof ctx.content === 'string' ? ctx.content : JSON.stringify(ctx.content), errorHandler: (error) => console.error('Context provider error:', error) }); console.log(`Initialized ${this.config.contextProviders.length} context providers`); } catch (error) { console.error('Failed to initialize Context Manager:', error); } } async initializeToolManager() { if (!this.config.tools || this.config.tools.length === 0) { console.log('[ContextualAgent] No tools configured for this agent'); return; } try { this.toolManager = new ToolManager_1.ToolManager(this.config.name); this.toolManager.initializeTools(this.config.tools); console.log(`[ContextualAgent] Tool manager initialized with ${this.config.tools.length} tools`); } catch (error) { console.error('[ContextualAgent] Failed to initialize tool manager:', error); } } initializeLLMManager(legacyOpenAIKey) { try { let llmConfig; if (this.config.llm) { llmConfig = { providers: this.config.llm.providers, defaultProvider: this.config.llm.defaultProvider, fallbackProvider: this.config.llm.fallbackProvider, retryAttempts: this.config.llm.retryAttempts }; } else if (legacyOpenAIKey) { llmConfig = { providers: { 'openai': { type: 'openai', apiKey: legacyOpenAIKey } }, defaultProvider: 'openai' }; } else { console.warn('No LLM providers configured. Using mock responses.'); return; } this.llmManager = new LLMManager_1.LLMManager(llmConfig); } catch (error) { console.error('Failed to initialize LLM Manager:', error); } } async generateResponseWithToolSupport(userMessage, context, targetModality) { if (!this.llmManager) { return this.generateMockResponse(userMessage, targetModality); } try { const systemPrompt = this.buildSystemPrompt(context, targetModality); const baseOptions = { messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userMessage } ], temperature: 0.7, maxTokens: targetModality === 'voice' ? 150 : 500 }; const availableTools = this.getAvailableTools(); const hasTools = availableTools && availableTools.length > 0; const providerSupportsTools = this.llmManager.supportsTools(); if (hasTools && providerSupportsTools) { console.log(`šŸ”§ Using tool-aware generation with ${availableTools.length} tools:`, availableTools.map(t => t.id)); const toolResponse = await this.llmManager.generateWithTools(baseOptions, availableTools); this.lastLLMResponse = toolResponse; if (toolResponse.toolCalls && toolResponse.toolCalls.length > 0) { console.log(`šŸŽÆ LLM made ${toolResponse.toolCalls.length} tool call(s):`, toolResponse.toolCalls.map(tc => tc.function.name)); let finalResponse = toolResponse.content || ''; for (const toolCall of toolResponse.toolCalls) { const tool = availableTools.find(t => t.id === toolCall.function.name); if (tool) { try { const params = JSON.parse(toolCall.function.arguments); const result = await tool.execute(params, { agentId: this.config.name || 'unknown', sessionId: userMessage, metadata: { toolCallId: toolCall.id } }); if (result.success) { const resultText = typeof result.data === 'string' ? result.data : JSON.stringify(result.data); finalResponse += `\n\nāœ… ${tool.name}: ${resultText}`; } else { finalResponse += `\n\nāŒ ${tool.name} failed: ${result.error}`; } } catch (error) { finalResponse += `\n\nāŒ ${tool.name} error: ${error.message}`; } } } return finalResponse; } return toolResponse.content || 'I apologize, but I cannot process your request right now.'; } else { if (!providerSupportsTools) { console.log('āš ļø Provider does not support tools, using basic generation'); } else { console.log('šŸ“ No tools available, using basic generation'); } const response = await this.llmManager.generateResponse(baseOptions); this.lastLLMResponse = response; return response.content || 'I apologize, but I cannot process your request right now.'; } } catch (error) { console.error('LLM API error:', error); this.lastLLMResponse = undefined; return this.generateMockResponse(userMessage, targetModality); } } async generateResponse(userMessage, context, targetModality) { if (!this.llmManager) { return this.generateMockResponse(userMessage, targetModality); } try { const systemPrompt = this.buildSystemPrompt(context, targetModality); const response = await this.llmManager.generateResponse({ messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userMessage } ], temperature: 0.7, maxTokens: targetModality === 'voice' ? 150 : 500 }); this.lastLLMResponse = response; return response.content || 'I apologize, but I cannot process your request right now.'; } catch (error) { console.error('LLM API error:', error); this.lastLLMResponse = undefined; return this.generateMockResponse(userMessage, targetModality); } } buildSystemPrompt(context, targetModality) { let prompt = this.config.systemPrompt; if (context) { prompt += `\n\nConversation Context:\n${context}`; } if (targetModality === 'voice') { prompt += '\n\nIMPORTANT: Respond in a natural, conversational tone suitable for voice interaction. Keep responses concise and engaging.'; } else { prompt += '\n\nIMPORTANT: Provide detailed, well-structured text responses with clear formatting when helpful.'; } return prompt; } combineContextSources(conversationContext, externalContext) { let combinedContext = ''; if (conversationContext) { combinedContext += `Conversation History:\n${conversationContext}`; } if (externalContext) { if (combinedContext) { combinedContext += '\n\n'; } combinedContext += `Relevant Knowledge:\n${externalContext}`; } return combinedContext; } generateMockResponse(userMessage, targetModality) { const baseResponse = "I understand you're asking about: " + userMessage; if (targetModality === 'voice') { return baseResponse + ". Let me help you with that."; } else { return baseResponse + "\n\nHere's how I can assist you:\n1. Provide detailed information\n2. Answer follow-up questions\n3. Help with related topics"; } } async getSession(sessionId) { return this.sessionManager.getSession(sessionId); } async getConversationSummary(sessionId) { return this.sessionManager.getConversationSummary(sessionId); } async destroySession(sessionId) { const success = await this.sessionManager.destroySession(sessionId); if (success) { this.emitEvent('session_ended', sessionId, {}); } return success; } on(eventType, callback) { if (!this.eventListeners.has(eventType)) { this.eventListeners.set(eventType, []); } this.eventListeners.get(eventType).push(callback); } off(eventType, callback) { const listeners = this.eventListeners.get(eventType); if (listeners) { const index = listeners.indexOf(callback); if (index > -1) { listeners.splice(index, 1); } } } emitEvent(eventType, sessionId, data) { const listeners = this.eventListeners.get(eventType); if (listeners) { const event = { type: eventType, sessionId, timestamp: new Date(), data }; listeners.forEach(callback => { try { callback(event); } catch (error) { console.error(`Error in event listener for ${eventType}:`, error); } }); } } initializeEventListeners() { const eventTypes = [ 'session_started', 'session_ended', 'message_received', 'message_sent', 'modality_switched', 'context_bridged', 'error_occurred', 'performance_metric', 'tools_executed', 'external_context_retrieved' ]; eventTypes.forEach(type => { this.eventListeners.set(type, []); }); } getConfig() { return { ...this.config }; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; } getCapabilities() { const capabilities = { ...this.modalityRouter.getCapabilities(), contextBridging: true, sessionManagement: true, eventSystem: true, llmProviders: this.llmManager?.getAvailableProviders() || [], llmProviderStatus: this.llmManager?.getProviderStatus() || [], toolIntegration: !!this.toolManager, toolCapabilities: this.toolManager?.getStats() || { totalTools: 0, availableTools: [] } }; return capabilities; } addLLMProvider(name, config) { if (!this.llmManager) { this.llmManager = new LLMManager_1.LLMManager({ providers: {}, defaultProvider: name }); } this.llmManager.addProvider(name, config); } getLLMProviderStatus() { return this.llmManager?.getProviderStatus() || []; } async testLLMProvider(name) { return this.llmManager?.testProvider(name) || { success: false, responseTime: 0, error: 'No LLM manager configured' }; } setDefaultLLMProvider(name) { this.llmManager?.setDefaultProvider(name); } getAvailableLLMProviders() { return this.llmManager?.getAvailableProviders() || []; } getAvailableTools() { return this.config.tools || []; } async shutdown() { await this.sessionManager.shutdown(); this.contextBridge.clearCache(); this.eventListeners.clear(); if (this.toolManager) { this.toolManager.cleanup(); } } } exports.ContextualAgent = ContextualAgent; //# sourceMappingURL=ContextualAgent.js.map