summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorMatt Silverlock <[email protected]>2026-02-03 16:18:28 -0500
committerGitHub <[email protected]>2026-02-03 15:18:28 -0600
commit25bdd77b1d765a9cac5d02ede93c075f25d6ca6f (patch)
treefe7e08b548fecfa260144484c76ff35ca50d84a3 /packages
parent2f12e8ee923ddbdb0050c7ff2f6e85227cdf9b9e (diff)
downloadopencode-25bdd77b1d765a9cac5d02ede93c075f25d6ca6f.tar.gz
opencode-25bdd77b1d765a9cac5d02ede93c075f25d6ca6f.zip
fix(opencode): use official ai-gateway-provider package for Cloudflare AI Gateway (#12014)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/package.json1
-rw-r--r--packages/opencode/src/provider/provider.ts56
2 files changed, 21 insertions, 36 deletions
diff --git a/packages/opencode/package.json b/packages/opencode/package.json
index 08eaae49d..7dd0cbd27 100644
--- a/packages/opencode/package.json
+++ b/packages/opencode/package.json
@@ -92,6 +92,7 @@
"@standard-schema/spec": "1.0.0",
"@zip.js/zip.js": "2.7.62",
"ai": "catalog:",
+ "ai-gateway-provider": "2.3.1",
"bonjour-service": "1.3.0",
"bun-pty": "0.4.4",
"chokidar": "4.0.3",
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index b85835b69..fc90571fe 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -461,52 +461,36 @@ export namespace Provider {
if (!accountId || !gateway) return { autoload: false }
- // Get API token from env or auth prompt
+ // Get API token from env or auth - required for authenticated gateways
const apiToken = await (async () => {
- const envToken = Env.get("CLOUDFLARE_API_TOKEN")
+ const envToken = Env.get("CLOUDFLARE_API_TOKEN") || Env.get("CF_AIG_TOKEN")
if (envToken) return envToken
const auth = await Auth.get(input.id)
if (auth?.type === "api") return auth.key
return undefined
})()
+ if (!apiToken) {
+ throw new Error(
+ "CLOUDFLARE_API_TOKEN (or CF_AIG_TOKEN) is required for Cloudflare AI Gateway. " +
+ "Set it via environment variable or run `opencode auth cloudflare-ai-gateway`.",
+ )
+ }
+
+ // Use official ai-gateway-provider package (v2.x for AI SDK v5 compatibility)
+ const { createAiGateway } = await import("ai-gateway-provider")
+ const { createUnified } = await import("ai-gateway-provider/providers/unified")
+
+ const aigateway = createAiGateway({ accountId, gateway, apiKey: apiToken })
+ const unified = createUnified()
+
return {
autoload: true,
- async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
- return sdk.languageModel(modelID)
- },
- options: {
- baseURL: `https://gateway.ai.cloudflare.com/v1/${accountId}/${gateway}/compat`,
- headers: {
- // Cloudflare AI Gateway uses cf-aig-authorization for authenticated gateways
- // This enables Unified Billing where Cloudflare handles upstream provider auth
- ...(apiToken ? { "cf-aig-authorization": `Bearer ${apiToken}` } : {}),
- "HTTP-Referer": "https://opencode.ai/",
- "X-Title": "opencode",
- },
- // Custom fetch to handle parameter transformation and auth
- fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
- const headers = new Headers(init?.headers)
- // Strip Authorization header - AI Gateway uses cf-aig-authorization instead
- headers.delete("Authorization")
-
- // Transform max_tokens to max_completion_tokens for newer models
- if (init?.body && init.method === "POST") {
- try {
- const body = JSON.parse(init.body as string)
- if (body.max_tokens !== undefined && !body.max_completion_tokens) {
- body.max_completion_tokens = body.max_tokens
- delete body.max_tokens
- init = { ...init, body: JSON.stringify(body) }
- }
- } catch (e) {
- // If body parsing fails, continue with original request
- }
- }
-
- return fetch(input, { ...init, headers })
- },
+ async getModel(_sdk: any, modelID: string, _options?: Record<string, any>) {
+ // Model IDs use Unified API format: provider/model (e.g., "anthropic/claude-sonnet-4-5")
+ return aigateway(unified(modelID))
},
+ options: {},
}
},
cerebras: async () => {