-
-
Notifications
You must be signed in to change notification settings - Fork 18
Stream AI reasoning as ephemeral thinking chunks #1729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,6 +19,24 @@ export interface ApiResponse<T> { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type AiStreamChunk = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | { type: 'thinking'; content: string } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | { type: 'thinking_reset' } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | { type: 'thinking_commit' } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | { type: 'text'; content: string }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function tryParseChunk(line: string): AiStreamChunk | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parsed = JSON.parse(line); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (parsed && typeof parsed.type === 'string') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return parsed as AiStreamChunk; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+30
to
+33
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parsed = JSON.parse(line); | |
| if (parsed && typeof parsed.type === 'string') { | |
| return parsed as AiStreamChunk; | |
| } | |
| const parsed: unknown = JSON.parse(line); | |
| if (!parsed || typeof parsed !== 'object') { | |
| return null; | |
| } | |
| const chunk = parsed as { type?: unknown; content?: unknown }; | |
| switch (chunk.type) { | |
| case 'thinking': | |
| return typeof chunk.content === 'string' | |
| ? { type: 'thinking', content: chunk.content } | |
| : null; | |
| case 'thinking_reset': | |
| return { type: 'thinking_reset' }; | |
| case 'thinking_commit': | |
| return { type: 'thinking_commit' }; | |
| case 'text': | |
| return typeof chunk.content === 'string' | |
| ? { type: 'text', content: chunk.content } | |
| : null; | |
| default: | |
| return null; | |
| } |
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In chunkStream(), when done is true you parse the remaining buffer, but you never flush the TextDecoder state. With { stream: true }, multi-byte characters can be buffered internally and will be lost unless you call decoder.decode() once at the end to flush pending bytes before processing the tail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stream is now NDJSON-framed (
JSON.stringify(chunk) + "\n"), but the response headers are still configured astext/event-streaminsetupResponseHeaders(). This content-type mismatch can confuse intermediaries/clients and is not a valid SSE payload format. Please update the headers to reflect NDJSON (e.g.,application/x-ndjson; charset=utf-8) or change the payload to proper SSE framing if SSE is intended.