Skip to content

fix(sse): resolve OpencodeExecutor target format through the provider alias - #11047

Merged
diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.50from
maxmad64bis:fix/opencode-target-format-alias-11045
Aug 21, 2026
Merged

diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.50from
maxmad64bis:fix/opencode-target-format-alias-11045

Conversation

@maxmad64bis

@maxmad64bis maxmad64bis commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

#10874 added targetFormat: "openai-responses" to the Muse Spark entries in the opencode provider registry. That broke every request to those models: HTTP 400, empty message, finish_reason: null.

Turns out PROVIDER_MODELS is keyed by a provider's public alias ("oc" for this one), not its raw registry id ("opencode"). OpencodeExecutor.execute() looks up the target format with the raw id, misses the registry entry, and falls back to "openai" — so buildUrl() keeps pointing at /chat/completions. Meanwhile chatCore's own resolution (which does alias correctly) already switched the request body to the Responses API shape. Result: a Responses-shaped body lands on the Chat Completions URL, and upstream has nothing sane to answer with.

This never showed up before because no model in this registry had ever used targetFormat — the alias bug was just sitting there, never exercised.

Fix: pulled the alias-resolving lookup into its own function (resolveOpencodeTargetFormat), same pattern chatCore/targetFormat.ts already uses, and pointed execute() at it instead of the raw call.

Related Issues

Validation

  • Change type: routing (open-sse/executors/opencode.ts)
  • node --import tsx/esm --test tests/unit/opencode-target-format-alias-11045.test.ts — 4/4
  • Related opencode-executor suites re-run too (noauth-proxy-routing, session-fingerprint-headers, cli-headers-synthesis, proxy-rotation, muse-spark-responses) — 32/32
  • npm run lint clean — the 2 pre-existing no-explicit-any in this file are untouched
  • tsc --noEmit — no new errors
  • Branched from current release/v3.8.50 tip
  • Test written first against the actual bug (raw id → wrong format), confirmed failing, then fixed

⚠️ base-red inherited: #9985

Tests Added Or Updated

tests/unit/opencode-target-format-alias-11045.test.ts:

  • resolveOpencodeTargetFormat resolves the muse-spark entries via the alias
  • Falls back to "openai" for a model with no registry targetFormat
  • Falls back to "openai" for a provider with no alias mapping
  • OpencodeExecutor.buildUrl actually returns /responses once _requestFormat is resolved through the alias (live-bug repro)

Coverage Notes

Only touches open-sse/executors/opencode.ts, covered by the new tests plus the full existing executor suite for this file.

Reviewer Notes

Same pattern other executors already use (github.ts, default.ts hardcode their alias as a literal since it's a fixed 1:1 mapping for those providers). This one resolves it dynamically via PROVIDER_ID_TO_ALIAS instead of hardcoding "oc", so it keeps working if the alias ever changes.

… alias

PROVIDER_MODELS is keyed by a provider's public alias (e.g. "oc"), not
its raw registry id ("opencode") -- OpencodeExecutor.execute() called
getModelTargetFormat() with the raw id, so it silently missed every
registry entry and fell back to "openai" for buildUrl()/buildHeaders(),
while chatCore's own (correctly aliased) request-body translation still
switched to the Responses API shape for targetFormat:"openai-responses"
models. Net effect: a Responses-shaped body got sent to the
/chat/completions URL, and upstream returned a degenerate empty response.

Surfaced live by cheaper-inference#10874, which added the first targetFormat:"openai-responses"
entries to the opencode provider's registry (oc/muse-spark-1.2 and
oc/muse-spark-1.2-contributor-free) -- no prior model in this registry
exercised this code path, so the alias mismatch was never triggered
before. Extracted the alias resolution into resolveOpencodeTargetFormat(),
mirroring chatCore/targetFormat.ts's own resolveChatCoreTargetFormat().
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for the careful diagnosis and the cleanly-typed fix with real regression tests — this is exactly the right area, and the write-up is very clear. One thing to verify before this can land: getModelTargetFormat() already resolves the provider alias internally at open-sse/config/providerModels.ts:175 (const alias = PROVIDER_ID_TO_ALIAS[aliasOrId] || aliasOrId; — added by #9994, merged 2026-08-11, which is in the current base). Running against the base tip, the OLD line getModelTargetFormat(this.provider, input.model) already returns "openai-responses" for opencode/muse-spark-1.2*, and buildUrl() (opencode.ts:367-371) already picks /responses. In other words, the alias wrapper is a no-op today and your regression test passes against pre-existing behavior. So this PR is effectively subsumed by #9994's internal alias. Options: (1) confirm on your side against origin/release/v3.8.50 that the real #11045/#10874 repro was already fixed, and if so close this to avoid a no-op production change (the code and tests are good — could be resubmitted as a hardening/doc-comment-only PR if you want the regression test committed); or (2) if you still observe the 400 in a running instance, keep the fix but show the failing-before/passing-after on the CURRENT base. Nice work either way.

@diegosouzapw
diegosouzapw merged commit ae2de45 into cheaper-inference:release/v3.8.50 Aug 21, 2026
9 of 16 checks passed
@maxmad64bis

Copy link
Copy Markdown
Contributor Author

Verified against upstream/release/v3.8.50: getModelTargetFormat() already resolves the provider alias internally — open-sse/config/providerModels.ts:175 does const alias = PROVIDER_ID_TO_ALIAS[aliasOrId] || aliasOrId; (added by #9994, 2026-08-11, in the current base). So the alias-wrapper added by this PR is a no-op on the base today, and the #11045/#10874 repro is already fixed upstream.

Since the PR is already merged, this is dead weight but harmless — leaving it in place. The regression test it added is genuinely good and could be resubmitted as a doc-only/hardening PR if a real repro ever surfaces. No revert opened unless you'd prefer to drop the no-op from production.

@maxmad64bis
maxmad64bis deleted the fix/opencode-target-format-alias-11045 branch September 24, 2026 21:14
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
… alias (cheaper-inference#11047)

⭐5 — OpencodeExecutor buscava targetFormat com o id bruto do provider em vez do alias público ("oc"), caindo em default openai → corpo Responses no URL chat/completions (400). resolveOpencodeTargetFormat resolve via PROVIDER_ID_TO_ALIAS. TDD 4/4 + 32/32 irmãos, lint/tsc limpos. Fecha cheaper-inference#11046. Base-red cheaper-inference#9985 inherited.
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.

fix(providers): OpenCode Zen models with targetFormat:"openai-responses" get a Responses-shaped body sent to /chat/completions (400, empty message)

2 participants