UNPKG

@ai2070/l0

Version:

L0: The Missing Reliability Substrate for AI

216 lines 6.91 kB
export function analyzeJsonStructure(content) { let openBraces = 0; let closeBraces = 0; let openBrackets = 0; let closeBrackets = 0; let inString = false; let escapeNext = false; const issues = []; for (let i = 0; i < content.length; i++) { const char = content[i]; if (escapeNext) { escapeNext = false; continue; } if (char === "\\") { escapeNext = true; continue; } if (char === '"') { inString = !inString; continue; } if (!inString) { if (char === "{") openBraces++; if (char === "}") closeBraces++; if (char === "[") openBrackets++; if (char === "]") closeBrackets++; } } if (inString) { issues.push("Unclosed string detected"); } if (openBraces !== closeBraces) { issues.push(`Unbalanced braces: ${openBraces} open, ${closeBraces} close`); } if (openBrackets !== closeBrackets) { issues.push(`Unbalanced brackets: ${openBrackets} open, ${closeBrackets} close`); } const isBalanced = openBraces === closeBraces && openBrackets === closeBrackets && !inString; return { openBraces, closeBraces, openBrackets, closeBrackets, inString, isBalanced, issues, }; } export function looksLikeJson(content) { if (!content) return false; const trimmed = content.trim(); return trimmed.startsWith("{") || trimmed.startsWith("["); } export function validateJsonStructure(context) { const { content, completed } = context; const violations = []; if (!looksLikeJson(content)) { return violations; } const structure = analyzeJsonStructure(content); if (!completed) { if (structure.closeBraces > structure.openBraces) { violations.push({ rule: "json-structure", message: `Too many closing braces: ${structure.closeBraces} close, ${structure.openBraces} open`, severity: "error", recoverable: true, }); } if (structure.closeBrackets > structure.openBrackets) { violations.push({ rule: "json-structure", message: `Too many closing brackets: ${structure.closeBrackets} close, ${structure.openBrackets} open`, severity: "error", recoverable: true, }); } } else { if (!structure.isBalanced) { for (const issue of structure.issues) { violations.push({ rule: "json-structure", message: issue, severity: "error", recoverable: true, suggestion: "Retry generation to get properly balanced JSON", }); } } } return violations; } export function validateJsonChunks(context) { const { content, delta } = context; const violations = []; if (!delta || !looksLikeJson(content)) { return violations; } const malformedPatterns = [ { pattern: /,,+/, message: "Multiple consecutive commas" }, { pattern: /\{\s*,/, message: "Comma immediately after opening brace" }, { pattern: /\[\s*,/, message: "Comma immediately after opening bracket" }, { pattern: /:\s*,/, message: "Comma immediately after colon" }, ]; for (const { pattern, message } of malformedPatterns) { if (pattern.test(content)) { violations.push({ rule: "json-chunks", message: `Malformed JSON: ${message}`, severity: "error", recoverable: true, }); } } return violations; } export function validateJsonParseable(context) { const { content, completed } = context; const violations = []; if (!completed || !looksLikeJson(content)) { return violations; } try { JSON.parse(content.trim()); } catch (error) { violations.push({ rule: "json-parseable", message: `JSON is not parseable: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "error", recoverable: true, suggestion: "Retry generation to get valid JSON", }); } return violations; } export function jsonRule() { return { name: "json-structure", description: "Validates JSON structure and balance", streaming: true, severity: "error", recoverable: true, check: (context) => { const violations = []; violations.push(...validateJsonStructure(context)); violations.push(...validateJsonChunks(context)); if (context.completed) { violations.push(...validateJsonParseable(context)); } return violations; }, }; } export function strictJsonRule() { return { name: "json-strict", description: "Strict JSON validation including structure and parseability", streaming: false, severity: "error", recoverable: true, check: (context) => { const violations = []; if (!context.completed) { return violations; } const { content } = context; if (!looksLikeJson(content)) { violations.push({ rule: "json-strict", message: "Content does not appear to be JSON (must start with { or [)", severity: "error", recoverable: true, }); return violations; } violations.push(...validateJsonParseable(context)); if (violations.length === 0) { try { const parsed = JSON.parse(content.trim()); if (typeof parsed !== "object" || parsed === null) { violations.push({ rule: "json-strict", message: "JSON root must be an object or array", severity: "error", recoverable: true, }); } } catch { } } return violations; }, }; } export class JsonGuardrail { rule; constructor(strict = false) { this.rule = strict ? strictJsonRule() : jsonRule(); } check(context) { return this.rule.check(context); } get name() { return this.rule.name; } } //# sourceMappingURL=json.js.map