@vezlo/ai-validator
Version:
AI Response Validator - Automated accuracy checking, hallucination prevention, and confidence scoring for AI responses
162 lines • 7.71 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AIValidator = void 0;
const QueryClassifier_1 = require("./QueryClassifier");
const AccuracyChecker_1 = require("./AccuracyChecker");
const HallucinationDetector_1 = require("./HallucinationDetector");
const ConfidenceScorer_1 = require("./ConfidenceScorer");
class AIValidator {
constructor(config) {
// Set defaults for optional config
this.config = {
confidenceThreshold: 0.7,
enableQueryClassification: true,
enableAccuracyCheck: true,
enableHallucinationDetection: true,
openaiModel: 'gpt-4o',
claudeModel: 'claude-sonnet-4-5-20250929',
...config
};
// Validate required configuration
this.validateConfig();
// Initialize components
this.queryClassifier = new QueryClassifier_1.QueryClassifier();
this.accuracyChecker = new AccuracyChecker_1.AccuracyChecker(this.config.openaiApiKey, this.config.claudeApiKey);
this.hallucinationDetector = new HallucinationDetector_1.HallucinationDetector(this.config.openaiApiKey, this.config.claudeApiKey);
this.confidenceScorer = new ConfidenceScorer_1.ConfidenceScorer();
}
async validate(input) {
try {
// Step 1: Query Classification
if (this.config.enableQueryClassification) {
const classification = await this.queryClassifier.classifyQuery(input.query);
if (classification.skip_validation) {
return {
confidence: 1.0,
valid: true,
accuracy: { verified: true, verification_rate: 1.0 },
context: { source_relevance: 1.0, source_usage_rate: 1.0, valid: true },
hallucination: { detected: false, risk: 0 },
warnings: [],
query_type: classification.type,
skip_validation: true
};
}
}
// Step 2: Run validations in parallel
const [accuracyResult, contextResult, hallucinationResult] = await Promise.all([
this.config.enableAccuracyCheck ?
this.accuracyChecker.checkAccuracy(input.response, input.sources, this.config.llmProvider, this.getModel()) :
Promise.resolve({ verified: true, verification_rate: 1.0 }),
this.calculateContextRelevance(input.query, input.response, input.sources),
this.config.enableHallucinationDetection ?
this.hallucinationDetector.detectHallucination(input.response, input.sources, this.config.llmProvider, this.getModel()) :
Promise.resolve({ detected: false, risk: 0 })
]);
// Step 3: Calculate confidence
const confidenceResult = this.confidenceScorer.calculateConfidence(accuracyResult, contextResult, hallucinationResult, input.sources);
// Step 4: Generate warnings
const warnings = this.generateWarnings(accuracyResult, contextResult, hallucinationResult, input.sources);
// Step 5: Determine if valid
const valid = confidenceResult.confidence_score >= (this.config.confidenceThreshold || 0.7);
return {
confidence: confidenceResult.confidence_score,
valid,
accuracy: accuracyResult,
context: contextResult,
hallucination: hallucinationResult,
warnings
};
}
catch (error) {
return {
confidence: 0,
valid: false,
accuracy: { verified: false, verification_rate: 0, reason: 'validation_error' },
context: { source_relevance: 0, source_usage_rate: 0, valid: false },
hallucination: { detected: true, risk: 1.0 },
warnings: [`Validation failed: ${error instanceof Error ? error.message : 'unknown error'}`]
};
}
}
validateConfig() {
if (!this.config.openaiApiKey && !this.config.claudeApiKey) {
throw new Error('At least one API key (OpenAI or Claude) must be provided');
}
if (this.config.llmProvider === 'openai' && !this.config.openaiApiKey) {
throw new Error('OpenAI API key is required when using OpenAI provider');
}
if (this.config.llmProvider === 'claude' && !this.config.claudeApiKey) {
throw new Error('Claude API key is required when using Claude provider');
}
}
async calculateContextRelevance(query, response, sources) {
// Simple context relevance calculation
// In a more sophisticated implementation, this could use embeddings
if (sources.length === 0) {
return {
source_relevance: 0,
source_usage_rate: 0,
valid: false
};
}
// Context relevance: Check if response is grounded in sources
const cleanResponse = response.toLowerCase().replace(/[^\w\s]/g, '');
const sourceContent = sources.map(s => s.content.toLowerCase().replace(/[^\w\s]/g, '')).join(' ');
const responseWords = cleanResponse.split(/\s+/).filter(word => word.length > 3);
const sourceWords = sourceContent.split(/\s+/).filter(word => word.length > 3);
// Check how many response words appear in sources
let wordsInSource = 0;
for (const word of responseWords) {
if (sourceWords.includes(word)) {
wordsInSource++;
}
}
// Source relevance: % of response words that appear in sources
const sourceRelevance = responseWords.length > 0 ? wordsInSource / responseWords.length : 0.5;
// Source usage rate: Check if query keywords appear in response
const cleanQuery = query.toLowerCase().replace(/[^\w\s]/g, '');
const queryWords = cleanQuery.split(/\s+/).filter(word => word.length > 2);
let queryWordsInResponse = 0;
for (const word of queryWords) {
if (responseWords.includes(word)) {
queryWordsInResponse++;
}
}
const sourceUsageRate = queryWords.length > 0 ? queryWordsInResponse / queryWords.length : 0.5;
return {
source_relevance: Math.min(sourceRelevance, 1.0),
source_usage_rate: sourceUsageRate,
valid: sourceRelevance > 0.3
};
}
getModel() {
if (this.config.llmProvider === 'openai') {
return this.config.openaiModel || 'gpt-4o';
}
else {
return this.config.claudeModel || 'claude-sonnet-4-5-20250929';
}
}
generateWarnings(accuracyResult, contextResult, hallucinationResult, sources) {
const warnings = [];
if (sources.length === 0) {
warnings.push('No sources provided - high hallucination risk');
}
if (accuracyResult.verification_rate < 0.5) {
warnings.push('Low accuracy verification rate');
}
if (contextResult.source_relevance < 0.3) {
warnings.push('Low context relevance');
}
if (hallucinationResult.risk > 0.5) {
warnings.push('High hallucination risk detected');
}
if (hallucinationResult.detected) {
warnings.push('Hallucination detected in response');
}
return warnings;
}
}
exports.AIValidator = AIValidator;
//# sourceMappingURL=AIValidator.js.map