/** * Entropy-based hallucination detector for real-time streaming verification. * * Monitors per-token logprob distributions from OpenAI's API and detects * hallucination through two complementary strategies: * * 1. FRACTION-BASED (works on weaker models like GPT-4o-mini): * Flags when the fraction of high-entropy tokens exceeds a threshold. * Based on "The Laugh" experiment (2026-03-19). * * 2. DYNAMICS-BASED (works on frontier models like GPT-5.4): * Uses entropy variance, derivatives, window variance, logprob gap patterns, * and competitive token fractions. Trained on 148 examples, F1=0.87. * Key insight: frontier models hallucinate at similar average confidence * but with different patterns of confidence fluctuation. * * The detector runs both strategies and uses the stronger signal. */ import type { VerificationEvent, CheckSeverity, EntropyContext, BayesianAnomalyScore } from './types.js'; import type { BASConfig } from '../bayesian/index.js'; export interface EntropyDetectorOptions { /** * Per-token entropy threshold. Tokens above this count as "high entropy". * Default: 0.5 (calibrated on 148 code verification examples) */ tokenThreshold?: number; /** * Fraction of high-entropy tokens in the window that triggers an alert. * Default: 0.35 (best F1 with acceptable FPR from tuning sweep) */ fractionThreshold?: number; /** Sliding window size for computing fraction. Default: 32 */ windowSize?: number; /** Minimum tokens before firing (avoid false positives on first few tokens). Default: 16 */ minTokens?: number; /** Severity for entropy findings. Default: 'medium' */ severity?: CheckSeverity; /** Callback for each entropy measurement */ onEntropy?: (info: EntropyMeasurement) => void; /** * Dynamics-based detection threshold (0-1). Default: 0.85. * Lower = more sensitive (more false positives). * Tuned on live GPT-5.4 data: real code scores 56-83%, fake scores 84-99%. */ dynamicsThreshold?: number; /** * When true (default), use the legacy dynamics features (logistic regression weights). * When false, use BAS when logprobs are available. * Allows A/B testing between old and new detection. */ useLegacyDynamics?: boolean; /** * BAS configuration. When omitted, sensible defaults are used. * Only relevant when useLegacyDynamics is false (default). */ basConfig?: BASConfig; } export interface EntropyMeasurement { token: string; tokenIndex: number; entropy: number; windowEntropy: number; exceedsThreshold: boolean; /** Dynamics score at this token (0-1, higher = more likely hallucinating) */ dynamicsScore?: number; } export interface TokenLogprob { token: string; logprob: number; topLogprobs: Array<{ token: string; logprob: number; }>; } /** * Compute Shannon entropy (bits) from a set of logprobs. * Normalizes the top-K probabilities to sum to 1 before computing. */ export declare function computeEntropy(topLogprobs: Array<{ logprob: number; }>): number; export declare class EntropyDetector { private readonly tokenThreshold; private readonly fractionThreshold; private readonly windowSize; private readonly minTokens; private readonly severity; private readonly dynamicsThreshold; private readonly useLegacyDynamics; private readonly onEntropy?; private readonly basCalculator; private highEntropyWindow; private tokenIndex; private totalEntropy; private alertCount; private tokenBuffer; private lastAlertIndex; private unitEntropies; private unitLogprobGaps; private unitCompetitiveCount; private unitBASResults; private allEntropies; constructor(options?: EntropyDetectorOptions); /** Set language context on the BAS calculator for structural entropy. */ setBASLanguage(lang: string): void; /** Feed prompt tokens to the BAS calculator for repetition confidence. */ setBASPromptTokens(tokens: string[]): void; /** * Compute BAS for a single token's logprobs. * Returns null when useLegacyDynamics is true, dynamics is explicitly * disabled (dynamicsThreshold > 1.0), or no logprobs provided. */ computeBAS(tokenLogprobs: TokenLogprob): BayesianAnomalyScore | null; /** * Process a single token with its logprob distribution. * Returns a VerificationEvent if either the fraction-based or * dynamics-based detector triggers. */ pushToken(tokenInfo: TokenLogprob): VerificationEvent | null; /** * Compute dynamics-based hallucination score (0-1). * Uses entropy variance, derivatives, window variance, and logprob patterns. */ private computeDynamicsScore; /** * Called by the streaming pipeline when a code unit boundary is detected. * Returns the entropy context for the tokens that made up that unit, * then resets the per-unit accumulator. */ consumeUnitEntropy(): EntropyContext | undefined; /** * Compute dynamics score for a specific code unit's features. */ private computeUnitDynamicsScore; /** Get cumulative stats. */ getStats(): { tokensProcessed: number; avgEntropy: number; alerts: number; }; /** Reset state. */ reset(): void; }