Skip to main content

Streaming JSON parser that yields partial valid trees as tokens arrive. For LLM tool calls, structured outputs, and partial recovery.

Project description

partial-json-stream

pypi python tests zero deps

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}
  • ```json code fences
  • // line and /* 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.

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

partial_json_stream-1.0.0.tar.gz (13.2 kB view details)

Uploaded Source

Built Distribution

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

partial_json_stream-1.0.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file partial_json_stream-1.0.0.tar.gz.

File metadata

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

File hashes

Hashes for partial_json_stream-1.0.0.tar.gz
Algorithm Hash digest
SHA256 332455e4bacb3b2affd24d7af04178450ed8ee87a3d279b781313ddfc7f2e740
MD5 edcd839d7416f54ff824f3d8dd5d4e57
BLAKE2b-256 5958cc793d3106d3ed1b6b48d9607cc69973cf0dde59d1b88f98f532011cdae2

See more details on using hashes here.

File details

Details for the file partial_json_stream-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for partial_json_stream-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 07f36b47d9bcb436f4d31d5a039733222bedccae2ead3f74ed9b4c74ffca1714
MD5 3765ecbc8ff35c538006447c789cde05
BLAKE2b-256 8610ca97941654c8459b0c12b2c8d7b8d12304a4a57de143567552e03ba89a52

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