feat(protocol): document the chat-completions SSE response - #61
Merged
Merged
Conversation
The route answers with `application/json` or with `text/event-stream` depending on `stream`, and HttpApi can only declare one success schema. So the generated spec described only the JSON, and a client built from it looks for choices[].message.content on a streaming response and finds nothing on every frame - because a chunk carries choices[].delta instead. That is the same gap /event has, and the same fix site. Adds ChatCompletionChunk, which is genuinely a different shape rather than a reuse: the object discriminator is chat.completion.chunk, and each choice carries a partial delta instead of a complete message. Every delta field is optional because OpenAI's stream uses the shape sparsely - the first frame typically carries only role, middle frames only content, a tool call arrives spread across frames, and the terminator carries an empty delta with finish_reason set. A schema that required content would reject the terminator. Declared for documentation and codegen only. The handler stays handleRaw and writes its own SSE frames, because choosing between JSON and a stream per request is exactly what HttpApi cannot express. Both content types are declared on the one 200 rather than the SSE replacing the JSON, so the spec says what actually happens instead of lying in the other direction. Registered through HttpApi.AdditionalSchemas. A $ref to a schema no endpoint references dangles otherwise, and a dangling ref generates a client with a missing type rather than failing loudly - which is why one of the three new tests asserts the ref resolves to a registered component rather than only that the ref is present. Coverage 211/0/0, route and wire tests 28/28, openapi tests 21/21, protocol and redrob typecheck clean. The generated artifacts are NOT regenerated here. `bun script/generate.ts` also rewrites packages/sdk/openapi.json, which is stale on clean develop by 991 lines including routes that are not mine, so committing it would sweep another session's unregenerated surface into this change. It needs its own regeneration commit, which is being raised separately.
No source change. `bun script/generate.ts` output only. Two things land together, and the split matters for review: The chat-completions SSE documentation from the previous commit - the 200 now declares both application/json and text/event-stream, and ChatCompletionChunk appears as a component. Pre-existing drift that was already in the repo before this branch. packages/sdk/openapi.json was stale on clean develop by roughly 900 lines, including routes that are not mine (/api/variant/paraphrase among them). Those were regenerated here because there is no way to regenerate only part of the file, and leaving them would mean the committed spec still does not describe the served API. This is why it is a separate commit: the source change is reviewable on its own, and this one is verifiable by re-running the generator. There is no CI check for this drift, which is why it accumulated. The client generator IS checked (packages/client check:generated fails on any diff) and passes; the SDK spec is not. Worth adding a check, but that is a change to CI rather than to artifacts and does not belong here. Also deliberately NOT committed: the ~54 files `generate.ts` reformats as a side effect, mostly markdown table alignment and line wrapping in untouched sources. Those are the generator running a formatter over the repo, not generated output, and sweeping them in would bury the three files that actually matter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(protocol): document the chat-completions SSE response
The route answers with
application/jsonor withtext/event-streamdepending onstream, and HttpApi can only declare one success schema. So the generated spec describedonly the JSON, and a client built from it looks for choices[].message.content on a
streaming response and finds nothing on every frame - because a chunk carries
choices[].delta instead. That is the same gap /event has, and the same fix site.
Adds ChatCompletionChunk, which is genuinely a different shape rather than a reuse: the
object discriminator is chat.completion.chunk, and each choice carries a partial delta
instead of a complete message. Every delta field is optional because OpenAI's stream uses
the shape sparsely - the first frame typically carries only role, middle frames only
content, a tool call arrives spread across frames, and the terminator carries an empty
delta with finish_reason set. A schema that required content would reject the terminator.
Declared for documentation and codegen only. The handler stays handleRaw and writes its own
SSE frames, because choosing between JSON and a stream per request is exactly what HttpApi
cannot express.
Both content types are declared on the one 200 rather than the SSE replacing the JSON, so
the spec says what actually happens instead of lying in the other direction.
Registered through HttpApi.AdditionalSchemas. A $ref to a schema no endpoint references
dangles otherwise, and a dangling ref generates a client with a missing type rather than
failing loudly - which is why one of the three new tests asserts the ref resolves to a
registered component rather than only that the ref is present.
Coverage 211/0/0, route and wire tests 28/28, openapi tests 21/21, protocol and redrob
typecheck clean.
The generated artifacts are NOT regenerated here.
bun script/generate.tsalso rewritespackages/sdk/openapi.json, which is stale on clean develop by 991 lines including routes
that are not mine, so committing it would sweep another session's unregenerated surface
into this change. It needs its own regeneration commit, which is being raised separately.