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 +3 -2
- package/dist/index.mjs +61 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +137 -8
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
type GatewayDelegate,
|
|
93
93
|
type ProviderPlugin,
|
|
94
94
|
type ResumeExpiredPolicy,
|
|
95
|
+
type WireFormat,
|
|
95
96
|
} from "./gateway-delegate";
|
|
96
97
|
|
|
97
98
|
// ---------------------------------------------------------------------------
|
|
@@ -103,6 +104,7 @@ import {
|
|
|
103
104
|
* configured. Every Cloudflare account has a `"default"` gateway.
|
|
104
105
|
*/
|
|
105
106
|
const DEFAULT_GATEWAY_ID = "default";
|
|
107
|
+
const DYNAMIC_ROUTE_WIRE_FORMAT: WireFormat = "openai";
|
|
106
108
|
|
|
107
109
|
export type WorkersAISettings = (
|
|
108
110
|
| {
|
|
@@ -178,9 +180,17 @@ type IsCatalogSlug<M extends string> = string extends M
|
|
|
178
180
|
? false
|
|
179
181
|
: M extends `@${string}`
|
|
180
182
|
? false
|
|
181
|
-
: M extends
|
|
182
|
-
?
|
|
183
|
-
:
|
|
183
|
+
: M extends `dynamic/${string}`
|
|
184
|
+
? false
|
|
185
|
+
: M extends `${string}/${string}`
|
|
186
|
+
? true
|
|
187
|
+
: false;
|
|
188
|
+
|
|
189
|
+
type IsDynamicRoute<M extends string> = string extends M
|
|
190
|
+
? false
|
|
191
|
+
: M extends `dynamic/${string}`
|
|
192
|
+
? true
|
|
193
|
+
: false;
|
|
184
194
|
|
|
185
195
|
/**
|
|
186
196
|
* Picks the per-model settings type from the (captured) literal model id:
|
|
@@ -189,7 +199,11 @@ type IsCatalogSlug<M extends string> = string extends M
|
|
|
189
199
|
* options while `workersai("@cf/…", { … })` autocompletes chat settings.
|
|
190
200
|
*/
|
|
191
201
|
type ModelSettings<M extends string> =
|
|
192
|
-
IsCatalogSlug<M> extends true
|
|
202
|
+
IsCatalogSlug<M> extends true
|
|
203
|
+
? DelegateCallOptions
|
|
204
|
+
: IsDynamicRoute<M> extends true
|
|
205
|
+
? DelegateCallOptions | WorkersAIChatSettings
|
|
206
|
+
: WorkersAIChatSettings;
|
|
193
207
|
|
|
194
208
|
export interface WorkersAI {
|
|
195
209
|
<M extends string>(
|
|
@@ -291,6 +305,112 @@ export function createWorkersAI(options: WorkersAISettings): WorkersAI {
|
|
|
291
305
|
isBinding,
|
|
292
306
|
});
|
|
293
307
|
|
|
308
|
+
const toGatewayOptions = (gateway: GatewayOptions | string | undefined): GatewayOptions | undefined =>
|
|
309
|
+
typeof gateway === "string" ? { id: gateway } : gateway;
|
|
310
|
+
|
|
311
|
+
const createDynamicRouteModel = (
|
|
312
|
+
modelId: TextGenerationModels,
|
|
313
|
+
settings: (WorkersAIChatSettings & DelegateCallOptions) = {},
|
|
314
|
+
) => {
|
|
315
|
+
if (
|
|
316
|
+
settings.fallback ||
|
|
317
|
+
settings.transport === "gateway" ||
|
|
318
|
+
settings.resume === true ||
|
|
319
|
+
settings.onProgress ||
|
|
320
|
+
settings.onResumeExpired ||
|
|
321
|
+
settings.byok
|
|
322
|
+
) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`"${modelId}" is an AI Gateway dynamic route. Dynamic routes use AI.run with ` +
|
|
325
|
+
"OpenAI-compatible chat-completions wire format; fallback, gateway transport, " +
|
|
326
|
+
"resume, BYOK, and resume callbacks must be configured on the dynamic route or " +
|
|
327
|
+
"gateway instead of per call.",
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const gateway = {
|
|
332
|
+
...(toGatewayOptions(settings.gateway) ?? options.gateway ?? { id: DEFAULT_GATEWAY_ID }),
|
|
333
|
+
};
|
|
334
|
+
if (settings.metadata) {
|
|
335
|
+
gateway.metadata = {
|
|
336
|
+
...(gateway.metadata ?? {}),
|
|
337
|
+
...settings.metadata,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
if (settings.collectLog !== undefined) {
|
|
341
|
+
gateway.collectLog = settings.collectLog;
|
|
342
|
+
}
|
|
343
|
+
if (settings.cacheTtl !== undefined) {
|
|
344
|
+
gateway.cacheTtl = settings.cacheTtl;
|
|
345
|
+
}
|
|
346
|
+
if (settings.skipCache !== undefined) {
|
|
347
|
+
gateway.skipCache = settings.skipCache;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const chatSettings = {
|
|
351
|
+
...settings,
|
|
352
|
+
gateway,
|
|
353
|
+
};
|
|
354
|
+
delete chatSettings.metadata;
|
|
355
|
+
delete chatSettings.collectLog;
|
|
356
|
+
delete chatSettings.cacheTtl;
|
|
357
|
+
delete chatSettings.skipCache;
|
|
358
|
+
delete chatSettings.resume;
|
|
359
|
+
delete chatSettings.fallback;
|
|
360
|
+
delete chatSettings.transport;
|
|
361
|
+
delete chatSettings.onDispatch;
|
|
362
|
+
delete chatSettings.onProgress;
|
|
363
|
+
delete chatSettings.onResumeExpired;
|
|
364
|
+
delete chatSettings.byok;
|
|
365
|
+
|
|
366
|
+
const plugin = options.providers?.find((p) => p.wireFormat === DYNAMIC_ROUTE_WIRE_FORMAT);
|
|
367
|
+
if (!plugin) {
|
|
368
|
+
if (options.providers?.length) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
`"${modelId}" is an AI Gateway dynamic route. Dynamic routes return OpenAI-compatible ` +
|
|
371
|
+
"chat-completions wire format on the AI.run path, so configure the OpenAI " +
|
|
372
|
+
'provider plugin: import { openai } from "workers-ai-provider/openai"; ' +
|
|
373
|
+
"createWorkersAI({ binding: env.AI, providers: [openai] }).",
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return createChatModel(modelId, chatSettings);
|
|
377
|
+
}
|
|
378
|
+
const fetchImpl = (async (_input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
|
|
379
|
+
const body = JSON.parse(String(init?.body ?? "{}")) as Record<string, unknown>;
|
|
380
|
+
delete body.model;
|
|
381
|
+
|
|
382
|
+
const runOptions: Record<string, unknown> = {
|
|
383
|
+
gateway,
|
|
384
|
+
returnRawResponse: true,
|
|
385
|
+
...(settings.extraHeaders ? { extraHeaders: settings.extraHeaders } : {}),
|
|
386
|
+
...(init?.signal ? { signal: init.signal } : {}),
|
|
387
|
+
};
|
|
388
|
+
const response = await (binding as unknown as {
|
|
389
|
+
run(
|
|
390
|
+
model: string,
|
|
391
|
+
inputs: Record<string, unknown>,
|
|
392
|
+
options: Record<string, unknown>,
|
|
393
|
+
): Promise<Response>;
|
|
394
|
+
}).run(modelId, body, runOptions);
|
|
395
|
+
settings.onDispatch?.({
|
|
396
|
+
transport: "run",
|
|
397
|
+
resumeEnabled: false,
|
|
398
|
+
warnings: [],
|
|
399
|
+
status: response.status,
|
|
400
|
+
runId: response.headers.get("cf-aig-run-id"),
|
|
401
|
+
cfStep: response.headers.get("cf-aig-step"),
|
|
402
|
+
cacheStatus: response.headers.get("cf-aig-cache-status"),
|
|
403
|
+
logId: response.headers.get("cf-aig-log-id"),
|
|
404
|
+
});
|
|
405
|
+
return response;
|
|
406
|
+
}) as typeof globalThis.fetch;
|
|
407
|
+
|
|
408
|
+
return plugin.create({
|
|
409
|
+
modelId,
|
|
410
|
+
fetch: fetchImpl,
|
|
411
|
+
}) as unknown as WorkersAIChatLanguageModel;
|
|
412
|
+
};
|
|
413
|
+
|
|
294
414
|
// Third-party catalog routing: when `providers` is configured, a non-`@cf/`
|
|
295
415
|
// `"<provider>/<model>"` slug is dispatched through the gateway delegate
|
|
296
416
|
// instead of being treated as a Workers AI model id. Built lazily so the
|
|
@@ -321,11 +441,14 @@ export function createWorkersAI(options: WorkersAISettings): WorkersAI {
|
|
|
321
441
|
return delegate;
|
|
322
442
|
};
|
|
323
443
|
|
|
324
|
-
// Workers AI model ids are
|
|
325
|
-
//
|
|
326
|
-
// treated as
|
|
444
|
+
// Workers AI model ids are usually `@cf/...`, but AI Gateway dynamic routes
|
|
445
|
+
// use the `dynamic/<route>` namespace and must pass through to `AI.run`.
|
|
446
|
+
// Other non-`@` ids with a slash are treated as catalog slugs.
|
|
327
447
|
const isGatewaySlug = (id: unknown): id is string =>
|
|
328
|
-
typeof id === "string" &&
|
|
448
|
+
typeof id === "string" &&
|
|
449
|
+
!id.startsWith("@") &&
|
|
450
|
+
!id.startsWith("dynamic/") &&
|
|
451
|
+
id.includes("/");
|
|
329
452
|
|
|
330
453
|
// Settings is the union of both shapes here; the public `WorkersAI` interface
|
|
331
454
|
// narrows it per call via `ModelSettings<M>`. We branch at runtime and cast to
|
|
@@ -334,6 +457,12 @@ export function createWorkersAI(options: WorkersAISettings): WorkersAI {
|
|
|
334
457
|
modelId: TextGenerationModels,
|
|
335
458
|
settings?: WorkersAIChatSettings | DelegateCallOptions,
|
|
336
459
|
): WorkersAIChatLanguageModel => {
|
|
460
|
+
if (typeof modelId === "string" && modelId.startsWith("dynamic/")) {
|
|
461
|
+
return createDynamicRouteModel(
|
|
462
|
+
modelId,
|
|
463
|
+
settings as (WorkersAIChatSettings & DelegateCallOptions) | undefined,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
337
466
|
if (isGatewaySlug(modelId)) {
|
|
338
467
|
// The delegate returns a `LanguageModelV3` built by the configured plugin.
|
|
339
468
|
// It's structurally compatible with the AI SDK consumers this provider is
|