Skip to main content

Validate, repair, retry LLM structured output (JSON/YAML/TOML/python-literal)

Project description

arcanada-output-guard

PyPI Python License: MIT

Validate, repair, and retry LLM structured output (JSON / YAML / TOML / Python literal) — battle-tested 15-strategy repair chain with two-pass orchestrator. Python sibling of @arcanada/output-guard with shared golden fixtures and byte-equal parity gate.

Install

pip install arcanada-output-guard
# or
uv add arcanada-output-guard
# or
poetry add arcanada-output-guard

Requires Python ≥ 3.11. Runtime deps: pyyaml, tomli-w, pydantic, jsonschema, json-repair.

Quick start

from output_guard import repair

# Functional API — format-only repair, no schema
result = repair('{"name": "Alice", "age": 30,}', "json")

print(result.data)                  # {'name': 'Alice', 'age': 30}
print(result.repaired)              # True
print(result.pass_)                 # 'A'
print(result.strategies_applied)    # ['fix-commas']

Stateful API with pydantic schema

from pydantic import BaseModel
from output_guard import OutputGuard

class User(BaseModel):
    name: str
    age: int

guard = OutputGuard(format="json", schema=User)
result = guard.repair('```json\n{"name": "Bob", "age": 25}\n```')

print(result.data)               # User(name='Bob', age=25)
print(result.pass_)              # 'A' or 'B'

Output format support

  • JSON — strict + relaxed parsing with common error recovery (trailing commas, unquoted keys, single→double quote, true/false casing, fenced blocks).
  • YAML — structural repair for indentation + missing keys.
  • TOML — fixing malformed tables and value assignments.
  • Python literaldict / list literals via safe ast.literal_eval.
  • Auto-detect — pass format="auto" to infer from content.

RepairResult semantics

@dataclass(frozen=True, slots=True)
class RepairResult:
    repaired: bool                       # False when raw was already valid
    raw: str                             # original input as supplied
    pass_: Literal["A", "B"]             # "A" = combined-apply; "B" = isolating fallback
    data: Any = None                     # parsed + (optionally) schema-validated payload
    strategies_applied: list[str] = []   # ordered list of repair strategies invoked

MAX_RETRIES exhaustion signals via ParseError raise, not via a pass_ value.

Async wrapper for LLM calls

import asyncio
from pydantic import BaseModel
from output_guard import guarded_generate, GuardedGenerateOptions

class Answer(BaseModel):
    result: int

async def my_llm(prompt: str, history=None) -> str:
    # call your LLM client here (OpenAI / Anthropic / OpenRouter / MC / etc.)
    return '{"result": 42}'

async def main():
    out = await guarded_generate(GuardedGenerateOptions(
        generate=my_llm,
        prompt='Return JSON {"result": <integer>}',
        schema=Answer,
        fmt="json",
        max_retries=3,
    ))
    print(out.data)                  # Answer(result=42)
    print(out.pass_)                 # 'A' | 'B'
    print(out.strategies_applied)

asyncio.run(main())

Error handling

from output_guard import (
    ParseError,
    SchemaValidationError,
    RepairError,
    GuardedGenerationError,
)

try:
    result = guard.repair(bad_input)
except SchemaValidationError:
    ...  # input parsed but doesn't satisfy schema
except ParseError:
    ...  # MAX_RETRIES exhausted — all strategies failed
except RepairError:
    ...  # a repair strategy itself raised
except GuardedGenerationError:
    ...  # guarded_generate gave up after max_retries LLM round-trips

Schema adapters

from output_guard import pydantic_adapter, jsonschema_adapter
from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

# pydantic v2
adapter = pydantic_adapter(User)

# JSON Schema (dict)
adapter = jsonschema_adapter({
    "type": "object",
    "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
    "required": ["name", "age"],
})

Batch API

from output_guard import repair_batch

results = repair_batch(
    ['{"a": 1,}', '{"b": 2,}', '{"c": 3,}'],
    fmt="json",
)
# returns list[RepairResult]; per-item failure surfaces as RepairResult with repaired=False

Documentation

License

MIT — see LICENSE.


Жизнь одного человека имеет значение / One human life matters

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arcanada_output_guard-0.1.1.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

arcanada_output_guard-0.1.1-py3-none-any.whl (26.2 kB view details)

Uploaded Python 3

File details

Details for the file arcanada_output_guard-0.1.1.tar.gz.

File metadata

  • Download URL: arcanada_output_guard-0.1.1.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for arcanada_output_guard-0.1.1.tar.gz
Algorithm Hash digest
SHA256 deabb6f0f921cdc3952020e088a45acb7ea6aa1ceea46708004662c892f58d2e
MD5 324560a66b1a7a901dfb9363ae83fbfc
BLAKE2b-256 999335722be1aaae34b43ca00579cc809bcb528c6f65640fb12c7dc45f5d7a21

See more details on using hashes here.

File details

Details for the file arcanada_output_guard-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for arcanada_output_guard-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 65d761cd038c7a5c66000eea753565ea32549e92585b89fc20846d2fb4d317b6
MD5 0d885291b9f73d93f5aa7c17972825d0
BLAKE2b-256 808c563d933cb362c5178ab498cccb8b3e00b20473f1852ab667d5cc9397de44

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page