From 7c410a0bd57e2766837d9b0cf8415de962a32a36 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Wed, 14 Oct 2026 15:32:31 +0200 Subject: [PATCH 01/13] [Agents] Document createAI catalog ids, dynamic routes, provider-native model objects, and stream resume --- ...10-14-models-catalog-ids-stream-resume.mdx | 52 ++++ .../docs/agents/harnesses/opencode.mdx | 26 +- src/content/docs/agents/models/ai-sdk.mdx | 222 ++++++++++++++---- src/content/docs/agents/models/index.mdx | 32 ++- src/content/docs/agents/models/pi-ai.mdx | 61 +++-- 5 files changed, 309 insertions(+), 84 deletions(-) create mode 100644 src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx diff --git a/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx b/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx new file mode 100644 index 00000000000..e06e725b9ce --- /dev/null +++ b/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx @@ -0,0 +1,52 @@ +--- +title: Agents SDK model providers take AI Gateway catalog ids and resume dropped streams +description: createAI in the Agents SDK now calls AI Gateway catalog models and dynamic routes by id, sends vendor model objects to provider-native endpoints, and resumes streams after a dropped connection. +products: + - agents + - workers +date: 2026-10-14 +--- + +import { TypeScriptExample } from "~/components"; + +The [Agents SDK model providers](/agents/models/) can now call any model in the [AI Gateway catalog](/ai/models/) by its id, with no vendor package and no vendor key in your Worker. Streams from those models resume on their own after a dropped connection. + + + +```ts +import { createAI } from "agents/models/ai-sdk"; +import { streamText } from "ai"; + +export default { + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI, id: "my-gateway" }); + + const result = streamText({ + model: ai("anthropic/claude-opus-4.8", { + fallback: ["dynamic/support"], + }), + prompt: "Write a haiku about Durable Objects", + }); + + return result.toTextStreamResponse(); + }, +}; +``` + + + +This release changes `createAI` in `agents/models/ai-sdk`, `agents/models/pi-ai`, and `agents/models/opencode`: + +- **Catalog ids.** `ai("anthropic/claude-opus-4.8")` calls a catalog model. Use the catalog's spelling of the id, not the vendor's. +- **Dynamic routes.** `ai("dynamic/support")` calls an [AI Gateway dynamic route](/ai-gateway/features/dynamic-routing/) by name. +- **Model objects on provider-native endpoints.** A vendor's own model object, such as `ai(anthropic("claude-opus-4-8"))`, now goes to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) instead of the deprecated [Universal Endpoint](/ai-gateway/usage/universal/). File uploads work. +- **Pi AI catalog models on `env.AI.run()`.** Pi AI sends Anthropic and OpenAI catalog models in their native Messages and Responses formats. +- **OpenCode.** The `agents/models/opencode` provider takes catalog ids and dynamic routes, and is now named "Cloudflare" in OpenCode. +- **New options.** `byokAlias` picks a stored key by alias. `zdr` turns Zero Data Retention on or off for a model object. `vendorKey: "request"` sends a key you hold with an AI SDK model object. +- **Stream resume.** If the connection drops mid-stream, the provider replays the run from the last complete event. The stream continues with nothing lost or repeated. + +Every request still goes through the `AI` binding. Workers AI ids, catalog ids, and dynamic routes run on `env.AI.run()`. AI Gateway picks a provider for a catalog model and bills it through [Unified Billing](/ai-gateway/features/unified-billing/) or a [key stored on the gateway](/ai-gateway/configuration/bring-your-own-keys/). A model object goes through the binding's `fetch` to the vendor's provider-native endpoint. AI Gateway keeps the events of each streamed run, which is what lets the provider resume it. Workers AI streams and model object streams do not resume. + +`createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` and `ai-gateway-provider`, which are now deprecated. + +For more information, refer to the [AI SDK provider](/agents/models/ai-sdk/), the [Pi AI provider](/agents/models/pi-ai/), and the [OpenCode harness](/agents/harnesses/opencode/). diff --git a/src/content/docs/agents/harnesses/opencode.mdx b/src/content/docs/agents/harnesses/opencode.mdx index 06bd00bd040..70e4a22257a 100644 --- a/src/content/docs/agents/harnesses/opencode.mdx +++ b/src/content/docs/agents/harnesses/opencode.mdx @@ -1,6 +1,6 @@ --- title: OpenCode -description: Run OpenCode 2.0 in a plain Durable Object with the Agents SDK, Workers AI models, bundled plugins, and durable sessions. +description: Run OpenCode 2.0 in a plain Durable Object with the Agents SDK, Workers AI and AI Gateway models, bundled plugins, and durable sessions. pcx_content_type: overview sidebar: order: 11 @@ -18,7 +18,7 @@ import { [OpenCode](https://opencode.ai/v2/docs/build/sdk/) is an extensible agent harness. The Agents SDK's `OpenCodeHarness` runs its Workerd SDK inside a plain Durable Object. Refer to [OpenCode's Cloudflare SDK documentation](https://opencode.ai/v2/docs/build/sdk/cloudflare/) for the embedded host API. -OpenCode owns the agent loop, transcript, tools, and session recovery. `OpenCodeHarness` supplies SQLite storage and wakes the object after an interrupted run. The `agents/models/opencode` provider connects OpenCode to Workers AI through your `AI` binding. +OpenCode owns the agent loop, transcript, tools, and session recovery. `OpenCodeHarness` supplies SQLite storage and wakes the object after an interrupted run. The `agents/models/opencode` provider connects OpenCode to Workers AI models, [AI Gateway](/ai-gateway/) catalog models, and dynamic routes through your `AI` binding. sqlite[("SQLite: opencode_ tables")] host <--> plugins["Bundled plugins"] end - host --> ai["Workers AI binding"] + host --> ai["AI binding"] plugins --> mcp["Remote MCP server"] plugins --> workspace["External execution environment"] ``` @@ -52,7 +52,7 @@ flowchart LR | Capability | Support | | ---------------------------------------------- | -------------------------------------------------------- | | Durable sessions and interrupted-turn recovery | OpenCode persists state; Lifecycle jobs wake the object. | -| Workers AI models | Register the `agents/models/opencode` plugin. | +| Workers AI and AI Gateway models | Register the `agents/models/opencode` plugin. | | Custom tools, agents, and skills | Bundle OpenCode plugins with your Worker. | | Remote Model Context Protocol (MCP) servers | Register remote servers through a plugin. | | Live events and durable event replay | Use `session.events()` and `session.log()`. | @@ -65,7 +65,7 @@ Install the Agents SDK and OpenCode packages: Use `agents` version 0.28.1 or later. The OpenCode packages are optional peer dependencies, required by this integration, with a supported range of `^2.0.24`. -Add the Workers AI binding, a SQLite-backed Durable Object, and `nodejs_compat` to your Wrangler configuration: +Add the `AI` binding, a SQLite-backed Durable Object, and `nodejs_compat` to your Wrangler configuration: @@ -172,12 +172,26 @@ export default { -Pass the factory's `storage` to `OpenCodeWorkerd.create()` so OpenCode uses the prefixed database. Register `this.ai.plugin` to make the Workers AI models available. `models: { fetch: false }` skips OpenCode's external model catalog refresh; the provider registers models you name with `this.ai()`. +Pass the factory's `storage` to `OpenCodeWorkerd.create()` so OpenCode uses the prefixed database. Register `this.ai.plugin` to add a provider named "Cloudflare" (id `cloudflare`) to OpenCode. `models: { fetch: false }` skips OpenCode's external model catalog refresh. The provider registers the models you name with `this.ai()`. The Lifecycle manages host startup. The factory runs again when the harness reopens an idle host, so it must support repeated calls. The `"demo"` object name is for this example. In an application, choose an object name based on an authenticated user or conversation. +## Choose a model + +`this.ai()` takes the same model ids as the [AI SDK provider](/agents/models/ai-sdk/), and accepts its model options: + +```ts +this.ai("@cf/moonshotai/kimi-k2.7-code"); // Workers AI +this.ai("anthropic/claude-opus-4.8"); // AI Gateway catalog +this.ai("dynamic/support"); // AI Gateway dynamic route +``` + +A catalog id uses the [model catalog](/ai/models/)'s spelling, such as `anthropic/claude-opus-4.8`. A dynamic route belongs to one gateway, so pass that gateway's `id` to `createAI`. An id that is not one of these three forms throws a `TypeError` on the line that names it. Streams from catalog models and dynamic routes [resume after a dropped connection](/agents/models/ai-sdk/#resume-a-dropped-stream). + +Each call returns OpenCode's model reference, `{ providerID, id }`. Pass it to `defaults.model` or `session.setModel()`. To set how OpenCode budgets a model's context, pass `limit: { context, output }`. The default is 128,000 context tokens and 8,192 output tokens. + ## Send prompts `harness.prompt()` submits to the root session and waits for completion. It returns `status`, optional `text` or `reason`, and the active transcript as `messages`. Check `status`: `"done"` means answered; `"unanswered"` means failed, interrupted, withdrawn, or not found. diff --git a/src/content/docs/agents/models/ai-sdk.mdx b/src/content/docs/agents/models/ai-sdk.mdx index 5f628c0ab15..e74fcdf7236 100644 --- a/src/content/docs/agents/models/ai-sdk.mdx +++ b/src/content/docs/agents/models/ai-sdk.mdx @@ -1,7 +1,7 @@ --- title: AI SDK pcx_content_type: how-to -description: Use createAI from agents/models/ai-sdk to call Workers AI models and route third-party AI SDK models through AI Gateway. +description: Use createAI from agents/models/ai-sdk to call Workers AI models, AI Gateway catalog models, dynamic routes, and vendor AI SDK models through the AI binding. tags: - AI sidebar: @@ -19,6 +19,22 @@ import { `agents/models/ai-sdk` is an [AI SDK](https://ai-sdk.dev/) provider for Workers AI and AI Gateway. `createAI` returns a full `ProviderV4`, so its models work anywhere the AI SDK takes a model: `generateText`, `streamText`, tools, structured output, and [Think](/agents/harnesses/think/). +The provider takes two kinds of model. Both go through the `AI` binding: + +```ts +// A string runs on env.AI.run. +ai("@cf/zai-org/glm-4.7-flash"); // Workers AI +ai("anthropic/claude-opus-4.8"); // AI Gateway catalog +ai("dynamic/support"); // AI Gateway dynamic route + +// A vendor's own model object goes to AI Gateway's provider-native endpoint. +ai(anthropic("claude-opus-4-8")); +``` + +A string needs no vendor package and no vendor key. A model object keeps the vendor's own request builder and parser, so every feature of the vendor's package works. + +`createAI` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Both are deprecated. + :::caution[Beta] This provider is in beta. Its API may change in a minor release. ::: @@ -29,7 +45,7 @@ The provider needs `ai@^7` and `@ai-sdk/provider@^4`. Both are optional peer dep -Add a vendor's AI SDK provider only if you call that vendor's models, for example `@ai-sdk/anthropic` or `@ai-sdk/openai`. +Add a vendor's AI SDK provider only if you pass that vendor's model objects, for example `@ai-sdk/anthropic` or `@ai-sdk/openai`. Catalog ids do not need one. Add the `AI` binding to your Wrangler configuration: @@ -71,11 +87,61 @@ export default { -Any string that is not a `@cf/` id throws a `TypeError` that names the provider package to install. +A string that is not a Workers AI id, a catalog id, or a dynamic route throws a `TypeError` that names the three forms. + +## Call an AI Gateway catalog model + +Pass an `/` id from the [model catalog](/ai/models/). No vendor package or key is needed: + + + +```ts +import { createAI } from "agents/models/ai-sdk"; +import { generateText } from "ai"; + +export default { + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI, id: "my-gateway" }); + + const { text } = await generateText({ + model: ai("anthropic/claude-opus-4.8"), + prompt: "Say hello in three words", + }); + + return Response.json({ text }); + }, +}; +``` + + + +Use the catalog's spelling of the id, not the vendor's. AI Gateway looks the model up by its exact catalog name, so `anthropic/claude-opus-4.8` works and `anthropic/claude-opus-4-8` does not. Other examples are `openai/gpt-5.5` and `cloudflare/auto`. + +The request goes through `env.AI.run()` to AI Gateway. The provider sends it in the OpenAI chat completions format, and AI Gateway converts it to and from the format of the provider that serves the model. The gateway also handles: + +- **Credentials.** [Unified Billing](/ai-gateway/features/unified-billing/) bills the model to your Cloudflare account. A key you [store on the gateway](/ai-gateway/configuration/bring-your-own-keys/) is used instead when one is configured. +- **Providers.** A catalog model can have several providers. AI Gateway picks one and retries across them. +- **Errors.** An unknown model, an unpaid account, or an upstream failure throws a `CloudflareAIError`, the same type as a Workers AI failure. + +Unified Billing requires [authentication](/ai-gateway/configuration/authentication/) on the gateway. Without it, the request fails with error `2049`. + +`reasoningEffort` is sent as `reasoning_effort`. `chatTemplateKwargs` is a Workers AI option, so the provider drops it with a warning. + +## Call a dynamic route -## Call a third-party model +An [AI Gateway dynamic route](/ai-gateway/features/dynamic-routing/) is named `dynamic/`. The route you configured on the gateway picks the model, and can apply conditions, percentages, rate and budget limits, and its own fallbacks: -Build the model with the vendor's own AI SDK provider, then pass it to `ai()`. The request goes through AI Gateway, which holds the credential, so the vendor's `apiKey` is a placeholder: +```ts +const ai = createAI({ binding: env.AI, id: "my-gateway" }); + +await generateText({ model: ai("dynamic/support"), prompt: "Hi" }); +``` + +A route belongs to one gateway, so set the `id` of the gateway you created it on. The route answers in the chat completions format whatever model it picks. Like a catalog model, it drops `chatTemplateKwargs` with a warning. A dynamic route is a language model only. The embedding, image, speech, transcription, and reranking methods reject it. + +## Call a vendor's model object + +Build the model with the vendor's own AI SDK provider, then pass it to `ai()`. AI Gateway holds the credential, so the vendor's `apiKey` is a placeholder: @@ -108,7 +174,15 @@ export default { -Use the vendor's own model id. The provider does not rewrite ids, so `claude-opus-4.8` reaches Anthropic as written and Anthropic rejects it. +The vendor's package builds the request and parses the response. The provider replaces the model's `fetch`, and sends the same request, with the same method, headers, and body, through the binding to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) for that vendor: + +```txt +https://workers-binding.ai/ai-gateway/gateways/{gateway}/{provider}/{endpoint} +``` + +The provider takes the gateway provider from the request host. For example, `api.anthropic.com` is `anthropic` and `api.openai.com` is `openai`. A host that AI Gateway has no provider for throws a `TypeError`. Gateway options travel as `cf-aig-*` headers. Multipart bodies, such as file uploads for transcription, work. + +Use the vendor's own model id, such as `claude-opus-4-8`. The vendor's package reads model capabilities from it. Providers on [Unified Billing](/ai-gateway/features/unified-billing/) answer with no key of yours. For any other provider, [store a key on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Until you do, the vendor's own error comes back unchanged. @@ -120,9 +194,33 @@ wrapLanguageModel({ model: ai(anthropic("claude-opus-4-8")), middleware }); A model that is already wrapped in middleware hides the settings the provider needs, so `ai()` rejects it. +### Use your own vendor key + +By default, the provider removes the vendor's credential header (`authorization`, `x-api-key`, `x-goog-api-key`, or `api-key`) so AI Gateway supplies the key. To use a key you hold, give it to the vendor's provider and set `vendorKey: "request"` on the model: + +```ts +const anthropic = createAnthropic({ apiKey: env.ANTHROPIC_API_KEY }); +const model = ai(anthropic("claude-opus-4-8"), { vendorKey: "request" }); +``` + +`vendorKey` belongs to the model you set it on. A fallback model does not inherit it, so one chain can mix a model on your key with a model on Unified Billing. + +### Choose a stored key or Zero Data Retention + +`byokAlias` picks a [stored key by alias](/ai-gateway/configuration/bring-your-own-keys/) when the gateway holds several keys for one provider. The provider sends it as `cf-aig-byok-alias` for catalog models and model objects. + +`zdr` turns [Zero Data Retention](/ai-gateway/features/unified-billing/#zero-data-retention-zdr) on (`true`) or off (`false`) for one Unified Billing request, whatever the gateway's own setting. The provider sends it as `cf-aig-zdr`. It applies to model objects only, because catalog models and dynamic routes do not read it. + +```ts +const model = ai(anthropic("claude-opus-4-8"), { + byokAlias: "production", + zdr: true, +}); +``` + ## Stream, call tools, and return structured output -Both kinds of model work with every AI SDK call: +Every kind of model works with every AI SDK call: @@ -173,6 +271,24 @@ export default { +## Resume a dropped stream + +A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything: + +```ts +const result = streamText({ + model: ai("anthropic/claude-opus-4.8"), + prompt: "Write a haiku about Durable Objects", +}); +// If the connection drops, result.textStream keeps going. +``` + +AI Gateway names each streamed run in the `cf-aig-run-id` header and keeps its events. If the connection drops mid-stream, the provider reconnects through the binding and replays the run from the last complete event. Your stream continues with nothing lost or repeated. The run keeps going on the gateway while the connection is down, so the resumed stream continues the same answer. + +The provider gives up and throws a `CloudflareAIError` after five drops in one stream, or once the gateway no longer holds the run. The gateway keeps a run for a few minutes after it ends. Aborting the call never resumes it. + +Workers AI `@cf/` streams and model objects have no run id, so they do not resume. The run id is in `providerMetadata.cloudflare.runId`. + ## Use it in Think Return a model from `getModel()`: @@ -233,19 +349,21 @@ await generateText({ ## Fall back to another model -`fallback` lists models to try in order if the primary model fails before it produces any output. Mix Workers AI ids and model objects: +`fallback` lists models to try in order if the primary model fails before it produces any output. A fallback model can be a Workers AI id, a catalog id, a dynamic route, or a model object: ```ts const model = ai(anthropic("claude-opus-4-8"), { - fallback: [ai(openai.responses("gpt-5-mini")), "@cf/zai-org/glm-4.7-flash"], + fallback: ["openai/gpt-5.5", "dynamic/support", "@cf/zai-org/glm-4.7-flash"], }); ``` +A model object in the list goes to the provider-native endpoint, like `ai(model)`. + A request that the vendor rejects falls back. So does a stream that fails before its first output part. Once a model has produced output, its answer is final, errors included. `providerMetadata.cloudflare.model` names the model that answered. -Each fallback model uses the chain's gateway options. The chain's `id`, `cacheTtl`, and the other gateway options win over a fallback model's own, and `metadata` merges. Headers and Workers AI options a fallback model was built with stay its own. +Each fallback model uses the chain's gateway options. The chain's `id`, `cacheTtl`, and the other gateway options win over a fallback model's own, and `metadata` merges. The `headers`, Workers AI options, and `vendorKey` a fallback model was built with stay its own. -Fallback runs in your Worker today. +Fallback runs in your Worker and moves across models. Within one catalog model, AI Gateway already retries across that model's providers, and a dynamic route runs its own fallback on the gateway. ## Set options @@ -256,7 +374,7 @@ Set gateway options on the provider, on a model, or per call under `providerOpti const ai = createAI({ binding: env.AI, id: "prod", metadata: { app: "demo" } }); // Model -const model = ai(anthropic("claude-opus-4-8"), { cacheTtl: 3600 }); +const model = ai("anthropic/claude-opus-4.8", { cacheTtl: 3600 }); // Call await generateText({ @@ -272,25 +390,28 @@ The provider removes `providerOptions.cloudflare` before the call reaches a vend Gateway options can be given flat or nested: `{ cacheTtl: 60 }` and `{ gateway: { cacheTtl: 60 } }` are the same. -| Option | Type | Description | -| -------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| `id` | `string` | Gateway id. Defaults to `"default"`, which is created the first time you use it. | -| `gateway` | `string \| GatewayOptions` | The gateway id, or the gateway options nested. | -| `skipCache` | `boolean` | Bypass the gateway cache. | -| `cacheTtl` | `number` | Cache lifetime in seconds. | -| `cacheKey` | `string` | Cache key to use instead of the one derived from the request. | -| `collectLog` | `boolean` | Whether the gateway stores the request and response in its log. | +| Option | Type | Description | +| -------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `id` | `string` | Gateway id. Defaults to `"default"`, which is created the first time you use it. | +| `gateway` | `string \| GatewayOptions` | The gateway id, or the gateway options nested. | +| `skipCache` | `boolean` | Bypass the gateway cache. | +| `cacheTtl` | `number` | Cache lifetime in seconds. | +| `cacheKey` | `string` | Cache key to use instead of the one derived from the request. | +| `collectLog` | `boolean` | Whether the gateway stores the request and response in its log. | | `metadata` | `Record` | Values attached to the gateway log entry. | -| `eventId` | `string` | Groups several requests into one gateway event. | -| `requestTimeoutMs` | `number` | Timeout for the upstream request. | -| `retries` | `{ maxAttempts, retryDelayMs, backoff }` | Gateway retry policy for the upstream call. `maxAttempts` is 1 to 5. | -| `fallback` | `(WorkersAIModelId \| LanguageModelV4)[]` | Models to try in order. Model and call only. | -| `headers` | `Record` | Extra request headers, merged last. Model and call only. | -| `sessionAffinity` | `string` | Workers AI only. Sends requests with the same key to one replica for prefix caching. | -| `reasoningEffort` | `"low" \| "medium" \| "high" \| null` | Workers AI only. `null` turns reasoning off on models that allow it. | -| `chatTemplateKwargs` | `Record` | Workers AI only. Sent as `chat_template_kwargs`, for example `{ enable_thinking: false }`. | - -For a vendor's model, ask for reasoning with the AI SDK's `reasoning` call option or the vendor's own `providerOptions`. +| `eventId` | `string` | Groups several requests into one gateway event. | +| `requestTimeoutMs` | `number` | Timeout for the upstream request. | +| `retries` | `{ maxAttempts, retryDelayMs, backoff }` | Gateway retry policy for the upstream call. `maxAttempts` is 1 to 5. | +| `byokAlias` | `string` | A stored BYOK key, by alias. Catalog models and model objects. | +| `zdr` | `boolean` | Zero Data Retention on or off for a Unified Billing request. Model objects only. | +| `vendorKey` | `"gateway" \| "request"` | `"request"` forwards the vendor key on the request. Model objects only, per model. | +| `fallback` | `(CloudflareLanguageModelId \| LanguageModelV4)[]` | Models to try in order. Model and call only. | +| `headers` | `Record` | Extra request headers, merged last. Model and call only. | +| `sessionAffinity` | `string` | Workers AI only. Sends requests with the same key to one replica for prefix caching. | +| `reasoningEffort` | `"low" \| "medium" \| "high" \| null` | Sent as `reasoning_effort`. `null` turns reasoning off on models that allow it. | +| `chatTemplateKwargs` | `Record` | Workers AI only. Sent as `chat_template_kwargs`, for example `{ enable_thinking: false }`. | + +`reasoningEffort` applies to Workers AI models, catalog models, and dynamic routes. When it is not set, the AI SDK's `reasoning` call option maps onto it. For a model object, ask for reasoning with the AI SDK's `reasoning` call option or the vendor's own `providerOptions`. The provider ignores `reasoningEffort` and `chatTemplateKwargs` on a model object and adds a warning. ## Read gateway metadata @@ -313,24 +434,24 @@ providerMetadata?.cloudflare; // } ``` -| Field | Description | -| ------------- | -------------------------------------------------------------- | -| `model` | The model that answered, after any fallback. | -| `gateway` | The gateway id the request went through. | -| `provider` | The gateway provider, for a vendor's model. | -| `logId` | The gateway log entry, from `cf-aig-log-id`. | -| `eventId` | The gateway event, from `cf-aig-event-id`. | -| `requestId` | The gateway request id, from `cf-aig-request-id`. | -| `traceId` | The trace this request belongs to, from `cf-aig-trace-id`. | -| `runId` | From `cf-aig-run-id`, when the gateway sends one. | -| `cacheStatus` | `HIT` or `MISS`, from `cf-aig-cache-status`. | -| `step` | The step of a gateway route that answered, from `cf-aig-step`. | +| Field | Description | +| ------------- | ------------------------------------------------------------------------------- | +| `model` | The model that answered, after any fallback. | +| `gateway` | The gateway id the request went through. | +| `provider` | The gateway provider, for a model object. | +| `logId` | The gateway log entry, from `cf-aig-log-id`. | +| `eventId` | The gateway event, from `cf-aig-event-id`. | +| `requestId` | The gateway request id, from `cf-aig-request-id`. | +| `traceId` | The trace this request belongs to, from `cf-aig-trace-id`. | +| `runId` | The streamed run, from `cf-aig-run-id`, for a catalog model or a dynamic route. | +| `cacheStatus` | `HIT` or `MISS`, from `cf-aig-cache-status`. | +| `step` | The step of a gateway route that answered, from `cf-aig-step`. | Only `model` and `gateway` are always present. Check for the other fields before you use them. ## Handle errors -The provider throws `CloudflareAIError`, which extends the AI SDK's `APICallError`. The AI SDK's retry loop reads its `isRetryable` flag: +The provider throws `CloudflareAIError`, which extends the AI SDK's `APICallError`, for Workers AI failures, AI Gateway failures, failed stream resumes, and a fallback chain where every model failed. The AI SDK's retry loop reads its `isRetryable` flag: ```ts import { CloudflareAIError } from "agents/models/ai-sdk"; @@ -347,7 +468,7 @@ try { `code` is one of `"auth"`, `"rate-limit"`, `"not-found"`, `"bad-request"`, `"provider-error"`, `"gateway-error"`, or `"unknown"`. -For a vendor's model, `CloudflareAIError` covers the gateway's own failures, such as a missing gateway or an unpaid account. The vendor's own errors, such as an unknown model id or a rate limit, come from the vendor's provider in its own error type. +For a model object, `CloudflareAIError` covers the gateway's own failures, such as a missing gateway or an unpaid account. The vendor's own errors, such as an unknown model id or a rate limit, come from the vendor's provider in its own error type. ## Use other modalities @@ -400,7 +521,7 @@ Model-specific settings go under `providerOptions.cloudflare`: An option a model does not take is listed in the result's `warnings` instead of being sent. For example, `flux-1-schnell` takes only a prompt and `steps`. Image generation makes one image per request, so `generateImage` with a larger `n` sends parallel requests. -To route a vendor's own embedding, image, speech, or transcription model through AI Gateway, pass it to `ai.routed(model)`. `ai.routed()` takes the gateway options, `headers`, and `sessionAffinity`. +These methods take Workers AI `@cf/` ids only, including in `fallback`. To route a vendor's own embedding, image, speech, or transcription model through AI Gateway, pass it to `ai.routed(model)`. It goes to the provider-native endpoint, like `ai(model)`. `ai.routed()` takes the gateway options, `headers`, `sessionAffinity`, and `vendorKey`. ## Use a provider registry @@ -411,7 +532,7 @@ import { createProviderRegistry, generateText } from "ai"; const registry = createProviderRegistry({ cloudflare: ai }); await generateText({ - model: registry.languageModel("cloudflare:@cf/zai-org/glm-4.7-flash"), + model: registry.languageModel("cloudflare:anthropic/claude-opus-4.8"), prompt: "Hi", }); @@ -420,17 +541,20 @@ globalThis.AI_SDK_DEFAULT_PROVIDER = createAI({ binding: env.AI }); await generateText({ model: "@cf/zai-org/glm-4.7-flash", prompt: "Hi" }); ``` -A string still has to be a `@cf/` id. Reach a vendor's model with a model object. +A string can be a Workers AI id, a catalog id, or a dynamic route. Reach a vendor's model through its own package with a model object. ## Limitations -- AI Gateway's universal request carries JSON only. A multipart body, such as a file upload sent as `FormData`, throws a `TypeError`. - `@cf/deepgram/nova-3` and `@cf/deepgram/flux` are not supported. They throw a `CloudflareAIError` before any request is sent. Use `@cf/openai/whisper-large-v3-turbo` for transcription. -- `reasoningEffort`, `chatTemplateKwargs`, and `sessionAffinity` apply to Workers AI models only. +- `chatTemplateKwargs` and `sessionAffinity` apply to Workers AI models only. +- `zdr` applies to model objects only. +- Workers AI streams and model object streams do not resume after a dropped connection. ## Related resources - [pi-ai provider](/agents/models/pi-ai/) - [Model catalog](/ai/models/) +- [Dynamic routing](/ai-gateway/features/dynamic-routing/) +- [AI Gateway provider-native endpoints](/ai-gateway/usage/providers/) - [AI Gateway Worker binding methods](/ai-gateway/usage/worker-binding-methods/) - [Unified Billing](/ai-gateway/features/unified-billing/) diff --git a/src/content/docs/agents/models/index.mdx b/src/content/docs/agents/models/index.mdx index d62bfd3ae84..b7c93fa1740 100644 --- a/src/content/docs/agents/models/index.mdx +++ b/src/content/docs/agents/models/index.mdx @@ -1,7 +1,7 @@ --- title: Models pcx_content_type: overview -description: Call Workers AI and third-party models through AI Gateway from the AI SDK or Pi AI with one createAI provider over the AI binding. +description: Call Workers AI, AI Gateway catalog models, dynamic routes, and vendor models from the AI SDK or Pi AI with one createAI provider over the AI binding. tags: - AI sidebar: @@ -22,7 +22,7 @@ The Agents SDK includes model providers for [Workers AI](/workers-ai/) and [AI G | `agents/models/ai-sdk` | [AI SDK](https://ai-sdk.dev/) v7 | `generateText`, `streamText`, Think, and any AI SDK consumer | | `agents/models/pi-ai` | [Pi AI](https://github.com/earendil-works/pi/tree/main/packages/ai) | The [Pi harness](/agents/harnesses/pi/), and Pi AI's `stream` | -Both take the same options, route through the same gateway, and handle Workers AI the same way. +Both take the same options, route through the same gateway, and handle Workers AI the same way. The [OpenCode harness](/agents/harnesses/opencode/) has a third, `agents/models/opencode`, built on the AI SDK provider. :::caution[Beta] The model providers are in beta. Their APIs may change in a minor release. @@ -30,19 +30,31 @@ The model providers are in beta. Their APIs may change in a minor release. ## How a model is chosen -The providers keep one model catalog up to date: Workers AI. Every other vendor's models come from that vendor's own package, and the provider routes them through AI Gateway. +Name a model with a string, or pass a model object built by the vendor's own package: -- **A Workers AI model** is a `@cf/` id, such as `ai("@cf/zai-org/glm-4.7-flash")`. It runs through `env.AI.run()`. A compatibility layer turns each model's response into the standard OpenAI chat completions shape. -- **A third-party model** is a model object built by the vendor's provider, such as `ai(anthropic("claude-opus-4-8"))`. The vendor's code builds the request and parses the response. The provider swaps the transport, so the request goes through AI Gateway with `env.AI.gateway(id).run()`. +```ts +ai("@cf/zai-org/glm-4.7-flash"); // Workers AI +ai("anthropic/claude-opus-4.8"); // AI Gateway catalog +ai("dynamic/support"); // AI Gateway dynamic route +ai(anthropic("claude-opus-4-8")); // the vendor's own model object +``` + +- **A Workers AI model** is a `@cf/` id. It runs through `env.AI.run()`. A compatibility layer turns each model's response into the standard OpenAI chat completions shape. +- **An AI Gateway catalog model** is an `/` id from the [model catalog](/ai/models/), such as `anthropic/claude-opus-4.8` or `openai/gpt-5.5`. It also runs through `env.AI.run()`, which sends it to AI Gateway. The gateway picks a provider and bills the call. Use the catalog's spelling, not the vendor's: `anthropic/claude-opus-4.8`, not `claude-opus-4-8`. +- **A [dynamic route](/ai-gateway/features/dynamic-routing/)** is `dynamic/`. The route you configured on the gateway picks the model. +- **A model object** comes from the vendor's provider package, such as `anthropic("claude-opus-4-8")`. The vendor's code builds the request and parses the response. The provider sends that request unchanged through the binding to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) for that vendor. -The provider does not keep third-party model ids, wire formats, or thinking settings. When a vendor ships a new model, update the vendor's package. +The providers do not use the deprecated [Universal Endpoint](/ai-gateway/usage/universal/). ## What every model gets -- **No API tokens in your Worker.** Requests go through the `AI` binding. AI Gateway holds third-party credentials, through [Unified Billing](/ai-gateway/features/unified-billing/) or a key you [store on the gateway](/ai-gateway/configuration/bring-your-own-keys/). -- **Gateway options.** Caching, logging, metadata, timeouts, and retries are options on the provider, the model, or the call. The `default` gateway is created the first time you use it. -- **Fallback.** List models to try in order if the first fails before it produces output. Mix Workers AI and third-party models freely. -- **Gateway metadata on every result.** Each response carries the gateway log id, cache status, and the model that actually answered. +- **No API tokens in your Worker.** Requests go through the `AI` binding. AI Gateway holds third-party credentials, through [Unified Billing](/ai-gateway/features/unified-billing/) or a key you [store on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Unified Billing requires [authentication](/ai-gateway/configuration/authentication/) on the gateway. +- **Gateway options.** Caching, logging, metadata, timeouts, retries, and BYOK key aliases are options on the provider, the model, or the call. The `default` gateway is created the first time you use it. +- **Fallback.** List models to try in order if the first fails before it produces output. Mix Workers AI ids, catalog ids, dynamic routes, and model objects. +- **Stream resume.** A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. +- **Gateway metadata on every result.** Each response carries the gateway log id, cache status, run id, and the model that actually answered. + +`createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`), which are deprecated. ## Configure the binding diff --git a/src/content/docs/agents/models/pi-ai.mdx b/src/content/docs/agents/models/pi-ai.mdx index 3a27c2096a5..b7e9b3db835 100644 --- a/src/content/docs/agents/models/pi-ai.mdx +++ b/src/content/docs/agents/models/pi-ai.mdx @@ -1,7 +1,7 @@ --- title: Pi AI pcx_content_type: how-to -description: Use createAI from agents/models/pi-ai to run Workers AI and third-party models through AI Gateway with Pi AI, Pi Durable, and the Pi harness. +description: Use createAI from agents/models/pi-ai to run Workers AI models, AI Gateway catalog models, dynamic routes, and Pi AI model objects through the AI binding with Pi AI, Pi Durable, and the Pi harness. tags: - AI sidebar: @@ -92,30 +92,37 @@ await ai.completeSimple(model, context, { reasoning: "low" }); ## Name a model -There are three ways to name a model: +There are four ways to name a model: ```ts import { createAI } from "agents/models/pi-ai"; import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all"; -const ai = createAI({ binding: env.AI }); +const ai = createAI({ binding: env.AI, id: "my-gateway" }); // 1. A Workers AI model, by its @cf/ id. ai("@cf/zai-org/glm-4.7-flash"); -// 2. A model from pi-ai's Cloudflare AI Gateway catalog, by /. +// 2. An AI Gateway dynamic route, by dynamic/. +ai("dynamic/support"); + +// 3. A model from Pi AI's Cloudflare AI Gateway catalog, by /. ai("anthropic/claude-opus-4.8"); ai("openai/gpt-5.4"); -// 3. Any pi-ai model object, from any pi-ai registry. +// 4. Any Pi AI model object, from any Pi AI registry. ai(getBuiltinModel("anthropic", "claude-opus-4-8")); ``` Any other string throws a `TypeError`. The provider does not guess what a prefix means. -The second and third forms keep Pi AI's model metadata, including `api`, `compat`, `thinkingLevelMap`, cost, context window, and maximum tokens. That metadata decides how each vendor is asked to think. For example, Claude Opus gets `thinking: { type: "adaptive" }` and GPT-5 gets `reasoning.effort`. `ai(model)` does not change the object you pass. +A [dynamic route](/ai-gateway/features/dynamic-routing/) runs on `env.AI.run()` under the gateway you created it on, so set that gateway's `id`. The route picks the model and answers in the chat completions format. It takes the same metadata defaults and overrides as a Workers AI id. + +A catalog id uses the gateway's spelling (`anthropic/claude-opus-4.8`). A model object from a vendor registry uses the vendor's spelling (`claude-opus-4-8`). -Third-party models go through AI Gateway, which holds the credential through [Unified Billing](/ai-gateway/features/unified-billing/) or a [key stored on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Your Worker holds no provider key. +The third and fourth forms keep Pi AI's model metadata, including `api`, `compat`, `thinkingLevelMap`, cost, context window, and maximum tokens. That metadata decides how each vendor is asked to think. For example, Claude Opus gets `thinking: { type: "adaptive" }` and GPT-5 gets `reasoning.effort`. `ai(model)` does not change the object you pass. + +Third-party models go through AI Gateway, which holds the credential through [Unified Billing](/ai-gateway/features/unified-billing/) or a [key stored on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Your Worker holds no provider key. Unified Billing requires [authentication](/ai-gateway/configuration/authentication/) on the gateway. ## Use it with the Pi harness @@ -136,7 +143,7 @@ models.setProvider(ai.provider); await session.setModel(ai("anthropic/claude-opus-4.8")); ``` -A Pi session stores only its model's provider and id, then looks the model up in the `Models` registry. Use the `@cf/` and `/` forms there: both belong to the `cloudflare` provider (`CLOUDFLARE_PROVIDER_ID`) that you registered. A model object keeps its vendor's provider id, which this registry does not know. Options passed to `ai(model, options)` do not reach a session. Set them on `createAI` instead. +A Pi session stores only its model's provider and id, then looks the model up in the `Models` registry. Use the `@cf/`, `dynamic/`, and `/` forms there. They belong to the `cloudflare` provider (`CLOUDFLARE_PROVIDER_ID`) that you registered. A model object keeps its vendor's provider id, which this registry does not know. Options passed to `ai(model, options)` do not reach a session. Set them on `createAI` instead. The provider's model list is every Workers AI model Pi AI knows, plus every `/` in Pi AI's AI Gateway catalog, so `models.getModel("cloudflare", "anthropic/claude-opus-4.8")` resolves. @@ -154,10 +161,13 @@ const model = ai("anthropic/claude-opus-4.8", { eventId: "turn-17", requestTimeoutMs: 30_000, retries: { maxAttempts: 3, backoff: "exponential", retryDelayMs: 500 }, + byokAlias: "production", }); ``` -The options are the same as the [AI SDK provider's options](/agents/models/ai-sdk/#set-options): `id`, `skipCache`, `cacheTtl`, `cacheKey`, `collectLog`, `metadata`, `eventId`, `requestTimeoutMs`, `retries`, `fallback`, `headers`, `sessionAffinity`, `reasoningEffort`, and `chatTemplateKwargs`. +The options are the same as the [AI SDK provider's options](/agents/models/ai-sdk/#set-options): `id`, `skipCache`, `cacheTtl`, `cacheKey`, `collectLog`, `metadata`, `eventId`, `requestTimeoutMs`, `retries`, `byokAlias`, `zdr`, `fallback`, `headers`, `sessionAffinity`, `reasoningEffort`, and `chatTemplateKwargs`. `vendorKey` is AI SDK only. + +`byokAlias` picks a [stored key by alias](/ai-gateway/configuration/bring-your-own-keys/) when the gateway holds several keys for one provider. The provider sends it as `cf-aig-byok-alias` for catalog models and model objects. `zdr` turns [Zero Data Retention](/ai-gateway/features/unified-billing/#zero-data-retention-zdr) on or off for a Unified Billing request. It applies only to model objects outside Pi AI's gateway catalog, because catalog models do not read it. Pi AI's own call options map onto them. `headers` merge last, `metadata` joins the gateway metadata, `sessionId` sets session affinity, and `reasoning` maps through the model's own `thinkingLevelMap`. A call option wins over a model option, which wins over `createAI`. @@ -173,7 +183,7 @@ ai(getBuiltinModel("anthropic", "claude-opus-4-8"), { }); ``` -Fallback models can be Workers AI ids, `/` strings, or model objects. The provider tries them in order. It moves on when a stream ends with an `error` before producing any output. Once a model has produced output, its answer is final. +Fallback models can be Workers AI ids, dynamic routes, `/` strings, or model objects. The provider tries them in order. It moves on when a stream ends with an `error` before producing any output. Once a model has produced output, its answer is final. The message that answers has a `cloudflare-fallback` diagnostic listing the attempts that failed, and `message.model` names the model that answered. @@ -196,20 +206,32 @@ A failed request ends the stream with an `error` event. Its message has a `cloud The gateway's own failures, such as a missing gateway or an unpaid account, become a `CloudflareAIError`. Errors from the vendor come back as the vendor sent them. +## Resume a dropped stream + +A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. + +AI Gateway names each streamed run in the `cf-aig-run-id` header and keeps its events. If the connection drops, the provider reconnects through the binding and replays the run from the last complete event. The stream continues with nothing lost or repeated. + +The provider gives up after five drops in one stream, or once the gateway no longer holds the run. The gateway keeps a run for a few minutes after it ends. Aborting the call never resumes it. Workers AI `@cf/` streams and model objects outside the catalog have no run id, so they do not resume. The run id is on the `cloudflare` diagnostic as `runId`. + ## How requests are routed -The provider routes by the model's Pi AI `api`: +The provider routes every request through the `AI` binding. It has no HTTP transport and needs no API token: + +| Model | `api` | Transport | +| ---------------------------------------------------------- | -------------------- | -------------------------------------------------------------- | +| `@cf/` models and dynamic routes | `cloudflare-ai` | `env.AI.run()`, with Cloudflare's compatibility layer | +| Anthropic models from Pi AI's gateway catalog | `anthropic-messages` | `env.AI.run()` to AI Gateway, in Anthropic Messages format | +| OpenAI models from Pi AI's gateway catalog | `openai-responses` | `env.AI.run()` to AI Gateway, in OpenAI Responses format | +| Any other model object, such as Anthropic, OpenAI, or Groq | its own `api` | The binding's `fetch` to AI Gateway's provider-native endpoint | + +A model from Pi AI's Cloudflare AI Gateway catalog runs on `env.AI.run()` under its catalog id, in the model's native format. AI Gateway converts nothing, so Pi AI's own stream processors read the answer. The gateway picks the provider and bills through Unified Billing or a key stored on the gateway. -| Model | `api` | Transport | -| ----------------------------- | -------------------- | ----------------------------------------------------- | -| `@cf/` models | `cloudflare-ai` | `env.AI.run()`, with Cloudflare's compatibility layer | -| Anthropic, from any registry | `anthropic-messages` | AI Gateway universal request to `v1/messages` | -| OpenAI, from any registry | `openai-responses` | AI Gateway universal request to `v1/responses` | -| Groq, DeepSeek, xAI, and more | `openai-completions` | AI Gateway universal request to `v1/chat/completions` | +Any other model object goes to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) through the binding's `fetch`. The provider takes the gateway provider from the model's `baseUrl` host. Pi AI's own converter builds the vendor's request body, and Pi AI parses the vendor's response stream. -For a third-party model, Pi AI's own converter builds the vendor's request body. The provider sends it to AI Gateway, and Pi AI parses the vendor's response stream. The provider never rewrites the model's `api`, `provider`, or `id`, so thinking signatures and encrypted reasoning items work on the next turn. +Either way, no credential header leaves your Worker. The provider never rewrites the model's `api`, `provider`, or `id`, so thinking signatures and encrypted reasoning items work on the next turn. -These four `api` values are the only ones the provider routes. Models from several Pi AI registries use other values and cannot be routed yet, including `mistral-conversations`, `google-generative-ai`, `google-vertex`, `amazon-bedrock`, `azure-openai-responses`, and `openai-codex`. Such a model's stream ends with an `error` event that names its `api`. +The provider routes four `api` values: `cloudflare-ai`, `anthropic-messages`, `openai-responses`, and `openai-completions`. Models from several Pi AI registries use other values and cannot be routed yet, including `mistral-conversations`, `google-generative-ai`, `google-vertex`, `amazon-bedrock`, `azure-openai-responses`, and `openai-codex`. Such a model's stream ends with an `error` event that names its `api`. So does a model whose `baseUrl` host AI Gateway has no provider for. On the `openai-completions` wire, the provider sends reasoning only as OpenAI's `reasoning_effort`. A model whose `compat.thinkingFormat` is anything else, such as `deepseek`, `qwen`, or `openrouter`, has its reasoning level dropped. A `cloudflare-compat` diagnostic on the message records the drop. Use Pi AI's own provider for that vendor if you need its thinking format. @@ -233,4 +255,5 @@ const result = await ai.generateImages(images, { - [AI SDK provider](/agents/models/ai-sdk/) - [Pi harness](/agents/harnesses/pi/) - [Model catalog](/ai/models/) +- [Dynamic routing](/ai-gateway/features/dynamic-routing/) - [Pi AI](https://github.com/earendil-works/pi/tree/main/packages/ai) From a5f67ac0d59df1407e62c0b47449ec515e9fff01 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Wed, 14 Oct 2026 17:59:58 +0200 Subject: [PATCH 02/13] [Agents] Align models docs and changelog with the merged createAI stack --- ...10-14-models-catalog-ids-stream-resume.mdx | 23 ++++++++----------- .../docs/agents/harnesses/opencode.mdx | 2 +- src/content/docs/agents/models/ai-sdk.mdx | 8 +++---- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx b/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx index e06e725b9ce..dda5fa55ef0 100644 --- a/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx +++ b/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx @@ -1,6 +1,6 @@ --- title: Agents SDK model providers take AI Gateway catalog ids and resume dropped streams -description: createAI in the Agents SDK now calls AI Gateway catalog models and dynamic routes by id, sends vendor model objects to provider-native endpoints, and resumes streams after a dropped connection. +description: createAI in the Agents SDK now runs AI Gateway catalog ids on env.AI.run, sends model objects to provider-native endpoints, and resumes a stream that drops mid-answer. products: - agents - workers @@ -9,7 +9,7 @@ date: 2026-10-14 import { TypeScriptExample } from "~/components"; -The [Agents SDK model providers](/agents/models/) can now call any model in the [AI Gateway catalog](/ai/models/) by its id, with no vendor package and no vendor key in your Worker. Streams from those models resume on their own after a dropped connection. +The [Agents SDK model providers](/agents/models/) now take any model id from the [AI Gateway catalog](/ai/models/), such as `anthropic/claude-opus-4.8`. You do not need a vendor package or a vendor key in your Worker. @@ -22,9 +22,7 @@ export default { const ai = createAI({ binding: env.AI, id: "my-gateway" }); const result = streamText({ - model: ai("anthropic/claude-opus-4.8", { - fallback: ["dynamic/support"], - }), + model: ai("anthropic/claude-opus-4.8"), prompt: "Write a haiku about Durable Objects", }); @@ -35,17 +33,14 @@ export default { -This release changes `createAI` in `agents/models/ai-sdk`, `agents/models/pi-ai`, and `agents/models/opencode`: +`createAI` no longer uses AI Gateway's deprecated [Universal Endpoint](/ai-gateway/usage/universal/). Model ids, catalog ids included, run on `env.AI.run()`. A vendor's own model object, such as `ai(anthropic("claude-opus-4-8"))`, goes through the binding to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) for that vendor. -- **Catalog ids.** `ai("anthropic/claude-opus-4.8")` calls a catalog model. Use the catalog's spelling of the id, not the vendor's. -- **Dynamic routes.** `ai("dynamic/support")` calls an [AI Gateway dynamic route](/ai-gateway/features/dynamic-routing/) by name. -- **Model objects on provider-native endpoints.** A vendor's own model object, such as `ai(anthropic("claude-opus-4-8"))`, now goes to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) instead of the deprecated [Universal Endpoint](/ai-gateway/usage/universal/). File uploads work. -- **Pi AI catalog models on `env.AI.run()`.** Pi AI sends Anthropic and OpenAI catalog models in their native Messages and Responses formats. -- **OpenCode.** The `agents/models/opencode` provider takes catalog ids and dynamic routes, and is now named "Cloudflare" in OpenCode. -- **New options.** `byokAlias` picks a stored key by alias. `zdr` turns Zero Data Retention on or off for a model object. `vendorKey: "request"` sends a key you hold with an AI SDK model object. -- **Stream resume.** If the connection drops mid-stream, the provider replays the run from the last complete event. The stream continues with nothing lost or repeated. +This release also: -Every request still goes through the `AI` binding. Workers AI ids, catalog ids, and dynamic routes run on `env.AI.run()`. AI Gateway picks a provider for a catalog model and bills it through [Unified Billing](/ai-gateway/features/unified-billing/) or a [key stored on the gateway](/ai-gateway/configuration/bring-your-own-keys/). A model object goes through the binding's `fetch` to the vendor's provider-native endpoint. AI Gateway keeps the events of each streamed run, which is what lets the provider resume it. Workers AI streams and model object streams do not resume. +- Adds the `byokAlias` option, which picks a [stored key](/ai-gateway/configuration/bring-your-own-keys/) by alias. +- Fixes catalog models and [dynamic routes](/ai-gateway/features/dynamic-routing/) in `agents/models/pi-ai`. +- Lets `agents/models/opencode` take catalog ids. +- Resumes a catalog or dynamic route stream that drops mid-answer, with nothing lost or repeated. `createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` and `ai-gateway-provider`, which are now deprecated. diff --git a/src/content/docs/agents/harnesses/opencode.mdx b/src/content/docs/agents/harnesses/opencode.mdx index 70e4a22257a..fcbdbb85f00 100644 --- a/src/content/docs/agents/harnesses/opencode.mdx +++ b/src/content/docs/agents/harnesses/opencode.mdx @@ -63,7 +63,7 @@ Install the Agents SDK and OpenCode packages: -Use `agents` version 0.28.1 or later. The OpenCode packages are optional peer dependencies, required by this integration, with a supported range of `^2.0.24`. +Use the latest `agents` release. The OpenCode packages are optional peer dependencies, required by this integration, with a supported range of `^2.0.24`. Add the `AI` binding, a SQLite-backed Durable Object, and `nodejs_compat` to your Wrangler configuration: diff --git a/src/content/docs/agents/models/ai-sdk.mdx b/src/content/docs/agents/models/ai-sdk.mdx index e74fcdf7236..3e6bbcf8609 100644 --- a/src/content/docs/agents/models/ai-sdk.mdx +++ b/src/content/docs/agents/models/ai-sdk.mdx @@ -31,7 +31,7 @@ ai("dynamic/support"); // AI Gateway dynamic route ai(anthropic("claude-opus-4-8")); ``` -A string needs no vendor package and no vendor key. A model object keeps the vendor's own request builder and parser, so every feature of the vendor's package works. +A string needs no vendor package and no vendor key. A model object keeps the vendor's own request builder and parser. `createAI` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Both are deprecated. @@ -194,7 +194,7 @@ wrapLanguageModel({ model: ai(anthropic("claude-opus-4-8")), middleware }); A model that is already wrapped in middleware hides the settings the provider needs, so `ai()` rejects it. -### Use your own vendor key +### Unified billing and BYOK By default, the provider removes the vendor's credential header (`authorization`, `x-api-key`, `x-goog-api-key`, or `api-key`) so AI Gateway supplies the key. To use a key you hold, give it to the vendor's provider and set `vendorKey: "request"` on the model: @@ -205,8 +205,6 @@ const model = ai(anthropic("claude-opus-4-8"), { vendorKey: "request" }); `vendorKey` belongs to the model you set it on. A fallback model does not inherit it, so one chain can mix a model on your key with a model on Unified Billing. -### Choose a stored key or Zero Data Retention - `byokAlias` picks a [stored key by alias](/ai-gateway/configuration/bring-your-own-keys/) when the gateway holds several keys for one provider. The provider sends it as `cf-aig-byok-alias` for catalog models and model objects. `zdr` turns [Zero Data Retention](/ai-gateway/features/unified-billing/#zero-data-retention-zdr) on (`true`) or off (`false`) for one Unified Billing request, whatever the gateway's own setting. The provider sends it as `cf-aig-zdr`. It applies to model objects only, because catalog models and dynamic routes do not read it. @@ -541,7 +539,7 @@ globalThis.AI_SDK_DEFAULT_PROVIDER = createAI({ binding: env.AI }); await generateText({ model: "@cf/zai-org/glm-4.7-flash", prompt: "Hi" }); ``` -A string can be a Workers AI id, a catalog id, or a dynamic route. Reach a vendor's model through its own package with a model object. +A string can be a Workers AI id, a catalog id, or a dynamic route. ## Limitations From 97dfe6b7a8ffd258be61f42f42aa645418753292 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Wed, 14 Oct 2026 18:32:27 +0200 Subject: [PATCH 03/13] [Agents] Changelog createAI as a new provider; use Claude Opus 5.5 in examples --- .../2026-10-14-createai-model-providers.mdx | 56 +++++++++++++++++++ ...10-14-models-catalog-ids-stream-resume.mdx | 47 ---------------- .../docs/agents/harnesses/opencode.mdx | 4 +- src/content/docs/agents/models/ai-sdk.mdx | 34 +++++------ src/content/docs/agents/models/index.mdx | 10 ++-- src/content/docs/agents/models/pi-ai.mdx | 16 +++--- 6 files changed, 88 insertions(+), 79 deletions(-) create mode 100644 src/content/changelog/agents/2026-10-14-createai-model-providers.mdx delete mode 100644 src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx diff --git a/src/content/changelog/agents/2026-10-14-createai-model-providers.mdx b/src/content/changelog/agents/2026-10-14-createai-model-providers.mdx new file mode 100644 index 00000000000..9ab15467551 --- /dev/null +++ b/src/content/changelog/agents/2026-10-14-createai-model-providers.mdx @@ -0,0 +1,56 @@ +--- +title: createAI, one model provider for the AI SDK and Pi AI +description: The Agents SDK's createAI calls Workers AI, the AI Gateway catalog, dynamic routes, and vendor models through the AI binding, from the AI SDK or Pi AI. It replaces the binding mode of workers-ai-provider and ai-gateway-provider. +products: + - agents + - workers +date: 2026-10-14 +--- + +import { TypeScriptExample } from "~/components"; + +The Agents SDK now ships [`createAI`](/agents/models/), one model provider for Workers AI and AI Gateway over the Worker's `AI` binding. There are two imports, with the same options: + +- `agents/models/ai-sdk` for the [AI SDK](/agents/models/ai-sdk/). It replaces the `AI` binding mode of `workers-ai-provider` and `ai-gateway-provider`. +- `agents/models/pi-ai` for [Pi AI](/agents/models/pi-ai/) and the [Pi harness](/agents/harnesses/pi/). It gives Pi AI the same binding-based support. + +The [OpenCode harness](/agents/harnesses/opencode/) uses the AI SDK provider through `agents/models/opencode`. + + + +```ts +import { createAI } from "agents/models/ai-sdk"; +import { streamText } from "ai"; + +export default { + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI, id: "my-gateway" }); + + const result = streamText({ + model: ai("anthropic/claude-opus-5.5", { + fallback: ["@cf/zai-org/glm-4.7-flash"], + }), + prompt: "Write a haiku about Durable Objects", + }); + + return result.toTextStreamResponse(); + }, +}; +``` + + + +With `createAI` you get: + +- **Every model by id.** Workers AI `@cf/` ids, any model in the [AI Gateway catalog](/ai/models/) such as `anthropic/claude-opus-5.5`, and [dynamic routes](/ai-gateway/features/dynamic-routing/) as `dynamic/`. +- **Vendor model objects.** Pass a model from the vendor's own package, such as `ai(anthropic("claude-opus-5-5"))`. The request goes unchanged to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/). +- **No API tokens.** Every request goes through the `AI` binding. AI Gateway bills through [Unified Billing](/ai-gateway/features/unified-billing/) or a [stored key](/ai-gateway/configuration/bring-your-own-keys/). `byokAlias` picks a stored key, `zdr` sets Zero Data Retention, and `vendorKey: "request"` sends a key you hold. +- **Gateway options.** Caching, logging, metadata, timeouts, and retries on the provider, the model, or the call. +- **Fallback.** Try models in order if one fails before it answers. Mix ids and model objects. +- **Stream resume.** A catalog or dynamic route stream that drops mid-answer resumes, with nothing lost or repeated. +- **Gateway metadata and typed errors.** Each result carries the log id, cache status, run id, and the model that answered. Failures throw `CloudflareAIError`. +- **Other model types.** Embeddings, images, speech, transcription, and reranking from Workers AI in the AI SDK provider, and images in Pi AI. + +`createAI` does not use AI Gateway's deprecated [Universal Endpoint](/ai-gateway/usage/universal/). Once `createAI` covers everything `workers-ai-provider` and `ai-gateway-provider` do, those packages will be deprecated. + +For more information, refer to [Models](/agents/models/). diff --git a/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx b/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx deleted file mode 100644 index dda5fa55ef0..00000000000 --- a/src/content/changelog/agents/2026-10-14-models-catalog-ids-stream-resume.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Agents SDK model providers take AI Gateway catalog ids and resume dropped streams -description: createAI in the Agents SDK now runs AI Gateway catalog ids on env.AI.run, sends model objects to provider-native endpoints, and resumes a stream that drops mid-answer. -products: - - agents - - workers -date: 2026-10-14 ---- - -import { TypeScriptExample } from "~/components"; - -The [Agents SDK model providers](/agents/models/) now take any model id from the [AI Gateway catalog](/ai/models/), such as `anthropic/claude-opus-4.8`. You do not need a vendor package or a vendor key in your Worker. - - - -```ts -import { createAI } from "agents/models/ai-sdk"; -import { streamText } from "ai"; - -export default { - async fetch(request: Request, env: Env) { - const ai = createAI({ binding: env.AI, id: "my-gateway" }); - - const result = streamText({ - model: ai("anthropic/claude-opus-4.8"), - prompt: "Write a haiku about Durable Objects", - }); - - return result.toTextStreamResponse(); - }, -}; -``` - - - -`createAI` no longer uses AI Gateway's deprecated [Universal Endpoint](/ai-gateway/usage/universal/). Model ids, catalog ids included, run on `env.AI.run()`. A vendor's own model object, such as `ai(anthropic("claude-opus-4-8"))`, goes through the binding to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) for that vendor. - -This release also: - -- Adds the `byokAlias` option, which picks a [stored key](/ai-gateway/configuration/bring-your-own-keys/) by alias. -- Fixes catalog models and [dynamic routes](/ai-gateway/features/dynamic-routing/) in `agents/models/pi-ai`. -- Lets `agents/models/opencode` take catalog ids. -- Resumes a catalog or dynamic route stream that drops mid-answer, with nothing lost or repeated. - -`createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` and `ai-gateway-provider`, which are now deprecated. - -For more information, refer to the [AI SDK provider](/agents/models/ai-sdk/), the [Pi AI provider](/agents/models/pi-ai/), and the [OpenCode harness](/agents/harnesses/opencode/). diff --git a/src/content/docs/agents/harnesses/opencode.mdx b/src/content/docs/agents/harnesses/opencode.mdx index fcbdbb85f00..35824d3ea1c 100644 --- a/src/content/docs/agents/harnesses/opencode.mdx +++ b/src/content/docs/agents/harnesses/opencode.mdx @@ -184,11 +184,11 @@ The `"demo"` object name is for this example. In an application, choose an objec ```ts this.ai("@cf/moonshotai/kimi-k2.7-code"); // Workers AI -this.ai("anthropic/claude-opus-4.8"); // AI Gateway catalog +this.ai("anthropic/claude-opus-5.5"); // AI Gateway catalog this.ai("dynamic/support"); // AI Gateway dynamic route ``` -A catalog id uses the [model catalog](/ai/models/)'s spelling, such as `anthropic/claude-opus-4.8`. A dynamic route belongs to one gateway, so pass that gateway's `id` to `createAI`. An id that is not one of these three forms throws a `TypeError` on the line that names it. Streams from catalog models and dynamic routes [resume after a dropped connection](/agents/models/ai-sdk/#resume-a-dropped-stream). +A catalog id uses the [model catalog](/ai/models/)'s spelling, such as `anthropic/claude-opus-5.5`. A dynamic route belongs to one gateway, so pass that gateway's `id` to `createAI`. An id that is not one of these three forms throws a `TypeError` on the line that names it. Streams from catalog models and dynamic routes [resume after a dropped connection](/agents/models/ai-sdk/#resume-a-dropped-stream). Each call returns OpenCode's model reference, `{ providerID, id }`. Pass it to `defaults.model` or `session.setModel()`. To set how OpenCode budgets a model's context, pass `limit: { context, output }`. The default is 128,000 context tokens and 8,192 output tokens. diff --git a/src/content/docs/agents/models/ai-sdk.mdx b/src/content/docs/agents/models/ai-sdk.mdx index 3e6bbcf8609..222cf4c03e3 100644 --- a/src/content/docs/agents/models/ai-sdk.mdx +++ b/src/content/docs/agents/models/ai-sdk.mdx @@ -24,16 +24,16 @@ The provider takes two kinds of model. Both go through the `AI` binding: ```ts // A string runs on env.AI.run. ai("@cf/zai-org/glm-4.7-flash"); // Workers AI -ai("anthropic/claude-opus-4.8"); // AI Gateway catalog +ai("anthropic/claude-opus-5.5"); // AI Gateway catalog ai("dynamic/support"); // AI Gateway dynamic route // A vendor's own model object goes to AI Gateway's provider-native endpoint. -ai(anthropic("claude-opus-4-8")); +ai(anthropic("claude-opus-5-5")); ``` A string needs no vendor package and no vendor key. A model object keeps the vendor's own request builder and parser. -`createAI` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Both are deprecated. +`createAI` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Those packages will be deprecated once `createAI` covers everything they do. :::caution[Beta] This provider is in beta. Its API may change in a minor release. @@ -104,7 +104,7 @@ export default { const ai = createAI({ binding: env.AI, id: "my-gateway" }); const { text } = await generateText({ - model: ai("anthropic/claude-opus-4.8"), + model: ai("anthropic/claude-opus-5.5"), prompt: "Say hello in three words", }); @@ -115,7 +115,7 @@ export default { -Use the catalog's spelling of the id, not the vendor's. AI Gateway looks the model up by its exact catalog name, so `anthropic/claude-opus-4.8` works and `anthropic/claude-opus-4-8` does not. Other examples are `openai/gpt-5.5` and `cloudflare/auto`. +Use the catalog's spelling of the id, not the vendor's. AI Gateway looks the model up by its exact catalog name, so `anthropic/claude-opus-5.5` works and `anthropic/claude-opus-5-5` does not. Other examples are `openai/gpt-5.5` and `cloudflare/auto`. The request goes through `env.AI.run()` to AI Gateway. The provider sends it in the OpenAI chat completions format, and AI Gateway converts it to and from the format of the provider that serves the model. The gateway also handles: @@ -158,7 +158,7 @@ export default { const openai = createOpenAI({ apiKey: "cloudflare" }); const claude = await generateText({ - model: ai(anthropic("claude-opus-4-8")), + model: ai(anthropic("claude-opus-5-5")), prompt: "Say hello in three words", }); @@ -182,14 +182,14 @@ https://workers-binding.ai/ai-gateway/gateways/{gateway}/{provider}/{endpoint} The provider takes the gateway provider from the request host. For example, `api.anthropic.com` is `anthropic` and `api.openai.com` is `openai`. A host that AI Gateway has no provider for throws a `TypeError`. Gateway options travel as `cf-aig-*` headers. Multipart bodies, such as file uploads for transcription, work. -Use the vendor's own model id, such as `claude-opus-4-8`. The vendor's package reads model capabilities from it. +Use the vendor's own model id, such as `claude-opus-5-5`. The vendor's package reads model capabilities from it. Providers on [Unified Billing](/ai-gateway/features/unified-billing/) answer with no key of yours. For any other provider, [store a key on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Until you do, the vendor's own error comes back unchanged. `ai()` clones the model for each call and never changes the object you passed. If you add middleware, wrap the model with `ai()` first: ```ts -wrapLanguageModel({ model: ai(anthropic("claude-opus-4-8")), middleware }); +wrapLanguageModel({ model: ai(anthropic("claude-opus-5-5")), middleware }); ``` A model that is already wrapped in middleware hides the settings the provider needs, so `ai()` rejects it. @@ -200,7 +200,7 @@ By default, the provider removes the vendor's credential header (`authorization` ```ts const anthropic = createAnthropic({ apiKey: env.ANTHROPIC_API_KEY }); -const model = ai(anthropic("claude-opus-4-8"), { vendorKey: "request" }); +const model = ai(anthropic("claude-opus-5-5"), { vendorKey: "request" }); ``` `vendorKey` belongs to the model you set it on. A fallback model does not inherit it, so one chain can mix a model on your key with a model on Unified Billing. @@ -210,7 +210,7 @@ const model = ai(anthropic("claude-opus-4-8"), { vendorKey: "request" }); `zdr` turns [Zero Data Retention](/ai-gateway/features/unified-billing/#zero-data-retention-zdr) on (`true`) or off (`false`) for one Unified Billing request, whatever the gateway's own setting. The provider sends it as `cf-aig-zdr`. It applies to model objects only, because catalog models and dynamic routes do not read it. ```ts -const model = ai(anthropic("claude-opus-4-8"), { +const model = ai(anthropic("claude-opus-5-5"), { byokAlias: "production", zdr: true, }); @@ -275,7 +275,7 @@ A stream from a catalog model or a dynamic route resumes after a dropped connect ```ts const result = streamText({ - model: ai("anthropic/claude-opus-4.8"), + model: ai("anthropic/claude-opus-5.5"), prompt: "Write a haiku about Durable Objects", }); // If the connection drops, result.textStream keeps going. @@ -335,7 +335,7 @@ Attach `metadata` to the gateway log entry. Set it once on the provider and add const ai = createAI({ binding: env.AI, metadata: { app: "support-bot" } }); await generateText({ - model: ai(anthropic("claude-opus-4-8")), + model: ai(anthropic("claude-opus-5-5")), prompt: "Summarize this ticket", providerOptions: { cloudflare: { metadata: { userId: "u_123" }, eventId: "ticket-42" }, @@ -350,7 +350,7 @@ await generateText({ `fallback` lists models to try in order if the primary model fails before it produces any output. A fallback model can be a Workers AI id, a catalog id, a dynamic route, or a model object: ```ts -const model = ai(anthropic("claude-opus-4-8"), { +const model = ai(anthropic("claude-opus-5-5"), { fallback: ["openai/gpt-5.5", "dynamic/support", "@cf/zai-org/glm-4.7-flash"], }); ``` @@ -372,7 +372,7 @@ Set gateway options on the provider, on a model, or per call under `providerOpti const ai = createAI({ binding: env.AI, id: "prod", metadata: { app: "demo" } }); // Model -const model = ai("anthropic/claude-opus-4.8", { cacheTtl: 3600 }); +const model = ai("anthropic/claude-opus-5.5", { cacheTtl: 3600 }); // Call await generateText({ @@ -417,13 +417,13 @@ Every result, and every stream's `finish` part, carries `providerMetadata.cloudf ```ts const { providerMetadata } = await generateText({ - model: ai(anthropic("claude-opus-4-8")), + model: ai(anthropic("claude-opus-5-5")), prompt: "hi", }); providerMetadata?.cloudflare; // { -// model: "claude-opus-4-8", +// model: "claude-opus-5-5", // provider: "anthropic", // gateway: "default", // logId: "01M1KZWN069WWNPC18V05NKHSS", @@ -530,7 +530,7 @@ import { createProviderRegistry, generateText } from "ai"; const registry = createProviderRegistry({ cloudflare: ai }); await generateText({ - model: registry.languageModel("cloudflare:anthropic/claude-opus-4.8"), + model: registry.languageModel("cloudflare:anthropic/claude-opus-5.5"), prompt: "Hi", }); diff --git a/src/content/docs/agents/models/index.mdx b/src/content/docs/agents/models/index.mdx index b7c93fa1740..95650775572 100644 --- a/src/content/docs/agents/models/index.mdx +++ b/src/content/docs/agents/models/index.mdx @@ -34,15 +34,15 @@ Name a model with a string, or pass a model object built by the vendor's own pac ```ts ai("@cf/zai-org/glm-4.7-flash"); // Workers AI -ai("anthropic/claude-opus-4.8"); // AI Gateway catalog +ai("anthropic/claude-opus-5.5"); // AI Gateway catalog ai("dynamic/support"); // AI Gateway dynamic route -ai(anthropic("claude-opus-4-8")); // the vendor's own model object +ai(anthropic("claude-opus-5-5")); // the vendor's own model object ``` - **A Workers AI model** is a `@cf/` id. It runs through `env.AI.run()`. A compatibility layer turns each model's response into the standard OpenAI chat completions shape. -- **An AI Gateway catalog model** is an `/` id from the [model catalog](/ai/models/), such as `anthropic/claude-opus-4.8` or `openai/gpt-5.5`. It also runs through `env.AI.run()`, which sends it to AI Gateway. The gateway picks a provider and bills the call. Use the catalog's spelling, not the vendor's: `anthropic/claude-opus-4.8`, not `claude-opus-4-8`. +- **An AI Gateway catalog model** is an `/` id from the [model catalog](/ai/models/), such as `anthropic/claude-opus-5.5` or `openai/gpt-5.5`. It also runs through `env.AI.run()`, which sends it to AI Gateway. The gateway picks a provider and bills the call. Use the catalog's spelling, not the vendor's: `anthropic/claude-opus-5.5`, not `claude-opus-5-5`. - **A [dynamic route](/ai-gateway/features/dynamic-routing/)** is `dynamic/`. The route you configured on the gateway picks the model. -- **A model object** comes from the vendor's provider package, such as `anthropic("claude-opus-4-8")`. The vendor's code builds the request and parses the response. The provider sends that request unchanged through the binding to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) for that vendor. +- **A model object** comes from the vendor's provider package, such as `anthropic("claude-opus-5-5")`. The vendor's code builds the request and parses the response. The provider sends that request unchanged through the binding to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) for that vendor. The providers do not use the deprecated [Universal Endpoint](/ai-gateway/usage/universal/). @@ -54,7 +54,7 @@ The providers do not use the deprecated [Universal Endpoint](/ai-gateway/usage/u - **Stream resume.** A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. - **Gateway metadata on every result.** Each response carries the gateway log id, cache status, run id, and the model that actually answered. -`createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`), which are deprecated. +`createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Those packages will be deprecated once `createAI` covers everything they do. ## Configure the binding diff --git a/src/content/docs/agents/models/pi-ai.mdx b/src/content/docs/agents/models/pi-ai.mdx index b7e9b3db835..0e45a457ff9 100644 --- a/src/content/docs/agents/models/pi-ai.mdx +++ b/src/content/docs/agents/models/pi-ai.mdx @@ -76,7 +76,7 @@ export default { `ai.stream()` returns Pi AI's `AssistantMessageEvent` stream. `ai.completeSimple()` and `ai.streamSimple()` take Pi AI's simple options, such as a `reasoning` level and `maxTokens`: ```ts -const model = ai("anthropic/claude-opus-4.8"); +const model = ai("anthropic/claude-opus-5.5"); const context = { messages: [{ role: "user", content: "Hi", timestamp: Date.now() }], }; @@ -107,18 +107,18 @@ ai("@cf/zai-org/glm-4.7-flash"); ai("dynamic/support"); // 3. A model from Pi AI's Cloudflare AI Gateway catalog, by /. -ai("anthropic/claude-opus-4.8"); +ai("anthropic/claude-opus-5.5"); ai("openai/gpt-5.4"); // 4. Any Pi AI model object, from any Pi AI registry. -ai(getBuiltinModel("anthropic", "claude-opus-4-8")); +ai(getBuiltinModel("anthropic", "claude-opus-5-5")); ``` Any other string throws a `TypeError`. The provider does not guess what a prefix means. A [dynamic route](/ai-gateway/features/dynamic-routing/) runs on `env.AI.run()` under the gateway you created it on, so set that gateway's `id`. The route picks the model and answers in the chat completions format. It takes the same metadata defaults and overrides as a Workers AI id. -A catalog id uses the gateway's spelling (`anthropic/claude-opus-4.8`). A model object from a vendor registry uses the vendor's spelling (`claude-opus-4-8`). +A catalog id uses the gateway's spelling (`anthropic/claude-opus-5.5`). A model object from a vendor registry uses the vendor's spelling (`claude-opus-5-5`). The third and fourth forms keep Pi AI's model metadata, including `api`, `compat`, `thinkingLevelMap`, cost, context window, and maximum tokens. That metadata decides how each vendor is asked to think. For example, Claude Opus gets `thinking: { type: "adaptive" }` and GPT-5 gets `reasoning.effort`. `ai(model)` does not change the object you pass. @@ -140,12 +140,12 @@ models.setProvider(ai.provider); // defaults: { model: ai("@cf/moonshotai/kimi-k2.7-code") } // For one session: -await session.setModel(ai("anthropic/claude-opus-4.8")); +await session.setModel(ai("anthropic/claude-opus-5.5")); ``` A Pi session stores only its model's provider and id, then looks the model up in the `Models` registry. Use the `@cf/`, `dynamic/`, and `/` forms there. They belong to the `cloudflare` provider (`CLOUDFLARE_PROVIDER_ID`) that you registered. A model object keeps its vendor's provider id, which this registry does not know. Options passed to `ai(model, options)` do not reach a session. Set them on `createAI` instead. -The provider's model list is every Workers AI model Pi AI knows, plus every `/` in Pi AI's AI Gateway catalog, so `models.getModel("cloudflare", "anthropic/claude-opus-4.8")` resolves. +The provider's model list is every Workers AI model Pi AI knows, plus every `/` in Pi AI's AI Gateway catalog, so `models.getModel("cloudflare", "anthropic/claude-opus-5.5")` resolves. ## Cache, tag, and time out requests @@ -154,7 +154,7 @@ Gateway options go on `createAI`, on a model, or on a call. They can be flat or ```ts const ai = createAI({ binding: env.AI, id: "prod", cacheTtl: 60 }); -const model = ai("anthropic/claude-opus-4.8", { +const model = ai("anthropic/claude-opus-5.5", { cacheTtl: 3600, cacheKey: "greeting-v1", metadata: { team: "growth" }, @@ -178,7 +178,7 @@ A Pi AI model is plain data. Per-model options are stored on it under symbol key ## Fall back to another model ```ts -ai(getBuiltinModel("anthropic", "claude-opus-4-8"), { +ai(getBuiltinModel("anthropic", "claude-opus-5-5"), { fallback: ["@cf/zai-org/glm-4.7-flash"], }); ``` From d37fd4b6e316fa6fa7ecf7b5fff680483fe0f1a2 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 14:07:31 +0100 Subject: [PATCH 04/13] [Agents] Date the createAI changelog 2026-10-20; require pi-ai 1.3 --- ...-providers.mdx => 2026-10-20-createai-model-providers.mdx} | 4 ++-- src/content/docs/agents/harnesses/pi/index.mdx | 2 +- src/content/docs/agents/models/pi-ai.mdx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/content/changelog/agents/{2026-10-14-createai-model-providers.mdx => 2026-10-20-createai-model-providers.mdx} (97%) diff --git a/src/content/changelog/agents/2026-10-14-createai-model-providers.mdx b/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx similarity index 97% rename from src/content/changelog/agents/2026-10-14-createai-model-providers.mdx rename to src/content/changelog/agents/2026-10-20-createai-model-providers.mdx index 9ab15467551..e58a19e14d1 100644 --- a/src/content/changelog/agents/2026-10-14-createai-model-providers.mdx +++ b/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx @@ -4,7 +4,7 @@ description: The Agents SDK's createAI calls Workers AI, the AI Gateway catalog, products: - agents - workers -date: 2026-10-14 +date: 2026-10-20 --- import { TypeScriptExample } from "~/components"; @@ -12,7 +12,7 @@ import { TypeScriptExample } from "~/components"; The Agents SDK now ships [`createAI`](/agents/models/), one model provider for Workers AI and AI Gateway over the Worker's `AI` binding. There are two imports, with the same options: - `agents/models/ai-sdk` for the [AI SDK](/agents/models/ai-sdk/). It replaces the `AI` binding mode of `workers-ai-provider` and `ai-gateway-provider`. -- `agents/models/pi-ai` for [Pi AI](/agents/models/pi-ai/) and the [Pi harness](/agents/harnesses/pi/). It gives Pi AI the same binding-based support. +- `agents/models/pi-ai` for [Pi AI](/agents/models/pi-ai/) and the [Pi harness](/agents/harnesses/pi/). It gives Pi AI the same binding-based support, and needs `@earendil-works/pi-ai` 1.3. The [OpenCode harness](/agents/harnesses/opencode/) uses the AI SDK provider through `agents/models/opencode`. diff --git a/src/content/docs/agents/harnesses/pi/index.mdx b/src/content/docs/agents/harnesses/pi/index.mdx index 2d99e4f7ad1..98ec3f90e06 100644 --- a/src/content/docs/agents/harnesses/pi/index.mdx +++ b/src/content/docs/agents/harnesses/pi/index.mdx @@ -51,7 +51,7 @@ Install `agents` with Pi Durable and Pi AI: -Both Pi packages are optional peer dependencies of `agents`. `PiHarness` needs version 1.0 or later of each. +Both Pi packages are optional peer dependencies of `agents`. `PiHarness` needs version 1.3 of each (`~1.3.0`). The `agents/models/pi-ai` entry point supports AI Gateway and Workers AI models, so you can get started with Cloudflare models right away or use your existing Pi AI provider. The examples on this page use Workers AI through the `AI` binding and the [Pi AI model provider](/agents/models/pi-ai/). Add the binding and a SQLite-backed Durable Object to your Wrangler configuration: diff --git a/src/content/docs/agents/models/pi-ai.mdx b/src/content/docs/agents/models/pi-ai.mdx index 0e45a457ff9..626b1316769 100644 --- a/src/content/docs/agents/models/pi-ai.mdx +++ b/src/content/docs/agents/models/pi-ai.mdx @@ -25,7 +25,7 @@ This provider is in beta. Its API may change in a minor release. ## Install -The provider needs `@earendil-works/pi-ai@^1.0.0`, an optional peer dependency of `agents`: +The provider needs `@earendil-works/pi-ai@~1.3.0`, an optional peer dependency of `agents`: @@ -118,7 +118,7 @@ Any other string throws a `TypeError`. The provider does not guess what a prefix A [dynamic route](/ai-gateway/features/dynamic-routing/) runs on `env.AI.run()` under the gateway you created it on, so set that gateway's `id`. The route picks the model and answers in the chat completions format. It takes the same metadata defaults and overrides as a Workers AI id. -A catalog id uses the gateway's spelling (`anthropic/claude-opus-5.5`). A model object from a vendor registry uses the vendor's spelling (`claude-opus-5-5`). +A catalog id uses the AI Gateway catalog's spelling, `anthropic/claude-opus-5.5`. Pi AI's own list spells Claude ids the way Anthropic does (`claude-opus-5-5`), so the provider lists them under the catalog spelling. A model object from a vendor registry keeps the vendor's spelling. The third and fourth forms keep Pi AI's model metadata, including `api`, `compat`, `thinkingLevelMap`, cost, context window, and maximum tokens. That metadata decides how each vendor is asked to think. For example, Claude Opus gets `thinking: { type: "adaptive" }` and GPT-5 gets `reasoning.effort`. `ai(model)` does not change the object you pass. From 1ec3c5a81b6f8127d917fbaa2b290091d1a8beff Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 16:00:29 +0100 Subject: [PATCH 05/13] [Agents] createAI with an account id and API token, and Nova-3 transcription --- .../2026-10-20-createai-model-providers.mdx | 17 ++++----- src/content/docs/agents/models/ai-sdk.mdx | 35 ++++++++++++++----- src/content/docs/agents/models/index.mdx | 24 +++++++++---- src/content/docs/agents/models/pi-ai.mdx | 32 +++++++++++------ 4 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx b/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx index e58a19e14d1..153ed8421e5 100644 --- a/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx +++ b/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx @@ -1,6 +1,6 @@ --- title: createAI, one model provider for the AI SDK and Pi AI -description: The Agents SDK's createAI calls Workers AI, the AI Gateway catalog, dynamic routes, and vendor models through the AI binding, from the AI SDK or Pi AI. It replaces the binding mode of workers-ai-provider and ai-gateway-provider. +description: The Agents SDK's createAI calls Workers AI, the AI Gateway catalog, dynamic routes, and vendor models through the AI binding or an account id and API token, from the AI SDK or Pi AI. It replaces workers-ai-provider and ai-gateway-provider. products: - agents - workers @@ -9,10 +9,10 @@ date: 2026-10-20 import { TypeScriptExample } from "~/components"; -The Agents SDK now ships [`createAI`](/agents/models/), one model provider for Workers AI and AI Gateway over the Worker's `AI` binding. There are two imports, with the same options: +The Agents SDK now ships [`createAI`](/agents/models/), one model provider for Workers AI and AI Gateway, over the Worker's `AI` binding or an account id and API token. There are two imports, with the same options: -- `agents/models/ai-sdk` for the [AI SDK](/agents/models/ai-sdk/). It replaces the `AI` binding mode of `workers-ai-provider` and `ai-gateway-provider`. -- `agents/models/pi-ai` for [Pi AI](/agents/models/pi-ai/) and the [Pi harness](/agents/harnesses/pi/). It gives Pi AI the same binding-based support, and needs `@earendil-works/pi-ai` 1.3. +- `agents/models/ai-sdk` for the [AI SDK](/agents/models/ai-sdk/). It replaces `workers-ai-provider` and `ai-gateway-provider`. +- `agents/models/pi-ai` for [Pi AI](/agents/models/pi-ai/) and the [Pi harness](/agents/harnesses/pi/). It gives Pi AI the same support, and needs `@earendil-works/pi-ai` 1.3. The [OpenCode harness](/agents/harnesses/opencode/) uses the AI SDK provider through `agents/models/opencode`. @@ -44,13 +44,14 @@ With `createAI` you get: - **Every model by id.** Workers AI `@cf/` ids, any model in the [AI Gateway catalog](/ai/models/) such as `anthropic/claude-opus-5.5`, and [dynamic routes](/ai-gateway/features/dynamic-routing/) as `dynamic/`. - **Vendor model objects.** Pass a model from the vendor's own package, such as `ai(anthropic("claude-opus-5-5"))`. The request goes unchanged to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/). -- **No API tokens.** Every request goes through the `AI` binding. AI Gateway bills through [Unified Billing](/ai-gateway/features/unified-billing/) or a [stored key](/ai-gateway/configuration/bring-your-own-keys/). `byokAlias` picks a stored key, `zdr` sets Zero Data Retention, and `vendorKey: "request"` sends a key you hold. +- **The binding, or an account id and API token.** Inside a Worker, requests go through the `AI` binding. To use a [gateway in another account](/agents/models/ai-sdk/#use-an-account-id-and-api-token), or to call from outside a Worker, pass `accountId` and `apiToken` instead. +- **No vendor keys.** AI Gateway bills through [Unified Billing](/ai-gateway/features/unified-billing/) or a [stored key](/ai-gateway/configuration/bring-your-own-keys/). `byokAlias` picks a stored key, `zdr` sets Zero Data Retention, and `vendorKey: "request"` sends a key you hold. - **Gateway options.** Caching, logging, metadata, timeouts, and retries on the provider, the model, or the call. - **Fallback.** Try models in order if one fails before it answers. Mix ids and model objects. -- **Stream resume.** A catalog or dynamic route stream that drops mid-answer resumes, with nothing lost or repeated. +- **Stream resume.** Through the binding, a catalog or dynamic route stream that drops mid-answer resumes, with nothing lost or repeated. - **Gateway metadata and typed errors.** Each result carries the log id, cache status, run id, and the model that answered. Failures throw `CloudflareAIError`. -- **Other model types.** Embeddings, images, speech, transcription, and reranking from Workers AI in the AI SDK provider, and images in Pi AI. +- **Other model types.** Embeddings, images, speech, transcription (Whisper and Deepgram Nova-3), and reranking from Workers AI in the AI SDK provider, and images in Pi AI. -`createAI` does not use AI Gateway's deprecated [Universal Endpoint](/ai-gateway/usage/universal/). Once `createAI` covers everything `workers-ai-provider` and `ai-gateway-provider` do, those packages will be deprecated. +`createAI` does not use AI Gateway's deprecated [Universal Endpoint](/ai-gateway/usage/universal/). `workers-ai-provider` and `ai-gateway-provider` are deprecated in favor of `createAI`, and of `ai-search-provider` for AI Search. For more information, refer to [Models](/agents/models/). diff --git a/src/content/docs/agents/models/ai-sdk.mdx b/src/content/docs/agents/models/ai-sdk.mdx index 222cf4c03e3..3b2b54ae5c2 100644 --- a/src/content/docs/agents/models/ai-sdk.mdx +++ b/src/content/docs/agents/models/ai-sdk.mdx @@ -1,7 +1,7 @@ --- title: AI SDK pcx_content_type: how-to -description: Use createAI from agents/models/ai-sdk to call Workers AI models, AI Gateway catalog models, dynamic routes, and vendor AI SDK models through the AI binding. +description: Use createAI from agents/models/ai-sdk to call Workers AI models, AI Gateway catalog models, dynamic routes, and vendor AI SDK models through the AI binding or an account id and API token. tags: - AI sidebar: @@ -19,7 +19,7 @@ import { `agents/models/ai-sdk` is an [AI SDK](https://ai-sdk.dev/) provider for Workers AI and AI Gateway. `createAI` returns a full `ProviderV4`, so its models work anywhere the AI SDK takes a model: `generateText`, `streamText`, tools, structured output, and [Think](/agents/harnesses/think/). -The provider takes two kinds of model. Both go through the `AI` binding: +The provider takes two kinds of model. Both go through the `AI` binding, or over HTTP with an [account id and API token](#use-an-account-id-and-api-token): ```ts // A string runs on env.AI.run. @@ -33,7 +33,7 @@ ai(anthropic("claude-opus-5-5")); A string needs no vendor package and no vendor key. A model object keeps the vendor's own request builder and parser. -`createAI` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Those packages will be deprecated once `createAI` covers everything they do. +`createAI` replaces `workers-ai-provider` and `ai-gateway-provider`. For AI Search, use `ai-search-provider`. :::caution[Beta] This provider is in beta. Its API may change in a minor release. @@ -61,6 +61,22 @@ Add the `AI` binding to your Wrangler configuration: +## Use an account id and API token + +To use a gateway in another account, or to call from outside a Worker, pass `accountId` and `apiToken` instead of `binding`: + +```ts +const ai = createAI({ + accountId: "", + apiToken: env.CLOUDFLARE_API_TOKEN, + gateway: "my-gateway", +}); +``` + +The call forms and options are the same, and the gateway still defaults to `default`. A string goes to AI Gateway's [REST API](/ai-gateway/usage/rest-api/), which runs it as `env.AI.run()` does. A model object goes to the [provider-native endpoint](/ai-gateway/usage/providers/) at `gateway.ai.cloudflare.com`, with the token in the `cf-aig-authorization` header. The token is sent to those two hosts only. + +[Create an API token](/fundamentals/api/get-started/create-token/) with the **Workers AI Read** permission for strings and **AI Gateway Run** for model objects. + ## Call a Workers AI model Pass a `@cf/` id. Model ids autocomplete from `@cloudflare/workers-types`: @@ -271,7 +287,7 @@ export default { ## Resume a dropped stream -A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything: +Through the binding, a stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything: ```ts const result = streamText({ @@ -390,6 +406,9 @@ Gateway options can be given flat or nested: `{ cacheTtl: 60 }` and `{ gateway: | Option | Type | Description | | -------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `binding` | `Ai` | The `AI` binding. Provider only. | +| `accountId` | `string` | The account for an [account id and API token](#use-an-account-id-and-api-token). Provider only. | +| `apiToken` | `string` | A Cloudflare API token for that account. Provider only. | | `id` | `string` | Gateway id. Defaults to `"default"`, which is created the first time you use it. | | `gateway` | `string \| GatewayOptions` | The gateway id, or the gateway options nested. | | `skipCache` | `boolean` | Bypass the gateway cache. | @@ -435,7 +454,7 @@ providerMetadata?.cloudflare; | Field | Description | | ------------- | ------------------------------------------------------------------------------- | | `model` | The model that answered, after any fallback. | -| `gateway` | The gateway id the request went through. | +| `gateway` | The gateway id, when a gateway handled the request. | | `provider` | The gateway provider, for a model object. | | `logId` | The gateway log entry, from `cf-aig-log-id`. | | `eventId` | The gateway event, from `cf-aig-event-id`. | @@ -445,7 +464,7 @@ providerMetadata?.cloudflare; | `cacheStatus` | `HIT` or `MISS`, from `cf-aig-cache-status`. | | `step` | The step of a gateway route that answered, from `cf-aig-step`. | -Only `model` and `gateway` are always present. Check for the other fields before you use them. +Only `model` is always present. Check for the other fields before you use them. ## Handle errors @@ -517,7 +536,7 @@ Model-specific settings go under `providerOptions.cloudflare`: | Transcription | `audio`, `mediaType` | `language`, `task`, `initialPrompt`, `prefix`, `vadFilter` | | Reranking | `query`, `documents`, `topN` (as `top_k`) | None | -An option a model does not take is listed in the result's `warnings` instead of being sent. For example, `flux-1-schnell` takes only a prompt and `steps`. Image generation makes one image per request, so `generateImage` with a larger `n` sends parallel requests. +An option a model does not take is listed in the result's `warnings` instead of being sent. For example, `flux-1-schnell` takes only a prompt and `steps`, and `@cf/deepgram/nova-3` takes `language` but not the Whisper-only transcription options. Image generation makes one image per request, so `generateImage` with a larger `n` sends parallel requests. These methods take Workers AI `@cf/` ids only, including in `fallback`. To route a vendor's own embedding, image, speech, or transcription model through AI Gateway, pass it to `ai.routed(model)`. It goes to the provider-native endpoint, like `ai(model)`. `ai.routed()` takes the gateway options, `headers`, `sessionAffinity`, and `vendorKey`. @@ -543,7 +562,7 @@ A string can be a Workers AI id, a catalog id, or a dynamic route. ## Limitations -- `@cf/deepgram/nova-3` and `@cf/deepgram/flux` are not supported. They throw a `CloudflareAIError` before any request is sent. Use `@cf/openai/whisper-large-v3-turbo` for transcription. +- `@cf/deepgram/flux` takes websocket connections only, so it throws a `CloudflareAIError` before any request is sent. Use `@cf/deepgram/nova-3` or `@cf/openai/whisper-large-v3-turbo` for transcription. - `chatTemplateKwargs` and `sessionAffinity` apply to Workers AI models only. - `zdr` applies to model objects only. - Workers AI streams and model object streams do not resume after a dropped connection. diff --git a/src/content/docs/agents/models/index.mdx b/src/content/docs/agents/models/index.mdx index 95650775572..62e6bf1159d 100644 --- a/src/content/docs/agents/models/index.mdx +++ b/src/content/docs/agents/models/index.mdx @@ -1,7 +1,7 @@ --- title: Models pcx_content_type: overview -description: Call Workers AI, AI Gateway catalog models, dynamic routes, and vendor models from the AI SDK or Pi AI with one createAI provider over the AI binding. +description: Call Workers AI, AI Gateway catalog models, dynamic routes, and vendor models from the AI SDK or Pi AI with one createAI provider over the AI binding or an account id and API token. tags: - AI sidebar: @@ -15,7 +15,7 @@ products: import { LinkCard, WranglerConfig } from "~/components"; -The Agents SDK includes model providers for [Workers AI](/workers-ai/) and [AI Gateway](/ai-gateway/). Each provider is one `createAI` factory over the Worker's `AI` binding. There are two, one per framework: +The Agents SDK includes model providers for [Workers AI](/workers-ai/) and [AI Gateway](/ai-gateway/). Each provider is one `createAI` factory over the Worker's `AI` binding, or an account id and API token. There are two, one per framework: | Import | Framework | Use it with | | ---------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------- | @@ -48,17 +48,17 @@ The providers do not use the deprecated [Universal Endpoint](/ai-gateway/usage/u ## What every model gets -- **No API tokens in your Worker.** Requests go through the `AI` binding. AI Gateway holds third-party credentials, through [Unified Billing](/ai-gateway/features/unified-billing/) or a key you [store on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Unified Billing requires [authentication](/ai-gateway/configuration/authentication/) on the gateway. +- **No vendor keys.** AI Gateway holds third-party credentials, through [Unified Billing](/ai-gateway/features/unified-billing/) or a key you [store on the gateway](/ai-gateway/configuration/bring-your-own-keys/). Unified Billing requires [authentication](/ai-gateway/configuration/authentication/) on the gateway. - **Gateway options.** Caching, logging, metadata, timeouts, retries, and BYOK key aliases are options on the provider, the model, or the call. The `default` gateway is created the first time you use it. - **Fallback.** List models to try in order if the first fails before it produces output. Mix Workers AI ids, catalog ids, dynamic routes, and model objects. -- **Stream resume.** A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. +- **Stream resume.** Through the binding, a stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. - **Gateway metadata on every result.** Each response carries the gateway log id, cache status, run id, and the model that actually answered. -`createAI` from `agents/models/ai-sdk` replaces the `AI` binding mode of `workers-ai-provider` (`createWorkersAI({ binding })`) and `ai-gateway-provider` (`createAiGateway({ binding })`). Those packages will be deprecated once `createAI` covers everything they do. +`createAI` from `agents/models/ai-sdk` replaces `workers-ai-provider` and `ai-gateway-provider`. For AI Search, use `ai-search-provider`. -## Configure the binding +## Connect to Cloudflare -Both providers need the `AI` binding: +Inside a Worker, pass the `AI` binding, `createAI({ binding: env.AI })`, and add it to your Wrangler configuration: @@ -72,6 +72,16 @@ Both providers need the `AI` binding: +To use a gateway in another account, or to call from outside a Worker, pass an account id and API token instead. [Create the token](/fundamentals/api/get-started/create-token/) with **Workers AI Read** and **AI Gateway Run**: + +```ts +const ai = createAI({ + accountId: "", + apiToken: env.CLOUDFLARE_API_TOKEN, + gateway: "my-gateway", +}); +``` + ## Choose a provider +To use a gateway in another account, or to call from outside a Worker, pass `accountId` and `apiToken` instead of `binding`. Everything else is the same, and the gateway still defaults to `default`: + +```ts +const ai = createAI({ + accountId: "", + apiToken: env.CLOUDFLARE_API_TOKEN, + gateway: "my-gateway", +}); +``` + +[Create the API token](/fundamentals/api/get-started/create-token/) with **Workers AI Read** and **AI Gateway Run**. The token is sent to the AI Gateway REST API and to `gateway.ai.cloudflare.com` only. + ## Call a model The provider carries Pi AI's entry points, so nothing is registered globally: @@ -208,7 +220,7 @@ The gateway's own failures, such as a missing gateway or an unpaid account, beco ## Resume a dropped stream -A stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. +Through the binding, a stream from a catalog model or a dynamic route resumes after a dropped connection. You do not need to configure anything. AI Gateway names each streamed run in the `cf-aig-run-id` header and keeps its events. If the connection drops, the provider reconnects through the binding and replays the run from the last complete event. The stream continues with nothing lost or repeated. @@ -216,20 +228,20 @@ The provider gives up after five drops in one stream, or once the gateway no lon ## How requests are routed -The provider routes every request through the `AI` binding. It has no HTTP transport and needs no API token: +The provider routes every request through the `AI` binding, or over HTTP with an account id and API token: -| Model | `api` | Transport | -| ---------------------------------------------------------- | -------------------- | -------------------------------------------------------------- | -| `@cf/` models and dynamic routes | `cloudflare-ai` | `env.AI.run()`, with Cloudflare's compatibility layer | -| Anthropic models from Pi AI's gateway catalog | `anthropic-messages` | `env.AI.run()` to AI Gateway, in Anthropic Messages format | -| OpenAI models from Pi AI's gateway catalog | `openai-responses` | `env.AI.run()` to AI Gateway, in OpenAI Responses format | -| Any other model object, such as Anthropic, OpenAI, or Groq | its own `api` | The binding's `fetch` to AI Gateway's provider-native endpoint | +| Model | `api` | Through the binding | With an account id and token | +| ---------------------------------------------------------- | -------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | +| `@cf/` models and dynamic routes | `cloudflare-ai` | `env.AI.run()`, with Cloudflare's compatibility layer | The AI Gateway REST API, with the same compatibility layer | +| Anthropic models from Pi AI's gateway catalog | `anthropic-messages` | `env.AI.run()` to AI Gateway, in Anthropic Messages format | The REST API's `/ai/v1/messages`, in the same format | +| OpenAI models from Pi AI's gateway catalog | `openai-responses` | `env.AI.run()` to AI Gateway, in OpenAI Responses format | The REST API's `/ai/v1/responses`, in the same format | +| Any other model object, such as Anthropic, OpenAI, or Groq | its own `api` | The binding's `fetch` to AI Gateway's provider-native endpoint | `gateway.ai.cloudflare.com`, the provider-native endpoint | A model from Pi AI's Cloudflare AI Gateway catalog runs on `env.AI.run()` under its catalog id, in the model's native format. AI Gateway converts nothing, so Pi AI's own stream processors read the answer. The gateway picks the provider and bills through Unified Billing or a key stored on the gateway. Any other model object goes to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/) through the binding's `fetch`. The provider takes the gateway provider from the model's `baseUrl` host. Pi AI's own converter builds the vendor's request body, and Pi AI parses the vendor's response stream. -Either way, no credential header leaves your Worker. The provider never rewrites the model's `api`, `provider`, or `id`, so thinking signatures and encrypted reasoning items work on the next turn. +Either way, no vendor credential leaves your Worker. The provider never rewrites the model's `api`, `provider`, or `id`, so thinking signatures and encrypted reasoning items work on the next turn. The provider routes four `api` values: `cloudflare-ai`, `anthropic-messages`, `openai-responses`, and `openai-completions`. Models from several Pi AI registries use other values and cannot be routed yet, including `mistral-conversations`, `google-generative-ai`, `google-vertex`, `amazon-bedrock`, `azure-openai-responses`, and `openai-codex`. Such a model's stream ends with an `error` event that names its `api`. So does a model whose `baseUrl` host AI Gateway has no provider for. From 28a91f388b3ec6fe25b82145c6b1f780ca8c0682 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 21:47:41 +0100 Subject: [PATCH 06/13] [Workers AI, AI Gateway] Move docs off workers-ai-provider and ai-gateway-provider --- ...rovider-ai-gateway-provider-deprecated.mdx | 33 +++ src/content/docs/agent-memory/get-started.mdx | 8 +- .../chat/chat-agents.mdx | 36 +-- .../agents/communication-channels/voice.mdx | 6 +- .../docs/agents/concepts/calling-llms.mdx | 24 +- .../docs/agents/examples/browser-agent.mdx | 12 +- .../docs/agents/examples/chat-agent.mdx | 8 +- .../docs/agents/examples/voice-agent.mdx | 6 +- .../agents/harnesses/think/configuration.mdx | 2 +- .../harnesses/think/getting-started.mdx | 8 +- .../docs/agents/harnesses/think/index.mdx | 6 +- .../apis/client-api.mdx | 6 +- .../agents/runtime/communication/http-sse.mdx | 6 +- .../runtime/operations/using-ai-models.mdx | 16 +- src/content/docs/agents/tools/browser.mdx | 6 +- .../docs/agents/tools/codemode/browser.mdx | 6 +- src/content/docs/agents/tools/filesystem.mdx | 4 +- src/content/docs/agents/tools/mcp.mdx | 6 +- src/content/docs/agents/tools/sandbox.mdx | 4 +- .../ai-gateway/integrations/vercel-ai-sdk.mdx | 124 ++++++++-- .../docs/ai-search/agent-sdks/agents-sdk.mdx | 10 +- .../how-to/talk-to-your-knowledge-base.mdx | 6 +- .../build-an-ai-code-interpreter.mdx | 10 +- .../sdk/tutorials/ai-code-executor.mdx | 2 +- .../docs/workers-ai/configuration/ai-sdk.mdx | 231 +++++++++++------- 25 files changed, 377 insertions(+), 209 deletions(-) create mode 100644 src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx diff --git a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx new file mode 100644 index 00000000000..30bc959491c --- /dev/null +++ b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx @@ -0,0 +1,33 @@ +--- +title: workers-ai-provider and ai-gateway-provider are deprecated +description: The workers-ai-provider and ai-gateway-provider npm packages are deprecated. Use createAI from agents/models/ai-sdk, and ai-search-provider for AI Search. +products: + - workers-ai + - ai-gateway +date: 2026-10-21 +--- + +The `workers-ai-provider` and `ai-gateway-provider` npm packages are deprecated. To call Workers AI and AI Gateway from the [AI SDK](https://ai-sdk.dev/), use [`createAI`](/agents/models/ai-sdk/) from `agents/models/ai-sdk`. For AI Search, use [`ai-search-provider`](/ai-search/agent-sdks/ai-sdk/). + +Existing code keeps working. Nothing changes at runtime. + +```sh +npm install agents ai @ai-sdk/provider +``` + +```ts +import { createAI } from "agents/models/ai-sdk"; + +const ai = createAI({ binding: env.AI }); + +const model = ai("@cf/zai-org/glm-4.7-flash"); +``` + +| Before | After | +| ------------------------------------------------------- | -------------------------------------------------------- | +| `createWorkersAI({ binding })` | `createAI({ binding })` | +| `createWorkersAI({ accountId, apiKey })` | `createAI({ accountId, apiToken })` | +| `createAiGateway(...)` | `createAI(...)`, with `fallback` for a list of models | +| `createAISearch` from `workers-ai-provider` | `createAISearchNamespace` from `ai-search-provider` | + +For more information, refer to [AI SDK](/agents/models/ai-sdk/). diff --git a/src/content/docs/agent-memory/get-started.mdx b/src/content/docs/agent-memory/get-started.mdx index 52931736416..ac18713e71f 100644 --- a/src/content/docs/agent-memory/get-started.mdx +++ b/src/content/docs/agent-memory/get-started.mdx @@ -55,7 +55,7 @@ cd memory-agent Install the dependencies used by this guide: - + ## 2. Create a namespace @@ -183,7 +183,7 @@ Change the `agents` import and add the AI SDK imports. Keep the `Session` import import { Agent, getAgentByName, routeAgentRequest } from "agents"; import { convertToModelMessages, generateText, stepCountIs } from "ai"; import type { UIMessage } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; ``` @@ -218,9 +218,9 @@ export class ChatAgent extends Agent { await this.scheduleIngest(); - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), system: await this.session.freezeSystemPrompt(), messages: await convertToModelMessages( (await this.session.getHistory()) as UIMessage[], diff --git a/src/content/docs/agents/communication-channels/chat/chat-agents.mdx b/src/content/docs/agents/communication-channels/chat/chat-agents.mdx index 0795d15836e..d09c77754bd 100644 --- a/src/content/docs/agents/communication-channels/chat/chat-agents.mdx +++ b/src/content/docs/agents/communication-channels/chat/chat-agents.mdx @@ -40,7 +40,7 @@ Built on the [AI SDK](https://ai-sdk.dev) and Cloudflare Durable Objects, you ge ### Install - + ### Server @@ -48,16 +48,16 @@ Built on the [AI SDK](https://ai-sdk.dev) and Cloudflare Durable Objects, you ge ```ts import { AIChatAgent } from "@cloudflare/ai-chat"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { streamText, convertToModelMessages } from "ai"; export class ChatAgent extends AIChatAgent { async onChatMessage() { - // Use any provider such as workers-ai-provider, openai, anthropic, google, etc. - const workersai = createWorkersAI({ binding: this.env.AI }); + // Use any AI SDK provider, such as createAI from agents/models/ai-sdk, openai, anthropic, or google. + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: await convertToModelMessages(this.messages), }); @@ -194,10 +194,10 @@ This is the main method you override. It receives the conversation context and s ```ts export class ChatAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), system: "You are a helpful assistant.", messages: await convertToModelMessages(this.messages), }); @@ -270,10 +270,10 @@ import { streamText, convertToModelMessages, pruneMessages } from "ai"; export class ChatAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: pruneMessages({ messages: await convertToModelMessages(this.messages), reasoning: "before-last-message", @@ -550,7 +550,7 @@ When a user clicks "stop" in the chat UI, the client sends a `CF_AGENT_CHAT_REQU export class ChatAgent extends AIChatAgent { async onChatMessage(_onFinish, options) { const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: await convertToModelMessages(this.messages), abortSignal: options?.abortSignal, // Pass through for cancellation }); @@ -949,10 +949,10 @@ import { streamText, convertToModelMessages, tool, stepCountIs } from "ai"; import { z } from "zod"; export class ChatAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: await convertToModelMessages(this.messages), tools: { getWeather: tool({ @@ -1201,12 +1201,12 @@ import { export class ChatAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const stream = createUIMessageStream({ execute: async ({ writer }) => { const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: await convertToModelMessages(this.messages), }); @@ -1380,7 +1380,7 @@ Storage (`maxPersistedMessages`) and LLM context are independent: export class ChatAgent extends AIChatAgent { async onChatMessage() { const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: pruneMessages({ // LLM context limit messages: await convertToModelMessages(this.messages), @@ -1405,11 +1405,11 @@ export class ChatAgent extends AIChatAgent { ```ts -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; -const workersai = createWorkersAI({ binding: this.env.AI }); +const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), messages: await convertToModelMessages(this.messages), }); ``` diff --git a/src/content/docs/agents/communication-channels/voice.mdx b/src/content/docs/agents/communication-channels/voice.mdx index 2314189c260..1d54af060c3 100644 --- a/src/content/docs/agents/communication-channels/voice.mdx +++ b/src/content/docs/agents/communication-channels/voice.mdx @@ -243,17 +243,17 @@ export class MyAgent extends VoiceAgent { ```ts import { streamText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class MyAgent extends VoiceAgent { transcriber = new WorkersAIFluxSTT(this.env.AI); tts = new WorkersAITTS(this.env.AI); async onTurn(transcript: string, context: VoiceTurnContext) { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/moonshotai/kimi-k2.6"), + model: ai("@cf/moonshotai/kimi-k2.6"), system: "You are a helpful voice assistant. Keep responses concise.", messages: [ ...context.messages.map((m) => ({ diff --git a/src/content/docs/agents/concepts/calling-llms.mdx b/src/content/docs/agents/concepts/calling-llms.mdx index 8ad30643d7c..45d48faf6ac 100644 --- a/src/content/docs/agents/concepts/calling-llms.mdx +++ b/src/content/docs/agents/concepts/calling-llms.mdx @@ -58,15 +58,15 @@ An Agent keeps running after the client disconnects. When the response arrives, ```ts import { Agent } from "agents"; import { streamText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class MyAgent extends Agent { async onMessage(connection: Connection, message: WSMessage) { const { prompt } = JSON.parse(message as string); - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt, }); @@ -103,9 +103,9 @@ export class DigestAgent extends Agent { SELECT title, body FROM articles WHERE created_at > datetime('now', '-1 day')`; - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const { text } = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: `Summarize these articles:\n${articles.map((a) => a.title + ": " + a.body).join("\n\n")}`, }); @@ -128,25 +128,25 @@ Because an Agent maintains state across calls, you can chain multiple models in ```ts import { Agent } from "agents"; import { generateText, embed } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class TriageAgent extends Agent { async triage(ticket: string) { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const { text: category } = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: `Classify this support ticket into one of: billing, technical, account. Ticket: ${ticket}`, }); const { embedding } = await embed({ - model: workersai("@cf/baai/bge-base-en-v1.5"), + model: ai.embedding("@cf/baai/bge-base-en-v1.5"), value: ticket, }); const similar = await this.env.VECTOR_DB.query(embedding, { topK: 5 }); const { text: response } = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: `Draft a response for this ${category} ticket. Similar resolved tickets: ${JSON.stringify(similar)}. Ticket: ${ticket}`, }); @@ -180,9 +180,9 @@ export class CachingAgent extends Agent { return cached[0].response; } - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const { text } = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt, }); diff --git a/src/content/docs/agents/examples/browser-agent.mdx b/src/content/docs/agents/examples/browser-agent.mdx index 2a0985df7ba..bfd90b3d310 100644 --- a/src/content/docs/agents/examples/browser-agent.mdx +++ b/src/content/docs/agents/examples/browser-agent.mdx @@ -93,12 +93,12 @@ Pass browser tools alongside your other tools. The `model` can be any AI SDK pro ```ts import { streamText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; -const workersai = createWorkersAI({ binding: env.AI }); +const ai = createAI({ binding: env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), system: "You are a helpful assistant that can inspect web pages.", messages, tools: { @@ -151,19 +151,19 @@ The typical pattern is to create browser tools inside an [`AIChatAgent`](/agents ```ts import { AIChatAgent } from "@cloudflare/ai-chat"; import { createBrowserTools } from "agents/browser/ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { streamText, convertToModelMessages, stepCountIs } from "ai"; export class MyAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const browserTools = createBrowserTools({ browser: this.env.BROWSER, loader: this.env.LOADER, }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), system: "You can browse the web and inspect pages.", messages: await convertToModelMessages(this.messages), tools: { diff --git a/src/content/docs/agents/examples/chat-agent.mdx b/src/content/docs/agents/examples/chat-agent.mdx index 1f44045af8d..32169801577 100644 --- a/src/content/docs/agents/examples/chat-agent.mdx +++ b/src/content/docs/agents/examples/chat-agent.mdx @@ -35,7 +35,7 @@ Select **"Hello World" Worker** when prompted. Then install the dependencies: ```sh cd chat-agent -npm install agents @cloudflare/ai-chat ai workers-ai-provider zod +npm install agents @cloudflare/ai-chat ai @ai-sdk/provider zod ``` ## 2. Configure Wrangler @@ -76,7 +76,7 @@ Create `src/server.ts`. This is where your agent lives: ```ts import { AIChatAgent } from "@cloudflare/ai-chat"; import { routeAgentRequest } from "agents"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { streamText, convertToModelMessages, @@ -88,10 +88,10 @@ import { z } from "zod"; export class ChatAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/meta/llama-4-scout-17b-16e-instruct"), + model: ai("@cf/meta/llama-4-scout-17b-16e-instruct"), system: "You are a helpful assistant. You can check the weather, " + "get the user's timezone, and run calculations.", diff --git a/src/content/docs/agents/examples/voice-agent.mdx b/src/content/docs/agents/examples/voice-agent.mdx index a20ef51b612..1a439fa4cd6 100644 --- a/src/content/docs/agents/examples/voice-agent.mdx +++ b/src/content/docs/agents/examples/voice-agent.mdx @@ -94,7 +94,7 @@ import { type VoiceTurnContext, } from "@cloudflare/voice"; import { streamText, tool, stepCountIs } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { z } from "zod"; const VoiceAgent = withVoice(Agent); @@ -104,10 +104,10 @@ export class MyVoiceAgent extends VoiceAgent { tts = new WorkersAITTS(this.env.AI); async onTurn(transcript: string, context: VoiceTurnContext) { - const workersAi = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersAi("@cf/moonshotai/kimi-k2.6"), + model: ai("@cf/moonshotai/kimi-k2.6"), system: "You are a helpful voice assistant. Keep responses concise — you are being spoken aloud.", messages: [ diff --git a/src/content/docs/agents/harnesses/think/configuration.mdx b/src/content/docs/agents/harnesses/think/configuration.mdx index ed1f3c95439..f6e15ba066c 100644 --- a/src/content/docs/agents/harnesses/think/configuration.mdx +++ b/src/content/docs/agents/harnesses/think/configuration.mdx @@ -52,7 +52,7 @@ export class MyAgent extends Think { fast: "@cf/moonshotai/kimi-k2.6", capable: "@cf/meta/llama-4-scout-17b-16e-instruct", }; - return createWorkersAI({ binding: this.env.AI })(models[tier]); + return createAI({ binding: this.env.AI })(models[tier]); } } ``` diff --git a/src/content/docs/agents/harnesses/think/getting-started.mdx b/src/content/docs/agents/harnesses/think/getting-started.mdx index 428046a1c87..065acce96db 100644 --- a/src/content/docs/agents/harnesses/think/getting-started.mdx +++ b/src/content/docs/agents/harnesses/think/getting-started.mdx @@ -37,7 +37,7 @@ npm init -y Install dependencies: ```sh -npm install @cloudflare/think @cloudflare/computer @cloudflare/ai-chat agents ai zod workers-ai-provider react react-dom +npm install @cloudflare/think @cloudflare/computer @cloudflare/ai-chat agents ai zod @ai-sdk/provider react react-dom npm install -D wrangler @cloudflare/vite-plugin @cloudflare/workers-types @vitejs/plugin-react @tailwindcss/vite tailwindcss typescript vite ``` @@ -101,12 +101,12 @@ Create `src/server.ts`: ```ts import { Think } from "@cloudflare/think"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { routeAgentRequest } from "agents"; export class MyAgent extends Think { getModel() { - return createWorkersAI({ binding: this.env.AI })( + return createAI({ binding: this.env.AI })( "@cf/moonshotai/kimi-k2.6", ); } @@ -226,7 +226,7 @@ Override `configureSession` to give the model writable memory that survives rest ```ts export class MyAgent extends Think { getModel(): LanguageModel { - return createWorkersAI({ binding: this.env.AI })( + return createAI({ binding: this.env.AI })( "@cf/moonshotai/kimi-k2.6", ); } diff --git a/src/content/docs/agents/harnesses/think/index.mdx b/src/content/docs/agents/harnesses/think/index.mdx index e11a8dbfe35..4fa40869ad9 100644 --- a/src/content/docs/agents/harnesses/think/index.mdx +++ b/src/content/docs/agents/harnesses/think/index.mdx @@ -32,7 +32,7 @@ If this is your first agent, start with the [Getting started tutorial](/agents/h ### Install - + Think supports AI SDK v6 and v7. Use `ai@^6` with `@ai-sdk/react@^3`, or use `ai@^7` with `@ai-sdk/react@^4`. Keep the AI SDK packages on matching major versions throughout your project. @@ -42,12 +42,12 @@ Think supports AI SDK v6 and v7. Use `ai@^6` with `@ai-sdk/react@^3`, or use `ai ```ts import { Think } from "@cloudflare/think"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { routeAgentRequest } from "agents"; export class MyAgent extends Think { getModel() { - return createWorkersAI({ binding: this.env.AI })( + return createAI({ binding: this.env.AI })( "@cf/moonshotai/kimi-k2.6", ); } diff --git a/src/content/docs/agents/model-context-protocol/apis/client-api.mdx b/src/content/docs/agents/model-context-protocol/apis/client-api.mdx index 2b061bc435e..097dccef32a 100644 --- a/src/content/docs/agents/model-context-protocol/apis/client-api.mdx +++ b/src/content/docs/agents/model-context-protocol/apis/client-api.mdx @@ -349,13 +349,13 @@ To use MCP tools with the AI SDK, use `this.mcp.getAITools()` which converts MCP ```ts import { generateText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class MyAgent extends Agent { async onRequest(request: Request) { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const response = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: "What's the weather in San Francisco?", tools: this.mcp.getAITools(), }); diff --git a/src/content/docs/agents/runtime/communication/http-sse.mdx b/src/content/docs/agents/runtime/communication/http-sse.mdx index cda13eb8cda..860023ce43c 100644 --- a/src/content/docs/agents/runtime/communication/http-sse.mdx +++ b/src/content/docs/agents/runtime/communication/http-sse.mdx @@ -118,7 +118,7 @@ The [AI SDK](https://ai-sdk.dev/) provides built-in SSE streaming: ```ts import { Agent } from "agents"; import { streamText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; interface Env { AI: Ai; @@ -128,10 +128,10 @@ export class ChatAgent extends Agent { async onRequest(request: Request): Promise { const { prompt } = await request.json<{ prompt: string }>(); - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: prompt, }); diff --git a/src/content/docs/agents/runtime/operations/using-ai-models.mdx b/src/content/docs/agents/runtime/operations/using-ai-models.mdx index 13a93257a05..9e5d8ff5066 100644 --- a/src/content/docs/agents/runtime/operations/using-ai-models.mdx +++ b/src/content/docs/agents/runtime/operations/using-ai-models.mdx @@ -39,7 +39,7 @@ Modern reasoning models can take some time to both generate a response _and_ str ```ts import { Agent } from "agents"; import { streamText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; interface Env { AI: Ai; @@ -57,9 +57,9 @@ export class MyAgent extends Agent { async queryReasoningModel(connection: Connection, userPrompt: string) { try { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: userPrompt, }); @@ -183,16 +183,16 @@ Visit the [AI Gateway documentation](/ai-gateway/) to learn how to configure a g ## AI SDK -The [AI SDK](https://ai-sdk.dev/docs/introduction) provides a unified API for text generation, tool calling, structured responses, and more. It works with any provider that has an AI SDK adapter, including Workers AI via [`workers-ai-provider`](https://www.npmjs.com/package/workers-ai-provider). +The [AI SDK](https://ai-sdk.dev/docs/introduction) provides a unified API for text generation, tool calling, structured responses, and more. It works with any provider that has an AI SDK adapter, including Workers AI and AI Gateway through [`createAI`](/agents/models/ai-sdk/) from `agents/models/ai-sdk`. - + ```ts import { Agent } from "agents"; import { generateText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; interface Env { AI: Ai; @@ -200,9 +200,9 @@ interface Env { export class MyAgent extends Agent { async onRequest(request: Request): Promise { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const { text } = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: "Build me an AI agent on Cloudflare Workers", }); diff --git a/src/content/docs/agents/tools/browser.mdx b/src/content/docs/agents/tools/browser.mdx index 8bc6f0d7305..04d906b59e8 100644 --- a/src/content/docs/agents/tools/browser.mdx +++ b/src/content/docs/agents/tools/browser.mdx @@ -344,11 +344,11 @@ Create browser tools with the Browser Run and Worker Loader bindings, then pass import { AIChatAgent } from "@cloudflare/ai-chat"; import { createBrowserTools } from "agents/browser/ai"; import { streamText, convertToModelMessages, stepCountIs } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class BrowserAgent extends AIChatAgent { async onChatMessage() { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const browserTools = createBrowserTools({ ctx: this.ctx, browser: this.env.BROWSER, @@ -356,7 +356,7 @@ export class BrowserAgent extends AIChatAgent { }); const result = streamText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), system: "You can inspect web pages with browser tools.", messages: await convertToModelMessages(this.messages), tools: browserTools, diff --git a/src/content/docs/agents/tools/codemode/browser.mdx b/src/content/docs/agents/tools/codemode/browser.mdx index 6429e787f97..94d293b855a 100644 --- a/src/content/docs/agents/tools/codemode/browser.mdx +++ b/src/content/docs/agents/tools/codemode/browser.mdx @@ -157,7 +157,7 @@ The browser creates the Code Mode tool and registers it as a dynamic client tool ```ts import { AIChatAgent, createToolsFromClientSchemas } from "@cloudflare/ai-chat"; import { convertToModelMessages, stepCountIs, streamText } from "ai"; - import { createWorkersAI } from "workers-ai-provider"; + import { createAI } from "agents/models/ai-sdk"; export class BrowserCodemode extends AIChatAgent { async onChatMessage( @@ -166,10 +166,10 @@ The browser creates the Code Mode tool and registers it as a dynamic client tool clientTools?: Parameters[0]; } ) { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/moonshotai/kimi-k2.7-code"), + model: ai("@cf/moonshotai/kimi-k2.7-code"), system: "Use the codemode tool to write JavaScript that calls browser-provided tools.", messages: await convertToModelMessages(this.messages), diff --git a/src/content/docs/agents/tools/filesystem.mdx b/src/content/docs/agents/tools/filesystem.mdx index f8a1cf1d3e7..9e6471eca72 100644 --- a/src/content/docs/agents/tools/filesystem.mdx +++ b/src/content/docs/agents/tools/filesystem.mdx @@ -175,14 +175,14 @@ import { Workspace } from "@cloudflare/computer"; import { createAITools } from "@cloudflare/computer/tools/ai-sdk"; import { generateText, stepCountIs } from "ai"; import { DurableObject } from "cloudflare:workers"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class WriterAgent extends DurableObject { workspace = new Workspace({ storage: this.ctx.storage }); async write(prompt: string) { const { text } = await generateText({ - model: createWorkersAI({ binding: this.env.AI })( + model: createAI({ binding: this.env.AI })( "@cf/moonshotai/kimi-k2.6", ), system: diff --git a/src/content/docs/agents/tools/mcp.mdx b/src/content/docs/agents/tools/mcp.mdx index 5d73ff761b0..9f9e9904206 100644 --- a/src/content/docs/agents/tools/mcp.mdx +++ b/src/content/docs/agents/tools/mcp.mdx @@ -30,7 +30,7 @@ Call `addMcpServer()` to connect to a remote MCP server, then pass `this.mcp.get ```ts import { Agent } from "agents"; import { generateText } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; export class ToolAgent extends Agent { async onStart() { @@ -38,10 +38,10 @@ export class ToolAgent extends Agent { } async onRequest(request: Request) { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const response = await generateText({ - model: workersai("@cf/zai-org/glm-4.7-flash"), + model: ai("@cf/zai-org/glm-4.7-flash"), prompt: "Use available tools to summarize the latest issue activity.", tools: this.mcp.getAITools(), }); diff --git a/src/content/docs/agents/tools/sandbox.mdx b/src/content/docs/agents/tools/sandbox.mdx index f6c5cecf49d..7e4b17b3af0 100644 --- a/src/content/docs/agents/tools/sandbox.mdx +++ b/src/content/docs/agents/tools/sandbox.mdx @@ -39,7 +39,7 @@ The following [Think](/agents/harnesses/think/) agent gives the model one tool, import { Think } from "@cloudflare/think"; import { routeAgentRequest } from "agents"; import { tool } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { z } from "zod"; const MAX_OUTPUT_CHARS = 10_000; @@ -59,7 +59,7 @@ export class CodeAgent extends Think { } getModel() { - return createWorkersAI({ binding: this.env.AI })( + return createAI({ binding: this.env.AI })( "@cf/moonshotai/kimi-k2.6", ); } diff --git a/src/content/docs/ai-gateway/integrations/vercel-ai-sdk.mdx b/src/content/docs/ai-gateway/integrations/vercel-ai-sdk.mdx index 41463ad0e53..2bbf8f94357 100644 --- a/src/content/docs/ai-gateway/integrations/vercel-ai-sdk.mdx +++ b/src/content/docs/ai-gateway/integrations/vercel-ai-sdk.mdx @@ -1,6 +1,6 @@ --- title: Vercel AI SDK -description: Route Vercel AI SDK requests through AI Gateway using the ai-gateway-provider package. +description: Call AI Gateway from the AI SDK with createAI from agents/models/ai-sdk. pcx_content_type: configuration sidebar: order: 2 @@ -8,60 +8,130 @@ products: - ai-gateway --- -import { Details, Tabs, TabItem, TypeScriptExample } from "~/components"; -import CodeSnippets from "~/components/ai-gateway/code-examples.astro"; +import { + PackageManagers, + TypeScriptExample, + WranglerConfig, +} from "~/components"; -The [Vercel AI SDK](https://sdk.vercel.ai/) is a TypeScript library for building AI applications. The SDK supports many different AI providers, tools for streaming completions, and more. -To use Cloudflare AI Gateway with Vercel AI SDK, you will need to use the `ai-gateway-provider` package. +To call AI Gateway from the [AI SDK](https://ai-sdk.dev/), use `createAI` from `agents/models/ai-sdk`. It calls models from the [model catalog](/ai/models/), [dynamic routes](/ai-gateway/features/dynamic-routing/), and a vendor's own AI SDK models through your gateway. This page covers the AI Gateway features. For the full reference, refer to [AI SDK](/agents/models/ai-sdk/). + +The `ai-gateway-provider` package is deprecated. Use `createAI` instead. ## Installation -```bash -npm install ai-gateway-provider -``` +Install the Agents SDK and the AI SDK. `createAI` needs `ai@^7` and `@ai-sdk/provider@^4`: + + -## Examples +In a Worker, add the `AI` binding to your Wrangler configuration: - + -### AI binding with third-party models +```jsonc +{ + "ai": { + "binding": "AI", + }, +} +``` -If you are already using the [`workers-ai-provider`](https://www.npmjs.com/package/workers-ai-provider) package, you can route requests through AI Gateway to call third-party models without needing separate provider SDKs. Pass a `gateway` option with your gateway ID to `createWorkersAI`: + + +## Call a catalog model + +Pass an `/` id from the [model catalog](/ai/models/). You do not need a vendor package or a vendor key. AI Gateway bills the request through [Unified Billing](/ai-gateway/features/unified-billing/), or uses a [key stored on the gateway](/ai-gateway/configuration/bring-your-own-keys/): ```ts -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { streamText } from "ai"; export default { - async fetch(request, env) { - const workersai = createWorkersAI({ - binding: env.AI, - gateway: { id: "my-gateway" }, - }); + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI, id: "my-gateway" }); const result = streamText({ - model: workersai("openai/gpt-4o"), + model: ai("anthropic/claude-opus-5.5"), messages: [{ role: "user", content: "Write a short story" }], }); return result.toTextStreamResponse(); }, -} satisfies ExportedHandler; +}; ``` -This works with any [supported provider and model](/ai/models/) available through AI Gateway. +Use the catalog's spelling of the id, such as `anthropic/claude-opus-5.5` or `openai/gpt-5.5`. `id` sets the gateway and defaults to `default`. A [dynamic route](/ai-gateway/features/dynamic-routing/) on that gateway is `dynamic/`, for example `ai("dynamic/support")`. + +## Call a vendor's model object + +Pass a model from the vendor's own AI SDK package. The request goes unchanged to the vendor's [provider-native endpoint](/ai-gateway/usage/providers/) on your gateway. AI Gateway holds the credential, so the vendor's `apiKey` is a placeholder: + +```ts +import { createAnthropic } from "@ai-sdk/anthropic"; +import { createAI } from "agents/models/ai-sdk"; + +const ai = createAI({ binding: env.AI, id: "my-gateway" }); +const anthropic = createAnthropic({ apiKey: "cloudflare" }); + +const model = ai(anthropic("claude-opus-5-5")); +``` + +Use the vendor's own model id here, such as `claude-opus-5-5`. To send a key you hold instead, refer to [Unified billing and BYOK](/agents/models/ai-sdk/#unified-billing-and-byok). + +## Fall back to another model + +`fallback` lists models to try in order if the primary model fails before it produces any output. Mix catalog ids, dynamic routes, Workers AI ids, and model objects: -### Fallback Providers +```ts +const model = ai(anthropic("claude-opus-5-5"), { + fallback: ["openai/gpt-5.5", "@cf/zai-org/glm-4.7-flash"], +}); +``` + +## Set gateway options + +Set [caching](/ai-gateway/features/caching/), logging, metadata, timeouts, and retries on the provider, on a model, or per call under `providerOptions.cloudflare`: + +```ts +const ai = createAI({ + binding: env.AI, + id: "my-gateway", + cacheTtl: 60, + metadata: { app: "support-bot" }, +}); + +await generateText({ + model: ai("openai/gpt-5.5"), + prompt: "Summarize this ticket", + providerOptions: { + cloudflare: { skipCache: true, metadata: { userId: "u_123" } }, + }, +}); +``` + +For every option, refer to [Set options](/agents/models/ai-sdk/#set-options). + +## Use an account id and API token -To specify model or provider fallbacks to handle request failures and ensure reliability, you can pass an array of models to the `model` option. +To use a gateway in another account, or to call from outside a Worker, pass `accountId` and `apiToken` instead of `binding`: -```js title="" -const { text } = await generateText({ - model: aigateway([openai.chat("gpt-5.1"), anthropic("claude-sonnet-4-5")]), - prompt: "Write a vegetarian lasagna recipe for 4 people.", +```ts +const ai = createAI({ + accountId: "", + apiToken: env.CLOUDFLARE_API_TOKEN, + gateway: "my-gateway", }); ``` + +The call forms and options are the same. [Create an API token](/fundamentals/api/get-started/create-token/) with the **Workers AI Read** permission for model ids and **AI Gateway Run** for model objects. + +## Related resources + +- [AI SDK provider reference](/agents/models/ai-sdk/) +- [Model catalog](/ai/models/) +- [Dynamic routing](/ai-gateway/features/dynamic-routing/) +- [Unified Billing](/ai-gateway/features/unified-billing/) diff --git a/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx b/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx index 4aa95cb2528..1a4c84fd5df 100644 --- a/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx +++ b/src/content/docs/ai-search/agent-sdks/agents-sdk.mdx @@ -53,9 +53,9 @@ cd ai-search-agent ## 2. Install the Agents SDK packages -Install the Agents SDK, the AI SDK, and the Workers AI provider: +Install the Agents SDK and the AI SDK. The example calls Workers AI with [`createAI`](/agents/models/ai-sdk/) from `agents/models/ai-sdk`: - + ## 3. Bind your Worker to AI Search @@ -100,7 +100,7 @@ Create `src/server.ts`. New AI Search instances use [hybrid search](/ai-search/c ```ts import { AIChatAgent } from "@cloudflare/ai-chat"; import { routeAgentRequest } from "agents"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { streamText, convertToModelMessages, tool, stepCountIs } from "ai"; import { z } from "zod"; @@ -149,10 +149,10 @@ export class SearchAgent extends AIChatAgent { // The agent exposes AI Search's search() as a tool instead of using AI // Search's own chat model, so this model must support function calling to // decide when to search and drive the tool calls. - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = streamText({ - model: workersai("@cf/zai-org/glm-5.2"), + model: ai("@cf/zai-org/glm-5.2"), system: "You are a support assistant. Use search_knowledge_base to find " + "relevant content before answering, and cite what you use.", diff --git a/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx b/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx index 6500558616f..2059cb136fd 100644 --- a/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx +++ b/src/content/docs/ai-search/how-to/talk-to-your-knowledge-base.mdx @@ -123,7 +123,7 @@ import { type VoiceTurnContext, } from "@cloudflare/voice"; import { generateText, tool, stepCountIs } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { z } from "zod"; const VoiceAgent = withVoice(Agent); @@ -137,11 +137,11 @@ export class TalkToDocs extends VoiceAgent { // the reply, calling AI Search as a retrieval tool when it needs facts from // your knowledge base. async onTurn(transcript: string, context: VoiceTurnContext) { - const workersai = createWorkersAI({ binding: this.env.AI }); + const ai = createAI({ binding: this.env.AI }); const result = await generateText({ // Use a Workers AI model that supports function calling. - model: workersai("@cf/zai-org/glm-5.2"), + model: ai("@cf/zai-org/glm-5.2"), system: "You are a helpful voice assistant that answers from a Cloudflare AI Search knowledge base. " + "For questions about the product, call the searchKnowledgeBase tool first and answer using the results. " + diff --git a/src/content/docs/sandbox/get-started/build-an-ai-code-interpreter.mdx b/src/content/docs/sandbox/get-started/build-an-ai-code-interpreter.mdx index e62d604eb3a..2688cc57b1d 100644 --- a/src/content/docs/sandbox/get-started/build-an-ai-code-interpreter.mdx +++ b/src/content/docs/sandbox/get-started/build-an-ai-code-interpreter.mdx @@ -65,9 +65,9 @@ You will learn how to: cd sandbox-code-interpreter ``` -3. Install the [AI SDK](https://ai-sdk.dev/), the [Workers AI provider](/workers-ai/configuration/ai-sdk/), and [Zod](https://zod.dev/): +3. Install the [Agents SDK](/agents/), the [AI SDK](https://ai-sdk.dev/), and [Zod](https://zod.dev/). The Worker calls Workers AI with [`createAI`](/agents/models/ai-sdk/) from `agents/models/ai-sdk`: - + 4. Replace `wrangler.jsonc` to add a Workers AI binding and a Worker Loader binding: @@ -192,7 +192,7 @@ Replace `src/index.ts`. The Worker gives the model a `runJavaScript` tool and re ```ts import { generateText, isStepCount, tool } from "ai"; -import { createWorkersAI } from "workers-ai-provider"; +import { createAI } from "agents/models/ai-sdk"; import { z } from "zod"; import { runJavaScript, type Output } from "./sandbox"; @@ -223,12 +223,12 @@ export default { ); } - const workersai = createWorkersAI({ binding: env.AI }); + const ai = createAI({ binding: env.AI }); const runs: Run[] = []; try { const result = await generateText({ - model: workersai(model), + model: ai(model), instructions, prompt: body.data.question, tools: { diff --git a/src/content/docs/sandbox/sdk/tutorials/ai-code-executor.mdx b/src/content/docs/sandbox/sdk/tutorials/ai-code-executor.mdx index c37b20bc743..21edb1077b0 100644 --- a/src/content/docs/sandbox/sdk/tutorials/ai-code-executor.mdx +++ b/src/content/docs/sandbox/sdk/tutorials/ai-code-executor.mdx @@ -250,4 +250,4 @@ You created an AI code execution system that: - [Anthropic Claude documentation](https://docs.anthropic.com/) - [Workers AI](/workers-ai/) - Use Cloudflare's built-in models -- [workers-ai-provider package](https://github.com/cloudflare/ai/tree/main/packages/workers-ai-provider) - Official Workers AI integration +- [AI SDK provider](/agents/models/ai-sdk/) - Call Workers AI and AI Gateway models from the AI SDK diff --git a/src/content/docs/workers-ai/configuration/ai-sdk.mdx b/src/content/docs/workers-ai/configuration/ai-sdk.mdx index b801f722eff..9460c961e5c 100644 --- a/src/content/docs/workers-ai/configuration/ai-sdk.mdx +++ b/src/content/docs/workers-ai/configuration/ai-sdk.mdx @@ -1,129 +1,194 @@ --- pcx_content_type: configuration title: Vercel AI SDK -description: Use Workers AI with the Vercel AI SDK for streaming text generation, tool calls, and structured output. +description: Use Workers AI models with the AI SDK through createAI from agents/models/ai-sdk. sidebar: order: 3 products: - workers-ai --- -import { PackageManagers } from "~/components"; +import { + PackageManagers, + TypeScriptExample, + WranglerConfig, +} from "~/components"; -Workers AI can be used with the [Vercel AI SDK](https://sdk.vercel.ai/) for JavaScript and TypeScript codebases. +To use Workers AI with the [AI SDK](https://ai-sdk.dev/), use `createAI` from `agents/models/ai-sdk`. This page covers Workers AI models. For AI Gateway catalog models, fallback, gateway options, and the full option reference, refer to [AI SDK](/agents/models/ai-sdk/). + +The `workers-ai-provider` package is deprecated. Use `createAI` instead. ## Setup -Install the [`workers-ai-provider` provider](https://sdk.vercel.ai/providers/community-providers/cloudflare-workers-ai): +Install the Agents SDK and the AI SDK. `createAI` needs `ai@^7` and `@ai-sdk/provider@^4`: + + - +Add the `AI` binding to your Wrangler configuration: -Then, add an AI binding in your Workers project Wrangler file: + -```toml -[ai] -binding = "AI" +```jsonc +{ + "ai": { + "binding": "AI", + }, +} ``` -## Models + -The AI SDK can be configured to work with [any AI model](/workers-ai/models/). +## Choose a model -```js -import { createWorkersAI } from "workers-ai-provider"; +Pass the id of any [Workers AI model](/workers-ai/models/). Model ids start with `@cf/` and autocomplete from `@cloudflare/workers-types`: -const workersai = createWorkersAI({ binding: env.AI }); +```ts +import { createAI } from "agents/models/ai-sdk"; -// Choose any model: https://developers.cloudflare.com/workers-ai/models/ -const model = workersai("@cf/meta/llama-3.1-8b-instruct", {}); -``` +const ai = createAI({ binding: env.AI }); -## Generate Text +const model = ai("@cf/zai-org/glm-4.7-flash"); +``` -Once you have selected your model, you can generate text from a given prompt. +## Generate text -```js -import { createWorkersAI } from 'workers-ai-provider'; -import { generateText } from 'ai'; + -type Env = { - AI: Ai; -}; +```ts +import { createAI } from "agents/models/ai-sdk"; +import { generateText } from "ai"; export default { - async fetch(_: Request, env: Env) { - const workersai = createWorkersAI({ binding: env.AI }); - const result = await generateText({ - model: workersai('@cf/meta/llama-2-7b-chat-int8'), - prompt: 'Write a 50-word essay about hello world.', - }); - - return new Response(result.text); - }, + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI }); + + const { text } = await generateText({ + model: ai("@cf/zai-org/glm-4.7-flash"), + prompt: "Write a 50-word essay about hello world.", + }); + + return new Response(text); + }, }; ``` -## Stream Text + -For longer responses, consider streaming responses to provide as the generation completes. +## Stream text -```js -import { createWorkersAI } from 'workers-ai-provider'; -import { streamText } from 'ai'; +For longer responses, stream the output as the model generates it: -type Env = { - AI: Ai; -}; + + +```ts +import { createAI } from "agents/models/ai-sdk"; +import { streamText } from "ai"; export default { - async fetch(_: Request, env: Env) { - const workersai = createWorkersAI({ binding: env.AI }); - const result = streamText({ - model: workersai('@cf/meta/llama-2-7b-chat-int8'), - prompt: 'Write a 50-word essay about hello world.', - }); - - return result.toTextStreamResponse({ - headers: { - // add these headers to ensure that the - // response is chunked and streamed - 'Content-Type': 'text/x-unknown', - 'content-encoding': 'identity', - 'transfer-encoding': 'chunked', - }, - }); - }, + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI }); + + const result = streamText({ + model: ai("@cf/zai-org/glm-4.7-flash"), + prompt: "Write a 50-word essay about hello world.", + }); + + return result.toTextStreamResponse(); + }, }; ``` -## Generate Structured Objects + -You can provide a Zod schema to generate a structured JSON response. +## Generate structured output -```js -import { createWorkersAI } from 'workers-ai-provider'; -import { generateObject } from 'ai'; -import { z } from 'zod'; +Pass a Zod schema to get a typed JSON response: -type Env = { - AI: Ai; -}; + + +```ts +import { createAI } from "agents/models/ai-sdk"; +import { generateText, Output } from "ai"; +import { z } from "zod"; export default { - async fetch(_: Request, env: Env) { - const workersai = createWorkersAI({ binding: env.AI }); - const result = await generateObject({ - model: workersai('@cf/meta/llama-3.1-8b-instruct'), - prompt: 'Generate a Lasagna recipe', - schema: z.object({ - recipe: z.object({ - ingredients: z.array(z.string()), - description: z.string(), - }), - }), - }); - - return Response.json(result.object); - }, + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI }); + + const { output } = await generateText({ + model: ai("@cf/zai-org/glm-4.7-flash"), + prompt: "Generate a lasagna recipe.", + output: Output.object({ + schema: z.object({ + recipe: z.object({ + ingredients: z.array(z.string()), + description: z.string(), + }), + }), + }), + }); + + return Response.json(output); + }, }; ``` + + + +## Use other model types + +Embeddings, images, speech, transcription, and reranking come from the same provider: + +```ts +import { + embedMany, + generateImage, + generateSpeech, + rerank, + transcribe, +} from "ai"; + +const { embeddings } = await embedMany({ + model: ai.embedding("@cf/baai/bge-base-en-v1.5"), + values: ["hello", "world"], +}); + +const { image } = await generateImage({ + model: ai.image("@cf/black-forest-labs/flux-1-schnell"), + prompt: "A cyberpunk lizard", +}); + +const { audio } = await generateSpeech({ + model: ai.speech("@cf/deepgram/aura-1"), + text: "Hello from Cloudflare.", +}); + +const { text } = await transcribe({ + model: ai.transcription("@cf/openai/whisper-large-v3-turbo"), + audio: audio.uint8Array, +}); + +const { rerankedDocuments } = await rerank({ + model: ai.reranking("@cf/baai/bge-reranker-base"), + documents: ["a cyberpunk lizard", "a rainy Tuesday"], + query: "Which one is cooler?", +}); +``` + +For model-specific settings, refer to [Use other modalities](/agents/models/ai-sdk/#use-other-modalities). + +## Use AI Gateway + +Requests go through [AI Gateway](/ai-gateway/). The gateway defaults to `default`. To use another gateway, set its `id`: + +```ts +const ai = createAI({ binding: env.AI, id: "my-gateway" }); +``` + +The same provider also calls models from the [AI Gateway catalog](/ai/models/), such as `anthropic/claude-opus-5.5`. Refer to [AI SDK](/agents/models/ai-sdk/) for catalog models, caching, fallback, and the other gateway options. + +## Related resources + +- [AI SDK provider reference](/agents/models/ai-sdk/) +- [Workers AI models](/workers-ai/models/) +- [AI SDK documentation](https://ai-sdk.dev/docs/introduction) From 5505cbd5a7116230d6e4a183aca3ece0925e7f88 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 21:55:21 +0100 Subject: [PATCH 07/13] [AI Gateway] createAI in the chat completion AI SDK samples; current model ids --- .../ai-gateway/code-example-selector.tsx | 63 ++++----- src/components/ai-gateway/code-examples.astro | 133 +++++++++--------- 2 files changed, 91 insertions(+), 105 deletions(-) diff --git a/src/components/ai-gateway/code-example-selector.tsx b/src/components/ai-gateway/code-example-selector.tsx index 9ab58fd68f7..c46dc8b31f8 100644 --- a/src/components/ai-gateway/code-example-selector.tsx +++ b/src/components/ai-gateway/code-example-selector.tsx @@ -382,58 +382,54 @@ export function CodeExample({ export const modelOptions = [ { - model: "gpt-5.2", + model: "gpt-6-luna", provider: "openai", aiSDK: { + id: "openai/gpt-6-luna", + pkg: "@ai-sdk/openai", providerFactory: "createOpenAI", - providerUsage: "openai.chat", provider: "openai", }, }, { - model: "claude-4-5-sonnet", + model: "claude-opus-5-5", provider: "anthropic", aiSDK: { + id: "anthropic/claude-opus-5.5", + pkg: "@ai-sdk/anthropic", providerFactory: "createAnthropic", - providerUsage: "anthropic", provider: "anthropic", }, }, { - model: "gemini-2.5-pro", + model: "gemini-4-argon", provider: "google", aiSDK: { - providerFactory: "createGoogle", - providerUsage: "google", + id: "google/gemini-4-argon", + pkg: "@ai-sdk/google", + providerFactory: "createGoogleGenerativeAI", provider: "google", }, }, { - model: "grok-4", + model: "grok-4.7", provider: "grok", aiSDK: { + id: "xai/grok-4.7", + pkg: "@ai-sdk/xai", providerFactory: "createXai", - providerUsage: "xai", provider: "xai", }, }, { model: "customer-support", provider: "dynamic", - aiSDK: { - providerFactory: "createUnified", - providerUsage: "unified", - provider: "unified", - }, + aiSDK: { id: "dynamic/customer-support" }, }, { model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast", provider: "workers-ai", - aiSDK: { - providerFactory: "createUnified", - providerUsage: "unified", - provider: "unified", - }, + aiSDK: { id: "@cf/meta/llama-3.3-70b-instruct-fp8-fast" }, }, ]; export const code = `import OpenAI from "openai"; @@ -450,38 +446,35 @@ const response = await client.chat.completions.create({ messages: [{ role: "user", content: "Hello, world!" }], });`; -export const aiSDKUnifiedCode = `import { createAiGateway } from 'ai-gateway-provider'; -import { createUnified } from 'ai-gateway-provider/providers/unified'; +export const aiSDKUnifiedCode = `import { createAI } from "agents/models/ai-sdk"; import { generateText } from "ai"; -const aigateway = createAiGateway({ +const ai = createAI({ accountId: "{CLOUDFLARE_ACCOUNT_ID}", - gateway: '{GATEWAY_NAME}', - apiKey: '{CF_AIG_TOKEN}', + apiToken: "{CF_API_TOKEN}", + gateway: "{GATEWAY_NAME}", }); -const unified = createUnified({apikey}); - const { text } = await generateText({ - model: aigateway(unified('{provider}/{model}')), - prompt: 'What is Cloudflare?', + model: ai("{id}"), + prompt: "What is Cloudflare?", });`; -export const aiSDKNativeCode = `import { createAiGateway } from 'ai-gateway-provider'; -import { {providerFactory} } from 'ai-gateway-provider/providers/{provider}'; +export const aiSDKNativeCode = `import { createAI } from "agents/models/ai-sdk"; +import { {providerFactory} } from "{pkg}"; import { generateText } from "ai"; -const aigateway = createAiGateway({ +const ai = createAI({ accountId: "{CLOUDFLARE_ACCOUNT_ID}", - gateway: '{GATEWAY_NAME}', - apiKey: '{CF_AIG_TOKEN}', + apiToken: "{CF_API_TOKEN}", + gateway: "{GATEWAY_NAME}", }); const {provider} = {providerFactory}({apikey}); const { text } = await generateText({ - model: aigateway({providerUsage}('{model}')), - prompt: 'What is Cloudflare?', + model: ai({provider}("{model}"){vendorKey}), + prompt: "What is Cloudflare?", });`; export const curlCode = `curl -X POST https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/compat/chat/completions \\ diff --git a/src/components/ai-gateway/code-examples.astro b/src/components/ai-gateway/code-examples.astro index a67d754d6eb..52f366fe8b6 100644 --- a/src/components/ai-gateway/code-examples.astro +++ b/src/components/ai-gateway/code-examples.astro @@ -103,81 +103,74 @@ const { forceClient, forceAPI } = props.parse(Astro.props); ))} {(!forceClient || forceClient === "aisdk") && - modelOptions.map((option) => ( - - - - ))} - - {(!forceClient || forceClient === "aisdk") && - modelOptions.map((option) => ( - - - - ))} + modelOptions + .filter((option) => option.aiSDK.pkg !== undefined) + .flatMap((option) => + (["unified", "native"] as const).map((apiType) => ({ + option, + apiType, + })), + ) + .map(({ option, apiType }) => ( + + + + ))} {(!forceClient || forceClient === "aisdk") && - modelOptions.map((option) => ( - - - - ))} + modelOptions + .filter((option) => option.aiSDK.pkg !== undefined) + .map((option) => ( + + + + ))} {(!forceClient || forceClient === "curl") && modelOptions.map((option) => ( From a478924d67ee0d0aeb74abc6e793a1b1014b9494 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 22:12:51 +0100 Subject: [PATCH 08/13] [Workers AI, AI Gateway] Changelog leads with createAI --- ...rovider-ai-gateway-provider-deprecated.mdx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx index 30bc959491c..c1ef8ff5011 100644 --- a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx +++ b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx @@ -1,18 +1,19 @@ --- -title: workers-ai-provider and ai-gateway-provider are deprecated -description: The workers-ai-provider and ai-gateway-provider npm packages are deprecated. Use createAI from agents/models/ai-sdk, and ai-search-provider for AI Search. +title: createAI replaces workers-ai-provider and ai-gateway-provider +description: The Agents SDK has a new createAI function for the AI SDK and Pi AI that unifies workers-ai-provider and ai-gateway-provider. Both packages are now feature frozen and deprecated. products: - workers-ai - ai-gateway + - agents date: 2026-10-21 --- -The `workers-ai-provider` and `ai-gateway-provider` npm packages are deprecated. To call Workers AI and AI Gateway from the [AI SDK](https://ai-sdk.dev/), use [`createAI`](/agents/models/ai-sdk/) from `agents/models/ai-sdk`. For AI Search, use [`ai-search-provider`](/ai-search/agent-sdks/ai-sdk/). +The Agents SDK has a new `createAI` function for the [AI SDK](/agents/models/ai-sdk/) and [Pi AI](/agents/models/pi-ai/). It unifies what `workers-ai-provider` and `ai-gateway-provider` did: Workers AI models, AI Gateway catalog models, dynamic routes, and vendor models, through the `AI` binding or an account id and API token. -Existing code keeps working. Nothing changes at runtime. +`workers-ai-provider` and `ai-gateway-provider` are now feature frozen and marked as deprecated. Existing code keeps working. Migrate at your convenience. ```sh -npm install agents ai @ai-sdk/provider +npm i agents ``` ```ts @@ -23,11 +24,11 @@ const ai = createAI({ binding: env.AI }); const model = ai("@cf/zai-org/glm-4.7-flash"); ``` -| Before | After | -| ------------------------------------------------------- | -------------------------------------------------------- | -| `createWorkersAI({ binding })` | `createAI({ binding })` | -| `createWorkersAI({ accountId, apiKey })` | `createAI({ accountId, apiToken })` | -| `createAiGateway(...)` | `createAI(...)`, with `fallback` for a list of models | -| `createAISearch` from `workers-ai-provider` | `createAISearchNamespace` from `ai-search-provider` | +| Before | After | +| ------------------------------------------- | ----------------------------------------------------- | +| `createWorkersAI({ binding })` | `createAI({ binding })` | +| `createWorkersAI({ accountId, apiKey })` | `createAI({ accountId, apiToken })` | +| `createAiGateway(...)` | `createAI(...)`, with `fallback` for a list of models | +| `createAISearch` from `workers-ai-provider` | `createAISearchNamespace` from `ai-search-provider` | -For more information, refer to [AI SDK](/agents/models/ai-sdk/). +For AI Search, use [`ai-search-provider`](/ai-search/agent-sdks/ai-sdk/). For more information, refer to [Models](/agents/models/). From 8b02beac5d2bebe048140a343aa8e1e72f516a89 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 22:13:44 +0100 Subject: [PATCH 09/13] [Workers AI, AI Gateway] Changelog wording --- ...10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx index c1ef8ff5011..cfa6dfb4216 100644 --- a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx +++ b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx @@ -10,7 +10,7 @@ date: 2026-10-21 The Agents SDK has a new `createAI` function for the [AI SDK](/agents/models/ai-sdk/) and [Pi AI](/agents/models/pi-ai/). It unifies what `workers-ai-provider` and `ai-gateway-provider` did: Workers AI models, AI Gateway catalog models, dynamic routes, and vendor models, through the `AI` binding or an account id and API token. -`workers-ai-provider` and `ai-gateway-provider` are now feature frozen and marked as deprecated. Existing code keeps working. Migrate at your convenience. +`workers-ai-provider` and `ai-gateway-provider` are now feature frozen and marked as deprecated. Please migrate at your convenience. ```sh npm i agents From 75ea9b45f9edc766c99f604693c9192bbb6aa8fb Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 22:22:55 +0100 Subject: [PATCH 10/13] [Agents, Workers AI, AI Gateway] One changelog entry for createAI and the deprecation --- .../2026-10-20-createai-model-providers.mdx | 57 ----------------- .../changelog/agents/2026-10-21-createai.mdx | 61 +++++++++++++++++++ ...rovider-ai-gateway-provider-deprecated.mdx | 34 ----------- 3 files changed, 61 insertions(+), 91 deletions(-) delete mode 100644 src/content/changelog/agents/2026-10-20-createai-model-providers.mdx create mode 100644 src/content/changelog/agents/2026-10-21-createai.mdx delete mode 100644 src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx diff --git a/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx b/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx deleted file mode 100644 index 153ed8421e5..00000000000 --- a/src/content/changelog/agents/2026-10-20-createai-model-providers.mdx +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: createAI, one model provider for the AI SDK and Pi AI -description: The Agents SDK's createAI calls Workers AI, the AI Gateway catalog, dynamic routes, and vendor models through the AI binding or an account id and API token, from the AI SDK or Pi AI. It replaces workers-ai-provider and ai-gateway-provider. -products: - - agents - - workers -date: 2026-10-20 ---- - -import { TypeScriptExample } from "~/components"; - -The Agents SDK now ships [`createAI`](/agents/models/), one model provider for Workers AI and AI Gateway, over the Worker's `AI` binding or an account id and API token. There are two imports, with the same options: - -- `agents/models/ai-sdk` for the [AI SDK](/agents/models/ai-sdk/). It replaces `workers-ai-provider` and `ai-gateway-provider`. -- `agents/models/pi-ai` for [Pi AI](/agents/models/pi-ai/) and the [Pi harness](/agents/harnesses/pi/). It gives Pi AI the same support, and needs `@earendil-works/pi-ai` 1.3. - -The [OpenCode harness](/agents/harnesses/opencode/) uses the AI SDK provider through `agents/models/opencode`. - - - -```ts -import { createAI } from "agents/models/ai-sdk"; -import { streamText } from "ai"; - -export default { - async fetch(request: Request, env: Env) { - const ai = createAI({ binding: env.AI, id: "my-gateway" }); - - const result = streamText({ - model: ai("anthropic/claude-opus-5.5", { - fallback: ["@cf/zai-org/glm-4.7-flash"], - }), - prompt: "Write a haiku about Durable Objects", - }); - - return result.toTextStreamResponse(); - }, -}; -``` - - - -With `createAI` you get: - -- **Every model by id.** Workers AI `@cf/` ids, any model in the [AI Gateway catalog](/ai/models/) such as `anthropic/claude-opus-5.5`, and [dynamic routes](/ai-gateway/features/dynamic-routing/) as `dynamic/`. -- **Vendor model objects.** Pass a model from the vendor's own package, such as `ai(anthropic("claude-opus-5-5"))`. The request goes unchanged to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/). -- **The binding, or an account id and API token.** Inside a Worker, requests go through the `AI` binding. To use a [gateway in another account](/agents/models/ai-sdk/#use-an-account-id-and-api-token), or to call from outside a Worker, pass `accountId` and `apiToken` instead. -- **No vendor keys.** AI Gateway bills through [Unified Billing](/ai-gateway/features/unified-billing/) or a [stored key](/ai-gateway/configuration/bring-your-own-keys/). `byokAlias` picks a stored key, `zdr` sets Zero Data Retention, and `vendorKey: "request"` sends a key you hold. -- **Gateway options.** Caching, logging, metadata, timeouts, and retries on the provider, the model, or the call. -- **Fallback.** Try models in order if one fails before it answers. Mix ids and model objects. -- **Stream resume.** Through the binding, a catalog or dynamic route stream that drops mid-answer resumes, with nothing lost or repeated. -- **Gateway metadata and typed errors.** Each result carries the log id, cache status, run id, and the model that answered. Failures throw `CloudflareAIError`. -- **Other model types.** Embeddings, images, speech, transcription (Whisper and Deepgram Nova-3), and reranking from Workers AI in the AI SDK provider, and images in Pi AI. - -`createAI` does not use AI Gateway's deprecated [Universal Endpoint](/ai-gateway/usage/universal/). `workers-ai-provider` and `ai-gateway-provider` are deprecated in favor of `createAI`, and of `ai-search-provider` for AI Search. - -For more information, refer to [Models](/agents/models/). diff --git a/src/content/changelog/agents/2026-10-21-createai.mdx b/src/content/changelog/agents/2026-10-21-createai.mdx new file mode 100644 index 00000000000..b7d7cac1747 --- /dev/null +++ b/src/content/changelog/agents/2026-10-21-createai.mdx @@ -0,0 +1,61 @@ +--- +title: createAI replaces workers-ai-provider and ai-gateway-provider +description: The Agents SDK has a new createAI function for the AI SDK and Pi AI that unifies workers-ai-provider and ai-gateway-provider. Both packages are now feature frozen and deprecated. +products: + - agents + - workers-ai + - ai-gateway +date: 2026-10-21 +--- + +import { TypeScriptExample } from "~/components"; + +The Agents SDK has a new [`createAI`](/agents/models/) function for the [AI SDK](/agents/models/ai-sdk/) and [Pi AI](/agents/models/pi-ai/). It unifies what `workers-ai-provider` and `ai-gateway-provider` did: Workers AI models, AI Gateway catalog models, dynamic routes, and vendor models, through the `AI` binding or an account id and API token. + +`workers-ai-provider` and `ai-gateway-provider` are now feature frozen and marked as deprecated. Please migrate at your convenience. + +```sh +npm i agents +``` + + + +```ts +import { createAI } from "agents/models/ai-sdk"; +import { streamText } from "ai"; + +export default { + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI, id: "my-gateway" }); + + const result = streamText({ + model: ai("anthropic/claude-opus-5.5", { + fallback: ["@cf/zai-org/glm-4.7-flash"], + }), + prompt: "Write a haiku about Durable Objects", + }); + + return result.toTextStreamResponse(); + }, +}; +``` + + + +With `createAI` you get: + +- **Every model by id.** Workers AI `@cf/` ids, any model in the [AI Gateway catalog](/ai/models/), and [dynamic routes](/ai-gateway/features/dynamic-routing/) as `dynamic/`. +- **Vendor model objects.** Pass a model from the vendor's own package, such as `ai(anthropic("claude-opus-5-5"))`. It goes unchanged to AI Gateway's [provider-native endpoint](/ai-gateway/usage/providers/). +- **The binding, or an account id and API token.** Use a gateway in another account, or call from outside a Worker. +- **Gateway options, fallback, and BYOK.** Caching, logging, metadata, retries, `byokAlias`, `zdr`, and `vendorKey`. +- **Stream resume.** Through the binding, a catalog or dynamic route stream that drops mid-answer resumes. +- **Other model types.** Embeddings, images, speech, transcription, and reranking in the AI SDK provider, and images in Pi AI. + +| Before | After | +| ------------------------------------------- | ----------------------------------------------------- | +| `createWorkersAI({ binding })` | `createAI({ binding })` | +| `createWorkersAI({ accountId, apiKey })` | `createAI({ accountId, apiToken })` | +| `createAiGateway(...)` | `createAI(...)`, with `fallback` for a list of models | +| `createAISearch` from `workers-ai-provider` | `createAISearchNamespace` from `ai-search-provider` | + +For AI Search, use [`ai-search-provider`](/ai-search/agent-sdks/ai-sdk/). For more information, refer to [Models](/agents/models/). diff --git a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx b/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx deleted file mode 100644 index cfa6dfb4216..00000000000 --- a/src/content/changelog/workers-ai/2026-10-21-workers-ai-provider-ai-gateway-provider-deprecated.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: createAI replaces workers-ai-provider and ai-gateway-provider -description: The Agents SDK has a new createAI function for the AI SDK and Pi AI that unifies workers-ai-provider and ai-gateway-provider. Both packages are now feature frozen and deprecated. -products: - - workers-ai - - ai-gateway - - agents -date: 2026-10-21 ---- - -The Agents SDK has a new `createAI` function for the [AI SDK](/agents/models/ai-sdk/) and [Pi AI](/agents/models/pi-ai/). It unifies what `workers-ai-provider` and `ai-gateway-provider` did: Workers AI models, AI Gateway catalog models, dynamic routes, and vendor models, through the `AI` binding or an account id and API token. - -`workers-ai-provider` and `ai-gateway-provider` are now feature frozen and marked as deprecated. Please migrate at your convenience. - -```sh -npm i agents -``` - -```ts -import { createAI } from "agents/models/ai-sdk"; - -const ai = createAI({ binding: env.AI }); - -const model = ai("@cf/zai-org/glm-4.7-flash"); -``` - -| Before | After | -| ------------------------------------------- | ----------------------------------------------------- | -| `createWorkersAI({ binding })` | `createAI({ binding })` | -| `createWorkersAI({ accountId, apiKey })` | `createAI({ accountId, apiToken })` | -| `createAiGateway(...)` | `createAI(...)`, with `fallback` for a list of models | -| `createAISearch` from `workers-ai-provider` | `createAISearchNamespace` from `ai-search-provider` | - -For AI Search, use [`ai-search-provider`](/ai-search/agent-sdks/ai-sdk/). For more information, refer to [Models](/agents/models/). From ff41481c87a79cab5b173f0ab02ae95adec1f191 Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Tue, 20 Oct 2026 22:46:38 +0100 Subject: [PATCH 11/13] [Workers AI, AI Gateway] Pi AI pages for createAI --- .../docs/ai-gateway/integrations/pi-ai.mdx | 128 ++++++++++++++++++ .../docs/workers-ai/configuration/pi-ai.mdx | 123 +++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 src/content/docs/ai-gateway/integrations/pi-ai.mdx create mode 100644 src/content/docs/workers-ai/configuration/pi-ai.mdx diff --git a/src/content/docs/ai-gateway/integrations/pi-ai.mdx b/src/content/docs/ai-gateway/integrations/pi-ai.mdx new file mode 100644 index 00000000000..0a23ea2c473 --- /dev/null +++ b/src/content/docs/ai-gateway/integrations/pi-ai.mdx @@ -0,0 +1,128 @@ +--- +title: Pi AI +description: Call AI Gateway from Pi AI with createAI from agents/models/pi-ai. +pcx_content_type: configuration +sidebar: + order: 2 +products: + - ai-gateway +--- + +import { + PackageManagers, + TypeScriptExample, + WranglerConfig, +} from "~/components"; + +To call AI Gateway from [Pi AI](https://github.com/earendil-works/pi/tree/main/packages/ai), use `createAI` from `agents/models/pi-ai`. It calls models from the [model catalog](/ai/models/), [dynamic routes](/ai-gateway/features/dynamic-routing/), and Pi AI's own model objects through your gateway. This page covers the AI Gateway features. For the full reference, refer to [Pi AI](/agents/models/pi-ai/). + +## Installation + +Install the Agents SDK and Pi AI. `createAI` needs `@earendil-works/pi-ai@~1.3.0`: + + + +In a Worker, add the `AI` binding to your Wrangler configuration: + + + +```jsonc +{ + "ai": { + "binding": "AI", + }, +} +``` + + + +## Call a catalog model + +Pass a `/` from Pi AI's AI Gateway catalog. You do not need a vendor key. AI Gateway bills the request through [Unified Billing](/ai-gateway/features/unified-billing/), or uses a [key stored on the gateway](/ai-gateway/configuration/bring-your-own-keys/): + + + +```ts +import { createAI } from "agents/models/pi-ai"; + +export default { + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI, id: "my-gateway" }); + + const reply = await ai.complete(ai("anthropic/claude-opus-5.5"), { + messages: [ + { role: "user", content: "Write a short story", timestamp: Date.now() }, + ], + }); + + return Response.json(reply.content); + }, +}; +``` + + + +Use the catalog's spelling of the id, such as `anthropic/claude-opus-5.5` or `openai/gpt-5.5`. The request goes in the model's own format, Anthropic Messages or OpenAI Responses, so Pi AI's own parsers read the answer and thinking works as it does with the vendor. `id` sets the gateway and defaults to `default`. A dynamic route on that gateway is `dynamic/`, for example `ai("dynamic/support")`. + +## Call a Pi AI model object + +Pass any model from a Pi AI registry. The request goes to the vendor's [provider-native endpoint](/ai-gateway/usage/providers/) on your gateway, and AI Gateway supplies the credential: + +```ts +import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all"; +import { createAI } from "agents/models/pi-ai"; + +const ai = createAI({ binding: env.AI, id: "my-gateway" }); + +const model = ai(getBuiltinModel("anthropic", "claude-opus-5-5")); +``` + +A model object keeps the vendor's own id, such as `claude-opus-5-5`, and all of Pi AI's metadata for it. + +## Fall back to another model + +`fallback` lists models to try in order if the primary model fails before it produces any output. Mix catalog ids, dynamic routes, Workers AI ids, and model objects: + +```ts +const model = ai("anthropic/claude-opus-5.5", { + fallback: ["openai/gpt-5.5", "@cf/zai-org/glm-4.7-flash"], +}); +``` + +## Set gateway options + +Set [caching](/ai-gateway/features/caching/), logging, metadata, timeouts, and retries on `createAI`, on a model, or per call: + +```ts +const ai = createAI({ + binding: env.AI, + id: "my-gateway", + cacheTtl: 60, + metadata: { app: "support-bot" }, +}); + +const model = ai("openai/gpt-5.5", { byokAlias: "production" }); +``` + +For every option, refer to [Cache, tag, and time out requests](/agents/models/pi-ai/#cache-tag-and-time-out-requests). + +## Use an account id and API token + +To use a gateway in another account, or to call from outside a Worker, pass `accountId` and `apiToken` instead of `binding`: + +```ts +const ai = createAI({ + accountId: "", + apiToken: env.CLOUDFLARE_API_TOKEN, + gateway: "my-gateway", +}); +``` + +[Create an API token](/fundamentals/api/get-started/create-token/) with the **Workers AI Read** permission for model ids and **AI Gateway Run** for model objects. + +## Related resources + +- [Pi AI provider reference](/agents/models/pi-ai/) +- [Pi harness](/agents/harnesses/pi/) +- [Model catalog](/ai/models/) +- [Vercel AI SDK](/ai-gateway/integrations/vercel-ai-sdk/) diff --git a/src/content/docs/workers-ai/configuration/pi-ai.mdx b/src/content/docs/workers-ai/configuration/pi-ai.mdx new file mode 100644 index 00000000000..9fa672c0d7c --- /dev/null +++ b/src/content/docs/workers-ai/configuration/pi-ai.mdx @@ -0,0 +1,123 @@ +--- +title: Pi AI +description: Use Workers AI models with Pi AI through createAI from agents/models/pi-ai. +pcx_content_type: configuration +sidebar: + order: 3 +products: + - workers-ai +--- + +import { + PackageManagers, + TypeScriptExample, + WranglerConfig, +} from "~/components"; + +To use Workers AI with [Pi AI](https://github.com/earendil-works/pi/tree/main/packages/ai), use `createAI` from `agents/models/pi-ai`. It returns Pi AI `Model` objects, so Workers AI models work with Pi AI's `stream` and `complete`, with [Pi Durable](https://github.com/earendil-works/pi/tree/main/packages/durable), and with the [Pi harness](/agents/harnesses/pi/). This page covers Workers AI models. For AI Gateway catalog models, fallback, gateway options, and the full reference, refer to [Pi AI](/agents/models/pi-ai/). + +## Setup + +Install the Agents SDK and Pi AI. `createAI` needs `@earendil-works/pi-ai@~1.3.0`: + + + +Add the `AI` binding to your Wrangler configuration: + + + +```jsonc +{ + "ai": { + "binding": "AI", + }, +} +``` + + + +## Generate text + +Name a Workers AI model by its `@cf/` id. The provider carries Pi AI's entry points, so nothing is registered globally: + + + +```ts +import { createAI } from "agents/models/pi-ai"; + +export default { + async fetch(request: Request, env: Env) { + const ai = createAI({ binding: env.AI }); + + const reply = await ai.complete(ai("@cf/zai-org/glm-4.7-flash"), { + messages: [ + { role: "user", content: "What is Cloudflare?", timestamp: Date.now() }, + ], + }); + + // `content` holds text, thinking, and tool call parts. + const text = reply.content + .filter((part) => part.type === "text") + .map((part) => part.text) + .join(""); + + return Response.json({ text }); + }, +}; +``` + + + +## Stream text + +`ai.stream()` returns Pi AI's event stream. `ai.streamSimple()` and `ai.completeSimple()` take Pi AI's simple options, such as a `reasoning` level and `maxTokens`: + +```ts +const model = ai("@cf/zai-org/glm-4.7-flash"); +const context = { + messages: [{ role: "user", content: "Hi", timestamp: Date.now() }], +}; + +for await (const event of ai.stream(model, context)) { + // text_delta, thinking_delta, toolcall_*, done, error +} +``` + +A compatibility layer turns each Workers AI model's response into the shape Pi AI's parsers read, so tool calls and reasoning work the same across model families. + +## Set model details + +A Workers AI id that Pi AI does not list resolves with a 128,000 token context window, 8,192 output tokens, and zero cost. Pass the real values when you know them: + +```ts +const model = ai("@cf/zai-org/glm-4.7-flash", { + contextWindow: 131_072, + maxTokens: 16_384, +}); +``` + +## Generate images + +```ts +const images = ai.images("@cf/black-forest-labs/flux-1-schnell", { steps: 8 }); +const result = await ai.generateImages(images, { + input: [{ type: "text", text: "A red bicycle" }], +}); +``` + +## Use AI Gateway + +Every request goes through AI Gateway. Set the gateway with `id`, which defaults to `default`: + +```ts +const ai = createAI({ binding: env.AI, id: "my-gateway" }); +``` + +To call from outside a Worker, pass `accountId` and `apiToken` instead of `binding`. For gateway options, catalog models, and fallback, refer to [Pi AI](/agents/models/pi-ai/). + +## Related resources + +- [Pi AI provider reference](/agents/models/pi-ai/) +- [Pi harness](/agents/harnesses/pi/) +- [Workers AI models](/workers-ai/models/) +- [Vercel AI SDK](/workers-ai/configuration/ai-sdk/) From 64eab2071f8749f96f8d10f10d935ebcfce2942f Mon Sep 17 00:00:00 2001 From: Matt <77928207+mattzcarey@users.noreply.github.com> Date: Wed, 21 Oct 2026 08:27:10 +0100 Subject: [PATCH 12/13] Update src/content/changelog/agents/2026-10-21-createai.mdx --- src/content/changelog/agents/2026-10-21-createai.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/changelog/agents/2026-10-21-createai.mdx b/src/content/changelog/agents/2026-10-21-createai.mdx index b7d7cac1747..2f968c40a8c 100644 --- a/src/content/changelog/agents/2026-10-21-createai.mdx +++ b/src/content/changelog/agents/2026-10-21-createai.mdx @@ -1,6 +1,6 @@ --- title: createAI replaces workers-ai-provider and ai-gateway-provider -description: The Agents SDK has a new createAI function for the AI SDK and Pi AI that unifies workers-ai-provider and ai-gateway-provider. Both packages are now feature frozen and deprecated. +description: The Agents SDK has a new createAI function for the AI SDK and Pi AI that unifies workers-ai-provider and ai-gateway-provider. Both packages are now feature frozen and marked as deprecated. products: - agents - workers-ai From 408b7091871a16deffa35c58738a1a54e39931d3 Mon Sep 17 00:00:00 2001 From: Matt <77928207+mattzcarey@users.noreply.github.com> Date: Wed, 21 Oct 2026 08:29:21 +0100 Subject: [PATCH 13/13] Update src/content/docs/agents/harnesses/pi/index.mdx --- src/content/docs/agents/harnesses/pi/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/agents/harnesses/pi/index.mdx b/src/content/docs/agents/harnesses/pi/index.mdx index 98ec3f90e06..13d6f393890 100644 --- a/src/content/docs/agents/harnesses/pi/index.mdx +++ b/src/content/docs/agents/harnesses/pi/index.mdx @@ -51,7 +51,7 @@ Install `agents` with Pi Durable and Pi AI: -Both Pi packages are optional peer dependencies of `agents`. `PiHarness` needs version 1.3 of each (`~1.3.0`). +Both Pi packages are optional peer dependencies of `agents`. `PiHarness` needs version 1.3 or later of each. The `agents/models/pi-ai` entry point supports AI Gateway and Workers AI models, so you can get started with Cloudflare models right away or use your existing Pi AI provider. The examples on this page use Workers AI through the `AI` binding and the [Pi AI model provider](/agents/models/pi-ai/). Add the binding and a SQLite-backed Durable Object to your Wrangler configuration: