contextual-agent-sdk
Version:
SDK for building AI agents with seamless voice-text context switching
471 lines ⢠20.5 kB
JavaScript
"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