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.
155 lines • 14.3 kB
JavaScript
;
/**
* Structure Engine
*
* Analyzes response structure for expected patterns.
* Detects JSON, lists, code blocks, and other structural elements.
*
* WHAT THIS DOES:
* ✅ Detects JSON in responses
* ✅ Counts list items and bullet points
* ✅ Identifies code blocks
* ✅ Flags unstructured responses when structure expected
*
* WHAT THIS DOES NOT DO:
* ❌ Validate JSON schema
* ❌ Assess content quality
* ❌ Determine if structure is appropriate for query
*
* @module engines/runtime/structure
* @author Haiec
* @license MIT
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.StructureEngine = StructureEngine;
const LIMITATIONS = [
'Pattern-based detection only',
'Cannot determine if structure is appropriate for the query',
'May miss custom formatting patterns',
'Does not validate structural correctness'
];
/**
* Checks if text contains valid JSON.
*/
function containsJSON(text) {
// Look for JSON-like patterns
const jsonPatterns = [
/\{[\s\S]*\}/, // Object
/\[[\s\S]*\]/ // Array
];
for (const pattern of jsonPatterns) {
const match = text.match(pattern);
if (match) {
try {
JSON.parse(match[0]);
return { found: true, valid: true };
}
catch {
return { found: true, valid: false };
}
}
}
return { found: false, valid: false };
}
/**
* Counts list items in text.
*/
function countListItems(text) {
const patterns = [
/^[-*•]\s+/gm, // Bullet points
/^\d+[.)]\s+/gm, // Numbered lists
/^[a-z][.)]\s+/gim // Lettered lists
];
let count = 0;
for (const pattern of patterns) {
const matches = text.match(pattern);
if (matches)
count += matches.length;
}
return count;
}
/**
* Counts code blocks in text.
*/
function countCodeBlocks(text) {
const fencedBlocks = (text.match(/```[\s\S]*?```/g) || []).length;
const indentedBlocks = (text.match(/^( |\t).+$/gm) || []).length;
return fencedBlocks + Math.floor(indentedBlocks / 3); // Group indented lines
}
/**
* Counts headers/sections in text.
*/
function countHeaders(text) {
const markdownHeaders = (text.match(/^#{1,6}\s+.+$/gm) || []).length;
const underlineHeaders = (text.match(/^.+\n[=-]+$/gm) || []).length;
return markdownHeaders + underlineHeaders;
}
/**
* Analyzes response structure for patterns.
*
* @param call - The call record to analyze
* @param expectStructure - Whether structure is expected (optional)
* @returns Engine result with structure analysis
*
* @example
* const result = StructureEngine(callRecord);
* if (result.details.isJson) {
* console.log('Response contains JSON');
* }
*/
function StructureEngine(call, expectStructure) {
const text = call.responseText || '';
// Analyze structure
const json = containsJSON(text);
const listCount = countListItems(text);
const codeBlockCount = countCodeBlocks(text);
const headerCount = countHeaders(text);
// Count structural elements
const structuralElements = [
json.found,
listCount > 0,
codeBlockCount > 0,
headerCount > 0
].filter(Boolean).length;
// Calculate anomaly score
let anomalies = 0;
const maxAnomalies = 3;
// Check for broken JSON
if (json.found && !json.valid)
anomalies++;
// Check for no structure when expected
if (expectStructure && structuralElements === 0)
anomalies++;
// Check for very short responses (potential truncation)
if (text.length < 50 && call.responseTokens > 10)
anomalies++;
const value = anomalies / maxAnomalies;
// Determine status
let status;
if (value === 0) {
status = 'ok';
}
else if (value < 0.5) {
status = 'warn';
}
else {
status = 'error';
}
return {
metric: 'structure',
value,
status,
details: {
isJson: json.found,
jsonValid: json.valid,
listCount,
codeBlockCount,
headerCount,
structuralElements,
anomalies,
textLength: text.length
},
limitations: LIMITATIONS
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RydWN0dXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VuZ2luZXMvcnVudGltZS9zdHJ1Y3R1cmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CRzs7QUFzRkgsMENBNkRDO0FBL0lELE1BQU0sV0FBVyxHQUFHO0lBQ2xCLDhCQUE4QjtJQUM5Qiw0REFBNEQ7SUFDNUQscUNBQXFDO0lBQ3JDLDBDQUEwQztDQUMzQyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxTQUFTLFlBQVksQ0FBQyxJQUFZO0lBQ2hDLDhCQUE4QjtJQUM5QixNQUFNLFlBQVksR0FBRztRQUNuQixhQUFhLEVBQUcsU0FBUztRQUN6QixhQUFhLENBQUcsUUFBUTtLQUN6QixDQUFDO0lBRUYsS0FBSyxNQUFNLE9BQU8sSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUNuQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2xDLElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixJQUFJLENBQUM7Z0JBQ0gsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDckIsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQ3RDLENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ1AsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDO1lBQ3ZDLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsQ0FBQztBQUN4QyxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLGNBQWMsQ0FBQyxJQUFZO0lBQ2xDLE1BQU0sUUFBUSxHQUFHO1FBQ2YsYUFBYSxFQUFZLGdC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S0FBSyxHQUFHLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDbEIsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLEdBQUcsT0FBTyxDQUFDO0lBQ25CLENBQUM7SUFFRCxPQUFPO1FBQ0wsTUFBTSxFQUFFLFdBQVc7UUFDbkIsS0FBSztRQUNMLE1BQU07UUFDTixPQUFPLEVBQUU7WUFDUCxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQUs7WUFDbEIsU0FBUyxFQUFFLElBQUksQ0FBQyxLQUFLO1lBQ3JCLFNBQVM7WUFDVCxjQUFjO1lBQ2QsV0FBVztZQUNYLGtCQUFrQjtZQUNsQixTQUFTO1lBQ1QsVUFBVSxFQUFFLElBQUksQ0FBQyxNQUFNO1NBQ3hCO1FBQ0QsV0FBVyxFQUFFLFdBQVc7S0FDekIsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFN0cnVjdHVyZSBFbmdpbmVcbiAqIFxuICogQW5hbHl6ZXMgcmVzcG9uc2Ugc3RydWN0dXJlIGZvciBleHBlY3RlZCBwYXR0ZXJucy5cbiAqIERldGVjdHMgSlNPTiwgbGlzdHMsIGNvZGUgYmxvY2tzLCBhbmQgb3RoZXIgc3RydWN0dXJhbCBlbGVtZW50cy5cbiAqIFxuICogV0hBVCBUSElTIERPRVM6XG4gKiDinIUgRGV0ZWN0cyBKU09OIGluIHJlc3BvbnNlc1xuICog4pyFIENvdW50cyBsaXN0IGl0ZW1zIGFuZCBidWxsZXQgcG9pbnRzXG4gKiDinIUgSWRlbnRpZmllcyBjb2RlIGJsb2Nrc1xuICog4pyFIEZsYWdzIHVuc3RydWN0dXJlZCByZXNwb25zZXMgd2hlbiBzdHJ1Y3R1cmUgZXhwZWN0ZWRcbiAqIFxuICogV0hBVCBUSElTIERPRVMgTk9UIERPOlxuICog4p2MIFZhbGlkYXRlIEpTT04gc2NoZW1hXG4gKiDinYwgQXNzZXNzIGNvbnRlbnQgcXVhbGl0eVxuICog4p2MIERldGVybWluZSBpZiBzdHJ1Y3R1cmUgaXMgYXBwcm9wcmlhdGUgZm9yIHF1ZXJ5XG4gKiBcbiAqIEBtb2R1bGUgZW5naW5lcy9ydW50aW1lL3N0cnVjdHVyZVxuICogQGF1dGhvciBIYWllY1xuICogQGxpY2Vuc2UgTUlUXG4gKi9cblxuaW1wb3J0IHsgQ2FsbFJlY29yZCwgRW5naW5lUmVzdWx0IH0gZnJvbSAnLi4vLi4vdHlwZXMvcnVudGltZSc7XG5cbmNvbnN0IExJTUlUQVRJT05TID0gW1xuICAnUGF0dGVybi1iYXNlZCBkZXRlY3Rpb24gb25seScsXG4gICdDYW5ub3QgZGV0ZXJtaW5lIGlmIHN0cnVjdHVyZSBpcyBhcHByb3ByaWF0ZSBmb3IgdGhlIHF1ZXJ5JyxcbiAgJ01heSBtaXNzIGN1c3RvbSBmb3JtYXR0aW5nIHBhdHRlcm5zJyxcbiAgJ0RvZXMgbm90IHZhbGlkYXRlIHN0cnVjdHVyYWwgY29ycmVjdG5lc3MnXG5dO1xuXG4vKipcbiAqIENoZWNrcyBpZiB0ZXh0IGNvbnRhaW5zIHZhbGlkIEpTT04uXG4gKi9cbmZ1bmN0aW9uIGNvbnRhaW5zSlNPTih0ZXh0OiBzdHJpbmcpOiB7IGZvdW5kOiBib29sZWFuOyB2YWxpZDogYm9vbGVhbiB9IHtcbiAgLy8gTG9vayBmb3IgSlNPTi1saWtlIHBhdHRlcm5zXG4gIGNvbnN0IGpzb25QYXR0ZXJucyA9IFtcbiAgICAvXFx7W1xcc1xcU10qXFx9LywgIC8vIE9iamVjdFxuICAgIC9cXFtbXFxzXFxTXSpcXF0vICAgLy8gQXJyYXlcbiAgXTtcblxuICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YganNvblBhdHRlcm5zKSB7XG4gICAgY29uc3QgbWF0Y2ggPSB0ZXh0Lm1hdGNoKHBhdHRlcm4pO1xuICAgIGlmIChtYXRjaCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgSlNPTi5wYXJzZShtYXRjaFswXSk7XG4gICAgICAgIHJldHVybiB7IGZvdW5kOiB0cnVlLCB2YWxpZDogdHJ1ZSB9O1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJldHVybiB7IGZvdW5kOiB0cnVlLCB2YWxpZDogZmFsc2UgfTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4geyBmb3VuZDogZmFsc2UsIHZhbGlkOiBmYWxzZSB9O1xufVxuXG4vKipcbiAqIENvdW50cyBsaXN0IGl0ZW1zIGluIHRleHQuXG4gKi9cbmZ1bmN0aW9uIGNvdW50TGlzdEl0ZW1zKHRleHQ6IHN0cmluZyk6IG51bWJlciB7XG4gIGNvbnN0IHBhdHRlcm5zID0gW1xuICAgIC9eWy0q4oCiXVxccysvZ20sICAgICAgICAgICAvLyBCdWxsZXQgcG9pbnRzXG4gICAgL15cXGQrWy4pXVxccysvZ20sICAgICAgICAgLy8gTnVtYmVyZWQgbGlzdHNcbiAgICAvXlthLXpdWy4pXVxccysvZ2ltICAgICAgIC8vIExldHRlcmVkIGxpc3RzXG4gIF07XG5cbiAgbGV0IGNvdW50ID0gMDtcbiAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIHBhdHRlcm5zKSB7XG4gICAgY29uc3QgbWF0Y2hlcyA9IHRleHQubWF0Y2gocGF0dGVybik7XG4gICAgaWYgKG1hdGNoZXMpIGNvdW50ICs9IG1hdGNoZXMubGVuZ3RoO1xuICB9XG5cbiAgcmV0dXJuIGNvdW50O1xufVxuXG4vKipcbiAqIENvdW50cyBjb2RlIGJsb2NrcyBpbiB0ZXh0LlxuICovXG5mdW5jdGlvbiBjb3VudENvZGVCbG9ja3ModGV4dDogc3RyaW5nKTogbnVtYmVyIHtcbiAgY29uc3QgZmVuY2VkQmxvY2tzID0gKHRleHQubWF0Y2goL2BgYFtcXHNcXFNdKj9gYGAvZykgfHwgW10pLmxlbmd0aDtcbiAgY29uc3QgaW5kZW50ZWRCbG9ja3MgPSAodGV4dC5tYXRjaCgvXiggICAgfFxcdCkuKyQvZ20pIHx8IFtdKS5sZW5ndGg7XG4gIHJldHVybiBmZW5jZWRCbG9ja3MgKyBNYXRoLmZsb29yKGluZGVudGVkQmxvY2tzIC8gMyk7IC8vIEdyb3VwIGluZGVudGVkIGxpbmVzXG59XG5cbi8qKlxuICogQ291bnRzIGhlYWRlcnMvc2VjdGlvbnMgaW4gdGV4dC5cbiAqL1xuZnVuY3Rpb24gY291bnRIZWFkZXJzKHRleHQ6IHN0cmluZyk6IG51bWJlciB7XG4gIGNvbnN0IG1hcmtkb3duSGVhZGVycyA9ICh0ZXh0Lm1hdGNoKC9eI3sxLDZ9XFxzKy4rJC9nbSkgfHwgW10pLmxlbmd0aDtcbiAgY29uc3QgdW5kZXJsaW5lSGVhZGVycyA9ICh0ZXh0Lm1hdGNoKC9eLitcXG5bPS1dKyQvZ20pIHx8IFtdKS5sZW5ndGg7XG4gIHJldHVybiBtYXJrZG93bkhlYWRlcnMgKyB1bmRlcmxpbmVIZWFkZXJzO1xufVxuXG4vKipcbiAqIEFuYWx5emVzIHJlc3BvbnNlIHN0cnVjdHVyZSBmb3IgcGF0dGVybnMuXG4gKiBcbiAqIEBwYXJhbSBjYWxsIC0gVGhlIGNhbGwgcmVjb3JkIHRvIGFuYWx5emVcbiAqIEBwYXJhbSBleHBlY3RTdHJ1Y3R1cmUgLSBXaGV0aGVyIHN0cnVjdHVyZSBpcyBleHBlY3RlZCAob3B0aW9uYWwpXG4gKiBAcmV0dXJucyBFbmdpbmUgcmVzdWx0IHdpdGggc3RydWN0dXJlIGFuYWx5c2lzXG4gKiBcbiAqIEBleGFtcGxlXG4gKiBjb25zdCByZXN1bHQgPSBTdHJ1Y3R1cmVFbmdpbmUoY2FsbFJlY29yZCk7XG4gKiBpZiAocmVzdWx0LmRldGFpbHMuaXNKc29uKSB7XG4gKiAgIGNvbnNvbGUubG9nKCdSZXNwb25zZSBjb250YWlucyBKU09OJyk7XG4gKiB9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBTdHJ1Y3R1cmVFbmdpbmUoXG4gIGNhbGw6IENhbGxSZWNvcmQsXG4gIGV4cGVjdFN0cnVjdHVyZT86IGJvb2xlYW5cbik6IEVuZ2luZVJlc3VsdCB7XG4gIGNvbnN0IHRleHQgPSBjYWxsLnJlc3BvbnNlVGV4dCB8fCAnJztcblxuICAvLyBBbmFseXplIHN0cnVjdHVyZVxuICBjb25zdCBqc29uID0gY29udGFpbnNKU09OKHRleHQpO1xuICBjb25zdCBsaXN0Q291bnQgPSBjb3VudExpc3RJdGVtcyh0ZXh0KTtcbiAgY29uc3QgY29kZUJsb2NrQ291bnQgPSBjb3VudENvZGVCbG9ja3ModGV4dCk7XG4gIGNvbnN0IGhlYWRlckNvdW50ID0gY291bnRIZWFkZXJzKHRleHQpO1xuXG4gIC8vIENvdW50IHN0cnVjdHVyYWwgZWxlbWVudHNcbiAgY29uc3Qgc3RydWN0dXJhbEVsZW1lbnRzID0gW1xuICAgIGpzb24uZm91bmQsXG4gICAgbGlzdENvdW50ID4gMCxcbiAgICBjb2RlQmxvY2tDb3VudCA+IDAsXG4gICAgaGVhZGVyQ291bnQgPiAwXG4gIF0uZmlsdGVyKEJvb2xlYW4pLmxlbmd0aDtcblxuICAvLyBDYWxjdWxhdGUgYW5vbWFseSBzY29yZVxuICBsZXQgYW5vbWFsaWVzID0gMDtcbiAgY29uc3QgbWF4QW5vbWFsaWVzID0gMztcblxuICAvLyBDaGVjayBmb3IgYnJva2VuIEpTT05cbiAgaWYgKGpzb24uZm91bmQgJiYgIWpzb24udmFsaWQpIGFub21hbGllcysrO1xuXG4gIC8vIENoZWNrIGZvciBubyBzdHJ1Y3R1cmUgd2hlbiBleHBlY3RlZFxuICBpZiAoZXhwZWN0U3RydWN0dXJlICYmIHN0cnVjdHVyYWxFbGVtZW50cyA9PT0gMCkgYW5vbWFsaWVzKys7XG5cbiAgLy8gQ2hlY2sgZm9yIHZlcnkgc2hvcnQgcmVzcG9uc2VzIChwb3RlbnRpYWwgdHJ1bmNhdGlvbilcbiAgaWYgKHRleHQubGVuZ3RoIDwgNTAgJiYgY2FsbC5yZXNwb25zZVRva2VucyA+IDEwKSBhbm9tYWxpZXMrKztcblxuICBjb25zdCB2YWx1ZSA9IGFub21hbGllcyAvIG1heEFub21hbGllcztcblxuICAvLyBEZXRlcm1pbmUgc3RhdHVzXG4gIGxldCBzdGF0dXM6ICdvaycgfCAnd2FybicgfCAnZXJyb3InO1xuICBpZiAodmFsdWUgPT09IDApIHtcbiAgICBzdGF0dXMgPSAnb2snO1xuICB9IGVsc2UgaWYgKHZhbHVlIDwgMC41KSB7XG4gICAgc3RhdHVzID0gJ3dhcm4nO1xuICB9IGVsc2Uge1xuICAgIHN0YXR1cyA9ICdlcnJvcic7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG1ldHJpYzogJ3N0cnVjdHVyZScsXG4gICAgdmFsdWUsXG4gICAgc3RhdHVzLFxuICAgIGRldGFpbHM6IHtcbiAgICAgIGlzSnNvbjoganNvbi5mb3VuZCxcbiAgICAgIGpzb25WYWxpZDoganNvbi52YWxpZCxcbiAgICAgIGxpc3RDb3VudCxcbiAgICAgIGNvZGVCbG9ja0NvdW50LFxuICAgICAgaGVhZGVyQ291bnQsXG4gICAgICBzdHJ1Y3R1cmFsRWxlbWVudHMsXG4gICAgICBhbm9tYWxpZXMsXG4gICAgICB0ZXh0TGVuZ3RoOiB0ZXh0Lmxlbmd0aFxuICAgIH0sXG4gICAgbGltaXRhdGlvbnM6IExJTUlUQVRJT05TXG4gIH07XG59XG4iXX0=