agents 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-routing-B2XLNMxq.d.ts → agent-routing-0DR0Tk2v.d.ts} +205 -132
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-routing.js.map +1 -1
- package/dist/agent-tool-types.d.ts +28 -22
- package/dist/agent-tool-types.js +23 -1
- package/dist/agent-tool-types.js.map +1 -1
- package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
- package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
- package/dist/{agent-tools-Dj5Mzlw7.d.ts → agent-tools-YrATZJu2.d.ts} +34 -20
- package/dist/agent-tools.d.ts +20 -18
- package/dist/agent-tools.js.map +1 -1
- package/dist/ai-fm7xx1BK.js +312 -0
- package/dist/ai-fm7xx1BK.js.map +1 -0
- package/dist/base64-redaction-C1Uu29t5.js +59 -0
- package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
- package/dist/browser/ai-sdk.d.ts +90 -0
- package/dist/browser/ai-sdk.js +51 -0
- package/dist/browser/ai-sdk.js.map +1 -0
- package/dist/browser/ai.d.ts +4 -4
- package/dist/browser/ai.js +1 -347
- package/dist/browser/index.d.ts +81 -79
- package/dist/browser/index.js +361 -2
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tanstack-ai.d.ts +96 -1
- package/dist/browser/tanstack-ai.js +62 -2
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/browser-DE3d-RLk.d.ts +275 -0
- package/dist/browser-tool-CQ10e6Kj.js +480 -0
- package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
- package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
- package/dist/capability-B4WbF81e.js.map +1 -1
- package/dist/{capability-runner-BUBa6Ake.d.ts → capability-runner-DBAnGr6L.d.ts} +7 -2
- package/dist/chat/index.d.ts +340 -234
- package/dist/chat/index.js +592 -66
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +42 -2
- package/dist/chat/react.js +191 -35
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.d.ts +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +17 -3
- package/dist/chat-sdk/index.js +28 -2
- package/dist/chat-sdk/index.js.map +1 -1
- package/dist/{client-C9DAjZIY.js → client-4OlMIJx-.js} +102 -29
- package/dist/client-4OlMIJx-.js.map +1 -0
- package/dist/{client-BxdkCuVy.d.ts → client-je3koD9Z.d.ts} +45 -10
- package/dist/client.d.ts +7 -5
- package/dist/client.js +2 -2
- package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
- package/dist/connector-58XDGvc3.js.map +1 -0
- package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
- package/dist/context/index.d.ts +29 -0
- package/dist/context/index.js +64 -17
- package/dist/context/index.js.map +1 -1
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/{current-agent-C4w86kcN.d.ts → current-agent-vAk_5JMt.d.ts} +4 -4
- package/dist/{durable-object-lifecycle-ei7M7lqB.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +15 -7
- package/dist/harness/pi/index.d.ts +344 -0
- package/dist/harness/pi/index.js +873 -0
- package/dist/harness/pi/index.js.map +1 -0
- package/dist/images-iiTDSyRu.js +1631 -0
- package/dist/images-iiTDSyRu.js.map +1 -0
- package/dist/{index-Dlgtd3Mj.d.ts → index-BreL0YrQ.d.ts} +2 -2
- package/dist/index-kbSUIer3.d.ts +343 -0
- package/dist/index.d.ts +46 -44
- package/dist/index.js +1 -1
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +3 -3
- package/dist/lifecycle/index.js +1 -1
- package/dist/{lifecycle-Mm_jQh7r.js → lifecycle-CFu6OV03.js} +39 -10
- package/dist/{lifecycle-Mm_jQh7r.js.map → lifecycle-CFu6OV03.js.map} +1 -1
- package/dist/mcp/client/index.d.ts +20 -20
- package/dist/mcp/client/x402.js +21 -19
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +1 -1
- package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
- package/dist/message-builder-DAl9AhHH.js.map +1 -0
- package/dist/models/ai-sdk/index.d.ts +353 -0
- package/dist/models/ai-sdk/index.js +2443 -0
- package/dist/models/ai-sdk/index.js.map +1 -0
- package/dist/models/pi-ai/index.d.ts +313 -0
- package/dist/models/pi-ai/index.js +1795 -0
- package/dist/models/pi-ai/index.js.map +1 -0
- package/dist/observability/ai/index.js +20 -4
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
- package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
- package/dist/queue/index.d.ts +3 -3
- package/dist/queue/index.js +1 -1
- package/dist/{queue-BC9F5cUo.js → queue-BB-zfIYF.js} +2 -2
- package/dist/{queue-BC9F5cUo.js.map → queue-BB-zfIYF.js.map} +1 -1
- package/dist/react.d.ts +6 -6
- package/dist/react.js +160 -64
- package/dist/react.js.map +1 -1
- package/dist/{retries-D9B2UCq3.d.ts → retries-CV6AkXmQ.d.ts} +18 -9
- package/dist/retries.d.ts +11 -9
- package/dist/retries.js +11 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +2 -2
- package/dist/routing/index.js +1 -1
- package/dist/{scheduler-D_KHqxBa.js → scheduler-DZ3xQfeK.js} +2 -2
- package/dist/{scheduler-D_KHqxBa.js.map → scheduler-DZ3xQfeK.js.map} +1 -1
- package/dist/{scheduler-BGq6M5Kd.d.ts → scheduler-RdACNXDP.d.ts} +3 -3
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +70 -3
- package/dist/sessions/index.js +276 -67
- package/dist/sessions/index.js.map +1 -1
- package/dist/skills/index.d.ts +29 -314
- package/dist/socket-address.d.ts +16 -0
- package/dist/socket-address.js +19 -0
- package/dist/socket-address.js.map +1 -0
- package/dist/{src-BNU3ZiJM.js → src-BZeu-SCD.js} +423 -115
- package/dist/src-BZeu-SCD.js.map +1 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/index.js +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/{streams-BcE8JP2X.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
- package/dist/{streams-CCPRV6dt.js → streams-DW-UCOUc.js} +9 -2
- package/dist/streams-DW-UCOUc.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/{tasks-D4nLqVSI.js → tasks-68DESxf0.js} +2 -2
- package/dist/{tasks-D4nLqVSI.js.map → tasks-68DESxf0.js.map} +1 -1
- package/dist/{tasks-V2lCT8ZV.d.ts → tasks-9YxYtByh.d.ts} +2 -2
- package/dist/transport-PfFnt-Ld.d.ts +525 -0
- package/dist/{types-DCD6BNZH.d.ts → types-BA4anrjI.d.ts} +2 -2
- package/dist/voice/index.d.ts +2 -2
- package/dist/websockets/index.d.ts +23 -6
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-D7IzWbZb.js → websockets-Ci-LZFHH.js} +45 -20
- package/dist/{websockets-D7IzWbZb.js.map → websockets-Ci-LZFHH.js.map} +1 -1
- package/dist/wire-types-CnMt6_HR.js.map +1 -1
- package/dist/wire-types-CrjKvDyi.d.ts +300 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +21 -21
- package/dist/workflows.js +1 -1
- package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
- package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
- package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
- package/docs/agent-tools.md +29 -4
- package/docs/browse-the-web.md +87 -5
- package/docs/chat-agents.md +124 -16
- package/docs/context.md +27 -0
- package/docs/durable-execution.md +2 -0
- package/docs/harnesses/pi.md +173 -0
- package/docs/human-in-the-loop.md +4 -0
- package/docs/index.md +3 -0
- package/docs/lifecycle.md +20 -4
- package/docs/models-pi-ai.md +325 -0
- package/docs/models.md +504 -0
- package/docs/resumable-streaming.md +2 -0
- package/docs/routing.md +13 -13
- package/docs/sessions.md +36 -1
- package/docs/state.md +1 -0
- package/docs/sub-agents.md +9 -0
- package/package.json +40 -2
- package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
- package/dist/browser/ai.js.map +0 -1
- package/dist/client-C9DAjZIY.js.map +0 -1
- package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
- package/dist/connector-CptFKzRh.js.map +0 -1
- package/dist/message-builder-BymO4N_D.js.map +0 -1
- package/dist/src-BNU3ZiJM.js.map +0 -1
- package/dist/streams-CCPRV6dt.js.map +0 -1
- package/dist/ws-chat-transport-rWwta645.js.map +0 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Models for pi-ai (Beta)
|
|
2
|
+
|
|
3
|
+
`agents/models/pi-ai` is the [pi-ai](https://github.com/earendil-works/pi/tree/main/packages/ai)
|
|
4
|
+
twin of [`agents/models/ai-sdk`](./models.md): the same `createAI` factory, the same options,
|
|
5
|
+
the same gateway, returning pi-ai `Model` objects instead of AI SDK ones. Use it with pi-ai's
|
|
6
|
+
`stream`/`complete`, with [pi-durable](https://github.com/earendil-works/pi/tree/main/packages/durable)'s
|
|
7
|
+
`Harness`, or with any framework built on a pi-ai `Models` registry.
|
|
8
|
+
|
|
9
|
+
## The rule
|
|
10
|
+
|
|
11
|
+
**Workers AI is the catalog Cloudflare keeps up with. Every other vendor is pi-ai's.**
|
|
12
|
+
|
|
13
|
+
`agents` ships no vendor ids, wire formats, thinking rules or effort tables for any model
|
|
14
|
+
that is not `@cf/…`. A third-party model comes from pi-ai's own registry — the package that
|
|
15
|
+
tracks that vendor — and `agents` only routes it through AI Gateway. So there are two call
|
|
16
|
+
forms:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { createAI } from "agents/models/pi-ai";
|
|
20
|
+
import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
|
|
21
|
+
|
|
22
|
+
const ai = createAI({ binding: env.AI, id: "prod", cacheTtl: 60 });
|
|
23
|
+
|
|
24
|
+
ai("@cf/zai-org/glm-4.7-flash"); // Workers AI: our catalog, our compat layer
|
|
25
|
+
ai(getBuiltinModel("anthropic", "claude-opus-4-8")); // their model, our transport
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For convenience, pi-ai also ships a generated catalog of the models Cloudflare's gateway
|
|
29
|
+
serves, so a `<provider>/<id>` string resolves without importing anything:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
ai("anthropic/claude-opus-4.8"); // the same model, as AI Gateway's catalog names it
|
|
33
|
+
ai("openai/gpt-5.4");
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Any other string is a `TypeError` that names the way in. There is no prefix guessing here by
|
|
37
|
+
design: a model this package cannot name exactly is yours to construct.
|
|
38
|
+
|
|
39
|
+
1. **Workers AI ids start with `@cf/`.** Everything else is a vendor's, and comes from a
|
|
40
|
+
vendor registry pi-ai maintains.
|
|
41
|
+
2. **The gateway is an option, not a product.** Caching, logging, metadata, timeouts and
|
|
42
|
+
retries are options on `createAI`, on the model, or on the call; the `default` gateway is
|
|
43
|
+
created on first use.
|
|
44
|
+
3. **No provider keys.** AI Gateway holds the credential — unified billing, or a key you
|
|
45
|
+
store on the gateway (BYOK) — so nothing vendor-shaped needs a secret in your Worker.
|
|
46
|
+
4. **The binding is the only way in.** Workers AI goes through `env.AI.run`, vendor models
|
|
47
|
+
through `env.AI.gateway(id).run`; there is no HTTP transport and no API token to hold.
|
|
48
|
+
5. Everything here is in beta and may change in a minor release.
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
// wrangler.jsonc
|
|
54
|
+
{ "ai": { "binding": "AI" } }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
npm install agents @earendil-works/pi-ai
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { createAI } from "agents/models/pi-ai";
|
|
63
|
+
|
|
64
|
+
export default {
|
|
65
|
+
async fetch(_request: Request, env: { AI: Ai }) {
|
|
66
|
+
const ai = createAI({ binding: env.AI });
|
|
67
|
+
const reply = await ai.complete(ai("@cf/zai-org/glm-4.7-flash"), {
|
|
68
|
+
messages: [{ role: "user", content: "Hello", timestamp: Date.now() }]
|
|
69
|
+
});
|
|
70
|
+
// `content` is an array of parts — text, thinking, tool calls.
|
|
71
|
+
const text = reply.content
|
|
72
|
+
.filter((part) => part.type === "text")
|
|
73
|
+
.map((part) => part.text)
|
|
74
|
+
.join("");
|
|
75
|
+
return Response.json({ text });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Where a model comes from
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// 1. Workers AI — ours.
|
|
84
|
+
ai("@cf/zai-org/glm-4.7-flash");
|
|
85
|
+
|
|
86
|
+
// 2. pi-ai's Cloudflare AI Gateway catalog — a string, resolved by pi, not us.
|
|
87
|
+
ai("anthropic/claude-opus-4.8");
|
|
88
|
+
|
|
89
|
+
// 3. Any pi-ai model object, from any registry you imported.
|
|
90
|
+
import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";
|
|
91
|
+
const claude = anthropicProvider()
|
|
92
|
+
.getModels()
|
|
93
|
+
.find((m) => m.id === "claude-opus-4-8")!;
|
|
94
|
+
ai(claude);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Forms 2 and 3 keep the model's own metadata verbatim — `api`, `compat`, `thinkingLevelMap`,
|
|
98
|
+
cost, context window, max tokens. That metadata is why Claude Opus gets
|
|
99
|
+
`thinking: { type: "adaptive" }` and GPT-5 gets `reasoning.effort`: the model's author said
|
|
100
|
+
so, and this package does not second-guess it. Form 3 never mutates the object you
|
|
101
|
+
passed; the options ride on a copy.
|
|
102
|
+
|
|
103
|
+
## Running a model
|
|
104
|
+
|
|
105
|
+
The instance carries pi-ai's four entry points, so nothing global is registered:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const model = ai("anthropic/claude-opus-4.8");
|
|
109
|
+
const context = {
|
|
110
|
+
messages: [{ role: "user", content: "Hi", timestamp: Date.now() }]
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const message = await ai.complete(model, context); // AssistantMessage
|
|
114
|
+
for await (const event of ai.stream(model, context)) {
|
|
115
|
+
// AssistantMessageEvent: text_delta, thinking_delta, toolcall_*, done, error
|
|
116
|
+
}
|
|
117
|
+
await ai.completeSimple(model, context, { reasoning: "low" }); // SimpleStreamOptions
|
|
118
|
+
ai.streamSimple(model, context, { reasoning: "high", maxTokens: 2048 });
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### With pi-durable
|
|
122
|
+
|
|
123
|
+
pi-durable's `Harness` takes a `Models` registry. Register the provider on one and name the
|
|
124
|
+
model by id:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { Harness } from "@earendil-works/pi-durable";
|
|
128
|
+
import { createModels } from "@earendil-works/pi-ai/models";
|
|
129
|
+
|
|
130
|
+
const models = createModels();
|
|
131
|
+
models.setProvider(ai.provider);
|
|
132
|
+
const harness = await Harness.open(storage, { models, registry }, context);
|
|
133
|
+
// a session's model: { provider: "cloudflare", modelId: "anthropic/claude-opus-4.8" }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A session stores its model as `{ provider, modelId }`, not as a model object, so use the
|
|
137
|
+
`@cf/…` and `<provider>/<id>` forms there. Options given to `ai(model, options)` do not reach a
|
|
138
|
+
session; set them on `createAI` instead.
|
|
139
|
+
|
|
140
|
+
`ai.streamFn` is `streamSimple`, bound, for frameworks that take a stream function, such as
|
|
141
|
+
pi-agent-core's `Agent({ streamFn })`.
|
|
142
|
+
|
|
143
|
+
### With a pi-ai `Models` registry
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { createModels } from "@earendil-works/pi-ai/models";
|
|
147
|
+
|
|
148
|
+
const models = createModels();
|
|
149
|
+
models.setProvider(ai.provider); // provider id: "cloudflare"
|
|
150
|
+
models.getModel("cloudflare", "anthropic/claude-opus-4.8");
|
|
151
|
+
models.streamSimple(ai("@cf/zai-org/glm-4.7-flash"), context);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The provider's static list is every Workers AI id pi-ai knows plus every `<provider>/<id>` in
|
|
155
|
+
its Cloudflare AI Gateway catalog, so `cloudflare/<id>` specifiers resolve. A model object
|
|
156
|
+
you pass to `ai(model)` streams whether or not it is on that list.
|
|
157
|
+
|
|
158
|
+
## Options
|
|
159
|
+
|
|
160
|
+
Gateway options may be given flat or nested, on any of the three layers:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
const ai = createAI({ binding: env.AI, id: "prod", cacheTtl: 60 });
|
|
164
|
+
|
|
165
|
+
ai("anthropic/claude-opus-4.8", {
|
|
166
|
+
gateway: { id: "my-gateway" }, // or a string id
|
|
167
|
+
cacheTtl: 3600, // the same keys, flat
|
|
168
|
+
skipCache: false,
|
|
169
|
+
cacheKey: "greeting-v1",
|
|
170
|
+
collectLog: true,
|
|
171
|
+
metadata: { team: "growth" },
|
|
172
|
+
eventId: "turn-17",
|
|
173
|
+
requestTimeoutMs: 30_000,
|
|
174
|
+
retries: { maxAttempts: 3, backoff: "exponential", retryDelayMs: 500 },
|
|
175
|
+
fallback: ["@cf/zai-org/glm-4.7-flash"], // ids or models, in any mix
|
|
176
|
+
headers: { "x-request-source": "docs" },
|
|
177
|
+
sessionAffinity: "chat-42", // Workers AI prefix-cache routing
|
|
178
|
+
reasoningEffort: "low", // "low" | "medium" | "high" | null
|
|
179
|
+
chatTemplateKwargs: { enable_thinking: false } // Workers AI only
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`reasoningEffort` and `chatTemplateKwargs` are Workers AI settings. On a model object or a
|
|
184
|
+
`<slug>/<id>` model they are not sent; the drop is recorded on the message as a
|
|
185
|
+
`cloudflare-compat` diagnostic, and the pi `reasoning` level (mapped by the model's own
|
|
186
|
+
metadata) is the way to ask a vendor model to think. A string in `fallback` follows the same
|
|
187
|
+
rule as `ai(string)`: a `@cf/` id or a registry-known `<slug>/<id>`, never a raw vendor id.
|
|
188
|
+
|
|
189
|
+
The `@cf/` form also takes pi-ai metadata overrides — `name`, `contextWindow`, `maxTokens`,
|
|
190
|
+
`reasoning`, `input`, `cost`, `streamIdleTimeoutMs` — because the Workers AI catalog moves
|
|
191
|
+
faster than any generated registry. A model object brings its own; nothing here overrides it.
|
|
192
|
+
|
|
193
|
+
A pi-ai model is plain data, and these per-model options ride on it under symbol keys, which
|
|
194
|
+
`JSON.stringify` and `structuredClone` drop. Routing survives that — a model stored in `Agent`
|
|
195
|
+
state and read back still reaches its vendor under the vendor's own id — but the options do
|
|
196
|
+
not, so re-apply them with `ai(model, options)` after loading if the model carried any.
|
|
197
|
+
|
|
198
|
+
Per call, pi-ai's own options map onto the same layers: `headers` are merged last, `metadata`
|
|
199
|
+
(scalars) joins the gateway metadata, `sessionId` sets session affinity, and `reasoning` (a pi
|
|
200
|
+
thinking level) is remapped through the model's own `thinkingLevelMap` — as far as the wire
|
|
201
|
+
can carry it; see [thinking shapes](#thinking-shapes) for the one limit. Precedence is call,
|
|
202
|
+
then model, then `createAI` settings.
|
|
203
|
+
|
|
204
|
+
## How a request travels
|
|
205
|
+
|
|
206
|
+
| Model | `api` | Transport |
|
|
207
|
+
| ------------------------ | -------------------- | ----------------------------------------- |
|
|
208
|
+
| `@cf/…` | `cloudflare-ai` | `env.AI.run` + Cloudflare's compat layer |
|
|
209
|
+
| Anthropic (any registry) | `anthropic-messages` | gateway universal → `v1/messages` |
|
|
210
|
+
| OpenAI (any registry) | `openai-responses` | gateway universal → `v1/responses` |
|
|
211
|
+
| Groq, DeepSeek, xAI, … | `openai-completions` | gateway universal → `v1/chat/completions` |
|
|
212
|
+
|
|
213
|
+
Everything that is not `@cf/` goes through **AI Gateway's universal request**: the vendor's
|
|
214
|
+
own body, built by pi-ai's own converter, posted to the gateway with the provider slug taken
|
|
215
|
+
from the model's `baseUrl` host. The gateway forwards it to the vendor with the credential it
|
|
216
|
+
holds, and streams the vendor's own SSE back untouched — pi-ai's stream processors parse it,
|
|
217
|
+
as they would if you had called the vendor directly, the thinking knobs aside (below). No
|
|
218
|
+
credential header leaves your Worker, and the model's `api`, `provider` and `id` are never
|
|
219
|
+
rewritten, so thinking signatures and encrypted reasoning items replay on the next turn.
|
|
220
|
+
|
|
221
|
+
The `api` markers in that table are the only ones this package routes, and the table is the
|
|
222
|
+
whole list: `cloudflare-ai`, `anthropic-messages`, `openai-responses`, `openai-completions`.
|
|
223
|
+
Check your model's `api` before you pass it — several pi registries declare something else and
|
|
224
|
+
are not routable today, among them `mistral` (`mistral-conversations`), `google`
|
|
225
|
+
(`google-generative-ai`), `google-vertex`, `amazon-bedrock`, `azure-openai-responses` and
|
|
226
|
+
`openai-codex`.
|
|
227
|
+
|
|
228
|
+
Workers AI stays on `env.AI.run`. Its departures from strict OpenAI chat completions (native
|
|
229
|
+
events, heartbeats, per-delta usage, per-family request quirks) are absorbed by the same
|
|
230
|
+
compat layer the [AI SDK provider](./models.md) uses, so the parser only ever sees strict
|
|
231
|
+
OpenAI chunks. Anything the layer had to drop is recorded on the message as a
|
|
232
|
+
`cloudflare-compat` diagnostic.
|
|
233
|
+
|
|
234
|
+
An `api` this package cannot route, or a `baseUrl` whose host AI Gateway does not serve, ends
|
|
235
|
+
the stream with an `error` event naming it.
|
|
236
|
+
|
|
237
|
+
### Thinking shapes
|
|
238
|
+
|
|
239
|
+
On the `openai-completions` wire this package builds one thinking shape: OpenAI's
|
|
240
|
+
`reasoning_effort`, with the level remapped through the model's own `thinkingLevelMap`, and
|
|
241
|
+
only when the model reasons and its `compat` does not say `supportsReasoningEffort: false`.
|
|
242
|
+
|
|
243
|
+
A model whose `compat.thinkingFormat` is anything else — `deepseek`, `zai`, `openrouter`,
|
|
244
|
+
`together`, `ant-ling`, `qwen`, `qwen-chat-template`, `chat-template`, `string-thinking` — has
|
|
245
|
+
its reasoning level **dropped**, and the drop recorded on the message as a `cloudflare-compat`
|
|
246
|
+
diagnostic. Those shapes are the vendor's, and building them here would mean keeping vendor
|
|
247
|
+
wire formats in `agents`, which is the one thing this package does not do: reach such a model
|
|
248
|
+
through pi-ai's own provider if you need its thinking shape. The `anthropic-messages` and
|
|
249
|
+
`openai-responses` wires build the thinking shape their own model metadata declares.
|
|
250
|
+
|
|
251
|
+
## Unified billing and BYOK
|
|
252
|
+
|
|
253
|
+
Most vendors work with no key at all: the gateway bills them to your Cloudflare account. A
|
|
254
|
+
vendor that is not on unified billing answers with its own error until you store a key on the
|
|
255
|
+
gateway — Google AI Studio, reached through a routable model, returns `403 unregistered
|
|
256
|
+
callers`. That error is the vendor's, surfaced as-is; the gateway's own failures (no such
|
|
257
|
+
gateway, an unpaid account) become a `CloudflareAIError`, so fallback and retry treat them the
|
|
258
|
+
way they treat Workers AI failures. A model whose `api` is not one of the four routed markers
|
|
259
|
+
never gets that far: the stream ends with an `error` event naming the `api`.
|
|
260
|
+
|
|
261
|
+
## Observability
|
|
262
|
+
|
|
263
|
+
Every assistant message carries a `cloudflare` diagnostic with the gateway correlation for its
|
|
264
|
+
response:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
const message = await ai.complete(model, context);
|
|
268
|
+
const cloudflare = message.diagnostics?.find(
|
|
269
|
+
(d) => d.type === "cloudflare"
|
|
270
|
+
)?.details;
|
|
271
|
+
// { model, gateway, provider?, specifier?, logId?, eventId?, cacheStatus?,
|
|
272
|
+
// step?, traceId?, runId? }
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
A failed dispatch ends the stream with an `error` event whose message carries a
|
|
276
|
+
`cloudflare-error` diagnostic (`status`, `code`, `logId`), and `onResponse` fires with the raw
|
|
277
|
+
status and headers for every request.
|
|
278
|
+
|
|
279
|
+
## Fallback
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
ai(claude, { fallback: ["@cf/zai-org/glm-4.7-flash"] });
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Legs may be Workers AI ids or models, in any mix. They are tried in order: a leg is abandoned
|
|
286
|
+
when its stream ends with an `error` before it produced any output, and once a leg has
|
|
287
|
+
produced output it is committed to. The message that answers carries a `cloudflare-fallback`
|
|
288
|
+
diagnostic listing the abandoned attempts, and `message.model` names the model that actually
|
|
289
|
+
answered.
|
|
290
|
+
|
|
291
|
+
A leg travels with the chain's gateway options — the chain's `id`, `cacheTtl` and the rest win
|
|
292
|
+
over a leg's own, and `metadata` merges — as they do in the [AI SDK provider](./models.md). A
|
|
293
|
+
leg built as `ai(model, options)` keeps everything else it was built with. A leg named by a
|
|
294
|
+
`@cf/` id has no options of its own, so it also inherits the chain's `headers`, session
|
|
295
|
+
affinity and Workers AI knobs; it never inherits the chain's model metadata (`name`, `cost`,
|
|
296
|
+
`contextWindow`, `maxTokens`, `reasoning`, `input`), which describes the model it was written
|
|
297
|
+
on — the leg's usage and cost are its own registry entry's.
|
|
298
|
+
|
|
299
|
+
## Images
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
const images = ai.images("@cf/black-forest-labs/flux-1-schnell", { steps: 8 });
|
|
303
|
+
const result = await ai.generateImages(images, {
|
|
304
|
+
input: [{ type: "text", text: "A red bicycle" }]
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Image generation is Workers AI only. The knobs a model takes are the model's:
|
|
309
|
+
`flux-1-schnell` takes a prompt and `steps` and nothing else, so `width`,
|
|
310
|
+
`height`, `seed`, `guidance` and `negativePrompt` are dropped for it and listed
|
|
311
|
+
in a `cloudflare-compat` diagnostic on the result rather than sent and ignored.
|
|
312
|
+
Every other model in the catalog takes the full set.
|
|
313
|
+
|
|
314
|
+
## Beta
|
|
315
|
+
|
|
316
|
+
This subpath needs `@earendil-works/pi-ai@^1.0.0`, an optional peer of `agents`. `agents`
|
|
317
|
+
takes no vendor package as a dependency, optional or otherwise. The surface is in beta
|
|
318
|
+
and may change in a minor release. An unlisted `@cf/` id resolves with default metadata (a
|
|
319
|
+
128k context window, 8192 output tokens, zero cost); pass overrides when you know better.
|
|
320
|
+
|
|
321
|
+
## See also
|
|
322
|
+
|
|
323
|
+
- [Models for the AI SDK](./models.md) — the same factory for the Vercel AI SDK
|
|
324
|
+
- [pi-ai](https://github.com/earendil-works/pi/tree/main/packages/ai) and
|
|
325
|
+
[pi-durable](https://github.com/earendil-works/pi/tree/main/packages/durable)
|