Skip to content

Add JSON Patch to SDK#3

Merged
kpwtxt merged 3 commits into
mainfrom
kpwtxt/gtm-744-json-patch-sdk
Jun 2, 2026
Merged

Add JSON Patch to SDK#3
kpwtxt merged 3 commits into
mainfrom
kpwtxt/gtm-744-json-patch-sdk

Conversation

@kpwtxt
Copy link
Copy Markdown
Contributor

@kpwtxt kpwtxt commented May 22, 2026

No description provided.

@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch 2 times, most recently from 602c697 to ff64ec5 Compare May 22, 2026 03:18
@kpwtxt kpwtxt requested review from RobinPicard, aschwa and rlouf May 22, 2026 12:34
Comment thread src/dottxt/client.py Outdated
Comment thread src/dottxt/streaming.py Outdated
Comment thread examples/stream_hitl_approval.py
Copy link
Copy Markdown
Contributor

@aschwa aschwa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slick examples! A few clarifying nits

Comment thread LICENSE
Comment thread src/dottxt/streaming.py Outdated
Comment thread src/dottxt/streaming.py Outdated
Comment thread src/dottxt/streaming.py Outdated
Comment thread src/dottxt/streaming.py Outdated
Comment thread examples/stream_fanout.py Outdated
Comment thread examples/stream_fanout.py
Comment thread examples/stream_early_routing.py Outdated
Comment thread examples/stream_early_routing.py Outdated
Comment thread docs/client.md Outdated
Comment thread docs/client.md Outdated
Comment thread examples/stream_hitl_approval.py Outdated
@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch 2 times, most recently from 8431029 to 1dcbafd Compare May 27, 2026 20:22
@kpwtxt kpwtxt requested review from aschwa and rlouf May 27, 2026 20:26
Comment thread src/dottxt/streaming.py
@rlouf
Copy link
Copy Markdown
Member

rlouf commented May 28, 2026

Almost there! Not sure what the LICENSE file is doing there + should we add an example of constructing the JSON object from the stream (there must be a python library that does that?).

@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch from 1dcbafd to 404fbc5 Compare May 28, 2026 17:32
@kpwtxt
Copy link
Copy Markdown
Contributor Author

kpwtxt commented May 28, 2026

@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch 2 times, most recently from ca02648 to 536f885 Compare May 28, 2026 19:53
@kpwtxt
Copy link
Copy Markdown
Contributor Author

kpwtxt commented May 28, 2026

Added a CLI wrapper around the stream client:

$ dottxt stream  --timing --model openai/gpt-oss-20b   --schema contact.schema.json   "Extract: John Smith <john@acme.com>, VP Engineering"
{"op":"add","path":"","value":{}}
[timing] #1   t=+2209.8ms  Δ=+2209.8ms  add /
{"op":"add","path":"/email","value":".<john@acme.com>"}
[timing] #2   t=+2210.1ms  Δ=+0.3ms     add /email
{"op":"add","path":"/name","value":"John Smith"}
[timing] #3   t=+2211.6ms  Δ=+1.5ms     add /name
{"op":"add","path":"/role","value":"VP Engineering"}
[timing] #4   t=+2242.8ms  Δ=+31.3ms    add /role
[timing] done events=4 elapsed=2251.6ms

@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch from 536f885 to fbb051f Compare May 28, 2026 19:58
Comment thread src/dottxt/cli.py Outdated
@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch from fbb051f to 1142e75 Compare May 28, 2026 20:49
@kpwtxt kpwtxt requested a review from rlouf May 28, 2026 21:49
Comment thread docs/cli.md Outdated
Comment thread docs/cli.md Outdated
Comment thread docs/client.md Outdated
Comment thread docs/client.md Outdated
Comment thread docs/client.md Outdated
- `event.op` — the raw RFC 6902 operation (`{"op": "add", "path": ..., "value": ...}`)
- `event.snapshot` — an independent deep copy of the document built up to
and including this op
- `event.is_leaf` / `event.field` / `event.value` convenience demux for
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the case for the is_leaf method?

Copy link
Copy Markdown
Contributor Author

@kpwtxt kpwtxt May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It lets you filter structural add ops like {} [] and the root object creation. If is_leaf == True then a field / value were set by the add op.

Copy link
Copy Markdown
Contributor Author

@kpwtxt kpwtxt May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rlouf Here's a real example showing what ops look like with nested objects:

{"op":"add","path":"","value":{}}
{"op":"add","path":"/address","value":{}}
{"op":"add","path":"/address/city","value":"Raleigh"}
{"op":"add","path":"/address/country","value":"/USA"}
{"op":"add","path":"/address/postal_code","value":"27604"}
{"op":"add","path":"/address/street","value":"123 Main St"}
{"op":"add","path":"/email","value":"john@acme.com"}
{"op":"add","path":"/name","value":"John Smith"}
{"op":"add","path":"/role","value":"VP Engineering"}

Two options come to mind:

  1. Drop is_leaf() and force the client to figure how to handle something like "path":"","value":{} or "path":"/address","value":{}

  2. Keep is_leaf() as a convience function, but leave it out of the example as the field name string match is sufficient to skip non-leaf values.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example would work without the is_leaf() check:

        async for event in stream:
            if not event.is_leaf:
                continue
            match event.field:
                case "action":
                    proposed_action = event.value
                    print(f"proposed action: {event.value}")
                    if event.value in HIGH_RISK_ACTIONS:
                        approved = await ask_human(f"Approve action '{event.value}'?")
                        if not approved:
                            print("operator declined — reply will not be sent")
                case "reply" if approved:
                    await send_reply(event.value)
                case "reply":
                    print(f"discarded reply (action '{proposed_action}' declined)")

But this example would change without adding its own check for non-leaf values:

        async for event in stream:
            if not event.is_leaf:
                continue
            print(f"{event.field:>24} = {event.value!r}")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went with 2) for now. Dropped is_leaf prior to the match since the field name/value check filters. Kept it for the field iteration as a filter against op events for empty objects.

Comment thread docs/client.md Outdated
Comment thread examples/stream_reconstruct.py
Comment thread examples/stream_reconstruct.py Outdated
Comment thread src/dottxt/cli.py
Comment thread src/dottxt/cli.py
Comment thread src/dottxt/cli.py
Comment thread src/dottxt/streaming.py Outdated
Comment thread src/dottxt/streaming.py Outdated
Comment thread src/dottxt/streaming.py
@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch 2 times, most recently from 3c29c46 to d5cc04a Compare May 29, 2026 18:48
@kpwtxt kpwtxt force-pushed the kpwtxt/gtm-744-json-patch-sdk branch from d5cc04a to 96d1b7c Compare May 29, 2026 19:01
@kpwtxt kpwtxt requested a review from rlouf May 29, 2026 19:02
@kpwtxt kpwtxt merged commit 72aa436 into main Jun 2, 2026
4 of 5 checks passed
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.

4 participants