fix(inference): tolerate both reasoning and reasoning_content keys (#3547)#3552
Conversation
…inyhumansai#3547) Custom OpenAI-compatible providers (some OpenRouter / vLLM-SGLang proxies) emit both `reasoning` and `reasoning_content` in the same message object. The serde `alias = "reasoning"` mapped both wire keys onto one field slot, so the derived visitor failed the whole response with `duplicate field `reasoning_content`` and dropped the model's entire reply. Replace the alias with a manual Deserialize on ResponseMessage and StreamDelta that reads the two names as distinct fields and folds them into the single canonical `reasoning_content` (canonical wins when both are present). Accepts any combination; reasoning-only providers keep working.
…nyhumansai#3547) Cover the TAURI-RUST-A5N failure on both the chat and stream-delta paths: a message carrying both `reasoning` and `reasoning_content` must parse without a duplicate-field error, and the canonical field wins.
📝 WalkthroughWalkthroughThis PR fixes a deserialization failure where OpenAI-compatible providers emit both ChangesOpenAI-compatible dual reasoning field deserialization
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
thanks for this... |
Summary
reasoningandreasoning_contentin the same message no longer fail to parse.alias = "reasoning"onResponseMessageandStreamDeltawith a manualDeserializethat folds the two wire keys into the single canonical field.Problem
ResponseMessageandStreamDeltainsrc/openhuman/inference/provider/compatible_types.rsdeclared:serde's
aliasmaps bothreasoningandreasoning_contentonto the same field slot. When a provider (some OpenRouter / vLLM-SGLang proxies) sends both keys in one object, the derived visitor sets the slot on the first key and returnsduplicate field \reasoning_content`on the second — failing the whole response parse and surfacing ascustom response parse error: duplicate field `reasoning_content``. The user's turn errors out with no reply.Regression from the alias added in #3124; before that an extra
reasoningkey was an unknown field and silently ignored.Solution
impl Deserializeon both structs backed by a private shadow struct that readsreasoningandreasoning_contentas distinct optional fields.reasoning_content.or(reasoning)— canonical wins when both are present, alias-only still folds in, neither staysNone.reasoning_contentfield);SerializeonResponseMessageis untouched.Submission Checklist
Deserializeimpls, both exercised by the added tests.Closes #NNNin the## RelatedsectionImpact
Related
reasoning_contentwhen provider emits bothreasoningandreasoning_content#3547AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:check— no frontend changespnpm typecheck— no frontend changescargo test --lib openhuman::inference::provider::compatible(159 passed);cargo test --lib both_present(new regression tests pass)cargo fmt --checkclean;cargo clippy --libclean on changed filesapp/src-taurinot changedValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
reasoning) and canonical-only (reasoning_content) inputs behave exactly as under fix(inference): acceptreasoningfield alias for thinking-mode providers #3124.Duplicate / Superseded PR Handling
Summary by CodeRabbit
Bug Fixes
Tests