// 🧠 THE BRAIN: NeuralCompiler.ts // Ansvarlig for at Γ¦de filer og gΓΈre dem til struktureret viden. // Renamed from KnowledgeCompiler to avoid conflict with existing service. import fs from 'fs/promises'; import path from 'path'; import crypto from 'crypto'; import chokidar from 'chokidar'; import { vectorService } from './VectorService'; // Conditional imports for optional services let neo4jService: any = null; let metricsService: any = null; export interface CompiledDocument { id: string; name: string; path: string; extension: string; size: number; content: string; embedding: number[]; lastModified: Date; tags: string[]; } export class NeuralCompiler { private static instance: NeuralCompiler; private watcher: chokidar.FSWatcher | null = null; private isProcessing = false; private documentCache = new Map(); private eventQueue: { type: string; path: string }[] = []; private processingInterval: NodeJS.Timeout | null = null; private constructor() { console.log('πŸ“š [KNOWLEDGE] Compiler Initialized'); this.initServices(); } private async initServices() { try { const neo4j = await import('./Neo4jService').catch(() => null); if (neo4j) neo4jService = neo4j.neo4jService; const metrics = await import('./MetricsService').catch(() => null); if (metrics) metricsService = metrics.metricsService; } catch { console.log('πŸ“š [KNOWLEDGE] Running in standalone mode'); } } public static getInstance(): NeuralCompiler { if (!NeuralCompiler.instance) { NeuralCompiler.instance = new NeuralCompiler(); } return NeuralCompiler.instance; } public async startWatching(dirPath: string): Promise { // Ensure directory exists await fs.mkdir(dirPath, { recursive: true }).catch(() => {}); console.log(`πŸ‘οΈ [KNOWLEDGE] Watching directory: ${dirPath}`); this.watcher = chokidar.watch(dirPath, { ignored: /(^|[\/\\])\../, // Ignore dotfiles persistent: true, depth: 5, awaitWriteFinish: { stabilityThreshold: 1000, pollInterval: 100 } }); this.watcher .on('add', p => this.queueEvent('ADD', p)) .on('change', p => this.queueEvent('MODIFY', p)) .on('unlink', p => this.queueEvent('DELETE', p)) .on('error', err => console.error('πŸ‘οΈ [KNOWLEDGE] Watcher error:', err)); // Process queue every 500ms to batch events this.processingInterval = setInterval(() => this.processQueue(), 500); } private queueEvent(type: string, filePath: string) { this.eventQueue.push({ type, path: filePath }); } private async processQueue() { if (this.isProcessing || this.eventQueue.length === 0) return; this.isProcessing = true; const events = [...this.eventQueue]; this.eventQueue = []; for (const event of events) { await this.handleFileEvent(event.type as 'ADD' | 'MODIFY' | 'DELETE', event.path); } this.isProcessing = false; } private async handleFileEvent( eventType: 'ADD' | 'MODIFY' | 'DELETE', filePath: string ): Promise { try { const fileId = this.generateFileId(filePath); const filename = path.basename(filePath); const ext = path.extname(filePath).toLowerCase(); // Skip unsupported files const supportedExts = ['.txt', '.md', '.json', '.ts', '.js', '.py', '.html', '.css', '.yaml', '.yml', '.xml', '.csv']; if (!supportedExts.includes(ext)) return; if (eventType === 'DELETE') { this.documentCache.delete(fileId); if (neo4jService) { await neo4jService.write( `MATCH (f:File {id: $id}) DETACH DELETE f`, { id: fileId } ); } console.log(`πŸ—‘οΈ [KNOWLEDGE] Forgot file: ${filename}`); return; } // 1. Read Content const content = await fs.readFile(filePath, 'utf-8'); // 2. Generate Vector Embedding (Cognitive Dark Matter) const textForEmbedding = content.substring(0, 2000); // Limit for speed const embedding = await vectorService.embedText(textForEmbedding); // 3. Create compiled document const doc: CompiledDocument = { id: fileId, name: filename, path: filePath, extension: ext, size: content.length, content: content.substring(0, 5000), // Store first 5KB embedding, lastModified: new Date(), tags: this.extractTags(content, ext) }; // 4. Cache locally this.documentCache.set(fileId, doc); // 5. Ingest into Neo4j if available if (neo4jService) { await neo4jService.write(` MERGE (f:File {id: $id}) SET f.name = $name, f.path = $path, f.extension = $ext, f.size = $size, f.lastModified = datetime(), f.contentPreview = $preview // Link to Directory MERGE (d:Directory {path: $dirPath}) MERGE (f)-[:LOCATED_IN]->(d) // Auto-Tagging based on extension MERGE (t:Tag {name: $ext}) MERGE (f)-[:TAGGED]->(t) `, { id: fileId, name: filename, path: filePath, dirPath: path.dirname(filePath), ext: ext, size: content.length, preview: content.substring(0, 500) }); } console.log(`✨ [KNOWLEDGE] Assimilated: ${filename} (${eventType})`); if (metricsService) { metricsService.incrementCounter('knowledge_files_ingested'); } } catch (error) { console.error(`πŸ“š [KNOWLEDGE] Error processing ${filePath}:`, error); } } private extractTags(content: string, ext: string): string[] { const tags: string[] = [ext]; // Extract hashtags const hashtags = content.match(/#\w+/g) || []; tags.push(...hashtags.map(t => t.toLowerCase())); // Detect language/framework if (content.includes('import React')) tags.push('react'); if (content.includes('from fastapi')) tags.push('fastapi'); if (content.includes('async function')) tags.push('async'); if (content.includes('class ')) tags.push('oop'); return [...new Set(tags)]; } private generateFileId(filePath: string): string { return crypto.createHash('md5').update(filePath).digest('hex'); } // Public API for querying public async searchSimilar(query: string, topK: number = 5): Promise { const items = Array.from(this.documentCache.values()).map(doc => ({ id: doc.id, embedding: doc.embedding })); const results = await vectorService.findSimilar(query, items, topK); return results .map(r => this.documentCache.get(r.id)) .filter((d): d is CompiledDocument => d !== undefined); } public getDocument(id: string): CompiledDocument | undefined { return this.documentCache.get(id); } public getAllDocuments(): CompiledDocument[] { return Array.from(this.documentCache.values()); } public getStats() { return { totalDocuments: this.documentCache.size, queueLength: this.eventQueue.length, isProcessing: this.isProcessing }; } public async stop(): Promise { if (this.watcher) { await this.watcher.close(); this.watcher = null; } if (this.processingInterval) { clearInterval(this.processingInterval); this.processingInterval = null; } console.log('πŸ“š [NEURAL] Compiler Stopped'); } } export const neuralCompiler = NeuralCompiler.getInstance();