From b515a45e1ec1ad29836ab6a65c38b86067a1c668 Mon Sep 17 00:00:00 2001 From: overcuriousity Date: Tue, 5 Aug 2025 22:09:46 +0200 Subject: [PATCH] cleanup --- src/components/AIQueryInterface.astro | 7 +-- src/config/prompts.ts | 8 ---- src/pages/index.astro | 7 --- src/utils/aiPipeline.ts | 65 ++++++++------------------- src/utils/embeddings.ts | 11 +---- src/utils/rateLimitedQueue.ts | 3 +- 6 files changed, 22 insertions(+), 79 deletions(-) diff --git a/src/components/AIQueryInterface.astro b/src/components/AIQueryInterface.astro index 68afb79..e8a7604 100644 --- a/src/components/AIQueryInterface.astro +++ b/src/components/AIQueryInterface.astro @@ -702,12 +702,10 @@ class AIQueryInterface { toolsByPhase[phase] = []; }); - // DEBUG: Log recommendation structure console.log('[AI Results] Recommendation structure:', recommendation); console.log('[AI Results] Recommended tools:', recommendation.recommended_tools); recommendation.recommended_tools?.forEach(recTool => { - // DEBUG: Log each tool's confidence data console.log('[AI Results] Tool confidence data:', recTool.name, recTool.confidence); if (toolsByPhase[recTool.phase]) { @@ -716,7 +714,7 @@ class AIQueryInterface { toolsByPhase[recTool.phase].push({ ...fullTool, recommendation: recTool, - confidence: recTool.confidence, // Ensure confidence is passed + confidence: recTool.confidence, justification: recTool.justification, priority: recTool.priority, recommendationStrength: recTool.recommendationStrength @@ -836,13 +834,11 @@ class AIQueryInterface { return ''; } - // Calculate summary statistics const totalTime = auditTrail.reduce((sum, entry) => sum + entry.processingTimeMs, 0); const avgConfidence = auditTrail.reduce((sum, entry) => sum + entry.confidence, 0) / auditTrail.length; const lowConfidenceSteps = auditTrail.filter(entry => entry.confidence < 60).length; const highConfidenceSteps = auditTrail.filter(entry => entry.confidence >= 80).length; - // Group entries by phase for better organization const groupedEntries = auditTrail.reduce((groups, entry) => { if (!groups[entry.phase]) groups[entry.phase] = []; groups[entry.phase].push(entry); @@ -1048,7 +1044,6 @@ class AIQueryInterface { second: '2-digit' }); - // Reuse existing grid and text utilities return `
diff --git a/src/config/prompts.ts b/src/config/prompts.ts index 2c880ee..86467b5 100644 --- a/src/config/prompts.ts +++ b/src/config/prompts.ts @@ -2,7 +2,6 @@ export const AI_PROMPTS = { - // Main tool selection prompt toolSelection: (mode: string, userQuery: string, selectionMethod: string, maxSelectedItems: number) => { const modeInstruction = mode === 'workflow' ? 'Der Benutzer möchte einen UMFASSENDEN WORKFLOW mit mehreren Tools/Methoden über verschiedene Phasen. Wählen Sie 15-25 Tools aus, die den vollständigen Untersuchungslebenszyklus abdecken.' @@ -51,7 +50,6 @@ Antworten Sie NUR mit diesem JSON-Format: }`; }, - // Scenario analysis prompt scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => { const analysisType = isWorkflow ? 'forensische Szenario' : 'technische Problem'; const considerations = isWorkflow ? @@ -74,7 +72,6 @@ ${considerations} WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen, Aufzählungen oder Markdown-Formatierung. Maximum 150 Wörter.`; }, - // Investigation approach prompt investigationApproach: (isWorkflow: boolean, userQuery: string) => { const approachType = isWorkflow ? 'Untersuchungsansatz' : 'Lösungsansatz'; const considerations = isWorkflow ? @@ -96,7 +93,6 @@ ${considerations} WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdown. Maximum 150 Wörter.`; }, - // Critical considerations prompt criticalConsiderations: (isWorkflow: boolean, userQuery: string) => { const considerationType = isWorkflow ? 'kritische forensische Überlegungen' : 'wichtige methodische Voraussetzungen'; const aspects = isWorkflow ? @@ -179,7 +175,6 @@ WICHTIG: - "pros" soll die Stärken für diese spezifische Aufgabe hervorheben`; }, - // Background knowledge selection prompt backgroundKnowledgeSelection: (userQuery: string, mode: string, selectedToolNames: string[], availableConcepts: any[]) => { return `Wählen Sie relevante forensische Konzepte für das Verständnis der empfohlenen Methodik. @@ -200,7 +195,6 @@ Antworten Sie AUSSCHLIESSLICH mit diesem JSON-Format: ]`; }, - // Final recommendations prompt finalRecommendations: (isWorkflow: boolean, userQuery: string, selectedToolNames: string[]) => { const prompt = isWorkflow ? `Erstellen Sie eine Workflow-Empfehlung basierend auf DFIR-Prinzipien. @@ -225,7 +219,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo } } as const; -// Type-safe prompt function with proper overloads export function getPrompt(key: 'toolSelection', mode: string, userQuery: string, selectionMethod: string, maxSelectedItems: number): string; export function getPrompt(key: 'scenarioAnalysis', isWorkflow: boolean, userQuery: string): string; export function getPrompt(key: 'investigationApproach', isWorkflow: boolean, userQuery: string): string; @@ -238,7 +231,6 @@ export function getPrompt(promptKey: keyof typeof AI_PROMPTS, ...args: any[]): s try { const promptFunction = AI_PROMPTS[promptKey]; if (typeof promptFunction === 'function') { - // Use type assertion since we've validated the function exists return (promptFunction as (...args: any[]) => string)(...args); } else { console.error(`[PROMPTS] Invalid prompt key: ${promptKey}`); diff --git a/src/pages/index.astro b/src/pages/index.astro index 2eb81c2..b5c8c35 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -265,20 +265,15 @@ const phases = data.phases; return; } - // AI Button click handler if (aiQueryBtn) { aiQueryBtn.addEventListener('click', () => { - // Visual feedback aiQueryBtn.classList.add('activated'); setTimeout(() => aiQueryBtn.classList.remove('activated'), 400); - // Switch to AI view switchToView('ai'); - // Trigger existing view change system window.dispatchEvent(new CustomEvent('viewChanged', { detail: 'ai' })); - // Scroll to AI interface if (window.scrollToElementById) { window.scrollToElementById('ai-interface'); } else { @@ -294,14 +289,12 @@ const phases = data.phases; const filtersSection = document.getElementById('filters-section'); const noResults = document.getElementById('no-results'); - // Hide all views first if (toolsGrid) toolsGrid.style.display = 'none'; if (matrixContainer) matrixContainer.style.display = 'none'; if (aiInterface) aiInterface.style.display = 'none'; if (filtersSection) filtersSection.style.display = 'none'; if (noResults) noResults.style.display = 'none'; - // Show selected view switch (view) { case 'grid': if (toolsGrid) toolsGrid.style.display = 'block'; diff --git a/src/utils/aiPipeline.ts b/src/utils/aiPipeline.ts index 34a94a5..c1583c0 100644 --- a/src/utils/aiPipeline.ts +++ b/src/utils/aiPipeline.ts @@ -63,17 +63,15 @@ interface AnalysisContext { auditTrail: AuditEntry[]; - // Store actual similarity data from embeddings embeddingsSimilarities: Map; } interface ConfidenceMetrics { - overall: number; // 0-100: Combined confidence score - semanticRelevance: number; // How well tool description matches query (from embeddings) - taskSuitability: number; // AI-determined fitness for this specific task - methodologicalConsistency: number; // How well different analysis steps agree - uncertaintyFactors: string[]; // Specific reasons why this might not work - strengthIndicators: string[]; // Specific reasons why this is a good choice + overall: number; + semanticRelevance: number; + taskSuitability: number; + uncertaintyFactors: string[]; + strengthIndicators: string[]; } class ImprovedMicroTaskAIPipeline { @@ -102,10 +100,10 @@ class ImprovedMicroTaskAIPipeline { }; private confidenceConfig: { - semanticWeight: number; // Weight for embeddings similarity - suitabilityWeight: number; // Weight for AI task fit evaluation - consistencyWeight: number; // Weight for cross-validation agreement - reliabilityWeight: number; // Weight for tool quality indicators + semanticWeight: number; + suitabilityWeight: number; + consistencyWeight: number; + reliabilityWeight: number; minimumThreshold: number; mediumThreshold: number; highThreshold: number; @@ -143,10 +141,9 @@ class ImprovedMicroTaskAIPipeline { retentionHours: parseInt(process.env.FORENSIC_AUDIT_RETENTION_HOURS || '72', 10) }; - // Updated confidence weights - more focused on AI evaluation this.confidenceConfig = { - semanticWeight: parseFloat(process.env.CONFIDENCE_SEMANTIC_WEIGHT || '0.3'), // Embeddings similarity - suitabilityWeight: parseFloat(process.env.CONFIDENCE_SUITABILITY_WEIGHT || '0.7'), // AI task fit evaluation + semanticWeight: parseFloat(process.env.CONFIDENCE_SEMANTIC_WEIGHT || '0.3'), + suitabilityWeight: parseFloat(process.env.CONFIDENCE_SUITABILITY_WEIGHT || '0.7'), consistencyWeight: 0, reliabilityWeight: 0, minimumThreshold: parseInt(process.env.CONFIDENCE_MINIMUM_THRESHOLD || '40', 10), @@ -235,7 +232,7 @@ class ImprovedMicroTaskAIPipeline { const selectionRatio = result.selectedTools.length / candidateCount; const hasReasoning = result.reasoning && result.reasoning.length > 50; - let confidence = 60; // Base confidence + let confidence = 60; if (selectionRatio > 0.05 && selectionRatio < 0.3) confidence += 20; else if (selectionRatio <= 0.05) confidence -= 10; @@ -386,7 +383,6 @@ class ImprovedMicroTaskAIPipeline { let candidateConcepts: any[] = []; let selectionMethod = 'unknown'; - // Initialize embeddings similarities storage context.embeddingsSimilarities = new Map(); if (process.env.AI_EMBEDDINGS_ENABLED === 'true') { @@ -409,7 +405,6 @@ class ImprovedMicroTaskAIPipeline { console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`); - // Store actual similarity scores for confidence calculation similarItems.forEach(item => { context.embeddingsSimilarities.set(item.name, item.similarity); }); @@ -707,18 +702,14 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; limitations: string[] = [] ): ConfidenceMetrics { - // 1. Semantic Relevance: Real embeddings similarity score const rawSemanticRelevance = context.embeddingsSimilarities.has(tool.name) ? context.embeddingsSimilarities.get(tool.name)! * 100 : 50; - // 2. Task Suitability: Enhanced with phase-awareness for workflow mode let enhancedTaskSuitability = taskRelevance; if (context.mode === 'workflow') { - // In workflow mode, boost score if tool is well-matched to its assigned phase const toolSelection = context.selectedTools?.find(st => st.tool.name === tool.name); if (toolSelection && tool.phases && tool.phases.includes(toolSelection.phase)) { - // Boost for phase alignment (but cap at 100) const phaseBonus = Math.min(15, 100 - taskRelevance); enhancedTaskSuitability = Math.min(100, taskRelevance + phaseBonus); @@ -726,7 +717,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; } } - // Simple weighted combination - no artificial scaling const overall = ( rawSemanticRelevance * this.confidenceConfig.semanticWeight + enhancedTaskSuitability * this.confidenceConfig.suitabilityWeight @@ -747,7 +737,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; overall: Math.round(overall), semanticRelevance: Math.round(rawSemanticRelevance), taskSuitability: Math.round(enhancedTaskSuitability), - methodologicalConsistency: 0, uncertaintyFactors, strengthIndicators }; @@ -756,18 +745,15 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; private identifySpecificUncertaintyFactors(tool: any, context: AnalysisContext, limitations: string[], confidence: number): string[] { const factors: string[] = []; - // Add AI-identified limitations first (most specific) if (limitations && limitations.length > 0) { - factors.push(...limitations.slice(0, 2)); // Limit to top 2 to leave room for others + factors.push(...limitations.slice(0, 2)); } - // Low semantic similarity const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5; if (similarity < 0.7) { factors.push('Geringe semantische Ähnlichkeit zur Anfrage - Tool-Beschreibung passt möglicherweise nicht optimal'); } - // Skill level vs scenario complexity mismatch if (tool.skillLevel === 'expert' && /schnell|rapid|triage|urgent|sofort/i.test(context.userQuery)) { factors.push('Experten-Tool für zeitkritisches Szenario - Setup und Einarbeitung könnten zu lange dauern'); } @@ -776,35 +762,29 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; factors.push('Einsteiger-Tool für komplexe Analyse - könnte funktionale Limitierungen haben'); } - // Access and deployment concerns if (tool.type === 'software' && !isToolHosted(tool) && tool.accessType === 'download') { factors.push('Installation und Setup erforderlich'); } - // License restrictions if (tool.license === 'Proprietary') { factors.push('Kommerzielle Software - Lizenzkosten und rechtliche Beschränkungen zu beachten'); } - // Low overall confidence warning if (confidence < 60) { factors.push('Moderate Gesamtbewertung - alternative Ansätze sollten ebenfalls betrachtet werden'); } - return factors.slice(0, 4); // Limit to 4 most relevant factors + return factors.slice(0, 4); } - // NEW: Identify specific strength indicators private identifySpecificStrengthIndicators(tool: any, context: AnalysisContext, confidence: number): string[] { const indicators: string[] = []; - // High semantic similarity const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5; if (similarity >= 0.7) { indicators.push('Sehr gute semantische Übereinstimmung mit Ihrer Anfrage'); } - // Quality indicators if (tool.knowledgebase === true) { indicators.push('Umfassende Dokumentation und Wissensbasis verfügbar'); } @@ -813,17 +793,15 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; indicators.push('Sofort verfügbar über gehostete Lösung - kein Setup erforderlich'); } - // Skill level match if (tool.skillLevel === 'intermediate' || tool.skillLevel === 'advanced') { indicators.push('Ausgewogenes Verhältnis zwischen Funktionalität und Benutzerfreundlichkeit'); } - // Method alignment if (tool.type === 'method' && /methodik|vorgehen|prozess|ansatz/i.test(context.userQuery)) { indicators.push('Methodischer Ansatz passt zu Ihrer prozeduralen Anfrage'); } - return indicators.slice(0, 4); // Limit to 4 most important indicators + return indicators.slice(0, 4); } private async analyzeScenario(context: AnalysisContext): Promise { @@ -902,11 +880,9 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; validSelections.forEach((sel: any) => { const tool = phaseTools.find((t: any) => t.name === sel.toolName); if (tool) { - // Ensure taskRelevance is a number const taskRelevance = typeof sel.taskRelevance === 'number' ? sel.taskRelevance : parseInt(String(sel.taskRelevance)) || 70; - // Derive priority automatically from score const priority = this.derivePriorityFromScore(taskRelevance); this.addToolToSelection(context, tool, phase.id, priority, sel.justification, taskRelevance, sel.limitations); @@ -967,7 +943,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; hasExplanation: !!evaluation.detailed_explanation, hasImplementationApproach: !!evaluation.implementation_approach, prosCount: evaluation.pros?.length || 0, - limitationsCount: evaluation.limitations?.length || 0, // ← Updated field name + limitationsCount: evaluation.limitations?.length || 0, hasLimitations: Array.isArray(evaluation.limitations) && evaluation.limitations.length > 0 }, 70, @@ -1101,7 +1077,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; const context: AnalysisContext = { userQuery, mode, - filteredData: {}, // Will be populated by getIntelligentCandidates + filteredData: {}, contextHistory: [], maxContextLength: this.maxContextTokens, currentContextLength: 0, @@ -1124,9 +1100,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; startTime, { auditEnabled: this.auditConfig.enabled, confidenceScoringEnabled: true } ); - - // MICRO-TASK SEQUENCE WITH ENHANCED CONFIDENCE TRACKING - + const analysisResult = await this.analyzeScenario(context); if (analysisResult.success) completeTasks++; else failedTasks++; await this.delay(this.microTaskDelay); @@ -1234,7 +1208,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; components: { semantic: confidence.semanticRelevance, suitability: confidence.taskSuitability, - consistency: confidence.methodologicalConsistency } }, confidence.overall, @@ -1286,7 +1259,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`; detailed_explanation: st.tool.evaluation?.detailed_explanation || '', implementation_approach: st.tool.evaluation?.implementation_approach || '', pros: st.tool.evaluation?.pros || [], - cons: st.tool.evaluation?.limitations || [], // ← FIXED: Use limitations as cons for display + cons: st.tool.evaluation?.limitations || [], alternatives: st.tool.evaluation?.alternatives || '', confidence: confidence, recommendationStrength: confidence.overall >= this.confidenceConfig.highThreshold ? 'strong' : diff --git a/src/utils/embeddings.ts b/src/utils/embeddings.ts index d70d0ca..165fb90 100644 --- a/src/utils/embeddings.ts +++ b/src/utils/embeddings.ts @@ -31,7 +31,7 @@ interface SimilarityResult extends EmbeddingData { class EmbeddingsService { private embeddings: EmbeddingData[] = []; private isInitialized = false; - private initializationPromise: Promise | null = null; // ADD THIS LINE + private initializationPromise: Promise | null = null; private readonly embeddingsPath = path.join(process.cwd(), 'data', 'embeddings.json'); private readonly batchSize: number; private readonly batchDelay: number; @@ -43,24 +43,19 @@ class EmbeddingsService { this.batchDelay = parseInt(process.env.AI_EMBEDDINGS_BATCH_DELAY_MS || '1000', 10); } - // REPLACE the existing initialize method with this: async initialize(): Promise { - // If initialization is already in progress, wait for it if (this.initializationPromise) { return this.initializationPromise; } - // If already initialized, return immediately if (this.isInitialized) { return Promise.resolve(); } - // Start initialization and store the promise this.initializationPromise = this.performInitialization(); return this.initializationPromise; } - // ADD THIS NEW METHOD: private async performInitialization(): Promise { if (!this.enabled) { console.log('[EMBEDDINGS] Embeddings disabled, skipping initialization'); @@ -70,13 +65,11 @@ class EmbeddingsService { try { console.log('[EMBEDDINGS] Initializing embeddings system...'); - // Create data directory if it doesn't exist await fs.mkdir(path.dirname(this.embeddingsPath), { recursive: true }); const toolsData = await getCompressedToolsDataForAI(); const currentDataHash = this.hashData(toolsData); - // Try to load existing embeddings const existingEmbeddings = await this.loadEmbeddings(); if (existingEmbeddings && existingEmbeddings.version === currentDataHash) { @@ -336,12 +329,10 @@ class EmbeddingsService { -// Global instance const embeddingsService = new EmbeddingsService(); export { embeddingsService, type EmbeddingData, type SimilarityResult }; -// Auto-initialize on import in server environment if (typeof window === 'undefined' && process.env.NODE_ENV !== 'test') { embeddingsService.initialize().catch(error => { console.error('[EMBEDDINGS] Auto-initialization failed:', error); diff --git a/src/utils/rateLimitedQueue.ts b/src/utils/rateLimitedQueue.ts index 18286f8..d2f078c 100644 --- a/src/utils/rateLimitedQueue.ts +++ b/src/utils/rateLimitedQueue.ts @@ -96,7 +96,6 @@ class RateLimitedQueue { this.tasks.push(queuedTask); - // Kick the processor soon. setTimeout(() => { this.processQueue(); }, 100); @@ -170,7 +169,7 @@ class RateLimitedQueue { .filter((t) => t.status === "queued") .sort((a, b) => a.addedAt - b.addedAt)[0]; - if (!nextTask) break; // No more work + if (!nextTask) break; nextTask.status = "processing"; nextTask.startedAt = Date.now();