import { neo4jService } from '../database/Neo4jService.js'; import { neuralStream, NeuralEvent } from './NeuralStream.js'; import { metricsService } from './MetricsService.js'; export interface KnowledgePacket { title: string; content: string; source: string; category: string; tags: string[]; } class KnowledgeAcquisitionService { private static instance: KnowledgeAcquisitionService; private constructor() { console.log('🧠 [KnowledgeAcquisition] Cortex Ingestor Online.'); this.setupNeuralListeners(); } public static getInstance(): KnowledgeAcquisitionService { if (!KnowledgeAcquisitionService.instance) { KnowledgeAcquisitionService.instance = new KnowledgeAcquisitionService(); } return KnowledgeAcquisitionService.instance; } /** * Lytter pĂ„ nervesystemet efter ny viden fra OmniHarvester eller andre agenter */ private setupNeuralListeners() { // NĂ„r OmniHarvester har fundet noget (SYSTEM_HEALED med context 'ActiveLearning') // eller nĂ„r vi manuelt pusher viden. neuralStream.on('KNOWLEDGE_INGEST_REQ', async (event: NeuralEvent) => { console.log('đŸ“„ [Acquisition] Receiving knowledge stream...', event.payload.title); await this.ingestKnowledge(event.payload as unknown as KnowledgePacket); }); } /** * KERNE-LOGIK: Konverterer rĂ„ tekst til Graf-Struktur * Dette er "Memory Consolidation" processen. */ public async ingestKnowledge(packet: KnowledgePacket): Promise { const startTime = Date.now(); try { // 1. Opret selve videns-noden (Fact / Document) // Vi bruger MERGE for at undgĂ„ duplikater baseret pĂ„ titel/source const cypher = ` MERGE (k:Knowledge {title: $title}) SET k.content = $content, k.source = $source, k.category = $category, k.ingestedAt = datetime(), k.hash = $hash // 2. Opret kategori-struktur MERGE (c:Category {name: $category}) MERGE (k)-[:BELONGS_TO]->(c) // 3. Auto-Tagging (Opretter tags og linker dem) FOREACH (tagName IN $tags | MERGE (t:Tag {name: tagName}) MERGE (k)-[:TAGGED_WITH]->(t) ) `; // Simpel hash for integritet const hash = Buffer.from(packet.title + packet.source).toString('base64'); await neo4jService.runQuery(cypher, { title: packet.title, content: packet.content, source: packet.source, category: packet.category || 'General', tags: packet.tags || [], hash }); // 4. THE MAGIC: Semantic Auto-Linking // Vi sĂžger efter eksisterende noder, der nĂŠvnes i den nye tekst, og linker dem. await this.createSemanticLinks(packet.title, packet.content); // 5. Metrics & Feedback const duration = Date.now() - startTime; metricsService.incrementCounter('knowledge_ingested'); console.log(`✅ [Acquisition] Absorbed: "${packet.title}" in ${duration}ms`); neuralStream.emitEvent('SYSTEM_HEALED', 'LOW', { action: 'MEMORY_CONSOLIDATED', target: packet.title }, 'KnowledgeAcquisition'); return true; } catch (error) { console.error('❌ [Acquisition] Failed to ingest:', error); return false; } } /** * Finder andre noder i grafen, der nĂŠvnes i denne tekst, og skaber relationer. * Dette gĂžr grafen "tĂŠttere" og klogere over tid. */ private async createSemanticLinks(nodeTitle: string, content: string) { // Find noder (Personer, Tech, Threats) hvis navne optrĂŠder i den nye tekst // UndgĂ„ at linke til sig selv const linkCypher = ` MATCH (k:Knowledge {title: $title}) MATCH (other) WHERE other <> k AND (other:Person OR other:Technology OR other:Threat OR other:Organization) AND size(other.name) > 3 // Ignorer stĂžj AND $content CONTAINS other.name MERGE (k)-[:MENTIONS]->(other) RETURN count(other) as links `; const result = await neo4jService.runQuery(linkCypher, { title: nodeTitle, content: content }); const linksCreated = (result[0]?.links as any)?.toNumber?.() || 0; if (linksCreated > 0) { console.log(`🔗 [Acquisition] Auto-linked "${nodeTitle}" to ${linksCreated} existing concepts.`); } } // Stubs for backward compatibility public async acquire(params: any): Promise { return { success: false, message: 'Deprecated' }; } public async batchAcquire(params: any): Promise { return []; } public async semanticSearch(query: string, limit: number): Promise { return []; } public async getVectorStats(): Promise { return { totalRecords: 0 }; } public async acquireFromTargets(ids?: string[]): Promise { return []; } public async acquireSingleTarget(id: string): Promise { return null; } } export const knowledgeAcquisition = KnowledgeAcquisitionService.getInstance();