Skip to content

ahmeddoghri/structstream

Repository files navigation

🧩 structstream

Get valid JSON out of a model that keeps almost getting it right.

CI tests python deps license

Raw json.loads parses 7% of realistic malformed model output. One repair pass recovers 100%, and checks it matches your schema. No retries, no dependencies: python -m structstream.eval.

You asked for JSON. The model gave you JSON wrapped in a ```json fence, with a trailing comma, using single quotes, preceded by "Sure, here you go!" like a customer service rep, and cut off mid-string because it hit the token limit mid-thought. Your parser throws. Your pipeline dies. You retry the whole expensive call and pray to a distribution you don't control.

There are two honest ways out. Constrain the decoding so only valid tokens are ever emitted (the grammar approach behind tools like Outlines), or repair the almost-valid output after the fact, which is cheaper and doesn't require rearchitecting your inference stack. structstream does the second, plus the part everyone conveniently forgets: it validates the repaired value against your schema, because valid-but-wrong JSON breaks your code exactly as hard as invalid JSON does, just with more confidence on the way down.

Pure stdlib. No model, no network, no dependencies, no eval() anywhere near it. Every repair is a few lines you can actually read.


The result in one command

python -m structstream.eval
json repair benchmark: 15 realistic malformed model outputs

  raw json.loads         1/15 = 7% parse
  structstream repair    15/15 = 100% parse AND match schema

  recovered by repair    14 outputs that would have crashed your pipeline

Every one of those 15 is a failure a real model actually produces, not a synthetic edge case I invented to pad the number. The single one raw json.loads handles is the already-valid case, the model having one good day. The other 14 are the daily-driver failures: code fences, trailing commas, single quotes, prose around the object, truncation. structstream turns all of them back into usable data, and confirms each result has the right keys and types before it hands them back, instead of celebrating too early.

Install

git clone https://github.com/ahmeddoghri/structstream
cd structstream && pip install -e .
python examples/quickstart.py

Use it

from structstream.repair import repair_json
from structstream.schema import matches

schema = {
    "type": "object",
    "required": ["name", "age"],
    "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
}

messy = "Here you go:\n```json\n{name: 'Ada', age: 36,}\n```"

result = repair_json(messy)
print(result.ok)              # True
print(result.value)           # {"name": "Ada", "age": 36}
print(result.fixes_applied)   # what it had to fix, so nothing is a black box
print(matches(result.value, schema))   # True: right shape, not just valid JSON

What it fixes

Each repair targets a specific, common failure, applied in a safe order. Text that is already valid passes straight through untouched.

Fix Handles
stripped_code_fence ```json ... ``` and bare ``` wrappers
extracted_json_span "Here is your JSON:" and other prose around the object
quoted_bare_keys {name: 1} to {"name": 1}
single_to_double_quotes {'a': 'b'} to {"a": "b"}
removed_trailing_comma [1, 2, 3,] and {"a": 1,}
closed_open_brackets objects and arrays cut off at the token limit
closed_open_string a string the model never closed

Why the schema check matters

Repairing to valid JSON is only half the job. A model can hand you flawless JSON with the wrong keys or a string where you needed a number, and your code downstream breaks exactly the same way. The bundled validator supports the common subset (typed object properties, required keys, typed arrays, scalars), so repair_json plus matches gives you a value that is both parseable and the right shape. The benchmark only counts a sample as recovered if it clears both bars.

Tests

pip install pytest && pytest -q      # 15 passing

License

MIT © Ahmed Doghri

About

Get valid JSON out of a model that keeps almost getting it right. A repair pass plus schema validator, with a benchmark of realistic malformed outputs. Zero-dependency Python.

Topics

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages