UNPKG

contaigents

Version:

Modular AI Content Ecosystem with Audio Generation

229 lines (228 loc) 8.66 kB
import { LLMFactory } from "./llm/LLMFactory.js"; import { FileService } from "./fileService.js"; import crypto from 'crypto'; import path from 'path'; export class ConversationManager { constructor() { this.conversations = new Map(); this.conversationStoragePath = './.config/conversations'; this.fileService = new FileService(); this.loadPersistedConversations(); } /** * Start a new conversation with an agent */ async startConversation(agentConfig) { const conversationId = crypto.randomUUID(); const llm = await LLMFactory.getConfiguredProvider(); if (!llm) { throw new Error("No LLM provider configured. Please configure an LLM provider first."); } const projectContext = await this.loadProjectContext(); const conversation = { id: conversationId, agent: agentConfig, llm: llm, messages: [], context: projectContext, createdAt: Date.now(), updatedAt: Date.now(), metadata: {} }; this.conversations.set(conversationId, conversation); await this.persistConversation(conversation); return conversationId; } /** * Continue an existing conversation */ async continueConversation(conversationId, message) { const conversation = this.conversations.get(conversationId); if (!conversation) { throw new Error(`Conversation ${conversationId} not found`); } // Add user message to history const userMessage = { role: 'user', content: message, timestamp: Date.now() }; conversation.messages.push(userMessage); // Build contextual prompt with conversation history const contextualPrompt = this.buildContextualPrompt(conversation, message); try { // Get response from LLM const response = await conversation.llm.executePrompt(contextualPrompt, { systemPrompt: conversation.agent.systemPrompt, temperature: 0.7, maxTokens: 2000 }); // Add assistant response to history const assistantMessage = { role: 'assistant', content: response.content, timestamp: Date.now() }; conversation.messages.push(assistantMessage); // Update conversation metadata conversation.updatedAt = Date.now(); // Persist updated conversation await this.persistConversation(conversation); return response.content; } catch (error) { console.error('Error in conversation:', error); throw new Error(`Failed to get response: ${error.message}`); } } /** * Get conversation history */ getConversation(conversationId) { return this.conversations.get(conversationId); } /** * List all active conversations */ listConversations() { return Array.from(this.conversations.values()); } /** * Delete a conversation */ async deleteConversation(conversationId) { const conversation = this.conversations.get(conversationId); if (!conversation) { return false; } this.conversations.delete(conversationId); // Remove persisted conversation file try { const conversationPath = path.join(this.conversationStoragePath, `${conversationId}.json`); await this.fileService.deleteFile(conversationPath); } catch (error) { console.warn(`Failed to delete conversation file: ${error}`); } return true; } /** * Build contextual prompt with conversation history and project context */ buildContextualPrompt(conversation, currentMessage) { const { agent, messages, context } = conversation; let prompt = `You are ${agent.name}, ${agent.systemPrompt}\n\n`; // Add project context if available if (context.relevantFiles.length > 0) { prompt += `Project Context:\n`; prompt += `Working Directory: ${context.workingDirectory}\n`; prompt += `Relevant Files: ${context.relevantFiles.join(', ')}\n\n`; } // Add conversation history (last 10 messages to avoid token limits) const recentMessages = messages.slice(-10); if (recentMessages.length > 0) { prompt += `Conversation History:\n`; recentMessages.forEach(msg => { prompt += `${msg.role}: ${msg.content}\n`; }); prompt += `\n`; } prompt += `Current Message: ${currentMessage}\n\n`; prompt += `Please respond as ${agent.name} with your expertise in ${agent.expertise?.join(', ') || 'general topics'}.`; prompt += ` Use a ${agent.writingStyle} writing style with a ${agent.tone} tone.`; return prompt; } /** * Load project context from current working directory */ async loadProjectContext() { const workingDirectory = process.cwd(); try { // Get list of relevant files (markdown, text, code files) const relevantFiles = await this.findRelevantFiles(workingDirectory); return { workingDirectory, relevantFiles, projectMetadata: { analyzedAt: Date.now(), fileCount: relevantFiles.length }, lastAnalyzed: Date.now() }; } catch (error) { console.warn('Failed to load project context:', error); return { workingDirectory, relevantFiles: [], projectMetadata: {}, lastAnalyzed: Date.now() }; } } /** * Find relevant files in the project directory */ async findRelevantFiles(directory) { try { const files = await this.fileService.listFiles(directory); // Filter for relevant file types const relevantExtensions = ['.md', '.txt', '.js', '.ts', '.json', '.py', '.java', '.cpp', '.c', '.h']; const relevantFiles = files.filter(file => relevantExtensions.some(ext => file.endsWith(ext))).slice(0, 20); // Limit to first 20 files to avoid overwhelming context return relevantFiles; } catch (error) { console.warn('Failed to find relevant files:', error); return []; } } /** * Persist conversation to disk */ async persistConversation(conversation) { try { const conversationPath = path.join(this.conversationStoragePath, `${conversation.id}.json`); // Create a serializable version (remove LLM instance) const serializable = { ...conversation, llm: conversation.llm.constructor.name // Store just the provider name }; await this.fileService.saveFile({ path: conversationPath, content: JSON.stringify(serializable, null, 2) }); } catch (error) { console.warn('Failed to persist conversation:', error); } } /** * Load persisted conversations from disk */ async loadPersistedConversations() { try { const conversationFiles = await this.fileService.listFiles(this.conversationStoragePath); for (const file of conversationFiles) { if (file.endsWith('.json')) { try { const content = await this.fileService.readFile(file); const conversationData = JSON.parse(content); // Restore LLM provider const llm = LLMFactory.getConfiguredProvider(); if (llm) { conversationData.llm = llm; this.conversations.set(conversationData.id, conversationData); } } catch (error) { console.warn(`Failed to load conversation from ${file}:`, error); } } } } catch (error) { // Directory might not exist yet, that's okay console.debug('No persisted conversations found'); } } }