Context too long, using prompt only
This commit is contained in:
parent
7c3cc7ec9a
commit
4b0d208ef5
38
.env.example
38
.env.example
@ -54,6 +54,11 @@ AI_SIMILARITY_THRESHOLD=0.3
|
|||||||
AI_EMBEDDING_SELECTION_LIMIT=30
|
AI_EMBEDDING_SELECTION_LIMIT=30
|
||||||
AI_EMBEDDING_CONCEPTS_LIMIT=15
|
AI_EMBEDDING_CONCEPTS_LIMIT=15
|
||||||
|
|
||||||
|
# Maximum tools/concepts sent to AI when embeddings are DISABLED
|
||||||
|
# Set to 0 for no limit (WARNING: may cause token overflow with large datasets)
|
||||||
|
AI_NO_EMBEDDINGS_TOOL_LIMIT=0
|
||||||
|
AI_NO_EMBEDDINGS_CONCEPT_LIMIT=0
|
||||||
|
|
||||||
# === AI SELECTION STAGE ===
|
# === AI SELECTION STAGE ===
|
||||||
# Maximum tools the AI can select from embedding candidates
|
# Maximum tools the AI can select from embedding candidates
|
||||||
# 🤖 This is the SECOND filter - AI intelligent selection
|
# 🤖 This is the SECOND filter - AI intelligent selection
|
||||||
@ -98,17 +103,21 @@ AI_EMBEDDINGS_BATCH_SIZE=10
|
|||||||
# Delay between embedding batches (milliseconds)
|
# Delay between embedding batches (milliseconds)
|
||||||
AI_EMBEDDINGS_BATCH_DELAY_MS=1000
|
AI_EMBEDDINGS_BATCH_DELAY_MS=1000
|
||||||
|
|
||||||
|
# Maximum tools sent to AI for detailed analysis (micro-tasks)
|
||||||
|
AI_MAX_TOOLS_TO_ANALYZE=20
|
||||||
|
AI_MAX_CONCEPTS_TO_ANALYZE=10
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 5. AI CONTEXT & TOKEN MANAGEMENT
|
# 5. AI CONTEXT & TOKEN MANAGEMENT
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# Maximum context tokens to maintain across micro-tasks
|
# Maximum context tokens to maintain across micro-tasks
|
||||||
# Controls how much conversation history is preserved between AI calls
|
# Controls how much conversation history is preserved between AI calls
|
||||||
AI_MAX_CONTEXT_TOKENS=3000
|
AI_MAX_CONTEXT_TOKENS=4000
|
||||||
|
|
||||||
# Maximum tokens per individual AI prompt
|
# Maximum tokens per individual AI prompt
|
||||||
# Larger = more context per call | Smaller = faster responses
|
# Larger = more context per call | Smaller = faster responses
|
||||||
AI_MAX_PROMPT_TOKENS=1200
|
AI_MAX_PROMPT_TOKENS=1500
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 6. AUTHENTICATION & AUTHORIZATION (OPTIONAL)
|
# 6. AUTHENTICATION & AUTHORIZATION (OPTIONAL)
|
||||||
@ -169,7 +178,7 @@ GIT_API_TOKEN=your-git-api-token
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# Enable detailed audit trail of AI decision-making
|
# Enable detailed audit trail of AI decision-making
|
||||||
FORENSIC_AUDIT_ENABLED=false
|
FORENSIC_AUDIT_ENABLED=true
|
||||||
|
|
||||||
# Audit detail level: minimal, standard, verbose
|
# Audit detail level: minimal, standard, verbose
|
||||||
FORENSIC_AUDIT_DETAIL_LEVEL=standard
|
FORENSIC_AUDIT_DETAIL_LEVEL=standard
|
||||||
@ -199,23 +208,16 @@ CONFIDENCE_HIGH_THRESHOLD=80
|
|||||||
# PERFORMANCE TUNING PRESETS
|
# PERFORMANCE TUNING PRESETS
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# 🚀 FOR FASTER RESPONSES (less comprehensive):
|
# 🚀 FOR FASTER RESPONSES (prevent token overflow):
|
||||||
# AI_EMBEDDING_CANDIDATES=20
|
# AI_NO_EMBEDDINGS_TOOL_LIMIT=25
|
||||||
# AI_MAX_SELECTED_ITEMS=15
|
# AI_NO_EMBEDDINGS_CONCEPT_LIMIT=10
|
||||||
# AI_MICRO_TASK_DELAY_MS=200
|
|
||||||
# AI_MAX_CONTEXT_TOKENS=2000
|
|
||||||
|
|
||||||
# 🎯 FOR BETTER QUALITY (more comprehensive):
|
# 🎯 FOR FULL DATABASE ACCESS (risk of truncation):
|
||||||
# AI_EMBEDDING_CANDIDATES=60
|
# AI_NO_EMBEDDINGS_TOOL_LIMIT=0
|
||||||
# AI_MAX_SELECTED_ITEMS=40
|
# AI_NO_EMBEDDINGS_CONCEPT_LIMIT=0
|
||||||
# AI_MICRO_TASK_DELAY_MS=800
|
|
||||||
# AI_MAX_CONTEXT_TOKENS=4000
|
|
||||||
|
|
||||||
# 🔋 FOR LOW-POWER SYSTEMS (minimal resources):
|
# 🔋 FOR LOW-POWER SYSTEMS:
|
||||||
# AI_EMBEDDING_CANDIDATES=15
|
# AI_NO_EMBEDDINGS_TOOL_LIMIT=15
|
||||||
# AI_MAX_SELECTED_ITEMS=10
|
|
||||||
# AI_RATE_LIMIT_MAX_REQUESTS=2
|
|
||||||
# AI_MICRO_TASK_DELAY_MS=1000
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# FEATURE COMBINATIONS GUIDE
|
# FEATURE COMBINATIONS GUIDE
|
||||||
|
@ -113,64 +113,6 @@ tools:
|
|||||||
accessType: download
|
accessType: download
|
||||||
license: VSL
|
license: VSL
|
||||||
knowledgebase: false
|
knowledgebase: false
|
||||||
- name: TheHive 5
|
|
||||||
icon: 🐝
|
|
||||||
type: software
|
|
||||||
description: >-
|
|
||||||
Die zentrale Incident-Response-Plattform orchestriert komplexe
|
|
||||||
Sicherheitsvorfälle vom ersten Alert bis zum Abschlussbericht. Jeder Case
|
|
||||||
wird strukturiert durch Observables (IOCs), Tasks und Zeitleisten
|
|
||||||
abgebildet. Die Cortex-Integration automatisiert Analysen durch Dutzende
|
|
||||||
Analyzer - von VirusTotal-Checks bis Sandbox-Detonation.
|
|
||||||
MISP-Synchronisation reichert Cases mit Threat-Intelligence an. Das
|
|
||||||
ausgeklügelte Rollen- und Rechtesystem ermöglicht sichere Zusammenarbeit
|
|
||||||
zwischen SOC-Analysten, Forensikern und Management. Templates
|
|
||||||
standardisieren Response-Prozesse nach Incident-Typ. Die RESTful API
|
|
||||||
integriert nahtlos mit SIEM, SOAR und Ticketing-Systemen. Metrics und
|
|
||||||
KPIs messen die Team-Performance. Die Community Edition bleibt kostenlos
|
|
||||||
für kleinere Teams, während Gold/Platinum-Lizenzen Enterprise-Features
|
|
||||||
bieten.
|
|
||||||
domains:
|
|
||||||
- incident-response
|
|
||||||
- static-investigations
|
|
||||||
- malware-analysis
|
|
||||||
- network-forensics
|
|
||||||
- fraud-investigation
|
|
||||||
phases:
|
|
||||||
- data-collection
|
|
||||||
- examination
|
|
||||||
- analysis
|
|
||||||
- reporting
|
|
||||||
platforms:
|
|
||||||
- Web
|
|
||||||
related_software:
|
|
||||||
- MISP
|
|
||||||
- Cortex
|
|
||||||
- Elasticsearch
|
|
||||||
domain-agnostic-software:
|
|
||||||
- collaboration-general
|
|
||||||
skillLevel: intermediate
|
|
||||||
accessType: server-based
|
|
||||||
url: https://strangebee.com/thehive/
|
|
||||||
projectUrl: ''
|
|
||||||
license: Community Edition (Discontinued) / Commercial
|
|
||||||
knowledgebase: false
|
|
||||||
statusUrl: https://uptime.example.lab/api/badge/1/status
|
|
||||||
tags:
|
|
||||||
- web-interface
|
|
||||||
- case-management
|
|
||||||
- collaboration
|
|
||||||
- api
|
|
||||||
- workflow
|
|
||||||
- multi-user-support
|
|
||||||
- cortex-analyzer
|
|
||||||
- misp-integration
|
|
||||||
- playbooks
|
|
||||||
- metrics
|
|
||||||
- rbac
|
|
||||||
- template-driven
|
|
||||||
related_concepts:
|
|
||||||
- Digital Evidence Chain of Custody
|
|
||||||
- name: MISP
|
- name: MISP
|
||||||
icon: 🌐
|
icon: 🌐
|
||||||
type: software
|
type: software
|
||||||
@ -223,7 +165,6 @@ tools:
|
|||||||
related_concepts:
|
related_concepts:
|
||||||
- Hash Functions & Digital Signatures
|
- Hash Functions & Digital Signatures
|
||||||
related_software:
|
related_software:
|
||||||
- TheHive 5
|
|
||||||
- Cortex
|
- Cortex
|
||||||
- OpenCTI
|
- OpenCTI
|
||||||
- name: DFIR-IRIS
|
- name: DFIR-IRIS
|
||||||
@ -260,7 +201,6 @@ tools:
|
|||||||
platforms:
|
platforms:
|
||||||
- Web
|
- Web
|
||||||
related_software:
|
related_software:
|
||||||
- TheHive 5
|
|
||||||
- MISP
|
- MISP
|
||||||
- OpenCTI
|
- OpenCTI
|
||||||
domain-agnostic-software:
|
domain-agnostic-software:
|
||||||
|
@ -94,18 +94,15 @@ ${input}
|
|||||||
`.trim();
|
`.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced AI service call function
|
|
||||||
async function callAIService(prompt: string): Promise<Response> {
|
async function callAIService(prompt: string): Promise<Response> {
|
||||||
const endpoint = AI_ENDPOINT;
|
const endpoint = AI_ENDPOINT;
|
||||||
const apiKey = AI_ANALYZER_API_KEY;
|
const apiKey = AI_ANALYZER_API_KEY;
|
||||||
const model = AI_ANALYZER_MODEL;
|
const model = AI_ANALYZER_MODEL;
|
||||||
|
|
||||||
// Simple headers - add auth only if API key exists
|
|
||||||
let headers: Record<string, string> = {
|
let headers: Record<string, string> = {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add authentication if API key is provided
|
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
headers['Authorization'] = `Bearer ${apiKey}`;
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
||||||
console.log('[ENHANCE API] Using API key authentication');
|
console.log('[ENHANCE API] Using API key authentication');
|
||||||
@ -113,7 +110,6 @@ async function callAIService(prompt: string): Promise<Response> {
|
|||||||
console.log('[ENHANCE API] No API key - making request without authentication');
|
console.log('[ENHANCE API] No API key - making request without authentication');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple request body
|
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
model,
|
model,
|
||||||
messages: [{ role: 'user', content: prompt }],
|
messages: [{ role: 'user', content: prompt }],
|
||||||
@ -124,8 +120,6 @@ async function callAIService(prompt: string): Promise<Response> {
|
|||||||
presence_penalty: 0.1
|
presence_penalty: 0.1
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXED: This function is already being called through enqueueApiCall in the main handler
|
|
||||||
// So we can use direct fetch here since the queuing happens at the caller level
|
|
||||||
return fetch(`${endpoint}/v1/chat/completions`, {
|
return fetch(`${endpoint}/v1/chat/completions`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
@ -214,7 +208,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
success: true,
|
success: true,
|
||||||
questions,
|
questions,
|
||||||
taskId,
|
taskId,
|
||||||
inputComplete: questions.length === 0 // Flag to indicate if input seems complete
|
inputComplete: questions.length === 0
|
||||||
}), {
|
}), {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
@ -31,7 +31,6 @@ interface AnalysisResult {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Audit Trail Types
|
|
||||||
interface AuditEntry {
|
interface AuditEntry {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
phase: string; // 'retrieval', 'selection', 'micro-task-N'
|
phase: string; // 'retrieval', 'selection', 'micro-task-N'
|
||||||
@ -40,10 +39,9 @@ interface AuditEntry {
|
|||||||
output: any; // What came out of this step
|
output: any; // What came out of this step
|
||||||
confidence: number; // 0-100: How confident we are in this step
|
confidence: number; // 0-100: How confident we are in this step
|
||||||
processingTimeMs: number;
|
processingTimeMs: number;
|
||||||
metadata: Record<string, any>; // Additional context
|
metadata: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced AnalysisContext with Audit Trail
|
|
||||||
interface AnalysisContext {
|
interface AnalysisContext {
|
||||||
userQuery: string;
|
userQuery: string;
|
||||||
mode: string;
|
mode: string;
|
||||||
@ -62,7 +60,6 @@ interface AnalysisContext {
|
|||||||
|
|
||||||
seenToolNames: Set<string>;
|
seenToolNames: Set<string>;
|
||||||
|
|
||||||
// NEW: Audit Trail
|
|
||||||
auditTrail: AuditEntry[];
|
auditTrail: AuditEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,25 +75,24 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
private similarityThreshold: number;
|
private similarityThreshold: number;
|
||||||
private microTaskDelay: number;
|
private microTaskDelay: number;
|
||||||
|
|
||||||
// NEW: Embedding selection limits (top N from pre-filtered candidates)
|
|
||||||
private embeddingSelectionLimit: number;
|
private embeddingSelectionLimit: number;
|
||||||
private embeddingConceptsLimit: number;
|
private embeddingConceptsLimit: number;
|
||||||
|
|
||||||
|
private noEmbeddingsToolLimit: number;
|
||||||
|
private noEmbeddingsConceptLimit: number;
|
||||||
|
|
||||||
// NEW: Embeddings efficiency thresholds
|
|
||||||
private embeddingsMinTools: number;
|
private embeddingsMinTools: number;
|
||||||
private embeddingsMaxReductionRatio: number;
|
private embeddingsMaxReductionRatio: number;
|
||||||
|
|
||||||
private maxContextTokens: number;
|
private maxContextTokens: number;
|
||||||
private maxPromptTokens: number;
|
private maxPromptTokens: number;
|
||||||
|
|
||||||
// Audit Configuration
|
|
||||||
private auditConfig: {
|
private auditConfig: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
detailLevel: 'minimal' | 'standard' | 'verbose';
|
detailLevel: 'minimal' | 'standard' | 'verbose';
|
||||||
retentionHours: number;
|
retentionHours: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Temporary audit storage for pre-context operations
|
|
||||||
private tempAuditEntries: AuditEntry[] = [];
|
private tempAuditEntries: AuditEntry[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -106,36 +102,33 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
model: this.getEnv('AI_ANALYZER_MODEL')
|
model: this.getEnv('AI_ANALYZER_MODEL')
|
||||||
};
|
};
|
||||||
|
|
||||||
// Core pipeline configuration
|
|
||||||
this.maxSelectedItems = parseInt(process.env.AI_MAX_SELECTED_ITEMS || '25', 10);
|
this.maxSelectedItems = parseInt(process.env.AI_MAX_SELECTED_ITEMS || '25', 10);
|
||||||
this.embeddingCandidates = parseInt(process.env.AI_EMBEDDING_CANDIDATES || '50', 10);
|
this.embeddingCandidates = parseInt(process.env.AI_EMBEDDING_CANDIDATES || '50', 10);
|
||||||
this.similarityThreshold = parseFloat(process.env.AI_SIMILARITY_THRESHOLD || '0.3');
|
this.similarityThreshold = parseFloat(process.env.AI_SIMILARITY_THRESHOLD || '0.3');
|
||||||
this.microTaskDelay = parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10);
|
this.microTaskDelay = parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10);
|
||||||
|
|
||||||
// NEW: Embedding selection limits (top N from pre-filtered candidates)
|
|
||||||
this.embeddingSelectionLimit = parseInt(process.env.AI_EMBEDDING_SELECTION_LIMIT || '30', 10);
|
this.embeddingSelectionLimit = parseInt(process.env.AI_EMBEDDING_SELECTION_LIMIT || '30', 10);
|
||||||
this.embeddingConceptsLimit = parseInt(process.env.AI_EMBEDDING_CONCEPTS_LIMIT || '15', 10);
|
this.embeddingConceptsLimit = parseInt(process.env.AI_EMBEDDING_CONCEPTS_LIMIT || '15', 10);
|
||||||
|
|
||||||
|
this.noEmbeddingsToolLimit = parseInt(process.env.AI_NO_EMBEDDINGS_TOOL_LIMIT || '0', 10);
|
||||||
|
this.noEmbeddingsConceptLimit = parseInt(process.env.AI_NO_EMBEDDINGS_CONCEPT_LIMIT || '0', 10);
|
||||||
|
|
||||||
// NEW: Embeddings efficiency thresholds
|
|
||||||
this.embeddingsMinTools = parseInt(process.env.AI_EMBEDDINGS_MIN_TOOLS || '8', 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.embeddingsMaxReductionRatio = parseFloat(process.env.AI_EMBEDDINGS_MAX_REDUCTION_RATIO || '0.75');
|
||||||
|
|
||||||
// Context management
|
|
||||||
this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10);
|
this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10);
|
||||||
this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10);
|
this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10);
|
||||||
|
|
||||||
// Audit configuration
|
|
||||||
this.auditConfig = {
|
this.auditConfig = {
|
||||||
enabled: process.env.FORENSIC_AUDIT_ENABLED === 'true',
|
enabled: process.env.FORENSIC_AUDIT_ENABLED === 'true',
|
||||||
detailLevel: (process.env.FORENSIC_AUDIT_DETAIL_LEVEL as any) || 'standard',
|
detailLevel: (process.env.FORENSIC_AUDIT_DETAIL_LEVEL as any) || 'standard',
|
||||||
retentionHours: parseInt(process.env.FORENSIC_AUDIT_RETENTION_HOURS || '72', 10)
|
retentionHours: parseInt(process.env.FORENSIC_AUDIT_RETENTION_HOURS || '72', 10)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Log configuration for debugging
|
|
||||||
console.log('[AI PIPELINE] Configuration loaded:', {
|
console.log('[AI PIPELINE] Configuration loaded:', {
|
||||||
embeddingCandidates: this.embeddingCandidates,
|
embeddingCandidates: this.embeddingCandidates,
|
||||||
embeddingSelection: `${this.embeddingSelectionLimit} tools, ${this.embeddingConceptsLimit} concepts`,
|
embeddingSelection: `${this.embeddingSelectionLimit} tools, ${this.embeddingConceptsLimit} concepts`,
|
||||||
embeddingsThresholds: `min ${this.embeddingsMinTools} tools, max ${this.embeddingsMaxReductionRatio * 100}% of total`,
|
noEmbeddingsLimits: `${this.noEmbeddingsToolLimit || 'unlimited'} tools, ${this.noEmbeddingsConceptLimit || 'unlimited'} concepts`,
|
||||||
auditEnabled: this.auditConfig.enabled
|
auditEnabled: this.auditConfig.enabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -148,7 +141,6 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Audit Trail Utility Functions
|
|
||||||
private addAuditEntry(
|
private addAuditEntry(
|
||||||
context: AnalysisContext | null,
|
context: AnalysisContext | null,
|
||||||
phase: string,
|
phase: string,
|
||||||
@ -175,22 +167,18 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
if (context) {
|
if (context) {
|
||||||
context.auditTrail.push(auditEntry);
|
context.auditTrail.push(auditEntry);
|
||||||
} else {
|
} else {
|
||||||
// Store in temporary array for later merging
|
|
||||||
this.tempAuditEntries.push(auditEntry);
|
this.tempAuditEntries.push(auditEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log for debugging when audit is enabled
|
|
||||||
console.log(`[AUDIT] ${phase}/${action}: ${confidence}% confidence, ${Date.now() - startTime}ms`);
|
console.log(`[AUDIT] ${phase}/${action}: ${confidence}% confidence, ${Date.now() - startTime}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Merge temporary audit entries into context
|
|
||||||
private mergeTemporaryAuditEntries(context: AnalysisContext): void {
|
private mergeTemporaryAuditEntries(context: AnalysisContext): void {
|
||||||
if (!this.auditConfig.enabled || this.tempAuditEntries.length === 0) return;
|
if (!this.auditConfig.enabled || this.tempAuditEntries.length === 0) return;
|
||||||
|
|
||||||
const entryCount = this.tempAuditEntries.length;
|
const entryCount = this.tempAuditEntries.length;
|
||||||
// Add temp entries to the beginning of the context audit trail
|
|
||||||
context.auditTrail.unshift(...this.tempAuditEntries);
|
context.auditTrail.unshift(...this.tempAuditEntries);
|
||||||
this.tempAuditEntries = []; // Clear temp storage
|
this.tempAuditEntries = [];
|
||||||
|
|
||||||
console.log(`[AUDIT] Merged ${entryCount} temporary audit entries into context`);
|
console.log(`[AUDIT] Merged ${entryCount} temporary audit entries into context`);
|
||||||
}
|
}
|
||||||
@ -222,15 +210,12 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
|
|
||||||
let confidence = 60; // Base confidence
|
let confidence = 60; // Base confidence
|
||||||
|
|
||||||
// Good selection ratio (not too many, not too few)
|
|
||||||
if (selectionRatio > 0.05 && selectionRatio < 0.3) confidence += 20;
|
if (selectionRatio > 0.05 && selectionRatio < 0.3) confidence += 20;
|
||||||
else if (selectionRatio <= 0.05) confidence -= 10; // Too few
|
else if (selectionRatio <= 0.05) confidence -= 10; // Too few
|
||||||
else confidence -= 15; // Too many
|
else confidence -= 15; // Too many
|
||||||
|
|
||||||
// Has detailed reasoning
|
|
||||||
if (hasReasoning) confidence += 15;
|
if (hasReasoning) confidence += 15;
|
||||||
|
|
||||||
// Selected tools have good distribution
|
|
||||||
if (result.selectedConcepts && result.selectedConcepts.length > 0) confidence += 5;
|
if (result.selectedConcepts && result.selectedConcepts.length > 0) confidence += 5;
|
||||||
|
|
||||||
return Math.min(95, Math.max(25, confidence));
|
return Math.min(95, Math.max(25, confidence));
|
||||||
@ -254,26 +239,106 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
|
|
||||||
private safeParseJSON(jsonString: string, fallback: any = null): any {
|
private safeParseJSON(jsonString: string, fallback: any = null): any {
|
||||||
try {
|
try {
|
||||||
const cleaned = jsonString
|
let cleaned = jsonString
|
||||||
.replace(/^```json\s*/i, '')
|
.replace(/^```json\s*/i, '')
|
||||||
.replace(/\s*```\s*$/g, '')
|
.replace(/\s*```\s*$/g, '')
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
|
if (!cleaned.endsWith('}') && !cleaned.endsWith(']')) {
|
||||||
|
console.warn('[AI PIPELINE] JSON appears truncated, attempting recovery...');
|
||||||
|
|
||||||
|
let lastCompleteStructure = '';
|
||||||
|
let braceCount = 0;
|
||||||
|
let bracketCount = 0;
|
||||||
|
let inString = false;
|
||||||
|
let escaped = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < cleaned.length; i++) {
|
||||||
|
const char = cleaned[i];
|
||||||
|
|
||||||
|
if (escaped) {
|
||||||
|
escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === '\\') {
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === '"' && !escaped) {
|
||||||
|
inString = !inString;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inString) {
|
||||||
|
if (char === '{') braceCount++;
|
||||||
|
if (char === '}') braceCount--;
|
||||||
|
if (char === '[') bracketCount++;
|
||||||
|
if (char === ']') bracketCount--;
|
||||||
|
|
||||||
|
if (braceCount === 0 && bracketCount === 0 && (char === '}' || char === ']')) {
|
||||||
|
lastCompleteStructure = cleaned.substring(0, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastCompleteStructure) {
|
||||||
|
console.log('[AI PIPELINE] Attempting to parse recovered JSON structure...');
|
||||||
|
cleaned = lastCompleteStructure;
|
||||||
|
} else {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const parsed = JSON.parse(cleaned);
|
const parsed = JSON.parse(cleaned);
|
||||||
|
|
||||||
|
if (parsed && typeof parsed === 'object') {
|
||||||
|
if (parsed.selectedTools === undefined) parsed.selectedTools = [];
|
||||||
|
if (parsed.selectedConcepts === undefined) parsed.selectedConcepts = [];
|
||||||
|
|
||||||
|
if (!Array.isArray(parsed.selectedTools)) parsed.selectedTools = [];
|
||||||
|
if (!Array.isArray(parsed.selectedConcepts)) parsed.selectedConcepts = [];
|
||||||
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[AI PIPELINE] JSON parsing failed:', error.message);
|
console.warn('[AI PIPELINE] JSON parsing failed:', error.message);
|
||||||
console.warn('[AI PIPELINE] Raw content:', jsonString.slice(0, 200));
|
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));
|
||||||
|
|
||||||
|
if (jsonString.includes('selectedTools')) {
|
||||||
|
const toolMatches = jsonString.match(/"([^"]+)"/g);
|
||||||
|
if (toolMatches && toolMatches.length > 0) {
|
||||||
|
console.log('[AI PIPELINE] Attempting partial recovery from broken JSON...');
|
||||||
|
const possibleTools = toolMatches
|
||||||
|
.map(match => match.replace(/"/g, ''))
|
||||||
|
.filter(name => name.length > 2 && !['selectedTools', 'selectedConcepts', 'reasoning'].includes(name))
|
||||||
|
.slice(0, 15); // Reasonable limit
|
||||||
|
|
||||||
|
if (possibleTools.length > 0) {
|
||||||
|
console.log(`[AI PIPELINE] Recovered ${possibleTools.length} possible tool names from broken JSON`);
|
||||||
|
return {
|
||||||
|
selectedTools: possibleTools,
|
||||||
|
selectedConcepts: [],
|
||||||
|
reasoning: 'Recovered from truncated response'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private addToolToSelection(context: AnalysisContext, tool: any, phase: string, priority: string, justification?: string): boolean {
|
private addToolToSelection(context: AnalysisContext, tool: any, phase: string, priority: string, justification?: string): boolean {
|
||||||
if (context.seenToolNames.has(tool.name)) {
|
|
||||||
console.log(`[AI PIPELINE] Skipping duplicate tool: ${tool.name}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.seenToolNames.add(tool.name);
|
context.seenToolNames.add(tool.name);
|
||||||
if (!context.selectedTools) context.selectedTools = [];
|
if (!context.selectedTools) context.selectedTools = [];
|
||||||
|
|
||||||
@ -302,11 +367,9 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
|
|
||||||
console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`);
|
console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`);
|
||||||
|
|
||||||
// Create lookup maps for O(1) access while preserving original data
|
|
||||||
const toolsMap = new Map<string, any>(toolsData.tools.map((tool: any) => [tool.name, tool]));
|
const toolsMap = new Map<string, any>(toolsData.tools.map((tool: any) => [tool.name, tool]));
|
||||||
const conceptsMap = new Map<string, any>(toolsData.concepts.map((concept: any) => [concept.name, concept]));
|
const conceptsMap = new Map<string, any>(toolsData.concepts.map((concept: any) => [concept.name, concept]));
|
||||||
|
|
||||||
// Process in similarity order, preserving the ranking
|
|
||||||
const similarTools = similarItems
|
const similarTools = similarItems
|
||||||
.filter((item): item is SimilarityResult => item.type === 'tool')
|
.filter((item): item is SimilarityResult => item.type === 'tool')
|
||||||
.map(item => toolsMap.get(item.name))
|
.map(item => toolsMap.get(item.name))
|
||||||
@ -319,7 +382,6 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
|
|
||||||
console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} tools, ${similarConcepts.length} concepts`);
|
console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} tools, ${similarConcepts.length} concepts`);
|
||||||
|
|
||||||
// FIXED: Better threshold logic - only use embeddings if we get meaningful filtering
|
|
||||||
const totalAvailableTools = toolsData.tools.length;
|
const totalAvailableTools = toolsData.tools.length;
|
||||||
const reductionRatio = similarTools.length / totalAvailableTools;
|
const reductionRatio = similarTools.length / totalAvailableTools;
|
||||||
|
|
||||||
@ -340,7 +402,6 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
selectionMethod = 'full_dataset';
|
selectionMethod = 'full_dataset';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced audit entry with reduction statistics
|
|
||||||
if (this.auditConfig.enabled) {
|
if (this.auditConfig.enabled) {
|
||||||
this.addAuditEntry(null, 'retrieval', 'embeddings-search',
|
this.addAuditEntry(null, 'retrieval', 'embeddings-search',
|
||||||
{ query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates },
|
{ query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates },
|
||||||
@ -420,25 +481,29 @@ class ImprovedMicroTaskAIPipeline {
|
|||||||
related_software: concept.related_software || []
|
related_software: concept.related_software || []
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// CORRECTED LOGIC:
|
|
||||||
let toolsToSend: any[];
|
let toolsToSend: any[];
|
||||||
let conceptsToSend: any[];
|
let conceptsToSend: any[];
|
||||||
|
|
||||||
if (selectionMethod === 'embeddings_candidates') {
|
if (selectionMethod === 'embeddings_candidates') {
|
||||||
// WITH EMBEDDINGS: Take top N from pre-filtered candidates
|
|
||||||
toolsToSend = toolsWithFullData.slice(0, this.embeddingSelectionLimit);
|
toolsToSend = toolsWithFullData.slice(0, this.embeddingSelectionLimit);
|
||||||
conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
|
conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Embeddings enabled: sending top ${toolsToSend.length} pre-filtered tools`);
|
console.log(`[AI PIPELINE] Embeddings enabled: sending top ${toolsToSend.length} similarity-ordered tools`);
|
||||||
} else {
|
} else {
|
||||||
// WITHOUT EMBEDDINGS: Send entire compressed database (original behavior)
|
const maxTools = this.noEmbeddingsToolLimit > 0 ?
|
||||||
toolsToSend = toolsWithFullData; // ALL tools from database
|
Math.min(this.noEmbeddingsToolLimit, candidateTools.length) :
|
||||||
conceptsToSend = conceptsWithFullData; // ALL concepts from database
|
candidateTools.length;
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Embeddings disabled: sending entire database (${toolsToSend.length} tools, ${conceptsToSend.length} concepts)`);
|
const maxConcepts = this.noEmbeddingsConceptLimit > 0 ?
|
||||||
|
Math.min(this.noEmbeddingsConceptLimit, candidateConcepts.length) :
|
||||||
|
candidateConcepts.length;
|
||||||
|
|
||||||
|
toolsToSend = toolsWithFullData.slice(0, maxTools);
|
||||||
|
conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
|
||||||
|
|
||||||
|
console.log(`[AI PIPELINE] Embeddings disabled: sending ${toolsToSend.length}/${candidateTools.length} tools (limit: ${this.noEmbeddingsToolLimit || 'none'})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the German prompt with appropriately selected tool data
|
|
||||||
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
|
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
|
||||||
const prompt = `${basePrompt}
|
const prompt = `${basePrompt}
|
||||||
|
|
||||||
@ -448,9 +513,12 @@ ${JSON.stringify(toolsToSend, null, 2)}
|
|||||||
VERFÜGBARE KONZEPTE (mit vollständigen Daten):
|
VERFÜGBARE KONZEPTE (mit vollständigen Daten):
|
||||||
${JSON.stringify(conceptsToSend, null, 2)}`;
|
${JSON.stringify(conceptsToSend, null, 2)}`;
|
||||||
|
|
||||||
// Log token usage for monitoring
|
|
||||||
const estimatedTokens = this.estimateTokens(prompt);
|
const estimatedTokens = this.estimateTokens(prompt);
|
||||||
console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${toolsToSend.length}, Tokens: ~${estimatedTokens}`);
|
console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${toolsToSend.length}, Estimated tokens: ~${estimatedTokens}`);
|
||||||
|
|
||||||
|
if (estimatedTokens > 35000) {
|
||||||
|
console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.callAI(prompt, 2500);
|
const response = await this.callAI(prompt, 2500);
|
||||||
@ -527,7 +595,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async callMicroTaskAI(prompt: string, context: AnalysisContext, maxTokens: number = 300): Promise<MicroTaskResult> {
|
private async callMicroTaskAI(prompt: string, context: AnalysisContext, maxTokens: number = 500): Promise<MicroTaskResult> {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
let contextPrompt = prompt;
|
let contextPrompt = prompt;
|
||||||
@ -552,11 +620,10 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
success: true
|
success: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// NEW: Add Audit Entry for Successful Micro-Task
|
|
||||||
this.addAuditEntry(context, 'micro-task', 'ai-analysis',
|
this.addAuditEntry(context, 'micro-task', 'ai-analysis',
|
||||||
{ promptLength: contextPrompt.length, maxTokens },
|
{ promptLength: contextPrompt.length, maxTokens },
|
||||||
{ responseLength: response.length, contentPreview: response.slice(0, 100) },
|
{ responseLength: response.length, contentPreview: response.slice(0, 100) },
|
||||||
response.length > 50 ? 80 : 60, // Confidence based on response quality
|
response.length > 50 ? 80 : 60,
|
||||||
startTime,
|
startTime,
|
||||||
{ aiModel: this.config.model, contextUsed: context.contextHistory.length > 0 }
|
{ aiModel: this.config.model, contextUsed: context.contextHistory.length > 0 }
|
||||||
);
|
);
|
||||||
@ -572,11 +639,10 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
error: error.message
|
error: error.message
|
||||||
};
|
};
|
||||||
|
|
||||||
// NEW: Add Audit Entry for Failed Micro-Task
|
|
||||||
this.addAuditEntry(context, 'micro-task', 'ai-analysis-failed',
|
this.addAuditEntry(context, 'micro-task', 'ai-analysis-failed',
|
||||||
{ promptLength: contextPrompt.length, maxTokens },
|
{ promptLength: contextPrompt.length, maxTokens },
|
||||||
{ error: error.message },
|
{ error: error.message },
|
||||||
5, // Very low confidence
|
5,
|
||||||
startTime,
|
startTime,
|
||||||
{ aiModel: this.config.model, contextUsed: context.contextHistory.length > 0 }
|
{ aiModel: this.config.model, contextUsed: context.contextHistory.length > 0 }
|
||||||
);
|
);
|
||||||
@ -589,7 +655,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
const isWorkflow = context.mode === 'workflow';
|
const isWorkflow = context.mode === 'workflow';
|
||||||
const prompt = getPrompt('scenarioAnalysis', isWorkflow, context.userQuery);
|
const prompt = getPrompt('scenarioAnalysis', isWorkflow, context.userQuery);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 220);
|
const result = await this.callMicroTaskAI(prompt, context, 400);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
if (isWorkflow) {
|
if (isWorkflow) {
|
||||||
@ -608,7 +674,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
const isWorkflow = context.mode === 'workflow';
|
const isWorkflow = context.mode === 'workflow';
|
||||||
const prompt = getPrompt('investigationApproach', isWorkflow, context.userQuery);
|
const prompt = getPrompt('investigationApproach', isWorkflow, context.userQuery);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 220);
|
const result = await this.callMicroTaskAI(prompt, context, 400);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
context.investigationApproach = result.content;
|
context.investigationApproach = result.content;
|
||||||
@ -622,7 +688,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
const isWorkflow = context.mode === 'workflow';
|
const isWorkflow = context.mode === 'workflow';
|
||||||
const prompt = getPrompt('criticalConsiderations', isWorkflow, context.userQuery);
|
const prompt = getPrompt('criticalConsiderations', isWorkflow, context.userQuery);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 180);
|
const result = await this.callMicroTaskAI(prompt, context, 350);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
context.criticalConsiderations = result.content;
|
context.criticalConsiderations = result.content;
|
||||||
@ -648,7 +714,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
|
|
||||||
const prompt = getPrompt('phaseToolSelection', context.userQuery, phase, phaseTools);
|
const prompt = getPrompt('phaseToolSelection', context.userQuery, phase, phaseTools);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 450);
|
const result = await this.callMicroTaskAI(prompt, context, 800);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const selections = this.safeParseJSON(result.content, []);
|
const selections = this.safeParseJSON(result.content, []);
|
||||||
@ -665,7 +731,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// NEW: Add audit entry for tool selection
|
|
||||||
this.addAuditEntry(context, 'micro-task', 'phase-tool-selection',
|
this.addAuditEntry(context, 'micro-task', 'phase-tool-selection',
|
||||||
{ phase: phase.id, availableTools: phaseTools.length },
|
{ phase: phase.id, availableTools: phaseTools.length },
|
||||||
{ validSelections: validSelections.length, selectedTools: validSelections.map(s => s.toolName) },
|
{ validSelections: validSelections.length, selectedTools: validSelections.map(s => s.toolName) },
|
||||||
@ -682,7 +747,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
private async evaluateSpecificTool(context: AnalysisContext, tool: any, rank: number): Promise<MicroTaskResult> {
|
private async evaluateSpecificTool(context: AnalysisContext, tool: any, rank: number): Promise<MicroTaskResult> {
|
||||||
const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank);
|
const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 650);
|
const result = await this.callMicroTaskAI(prompt, context, 1200);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const evaluation = this.safeParseJSON(result.content, {
|
const evaluation = this.safeParseJSON(result.content, {
|
||||||
@ -702,7 +767,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
}
|
}
|
||||||
}, 'evaluation', evaluation.suitability_score);
|
}, 'evaluation', evaluation.suitability_score);
|
||||||
|
|
||||||
// NEW: Add audit entry for tool evaluation
|
|
||||||
this.addAuditEntry(context, 'micro-task', 'tool-evaluation',
|
this.addAuditEntry(context, 'micro-task', 'tool-evaluation',
|
||||||
{ toolName: tool.name, rank },
|
{ toolName: tool.name, rank },
|
||||||
{ suitabilityScore: evaluation.suitability_score, hasExplanation: !!evaluation.detailed_explanation },
|
{ suitabilityScore: evaluation.suitability_score, hasExplanation: !!evaluation.detailed_explanation },
|
||||||
@ -730,7 +794,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
const selectedToolNames = context.selectedTools?.map(st => st.tool.name) || [];
|
const selectedToolNames = context.selectedTools?.map(st => st.tool.name) || [];
|
||||||
const prompt = getPrompt('backgroundKnowledgeSelection', context.userQuery, context.mode, selectedToolNames, availableConcepts);
|
const prompt = getPrompt('backgroundKnowledgeSelection', context.userQuery, context.mode, selectedToolNames, availableConcepts);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 400);
|
const result = await this.callMicroTaskAI(prompt, context, 700);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const selections = this.safeParseJSON(result.content, []);
|
const selections = this.safeParseJSON(result.content, []);
|
||||||
@ -743,7 +807,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
relevance: sel.relevance
|
relevance: sel.relevance
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// NEW: Add audit entry for background knowledge selection
|
|
||||||
this.addAuditEntry(context, 'micro-task', 'background-knowledge-selection',
|
this.addAuditEntry(context, 'micro-task', 'background-knowledge-selection',
|
||||||
{ availableConcepts: availableConcepts.length },
|
{ availableConcepts: availableConcepts.length },
|
||||||
{ selectedConcepts: context.backgroundKnowledge?.length || 0 },
|
{ selectedConcepts: context.backgroundKnowledge?.length || 0 },
|
||||||
@ -761,21 +824,19 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
const selectedToolNames = context.selectedTools?.map(st => st.tool.name) || [];
|
const selectedToolNames = context.selectedTools?.map(st => st.tool.name) || [];
|
||||||
const prompt = getPrompt('finalRecommendations', context.mode === 'workflow', context.userQuery, selectedToolNames);
|
const prompt = getPrompt('finalRecommendations', context.mode === 'workflow', context.userQuery, selectedToolNames);
|
||||||
|
|
||||||
const result = await this.callMicroTaskAI(prompt, context, 180);
|
const result = await this.callMicroTaskAI(prompt, context, 350);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async callAI(prompt: string, maxTokens: number = 1000): Promise<string> {
|
private async callAI(prompt: string, maxTokens: number = 1500): Promise<string> {
|
||||||
const endpoint = this.config.endpoint;
|
const endpoint = this.config.endpoint;
|
||||||
const apiKey = this.config.apiKey;
|
const apiKey = this.config.apiKey;
|
||||||
const model = this.config.model;
|
const model = this.config.model;
|
||||||
|
|
||||||
// Simple headers - add auth only if API key exists
|
|
||||||
let headers: Record<string, string> = {
|
let headers: Record<string, string> = {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add authentication if API key is provided
|
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
headers['Authorization'] = `Bearer ${apiKey}`;
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
||||||
console.log('[AI PIPELINE] Using API key authentication');
|
console.log('[AI PIPELINE] Using API key authentication');
|
||||||
@ -783,7 +844,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
console.log('[AI PIPELINE] No API key - making request without authentication');
|
console.log('[AI PIPELINE] No API key - making request without authentication');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple request body
|
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
model,
|
model,
|
||||||
messages: [{ role: 'user', content: prompt }],
|
messages: [{ role: 'user', content: prompt }],
|
||||||
@ -792,7 +852,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// FIXED: Use direct fetch since entire pipeline is already queued at query.ts level
|
|
||||||
const response = await fetch(`${endpoint}/v1/chat/completions`, {
|
const response = await fetch(`${endpoint}/v1/chat/completions`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
@ -826,13 +885,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
let completedTasks = 0;
|
let completedTasks = 0;
|
||||||
let failedTasks = 0;
|
let failedTasks = 0;
|
||||||
|
|
||||||
// NEW: Clear any previous temporary audit entries
|
|
||||||
this.tempAuditEntries = [];
|
this.tempAuditEntries = [];
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Starting ${mode} query processing with context continuity and audit trail`);
|
console.log(`[AI PIPELINE] Starting ${mode} query processing with context continuity and audit trail`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Stage 1: Get intelligent candidates (embeddings + AI selection)
|
|
||||||
const toolsData = await getCompressedToolsDataForAI();
|
const toolsData = await getCompressedToolsDataForAI();
|
||||||
const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode);
|
const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode);
|
||||||
|
|
||||||
@ -844,20 +901,17 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
maxContextLength: this.maxContextTokens,
|
maxContextLength: this.maxContextTokens,
|
||||||
currentContextLength: 0,
|
currentContextLength: 0,
|
||||||
seenToolNames: new Set<string>(),
|
seenToolNames: new Set<string>(),
|
||||||
// NEW: Initialize audit trail
|
|
||||||
auditTrail: []
|
auditTrail: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// NEW: Merge any temporary audit entries from pre-context operations
|
|
||||||
this.mergeTemporaryAuditEntries(context);
|
this.mergeTemporaryAuditEntries(context);
|
||||||
|
|
||||||
console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`);
|
console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`);
|
||||||
|
|
||||||
// NEW: Add initial audit entry
|
|
||||||
this.addAuditEntry(context, 'initialization', 'pipeline-start',
|
this.addAuditEntry(context, 'initialization', 'pipeline-start',
|
||||||
{ userQuery, mode, toolsDataLoaded: !!toolsData },
|
{ userQuery, mode, toolsDataLoaded: !!toolsData },
|
||||||
{ candidateTools: filteredData.tools.length, candidateConcepts: filteredData.concepts.length },
|
{ candidateTools: filteredData.tools.length, candidateConcepts: filteredData.concepts.length },
|
||||||
90, // High confidence for initialization
|
90,
|
||||||
startTime,
|
startTime,
|
||||||
{ auditEnabled: this.auditConfig.enabled }
|
{ auditEnabled: this.auditConfig.enabled }
|
||||||
);
|
);
|
||||||
@ -896,19 +950,15 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Task 5: Background Knowledge Selection
|
|
||||||
const knowledgeResult = await this.selectBackgroundKnowledge(context);
|
const knowledgeResult = await this.selectBackgroundKnowledge(context);
|
||||||
if (knowledgeResult.success) completedTasks++; else failedTasks++;
|
if (knowledgeResult.success) completedTasks++; else failedTasks++;
|
||||||
await this.delay(this.microTaskDelay);
|
await this.delay(this.microTaskDelay);
|
||||||
|
|
||||||
// Task 6: Final Recommendations
|
|
||||||
const finalResult = await this.generateFinalRecommendations(context);
|
const finalResult = await this.generateFinalRecommendations(context);
|
||||||
if (finalResult.success) completedTasks++; else failedTasks++;
|
if (finalResult.success) completedTasks++; else failedTasks++;
|
||||||
|
|
||||||
// Build final recommendation
|
|
||||||
const recommendation = this.buildRecommendation(context, mode, finalResult.content);
|
const recommendation = this.buildRecommendation(context, mode, finalResult.content);
|
||||||
|
|
||||||
// NEW: Add final audit entry
|
|
||||||
this.addAuditEntry(context, 'completion', 'pipeline-end',
|
this.addAuditEntry(context, 'completion', 'pipeline-end',
|
||||||
{ completedTasks, failedTasks },
|
{ completedTasks, failedTasks },
|
||||||
{ finalRecommendation: !!recommendation, auditEntriesGenerated: context.auditTrail.length },
|
{ finalRecommendation: !!recommendation, auditEntriesGenerated: context.auditTrail.length },
|
||||||
@ -935,7 +985,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
return {
|
return {
|
||||||
recommendation: {
|
recommendation: {
|
||||||
...recommendation,
|
...recommendation,
|
||||||
// NEW: Include audit trail in response
|
|
||||||
auditTrail: this.auditConfig.enabled ? context.auditTrail : undefined
|
auditTrail: this.auditConfig.enabled ? context.auditTrail : undefined
|
||||||
},
|
},
|
||||||
processingStats
|
processingStats
|
||||||
@ -944,7 +993,6 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[AI PIPELINE] Processing failed:', error);
|
console.error('[AI PIPELINE] Processing failed:', error);
|
||||||
|
|
||||||
// NEW: Ensure temp audit entries are cleared even on error
|
|
||||||
this.tempAuditEntries = [];
|
this.tempAuditEntries = [];
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -130,7 +130,6 @@ async function loadRawData(): Promise<ToolsData> {
|
|||||||
try {
|
try {
|
||||||
cachedData = ToolsDataSchema.parse(rawData);
|
cachedData = ToolsDataSchema.parse(rawData);
|
||||||
|
|
||||||
// Enhanced: Add default skill level descriptions if not provided
|
|
||||||
if (!cachedData.skill_levels || Object.keys(cachedData.skill_levels).length === 0) {
|
if (!cachedData.skill_levels || Object.keys(cachedData.skill_levels).length === 0) {
|
||||||
cachedData.skill_levels = {
|
cachedData.skill_levels = {
|
||||||
novice: "Minimal technical background required, guided interfaces",
|
novice: "Minimal technical background required, guided interfaces",
|
||||||
@ -178,21 +177,18 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
|
|||||||
if (!cachedCompressedData) {
|
if (!cachedCompressedData) {
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
|
|
||||||
// Enhanced: More detailed tool information for micro-tasks
|
|
||||||
const compressedTools = data.tools
|
const compressedTools = data.tools
|
||||||
.filter(tool => tool.type !== 'concept')
|
.filter(tool => tool.type !== 'concept')
|
||||||
.map(tool => {
|
.map(tool => {
|
||||||
const { projectUrl, statusUrl, ...compressedTool } = tool;
|
const { projectUrl, statusUrl, ...compressedTool } = tool;
|
||||||
return {
|
return {
|
||||||
...compressedTool,
|
...compressedTool,
|
||||||
// Enhanced: Add computed fields for AI
|
|
||||||
is_hosted: projectUrl !== undefined && projectUrl !== null && projectUrl !== "" && projectUrl.trim() !== "",
|
is_hosted: projectUrl !== undefined && projectUrl !== null && projectUrl !== "" && projectUrl.trim() !== "",
|
||||||
is_open_source: tool.license && tool.license !== 'Proprietary',
|
is_open_source: tool.license && tool.license !== 'Proprietary',
|
||||||
complexity_score: tool.skillLevel === 'expert' ? 5 :
|
complexity_score: tool.skillLevel === 'expert' ? 5 :
|
||||||
tool.skillLevel === 'advanced' ? 4 :
|
tool.skillLevel === 'advanced' ? 4 :
|
||||||
tool.skillLevel === 'intermediate' ? 3 :
|
tool.skillLevel === 'intermediate' ? 3 :
|
||||||
tool.skillLevel === 'beginner' ? 2 : 1,
|
tool.skillLevel === 'beginner' ? 2 : 1,
|
||||||
// Enhanced: Phase-specific suitability hints
|
|
||||||
phase_suitability: tool.phases?.map(phase => ({
|
phase_suitability: tool.phases?.map(phase => ({
|
||||||
phase,
|
phase,
|
||||||
primary_use: tool.tags?.find(tag => tag.includes(phase)) ? 'primary' : 'secondary'
|
primary_use: tool.tags?.find(tag => tag.includes(phase)) ? 'primary' : 'secondary'
|
||||||
@ -206,7 +202,6 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
|
|||||||
const { projectUrl, statusUrl, platforms, accessType, license, ...compressedConcept } = concept;
|
const { projectUrl, statusUrl, platforms, accessType, license, ...compressedConcept } = concept;
|
||||||
return {
|
return {
|
||||||
...compressedConcept,
|
...compressedConcept,
|
||||||
// Enhanced: Learning difficulty indicator
|
|
||||||
learning_complexity: concept.skillLevel === 'expert' ? 'very_high' :
|
learning_complexity: concept.skillLevel === 'expert' ? 'very_high' :
|
||||||
concept.skillLevel === 'advanced' ? 'high' :
|
concept.skillLevel === 'advanced' ? 'high' :
|
||||||
concept.skillLevel === 'intermediate' ? 'medium' :
|
concept.skillLevel === 'intermediate' ? 'medium' :
|
||||||
|
Loading…
x
Reference in New Issue
Block a user