fix(providers): normalize Responses reasoning items onto assistant messages - #74
fix(providers): normalize Responses reasoning items onto assistant messages#74devin-ai-integration[bot] wants to merge 4 commits into
Conversation
…ssages Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Original prompt from Alan
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…tails column Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…t-item extractors Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ning Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
TL;DR
Reasoning items returned on the Responses path are now normalized onto the assistant message, restoring the readable
reasoningstring in parquet results that was dropped when the chat-completions client was removed.What changed?
extractReasoninginsrc/providers/responses-client.ts, besideextractMessageTextandextractCitations: walks a Responses output array and maps everytype: "reasoning"item to normalized fields —reasoning(readable text) andreasoningDetails(reasoning.text/reasoning.summary/reasoning.encrypted, carryingid,format,signaturewhere present).responsesTurnToModelOutputspreads its result onto the assistant message.responseItemsstill carries the raw items for replay; nothing else about the turn conversion changes.responses-client.test.ts, provider-path coverage onresponsesTurnToModelOutput, and an end-to-end case that pipes a provider-shaped turn through the parquet writer.The stored parquet payload is unchanged apart from
reasoningcoming back.reasoningDetailslives on the message only, which is what the chat-completions client did with it — it fed the outbound echo and was never serialized.Precedence rules, deliberately:
reasoningis populated from plaintextreasoning_textparts. When a provider exposes only a summary, the summary becomes the readable text, since that is the whole of the trace it emits.reasoning.encrypteddetail for replay and is not decrypted.reasoning_tokens.Why?
Since generation moved to the Responses path,
responsesTurnToModelOutputbuilt the assistant message from visible text plus tool calls plus raw output items and populated neither reasoning field.ModelMessage.reasoningandModelMessage.reasoningDetailsare still declared, andparquet.tsstill has a branch that emitsreasoning— a declared capability with no producer. Its regression test hand-feedsmessage.reasoning, so it kept passing while the provider path that should fill it had no coverage.The result in production: benchmark parquet rows carry non-zero
reasoning_tokensbut no trace text, for every reasoning model, on every task. Sampled prod result chunks show readable reasoning present in runs through 2026-08-22 and absent from 2026-08-27 onward, which brackets the switch. Traces from those runs are not recoverable from the results bucket; this fix is forward-looking.The plaintext case is real, not hypothetical: OpenRouter's Responses skin emits GLM reasoning as
{"type":"reasoning","content":[{"type":"reasoning_text","text":"..."}]}with noencrypted_content.include: ["reasoning.encrypted_content"]on the request is a multi-turn replay device for opaque-reasoning providers (store: falsemakes a barers_...id unreplayable), not a signal that traces arrive encrypted — so both shapes have to be handled, and both now are.How to test
Expected: all green. The end-to-end case in
parquet.test.ts(serializes reasoning from a provider Responses turn end to end) fails onmainbecause the serialized message has noreasoningkey.Benchmark impact
No score, dataset, solver, or scorer change. Rows for reasoning models regain a
reasoningstring, which increases result-file size for those runs.Reviewer focus
reasoningfield, with the summary reachable only in memory; I chose the fallback because the summary is the only trace those providers expose.reasoning.*convention rather than being raw provider items, so they match what the chat-completions client put inreasoningDetailsand stay replayable.responseItemsare not serialized into the parquetmessagescolumn, and the sample-levelresponse_itemscolumn reads task state that only agentic solvers set, so it stays null for single-turn benchmarks. Neither was ever stored, so neither is a regression.Checklist
Link to Devin session: https://openrouter.devinenterprise.com/sessions/e69ee5580147481d8371df26bd73582a
Open in Devin Desktop: https://openrouter.devinenterprise.com/desktop/session/e69ee5580147481d8371df26bd73582a?variant=devin
Requested by: @alan-carroll