Skip to content

fix: restore type safety on streaming chunk schema#21831

Open
peda-cos wants to merge 2 commits intoanomalyco:devfrom
peda-cos:dev
Open

fix: restore type safety on streaming chunk schema#21831
peda-cos wants to merge 2 commits intoanomalyco:devfrom
peda-cos:dev

Conversation

@peda-cos
Copy link
Copy Markdown

@peda-cos peda-cos commented Apr 10, 2026

Issue for this PR

Closes #21833

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

There was a TODO in the Copilot chat streaming transform saying type safety was lost on the Chunk type. The root cause is that this.chunkSchema is a z.union where the second arm is a generic error schema. TypeScript can't infer z.infer<typeof this.chunkSchema> correctly through that union, so chunk.value ends up untyped on the success path.

The fix extracts the base chunk object schema into a module-level const chunkBaseSchema instead of having it inlined inside the class method. Then, after the two error guards (!chunk.success and "error" in chunk.value) have already narrowed the path, it casts chunk.value as z.infer<typeof chunkBaseSchema>. This is the same pattern used in the upstream vercel/ai repo for the same problem.

How did you verify your code works?

Ran bun typecheck from packages/opencode and got no errors. The TODO comment is gone and the success path now has a concrete type instead of any.

Screenshots / recordings

N/A, no UI changes.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

## 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.
@github-actions github-actions bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions bot removed needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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.

fix: type safety lost on Chunk in Copilot chat streaming transform

1 participant