Skip to content
reaatechREAATECH

@reaatech/structured-repair-core

pending npm

Fixes malformed LLM JSON output by applying a configurable pipeline of repair strategies to ensure compatibility with a provided Zod schema. It exports utility functions that return validated, type-inferred data or detailed metadata about the repair process.

@reaatech/structured-repair-core

npm version License: MIT CI

Status: Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production.

Core repair engine that catches malformed LLM structured outputs and repairs them instead of crashing. Takes a Zod schema plus raw LLM output and applies a graduated pipeline of four strategies to produce valid, schema-conforming data.

Installation

terminal
npm install @reaatech/structured-repair-core
# or
pnpm add @reaatech/structured-repair-core

Feature Overview

  • 4 graduated repair strategies — applied in sequence, each targeting a specific class of LLM output failure
  • Type coercion — auto-converts string→number, string→boolean, string→date, and more via Zod’s coercion primitives
  • Extra field removal — recursively strips hallucinated fields not defined in your schema (critical for .strict())
  • Input analysis — inspects raw input for common issues without applying repairs
  • Strategy customization — pick which strategies to run, in what order
  • Detailed result tracking — per-step success/failure metadata for debugging
  • Full type inference — repaired data inherits the exact z.infer<T> type from your schema
  • Dual ESM/CJS output — works with import and require

Quick Start

typescript
import { z } from "zod";
import { repair, repairOutput, isValid, analyzeInput } from "@reaatech/structured-repair-core";
 
const userSchema = z.object({
  name: z.string(),
  age: z.number(),
  email: z.string().email().optional(),
});
 
// LLM output with fences, trailing comma, string coercion
const llmOutput = '```json\n{ "name": "John", "age": "30" }\n```';
 
// Quick repair — throws UnrepairableError on failure
const data = await repair(userSchema, llmOutput);
// => { name: "John", age: 30 }
 
// Full repair with detailed result
const result = await repairOutput({
  schema: userSchema,
  input: llmOutput,
  debug: true,
  strategies: ["strip-fences", "fix-json-syntax", "coerce-types"],
});

Repair Strategies

StrategyWhat it fixes
strip-fencesMarkdown code fences (```json {...} ```), nested fences, language hints
fix-json-syntaxTrailing commas, missing braces/brackets, unquoted keys, single quotes, missing commas, NaN/Infinity/undefined, comments
coerce-typesString→number, string→boolean, string→bigint, string→date, nested object/array coercion
remove-extra-fieldsHallucinated fields not in schema, deeply nested (works with .strict() schemas)

API Reference

repair(schema, input?, options?)

Quick repair that returns typed data or throws UnrepairableError.

typescript
import { repair } from "@reaatech/structured-repair-core";
 
const data = await repair(userSchema, rawLlmOutput);
const data = await repair(userSchema, rawLlmOutput, { debug: true });
ArgumentTypeDescription
schemaz.ZodType<T>Zod schema to validate against
inputstringRaw LLM output string
optionsRepairOptions<T>Optional configuration (see below)

repairOutput(options)

Full repair with detailed step-by-step result information.

typescript
import { repairOutput } from "@reaatech/structured-repair-core";
 
const result = await repairOutput({
  schema: userSchema,
  input: rawLlmOutput,
  debug: true,
  strategies: ["strip-fences", "fix-json-syntax", "coerce-types"],
  onFailure: (context) => {
    console.error("Repair failed:", context.errors);
  },
});
 
if (result.success) {
  console.log("Repaired:", result.data);
  console.log("Steps:", result.steps);
} else {
  console.log("Errors:", result.errors);
}

RepairOptions<T>

PropertyTypeDefaultDescription
schemaz.ZodType<T>(required)Zod schema to validate against
inputstring(required)Raw LLM output string
debugbooleanfalseEnable debug logging to stderr
strategiesRepairStrategyName[]All fourWhich strategies to apply, in order
onFailure(context: RepairFailureContext) => voidCallback invoked when all strategies fail

RepairResult<T>

PropertyTypeDescription
successbooleanWhether repair produced valid data
dataT | nullRepaired data (typed per schema), or null on failure
originalInputstringThe input as received
repairedInputstring?Input after string-level repairs
stepsRepairStep[]Per-strategy attempt details
errorsRepairError[]Accumulated errors across all strategies

isValid(schema, input)

Check if input is valid against the schema without applying repairs.

typescript
import { isValid } from "@reaatech/structured-repair-core";
 
const ok = isValid(userSchema, '{ "name": "test", "age": 25 }');
// => true

analyzeInput(input)

Analyze raw input for common issues without applying repairs.

typescript
import { analyzeInput } from "@reaatech/structured-repair-core";
 
const analysis = analyzeInput('```json\n{ "a": 1, }\n```');
// {
//   isValidJson: false,
//   hasFences: true,
//   issues: [
//     { type: 'fence-wrapper', description: '...' },
//     { type: 'trailing-comma', description: '...' }
//   ]
// }

Error Classes

All errors extend StructuredRepairError which includes code: string and message: string.

ClassCodeWhen
StructuredRepairError(custom)Base class for all repair errors
UnrepairableErrorUNREPAIRABLEAll repair strategies exhausted without success
SchemaMismatchErrorSCHEMA_MISMATCHType coercion failed at the Zod level
JsonSyntaxErrorJSON_SYNTAXInput could not be parsed as JSON

Usage Patterns

Debugging a Failed Repair

typescript
const result = await repairOutput({
  schema: mySchema,
  input: badLlmOutput,
  debug: true,
  onFailure: ({ originalInput, lastAttempt, errors, steps }) => {
    console.error("Repair failed for input:", originalInput);
    console.error("Last attempt:", lastAttempt);
    console.error("Steps:", JSON.stringify(steps, null, 2));
    console.error("Errors:", errors);
  },
});

Custom Strategy Order

typescript
// Skip extra field removal, run coerce-types first
const result = await repairOutput({
  schema: mySchema,
  input: rawLlmOutput,
  strategies: ["coerce-types", "fix-json-syntax"],
});

Strict Schema with Extra Fields

typescript
const strictSchema = z.object({
  id: z.number(),
  name: z.string(),
}).strict(); // Any extra field will cause validation failure
 
const llmOutput = '{ "id": 1, "name": "Alice", "hallucinated": true }';
 
// remove-extra-fields strategy strips "hallucinated" automatically
const data = await repair(strictSchema, llmOutput);
// => { id: 1, name: "Alice" }

License

MIT