Parse partial / streaming JSON — the best-effort object out of an incomplete string (LLM token streams, SSE), and array items or OpenAI deltas emitted as they complete. Stdlib only.
from streamjson import parse_partial
parse_partial('{"a": 1, "b": [1, 2, 3') # token limit / mid-stream
# {'a': 1, 'b': [1, 2, 3]}
parse_partial('{"name": "Bob", "bio": "hal') # unterminated trailing string
# {'name': 'Bob'} # dropped, not guessedNo dependencies. No model call. A tiny tolerant parser plus two streaming helpers.
json.loads() throws the instant the stream is cut off — which is every
intermediate frame of an LLM response. You want the object so far, and you
want to act on array items the moment each one finishes, not after the whole
payload lands.
streamjson gives you the best partial object: completed pairs and elements are
kept, an incomplete trailing string / key / value is dropped, and open
containers are implicitly closed.
from streamjson import parse_partial, JSONStream, stream_items, iter_sse, accumulate_openaiBest-effort Python value from a possibly-incomplete JSON string. None if
nothing complete can be recovered.
parse_partial('{"a":1,"b":2,"c":') # {'a': 1, 'b': 2} — dangling key dropped
parse_partial('{"a":{"b":[1,2') # {'a': {'b': [1, 2]}} — containers closed
parse_partial('{"a":1,"ok":tr') # {'a': 1} — partial literal droppeds = JSONStream()
s.feed('{"a": 1, "b":')
s.value() # {'a': 1}
s.feed(' 2}')
s.value() # {'a': 1, 'b': 2}Yields each completed element of a top-level array (or the array under key)
exactly once, never a partial — even if a chunk boundary lands in the middle
of an element.
chunks = ['[{"i":0},{', '"i":1},{"i', '":2}]']
list(stream_items(chunks)) # [{'i': 0}, {'i': 1}, {'i': 2}]
doc = ['{"items":[{"x":1},', '{"x":2}]}']
list(stream_items(doc, key="items")) # [{'x': 1}, {'x': 2}]sse = (
'data: {"choices":[{"delta":{"content":"Hello"}}]}\n\n'
'data: {"choices":[{"delta":{"content":" world"}}]}\n\n'
'data: [DONE]'
)
accumulate_openai(sse) # 'Hello world' — the reconstructed message
list(iter_sse(sse)) # the two data payloads ([DONE] skipped)accumulate_openai is robust to partial / garbage payloads — it parses each
event best-effort and concatenates every choices[].delta.content it finds.
truncated_dump.json | streamjson # repair a cut-off dump → pretty JSON
streamjson reply.json --items # each completed array item, one per line
streamjson reply.json --items --key items # ... for the array under "items"
some-llm-call | streamjson --sse # reconstruct the assistant message
some-llm-call | streamjson --sse --raw # each SSE data payload, one per lineReads a file argument or stdin.
streamjson/
partial.py tolerant recursive-descent parser → best partial object
stream.py JSONStream + stream_items (array items as they complete)
sse.py iter_sse + accumulate_openai (OpenAI-style SSE)
cli.py the `streamjson` command
MIT. Stdlib only — no dependencies, no network, no API keys.