partial-json-stream
A streaming JSON parser that yields partial valid trees as tokens arrive.
Python port of @mukundakatta/streamparse.
Built for LLM tool-call payloads, structured-output streams, and any place
json.loads waits too long.
from partial_json_stream import JsonStreamParser
p = JsonStreamParser()
p.push('{"name":"Cl')
p.snapshot().value # {'name': 'Cl'}
p.push('aude","tools":[1,2')
p.snapshot().value # {'name': 'Claude', 'tools': [1, 2]}
p.push(']}')
p.end()
p.snapshot().complete # True
Every snapshot is valid JSON. Synthetic closure of in-progress strings,
numbers, and containers means you can render it directly, persist it, or
round-trip it through json.loads(json.dumps(...)).
Why
Every Python LLM project ships its own broken version of this. They wait for
the full payload (slow), or hand-roll json.loads in a try/except on a growing
buffer (O(n²) and useless until the very last byte), or use ad-hoc regex.
partial-json-stream is the version you should reuse.
Install
pip install partial-json-stream
Zero runtime dependencies. Python 3.9+.
API
JsonStreamParser(lenient=True, max_depth=256, max_string_length=None)
Lenient mode (default) tolerates the LLM-isms you actually see in production:
- trailing commas:
{"a": 1,} - single-quoted strings:
{'a': 'hi'} - unquoted keys:
{a: 1, b: 2} ```jsoncode fences// lineand/* block */comments- prose before/after the JSON:
Sure! Here it is: {...}
Set lenient=False for strict RFC 8259 mode.
parser.push(chunk: str) -> None
Feed in more bytes.
parser.end() -> None
Signal end of input. Lenient mode silently closes any open containers.
parser.snapshot() -> Snapshot
Take a snapshot. Always returns valid JSON.
@dataclass
class Snapshot:
value: Any
complete: bool
path: list # cursor location, e.g. ['tools', 2, 'input', 'path']
bytes_in: int
confidence: float # 0..1
parser.on(event, fn)
Subscribe to events. Returns an unsubscribe function.
| event | callback signature | when |
|---|---|---|
field |
(path, value) |
every leaf commit |
container |
(path, value) |
every {} or [] close |
partial |
(snapshot) |
every push() |
complete |
(value) |
once, when top-level value closes |
error |
(err) |
on syntax error (suppresses raise) |
parse_partial(text: str, lenient=True) -> Any
One-shot helper for a possibly-truncated blob.
from partial_json_stream import parse_partial
truncated = '{"type":"tool_use","name":"edit","input":{"path":"a/b.ts","cont'
parse_partial(truncated)
# {'type': 'tool_use', 'name': 'edit', 'input': {'path': 'a/b.ts', 'cont': None}}
Real-world usage
Recover a dropped tool call
from partial_json_stream import parse_partial
# The connection dropped before the model finished writing its tool call.
partial = '{"type":"tool_use","name":"edit_file","input":{"path":"a.ts","patches":[{"line":10,"op":"insert","content":"print('
value = parse_partial(partial)
# value['input']['patches'][0] is fully usable.
Stream Anthropic tool calls
from partial_json_stream import JsonStreamParser
parser = JsonStreamParser()
@parser.on("partial", lambda snap: ui.update(snap.value))
for chunk in client.messages.stream(...).text_stream:
parser.push(chunk)
parser.end()
License
MIT, by Mukunda Katta.
Metadata
Release files for partial-json-stream 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| partial_json_stream-1.0.0.tar.gz | 13.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| partial_json_stream-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.1 kB
Release files / partial_json_stream-1.0.0.tar.gz
| Download URL | partial_json_stream-1.0.0.tar.gz |
|---|---|
| Size | 13.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
332455e4bacb3b2affd24d7af04178450ed8ee87a3d279b781313ddfc7f2e740
|
|
BLAKE2b-256 checksum How to use checksums |
5958cc793d3106d3ed1b6b48d9607cc69973cf0dde59d1b88f98f532011cdae2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / partial_json_stream-1.0.0-py3-none-any.whl
| Download URL | partial_json_stream-1.0.0-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
07f36b47d9bcb436f4d31d5a039733222bedccae2ead3f74ed9b4c74ffca1714
|
|
BLAKE2b-256 checksum How to use checksums |
8610ca97941654c8459b0c12b2c8d7b8d12304a4a57de143567552e03ba89a52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Security for partial-json-stream 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.