mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-26 17:51:55 +00:00
fix(provider): send Anthropic's dashed native slug through the AI Gateway (#44281)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
611cc73d84
commit
f8b4dd70ac
2 changed files with 19 additions and 2 deletions
|
|
@ -844,7 +844,12 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
|||
// The passthrough wrappers inject a CF_TEMP_TOKEN sentinel that the gateway strips before
|
||||
// dispatch, so upstream billing stays on the gateway (Unified Billing / stored BYOK).
|
||||
if (modelID.startsWith("openai/")) return aigateway(createOpenAI()(modelID.slice("openai/".length)))
|
||||
if (modelID.startsWith("anthropic/")) return aigateway(createAnthropic()(modelID.slice("anthropic/".length)))
|
||||
// models.dev lists Anthropic ids with dotted versions (claude-haiku-4.5); Anthropic's
|
||||
// Messages API expects dashed native slugs (claude-haiku-4-5), so translate before passing.
|
||||
// No native Anthropic slug contains a dot, so the blanket replacement is lossless here -
|
||||
// unlike OpenAI above, whose native ids (e.g. gpt-4.1) keep their dots and must not be touched.
|
||||
if (modelID.startsWith("anthropic/"))
|
||||
return aigateway(createAnthropic()(modelID.slice("anthropic/".length).replaceAll(".", "-")))
|
||||
// Workers AI is the only first-party provider whose upstream is Cloudflare itself, so it is
|
||||
// the only one that should receive the Cloudflare token as its upstream Authorization header.
|
||||
// The Unified API addresses Workers AI both with the explicit "workers-ai/" prefix and as
|
||||
|
|
|
|||
|
|
@ -165,7 +165,8 @@ function extractUpstreamHeaders(body: unknown): Record<string, unknown> | undefi
|
|||
function gatewayModel(apiId: string, gatewayToken = "test") {
|
||||
const aigateway = createAiGateway({ accountId: "test", gateway: "test", apiKey: gatewayToken })
|
||||
if (apiId.startsWith("openai/")) return aigateway(createOpenAI()(apiId.slice("openai/".length)))
|
||||
if (apiId.startsWith("anthropic/")) return aigateway(createAnthropic()(apiId.slice("anthropic/".length)))
|
||||
if (apiId.startsWith("anthropic/"))
|
||||
return aigateway(createAnthropic()(apiId.slice("anthropic/".length).replaceAll(".", "-")))
|
||||
const isWorkersAi = apiId.startsWith("workers-ai/") || apiId.startsWith("@cf/")
|
||||
const unified = createUnified(isWorkersAi ? { apiKey: gatewayToken } : {})
|
||||
return aigateway(unified(apiId))
|
||||
|
|
@ -195,6 +196,17 @@ describe("cf-ai-gateway routing", () => {
|
|||
expect(upstream?.model).toBe("claude-sonnet-4-6")
|
||||
})
|
||||
|
||||
test("anthropic/* with a dotted models.dev id reaches Anthropic as a dashed native slug", async () => {
|
||||
// models.dev ids are dotted (claude-haiku-4.5); Anthropic's Messages API 404s unless the
|
||||
// version is dashed (claude-haiku-4-5). Regression guard for the dotted-id translation.
|
||||
await callThroughGateway("anthropic/claude-haiku-4.5", {})
|
||||
const step = firstStep(captured?.outerBody)
|
||||
expect(step?.provider).toBe("anthropic")
|
||||
expect(step?.endpoint).toBe("v1/messages")
|
||||
const upstream = extractUpstreamQuery(captured?.outerBody)
|
||||
expect(upstream?.model).toBe("claude-haiku-4-5")
|
||||
})
|
||||
|
||||
test("workers-ai models stay on the unified /compat route", async () => {
|
||||
await callThroughGateway("workers-ai/@cf/moonshotai/kimi-k2.6", {})
|
||||
const step = firstStep(captured?.outerBody)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue