Skip to content

refactor(providers): drop the chat-completions client - #55

Merged
abhinav-pola merged 5 commits into
mainfrom
devin/1787683573-responses-only-client
Aug 26, 2026
Merged

refactor(providers): drop the chat-completions client#55
abhinav-pola merged 5 commits into
mainfrom
devin/1787683573-responses-only-client

Conversation

@abhinav-pola

@abhinav-pola abhinav-pola commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

TL;DR

The harness now speaks one wire protocol, /v1/responses, so benchmark requests carry the ephemeral cache_control breakpoint the Responses client already sets, and the chat-shaped naming that outlived the chat transport is gone.

What changed?

  • Removed the chat-completions transport. makeOpenRouterModelLayer keeps its name and public export path, but is now an adapter over ResponsesModel, so every solver reaches OpenRouter on /v1/responses with stream: true.
  • Added src/providers/messages-to-responses.ts, a pure mapper with no I/O, turning the harness's message history and chat-format tool definitions into Responses input items and mapping a ResponsesTurn back to ModelOutput. Tool-call identity is preserved by keying on call_id rather than the Responses item id.
  • Added optional responseItems to the harness message type. Assistant items, including encrypted reasoning, replay verbatim on later turns instead of being flattened back into chat fields.
  • Ported both tau user simulators to ResponsesModelService, mapping their histories through messagesToResponses so tool results go out as function_call_output. Airline's fallback, retry, and auxiliary usage behavior is unchanged.
  • Fixed the vision path: the SDK's outbound ResponsesRequest schema requires detail on an input_image item, so the mapper now defaults it to "auto" when a content part doesn't specify one. Omitting it rejected every multimodal request client-side.
  • Renamed the chat-shaped seams left behind by the transport removal: ChatMessage to ModelMessage, chatMessagesToResponses to messagesToResponses, defineChatBenchmark to defineSingleTurnBenchmark, chatMessageToPojo to messageToPojo, plus the corresponding files. Parquet column keys and the pojo projection are untouched, so result files keep their existing shape.

Why?

The benchmark-always-opus arm of tau3-bench-banking was getting zero cached prompt tokens: 674 requests and 19,063,190 prompt tokens with native_tokens_cached at 0, about $103 burned in six hours. The harness called Switchyard on /chat/completions, and that chat-to-Responses translation dropped the caller's top-level cache_control, while the same Opus endpoint caches at roughly 56% on Responses traffic generally. Routing the harness onto Responses keeps the breakpoint on the wire end to end rather than patching one route's config.

How to test

bun run bench -- --benchmark gpqa_diamond --model openai/gpt-4o-mini --limit 1 --concurrency 1
bun run bench -- --benchmark mmmu_pro_vision --model openai/gpt-4o-mini --limit 1 --concurrency 1
bun run bench -- --benchmark tau3_bench_banking --model anthropic/claude-opus-4.8 --limit 1 --concurrency 1

Each run should reach POST /api/v1/responses with stream: true and cache_control: {"type": "ephemeral"} in the body, and the vision run should send detail on every image part.

src/providers/responses-wire.test.ts validates the mapper's output against the SDK's outbound ResponsesRequest schema through the same conversion production uses, covering text, vision with and without explicit detail, video, and a function-call cycle. That is the check the earlier source-text grep test failed to provide.

Benchmark impact

Every previously chat-shaped benchmark changes wire shape, so its numbers are not strictly comparable to past runs. Those requests now stream and carry the default ephemeral cache_control. Chat-shaped reasoning_details are no longer populated, since reasoning replays as Responses items instead. Affected: gpqa, mmlu_pro, mmmu_pro_vision, ifstruct, vgi_bench, tau_bench_verified_airline, tau3_bench_banking. Datasets, solvers, and scorers are otherwise unchanged.

Reviewer focus

  • messages-to-responses.ts: whether the assistant branch's precedence of verbatim responseItems over synthesized content is right for multi-turn replay.
  • Both user simulators: the tool-result path now depends on call_id matching across the mapped history.
  • Whether defaulting image detail to "auto" is acceptable, given the chat path previously omitted it and let the API default.

Checklist

  • Tests cover changed behavior
  • Public API or configuration changes are backward compatible, or the break is documented
  • Benchmark changes document dataset provenance and licensing
  • No credentials, private results, or restricted dataset contents are included
  • Documentation is updated where needed

