workers-ai-provider 3.2.0 → 3.2.1

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/index.d.mts CHANGED
@@ -437,14 +437,15 @@ type WorkersAISettings = ({
437
437
  * slug rather than a `@cf/...` Workers AI id. Bare `string` (a non-literal,
438
438
  * e.g. a variable) resolves to `false` so the common path keeps chat settings.
439
439
  */
440
- type IsCatalogSlug<M extends string> = string extends M ? false : M extends `@${string}` ? false : M extends `${string}/${string}` ? true : false;
440
+ type IsCatalogSlug<M extends string> = string extends M ? false : M extends `@${string}` ? false : M extends `dynamic/${string}` ? false : M extends `${string}/${string}` ? true : false;
441
+ type IsDynamicRoute<M extends string> = string extends M ? false : M extends `dynamic/${string}` ? true : false;
441
442
  /**
442
443
  * Picks the per-model settings type from the (captured) literal model id:
443
444
  * `DelegateCallOptions` for catalog slugs, `WorkersAIChatSettings` otherwise.
444
445
  * This is what lets `workersai("openai/gpt-5", { … })` autocomplete delegate
445
446
  * options while `workersai("@cf/…", { … })` autocompletes chat settings.
446
447
  */
447
- type ModelSettings<M extends string> = IsCatalogSlug<M> extends true ? DelegateCallOptions : WorkersAIChatSettings;
448
+ type ModelSettings<M extends string> = IsCatalogSlug<M> extends true ? DelegateCallOptions : IsDynamicRoute<M> extends true ? DelegateCallOptions | WorkersAIChatSettings : WorkersAIChatSettings;
448
449
  interface WorkersAI {
449
450
  <M extends string>(modelId: M | KnownTextGenerationModels, settings?: ModelSettings<M>): WorkersAIChatLanguageModel;
450
451
  /**
package/dist/index.mjs CHANGED
@@ -2116,6 +2116,7 @@ function makeGatewayFetch(binding, info, gatewayId, gatewayOptions, opts, select
2116
2116
  * configured. Every Cloudflare account has a `"default"` gateway.
2117
2117
  */
2118
2118
  const DEFAULT_GATEWAY_ID = "default";
2119
+ const DYNAMIC_ROUTE_WIRE_FORMAT = "openai";
2119
2120
  /**
2120
2121
  * Create a Workers AI provider instance.
2121
2122
  */
@@ -2138,6 +2139,64 @@ function createWorkersAI(options) {
2138
2139
  provider: "workersai.chat",
2139
2140
  isBinding
2140
2141
  });
2142
+ const toGatewayOptions = (gateway) => typeof gateway === "string" ? { id: gateway } : gateway;
2143
+ const createDynamicRouteModel = (modelId, settings = {}) => {
2144
+ if (settings.fallback || settings.transport === "gateway" || settings.resume === true || settings.onProgress || settings.onResumeExpired || settings.byok) throw new Error(`"${modelId}" is an AI Gateway dynamic route. Dynamic routes use AI.run with OpenAI-compatible chat-completions wire format; fallback, gateway transport, resume, BYOK, and resume callbacks must be configured on the dynamic route or gateway instead of per call.`);
2145
+ const gateway = { ...toGatewayOptions(settings.gateway) ?? options.gateway ?? { id: DEFAULT_GATEWAY_ID } };
2146
+ if (settings.metadata) gateway.metadata = {
2147
+ ...gateway.metadata ?? {},
2148
+ ...settings.metadata
2149
+ };
2150
+ if (settings.collectLog !== void 0) gateway.collectLog = settings.collectLog;
2151
+ if (settings.cacheTtl !== void 0) gateway.cacheTtl = settings.cacheTtl;
2152
+ if (settings.skipCache !== void 0) gateway.skipCache = settings.skipCache;
2153
+ const chatSettings = {
2154
+ ...settings,
2155
+ gateway
2156
+ };
2157
+ delete chatSettings.metadata;
2158
+ delete chatSettings.collectLog;
2159
+ delete chatSettings.cacheTtl;
2160
+ delete chatSettings.skipCache;
2161
+ delete chatSettings.resume;
2162
+ delete chatSettings.fallback;
2163
+ delete chatSettings.transport;
2164
+ delete chatSettings.onDispatch;
2165
+ delete chatSettings.onProgress;
2166
+ delete chatSettings.onResumeExpired;
2167
+ delete chatSettings.byok;
2168
+ const plugin = options.providers?.find((p) => p.wireFormat === DYNAMIC_ROUTE_WIRE_FORMAT);
2169
+ if (!plugin) {
2170
+ if (options.providers?.length) throw new Error(`"${modelId}" is an AI Gateway dynamic route. Dynamic routes return OpenAI-compatible chat-completions wire format on the AI.run path, so configure the OpenAI provider plugin: import { openai } from "workers-ai-provider/openai"; createWorkersAI({ binding: env.AI, providers: [openai] }).`);
2171
+ return createChatModel(modelId, chatSettings);
2172
+ }
2173
+ const fetchImpl = (async (_input, init) => {
2174
+ const body = JSON.parse(String(init?.body ?? "{}"));
2175
+ delete body.model;
2176
+ const runOptions = {
2177
+ gateway,
2178
+ returnRawResponse: true,
2179
+ ...settings.extraHeaders ? { extraHeaders: settings.extraHeaders } : {},
2180
+ ...init?.signal ? { signal: init.signal } : {}
2181
+ };
2182
+ const response = await binding.run(modelId, body, runOptions);
2183
+ settings.onDispatch?.({
2184
+ transport: "run",
2185
+ resumeEnabled: false,
2186
+ warnings: [],
2187
+ status: response.status,
2188
+ runId: response.headers.get("cf-aig-run-id"),
2189
+ cfStep: response.headers.get("cf-aig-step"),
2190
+ cacheStatus: response.headers.get("cf-aig-cache-status"),
2191
+ logId: response.headers.get("cf-aig-log-id")
2192
+ });
2193
+ return response;
2194
+ });
2195
+ return plugin.create({
2196
+ modelId,
2197
+ fetch: fetchImpl
2198
+ });
2199
+ };
2141
2200
  let delegate;
2142
2201
  const getDelegate = (slug) => {
2143
2202
  if (!options.providers?.length) throw new Error(`"${slug}" looks like a third-party AI Gateway catalog model, but this Workers AI provider was not configured to route them. Pass provider plugins, e.g.:
@@ -2153,8 +2212,9 @@ A gateway defaults to "default" but can be set via \`gateway\`. Otherwise use a
2153
2212
  }));
2154
2213
  return delegate;
2155
2214
  };
2156
- const isGatewaySlug = (id) => typeof id === "string" && !id.startsWith("@") && id.includes("/");
2215
+ const isGatewaySlug = (id) => typeof id === "string" && !id.startsWith("@") && !id.startsWith("dynamic/") && id.includes("/");
2157
2216
  const buildChat = (modelId, settings) => {
2217
+ if (typeof modelId === "string" && modelId.startsWith("dynamic/")) return createDynamicRouteModel(modelId, settings);
2158
2218
  if (isGatewaySlug(modelId)) return getDelegate(modelId)(modelId, settings);
2159
2219
  return createChatModel(modelId, settings);
2160
2220
  };