Skip to content

fix(cf-ai-gateway): route provider options through openaiCompatible key (#24432)#25573

Open
NathanDrake2406 wants to merge 5 commits intoanomalyco:devfrom
NathanDrake2406:fix/cf-ai-gateway-provider-options
Open

fix(cf-ai-gateway): route provider options through openaiCompatible key (#24432)#25573
NathanDrake2406 wants to merge 5 commits intoanomalyco:devfrom
NathanDrake2406:fix/cf-ai-gateway-provider-options

Conversation

@NathanDrake2406
Copy link
Copy Markdown

@NathanDrake2406 NathanDrake2406 commented May 3, 2026

Issue for this PR

Closes #24432

Type of change

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

What does this PR do?

reasoningEffort and workflow variant inputs were silently dropped for cf-ai-gateway models routed through ai-gateway-provider. The outgoing request to gateway.ai.cloudflare.com had no reasoning_effort field regardless of config.

Root cause in packages/opencode/src/provider/transform.ts:

  • sdkKey() had no "ai-gateway-provider" case, so it fell back to model.providerID = "cloudflare-ai-gateway". ai-gateway-provider/unified is createOpenAICompatible({ name: "Unified" }), and @ai-sdk/openai-compatible only reads provider options from openai-compatible / openaiCompatible / Unified / unified. Wrong key, never read.
  • variants() had no case either, so variant: xhigh returned {}.

Fix: add the sdkKey case returning "openaiCompatible" (kebab form emits a runtime deprecation warning), and a variants() case dispatching on model.api.id upstream prefix.

First commit is a refactor done so the new case could reuse logic: extracts openaiReasoningEfforts() from the @ai-sdk/openai case, names the unexplained 2025-11-13 / 2025-12-04 date literals, and replaces the gpt-5- substring matcher with an anchored regex so dotted ids like gpt-5.4 are caught.

Anthropic / Google on cf-ai-gateway resolve through bundled SDKs per models.dev and were unaffected.

How did you verify your code works?

test/provider/cf-ai-gateway-e2e.test.ts runs the real ai-gateway-provider + @ai-sdk/openai-compatible chain through a stubbed fetch and asserts reasoning_effort lands in the upstream body. Plus 7 unit tests for the variants / providerOptions / matcher changes.

bun test ... 154 pass, 0 fail

bunx tsc --noEmit clean. Manually repro'd: outgoing body went from no reasoning_effort to reasoning_effort: "xhigh".

Screenshots / recordings

N/A.

Checklist

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

Copilot AI review requested due to automatic review settings May 3, 2026 09:03
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label May 3, 2026
@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label May 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 3, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

…etection

The OpenAI variants block held two undocumented date literals ("2025-11-13",
"2025-12-04") and a matcher that missed dotted gpt-5.x ids. The dates gate
which reasoning_effort tiers a model exposes, mirroring OpenAI's API rollout
schedule, but nothing in the file said so.

Extract OPENAI_NONE_EFFORT_RELEASE_DATE / OPENAI_XHIGH_EFFORT_RELEASE_DATE
with a comment naming what the dates mean, and lift the effort selection into
openaiReasoningEfforts(id, releaseDate). Replace the
"id.includes('gpt-5-') || id === 'gpt-5'" check with an anchored regex that
matches gpt-5, gpt-5-nano, gpt-5.4, openai/gpt-5.5, and rejects gpt-50 / gpt-5o.

The regex change is a real behaviour fix: gpt-5.x models now correctly expose
the "minimal" reasoning_effort tier, which OpenAI's API has accepted on the
gpt-5 family since launch. Previously variant=minimal was a no-op for any
gpt-5.x model.

Adds two regression tests pinning both halves: the dotted id now gets minimal,
and the gpt-50 lookalike still does not.
Variant input (variant: xhigh) and provider options
(provider.cloudflare-ai-gateway.models.<id>.options.reasoningEffort) on
cf-ai-gateway models routed through ai-gateway-provider were silently dropped.
Outgoing requests to gateway.ai.cloudflare.com used the OpenAI-compatible
endpoint without the reasoning_effort field set, so OpenAI upstreams ran at
their default effort regardless of user config.

sdkKey() had no case for "ai-gateway-provider" and fell back to the providerID
"cloudflare-ai-gateway". providerOptions() therefore wrote the payload under
that key, but ai-gateway-provider/unified wraps createOpenAICompatible({ name:
"Unified" }), and @ai-sdk/openai-compatible only reads compatibleOptions from
"openai-compatible" / "openaiCompatible" / "Unified" / "unified". The wrong
key was never read, so reasoningEffort never reached the request body.
variants() likewise had no "ai-gateway-provider" case, so workflow variant
inputs produced an empty options object.

Add the sdkKey case returning "openaiCompatible" (the camelCase form avoids
the SDK's deprecation warning emitted on the kebab form). Add a variants case
that dispatches on the model.api.id upstream prefix and reuses
openaiReasoningEfforts() for openai/* models, falling back to
WIDELY_SUPPORTED_EFFORTS for other upstreams since the Cloudflare /v1/compat
endpoint translates reasoning_effort to provider-native controls.

Adds an end-to-end test that wires the actual ai-gateway-provider +
@ai-sdk/openai-compatible chain through a stubbed fetch and asserts
reasoning_effort lands in the body Cloudflare AI Gateway forwards upstream.
The test also pins the legacy buggy key so a future refactor that resurrects
providerID-keyed providerOptions fails before it reaches users.

Fixes anomalyco#24432.
@NathanDrake2406 NathanDrake2406 force-pushed the fix/cf-ai-gateway-provider-options branch from ad683bd to a980260 Compare May 3, 2026 09:08

This comment was marked as low quality.

@rekram1-node
Copy link
Copy Markdown
Collaborator

/review


// Computes the reasoning_effort tiers an OpenAI (or OpenAI-compatible upstream
// routed through it, e.g. cf-ai-gateway) model exposes. Returns null for models
// with no tunable effort knob (gpt-5-pro). Effort order: weakest → strongest.
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.

Suggestion: same ASCII style note here: this new comment introduces a Unicode arrow. Consider weakest to strongest or weakest -> strongest.

import { createUnified } from "ai-gateway-provider/providers/unified"
import { ProviderTransform } from "@/provider/transform"

type Captured = { url: string; outerBody: any }
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.

Suggestion: this new test file introduces several anys (the captured body, fetch parameters, mock model, and providerOptions). The style guide asks us to avoid any when a narrower type is practical; a small shape for the captured gateway body plus Parameters<typeof fetch> / unknown for parsed JSON would keep the regression test type-safe without much extra code.

// chain that provider.ts:811 builds at runtime, with only the network boundary
// stubbed. Asserts that `reasoning_effort` (and other provider options the
// transform emits) actually land in the body Cloudflare AI Gateway forwards
// upstream — which is the only place the bug was observable.
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.

Suggestion: the style guide says to default to ASCII when editing or creating files. This new file introduces an em dash here and Unicode arrows below; consider ASCII - / -> unless the Unicode punctuation is intentional.

NathanDrake2406 and others added 3 commits May 4, 2026 13:09
…fforts comment

A reviewer comment on PR anomalyco#25573 flagged that the new comment introduced a unicode arrow inconsistent with the repo's ASCII-default style for code comments. The arrow shows up as a glyph that does not survive every editor or terminal cleanly and breaks grep on plain ASCII.

Reword "weakest -> strongest" using ASCII so the comment matches the surrounding style guidelines without changing its meaning.
…comments

Reviewer feedback on PR anomalyco#25573 flagged two issues in the new regression test: the file used em dashes and unicode arrows in comments against the repo's ASCII-default style, and several `any` annotations (captured body, fetch parameters, mock model, providerOptions) bypassed the type system in a file whose entire purpose is catching upstream contract drift.

The wide `any` annotations meant a future change to `Provider.Model` or to the AI SDK's `providerOptions` shape would silently typecheck instead of surfacing here, which defeats the regression test. The comments used unicode purely cosmetically.

Replace `any` with the actual types the runtime uses: `Provider.Model` for the mock factory (constructed via `ModelID.make` / `ProviderID.make` to satisfy the branded ID schema), `Record<string, Record<string, JSONValue>>` for providerOptions to match what `generateText` accepts, `Parameters<typeof fetch>` for the stub's signature, and a typed `isRecord` guard to narrow parsed JSON without an `any` cast. Preserve Bun's `preconnect` method on the stub via `Object.assign` so the assignment satisfies `typeof fetch` honestly. Convert the unicode arrows and em dashes in the file's comments to ASCII.

All 283 provider tests still pass and `bunx tsc --noEmit` is clean.
@NathanDrake2406
Copy link
Copy Markdown
Author

/review

Has anyone ever told you that you look like a mix of Arnold and Andre the Giant?

@rekram1-node
Copy link
Copy Markdown
Collaborator

lol! sometimes the former but never andre hahahaaa

@rekram1-node
Copy link
Copy Markdown
Collaborator

We will merge this in today, thx for fix

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.

cf-ai-gateway silently drops reasoningEffort, variants, and other provider options (sdkKey/variants missing ai-gateway-provider case)

3 participants