Skip to content

fix(provider): send Anthropic's dashed native slug through the AI Gateway - #44281

Merged
rekram1-node merged 3 commits into
anomalyco:devfrom
superhighfives:gateway-anthropic-slug
Aug 24, 2026
Merged

fix(provider): send Anthropic's dashed native slug through the AI Gateway#44281
rekram1-node merged 3 commits into
anomalyco:devfrom
superhighfives:gateway-anthropic-slug

Conversation

@superhighfives

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #44280

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Every Anthropic model under cloudflare-ai-gateway fails with Error: model: claude-haiku-4.5, so none of them are usable.

The plugin already routes anthropic/* models through Anthropic's native Messages passthrough (createAnthropic), which forwards the sliced model id to Anthropic's API unchanged. models.dev lists Cloudflare's canonical dotted ids (anthropic/claude-haiku-4.5), but Anthropic's Messages API expects dashed native slugs (claude-haiku-4-5) — so it 404s.

The fix translates dots to dashes when slicing the anthropic/ prefix. OpenAI is unaffected because its native slugs already use dots (gpt-4.1), which is why only Anthropic was broken.

Worth noting: Cloudflare's own catalog-aware REST endpoints (/ai/v1/messages, /ai/run) accept the dotted id and do this exact dot→dash translation internally before calling Anthropic. The native passthrough route this plugin uses forwards the slug raw, so the plugin has to do the same translation. The dotted id in models.dev is correct — it's Cloudflare's canonical id (see https://developers.cloudflare.com/ai/models/anthropic/claude-haiku-4.5/), so this is fixed on the opencode side, not in the catalog. (Context: surfaced while testing anomalyco/models.dev#4922.)

How did you verify your code works?

  • Built the single binary and ran against a live Cloudflare AI Gateway: anthropic/claude-haiku-4.5 went from Error: model: claude-haiku-4.5 to a normal completion; openai/* and workers-ai/@cf/* still work.
  • Added a regression test in cf-ai-gateway-e2e.test.ts asserting a dotted Anthropic id reaches the Anthropic Messages wire as a dashed slug; confirmed it fails without the fix and passes with it.
  • bun test test/provider/ in packages/opencode passes (one unrelated pre-existing flake in header-timeout.test.ts, which also fails on dev). tsgo --noEmit clean.

Screenshots / recordings

N/A (not a UI change).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…eway

The Cloudflare AI Gateway plugin routes anthropic/* models through the native
Anthropic Messages passthrough (createAnthropic), which forwards the model id
to Anthropic's API unchanged. models.dev lists Cloudflare's canonical dotted
ids (anthropic/claude-haiku-4.5), but Anthropic's Messages API expects dashed
native slugs (claude-haiku-4-5), so every Anthropic model 404s with
"model: claude-haiku-4.5".

Translate dots to dashes when slicing the anthropic/ prefix. OpenAI is
unaffected because its native slugs already use dots (gpt-4.1). Cloudflare's
own catalog-aware REST endpoints (/ai/v1/messages, /ai/run) do this same
translation internally; the native passthrough route opencode uses does not,
so the plugin has to.

Verified end-to-end against a live gateway: anthropic/claude-haiku-4.5 goes
from "Error: model: claude-haiku-4.5" to a normal completion, with OpenAI and
Workers AI models unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@superhighfives
superhighfives marked this pull request as ready for review August 23, 2026 00:39
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference, author can ignore or act on any point.

Correct diagnosis and a tidy fix: models.dev lists Anthropic ids dotted (claude-haiku-4.5) while the Messages passthrough forwards them verbatim, so every anthropic/* model under the gateway was doomed to a 404. The new e2e assertion pins both the endpoint (v1/messages) and the translated upstream slug (claude-haiku-4-5) — exactly the regression guard this needs.

Two suggestions:

  1. Extract the shared translation. The .replaceAll(".", "-") logic now exists verbatim in two places: the production loader (packages/opencode/src/provider/provider.ts, ~line 850) and the test harness's gatewayModel (cf-ai-gateway-e2e.test.ts, ~line 168). Mirrored helpers like this drift silently — if the rule ever changes (say, only version-position dots), the tests will keep validating the old behavior. Exporting something like anthropicNativeSlug(id) from the provider module and using it in both spots keeps the test honest.

  2. Document why replaceAll is safe. It works today because no native Anthropic slug contains a dot, so blanket replacement can't corrupt a legitimate id. The code comment explains the dotted→dashed direction well; half a sentence noting the inverse invariant ("no native slug contains '.', so global replacement is lossless here") would preempt someone generalizing this pattern to prefixes where dots are meaningful — e.g., the workers-ai/@cf/moonshotai/kimi-k2.6 family, where dotted ids are apparently accepted as-is on the unified route.

Minor: worth a quick sanity pass over the other first-party branches in custom() to confirm none of them have the same catalog-vs-native mismatch — this one only surfaced because Anthropic is strict about unknown model names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@superhighfives

superhighfives commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Passed this back to the robot and got the below, which added a comment and rationalized the other two. Happy to discuss!


Thanks for the review (re: #44281 (comment)).

2. Documented the safety invariant — done in 70ae461. Added a note that no native Anthropic slug contains a dot, so the blanket replacement is lossless, and contrasted it with the OpenAI branch above (whose native ids like gpt-4.1 keep their dots and must not be touched).

3. Sanity pass over the other custom() branches — checked; no other branch has the mismatch. openai/* is the only other native-passthrough route and it deliberately does not translate, because OpenAI's real slugs legitimately contain dots (gpt-4.1). Everything else rides the unified route, which accepts dotted ids as-is (e.g. kimi-k2.6). The dotted/dashed mismatch is Anthropic-only, which is why the fix is scoped to that branch.

1. Extracting a shared anthropicNativeSlug() — not changing this. The test's gatewayModel is a deliberate independent mirror of the full routing (all three branches), not just the translation line — it reconstructs routing so the test can inspect the wire. The assertion checks the resulting slug against a hardcoded literal (claude-haiku-4-5), so it catches drift whether or not it imports the prod function. Extracting just one of three mirrored branches into a new prod export is inconsistent and widens the module surface for marginal gain, so I'd rather keep the change minimal.

@rekram1-node
rekram1-node merged commit f8b4dd7 into anomalyco:dev Aug 24, 2026
8 checks passed
Anonymouse8882 pushed a commit to Anonymouse8882/opencode that referenced this pull request Aug 27, 2026
…eway (anomalyco#44281)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f8b4dd7)
Archipel pushed a commit to Archipel/opencode that referenced this pull request Aug 29, 2026
…eway (anomalyco#44281)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1056674754 added a commit to 1056674754/opencode that referenced this pull request Aug 31, 2026
Upstream v1.18.22 (47b6b6f, merged at 2686e4c) -> v1.18.23 (ef2880f),
13 commits / 46 files. Upstream superseded the v1.18.22 release commit with
55f9841 (sync release versions for v1.18.22) in the v1.18.23 lineage; merge
base is 2a6be0a (chore: generate), effective input identical to the audited
13-commit range.

Functional changes:
- fix(provider): route non-native Cloudflare AI Gateway providers via the
  REST API (anomalyco#44828) — adopted onto fork provider.ts; the custom cloudflare
  loader region was untouched by the fork snapshot architecture, so the
  change auto-merged. Third-party models (google, xai, deepseek, ...) now
  point an OpenAI-compatible client at the Cloudflare REST endpoint
  (api.cloudflare.com/client/v4/accounts/<id>/ai/v1) bound with
  cf-aig-gateway-id, keeping requests gateway-routed; Workers AI keeps the
  unified compat route with the Cloudflare token as upstream Authorization.
- fix(provider): send Anthropic's dashed native slug through the AI Gateway
  (anomalyco#44281) — anthropic/<id> models translate dotted versions to dashes
  (claude-haiku-4.5 -> claude-haiku-4-5) before the native Messages
  passthrough; OpenAI dotted ids untouched.
- fix(opencode): send parent session header (anomalyco#44752) — x-parent-session-id
  moved to the provider-independent header level so opencode providers also
  send it; session routing headers (x-session-affinity/X-Session-Id) and
  opencode x-opencode-* headers unchanged.
- fix(github): support immutable OIDC subjects (anomalyco#44776) — token exchange
  reads the repository claim via new parseRepositoryClaim
  (packages/function/src/github.ts + 5 tests) instead of parsing the
  subject, so immutable subjects (repo:owner@id/repo@id) resolve;
  exchange failures return 502 with context; github.handler.ts guards
  error reporting behind GitHub client readiness and reports raw text on
  token-exchange failure.
- console: zen inference header lifecycle ($client replacement, keep
  x-opencode-* headers on new inference), requestBody nested-model parsing
  + tests, revert of the first-month-50 coupon delay, legal/prompt-data
  docs; docs(github.mdx) action token configuration.

Conflicts resolved: 29 package.json version bumps (rebranded 1.18.23-sscity),
bun.lock (took upstream, rebranded workspace entries via bun install).
provider.ts and request.ts auto-merged; provider.ts verified to differ from
pre-merge fork HEAD only by the Cloudflare changes — fork snapshot /
small-model-chain / session-pinning / GLM architecture untouched.
packages/function/package.json adopted upstream's new "test" script, wiring
the new github tests into turbo alongside the new @opencode-ai/function#test
task. Tests from both sides kept: provider 105, transform 437, retry 68,
run-process 15, session llm 30 (incl. new parent session header test),
cf-ai-gateway-e2e 13 (incl. dashed-slug and REST-route assertions), function
github 5 (new), console requestBody 6 all pass; typecheck 30/30.
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.

cloudflare-ai-gateway: Anthropic models 404 (dotted id not translated to Anthropic's dashed slug)

3 participants