UNPKG

llmverify

Version:

AI Output Verification Toolkit — Local-first LLM safety, hallucination detection, PII redaction, prompt injection defense, and runtime monitoring. Zero telemetry. OWASP LLM Top 10 aligned.

132 lines 14 kB
"use strict"; /** * Fingerprint Engine * * Detects behavioral drift by analyzing response structure patterns. * Uses entropy, sentence structure, and length patterns to identify changes. * * WHAT THIS DOES: * ✅ Calculates response fingerprint (tokens, sentences, entropy) * ✅ Compares to baseline fingerprint * ✅ Detects structural drift in responses * * WHAT THIS DOES NOT DO: * ❌ Analyze semantic content * ❌ Detect quality changes * ❌ Identify specific model changes * * @module engines/runtime/fingerprint * @author Haiec * @license MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FingerprintEngine = FingerprintEngine; exports.extractFingerprint = extractFingerprint; const LIMITATIONS = [ 'Structural analysis only - does not assess content quality', 'Entropy calculation is character-based, not semantic', 'Requires baseline for meaningful comparison', 'May flag legitimate style variations as drift' ]; /** * Computes Shannon entropy of a text string. * Higher entropy indicates more randomness/variety. */ function computeEntropy(text) { if (!text || text.length === 0) return 0; const freq = {}; for (const ch of text) { freq[ch] = (freq[ch] || 0) + 1; } const len = text.length; let entropy = 0; for (const ch in freq) { const p = freq[ch] / len; entropy -= p * Math.log2(p); } return entropy; } /** * Calculates normalized difference between two values. */ function normalizedDiff(a, b) { return Math.abs(a - b) / Math.max(b, 1); } /** * Extracts fingerprint from response text. */ function extractFingerprint(text) { const tokens = text.split(/\s+/).filter(Boolean).length; const sentences = text.split(/[.!?]/).filter(s => s.trim().length > 0).length || 1; const avgSentLength = tokens / sentences; const entropy = computeEntropy(text); return { tokens, sentences, avgSentLength, entropy }; } /** * Analyzes response fingerprint for behavioral drift. * * @param call - The call record to analyze * @param baselineFingerprint - Baseline fingerprint for comparison * @returns Engine result with fingerprint analysis * * @example * const result = FingerprintEngine(callRecord, baseline.fingerprint); * if (result.status === 'warn') { * console.log('Response structure has changed'); * } */ function FingerprintEngine(call, baselineFingerprint) { const text = call.responseText || ''; const curr = extractFingerprint(text); // No baseline yet - initialize if (!baselineFingerprint || !('tokens' in baselineFingerprint) || baselineFingerprint.tokens === undefined) { return { metric: 'fingerprint', value: 0, status: 'ok', details: { initialized: true, curr, message: 'Fingerprint baseline initialized' }, limitations: LIMITATIONS }; } const baseline = baselineFingerprint; // Calculate component differences const dLen = normalizedDiff(curr.tokens, baseline.tokens); const dSent = normalizedDiff(curr.avgSentLength, baseline.avgSentLength); const dSentCount = normalizedDiff(curr.sentences, baseline.sentences); const dEnt = normalizedDiff(curr.entropy, baseline.entropy); // Weighted composite score const value = Math.min(1, 0.25 * dLen + 0.25 * dSent + 0.25 * dSentCount + 0.25 * dEnt); // Determine status let status; if (value < 0.3) { status = 'ok'; } else if (value < 0.6) { status = 'warn'; } else { status = 'error'; } return { metric: 'fingerprint', value, status, details: { curr, baseline, diffs: { tokenLength: Math.round(dLen * 100) / 100, sentenceLength: Math.round(dSent * 100) / 100, sentenceCount: Math.round(dSentCount * 100) / 100, entropy: Math.round(dEnt * 100) / 100 } }, limitations: LIMITATIONS }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmluZ2VycHJpbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZW5naW5lcy9ydW50aW1lL2ZpbmdlcnByaW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRzs7QUFpRUgsOENBMkRDO0FBS1EsZ0RBQWtCO0FBN0gzQixNQUFNLFdBQVcsR0FBRztJQUNsQiw0REFBNEQ7SUFDNUQsc0RBQXNEO0lBQ3RELDZDQUE2QztJQUM3QywrQ0FBK0M7Q0FDaEQsQ0FBQztBQUVGOzs7R0FHRztBQUNILFNBQVMsY0FBYyxDQUFDLElBQVk7SUFDbEMsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLENBQUMsQ0FBQztJQUV6QyxNQUFNLElBQUksR0FBMkIsRUFBRSxDQUFDO0lBQ3hDLEtBQUssTUFBTSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUM7UUFDdEIsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBRUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUN4QixJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUM7SUFDaEIsS0FBSyxNQUFNLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN0QixNQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDO1FBQ3pCLE9BQU8sSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5QixDQUFDO0lBRUQsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUyxjQUFjLENBQUMsQ0FBUyxFQUFFLENBQVM7SUFDMUMsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFD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QUFRO1lBQ1IsS0FBSyxFQUFFO2dCQUNMLFdBQVcsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsR0FBRyxHQUFHO2dCQUN6QyxjQUFjLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRztnQkFDN0MsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLEdBQUcsQ0FBQyxHQUFHLEdBQUc7Z0JBQ2pELE9BQU8sRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsR0FBRyxHQUFHO2FBQ3RDO1NBQ0Y7UUFDRCxXQUFXLEVBQUUsV0FBVztLQUN6QixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRmluZ2VycHJpbnQgRW5naW5lXG4gKiBcbiAqIERldGVjdHMgYmVoYXZpb3JhbCBkcmlmdCBieSBhbmFseXppbmcgcmVzcG9uc2Ugc3RydWN0dXJlIHBhdHRlcm5zLlxuICogVXNlcyBlbnRyb3B5LCBzZW50ZW5jZSBzdHJ1Y3R1cmUsIGFuZCBsZW5ndGggcGF0dGVybnMgdG8gaWRlbnRpZnkgY2hhbmdlcy5cbiAqIFxuICogV0hBVCBUSElTIERPRVM6XG4gKiDinIUgQ2FsY3VsYXRlcyByZXNwb25zZSBmaW5nZXJwcmludCAodG9rZW5zLCBzZW50ZW5jZXMsIGVudHJvcHkpXG4gKiDinIUgQ29tcGFyZXMgdG8gYmFzZWxpbmUgZmluZ2VycHJpbnRcbiAqIOKchSBEZXRlY3RzIHN0cnVjdHVyYWwgZHJpZnQgaW4gcmVzcG9uc2VzXG4gKiBcbiAqIFdIQVQgVEhJUyBET0VTIE5PVCBETzpcbiAqIOKdjCBBbmFseXplIHNlbWFudGljIGNvbnRlbnRcbiAqIOKdjCBEZXRlY3QgcXVhbGl0eSBjaGFuZ2VzXG4gKiDinYwgSWRlbnRpZnkgc3BlY2lmaWMgbW9kZWwgY2hhbmdlc1xuICogXG4gKiBAbW9kdWxlIGVuZ2luZXMvcnVudGltZS9maW5nZXJwcmludFxuICogQGF1dGhvciBIYWllY1xuICogQGxpY2Vuc2UgTUlUXG4gKi9cblxuaW1wb3J0IHsgQ2FsbFJlY29yZCwgRW5naW5lUmVzdWx0LCBSZXNwb25zZUZpbmdlcnByaW50IH0gZnJvbSAnLi4vLi4vdHlwZXMvcnVudGltZSc7XG5cbmNvbnN0IExJTUlUQVRJT05TID0gW1xuICAnU3RydWN0dXJhbCBhbmFseXNpcyBvbmx5IC0gZG9lcyBub3QgYXNzZXNzIGNvbnRlbnQgcXVhbGl0eScsXG4gICdFbnRyb3B5IGNhbGN1bGF0aW9uIGlzIGNoYXJhY3Rlci1iYXNlZCwgbm90IHNlbWFudGljJyxcbiAgJ1JlcXVpcmVzIGJhc2VsaW5lIGZvciBtZWFuaW5nZnVsIGNvbXBhcmlzb24nLFxuICAnTWF5IGZsYWcgbGVnaXRpbWF0ZSBzdHlsZSB2YXJpYXRpb25zIGFzIGRyaWZ0J1xuXTtcblxuLyoqXG4gKiBDb21wdXRlcyBTaGFubm9uIGVudHJvcHkgb2YgYSB0ZXh0IHN0cmluZy5cbiAqIEhpZ2hlciBlbnRyb3B5IGluZGljYXRlcyBtb3JlIHJhbmRvbW5lc3MvdmFyaWV0eS5cbiAqL1xuZnVuY3Rpb24gY29tcHV0ZUVudHJvcHkodGV4dDogc3RyaW5nKTogbnVtYmVyIHtcbiAgaWYgKCF0ZXh0IHx8IHRleHQubGVuZ3RoID09PSAwKSByZXR1cm4gMDtcbiAgXG4gIGNvbnN0IGZyZXE6IFJlY29yZDxzdHJpbmcsIG51bWJlcj4gPSB7fTtcbiAgZm9yIChjb25zdCBjaCBvZiB0ZXh0KSB7XG4gICAgZnJlcVtjaF0gPSAoZnJlcVtjaF0gfHwgMCkgKyAxO1xuICB9XG4gIFxuICBjb25zdCBsZW4gPSB0ZXh0Lmxlbmd0aDtcbiAgbGV0IGVudHJvcHkgPSAwO1xuICBmb3IgKGNvbnN0IGNoIGluIGZyZXEpIHtcbiAgICBjb25zdCBwID0gZnJlcVtjaF0gLyBsZW47XG4gICAgZW50cm9weSAtPSBwICogTWF0aC5sb2cyKHApO1xuICB9XG4gIFxuICByZXR1cm4gZW50cm9weTtcbn1cblxuLyoqXG4gKiBDYWxjdWxhdGVzIG5vcm1hbGl6ZWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byB2YWx1ZXMuXG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZWREaWZmKGE6IG51bWJlciwgYjogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE1hdGguYWJzKGEgLSBiKSAvIE1hdGgubWF4KGIsIDEpO1xufVxuXG4vKipcbiAqIEV4dHJhY3RzIGZpbmdlcnByaW50IGZyb20gcmVzcG9uc2UgdGV4dC5cbiAqL1xuZnVuY3Rpb24gZXh0cmFjdEZpbmdlcnByaW50KHRleHQ6IHN0cmluZyk6IFJlc3BvbnNlRmluZ2VycHJpbnQge1xuICBjb25zdCB0b2tlbnMgPSB0ZXh0LnNwbGl0KC9cXHMrLykuZmlsdGVyKEJvb2xlYW4pLmxlbmd0aDtcbiAgY29uc3Qgc2VudGVuY2VzID0gdGV4dC5zcGxpdCgvWy4hP10vKS5maWx0ZXIocyA9PiBzLnRyaW0oKS5sZW5ndGggPiAwKS5sZW5ndGggfHwgMTtcbiAgY29uc3QgYXZnU2VudExlbmd0aCA9IHRva2VucyAvIHNlbnRlbmNlcztcbiAgY29uc3QgZW50cm9weSA9IGNvbXB1dGVFbnRyb3B5KHRleHQpO1xuICBcbiAgcmV0dXJuIHsgdG9rZW5zLCBzZW50ZW5jZXMsIGF2Z1NlbnRMZW5ndGgsIGVudHJvcHkgfTtcbn1cblxuLyoqXG4gKiBBbmFseXplcyByZXNwb25zZSBmaW5nZXJwcmludCBmb3IgYmVoYXZpb3JhbCBkcmlmdC5cbiAqIFxuICogQHBhcmFtIGNhbGwgLSBUaGUgY2FsbCByZWNvcmQgdG8gYW5hbHl6ZVxuICogQHBhcmFtIGJhc2VsaW5lRmluZ2VycHJpbnQgLSBCYXNlbGluZSBmaW5nZXJwcmludCBmb3IgY29tcGFyaXNvblxuICogQHJldHVybnMgRW5naW5lIHJlc3VsdCB3aXRoIGZpbmdlcnByaW50IGFuYWx5c2lzXG4gKiBcbiAqIEBleGFtcGxlXG4gKiBjb25zdCByZXN1bHQgPSBGaW5nZXJwcmludEVuZ2luZShjYWxsUmVjb3JkLCBiYXNlbGluZS5maW5nZXJwcmludCk7XG4gKiBpZiAocmVzdWx0LnN0YXR1cyA9PT0gJ3dhcm4nKSB7XG4gKiAgIGNvbnNvbGUubG9nKCdSZXNwb25zZSBzdHJ1Y3R1cmUgaGFzIGNoYW5nZWQnKTtcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEZpbmdlcnByaW50RW5naW5lKFxuICBjYWxsOiBDYWxsUmVjb3JkLFxuICBiYXNlbGluZUZpbmdlcnByaW50OiBSZXNwb25zZUZpbmdlcnByaW50IHwgUmVjb3JkPHN0cmluZywgbmV2ZXI+XG4pOiBFbmdpbmVSZXN1bHQge1xuICBjb25zdCB0ZXh0ID0gY2FsbC5yZXNwb25zZVRleHQgfHwgJyc7XG4gIGNvbnN0IGN1cnIgPSBleHRyYWN0RmluZ2VycHJpbnQodGV4dCk7XG5cbiAgLy8gTm8gYmFzZWxpbmUgeWV0IC0gaW5pdGlhbGl6ZVxuICBpZiAoIWJhc2VsaW5lRmluZ2VycHJpbnQgfHwgISgndG9rZW5zJyBpbiBiYXNlbGluZUZpbmdlcnByaW50KSB8fCBiYXNlbGluZUZpbmdlcnByaW50LnRva2VucyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG1ldHJpYzogJ2ZpbmdlcnByaW50JyxcbiAgICAgIHZhbHVlOiAwLFxuICAgICAgc3RhdHVzOiAnb2snLFxuICAgICAgZGV0YWlsczoge1xuICAgICAgICBpbml0aWFsaXplZDogdHJ1ZSxcbiAgICAgICAgY3VycixcbiAgICAgICAgbWVzc2FnZTogJ0ZpbmdlcnByaW50IGJhc2VsaW5lIGluaXRpYWxpemVkJ1xuICAgICAgfSxcbiAgICAgIGxpbWl0YXRpb25zOiBMSU1JVEFUSU9OU1xuICAgIH07XG4gIH1cblxuICBjb25zdCBiYXNlbGluZSA9IGJhc2VsaW5lRmluZ2VycHJpbnQgYXMgUmVzcG9uc2VGaW5nZXJwcmludDtcblxuICAvLyBDYWxjdWxhdGUgY29tcG9uZW50IGRpZmZlcmVuY2VzXG4gIGNvbnN0IGRMZW4gPSBub3JtYWxpemVkRGlmZihjdXJyLnRva2VucywgYmFzZWxpbmUudG9rZW5zKTtcbiAgY29uc3QgZFNlbnQgPSBub3JtYWxpemVkRGlmZihjdXJyLmF2Z1NlbnRMZW5ndGgsIGJhc2VsaW5lLmF2Z1NlbnRMZW5ndGgpO1xuICBjb25zdCBkU2VudENvdW50ID0gbm9ybWFsaXplZERpZmYoY3Vyci5zZW50ZW5jZXMsIGJhc2VsaW5lLnNlbnRlbmNlcyk7XG4gIGNvbnN0IGRFbnQgPSBub3JtYWxpemVkRGlmZihjdXJyLmVudHJvcHksIGJhc2VsaW5lLmVudHJvcHkpO1xuXG4gIC8vIFdlaWdodGVkIGNvbXBvc2l0ZSBzY29yZVxuICBjb25zdCB2YWx1ZSA9IE1hdGgubWluKDEsIDAuMjUgKiBkTGVuICsgMC4yNSAqIGRTZW50ICsgMC4yNSAqIGRTZW50Q291bnQgKyAwLjI1ICogZEVudCk7XG5cbiAgLy8gRGV0ZXJtaW5lIHN0YXR1c1xuICBsZXQgc3RhdHVzOiAnb2snIHwgJ3dhcm4nIHwgJ2Vycm9yJztcbiAgaWYgKHZhbHVlIDwgMC4zKSB7XG4gICAgc3RhdHVzID0gJ29rJztcbiAgfSBlbHNlIGlmICh2YWx1ZSA8IDAuNikge1xuICAgIHN0YXR1cyA9ICd3YXJuJztcbiAgfSBlbHNlIHtcbiAgICBzdGF0dXMgPSAnZXJyb3InO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBtZXRyaWM6ICdmaW5nZXJwcmludCcsXG4gICAgdmFsdWUsXG4gICAgc3RhdHVzLFxuICAgIGRldGFpbHM6IHtcbiAgICAgIGN1cnIsXG4gICAgICBiYXNlbGluZSxcbiAgICAgIGRpZmZzOiB7XG4gICAgICAgIHRva2VuTGVuZ3RoOiBNYXRoLnJvdW5kKGRMZW4gKiAxMDApIC8gMTAwLFxuICAgICAgICBzZW50ZW5jZUxlbmd0aDogTWF0aC5yb3VuZChkU2VudCAqIDEwMCkgLyAxMDAsXG4gICAgICAgIHNlbnRlbmNlQ291bnQ6IE1hdGgucm91bmQoZFNlbnRDb3VudCAqIDEwMCkgLyAxMDAsXG4gICAgICAgIGVudHJvcHk6IE1hdGgucm91bmQoZEVudCAqIDEwMCkgLyAxMDBcbiAgICAgIH1cbiAgICB9LFxuICAgIGxpbWl0YXRpb25zOiBMSU1JVEFUSU9OU1xuICB9O1xufVxuXG4vKipcbiAqIFV0aWxpdHkgdG8gZXh0cmFjdCBmaW5nZXJwcmludCBmb3IgZXh0ZXJuYWwgdXNlLlxuICovXG5leHBvcnQgeyBleHRyYWN0RmluZ2VycHJpbnQgfTtcbiJdfQ==