Skip to content

Port JSONCompleter and drop the PartialJSONDecoder dependency - #259

Merged
mattt merged 4 commits into
mainfrom
mattt/port-jsoncompleter
Sep 17, 2026
Merged

mattt merged 4 commits into
mainfrom
mattt/port-jsoncompleter

Conversation

@mattt

@mattt mattt commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

GeneratedContent(json:) completed truncated JSON by appending a single closing brace, bracket, or empty string. That fails on every realistic streaming cut, including nested objects, open arrays, partial literals, and the example in its own doc comment, and it turned a trailing backslash into a literal quote. Meanwhile SystemLanguageModel pulled in PartialJSONDecoder for real completion of the same kind of input.

This ports JSONCompleter from that package as an internal type and uses it in GeneratedContent(json:), so truncated JSON now completes to the exact structure the model was emitting. Text that ends inside an escape sequence is cut back to the escape start so the result stays valid JSON. SystemLanguageModel now calls GeneratedContent(json:) directly, and the package dependency is removed.

GeneratedContent(json:) completed truncated JSON by appending a single
closing brace, bracket, or empty string. That failed on every realistic
streaming cut, including nested objects, open arrays, partial literals,
and the example in its own doc comment, and turned a trailing backslash
into a literal quote. The system model separately pulled in
PartialJSONDecoder to get real completion for the same job.

Port JSONCompleter from PartialJSONDecoder as an internal type and use
it in GeneratedContent(json:). It walks the text as a JSON value and
appends exactly the closing characters the input is missing, with a
depth limit. Text that ends inside an escape sequence is cut back to
the escape start so the result stays valid JSON. The system model now
calls GeneratedContent(json:) directly, and the package dependency is
removed.
…mmas

A complete empty object or array was reported as needing a closing
character, so the enclosing container closed right after it and every
later value was discarded. Whitespace between a complete value and the
following comma was treated as a missing comma with the same effect.
Treat an empty container as complete and skip whitespace after a value
before looking for the comma.

Also cite the exact upstream revision in the attribution: the ported
source is MIT licensed at that revision, and the same source ships in
the 1.0.0 release under Apache-2.0.
findEndOfCompleteValue re-ran the completer on a value its callers had
already found complete, then scanned it again to locate its end. Each
nested complete sibling doubled the work, so a complete sibling nested
20 deep before an incomplete tail took half a second and 30 deep would
take minutes. This runs on every streamed snapshot. Drop the rescan and
rely on the linear string, bracket, literal, and number scanners.

The depth counter also advanced twice per nesting level, once entering
a container and once per element, so the effective limit was half the
documented value. Count each level once.
@mattt
mattt merged commit 364badc into main Sep 17, 2026
12 checks passed
@mattt
mattt deleted the mattt/port-jsoncompleter branch September 17, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant