cleanup
This commit is contained in:
		
							parent
							
								
									1c0025796a
								
							
						
					
					
						commit
						b515a45e1e
					
				@ -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 `
 | 
			
		||||
      <div class="border-l-2 pl-3 py-2 mb-2" style="border-left-color: ${confidenceColor};">
 | 
			
		||||
        <div class="flex justify-between items-center mb-1">
 | 
			
		||||
 | 
			
		||||
@ -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}`);
 | 
			
		||||
 | 
			
		||||
@ -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';
 | 
			
		||||
 | 
			
		||||
@ -63,17 +63,15 @@ interface AnalysisContext {
 | 
			
		||||
  
 | 
			
		||||
  auditTrail: AuditEntry[];
 | 
			
		||||
  
 | 
			
		||||
  // Store actual similarity data from embeddings
 | 
			
		||||
  embeddingsSimilarities: Map<string, number>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<string, number>();
 | 
			
		||||
    
 | 
			
		||||
    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<MicroTaskResult> {
 | 
			
		||||
@ -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' : 
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ interface SimilarityResult extends EmbeddingData {
 | 
			
		||||
class EmbeddingsService {
 | 
			
		||||
  private embeddings: EmbeddingData[] = [];
 | 
			
		||||
  private isInitialized = false;
 | 
			
		||||
  private initializationPromise: Promise<void> | null = null; // ADD THIS LINE
 | 
			
		||||
  private initializationPromise: Promise<void> | 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<void> {
 | 
			
		||||
    // 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<void> {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user