UNPKG

contaigents

Version:

Modular AI Content Ecosystem with Audio Generation

218 lines (217 loc) 7.77 kB
import { FileService } from '../services/fileService.js'; import crypto from 'crypto'; /** * Local knowledge base for storing and retrieving project-specific learning */ export class LocalKnowledgeBase { constructor() { this.knowledgeDir = './.contaigents/knowledge'; this.knowledgeCache = new Map(); this.fileService = new FileService(); } /** * Store new knowledge */ async storeKnowledge(type, content, source, confidence = 0.8) { const knowledge = { id: crypto.randomUUID(), type, content, confidence, usage: 0, lastUsed: Date.now(), source }; await this.saveKnowledge(knowledge); this.invalidateCache(); return knowledge.id; } /** * Retrieve knowledge by type */ async getKnowledgeByType(type) { const allKnowledge = await this.loadAllKnowledge(); return allKnowledge.filter(k => k.type === type); } /** * Search knowledge by content */ async searchKnowledge(query, limit = 10) { const allKnowledge = await this.loadAllKnowledge(); const queryLower = query.toLowerCase(); return allKnowledge .filter(k => k.content.toLowerCase().includes(queryLower)) .sort((a, b) => b.confidence * b.usage - a.confidence * a.usage) .slice(0, limit); } /** * Get relevant knowledge for context */ async getRelevantKnowledge(context, limit = 5) { const allKnowledge = await this.loadAllKnowledge(); const projectType = context.metadata.type; // Score knowledge based on relevance to current context const scoredKnowledge = allKnowledge.map(k => ({ knowledge: k, score: this.calculateRelevanceScore(k, context) })); return scoredKnowledge .sort((a, b) => b.score - a.score) .slice(0, limit) .map(item => item.knowledge); } /** * Update knowledge usage */ async useKnowledge(knowledgeId) { const knowledge = await this.getKnowledgeById(knowledgeId); if (knowledge) { knowledge.usage++; knowledge.lastUsed = Date.now(); await this.saveKnowledge(knowledge); this.invalidateCache(); } } /** * Update knowledge confidence */ async updateConfidence(knowledgeId, newConfidence) { const knowledge = await this.getKnowledgeById(knowledgeId); if (knowledge) { knowledge.confidence = Math.max(0, Math.min(1, newConfidence)); await this.saveKnowledge(knowledge); this.invalidateCache(); } } /** * Learn from successful interactions */ async learnFromSuccess(action, context, result) { const pattern = `Successful ${action} in ${context.metadata.type} project`; const content = `Action: ${action}\nContext: ${JSON.stringify(context.metadata)}\nResult: ${JSON.stringify(result)}`; await this.storeKnowledge('pattern', content, 'interaction', 0.7); } /** * Learn from failures */ async learnFromFailure(action, context, error) { const rule = `Avoid ${action} in ${context.metadata.type} project when ${error}`; const content = `Failed Action: ${action}\nContext: ${JSON.stringify(context.metadata)}\nError: ${error}`; await this.storeKnowledge('rule', content, 'error', 0.6); } /** * Get knowledge statistics */ async getKnowledgeStats() { const allKnowledge = await this.loadAllKnowledge(); const byType = {}; let totalConfidence = 0; allKnowledge.forEach(k => { byType[k.type] = (byType[k.type] || 0) + 1; totalConfidence += k.confidence; }); const mostUsed = allKnowledge .sort((a, b) => b.usage - a.usage) .slice(0, 5); return { total: allKnowledge.length, byType, averageConfidence: allKnowledge.length > 0 ? totalConfidence / allKnowledge.length : 0, mostUsed }; } /** * Clean up low-confidence, unused knowledge */ async cleanupKnowledge() { const allKnowledge = await this.loadAllKnowledge(); const threshold = Date.now() - (30 * 24 * 60 * 60 * 1000); // 30 days ago const toRemove = allKnowledge.filter(k => k.confidence < 0.3 && k.usage < 2 && k.lastUsed < threshold); for (const knowledge of toRemove) { await this.deleteKnowledge(knowledge.id); } this.invalidateCache(); return toRemove.length; } /** * Private helper methods */ async loadAllKnowledge() { const cacheKey = 'all'; if (this.knowledgeCache.has(cacheKey)) { return this.knowledgeCache.get(cacheKey); } try { const tree = await this.fileService.getFileTree(); const knowledgeFiles = this.findKnowledgeFiles(tree); const allKnowledge = []; for (const file of knowledgeFiles) { try { const content = await this.fileService.readFile(file.path); const knowledge = JSON.parse(content); allKnowledge.push(knowledge); } catch (error) { console.warn(`Failed to load knowledge from ${file.path}`); } } this.knowledgeCache.set(cacheKey, allKnowledge); return allKnowledge; } catch (error) { console.error('Failed to load knowledge base:', error); return []; } } async getKnowledgeById(id) { const allKnowledge = await this.loadAllKnowledge(); return allKnowledge.find(k => k.id === id) || null; } async saveKnowledge(knowledge) { const filePath = `${this.knowledgeDir}/${knowledge.id}.json`; await this.fileService.saveFile({ path: filePath, content: JSON.stringify(knowledge, null, 2) }); } async deleteKnowledge(id) { // Note: FileService doesn't have delete method, would need to implement console.log(`Would delete knowledge ${id}`); } findKnowledgeFiles(tree) { const files = []; const traverse = (node) => { if (node.type === 'file' && node.path.includes('.contaigents/knowledge') && node.path.endsWith('.json')) { files.push({ path: node.path }); } if (node.children) { node.children.forEach(traverse); } }; if (Array.isArray(tree)) { tree.forEach(traverse); } else { traverse(tree); } return files; } calculateRelevanceScore(knowledge, context) { let score = knowledge.confidence * 0.4 + (knowledge.usage / 10) * 0.3; // Boost score if knowledge content matches project context const contextStr = JSON.stringify(context.metadata).toLowerCase(); const knowledgeStr = knowledge.content.toLowerCase(); if (knowledgeStr.includes(context.metadata.type)) score += 0.2; if (context.metadata.targetAudience?.some(audience => knowledgeStr.includes(audience))) score += 0.1; // Penalize very old knowledge const daysSinceUsed = (Date.now() - knowledge.lastUsed) / (24 * 60 * 60 * 1000); if (daysSinceUsed > 30) score *= 0.8; return Math.min(1, score); } invalidateCache() { this.knowledgeCache.clear(); } }