diff --git a/src/config/prompts.ts b/src/config/prompts.ts index a75df21..14af010 100644 --- a/src/config/prompts.ts +++ b/src/config/prompts.ts @@ -50,6 +50,18 @@ ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT: }`; }, + toolSelectionWithData: (basePrompt: string, toolsToSend: any[], conceptsToSend: any[]) => { + return `${basePrompt} + +VERFÜGBARE TOOLS (${toolsToSend.length} Items - Methoden und Software): +${JSON.stringify(toolsToSend, null, 2)} + +VERFÜGBARE KONZEPTE (${conceptsToSend.length} Items - theoretisches Wissen): +${JSON.stringify(conceptsToSend, null, 2)} + +WICHTIGER HINWEIS: Wähle sowohl aus TOOLS als auch aus KONZEPTEN aus! Konzepte sind essentiell für methodische Fundierung.`; + }, + scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => { const analysisType = isWorkflow ? 'Szenario' : 'Problem'; const focus = isWorkflow ? @@ -148,6 +160,7 @@ ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB: } ]`; }, + toolEvaluation: (userQuery: string, tool: any, rank: number, taskRelevance: number) => { const itemType = tool.type === 'method' ? 'Methode' : 'Tool'; @@ -247,6 +260,7 @@ Antwort: Fließtext ohne Listen, max ${isWorkflow ? '100' : '80'} Wörter.`; } as const; export function getPrompt(key: 'toolSelection', mode: string, userQuery: string, selectionMethod: string, maxSelectedItems: number): string; +export function getPrompt(key: 'toolSelectionWithData', basePrompt: string, toolsToSend: any[], conceptsToSend: any[]): string; export function getPrompt(key: 'scenarioAnalysis', isWorkflow: boolean, userQuery: string): string; export function getPrompt(key: 'investigationApproach', isWorkflow: boolean, userQuery: string): string; export function getPrompt(key: 'criticalConsiderations', isWorkflow: boolean, userQuery: string): string; diff --git a/src/utils/aiPipeline.ts b/src/utils/aiPipeline.ts index 2ae0fd4..2b35889 100644 --- a/src/utils/aiPipeline.ts +++ b/src/utils/aiPipeline.ts @@ -1,12 +1,12 @@ -// src/utils/aiPipeline.ts +// src/utils/aiPipeline.ts import { getCompressedToolsDataForAI } from './dataService.js'; import { embeddingsService, type EmbeddingData, type SimilarityResult } from './embeddings.js'; import { AI_PROMPTS, getPrompt } from '../config/prompts.js'; import { isToolHosted } from './toolHelpers.js'; -import { configDotenv } from 'dotenv'; +import dotenv from 'dotenv'; -configDotenv() +dotenv.config(); interface AIConfig { endpoint: string; @@ -37,11 +37,11 @@ interface AnalysisResult { interface AuditEntry { timestamp: number; - phase: string; - action: string; - input: any; - output: any; - confidence: number; + phase: string; + action: string; + input: any; + output: any; + confidence: number; processingTimeMs: number; metadata: Record; } @@ -51,32 +51,37 @@ interface AnalysisContext { mode: string; filteredData: any; contextHistory: string[]; - maxContextLength: number; currentContextLength: number; - scenarioAnalysis?: string; problemAnalysis?: string; investigationApproach?: string; criticalConsiderations?: string; - selectedTools?: Array<{tool: any, phase: string, priority: string, justification?: string, taskRelevance?: number, limitations?: string[]}>; - backgroundKnowledge?: Array<{concept: any, relevance: string}>; - + selectedTools?: Array<{ + tool: any; + phase: string; + priority: string; + justification?: string; + taskRelevance?: number; + limitations?: string[]; + }>; + backgroundKnowledge?: Array<{ + concept: any; + relevance: string; + }>; seenToolNames: Set; - auditTrail: AuditEntry[]; - embeddingsSimilarities: Map; aiSelectedTools?: any[]; aiSelectedConcepts?: any[]; } interface ConfidenceMetrics { - overall: number; - semanticRelevance: number; - taskSuitability: number; - uncertaintyFactors: string[]; - strengthIndicators: string[]; + overall: number; + semanticRelevance: number; + taskSuitability: number; + uncertaintyFactors: string[]; + strengthIndicators: string[]; } class ImprovedMicroTaskAIPipeline { @@ -85,109 +90,90 @@ class ImprovedMicroTaskAIPipeline { private embeddingCandidates: number; private similarityThreshold: number; private microTaskDelay: number; - private embeddingSelectionLimit: number; private embeddingConceptsLimit: number; - private noEmbeddingsToolLimit: number; private noEmbeddingsConceptLimit: number; - private embeddingsMinTools: number; private embeddingsMaxReductionRatio: number; - private methodSelectionRatio: number; private softwareSelectionRatio: number; - private maxContextTokens: number; private maxPromptTokens: number; - private auditConfig: { enabled: boolean; detailLevel: string; }; - private tempAuditEntries: AuditEntry[] = []; - private confidenceConfig: { - semanticWeight: number; - suitabilityWeight: number; - consistencyWeight: number; - reliabilityWeight: number; + semanticWeight: number; + suitabilityWeight: number; minimumThreshold: number; mediumThreshold: number; highThreshold: number; }; - + constructor() { this.config = { - endpoint: this.getEnv('AI_ANALYZER_ENDPOINT'), - apiKey: this.getEnv('AI_ANALYZER_API_KEY'), - model: this.getEnv('AI_ANALYZER_MODEL') + endpoint: this.getRequiredEnv('AI_ANALYZER_ENDPOINT'), + apiKey: this.getRequiredEnv('AI_ANALYZER_API_KEY'), + model: this.getRequiredEnv('AI_ANALYZER_MODEL') }; - this.maxSelectedItems = parseInt(process.env.AI_MAX_SELECTED_ITEMS || '25', 10); - this.embeddingCandidates = parseInt(process.env.AI_EMBEDDING_CANDIDATES || '50', 10); - this.similarityThreshold = parseFloat(process.env.AI_SIMILARITY_THRESHOLD || '0.3'); - this.microTaskDelay = parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10); - - this.embeddingSelectionLimit = parseInt(process.env.AI_EMBEDDING_SELECTION_LIMIT || '30', 10); - this.embeddingConceptsLimit = parseInt(process.env.AI_EMBEDDING_CONCEPTS_LIMIT || '15', 10); + this.maxSelectedItems = this.getEnvInt('AI_MAX_SELECTED_ITEMS', 25); + this.embeddingCandidates = this.getEnvInt('AI_EMBEDDING_CANDIDATES', 50); + this.similarityThreshold = this.getEnvFloat('AI_SIMILARITY_THRESHOLD', 0.3); + this.microTaskDelay = this.getEnvInt('AI_MICRO_TASK_DELAY_MS', 500); + this.embeddingSelectionLimit = this.getEnvInt('AI_EMBEDDING_SELECTION_LIMIT', 30); + this.embeddingConceptsLimit = this.getEnvInt('AI_EMBEDDING_CONCEPTS_LIMIT', 15); + this.noEmbeddingsToolLimit = this.getEnvInt('AI_NO_EMBEDDINGS_TOOL_LIMIT', 25); + this.noEmbeddingsConceptLimit = this.getEnvInt('AI_NO_EMBEDDINGS_CONCEPT_LIMIT', 10); + this.embeddingsMinTools = this.getEnvInt('AI_EMBEDDINGS_MIN_TOOLS', 8); + this.embeddingsMaxReductionRatio = this.getEnvFloat('AI_EMBEDDINGS_MAX_REDUCTION_RATIO', 0.75); + this.methodSelectionRatio = this.getEnvFloat('AI_METHOD_SELECTION_RATIO', 0.4); + this.softwareSelectionRatio = this.getEnvFloat('AI_SOFTWARE_SELECTION_RATIO', 0.5); + this.maxContextTokens = this.getEnvInt('AI_MAX_CONTEXT_TOKENS', 4000); + this.maxPromptTokens = this.getEnvInt('AI_MAX_PROMPT_TOKENS', 1500); - this.noEmbeddingsToolLimit = parseInt(process.env.AI_NO_EMBEDDINGS_TOOL_LIMIT || '25', 10); - this.noEmbeddingsConceptLimit = parseInt(process.env.AI_NO_EMBEDDINGS_CONCEPT_LIMIT || '10', 10); - - this.embeddingsMinTools = parseInt(process.env.AI_EMBEDDINGS_MIN_TOOLS || '8', 10); - this.embeddingsMaxReductionRatio = parseFloat(process.env.AI_EMBEDDINGS_MAX_REDUCTION_RATIO || '0.75'); - - this.methodSelectionRatio = parseFloat(process.env.AI_METHOD_SELECTION_RATIO || '0.4'); - this.softwareSelectionRatio = parseFloat(process.env.AI_SOFTWARE_SELECTION_RATIO || '0.5'); - - this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10); - this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10); - this.auditConfig = { enabled: process.env.FORENSIC_AUDIT_ENABLED === 'true', detailLevel: process.env.FORENSIC_AUDIT_DETAIL_LEVEL || 'standard' }; - - console.log('[AI PIPELINE] Audit trail enabled:', this.auditConfig.enabled); - console.log('[AI PIPELINE] Method/Software balance:', `${(this.methodSelectionRatio * 100).toFixed(0)}%/${(this.softwareSelectionRatio * 100).toFixed(0)}%`); - + this.confidenceConfig = { - 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), - mediumThreshold: parseInt(process.env.CONFIDENCE_MEDIUM_THRESHOLD || '60', 10), - highThreshold: parseInt(process.env.CONFIDENCE_HIGH_THRESHOLD || '80', 10) + semanticWeight: this.getEnvFloat('CONFIDENCE_SEMANTIC_WEIGHT', 0.3), + suitabilityWeight: this.getEnvFloat('CONFIDENCE_SUITABILITY_WEIGHT', 0.7), + minimumThreshold: this.getEnvInt('CONFIDENCE_MINIMUM_THRESHOLD', 40), + mediumThreshold: this.getEnvInt('CONFIDENCE_MEDIUM_THRESHOLD', 60), + highThreshold: this.getEnvInt('CONFIDENCE_HIGH_THRESHOLD', 80) }; - console.log('[AI PIPELINE] Simplified confidence scoring enabled:', { - weights: `Semantic:${this.confidenceConfig.semanticWeight} Suitability:${this.confidenceConfig.suitabilityWeight}`, - thresholds: `${this.confidenceConfig.minimumThreshold}/${this.confidenceConfig.mediumThreshold}/${this.confidenceConfig.highThreshold}` - }); - - console.log('[AI PIPELINE] Environment variable debug:', { - FORENSIC_AUDIT_ENABLED: process.env.FORENSIC_AUDIT_ENABLED, - FORENSIC_AUDIT_DETAIL_LEVEL: process.env.FORENSIC_AUDIT_DETAIL_LEVEL, - NODE_ENV: process.env.NODE_ENV, - allEnvKeys: Object.keys(process.env).filter(k => k.includes('AUDIT')), - dotenvLoaded: !!process.env.PUBLIC_BASE_URL - }); - - console.log('[AI PIPELINE] Final audit config:', this.auditConfig); + this.logPipelineInit(); } - - - private getEnv(key: string): string { + private getRequiredEnv(key: string): string { const value = process.env[key]; if (!value) { - throw new Error(`Missing environment variable: ${key}`); + throw new Error(`Missing required environment variable: ${key}`); } return value; } + private getEnvInt(key: string, defaultValue: number): number { + const value = process.env[key]; + return value ? parseInt(value, 10) : defaultValue; + } + + private getEnvFloat(key: string, defaultValue: number): number { + const value = process.env[key]; + return value ? parseFloat(value) : defaultValue; + } + + private logPipelineInit(): void { + console.log('[AI-PIPELINE] Initialized with audit:', this.auditConfig.enabled); + console.log('[AI-PIPELINE] Method/Software balance:', + `${(this.methodSelectionRatio * 100).toFixed(0)}%/${(this.softwareSelectionRatio * 100).toFixed(0)}%`); + } + private addAuditEntry( context: AnalysisContext, phase: string, @@ -211,48 +197,34 @@ class ImprovedMicroTaskAIPipeline { metadata }; - if (!context.auditTrail) { - context.auditTrail = []; - } context.auditTrail.push(entry); - - console.log(`[AUDIT] ${phase}/${action}: ${confidence}% confidence, ${entry.processingTimeMs}ms`); + this.logAuditEntry(phase, action, confidence, entry.processingTimeMs); } - - private mergeTemporaryAuditEntries(context: AnalysisContext): void { - if (!this.auditConfig.enabled || this.tempAuditEntries.length === 0) return; - if (!context.auditTrail) { - context.auditTrail = []; - } - - context.auditTrail.unshift(...this.tempAuditEntries); - this.tempAuditEntries = []; - - console.log('[AUDIT] Merged temporary entries into context'); + private logAuditEntry(phase: string, action: string, confidence: number, timeMs: number): void { + console.log(`[AUDIT] ${phase}/${action}: ${confidence}% confidence, ${timeMs}ms`); } private calculateSelectionConfidence(result: any, candidateCount: number): number { - if (!result || !result.selectedTools) return 30; + if (!result?.selectedTools) return 30; const selectionRatio = result.selectedTools.length / candidateCount; const hasReasoning = result.reasoning && result.reasoning.length > 50; - let confidence = 60; + let confidence = 60; if (selectionRatio > 0.05 && selectionRatio < 0.3) confidence += 20; - else if (selectionRatio <= 0.05) confidence -= 10; - else confidence -= 15; + else if (selectionRatio <= 0.05) confidence -= 10; + else confidence -= 15; if (hasReasoning) confidence += 15; - - if (result.selectedConcepts && result.selectedConcepts.length > 0) confidence += 5; + if (result.selectedConcepts?.length > 0) confidence += 5; return Math.min(95, Math.max(25, confidence)); } private estimateTokens(text: string): number { - return Math.ceil(text.length / 4); + return Math.ceil(text.length / 4); } private addToContextHistory(context: AnalysisContext, newEntry: string): void { @@ -269,48 +241,30 @@ class ImprovedMicroTaskAIPipeline { private safeParseJSON(jsonString: string, fallback: any = null): any { try { - // First, try to extract JSON block from markdown or mixed content let cleaned = jsonString.trim(); - // Look for JSON block patterns (most specific first) const jsonBlockPatterns = [ - /```json\s*([\s\S]*?)\s*```/i, // ```json ... ``` - /```\s*([\s\S]*?)\s*```/i, // ``` ... ``` - /\{[\s\S]*\}/, // Anything that looks like JSON object + /```json\s*([\s\S]*?)\s*```/i, + /```\s*([\s\S]*?)\s*```/i, + /\{[\s\S]*\}/, ]; - let jsonMatch: RegExpMatchArray | null = null; for (const pattern of jsonBlockPatterns) { - jsonMatch = cleaned.match(pattern); - if (jsonMatch) { - cleaned = jsonMatch[1] || jsonMatch[0]; - console.log('[AI PIPELINE] Extracted JSON block using pattern:', pattern.source); + const match = cleaned.match(pattern); + if (match) { + cleaned = match[1] || match[0]; break; } } - // If no pattern matched, try to find JSON-like content manually - if (!jsonMatch) { - const jsonStart = cleaned.indexOf('{'); - const jsonEnd = cleaned.lastIndexOf('}'); - if (jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart) { - cleaned = cleaned.substring(jsonStart, jsonEnd + 1); - console.log('[AI PIPELINE] Manually extracted JSON from position', jsonStart, 'to', jsonEnd); - } - } - - // Clean up the extracted content - cleaned = cleaned.trim(); - - // Handle truncated JSON by finding the last complete structure if (!cleaned.endsWith('}') && !cleaned.endsWith(']')) { - console.warn('[AI PIPELINE] JSON appears truncated, attempting recovery...'); + console.warn('[AI-PIPELINE] JSON appears truncated, attempting recovery'); - let lastCompleteStructure = ''; let braceCount = 0; let bracketCount = 0; let inString = false; let escaped = false; + let lastCompleteStructure = ''; for (let i = 0; i < cleaned.length; i++) { const char = cleaned[i]; @@ -343,70 +297,47 @@ class ImprovedMicroTaskAIPipeline { } if (lastCompleteStructure) { - console.log('[AI PIPELINE] Using recovered JSON structure'); cleaned = lastCompleteStructure; } else { - // Try to close unclosed braces/brackets - if (braceCount > 0) { - cleaned += '}'; - console.log('[AI PIPELINE] Added closing brace to truncated JSON'); - } - if (bracketCount > 0) { - cleaned += ']'; - console.log('[AI PIPELINE] Added closing bracket to truncated JSON'); - } + if (braceCount > 0) cleaned += '}'; + if (bracketCount > 0) cleaned += ']'; } } const parsed = JSON.parse(cleaned); - // Ensure the structure has the expected fields if (parsed && typeof parsed === 'object') { - if (parsed.selectedTools === undefined) parsed.selectedTools = []; - if (parsed.selectedConcepts === undefined) parsed.selectedConcepts = []; - + if (!parsed.selectedTools) parsed.selectedTools = []; + if (!parsed.selectedConcepts) parsed.selectedConcepts = []; if (!Array.isArray(parsed.selectedTools)) parsed.selectedTools = []; if (!Array.isArray(parsed.selectedConcepts)) parsed.selectedConcepts = []; - - console.log(`[AI PIPELINE] Successfully parsed JSON: ${parsed.selectedTools.length} tools, ${parsed.selectedConcepts.length} concepts`); } return parsed; } catch (error) { - console.warn('[AI PIPELINE] JSON parsing failed:', error.message); - console.warn('[AI PIPELINE] Raw content (first 300 chars):', jsonString.slice(0, 300)); - console.warn('[AI PIPELINE] Raw content (last 300 chars):', jsonString.slice(-300)); + console.warn('[AI-PIPELINE] JSON parsing failed:', error.message); - // Enhanced recovery mechanism that preserves tool/concept distinction if (jsonString.includes('selectedTools') || jsonString.includes('selectedConcepts')) { - console.log('[AI PIPELINE] Attempting enhanced recovery from broken JSON...'); - - // Try to extract tool and concept arrays separately const selectedTools: string[] = []; const selectedConcepts: string[] = []; - // Look for selectedTools array const toolsMatch = jsonString.match(/"selectedTools"\s*:\s*\[([\s\S]*?)\]/i); if (toolsMatch) { - const toolsContent = toolsMatch[1]; - const toolMatches = toolsContent.match(/"([^"]+)"/g); + const toolMatches = toolsMatch[1].match(/"([^"]+)"/g); if (toolMatches) { selectedTools.push(...toolMatches.map(match => match.replace(/"/g, ''))); } } - // Look for selectedConcepts array const conceptsMatch = jsonString.match(/"selectedConcepts"\s*:\s*\[([\s\S]*?)\]/i); if (conceptsMatch) { - const conceptsContent = conceptsMatch[1]; - const conceptMatches = conceptsContent.match(/"([^"]+)"/g); + const conceptMatches = conceptsMatch[1].match(/"([^"]+)"/g); if (conceptMatches) { selectedConcepts.push(...conceptMatches.map(match => match.replace(/"/g, ''))); } } - // If we couldn't parse arrays separately, fall back to generic name extraction if (selectedTools.length === 0 && selectedConcepts.length === 0) { const allMatches = jsonString.match(/"([^"]+)"/g); if (allMatches) { @@ -415,18 +346,17 @@ class ImprovedMicroTaskAIPipeline { .filter(name => name.length > 2 && !['selectedTools', 'selectedConcepts', 'reasoning'].includes(name) && - !name.includes(':') && // Avoid JSON keys - !name.match(/^\d+$/) // Avoid pure numbers + !name.includes(':') && + !name.match(/^\d+$/) ) .slice(0, 15); - // Assume all recovered names are tools (since concepts are usually fewer) selectedTools.push(...possibleNames); } } if (selectedTools.length > 0 || selectedConcepts.length > 0) { - console.log(`[AI PIPELINE] Recovery successful: ${selectedTools.length} tools, ${selectedConcepts.length} concepts`); + console.log('[AI-PIPELINE] JSON recovery successful:', selectedTools.length, 'tools,', selectedConcepts.length, 'concepts'); return { selectedTools, selectedConcepts, @@ -435,12 +365,19 @@ class ImprovedMicroTaskAIPipeline { } } - console.error('[AI PIPELINE] All recovery attempts failed'); return fallback; } } - private addToolToSelection(context: AnalysisContext, tool: any, phase: string, priority: string, justification?: string, taskRelevance?: number, limitations?: string[]): boolean { + private addToolToSelection( + context: AnalysisContext, + tool: any, + phase: string, + priority: string, + justification?: string, + taskRelevance?: number, + limitations?: string[] + ): boolean { context.seenToolNames.add(tool.name); if (!context.selectedTools) context.selectedTools = []; @@ -456,7 +393,32 @@ class ImprovedMicroTaskAIPipeline { return true; } - private async getIntelligentCandidates(userQuery: string, toolsData: any, mode: string, context: AnalysisContext) { + private generatePhaseQueryTemplates(phases: any[]): Record { + const templates: Record = {}; + + phases.forEach((phase: any) => { + if (phase?.id && phase?.name) { + const phaseKeywords = [ + 'forensic', + phase.name.toLowerCase(), + ...(phase.description ? phase.description.toLowerCase().split(' ').filter((word: string) => word.length > 3) : []), + ...(phase.key_activities || []).map((activity: string) => activity.toLowerCase()), + ...(phase.typical_tools || []).map((tool: string) => tool.toLowerCase()) + ].join(' '); + + templates[phase.id] = phaseKeywords; + } + }); + + return templates; + } + + private async getIntelligentCandidates( + userQuery: string, + toolsData: any, + mode: string, + context: AnalysisContext + ) { let candidateTools: any[] = []; let candidateConcepts: any[] = []; let selectionMethod = 'unknown'; @@ -464,41 +426,37 @@ class ImprovedMicroTaskAIPipeline { context.embeddingsSimilarities = new Map(); try { - console.log('[AI PIPELINE] Attempting embeddings initialization...'); await embeddingsService.waitForInitialization(); - console.log('[AI PIPELINE] Embeddings initialization completed'); } catch (error) { - console.error('[AI PIPELINE] Embeddings initialization failed:', error); + console.error('[AI-PIPELINE] Embeddings initialization failed:', error); } if (embeddingsService.isEnabled()) { const embeddingsStart = Date.now(); const similarItems = await embeddingsService.findSimilar( - userQuery, - this.embeddingCandidates, + userQuery, + this.embeddingCandidates, this.similarityThreshold ) as SimilarityResult[]; - console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`); + console.log('[AI-PIPELINE] Embeddings found', similarItems.length, 'similar items'); similarItems.forEach(item => { context.embeddingsSimilarities.set(item.name, item.similarity); }); - const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool])); - const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept])); + const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool])); + const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept])); const similarTools = similarItems - .filter((item): item is SimilarityResult => item.type === 'tool') - .map(item => toolsMap.get(item.name)) - .filter((tool): tool is any => tool !== undefined); + .filter((item: any) => item.type === 'tool') + .map((item: any) => toolsMap.get(item.name)) + .filter((tool: any): tool is NonNullable => tool !== undefined && tool !== null); const similarConcepts = similarItems - .filter((item): item is SimilarityResult => item.type === 'concept') - .map(item => conceptsMap.get(item.name)) - .filter((concept): concept is any => concept !== undefined); - - console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} tools, ${similarConcepts.length} concepts`); + .filter((item: any) => item.type === 'concept') + .map((item: any) => conceptsMap.get(item.name)) + .filter((concept: any): concept is NonNullable => concept !== undefined && concept !== null); const totalAvailableTools = toolsData.tools.length; const reductionRatio = similarTools.length / totalAvailableTools; @@ -508,50 +466,47 @@ class ImprovedMicroTaskAIPipeline { candidateConcepts = similarConcepts; selectionMethod = 'embeddings_candidates'; - console.log(`[AI PIPELINE] Using embeddings filtering: ${totalAvailableTools} → ${similarTools.length} tools (${(reductionRatio * 100).toFixed(1)}% reduction)`); + console.log('[AI-PIPELINE] Using embeddings filtering:', totalAvailableTools, '→', similarTools.length, 'tools'); } else { - if (similarTools.length < this.embeddingsMinTools) { - console.log(`[AI PIPELINE] Embeddings found too few tools (${similarTools.length} < ${this.embeddingsMinTools}), using full dataset`); - } else { - console.log(`[AI PIPELINE] Embeddings didn't filter enough (${(reductionRatio * 100).toFixed(1)}% > ${(this.embeddingsMaxReductionRatio * 100).toFixed(1)}%), using full dataset`); - } + console.log('[AI-PIPELINE] Embeddings filtering insufficient, using full dataset'); candidateTools = toolsData.tools; candidateConcepts = toolsData.concepts; selectionMethod = 'full_dataset'; } if (this.auditConfig.enabled) { - this.addAuditEntry(context, 'retrieval', 'embeddings-search', - { query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates }, - { - candidatesFound: similarItems.length, - toolsInOrder: similarTools.slice(0, 3).map((t: any) => t.name), - conceptsInOrder: similarConcepts.slice(0, 3).map((c: any) => c.name), + this.addAuditEntry( + context, + 'retrieval', + 'embeddings-search', + { query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates }, + { + candidatesFound: similarItems.length, reductionRatio: reductionRatio, usingEmbeddings: selectionMethod === 'embeddings_candidates', totalAvailable: totalAvailableTools, - filtered: similarTools.length, - avgSimilarity: similarItems.length > 0 ? similarItems.reduce((sum, item) => sum + item.similarity, 0) / similarItems.length : 0 + filtered: similarTools.length }, selectionMethod === 'embeddings_candidates' ? 85 : 60, embeddingsStart, - { - selectionMethod, - embeddingsEnabled: true, - reductionAchieved: selectionMethod === 'embeddings_candidates', - tokenSavingsExpected: selectionMethod === 'embeddings_candidates' - } + { selectionMethod, embeddingsEnabled: true } ); } } else { - console.log(`[AI PIPELINE] Embeddings disabled or not ready, using full dataset`); + console.log('[AI-PIPELINE] Embeddings disabled, using full dataset'); candidateTools = toolsData.tools; candidateConcepts = toolsData.concepts; selectionMethod = 'full_dataset'; } - console.log(`[AI PIPELINE] AI will analyze ${candidateTools.length} candidate tools (method: ${selectionMethod})`); - const finalSelection = await this.aiSelectionWithFullData(userQuery, candidateTools, candidateConcepts, mode, selectionMethod, context); + const finalSelection = await this.aiSelectionWithFullData( + userQuery, + candidateTools, + candidateConcepts, + mode, + selectionMethod, + context + ); return { tools: finalSelection.selectedTools, @@ -563,79 +518,36 @@ class ImprovedMicroTaskAIPipeline { } private async aiSelectionWithFullData( - userQuery: string, - candidateTools: any[], - candidateConcepts: any[], + userQuery: string, + candidateTools: any[], + candidateConcepts: any[], mode: string, selectionMethod: string, context: AnalysisContext ) { const selectionStart = Date.now(); - const candidateMethods = candidateTools.filter(tool => tool.type === 'method'); - const candidateSoftware = candidateTools.filter(tool => tool.type === 'software'); + const candidateMethods = candidateTools.filter((tool: any) => tool && tool.type === 'method'); + const candidateSoftware = candidateTools.filter((tool: any) => tool && tool.type === 'software'); - console.log(`[AI PIPELINE] Candidates: ${candidateMethods.length} methods, ${candidateSoftware.length} software, ${candidateConcepts.length} concepts`); + console.log('[AI-PIPELINE] Tool selection candidates:', candidateMethods.length, 'methods,', candidateSoftware.length, 'software,', candidateConcepts.length, 'concepts'); - const methodsWithFullData = candidateMethods.map((tool: any) => ({ - name: tool.name, - type: tool.type, - description: tool.description, - domains: tool.domains, - phases: tool.phases, - platforms: tool.platforms || [], - tags: tool.tags || [], - skillLevel: tool.skillLevel, - license: tool.license, - accessType: tool.accessType, - projectUrl: tool.projectUrl, - knowledgebase: tool.knowledgebase, - related_concepts: tool.related_concepts || [], - related_software: tool.related_software || [] - })); - - const softwareWithFullData = candidateSoftware.map((tool: any) => ({ - name: tool.name, - type: tool.type, - description: tool.description, - domains: tool.domains, - phases: tool.phases, - platforms: tool.platforms || [], - tags: tool.tags || [], - skillLevel: tool.skillLevel, - license: tool.license, - accessType: tool.accessType, - projectUrl: tool.projectUrl, - knowledgebase: tool.knowledgebase, - related_concepts: tool.related_concepts || [], - related_software: tool.related_software || [] - })); - - const conceptsWithFullData = candidateConcepts.map((concept: any) => ({ - name: concept.name, - type: 'concept', - description: concept.description, - domains: concept.domains, - phases: concept.phases, - tags: concept.tags || [], - skillLevel: concept.skillLevel, - related_concepts: concept.related_concepts || [], - related_software: concept.related_software || [] - })); + const methodsWithFullData = candidateMethods.map(this.createToolData); + const softwareWithFullData = candidateSoftware.map(this.createToolData); + const conceptsWithFullData = candidateConcepts.map(this.createConceptData); let toolsToSend: any[]; let conceptsToSend: any[]; if (selectionMethod === 'embeddings_candidates') { const totalLimit = this.embeddingSelectionLimit; - const methodLimit = Math.ceil(totalLimit * this.methodSelectionRatio); const softwareLimit = Math.floor(totalLimit * this.softwareSelectionRatio); - const selectedMethods = methodsWithFullData.slice(0, methodLimit); - const selectedSoftware = softwareWithFullData.slice(0, softwareLimit); - - toolsToSend = [...selectedMethods, ...selectedSoftware]; + toolsToSend = [ + ...methodsWithFullData.slice(0, methodLimit), + ...softwareWithFullData.slice(0, softwareLimit) + ]; const remainingCapacity = totalLimit - toolsToSend.length; if (remainingCapacity > 0) { @@ -647,23 +559,16 @@ class ImprovedMicroTaskAIPipeline { } conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit); - - console.log(`[AI PIPELINE] Balanced selection: ${selectedMethods.length} methods, ${selectedSoftware.length} software, ${conceptsToSend.length} concepts`); - console.log(`[AI PIPELINE] Method names: ${selectedMethods.slice(0, 5).map(m => m.name).join(', ')}${selectedMethods.length > 5 ? '...' : ''}`); - console.log(`[AI PIPELINE] Software names: ${selectedSoftware.slice(0, 5).map(s => s.name).join(', ')}${selectedSoftware.length > 5 ? '...' : ''}`); - console.log(`[AI PIPELINE] Concept names: ${conceptsToSend.map(c => c.name).join(', ')}`); - } else { - const maxTools = this.noEmbeddingsToolLimit > 0 ? this.noEmbeddingsToolLimit : 25; - const maxConcepts = this.noEmbeddingsConceptLimit > 0 ? this.noEmbeddingsConceptLimit : 10; - + const maxTools = this.noEmbeddingsToolLimit; + const maxConcepts = this.noEmbeddingsConceptLimit; const methodLimit = Math.ceil(maxTools * 0.4); const softwareLimit = Math.floor(maxTools * 0.5); - const selectedMethods = methodsWithFullData.slice(0, methodLimit); - const selectedSoftware = softwareWithFullData.slice(0, softwareLimit); - - toolsToSend = [...selectedMethods, ...selectedSoftware]; + toolsToSend = [ + ...methodsWithFullData.slice(0, methodLimit), + ...softwareWithFullData.slice(0, softwareLimit) + ]; const remainingCapacity = maxTools - toolsToSend.length; if (remainingCapacity > 0) { @@ -675,44 +580,35 @@ class ImprovedMicroTaskAIPipeline { } conceptsToSend = conceptsWithFullData.slice(0, maxConcepts); - - console.log(`[AI PIPELINE] Balanced selection (no embeddings): ${selectedMethods.length} methods, ${selectedSoftware.length} software, ${conceptsToSend.length} concepts`); } const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems); - const prompt = `${basePrompt} - - VERFÜGBARE TOOLS (${toolsToSend.length} Items - Methoden und Software): - ${JSON.stringify(toolsToSend, null, 2)} - - VERFÜGBARE KONZEPTE (${conceptsToSend.length} Items - theoretisches Wissen): - ${JSON.stringify(conceptsToSend, null, 2)} - - WICHTIGER HINWEIS: Wähle sowohl aus TOOLS als auch aus KONZEPTEN aus! Konzepte sind essentiell für methodische Fundierung.`; + const prompt = getPrompt('toolSelectionWithData', basePrompt, toolsToSend, conceptsToSend); const estimatedTokens = this.estimateTokens(prompt); - console.log(`[AI PIPELINE] Sending to AI: ${toolsToSend.filter(t => t.type === 'method').length} methods, ${toolsToSend.filter(t => t.type === 'software').length} software, ${conceptsToSend.length} concepts`); - console.log(`[AI PIPELINE] Estimated tokens: ~${estimatedTokens}`); + console.log('[AI-PIPELINE] Sending to AI:', toolsToSend.filter((t: any) => t.type === 'method').length, 'methods,', toolsToSend.filter((t: any) => t.type === 'software').length, 'software,', conceptsToSend.length, 'concepts'); if (estimatedTokens > 35000) { - console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`); + console.warn('[AI-PIPELINE] WARNING: Prompt tokens may exceed model limits:', estimatedTokens); } try { const response = await this.callAI(prompt, 2500); - const result = this.safeParseJSON(response, null); if (!result || !Array.isArray(result.selectedTools) || !Array.isArray(result.selectedConcepts)) { - console.error('[AI PIPELINE] AI selection returned invalid structure:', response.slice(0, 200)); + console.error('[AI-PIPELINE] AI selection returned invalid structure'); if (this.auditConfig.enabled) { - this.addAuditEntry(context, 'selection', 'ai-tool-selection-failed', - { candidateCount: candidateTools.length, candidateConceptsCount: candidateConcepts.length, mode, prompt: prompt.slice(0, 200) }, - { error: 'Invalid JSON structure', response: response.slice(0, 200) }, + this.addAuditEntry( + context, + 'selection', + 'ai-tool-selection-failed', + { candidateCount: candidateTools.length, mode }, + { error: 'Invalid JSON structure' }, 10, selectionStart, - { aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, toolsSent: toolsToSend.length, conceptsSent: conceptsToSend.length } + { aiModel: this.config.model, selectionMethod } ); } @@ -721,77 +617,105 @@ class ImprovedMicroTaskAIPipeline { const totalSelected = result.selectedTools.length + result.selectedConcepts.length; if (totalSelected === 0) { - console.error('[AI PIPELINE] AI selection returned no tools or concepts'); throw new Error('AI selection returned empty selection'); } - // Create lookup maps for efficient filtering - const toolsMap = new Map(candidateTools.map(tool => [tool.name, tool])); - const conceptsMap = new Map(candidateConcepts.map(concept => [concept.name, concept])); + const toolsMap = new Map(candidateTools.map((tool: any) => [tool.name, tool])); + const conceptsMap = new Map(candidateConcepts.map((concept: any) => [concept.name, concept])); const selectedTools = result.selectedTools - .map(name => toolsMap.get(name)) - .filter((tool): tool is any => tool !== undefined); + .map((name: string) => toolsMap.get(name)) + .filter((tool: any): tool is NonNullable => tool !== undefined && tool !== null); const selectedConcepts = result.selectedConcepts - .map(name => conceptsMap.get(name)) - .filter((concept): concept is any => concept !== undefined); + .map((name: string) => conceptsMap.get(name)) + .filter((concept: any): concept is NonNullable => concept !== undefined && concept !== null); - const selectedMethods = selectedTools.filter(t => t.type === 'method'); - const selectedSoftware = selectedTools.filter(t => t.type === 'software'); + const selectedMethods = selectedTools.filter((t: any) => t && t.type === 'method'); + const selectedSoftware = selectedTools.filter((t: any) => t && t.type === 'software'); - console.log(`[AI PIPELINE] AI selected: ${selectedMethods.length} methods, ${selectedSoftware.length} software, ${selectedConcepts.length} concepts`); - console.log(`[AI PIPELINE] Selection balance: ${((selectedMethods.length / (selectedTools.length || 1)) * 100).toFixed(0)}% methods`); - console.log(`[AI PIPELINE] Selected tool names: ${selectedTools.map(t => t.name).join(', ')}`); - console.log(`[AI PIPELINE] Selected concept names: ${selectedConcepts.map(c => c.name).join(', ')}`); + console.log('[AI-PIPELINE] AI selected:', selectedMethods.length, 'methods,', selectedSoftware.length, 'software,', selectedConcepts.length, 'concepts'); if (this.auditConfig.enabled) { const confidence = this.calculateSelectionConfidence(result, candidateTools.length + candidateConcepts.length); - this.addAuditEntry(context, 'selection', 'ai-tool-selection', - { candidateCount: candidateTools.length, candidateConceptsCount: candidateConcepts.length, mode, promptLength: prompt.length }, - { + this.addAuditEntry( + context, + 'selection', + 'ai-tool-selection', + { candidateCount: candidateTools.length, mode }, + { selectedMethodCount: selectedMethods.length, - selectedSoftwareCount: selectedSoftware.length, + selectedSoftwareCount: selectedSoftware.length, selectedConceptCount: selectedConcepts.length, - reasoning: result.reasoning?.slice(0, 200) + '...', - finalToolNames: selectedTools.map(t => t.name), - finalConceptNames: selectedConcepts.map(c => c.name), - methodBalance: `${((selectedMethods.length / (selectedTools.length || 1)) * 100).toFixed(0)}%`, - conceptsSelected: selectedConcepts.length > 0 + reasoning: result.reasoning?.slice(0, 200), + methodBalance: `${((selectedMethods.length / (selectedTools.length || 1)) * 100).toFixed(0)}%` }, confidence, selectionStart, - { aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, toolsSent: toolsToSend.length, conceptsSent: conceptsToSend.length } + { aiModel: this.config.model, selectionMethod } ); } - return { - selectedTools, - selectedConcepts - }; + return { selectedTools, selectedConcepts }; } catch (error) { - console.error('[AI PIPELINE] AI selection failed:', error); + console.error('[AI-PIPELINE] AI selection failed:', error); if (this.auditConfig.enabled) { - this.addAuditEntry(context, 'selection', 'ai-tool-selection-error', - { candidateCount: candidateTools.length, candidateConceptsCount: candidateConcepts.length, mode }, + this.addAuditEntry( + context, + 'selection', + 'ai-tool-selection-error', + { candidateCount: candidateTools.length, mode }, { error: error.message }, 5, selectionStart, - { aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens } + { aiModel: this.config.model, selectionMethod } ); } throw error; } } + private createToolData = (tool: any) => ({ + name: tool.name, + type: tool.type, + description: tool.description, + domains: tool.domains, + phases: tool.phases, + platforms: tool.platforms || [], + tags: tool.tags || [], + skillLevel: tool.skillLevel, + license: tool.license, + accessType: tool.accessType, + projectUrl: tool.projectUrl, + knowledgebase: tool.knowledgebase, + related_concepts: tool.related_concepts || [], + related_software: tool.related_software || [] + }); + + private createConceptData = (concept: any) => ({ + name: concept.name, + type: 'concept', + description: concept.description, + domains: concept.domains, + phases: concept.phases, + tags: concept.tags || [], + skillLevel: concept.skillLevel, + related_concepts: concept.related_concepts || [], + related_software: concept.related_software || [] + }); + private async delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } - private async callMicroTaskAI(prompt: string, context: AnalysisContext, maxTokens: number = 500): Promise { + private async callMicroTaskAI( + prompt: string, + context: AnalysisContext, + maxTokens: number = 500 + ): Promise { const startTime = Date.now(); let contextPrompt = prompt; @@ -801,8 +725,6 @@ class ImprovedMicroTaskAIPipeline { if (this.estimateTokens(combinedPrompt) <= this.maxPromptTokens) { contextPrompt = combinedPrompt; - } else { - console.warn('[AI PIPELINE] Context too long, using prompt only'); } } @@ -816,9 +738,12 @@ class ImprovedMicroTaskAIPipeline { success: true }; - this.addAuditEntry(context, 'micro-task', 'ai-analysis', + this.addAuditEntry( + context, + 'micro-task', + 'ai-analysis', { promptLength: contextPrompt.length, maxTokens }, - { responseLength: response.length, contentPreview: response.slice(0, 100) }, + { responseLength: response.length }, response.length > 50 ? 80 : 60, startTime, { aiModel: this.config.model, contextUsed: context.contextHistory.length > 0 } @@ -835,12 +760,15 @@ class ImprovedMicroTaskAIPipeline { error: error.message }; - this.addAuditEntry(context, 'micro-task', 'ai-analysis-failed', + this.addAuditEntry( + context, + 'micro-task', + 'ai-analysis-failed', { promptLength: contextPrompt.length, maxTokens }, { error: error.message }, 5, startTime, - { aiModel: this.config.model, contextUsed: context.contextHistory.length > 0 } + { aiModel: this.config.model } ); return result; @@ -848,24 +776,21 @@ class ImprovedMicroTaskAIPipeline { } private calculateRecommendationConfidence( - tool: any, + tool: any, context: AnalysisContext, taskRelevance: number = 70, limitations: string[] = [] ): ConfidenceMetrics { - - const rawSemanticRelevance = context.embeddingsSimilarities.has(tool.name) ? + const rawSemanticRelevance = context.embeddingsSimilarities.has(tool.name) ? context.embeddingsSimilarities.get(tool.name)! * 100 : 50; let enhancedTaskSuitability = taskRelevance; if (context.mode === 'workflow') { - const toolSelection = context.selectedTools?.find(st => st.tool.name === tool.name); - if (toolSelection && tool.phases && tool.phases.includes(toolSelection.phase)) { + const toolSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name); + if (toolSelection && tool.phases && Array.isArray(tool.phases) && tool.phases.includes(toolSelection.phase)) { const phaseBonus = Math.min(15, 100 - taskRelevance); enhancedTaskSuitability = Math.min(100, taskRelevance + phaseBonus); - - console.log(`[CONFIDENCE] Phase bonus for ${tool.name}: ${taskRelevance} -> ${enhancedTaskSuitability} (phase: ${toolSelection.phase})`); } } @@ -874,44 +799,41 @@ class ImprovedMicroTaskAIPipeline { enhancedTaskSuitability * this.confidenceConfig.suitabilityWeight ); - const uncertaintyFactors = this.identifySpecificUncertaintyFactors(tool, context, limitations, overall); - const strengthIndicators = this.identifySpecificStrengthIndicators(tool, context, overall); - - console.log(`[CONFIDENCE DEBUG] ${tool.name}:`, { - rawSemantic: Math.round(rawSemanticRelevance), - rawTaskSuitability: taskRelevance, - enhancedTaskSuitability: Math.round(enhancedTaskSuitability), - overall: Math.round(overall), - mode: context.mode - }); + const uncertaintyFactors = this.identifyUncertaintyFactors(tool, context, limitations, overall); + const strengthIndicators = this.identifyStrengthIndicators(tool, context, overall); return { overall: Math.round(overall), semanticRelevance: Math.round(rawSemanticRelevance), - taskSuitability: Math.round(enhancedTaskSuitability), + taskSuitability: Math.round(enhancedTaskSuitability), uncertaintyFactors, strengthIndicators }; } - private identifySpecificUncertaintyFactors(tool: any, context: AnalysisContext, limitations: string[], confidence: number): string[] { + private identifyUncertaintyFactors( + tool: any, + context: AnalysisContext, + limitations: string[], + confidence: number + ): string[] { const factors: string[] = []; - if (limitations && limitations.length > 0) { + if (limitations?.length > 0) { factors.push(...limitations.slice(0, 2)); } 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'); + factors.push('Geringe semantische Ähnlichkeit zur Anfrage'); } 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'); + factors.push('Experten-Tool für zeitkritisches Szenario'); } if (tool.skillLevel === 'novice' && /komplex|erweitert|tiefgehend|advanced|forensisch/i.test(context.userQuery)) { - factors.push('Einsteiger-Tool für komplexe Analyse - könnte funktionale Limitierungen haben'); + factors.push('Einsteiger-Tool für komplexe Analyse'); } if (tool.type === 'software' && !isToolHosted(tool) && tool.accessType === 'download') { @@ -919,17 +841,17 @@ class ImprovedMicroTaskAIPipeline { } if (tool.license === 'Proprietary') { - factors.push('Kommerzielle Software - Lizenzkosten und rechtliche Beschränkungen zu beachten'); + factors.push('Kommerzielle Software - Lizenzkosten zu beachten'); } if (confidence < 60) { - factors.push('Moderate Gesamtbewertung - alternative Ansätze sollten ebenfalls betrachtet werden'); + factors.push('Moderate Gesamtbewertung - alternative Ansätze empfohlen'); } return factors.slice(0, 4); } - private identifySpecificStrengthIndicators(tool: any, context: AnalysisContext, confidence: number): string[] { + private identifyStrengthIndicators(tool: any, context: AnalysisContext, confidence: number): string[] { const indicators: string[] = []; const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5; @@ -942,7 +864,7 @@ class ImprovedMicroTaskAIPipeline { } if (isToolHosted(tool)) { - indicators.push('Sofort verfügbar über gehostete Lösung - kein Setup erforderlich'); + indicators.push('Sofort verfügbar über gehostete Lösung'); } if (tool.skillLevel === 'intermediate' || tool.skillLevel === 'advanced') { @@ -953,10 +875,11 @@ class ImprovedMicroTaskAIPipeline { indicators.push('Methodischer Ansatz passt zu Ihrer prozeduralen Anfrage'); } - return indicators.slice(0, 4); + return indicators.slice(0, 4); } private async analyzeScenario(context: AnalysisContext): Promise { + console.log('[AI-PIPELINE] Starting scenario analysis micro-task'); const isWorkflow = context.mode === 'workflow'; const prompt = getPrompt('scenarioAnalysis', isWorkflow, context.userQuery); @@ -976,6 +899,7 @@ class ImprovedMicroTaskAIPipeline { } private async generateApproach(context: AnalysisContext): Promise { + console.log('[AI-PIPELINE] Starting investigation approach micro-task'); const isWorkflow = context.mode === 'workflow'; const prompt = getPrompt('investigationApproach', isWorkflow, context.userQuery); @@ -990,6 +914,7 @@ class ImprovedMicroTaskAIPipeline { } private async generateCriticalConsiderations(context: AnalysisContext): Promise { + console.log('[AI-PIPELINE] Starting critical considerations micro-task'); const isWorkflow = context.mode === 'workflow'; const prompt = getPrompt('criticalConsiderations', isWorkflow, context.userQuery); @@ -1004,15 +929,13 @@ class ImprovedMicroTaskAIPipeline { } private async selectToolsForPhase(context: AnalysisContext, phase: any): Promise { - const phaseTools = context.filteredData.tools.filter((tool: any) => - tool.phases && tool.phases.includes(phase.id) + console.log('[AI-PIPELINE] Starting phase tool selection micro-task for:', phase.id); + const phaseTools = context.filteredData.tools.filter((tool: any) => + tool && tool.phases && Array.isArray(tool.phases) && tool.phases.includes(phase.id) ); - console.log(`[AI PIPELINE] Phase ${phase.id} (${phase.name}): Found ${phaseTools.length} matching tools`); - console.log(`[AI PIPELINE] Available tools for phase: ${phaseTools.map(t => `${t.name}(${t.type})`).join(', ')}`); - if (phaseTools.length === 0) { - console.log(`[AI PIPELINE] No tools available for phase ${phase.id}, skipping`); + console.log('[AI-PIPELINE] No tools available for phase:', phase.id); return { taskType: 'tool-selection', content: JSON.stringify([]), @@ -1021,123 +944,92 @@ class ImprovedMicroTaskAIPipeline { }; } - const phaseMethods = phaseTools.filter(t => t.type === 'method'); - const phaseSoftware = phaseTools.filter(t => t.type === 'software'); + const phaseMethods = phaseTools.filter((t: any) => t && t.type === 'method'); + const phaseSoftware = phaseTools.filter((t: any) => t && t.type === 'software'); - console.log(`[AI PIPELINE] Phase ${phase.id}: ${phaseMethods.length} methods, ${phaseSoftware.length} software`); + console.log('[AI-PIPELINE] Phase tools available:', phaseMethods.length, 'methods,', phaseSoftware.length, 'software'); const prompt = getPrompt('phaseToolSelection', context.userQuery, phase, phaseTools); - const result = await this.callMicroTaskAI(prompt, context, 1000); if (result.success) { - console.log(`[AI PIPELINE] Phase ${phase.id} AI response length: ${result.content.length}`); - console.log(`[AI PIPELINE] Phase ${phase.id} AI response preview: ${result.content.slice(0, 200)}...`); - const selections = this.safeParseJSON(result.content, []); if (Array.isArray(selections)) { - console.log(`[AI PIPELINE] Phase ${phase.id}: Parsed ${selections.length} selections from AI`); - const validSelections = selections.filter((sel: any) => { - if (!sel.toolName) return false; - - let matchingTool = phaseTools.find((tool: any) => tool.name === sel.toolName); - - const isValid = !!matchingTool; - if (!isValid) { - console.warn(`[AI PIPELINE] Phase ${phase.id}: Invalid selection: ${JSON.stringify(sel)}`); - console.warn(`[AI PIPELINE] Phase ${phase.id}: Available tool names: ${phaseTools.map(t => t.name).join(', ')}`); + const matchingTool = phaseTools.find((tool: any) => tool && tool.name === sel.toolName); + if (!matchingTool) { + console.warn('[AI-PIPELINE] Invalid tool selection for phase:', phase.id, sel.toolName); } - return isValid; + return !!matchingTool; }); - console.log(`[AI PIPELINE] Phase ${phase.id}: ${validSelections.length} valid selections after filtering`); + console.log('[AI-PIPELINE] Valid selections for phase:', phase.id, validSelections.length); validSelections.forEach((sel: any) => { - const tool = phaseTools.find((t: any) => t.name === sel.toolName); + const tool = phaseTools.find((t: any) => t && t.name === sel.toolName); if (tool) { - const taskRelevance = typeof sel.taskRelevance === 'number' ? + const taskRelevance = typeof sel.taskRelevance === 'number' ? sel.taskRelevance : parseInt(String(sel.taskRelevance)) || 70; const priority = this.derivePriorityFromScore(taskRelevance); - console.log(`[AI PIPELINE] Phase ${phase.id}: Adding ${tool.name} (${tool.type}) with priority ${priority}, relevance ${taskRelevance}%`); - this.addToolToSelection(context, tool, phase.id, priority, sel.justification, taskRelevance, sel.limitations); } }); - this.addAuditEntry(context, 'micro-task', 'phase-tool-selection', - { phase: phase.id, availableTools: phaseTools.length, availableMethods: phaseMethods.length, availableSoftware: phaseSoftware.length }, - { - validSelections: validSelections.length, - selectedTools: validSelections.map(s => ({ - name: s.toolName, - taskRelevance: s.taskRelevance, - derivedPriority: this.derivePriorityFromScore(s.taskRelevance) - })), - methodsSelected: validSelections.filter(s => { - const tool = phaseTools.find(t => t.name === s.toolName); - return tool && tool.type === 'method'; - }).length + this.addAuditEntry( + context, + 'micro-task', + 'phase-tool-selection', + { phase: phase.id, availableTools: phaseTools.length }, + { + validSelections: validSelections.length, + selectedTools: validSelections.map((s: any) => ({ + name: s.toolName, + taskRelevance: s.taskRelevance, + derivedPriority: this.derivePriorityFromScore(s.taskRelevance) + })) }, validSelections.length > 0 ? 75 : 30, Date.now() - result.processingTimeMs, - { phaseName: phase.name, comparativeEvaluation: true, priorityDerived: true } + { phaseName: phase.name } ); - } else { - console.error(`[AI PIPELINE] Phase ${phase.id}: Failed to parse selections as array:`, selections); } - } else { - console.error(`[AI PIPELINE] Phase ${phase.id}: AI call failed:`, result.error); } return result; } private async completeUnderrepresentedPhases( - context: AnalysisContext, + context: AnalysisContext, toolsData: any, originalQuery: string ): Promise { const phases = toolsData.phases || []; const selectedPhases = new Map(); - // Count tools per phase from current selection - context.selectedTools?.forEach(st => { + context.selectedTools?.forEach((st: any) => { const count = selectedPhases.get(st.phase) || 0; selectedPhases.set(st.phase, count + 1); }); - console.log(`[AI PIPELINE] Phase coverage analysis:`); - phases.forEach(phase => { - const count = selectedPhases.get(phase.id) || 0; - console.log(`[AI PIPELINE] ${phase.id}: ${count} tools`); - }); + console.log('[AI-PIPELINE] Phase coverage analysis complete'); - // Define phase-specific semantic queries - const phaseQueryTemplates = { - 'data-collection': 'forensic data acquisition imaging memory disk capture evidence collection', - 'examination': 'forensic analysis parsing extraction artifact examination file system', - 'analysis': 'forensic correlation timeline analysis pattern detection investigation', - 'reporting': 'forensic report documentation case management collaboration presentation findings' - }; + const phaseQueryTemplates = this.generatePhaseQueryTemplates(phases); - // Identify underrepresented phases (0 tools = missing, 1 tool = underrepresented) - const underrepresentedPhases = phases.filter(phase => { + const underrepresentedPhases = phases.filter((phase: any) => { const count = selectedPhases.get(phase.id) || 0; - return count <= 1; // Missing (0) or underrepresented (1) + return count <= 1; }); if (underrepresentedPhases.length === 0) { - console.log(`[AI PIPELINE] All phases adequately represented, no completion needed`); + console.log('[AI-PIPELINE] All phases adequately represented'); return; } - console.log(`[AI PIPELINE] Underrepresented phases: ${underrepresentedPhases.map(p => p.id).join(', ')}`); + console.log('[AI-PIPELINE] Completing underrepresented phases:', underrepresentedPhases.map((p: any) => p.id).join(', ')); - // Process each underrepresented phase for (const phase of underrepresentedPhases) { await this.completePhaseWithSemanticSearch(context, phase, phaseQueryTemplates, toolsData, originalQuery); await this.delay(this.microTaskDelay); @@ -1152,103 +1044,86 @@ class ImprovedMicroTaskAIPipeline { originalQuery: string ): Promise { const phaseStart = Date.now(); - - // Generate phase-specific semantic query const phaseQuery = phaseQueryTemplates[phase.id] || `forensic ${phase.name.toLowerCase()} tools methods`; - console.log(`[AI PIPELINE] Completing phase ${phase.id} with query: "${phaseQuery}"`); + console.log('[AI-PIPELINE] Starting phase completion micro-task for:', phase.id); try { - // Run semantic search with phase-specific query - const phaseResults = await embeddingsService.findSimilar( - phaseQuery, - 20, // Smaller set for phase completion - 0.2 // Lower threshold for more results - ); + const phaseResults = await embeddingsService.findSimilar(phaseQuery, 20, 0.2); if (phaseResults.length === 0) { - console.log(`[AI PIPELINE] No semantic results for phase ${phase.id}`); + console.log('[AI-PIPELINE] No semantic results for phase:', phase.id); return; } - // Filter to tools that actually belong to this phase - const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool])); - const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept])); + const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool])); + const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept])); const phaseTools = phaseResults - .filter(result => result.type === 'tool') - .map(result => toolsMap.get(result.name)) - .filter((tool): tool is any => - tool !== undefined && - tool.phases && + .filter((result: any) => result.type === 'tool') + .map((result: any) => toolsMap.get(result.name)) + .filter((tool: any): tool is NonNullable => + tool !== undefined && + tool !== null && + tool.phases && + Array.isArray(tool.phases) && tool.phases.includes(phase.id) && - !context.seenToolNames.has(tool.name) // Don't re-select already chosen tools + !context.seenToolNames.has(tool.name) ) - .slice(0, 5); // Top 5 candidates for this phase + .slice(0, 5); const phaseConcepts = phaseResults - .filter(result => result.type === 'concept') - .map(result => conceptsMap.get(result.name)) - .filter((concept): concept is any => concept !== undefined) - .slice(0, 2); // Top 2 concepts - - console.log(`[AI PIPELINE] Phase ${phase.id} semantic search found: ${phaseTools.length} tools, ${phaseConcepts.length} concepts`); + .filter((result: any) => result.type === 'concept') + .map((result: any) => conceptsMap.get(result.name)) + .filter((concept: any): concept is NonNullable => concept !== undefined && concept !== null) + .slice(0, 2); if (phaseTools.length === 0) { - console.log(`[AI PIPELINE] No suitable tools found for phase ${phase.id} after filtering`); + console.log('[AI-PIPELINE] No suitable tools for phase completion:', phase.id); return; } const prompt = AI_PROMPTS.generatePhaseCompletionPrompt(originalQuery, phase, phaseTools, phaseConcepts); - const response = await this.callAI(prompt, 800); const selection = this.safeParseJSON(response, { selectedTools: [], selectedConcepts: [] }); const validTools = selection.selectedTools - .map(name => phaseTools.find(t => t.name === name)) - .filter((tool): tool is any => tool !== undefined) - .slice(0, 2); + .map((name: string) => phaseTools.find((t: any) => t && t.name === name)) + .filter((tool: any): tool is NonNullable => tool !== undefined && tool !== null) + .slice(0, 2); - validTools.forEach(tool => { - console.log(`[AI PIPELINE] Adding phase completion tool: ${tool.name} for ${phase.id}`); + validTools.forEach((tool: any) => { + console.log('[AI-PIPELINE] Adding phase completion tool:', tool.name, 'for', phase.id); this.addToolToSelection( context, tool, phase.id, - 'medium', // Phase completion tools get medium priority + 'medium', `Hinzugefügt zur Vervollständigung der ${phase.name}-Phase`, - 75, // Good relevance for phase-specific search + 75, ['Via phasenspezifische semantische Suche hinzugefügt'] ); }); - // Audit the phase completion - this.addAuditEntry(context, 'validation', 'phase-completion', - { - phase: phase.id, - phaseQuery, - candidatesFound: phaseTools.length, - originalQuery: originalQuery.slice(0, 100) + '...' - }, - { - toolsAdded: validTools.length, - addedTools: validTools.map(t => t.name), - semanticResults: phaseResults.length - }, + this.addAuditEntry( + context, + 'validation', + 'phase-completion', + { phase: phase.id, phaseQuery, candidatesFound: phaseTools.length }, + { toolsAdded: validTools.length, addedTools: validTools.map((t: any) => t.name) }, validTools.length > 0 ? 80 : 40, phaseStart, - { - phaseCompletion: true, - semanticSearch: true, - originalQueryBias: true - } + { phaseCompletion: true, semanticSearch: true } ); } catch (error) { - console.error(`[AI PIPELINE] Phase completion failed for ${phase.id}:`, error); + console.error('[AI-PIPELINE] Phase completion failed for:', phase.id, error); - this.addAuditEntry(context, 'validation', 'phase-completion-failed', + this.addAuditEntry( + context, + 'validation', + 'phase-completion-failed', { phase: phase.id, phaseQuery }, { error: error.message }, 10, @@ -1259,12 +1134,12 @@ class ImprovedMicroTaskAIPipeline { } private async evaluateSpecificTool(context: AnalysisContext, tool: any, rank: number): Promise { - const existingSelection = context.selectedTools?.find(st => st.tool.name === tool.name); + console.log('[AI-PIPELINE] Starting tool evaluation micro-task for:', tool.name); + const existingSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name); const taskRelevance = existingSelection?.taskRelevance || 70; const priority = this.derivePriorityFromScore(taskRelevance); const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank, taskRelevance); - const result = await this.callMicroTaskAI(prompt, context, 1000); if (result.success) { @@ -1272,7 +1147,7 @@ class ImprovedMicroTaskAIPipeline { detailed_explanation: 'Evaluation failed', implementation_approach: '', pros: [], - limitations: [], + limitations: [], alternatives: '' }); @@ -1283,21 +1158,23 @@ class ImprovedMicroTaskAIPipeline { rank, task_relevance: taskRelevance } - }, 'evaluation', priority, evaluation.detailed_explanation, - taskRelevance, evaluation.limitations); + }, 'evaluation', priority, evaluation.detailed_explanation, + taskRelevance, evaluation.limitations); - this.addAuditEntry(context, 'micro-task', 'tool-evaluation', - { toolName: tool.name, rank, existingTaskRelevance: taskRelevance, derivedPriority: priority }, - { + this.addAuditEntry( + context, + 'micro-task', + 'tool-evaluation', + { toolName: tool.name, rank, existingTaskRelevance: taskRelevance }, + { hasExplanation: !!evaluation.detailed_explanation, hasImplementationApproach: !!evaluation.implementation_approach, prosCount: evaluation.pros?.length || 0, - limitationsCount: evaluation.limitations?.length || 0, - hasLimitations: Array.isArray(evaluation.limitations) && evaluation.limitations.length > 0 + limitationsCount: evaluation.limitations?.length || 0 }, 70, Date.now() - result.processingTimeMs, - { toolType: tool.type, explanationOnly: true, priorityDerived: true, limitationsExtracted: true } + { toolType: tool.type } ); } @@ -1305,6 +1182,7 @@ class ImprovedMicroTaskAIPipeline { } private async selectBackgroundKnowledge(context: AnalysisContext): Promise { + console.log('[AI-PIPELINE] Starting background knowledge selection micro-task'); const availableConcepts = context.filteredData.concepts; if (availableConcepts.length === 0) { @@ -1316,23 +1194,25 @@ class ImprovedMicroTaskAIPipeline { }; } - const selectedToolNames = context.selectedTools?.map(st => st.tool.name) || []; + const selectedToolNames = context.selectedTools?.map((st: any) => st.tool && st.tool.name).filter(Boolean) || []; const prompt = getPrompt('backgroundKnowledgeSelection', context.userQuery, context.mode, selectedToolNames, availableConcepts); - const result = await this.callMicroTaskAI(prompt, context, 700); if (result.success) { const selections = this.safeParseJSON(result.content, []); if (Array.isArray(selections)) { - context.backgroundKnowledge = selections.filter((sel: any) => + context.backgroundKnowledge = selections.filter((sel: any) => sel.conceptName && availableConcepts.some((concept: any) => concept.name === sel.conceptName) ).map((sel: any) => ({ concept: availableConcepts.find((c: any) => c.name === sel.conceptName), relevance: sel.relevance })); - this.addAuditEntry(context, 'micro-task', 'background-knowledge-selection', + this.addAuditEntry( + context, + 'micro-task', + 'background-knowledge-selection', { availableConcepts: availableConcepts.length }, { selectedConcepts: context.backgroundKnowledge?.length || 0 }, context.backgroundKnowledge && context.backgroundKnowledge.length > 0 ? 75 : 40, @@ -1346,9 +1226,10 @@ class ImprovedMicroTaskAIPipeline { } private async generateFinalRecommendations(context: AnalysisContext): Promise { - const selectedToolNames = context.selectedTools?.map(st => st.tool.name) || []; + console.log('[AI-PIPELINE] Starting final recommendations micro-task'); + const selectedToolNames = context.selectedTools?.map((st: any) => st.tool && st.tool.name).filter(Boolean) || []; const prompt = getPrompt('finalRecommendations', context.mode === 'workflow', context.userQuery, selectedToolNames); - + const result = await this.callMicroTaskAI(prompt, context, 350); return result; } @@ -1364,9 +1245,6 @@ class ImprovedMicroTaskAIPipeline { if (apiKey) { headers['Authorization'] = `Bearer ${apiKey}`; - console.log('[AI PIPELINE] Using API key authentication'); - } else { - console.log('[AI PIPELINE] No API key - making request without authentication'); } const requestBody = { @@ -1385,7 +1263,7 @@ class ImprovedMicroTaskAIPipeline { if (!response.ok) { const errorText = await response.text(); - console.error(`[AI PIPELINE] AI API Error ${response.status}:`, errorText); + console.error('[AI-PIPELINE] AI API Error:', response.status, errorText); throw new Error(`AI API error: ${response.status} - ${errorText}`); } @@ -1393,14 +1271,14 @@ class ImprovedMicroTaskAIPipeline { const content = data.choices?.[0]?.message?.content; if (!content) { - console.error('[AI PIPELINE] No response content:', data); + console.error('[AI-PIPELINE] No response content from AI model'); throw new Error('No response from AI model'); } return content; } catch (error) { - console.error('[AI PIPELINE] AI service call failed:', error.message); + console.error('[AI-PIPELINE] AI service call failed:', error.message); throw error; } } @@ -1417,9 +1295,6 @@ class ImprovedMicroTaskAIPipeline { mode: string, context: AnalysisContext ): Promise<{ tools: any[], concepts: any[] }> { - const selectionStart = Date.now(); - - // Call the existing aiSelectionWithFullData const result = await this.aiSelectionWithFullData( userQuery, filteredData.tools, @@ -1429,7 +1304,7 @@ class ImprovedMicroTaskAIPipeline { context ); - console.log(`[AI PIPELINE] AI selection complete: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`); + console.log('[AI-PIPELINE] AI selection complete:', result.selectedTools.length, 'tools,', result.selectedConcepts.length, 'concepts'); return { tools: result.selectedTools, @@ -1442,9 +1317,7 @@ class ImprovedMicroTaskAIPipeline { let completeTasks = 0; let failedTasks = 0; - this.tempAuditEntries = []; - - console.log(`[AI PIPELINE] Starting ${mode} query processing with enhanced confidence scoring`); + console.log('[AI-PIPELINE] Starting', mode, 'query processing'); try { const toolsData = await getCompressedToolsDataForAI(); @@ -1459,22 +1332,16 @@ class ImprovedMicroTaskAIPipeline { seenToolNames: new Set(), auditTrail: [], embeddingsSimilarities: new Map(), - // Add this new property to store AI selections aiSelectedTools: [], aiSelectedConcepts: [] }; - // Get embedding-filtered candidates const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode, context); - - // IMPORTANT: Now do the AI selection from those candidates const aiSelection = await this.performAISelection(filteredData, userQuery, mode, context); - // Store the AI's selections in context context.aiSelectedTools = aiSelection.tools; context.aiSelectedConcepts = aiSelection.concepts; - // Update filteredData to only include what AI selected context.filteredData = { tools: aiSelection.tools, concepts: aiSelection.concepts, @@ -1482,17 +1349,16 @@ class ImprovedMicroTaskAIPipeline { phases: filteredData.phases, 'domain-agnostic-software': filteredData['domain-agnostic-software'] }; - - this.mergeTemporaryAuditEntries(context); - console.log(`[AI PIPELINE] Starting micro-tasks with ${context.filteredData.tools.length} AI-selected tools`); - - this.addAuditEntry(context, 'initialization', 'pipeline-start', + this.addAuditEntry( + context, + 'initialization', + 'pipeline-start', { userQuery, mode, toolsDataLoaded: !!toolsData }, { candidateTools: filteredData.tools.length, candidateConcepts: filteredData.concepts.length }, - 90, + 90, startTime, - { auditEnabled: this.auditConfig.enabled, confidenceScoringEnabled: true } + { auditEnabled: this.auditConfig.enabled } ); const analysisResult = await this.analyzeScenario(context); @@ -1510,33 +1376,12 @@ class ImprovedMicroTaskAIPipeline { if (mode === 'workflow') { const phases = toolsData.phases || []; - console.log(`[AI PIPELINE] Debug: Starting phase-specific selection with ${context.filteredData.tools.length} AI-selected tools`); - console.log(`[AI PIPELINE] Debug: Available phases: ${phases.map(p => p.id).join(', ')}`); - - context.filteredData.tools.forEach(tool => { - console.log(`[AI PIPELINE] Debug: ${tool.name} (${tool.type}) - phases: ${tool.phases?.join(', ') || 'NO PHASES'}`); - }); - - phases.forEach(phase => { - const matchingTools = context.filteredData.tools.filter(tool => - tool.phases && tool.phases.includes(phase.id) - ); - const matchingMethods = matchingTools.filter(t => t.type === 'method'); - const matchingSoftware = matchingTools.filter(t => t.type === 'software'); - - console.log(`[AI PIPELINE] Debug: Phase ${phase.id} has ${matchingTools.length} matching tools (${matchingMethods.length} methods, ${matchingSoftware.length} software)`); - if (matchingTools.length > 0) { - console.log(`[AI PIPELINE] Debug: Phase ${phase.id} tools: ${matchingTools.map(t => `${t.name}(${t.type})`).join(', ')}`); - } - }); - for (const phase of phases) { const toolSelectionResult = await this.selectToolsForPhase(context, phase); if (toolSelectionResult.success) completeTasks++; else failedTasks++; await this.delay(this.microTaskDelay); } - console.log('[AI PIPELINE] Checking for underrepresented phases...'); await this.completeUnderrepresentedPhases(context, toolsData, userQuery); } else { @@ -1557,28 +1402,28 @@ class ImprovedMicroTaskAIPipeline { const recommendation = this.buildRecommendation(context, mode, finalResult.content); - this.addAuditEntry(context, 'completion', 'pipeline-end', + this.addAuditEntry( + context, + 'completion', + 'pipeline-end', { completedTasks: completeTasks, failedTasks }, { finalRecommendation: !!recommendation, auditEntriesGenerated: context.auditTrail.length }, completeTasks > failedTasks ? 85 : 60, startTime, - { totalProcessingTimeMs: Date.now() - startTime, confidenceScoresGenerated: context.selectedTools?.length || 0 } + { totalProcessingTimeMs: Date.now() - startTime } ); const processingStats = { embeddingsUsed: embeddingsService.isEnabled(), candidatesFromEmbeddings: filteredData.tools.length, - finalSelectedItems: (context.selectedTools?.length || 0) + - (context.backgroundKnowledge?.length || 0), + finalSelectedItems: (context.selectedTools?.length || 0) + (context.backgroundKnowledge?.length || 0), processingTimeMs: Date.now() - startTime, microTasksCompleted: completeTasks, microTasksFailed: failedTasks, contextContinuityUsed: true }; - console.log(`[AI PIPELINE] Completed: ${completeTasks} tasks, Failed: ${failedTasks} tasks`); - console.log(`[AI PIPELINE] Enhanced confidence scores generated: ${context.selectedTools?.length || 0}`); - console.log(`[AI PIPELINE] Audit trail entries: ${context.auditTrail.length}`); + console.log('[AI-PIPELINE] Processing complete. Tasks completed:', completeTasks, 'failed:', failedTasks); return { recommendation: { @@ -1589,10 +1434,7 @@ class ImprovedMicroTaskAIPipeline { }; } catch (error) { - console.error('[AI PIPELINE] Processing failed:', error); - - this.tempAuditEntries = []; - + console.error('[AI-PIPELINE] Processing failed:', error); throw error; } } @@ -1600,22 +1442,21 @@ class ImprovedMicroTaskAIPipeline { private buildRecommendation(context: AnalysisContext, mode: string, finalContent: string): any { const isWorkflow = mode === 'workflow'; - console.log(`[AI PIPELINE] Building recommendation for ${mode} mode`); - console.log(`[AI PIPELINE] Selected tools count: ${context.selectedTools?.length || 0}`); + console.log('[AI-PIPELINE] Building recommendation for', mode, 'mode with', context.selectedTools?.length || 0, 'tools'); if (context.selectedTools && context.selectedTools.length > 0) { - const methods = context.selectedTools.filter(st => st.tool.type === 'method'); - const software = context.selectedTools.filter(st => st.tool.type === 'software'); + const methods = context.selectedTools.filter((st: any) => st.tool && st.tool.type === 'method'); + const software = context.selectedTools.filter((st: any) => st.tool && st.tool.type === 'software'); - console.log(`[AI PIPELINE] Final selection breakdown: ${methods.length} methods, ${software.length} software`); - console.log(`[AI PIPELINE] Method names: ${methods.map(m => m.tool.name).join(', ')}`); - console.log(`[AI PIPELINE] Software names: ${software.map(s => s.tool.name).join(', ')}`); + console.log('[AI-PIPELINE] Final selection breakdown:', methods.length, 'methods,', software.length, 'software'); + console.log('[AI-PIPELINE] Method names:', methods.map((m: any) => m.tool.name).join(', ')); + console.log('[AI-PIPELINE] Software names:', software.map((s: any) => s.tool.name).join(', ')); - context.selectedTools.forEach((st, index) => { - console.log(`[AI PIPELINE] Selected tool ${index + 1}: ${st.tool.name} (${st.tool.type}) - Phase: ${st.phase}, Priority: ${st.priority}`); + context.selectedTools.forEach((st: any, index: number) => { + console.log('[AI-PIPELINE] Selected tool', index + 1, ':', st.tool.name, '(' + st.tool.type + ') - Phase:', st.phase, ', Priority:', st.priority); }); } else { - console.warn(`[AI PIPELINE] WARNING: No tools in selectedTools array!`); + console.warn('[AI-PIPELINE] WARNING: No tools in selectedTools array!'); } const base = { @@ -1623,18 +1464,16 @@ class ImprovedMicroTaskAIPipeline { isWorkflow ? context.scenarioAnalysis : context.problemAnalysis, investigation_approach: context.investigationApproach, critical_considerations: context.criticalConsiderations, - background_knowledge: context.backgroundKnowledge?.map(bk => ({ + background_knowledge: context.backgroundKnowledge?.map((bk: any) => ({ concept_name: bk.concept.name, relevance: bk.relevance })) || [] }; - const processedAuditTrail = this.auditConfig.enabled && context.auditTrail - ? context.auditTrail - : []; + const processedAuditTrail = this.auditConfig.enabled && context.auditTrail ? context.auditTrail : []; if (isWorkflow) { - const recommendedToolsWithConfidence = context.selectedTools?.map(st => { + const recommendedToolsWithConfidence = context.selectedTools?.map((st: any) => { const confidence = this.calculateRecommendationConfidence( st.tool, context, @@ -1642,9 +1481,12 @@ class ImprovedMicroTaskAIPipeline { st.limitations || [] ); - this.addAuditEntry(context, 'validation', 'confidence-scoring', + this.addAuditEntry( + context, + 'validation', + 'confidence-scoring', { toolName: st.tool.name, toolType: st.tool.type, phase: st.phase }, - { + { overall: confidence.overall, components: { semantic: confidence.semanticRelevance, @@ -1668,11 +1510,6 @@ class ImprovedMicroTaskAIPipeline { }; }) || []; - console.log(`[AI PIPELINE] Final workflow recommendations: ${recommendedToolsWithConfidence.length} tools`); - const finalMethods = recommendedToolsWithConfidence.filter(r => r.type === 'method'); - const finalSoftware = recommendedToolsWithConfidence.filter(r => r.type === 'software'); - console.log(`[AI PIPELINE] Final breakdown: ${finalMethods.length} methods, ${finalSoftware.length} software`); - return { ...base, recommended_tools: recommendedToolsWithConfidence, @@ -1680,24 +1517,26 @@ class ImprovedMicroTaskAIPipeline { auditTrail: processedAuditTrail }; } else { - const recommendedToolsWithConfidence = context.selectedTools?.map(st => { + const recommendedToolsWithConfidence = context.selectedTools?.map((st: any) => { const confidence = this.calculateRecommendationConfidence( st.tool, context, st.taskRelevance || 70, - st.limitations || [] + st.limitations || [] ); - this.addAuditEntry(context, 'validation', 'confidence-scoring', + this.addAuditEntry( + context, + 'validation', + 'confidence-scoring', { toolName: st.tool.name, toolType: st.tool.type, rank: st.tool.evaluation?.rank || 1 }, - { + { overall: confidence.overall, - suitabilityAlignment: st.priority === 'high' && confidence.overall >= this.confidenceConfig.highThreshold, - limitationsUsed: st.limitations?.length || 0 + suitabilityAlignment: st.priority === 'high' && confidence.overall >= this.confidenceConfig.highThreshold }, confidence.overall, Date.now(), - { strengthCount: confidence.strengthIndicators.length, limitationsCount: confidence.uncertaintyFactors.length } + { strengthCount: confidence.strengthIndicators.length } ); return { @@ -1716,8 +1555,6 @@ class ImprovedMicroTaskAIPipeline { }; }) || []; - console.log(`[AI PIPELINE] Final tool recommendations: ${recommendedToolsWithConfidence.length} tools`); - return { ...base, recommended_tools: recommendedToolsWithConfidence,