fix: restore type safety on streaming chunk schema#21831
Open
peda-cos wants to merge 2 commits intoanomalyco:devfrom
Open
fix: restore type safety on streaming chunk schema#21831peda-cos wants to merge 2 commits intoanomalyco:devfrom
peda-cos wants to merge 2 commits intoanomalyco:devfrom
Conversation
## Problem
The streaming `transform` function in `OpenAICompatibleChatLanguageModel`
had lost type safety on the `Chunk` union type, caused by the error schema
being embedded directly inside the union alongside the base chunk shape.
This forced early binding of `chunk.value` before discriminating the error
branch, making TypeScript unable to narrow the type correctly downstream.
A TODO comment marked this as a known regression: "we lost type safety on
Chunk, most likely due to the error schema. MUST FIX".
## Root Cause
`createOpenAICompatibleChatChunkSchema` previously returned:
z.union([z.object({ ...fullShape... }), errorSchema])
The full base shape was inlined anonymously inside the union, making it
impossible to reference it as a standalone named type. As a result,
`chunk.value` could not be safely typed after the error branch was
discriminated away — TypeScript still saw it as the full union.
## Solution
Extract the base chunk object into a named top-level constant:
const chunkBaseSchema = z.object({ ... })
`createOpenAICompatibleChatChunkSchema` now composes it cleanly:
z.union([chunkBaseSchema, errorSchema])
In the `transform` function:
- Move `const value = chunk.value` *after* the error branch guard.
- Cast it explicitly as `z.infer<typeof chunkBaseSchema>` at the point of
assignment, which is now sound because the error branch has already
returned early.
- Access `chunk.value.error.message` directly inside the error branch,
before the cast, which is also correct since the union still carries the
error shape there.
## Changes
- `openai-compatible-chat-language-model.ts`
- Hoist `chunkBaseSchema` as a named module-level `const`.
- Simplify `createOpenAICompatibleChatChunkSchema` to
`z.union([chunkBaseSchema, errorSchema])`.
- Reorder `transform`: error guard first, then typed `value` assignment.
- Remove stale TODO comment.
## Impact
No runtime behavior change. Pure type-system fix: downstream code in
`transform` that consumes `value.choices`, `value.usage`, etc. is now
fully typed and will surface compile-time errors on schema drift.
Contributor
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
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.
Issue for this PR
Closes #21833
Type of change
What does this PR do?
There was a TODO in the Copilot chat streaming transform saying type safety was lost on the
Chunktype. The root cause is thatthis.chunkSchemais az.unionwhere the second arm is a generic error schema. TypeScript can't inferz.infer<typeof this.chunkSchema>correctly through that union, sochunk.valueends up untyped on the success path.The fix extracts the base chunk object schema into a module-level
const chunkBaseSchemainstead of having it inlined inside the class method. Then, after the two error guards (!chunk.successand"error" in chunk.value) have already narrowed the path, it castschunk.value as z.infer<typeof chunkBaseSchema>. This is the same pattern used in the upstreamvercel/airepo for the same problem.How did you verify your code works?
Ran
bun typecheckfrompackages/opencodeand got no errors. The TODO comment is gone and the success path now has a concrete type instead ofany.Screenshots / recordings
N/A, no UI changes.
Checklist