Commit 9ed751e
authored
feat(stream-object): add /stream-object endpoint with SDK support (#73)
* feat(gateway): add /stream-object endpoint for streaming JSON objects
Add a new /stream-object endpoint that streams partial JSON objects as
they're generated using Server-Sent Events (SSE). This follows
Anthropic's input_json_delta pattern for real-time structured output.
Features:
- Partial JSON parsing using partial-json library
- Emits partial-object events with parsed objects as tokens arrive
- Emits object event with final validated object
- Full SSE event schema: session, partial-object, object, result, done
- Uses --json-schema CLI flag for constrained decoding
Closes #67
* fix(gateway): improve error handling and validation in stream-object
- Add logging for unexpected errors in partial JSON parsing
- Distinguish interrupts from real errors in buffer processing
- Send error event when structured_output missing and JSON parse fails
- Send error event for internal stream processing errors instead of re-throwing
- Add JSON Schema validation requiring valid schema keywords
- Apply JSON Schema validation to generateObjectRequest as well
Tests:
- Add test for buffer processing on CLI close
- Add test for non-JSON line handling
- Add test for unparseable JSON fallback case
- Add test for stdin.end() verification
- Add tests for JSON Schema validation
* feat(typescript-sdk): add streamObject() for streaming JSON objects
Add streamObject() function to the TypeScript SDK that consumes the
gateway's /stream-object endpoint:
- Stream partial objects via partialObjectStream (async iterable)
- Get final validated object via object promise
- Session ID resolves early, usage resolves at end
- Zod schema validation for partial objects (best-effort) and final object (strict)
- Comprehensive test coverage (17 tests)
- Example usage in examples/stream-object.ts
Part of #67
* fix(gateway): use prompt injection for stream-object to enable real partial streaming
The --json-schema flag doesn't stream JSON tokens incrementally - it streams
natural language text and only provides the JSON at the end in structured_output.
This change uses prompt injection instead:
- Inject schema into prompt with JSON output instructions
- Add JSON generator system prompt
- Parse accumulated text with partial-json library
- Add parseJsonResponse() fallback for markdown/text wrapping
Workaround for: anthropics/claude-code#15511
* fix(example): show user-visible updates and debug info in stream-object
- Number updates based on meaningful changes (day count) shown to user
- Track total stream updates from gateway separately
- Display debug info at the bottom with both counts and token usage
* fix(stream-object): improve error handling and add comprehensive tests
Addresses review feedback from PR review agents:
Gateway improvements:
- Fix silent failure in close handler catch block (now logs and emits error)
- Add diagnostic info to parseJsonResponse errors (tracks all parse attempts)
- Add logging for content_block_delta events missing text field
- Improve cleanup function with try/catch for kill(), SIGKILL escalation logging
- Simplify buildStreamObjectArgs to return string[] (remove unused values)
- Update endpoint docstring with precise streaming limitation explanation
SDK improvements:
- Use StreamObjectOptions<T> interface in function signature (was inline type)
- Optimize isCriticalEvent check with const Set instead of array
New tests:
- Empty response handling (CLI returns no JSON)
- Markdown code block stripping fallback
- JSON embedded in surrounding text extraction
- Array schema parsing
* test(sdk): add abort signal integration test for streamObject
Adds test infrastructure and integration test for aborting streams mid-flight:
- createDelayedSSEStream: mock SSE stream with configurable delays
- createDelayedMockSSEResponse: wrapper for delayed SSE responses
- Test verifies: partial objects received, abort triggered, AbortError thrown
* feat(python-sdk): add stream_object() for streaming JSON objects
Add stream_object() function to the Python SDK for streaming structured
JSON responses via SSE. Follows patterns from TypeScript SDK and
stream_text() implementation.
Features:
- StreamObjectResult[T] dataclass with partial_object_stream async iterator
- Pydantic validation: best-effort for partials, strict for final object
- Futures for session_id(), usage(), and object() resolution
- HTTPObjectStreamContext async context manager for resource cleanup
Includes:
- 10 comprehensive tests covering success, errors, and edge cases
- Example script demonstrating travel itinerary streaming
- NO_OBJECT error code for incomplete streams
* chore(ts-sdk): update stream-object example to 3-day itinerary
Align TypeScript SDK example with Python SDK example for parity.
Both now generate a 3-day Tokyo itinerary with 2-3 activities per day.
* fix(stream-object): address PR review feedback
Improvements from comprehensive PR review:
Python SDK:
- Add T = TypeVar("T", bound=BaseModel) for better type safety
- Make SSE event models frozen with ConfigDict
- Refine SSE event type annotations (dict[str, Any] | None)
- Add explicit httpx exception handling (ReadTimeout, RemoteProtocolError)
- Improve docstrings documenting validation rejection
- Add cancellation mid-stream test (parity with TypeScript abort test)
TypeScript SDK:
- Add @throws JSDoc annotations for validation errors
- Clarify partial object comments
- Document object promise rejection on validation failure
Gateway:
- Fix silent failure: parseJsonResponse now returns extraction strategy
- Emit warning event when fallback extraction is used (markdown-block, etc.)
- Fix silent failure: emit warning event on buffer data loss (clean CLI exit)
- Add streamObject integration tests
All tests passing (204 total)
* test(stream-object): add timeout and connection error tests
- Add timeout behavior test for TypeScript SDK streamObject
- Add mid-stream connection error test for TypeScript SDK
- Add connection/timeout/protocol error tests for Python SDK
- Fix Python SDK to wrap httpx exceptions during request phase
* docs: regenerate OpenAPI spec for stream-object endpoint1 parent 1e14a82 commit 9ed751e
File tree
30 files changed
+4399
-29
lines changed- docs
- packages
- gateway
- __tests__
- integration
- routes
- src
- openapi
- routes
- sdks
- python
- examples
- src/koine_sdk
- stream
- tests
- typescript
- __tests__
- examples
- src
30 files changed
+4399
-29
lines changedSome generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
67 | 96 | | |
68 | 97 | | |
69 | 98 | | |
| |||
86 | 115 | | |
87 | 116 | | |
88 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
89 | 160 | | |
90 | 161 | | |
91 | 162 | | |
| |||
164 | 235 | | |
165 | 236 | | |
166 | 237 | | |
167 | | - | |
| 238 | + | |
168 | 239 | | |
169 | 240 | | |
170 | 241 | | |
| |||
183 | 254 | | |
184 | 255 | | |
185 | 256 | | |
186 | | - | |
| 257 | + | |
187 | 258 | | |
188 | 259 | | |
189 | 260 | | |
190 | 261 | | |
191 | | - | |
| 262 | + | |
192 | 263 | | |
193 | 264 | | |
194 | 265 | | |
| |||
247 | 318 | | |
248 | 319 | | |
249 | 320 | | |
250 | | - | |
| 321 | + | |
251 | 322 | | |
252 | 323 | | |
253 | | - | |
| 324 | + | |
254 | 325 | | |
255 | 326 | | |
256 | 327 | | |
| |||
298 | 369 | | |
299 | 370 | | |
300 | 371 | | |
301 | | - | |
| 372 | + | |
302 | 373 | | |
303 | 374 | | |
304 | | - | |
| 375 | + | |
305 | 376 | | |
306 | 377 | | |
307 | 378 | | |
| |||
439 | 510 | | |
440 | 511 | | |
441 | 512 | | |
442 | | - | |
| 513 | + | |
443 | 514 | | |
444 | 515 | | |
445 | 516 | | |
| |||
478 | 549 | | |
479 | 550 | | |
480 | 551 | | |
481 | | - | |
| 552 | + | |
482 | 553 | | |
483 | 554 | | |
484 | 555 | | |
| |||
511 | 582 | | |
512 | 583 | | |
513 | 584 | | |
514 | | - | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
515 | 608 | | |
516 | 609 | | |
517 | 610 | | |
| |||
562 | 655 | | |
563 | 656 | | |
564 | 657 | | |
565 | | - | |
| 658 | + | |
566 | 659 | | |
567 | 660 | | |
568 | 661 | | |
| |||
601 | 694 | | |
602 | 695 | | |
603 | 696 | | |
604 | | - | |
| 697 | + | |
605 | 698 | | |
606 | 699 | | |
607 | 700 | | |
| |||
652 | 745 | | |
653 | 746 | | |
654 | 747 | | |
655 | | - | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
656 | 848 | | |
657 | 849 | | |
658 | 850 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| 124 | + | |
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
| |||
134 | 135 | | |
135 | 136 | | |
136 | 137 | | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
137 | 155 | | |
138 | 156 | | |
139 | 157 | | |
| |||
0 commit comments