fix(providers/openai): accept streaming chunks with both reasoning fields#8715
Merged
lifeizhou-ap merged 2 commits intoApr 22, 2026
Conversation
…parse error
vLLM/SGLang-style OpenAI-compatible servers (e.g. gpt-oss-120b) emit
both `reasoning` and `reasoning_content` in the same chat.completion.chunk
delta. The streaming Delta struct uses `#[serde(alias = "reasoning")]`
on `reasoning_content`, so serde treats the two keys as duplicates and
the whole stream parse fails with:
duplicate field `reasoning_content`
Add three tests:
- non-streaming response with both fields (already works via the manual
`.get().or_else()` fallback in response_to_message)
- streaming chunk with only `reasoning_content` (guardrail)
- streaming chunk with both `reasoning` and `reasoning_content`
(currently fails; the fix in the next commit flips it green)
Signed-off-by: Sam Osborn <samosborn88@gmail.com>
…elds Some OpenAI-compatible servers (vLLM serving gpt-oss, see vllm-project/vllm#27755) emit both `reasoning` and `reasoning_content` in the same chat.completion.chunk delta, usually mirroring each other. The previous `Delta` struct used `#[serde(alias = "reasoning")]` on `reasoning_content`, which made serde treat the two keys as duplicates of the same field and fail the whole stream parse with: duplicate field `reasoning_content` Split `reasoning` and `reasoning_content` into separate optional fields and read through a `reasoning_text()` helper that prefers `reasoning_content` (the DeepSeek/OpenRouter convention), falls back to `reasoning` (vLLM), and skips empty values so empty-delta chunks don't emit empty thinking content. This matches the tolerance already present in the non-streaming `response_to_message` path. Flips the failing test from the previous commit to green. Signed-off-by: Sam Osborn <samosborn88@gmail.com>
3894455 to
d42929e
Compare
lifeizhou-ap
approved these changes
Apr 22, 2026
Collaborator
lifeizhou-ap
left a comment
There was a problem hiding this comment.
Thank you for the contribution!
lifeizhou-ap
added a commit
that referenced
this pull request
Apr 22, 2026
* main: (41 commits) removed the specific code owner for documentation change (#8749) fix(providers): handle missing delta field in streaming chunks (#8700) refactor(providers): extract http_status module and rename handle_status_openai_compat (#8620) fix(providers/openai): accept streaming chunks with both reasoning fields (#8715) feat: associate threads with projects (#8745) upgrade goose sdk and tui to be compatible with the latest agentclientprotocol/sdk package (#8667) feat: extend goose2 context window ux with auto-compaction (#8721) improve goose2 agent management flows (#8737) alexhancock/tui-improvements (#8736) fix: add strict:false to Responses API tools and gpt-5.4 to known models (#8636) persist and reliably apply chat model selection (#8734) merge goose-acp crate into goose (#8726) docs: AGENTS.md section on goose2 desktop backend architecture (#8732) feat: goose2 message bubble + action tray (#8720) consolidate provider ACP methods onto inventory (#8710) ci: declare and enforce MSRV of 1.91.1 (#8670) fix(ui): correct grammar in apps view description (#8668) (#8679) Stop load openai fast model for openapi compatible custom endpoint (#8644) feat(hooks): add Husky git hooks for ui/goose2 (#8577) fix: links in chat could not be opened (#8544) ...
lifeizhou-ap
added a commit
that referenced
this pull request
Apr 23, 2026
* main: (34 commits) fix(goose-server): cache TLS cert to disk to avoid slow startup on first launch (#8174) feat: add Exa AI-powered search tool (#8487) fix: preprompt would show after loading session (#8744) commands to acp+ migration: extensions management (#8733) feat: desktop notification when goose finishes a task (#8647) harden code review skill for async state and default-resolution bugs (#8740) Feature/at agent mention (#8571) fix: removed hardcoded dependency of goose-acp-macro (#8753) perf: split agent setup into staged phases to reduce startup blocking (#8746) Add /skills command (#8600) Replace deprecated Claude ACP package links (#8625) removed the specific code owner for documentation change (#8749) fix(providers): handle missing delta field in streaming chunks (#8700) refactor(providers): extract http_status module and rename handle_status_openai_compat (#8620) fix(providers/openai): accept streaming chunks with both reasoning fields (#8715) feat: associate threads with projects (#8745) upgrade goose sdk and tui to be compatible with the latest agentclientprotocol/sdk package (#8667) feat: extend goose2 context window ux with auto-compaction (#8721) improve goose2 agent management flows (#8737) alexhancock/tui-improvements (#8736) ...
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.
Summary
Some OpenAI-compatible servers (notably vLLM serving
gpt-oss-120b, see vllm-project/vllm#27755) emit bothreasoningandreasoning_contentin the samechat.completion.chunkdelta, usually mirroring each other:{"choices":[{"delta":{"reasoning":"","reasoning_content":""}}]}The streaming
Deltastruct used#[serde(alias = "reasoning")]onreasoning_content, so serde treated the two keys as duplicates of the same field and failed the whole stream parse with:This splits
reasoningandreasoning_contentinto two separateOption<String>fields onDeltaand reads them through a newreasoning_text()helper that prefersreasoning_content(DeepSeek/OpenRouter convention), falls back toreasoning(vLLM), and skips empty values. The non-streamingresponse_to_messagepath already tolerated both fields via a manual.get().or_else()fallback — this brings the streaming path to parity.The two commits are structured for TDD: the first adds tests (one of which fails on that commit with the duplicate-field error); the second is the fix that flips it to green.
Testing
cargo test -p goose --lib providers::formats::openai— all tests pass after the fix; the newtest_streaming_chunk_with_both_reasoning_fieldsfails on the test-only commit as designed.cargo fmtcargo clippy --all-targets -- -D warningsgpt-oss-120bendpoint and confirmed chat + tool-call streaming no longer errors.Related Issues
No open goose issue — discovered while configuring goose against a local vLLM
gpt-oss-120bendpoint. Upstream vLLM context: vllm-project/vllm#27755.