Link to Devin session: https://openrouter.devinenterprise.com/sessions/f34bd5c90a0742829720956f5bb07bbe
Requested by: @abhinav-pola


Open in Devin Review

devin-ai-integration Bot and others added 2 commits August 25, 2026 18:51
Co-Authored-By: Abhinav Pola <abhinav.pola@openrouter.ai>
Co-Authored-By: Abhinav Pola <abhinav.pola@openrouter.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor
Original prompt from Abhinav

SYSTEM:
<latest_message>
Abhinav Pola (U090K0G7JF3) [ts=1787680253.843829]: @Devin which model
</latest_message>

=== BEGIN THREAD HISTORY (in #alerts-benchmarking) ===
Datadog (U0672C4DEV6) [ts=1787680096.847399]:

Triggered: [Bench] Benchmark run is getting no prompt cache tokens

Abhinav Pola (U090K0G7JF3) [ts=1787680253.843829]: @Devin which model
=== END THREAD HISTORY ===
Channel ID: C0AL3LVS9JL
Thread URL: https://openrouter.slack.com/archives/C0AL3LVS9JL/p1787680096847399?thread_ts=1787680096.847399&amp;cid=C0AL3LVS9JL

The <latest_message> is the message that you should use to guide your goals + task for this session, and you should use the rest of the slack thread as context.
A [ts=...] marker on a Slack message is that message's timestamp. To act on a specific message with the slack tool (e.g. adding an emoji reaction via the reaction command), pass that value as timestamp along with the Channel ID — no extra lookup call is needed.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

devin-ai-integration Bot and others added 3 commits August 25, 2026 20:38
Co-Authored-By: Abhinav Pola <abhinav.pola@openrouter.ai>
Co-Authored-By: Abhinav Pola <abhinav.pola@openrouter.ai>
Co-Authored-By: Abhinav Pola <abhinav.pola@openrouter.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

All six affected benchmarks complete against real OpenRouter at 3f771a4, and every request goes to /api/v1/responses carrying the ephemeral cache breakpoint.

Each ran in CLI mode with one sample and no inference flags, through a logging forwarding proxy so request bodies could be read off the wire. 19 requests total, all on /api/v1/responses, all with stream: true and cache_control: {"type": "ephemeral"}, none on a chat-completions path.

benchmark model result
gpqa_diamond openai/gpt-4o-mini completed, 1/1
mmlu_pro openai/gpt-4o-mini completed, 1/1
mmmu_pro_vision openai/gpt-4o-mini completed, image sent and billed at 25.7k input tokens, model answered A vs target B so 0/1
ifstruct openai/gpt-4o-mini completed, 1/1, schema match 100%
tau_bench_verified_airline openai/gpt-4o-mini completed, 1/1, 11 steps, USER_STOP
tau3_bench_banking anthropic/claude-opus-4.8 completed, 1/1, 8 steps, db + action + communicate all 1
Proof

Observed — vision request body, showing detail present on the image part, which is what the SDK's outbound schema requires and what the earlier mapper omitted:

{"path":"/api/v1/responses","model":"openai/gpt-4o-mini","stream":true,
 "cache_control":{"type":"ephemeral"},
 "input":[{"type":"message","role":"system","content":"You are a helpful assistant."},
          {"type":"message","role":"user","content":[
            {"type":"input_text","text":"Answer the following multiple choice question..."},
            {"type":"input_image","detail":"auto","image_url":"https://datasets-server.huggingface.co/cached-assets/MMMU/MMMU_Pro/..."}]}]}

Observed — tau3 banking, third Opus solver turn, showing chat-format tool definitions converted and the prior turn's call and result replayed as Responses items with matching call_id:

{"path":"/api/v1/responses","model":"anthropic/claude-opus-4.8","stream":true,
 "cache_control":{"type":"ephemeral"},"tool_count":16,
 "input_item_types":["message","message","message","message","function_call","function_call_output"],
 "function_call":{"type":"function_call","name":"KB_search","call_id":"toolu_014frKwSLWmj2S2SHZsz4vFo",
                  "arguments":"{\"query\": \"personal credit card cash back rewards no annual fee\"}"},
 "function_call_output":{"type":"function_call_output","call_id":"toolu_014frKwSLWmj2S2SHZsz4vFo",
                         "output":"1. Business Gold Rewards Card: Premium Business Credit Card Overview..."}}

Observed — airline produced 9 requests alternating solver (14 tools) and user simulator (0 tools), input items growing 2, 3, 4, 5, 7, 9, 11, with call and output items from turn 5 on. Banking produced 3 Opus solver calls and 3 openai/gpt-5.4-mini simulator calls, the latter replaying reasoning items. Both simulator ports work end to end.

Boundary — one sample per benchmark, so the scores are anecdotal, and the vision 0/1 is a wrong model answer rather than a harness error. This establishes that the harness sends the breakpoint, not that OpenRouter registered a cache hit, since the runs were cold and single-sample. The other Responses-backed benchmarks (terminal_bench, vgi_bench, search_*, swe_atlas, deep_swe, draco, wandr) were untouched by this change and were not rerun.

@abhinav-pola
abhinav-pola merged commit 76ceb1a into main Aug 26, 2026
5 checks passed
@abhinav-pola
abhinav-pola deleted the devin/1787683573-responses-only-client branch August 26, 2026 16:07
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Post-merge re-verification on main (76ceb1a) — all 6 benchmarks completed

Re-ran the same CLI sweep against merged main (which also contains #53 and #56) with the primary OpenRouter key, one sample each, --epochs 1 --concurrency 1, no inference flags, through a logging proxy. 25 requests, all to /api/v1/responses; every one carried stream: true and cache_control: {"type":"ephemeral"}; zero chat-completions requests; no errors.

benchmark model result
gpqa_diamond openai/gpt-4o-mini completed, accuracy 1.0
mmlu_pro openai/gpt-4o-mini completed, accuracy 1.0
mmmu_pro_vision openai/gpt-4o-mini completed (image sent, 25.7k input tokens); answered A vs target B → 0/1
ifstruct openai/gpt-4o-mini completed, accuracy 1.0, schema match 100%
tau_bench_verified_airline openai/gpt-4o-mini completed, accuracy 0.0 (db_match=false) — model variance, see below
tau3_bench_banking anthropic/claude-opus-4.8 completed, accuracy 1.0, db+action+communicate all 1
Wire evidence — vision detail and Opus tool-calling
{"path":"/api/v1/responses","model":"openai/gpt-4o-mini","stream":true,"cache_control":{"type":"ephemeral"}}
{"type":"input_image","detail":"auto","image_url":"https://datasets-server.huggingface.co/cached-assets/MMMU/MMMU_Pro/..."}
{"path":"/api/v1/responses","model":"anthropic/claude-opus-4.8","stream":true,
 "cache_control":{"type":"ephemeral"},"tool_count":16,
 "item_types":["message","message","message","message","function_call","function_call_output"],
 "fc":{"name":"KB_search","call_id":"toolu_01UCMca9Qmov96yr8soe3AqA",
       "arguments":"{\"query\": \"personal credit card cash back rewards annual fee\"}"},
 "fco":{"type":"function_call_output","call_id":"toolu_01UCMca9Qmov96yr8soe3AqA",
        "output":"1. Business Gold Rewards Card: Premium Business Credit Card Overview..."}}
Why the airline score differs from the pre-merge run (not a regression)

The airline sample is the "agent must refuse a disallowed cancellation" task. In this run gpt-4o-mini actually called cancel_reservation, so db_match=false; in the earlier passing run it only called get_reservation_details + transfer_to_human_agents. A retry on main failed the same way, and running the pre-merge commit 3f771a4 as a control today also produced db_match=false, communicate_met=true, termination=USER_STOP. git diff 3f771a4 76ceb1a is 2 added package.json export lines and nothing else, and #53 was already an ancestor of the branch tip. So this is single-sample model nondeterminism on an adversarial task, not code. The request shapes were correct in all attempts.

Caveats

One sample per benchmark, so scores are anecdotal (the airline flake demonstrates this). Verified the harness sends cache_control; did not verify OpenRouter registered a cache hit. terminal_bench, vgi_bench, search_*, swe_atlas, deep_swe, draco, and wandr were not exercised.

Written by Devin

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.

1 participant