You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit cfda9da
Browse filesBrowse the repository at this point in the historyBrowse files
`db`, `kv`, and `storage` accept a string to customize the binding name (e.g. `"db": "MY_DB"`). `ai` only accepts a boolean.
106
+
Every binding accepts a string to customize its name (for example, `"db": "MY_DB"` or `"ai": "MY_AI"`).
107
107
108
108
See [Configuration](../reference/config.md) for details.
109
109
@@ -349,7 +349,7 @@ That's it. The Cloudflare Vite plugin produces a complete build output with a me
349
349
350
350
`void/ai` works on your own Cloudflare account, along two paths:
351
351
352
-
-**Workers AI** (`ai.run`, `ai.stream`, `ai.image`) works out of the box. When your app imports `void/ai`, `vite build` infers that you need AI and adds a Workers AI binding (`env.AI`) to the generated `wrangler.json` automatically -- you do **not** add it to `wrangler.jsonc`.
352
+
-**Workers AI** (`ai.run`, `ai.stream`, `ai.image`) works out of the box. When your app imports `void/ai`, `vite build` infers that you need AI and adds a Workers AI binding (`env.AI`) to the generated `wrangler.json` automatically -- you do **not** add it to `wrangler.jsonc`. Set `inference.bindings.ai` to a string to use a custom name.
353
353
-**Provider models** (`ai.provider("openai").fetch(...)`) route through _your own_ Cloudflare AI Gateway. Set its id in `void.json` and add the provider's API key as a Worker secret.
354
354
355
355
```jsonc
@@ -372,7 +372,7 @@ import { defineHandler } from 'void';
372
372
import { ai } from'void/ai';
373
373
374
374
exportconst POST =defineHandler(async (c) => {
375
-
// Workers AI -- uses env.AI directly
375
+
// Workers AI -- uses the inferred or configured AI binding directly
-**No cross-tenant usage metering.** On the Void platform, AI calls are metered and billed through a shared proxy. Self-hosted, there is no metering -- you get your own [Cloudflare AI Gateway analytics](https://developers.cloudflare.com/ai-gateway/) instead.
396
-
-**The runtime reads the `env.AI` binding by name** -- a custom-named root AI binding is not supported.
396
+
-**Custom AI binding names are supported.** Configure `"inference": { "bindings": { "ai": "MY_AI" } }`, or declare the custom`ai.binding` in your root Wrangler config; the generated worker records the resolved name for `void/ai` automatically.
397
397
-**`void dev` has no local Workers AI emulation.** In development, AI still routes through Void, so `void dev` AI requires `void auth login` even when you deploy self-hosted.
Custom names are used in the deploy manifest, Wrangler config generation/sync, remote binding proxying, internal migrations, and generated runtime helpers such as `void/db`, `void/kv`, and `void/storage`.
435
+
Custom names are used in generated Wrangler config and runtime helpers such as `void/db`, `void/kv`, `void/storage`, and `void/ai`. Resource binding names also flow through deploy manifests, remote binding proxying, and internal migrations where applicable. Managed Workers AI continues to use Void's service proxy.
Copy file name to clipboardExpand all lines: packages/void/CLAUDE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -93,8 +93,8 @@ src/
93
93
94
94
Two features need extra wiring on a self-hosted `vite build && wrangler deploy` (no Void platform proxy/dispatch in front of the worker). Both are inert on managed `void deploy`.
95
95
96
-
- **`ai.gateway` config + `__VOID_AI_GATEWAY` var** — `void.json`'s `ai.gateway` (validated in `config.ts` as `ai?: { gateway?: string }`; non-empty string, `additionalProperties: false`) names a Cloudflare AI Gateway in the user's own account. `mergeBindings` (`index.ts`): (1) injects `{ ai: { binding: 'AI' } }`, but only when `shouldBindWorkersAi()` is true — the app infers `needsAI`, `command === 'build'`, and neither the resolved root wrangler config nor the result already declares an `ai` binding (so it never clobbers a user's own `ai` binding); (2) emits `vars.__VOID_AI_GATEWAY = config.ai.gateway` whenever `config.ai?.gateway` is set. At runtime, `runtime/ai.ts` `resolveBackend()` selects the direct backend (Priority 3) when `env.AI` is present and no `__VOID_PROXY`/`__VOID_TOKEN` exists. `ai.run/stream/image` hit Workers AI directly; `ai.provider().fetch()` routes through the user's own gateway via `directAi.gateway(__VOID_AI_GATEWAY).getUrl(provider)` with the provider key from the user's own secret (`resolveProviderKey` → `env[PROVIDER_KEY_MAP[provider]]`), returning HTTP 501 if `__VOID_AI_GATEWAY` is unset. No cross-tenant metering on this path. The runtime reads `env.AI` **by name** — a custom-named root AI binding is not honored.
97
-
-**Internal-binding leak check permits exactly `__VOID_AI_GATEWAY`** — the integration guard (`test/integration/kitchen-sink-build.test.ts`) that asserts no `__VOID_*` var reaches `dist/ssr/wrangler.json`explicitly excludes `__VOID_AI_GATEWAY`, because it is a legitimate, non-credential runtime var (unlike the credential keys in `INTERNAL_ENV_KEYS`, which are stripped).
96
+
- **`ai.gateway`, custom AI bindings, and private runtime vars** — `void.json`'s `ai.gateway` (validated in `config.ts` as `ai?: { gateway?: string }`; non-empty string, `additionalProperties: false`) names a Cloudflare AI Gateway in the user's own account. `inference.bindings.ai` accepts a string custom binding name. `mergeBindings` (`index.ts`) injects the resolved binding only when `shouldBindWorkersAi()` is true — the app infers `needsAI`, `command === 'build'`, and neither the resolved root wrangler config nor the result already declares an `ai` binding — so a root Wrangler declaration stays authoritative. It emits `vars.__VOID_AI_BINDING` for a non-default binding and `vars.__VOID_AI_GATEWAY` when configured. At runtime, `runtime/ai.ts` `resolveBackend()` uses that resolved binding when no `__VOID_PROXY`/`__VOID_TOKEN` exists. `ai.run/stream/image` hit Workers AI directly; `ai.provider().fetch()` routes through the user's own gateway via `directAi.gateway(__VOID_AI_GATEWAY).getUrl(provider)` with the provider key from the user's own secret (`resolveProviderKey` → `env[PROVIDER_KEY_MAP[provider]]`), returning HTTP 501 if `__VOID_AI_GATEWAY` is unset. No cross-tenant metering on this path.
97
+
-**Internal-binding leak check permits the AI routing markers** — the integration guard (`test/integration/kitchen-sink-build.test.ts`) that asserts no credential-bearing `__VOID_*` var reaches `dist/ssr/wrangler.json`permits `__VOID_AI_BINDING` and `__VOID_AI_GATEWAY`, because they are legitimate, non-credential runtime routing values (unlike the credential keys in `INTERNAL_ENV_KEYS`, which are stripped).
98
98
-**`ISR_CACHE` self-host requirement** — self-host ISR (`runtime/isr-cache.ts`, `runtime/isr.ts`) reads `env.ISR_CACHE` (a `KVNamespace`) + `env.__VOID_ISR_DEPLOYMENT_ID`. Unlike D1/KV/R2, `ISR_CACHE` is **not** auto-injected (a KV binding needs a real namespace id), so the user must create the namespace and declare it as `ISR_CACHE` in their root wrangler config. `routing.revalidate` (void.json) and per-page `export const revalidate` are merged by `collectRevalidate` (`isr-config.ts`) and baked as `__VOID_ISR_REVALIDATE`. On-demand `revalidate()` on self-host purges the worker's own KV (global) plus the local-colo `caches.default` edge variants only.
reason: `generated config declares an ai binding the build injected — the pre-build inference found no Workers AI use in the source and the root config declares no ai binding.`,
@@ -2481,10 +2483,12 @@ export async function computeDesiredCloudflareResources(
2481
2483
migrations: Array<{name: string;sql: string}>;
2482
2484
/**
2483
2485
* Whether the pre-build inference found Workers AI use in the source — the SAME `needsAI`
2484
-
* signal `shouldBindWorkersAi` (index.ts) gates the emitted `{binding:'AI'}` on. Threaded
2485
-
* into {@link verifyGeneratedDeployConfig} to authorize that emitted ai binding.
2486
+
* signal `shouldBindWorkersAi` (index.ts) gates the emitted AI binding on. Threaded into
2487
+
* {@link verifyGeneratedDeployConfig} to authorize that binding.
2486
2488
*/
2487
2489
needsAI: boolean;
2490
+
/** Workers AI binding emitted by the build (default `AI`). */
2491
+
aiBindingName: string;
2488
2492
/**
2489
2493
* Whether Void's build emits the sandbox container application — the SAME signal `mergeBindings`
2490
2494
* (index.ts) gates emission on: `isSandboxEnabled(config)` (a `sandbox` field in `void.json`) OR
@@ -2642,6 +2646,7 @@ export async function computeDesiredCloudflareResources(
0 commit comments