workers-ai-provider 3.2.1 → 3.3.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/README.md +24 -15
- package/dist/anthropic.d.mts +1 -1
- package/dist/{gateway-delegate-BfaUTwDZ.d.mts → gateway-delegate-BAALxuBO.d.mts} +75 -11
- package/dist/{gateway-provider-1USFWm7c.mjs → gateway-provider-BjPLZLob.mjs} +560 -168
- package/dist/gateway-provider-BjPLZLob.mjs.map +1 -0
- package/dist/gateway-provider.mjs +1 -1
- package/dist/google.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +327 -278
- package/dist/index.mjs.map +1 -1
- package/dist/openai.d.mts +1 -1
- package/package.json +5 -4
- package/src/gateway-delegate.ts +290 -86
- package/src/gateway-provider.ts +10 -34
- package/src/gateway-providers.ts +13 -455
- package/src/index.ts +19 -12
- package/src/resumable-stream.ts +9 -221
- package/src/streaming.ts +1 -41
- package/src/utils.ts +40 -125
- package/src/workersai-chat-language-model.ts +37 -16
- package/src/workersai-embedding-model.ts +22 -11
- package/src/workersai-error.ts +70 -0
- package/src/workersai-image-model.ts +19 -11
- package/src/workersai-reranking-model.ts +16 -5
- package/src/workersai-speech-model.ts +35 -13
- package/src/workersai-transcription-model.ts +15 -4
- package/dist/gateway-provider-1USFWm7c.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { TooManyEmbeddingValuesForCallError, UnsupportedFunctionalityError } from "@ai-sdk/provider";
|
|
1
|
+
import { C as GatewayDelegateError, S as headersToObject, _ as detectProviderByUrl, a as WORKERS_AI_ERROR_CODE_TO_STATUS, b as asText, c as parseWorkersAIErrorCode, d as isForcedToolChoice, f as normalizeMessagesForBinding$1, g as GATEWAY_PROVIDERS, h as createResumableStream, i as WorkersAIGatewayError, l as SSEDecoder, m as processText, n as createGatewayProvider, o as isAbortError, p as parseLeakedToolCalls$1, r as WorkersAIFallbackError, s as messageOf, t as createGatewayFetch, u as getToolNames, v as findProviderBySlug, w as _defineProperty, x as buildGatewayEntry, y as wireableProviders } from "./gateway-provider-BjPLZLob.mjs";
|
|
2
|
+
import { APICallError, TooManyEmbeddingValuesForCallError, UnsupportedFunctionalityError } from "@ai-sdk/provider";
|
|
3
3
|
import { generateId } from "ai";
|
|
4
|
+
//#region src/workersai-error.ts
|
|
5
|
+
/**
|
|
6
|
+
* Normalize an error thrown by the Workers AI **binding** (`env.AI.run`) into an
|
|
7
|
+
* `APICallError` so the AI SDK can classify and retry it.
|
|
8
|
+
*
|
|
9
|
+
* Cancellations (`AbortError` / `TimeoutError` / `ResponseAborted`, including
|
|
10
|
+
* `DOMException` aborts) and errors that are already an `APICallError` pass
|
|
11
|
+
* through unchanged. Everything else becomes an
|
|
12
|
+
* `APICallError`; when the internal code maps to a known HTTP status, that
|
|
13
|
+
* `statusCode` is attached and `APICallError` derives `isRetryable` from it.
|
|
14
|
+
* Unrecognized errors get no `statusCode`, so they stay non-retryable (the
|
|
15
|
+
* prior behavior).
|
|
16
|
+
*/
|
|
17
|
+
function normalizeBindingError(error, context) {
|
|
18
|
+
if (APICallError.isInstance(error) || isAbortError(error)) return error;
|
|
19
|
+
const code = parseWorkersAIErrorCode(error);
|
|
20
|
+
const statusCode = code != null ? WORKERS_AI_ERROR_CODE_TO_STATUS[code] : void 0;
|
|
21
|
+
const message = messageOf(error);
|
|
22
|
+
return new APICallError({
|
|
23
|
+
message,
|
|
24
|
+
url: `workers-ai:binding/run/${context.model}`,
|
|
25
|
+
requestBodyValues: context.requestBodyValues,
|
|
26
|
+
statusCode,
|
|
27
|
+
responseBody: message,
|
|
28
|
+
cause: error,
|
|
29
|
+
...code != null ? { data: { workersAIErrorCode: code } } : {}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build an `APICallError` from a non-OK Workers AI **REST** response. The HTTP
|
|
34
|
+
* status is authoritative here, so `APICallError` derives `isRetryable` from it
|
|
35
|
+
* directly (429 / 5xx → retryable). Response headers are preserved so the AI
|
|
36
|
+
* SDK can honor `Retry-After`. The message keeps the historical
|
|
37
|
+
* `"Workers AI API error (<status> <statusText>): <body>"` shape.
|
|
38
|
+
*/
|
|
39
|
+
function apiCallErrorFromResponse(response, errorBody, context) {
|
|
40
|
+
return new APICallError({
|
|
41
|
+
message: `Workers AI API error (${response.status} ${response.statusText}): ${errorBody}`,
|
|
42
|
+
url: context.url,
|
|
43
|
+
requestBodyValues: context.requestBodyValues,
|
|
44
|
+
statusCode: response.status,
|
|
45
|
+
responseHeaders: headersToObject(response.headers),
|
|
46
|
+
responseBody: errorBody
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
4
50
|
//#region src/utils.ts
|
|
5
51
|
/**
|
|
6
52
|
* Normalize messages before passing to the Workers AI binding.
|
|
@@ -9,11 +55,7 @@ import { generateId } from "ai";
|
|
|
9
55
|
* - `content` must not be null
|
|
10
56
|
*/
|
|
11
57
|
function normalizeMessagesForBinding(messages) {
|
|
12
|
-
return messages
|
|
13
|
-
const normalized = { ...msg };
|
|
14
|
-
if (normalized.content === null || normalized.content === void 0) normalized.content = "";
|
|
15
|
-
return normalized;
|
|
16
|
-
});
|
|
58
|
+
return normalizeMessagesForBinding$1(messages);
|
|
17
59
|
}
|
|
18
60
|
/**
|
|
19
61
|
* Creates a run method that emulates the Cloudflare Workers AI binding,
|
|
@@ -62,7 +104,10 @@ function createRun(config) {
|
|
|
62
104
|
} catch {
|
|
63
105
|
errorBody = "<unable to read response body>";
|
|
64
106
|
}
|
|
65
|
-
throw
|
|
107
|
+
throw apiCallErrorFromResponse(response, errorBody, {
|
|
108
|
+
url,
|
|
109
|
+
requestBodyValues: inputs
|
|
110
|
+
});
|
|
66
111
|
}
|
|
67
112
|
if (returnRawResponse) return response;
|
|
68
113
|
if (inputs.stream === true) {
|
|
@@ -85,7 +130,10 @@ function createRun(config) {
|
|
|
85
130
|
} catch {
|
|
86
131
|
errorBody = "<unable to read response body>";
|
|
87
132
|
}
|
|
88
|
-
throw
|
|
133
|
+
throw apiCallErrorFromResponse(retryResponse, errorBody, {
|
|
134
|
+
url,
|
|
135
|
+
requestBodyValues: inputs
|
|
136
|
+
});
|
|
89
137
|
}
|
|
90
138
|
return (await retryResponse.json()).result;
|
|
91
139
|
}
|
|
@@ -123,7 +171,13 @@ async function createRunBinary(config, model, audioBytes, contentType, signal) {
|
|
|
123
171
|
} catch {
|
|
124
172
|
errorBody = "<unable to read response body>";
|
|
125
173
|
}
|
|
126
|
-
throw
|
|
174
|
+
throw apiCallErrorFromResponse(response, errorBody, {
|
|
175
|
+
url,
|
|
176
|
+
requestBodyValues: {
|
|
177
|
+
contentType,
|
|
178
|
+
byteLength: audioBytes.byteLength
|
|
179
|
+
}
|
|
180
|
+
});
|
|
127
181
|
}
|
|
128
182
|
const data = await response.json();
|
|
129
183
|
return data.result ?? data;
|
|
@@ -235,56 +289,20 @@ function processToolCalls(output) {
|
|
|
235
289
|
return [];
|
|
236
290
|
}
|
|
237
291
|
/**
|
|
238
|
-
* Was a specific tool forced for this request?
|
|
239
|
-
*
|
|
240
|
-
* True for both `tool_choice: "required"` and the named-function form
|
|
241
|
-
* `{ type: "function", function: { name } }`.
|
|
242
|
-
*/
|
|
243
|
-
function isForcedToolChoice(toolChoice) {
|
|
244
|
-
if (toolChoice === "required") return true;
|
|
245
|
-
return typeof toolChoice === "object" && toolChoice !== null && toolChoice.type === "function";
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
292
|
* Parse tool calls that a model leaked as JSON text instead of structured
|
|
249
|
-
* `tool_calls
|
|
293
|
+
* `tool_calls`, assigning AI-SDK tool-call ids.
|
|
250
294
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
295
|
+
* The recovery logic (which JSON shapes count as a leaked call) lives in
|
|
296
|
+
* `@cloudflare/gateway-core`; this wrapper only layers the framework id on each
|
|
297
|
+
* neutral result so the existing `LanguageModelV3ToolCall` shape is preserved.
|
|
254
298
|
*/
|
|
255
299
|
function parseLeakedToolCalls(text, knownToolNames) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
const candidates = Array.isArray(parsed) ? parsed : [parsed];
|
|
263
|
-
const salvaged = [];
|
|
264
|
-
for (const candidate of candidates) {
|
|
265
|
-
if (typeof candidate !== "object" || candidate === null) continue;
|
|
266
|
-
const obj = candidate;
|
|
267
|
-
const name = obj.name;
|
|
268
|
-
if (typeof name !== "string" || !knownToolNames.has(name)) continue;
|
|
269
|
-
let args;
|
|
270
|
-
if ("arguments" in obj) args = obj.arguments;
|
|
271
|
-
else if ("parameters" in obj) args = obj.parameters;
|
|
272
|
-
else {
|
|
273
|
-
const { name: _name, ...rest } = obj;
|
|
274
|
-
args = rest;
|
|
275
|
-
}
|
|
276
|
-
salvaged.push({
|
|
277
|
-
input: typeof args === "string" ? args : JSON.stringify(args ?? {}),
|
|
278
|
-
toolCallId: createAISDKToolCallId(void 0),
|
|
279
|
-
type: "tool-call",
|
|
280
|
-
toolName: name
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
return salvaged;
|
|
284
|
-
}
|
|
285
|
-
/** Collect the requested tool names from mapped tools. */
|
|
286
|
-
function getToolNames(tools) {
|
|
287
|
-
return new Set((tools ?? []).map((tool) => tool.function?.name).filter((name) => typeof name === "string"));
|
|
300
|
+
return parseLeakedToolCalls$1(text, knownToolNames).map((call) => ({
|
|
301
|
+
input: call.input,
|
|
302
|
+
toolCallId: createAISDKToolCallId(void 0),
|
|
303
|
+
type: "tool-call",
|
|
304
|
+
toolName: call.toolName
|
|
305
|
+
}));
|
|
288
306
|
}
|
|
289
307
|
/**
|
|
290
308
|
* Salvage a tool call that a model leaked into text content instead of the
|
|
@@ -320,24 +338,6 @@ function salvageToolCallsFromText(output, context) {
|
|
|
320
338
|
const salvaged = parseLeakedToolCalls(text, knownToolNames);
|
|
321
339
|
return salvaged.length > 0 ? salvaged : null;
|
|
322
340
|
}
|
|
323
|
-
/**
|
|
324
|
-
* Extract text from a Workers AI response, handling multiple response formats:
|
|
325
|
-
* - OpenAI format: { choices: [{ message: { content: "..." } }] }
|
|
326
|
-
* - Native format: { response: "..." }
|
|
327
|
-
* - Structured output quirk: { response: { ... } } (object instead of string)
|
|
328
|
-
* - Structured output quirk: { response: "{ ... }" } (JSON string)
|
|
329
|
-
*/
|
|
330
|
-
function processText(output) {
|
|
331
|
-
const choiceContent = output.choices?.[0]?.message?.content;
|
|
332
|
-
if (choiceContent != null && String(choiceContent).length > 0) return String(choiceContent);
|
|
333
|
-
if ("response" in output) {
|
|
334
|
-
const response = output.response;
|
|
335
|
-
if (typeof response === "object" && response !== null) return JSON.stringify(response);
|
|
336
|
-
if (typeof response === "number") return String(response);
|
|
337
|
-
if (response === null || response === void 0) return;
|
|
338
|
-
return String(response);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
341
|
//#endregion
|
|
342
342
|
//#region src/convert-to-workersai-chat-messages.ts
|
|
343
343
|
/**
|
|
@@ -945,37 +945,6 @@ function getMappedStream(response, salvageContext) {
|
|
|
945
945
|
}
|
|
946
946
|
}
|
|
947
947
|
}
|
|
948
|
-
/**
|
|
949
|
-
* TransformStream that decodes a raw byte stream into SSE `data:` payloads.
|
|
950
|
-
* Each output chunk is the string content after "data: " (one per SSE event).
|
|
951
|
-
* Handles line buffering for partial chunks.
|
|
952
|
-
*/
|
|
953
|
-
var SSEDecoder = class extends TransformStream {
|
|
954
|
-
constructor() {
|
|
955
|
-
let buffer = "";
|
|
956
|
-
const decoder = new TextDecoder();
|
|
957
|
-
super({
|
|
958
|
-
transform(chunk, controller) {
|
|
959
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
960
|
-
const lines = buffer.split("\n");
|
|
961
|
-
buffer = lines.pop() || "";
|
|
962
|
-
for (const line of lines) {
|
|
963
|
-
const trimmed = line.trim();
|
|
964
|
-
if (!trimmed) continue;
|
|
965
|
-
if (trimmed.startsWith("data: ")) controller.enqueue(trimmed.slice(6));
|
|
966
|
-
else if (trimmed.startsWith("data:")) controller.enqueue(trimmed.slice(5));
|
|
967
|
-
}
|
|
968
|
-
},
|
|
969
|
-
flush(controller) {
|
|
970
|
-
if (buffer.trim()) {
|
|
971
|
-
const trimmed = buffer.trim();
|
|
972
|
-
if (trimmed.startsWith("data: ")) controller.enqueue(trimmed.slice(6));
|
|
973
|
-
else if (trimmed.startsWith("data:")) controller.enqueue(trimmed.slice(5));
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
948
|
//#endregion
|
|
980
949
|
//#region src/aisearch-chat-language-model.ts
|
|
981
950
|
var AISearchChatLanguageModel = class {
|
|
@@ -1089,12 +1058,21 @@ var WorkersAIEmbeddingModel = class {
|
|
|
1089
1058
|
values
|
|
1090
1059
|
});
|
|
1091
1060
|
const { gateway, maxEmbeddingsPerCall: _maxEmbeddingsPerCall, supportsParallelCalls: _supportsParallelCalls, ...passthroughOptions } = this.settings;
|
|
1092
|
-
|
|
1093
|
-
|
|
1061
|
+
let response;
|
|
1062
|
+
try {
|
|
1063
|
+
response = await this.config.binding.run(this.modelId, { text: values }, {
|
|
1094
1064
|
gateway: this.config.gateway ?? gateway,
|
|
1095
1065
|
signal: abortSignal,
|
|
1096
1066
|
...passthroughOptions
|
|
1097
|
-
})
|
|
1067
|
+
});
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
throw normalizeBindingError(error, {
|
|
1070
|
+
model: this.modelId,
|
|
1071
|
+
requestBodyValues: { text: values }
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
embeddings: response.data,
|
|
1098
1076
|
warnings: []
|
|
1099
1077
|
};
|
|
1100
1078
|
}
|
|
@@ -1254,10 +1232,18 @@ var WorkersAIChatLanguageModel = class {
|
|
|
1254
1232
|
const { messages } = convertToWorkersAIChatMessages(options.prompt);
|
|
1255
1233
|
const inputs = this.buildRunInputs(args, messages, { providerOptions: options.providerOptions });
|
|
1256
1234
|
const runOptions = this.getRunOptions();
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1235
|
+
let output;
|
|
1236
|
+
try {
|
|
1237
|
+
output = await this.config.binding.run(args.model, inputs, {
|
|
1238
|
+
...runOptions,
|
|
1239
|
+
signal: options.abortSignal
|
|
1240
|
+
});
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
throw normalizeBindingError(error, {
|
|
1243
|
+
model: args.model,
|
|
1244
|
+
requestBodyValues: inputs
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1261
1247
|
if (output instanceof ReadableStream) throw new Error("Unexpected streaming response from non-streaming request. Check that `stream: true` was not passed.");
|
|
1262
1248
|
const outputRecord = output;
|
|
1263
1249
|
const { reasoningContent, text, toolCalls, finishReason } = this.extractContent(outputRecord, args, warnings);
|
|
@@ -1286,10 +1272,18 @@ var WorkersAIChatLanguageModel = class {
|
|
|
1286
1272
|
providerOptions: options.providerOptions
|
|
1287
1273
|
});
|
|
1288
1274
|
const runOptions = this.getRunOptions();
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1275
|
+
let response;
|
|
1276
|
+
try {
|
|
1277
|
+
response = await this.config.binding.run(args.model, inputs, {
|
|
1278
|
+
...runOptions,
|
|
1279
|
+
signal: options.abortSignal
|
|
1280
|
+
});
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
throw normalizeBindingError(error, {
|
|
1283
|
+
model: args.model,
|
|
1284
|
+
requestBodyValues: inputs
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1293
1287
|
if (response instanceof ReadableStream) return { stream: prependStreamStart(getMappedStream(response, {
|
|
1294
1288
|
tools: args.tools,
|
|
1295
1289
|
toolChoice: args.tool_choice
|
|
@@ -1369,15 +1363,25 @@ var WorkersAIImageModel = class {
|
|
|
1369
1363
|
type: "unsupported"
|
|
1370
1364
|
});
|
|
1371
1365
|
const generateImage = async () => {
|
|
1372
|
-
|
|
1366
|
+
const inputs = {
|
|
1373
1367
|
height,
|
|
1374
1368
|
prompt: prompt ?? "",
|
|
1375
1369
|
seed,
|
|
1376
1370
|
width
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1371
|
+
};
|
|
1372
|
+
let output;
|
|
1373
|
+
try {
|
|
1374
|
+
output = await this.config.binding.run(this.modelId, inputs, {
|
|
1375
|
+
gateway: this.config.gateway,
|
|
1376
|
+
signal: abortSignal
|
|
1377
|
+
});
|
|
1378
|
+
} catch (error) {
|
|
1379
|
+
throw normalizeBindingError(error, {
|
|
1380
|
+
model: this.modelId,
|
|
1381
|
+
requestBodyValues: inputs
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
return toUint8Array$1(output);
|
|
1381
1385
|
};
|
|
1382
1386
|
return {
|
|
1383
1387
|
images: await Promise.all(Array.from({ length: n }, () => generateImage())),
|
|
@@ -1466,8 +1470,15 @@ var WorkersAITranscriptionModel = class {
|
|
|
1466
1470
|
const audioBytes = typeof audio === "string" ? Uint8Array.from(atob(audio), (c) => c.charCodeAt(0)) : audio;
|
|
1467
1471
|
const isNova3 = this.modelId === "@cf/deepgram/nova-3";
|
|
1468
1472
|
let rawResult;
|
|
1469
|
-
|
|
1470
|
-
|
|
1473
|
+
try {
|
|
1474
|
+
if (isNova3) rawResult = await this.runNova3(audioBytes, mediaType, abortSignal);
|
|
1475
|
+
else rawResult = await this.runWhisper(audioBytes, abortSignal);
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
throw normalizeBindingError(error, {
|
|
1478
|
+
model: this.modelId,
|
|
1479
|
+
requestBodyValues: { mediaType }
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1471
1482
|
const result = rawResult;
|
|
1472
1483
|
if (isNova3) return this.normalizeNova3Response(result, warnings);
|
|
1473
1484
|
return this.normalizeWhisperResponse(result, warnings);
|
|
@@ -1581,12 +1592,28 @@ var WorkersAISpeechModel = class {
|
|
|
1581
1592
|
const inputs = { text };
|
|
1582
1593
|
if (voice) inputs.voice = voice;
|
|
1583
1594
|
if (speed != null) inputs.speed = speed;
|
|
1584
|
-
|
|
1585
|
-
|
|
1595
|
+
let result;
|
|
1596
|
+
try {
|
|
1597
|
+
result = await this.config.binding.run(this.modelId, inputs, {
|
|
1586
1598
|
gateway: this.config.gateway,
|
|
1587
1599
|
signal: abortSignal,
|
|
1588
1600
|
returnRawResponse: true
|
|
1589
|
-
})
|
|
1601
|
+
});
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
throw normalizeBindingError(error, {
|
|
1604
|
+
model: this.modelId,
|
|
1605
|
+
requestBodyValues: inputs
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
if (result instanceof Response && !result.ok) {
|
|
1609
|
+
const errorBody = await result.text().catch(() => "<unable to read response body>");
|
|
1610
|
+
throw apiCallErrorFromResponse(result, errorBody, {
|
|
1611
|
+
url: `workers-ai:run/${this.modelId}`,
|
|
1612
|
+
requestBodyValues: inputs
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
return {
|
|
1616
|
+
audio: await toUint8Array(result),
|
|
1590
1617
|
warnings,
|
|
1591
1618
|
response: {
|
|
1592
1619
|
timestamp: /* @__PURE__ */ new Date(),
|
|
@@ -1661,11 +1688,20 @@ var WorkersAIRerankingModel = class {
|
|
|
1661
1688
|
contexts: documentsToContexts(documents, warnings)
|
|
1662
1689
|
};
|
|
1663
1690
|
if (topN != null) inputs.top_k = topN;
|
|
1664
|
-
|
|
1665
|
-
|
|
1691
|
+
let result;
|
|
1692
|
+
try {
|
|
1693
|
+
result = await this.config.binding.run(this.modelId, inputs, {
|
|
1666
1694
|
gateway: this.config.gateway,
|
|
1667
1695
|
signal: abortSignal
|
|
1668
|
-
})
|
|
1696
|
+
});
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
throw normalizeBindingError(error, {
|
|
1699
|
+
model: this.modelId,
|
|
1700
|
+
requestBodyValues: inputs
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
return {
|
|
1704
|
+
ranking: (result.response ?? []).map((item) => ({
|
|
1669
1705
|
index: item.id ?? 0,
|
|
1670
1706
|
relevanceScore: item.score ?? 0
|
|
1671
1707
|
})).sort((a, b) => b.relevanceScore - a.relevanceScore),
|
|
@@ -1750,110 +1786,6 @@ function createClientFallbackModel(legs) {
|
|
|
1750
1786
|
};
|
|
1751
1787
|
}
|
|
1752
1788
|
//#endregion
|
|
1753
|
-
//#region src/resumable-stream.ts
|
|
1754
|
-
function concat(a, b) {
|
|
1755
|
-
const out = new Uint8Array(new ArrayBuffer(a.length + b.length));
|
|
1756
|
-
out.set(a, 0);
|
|
1757
|
-
out.set(b, a.length);
|
|
1758
|
-
return out;
|
|
1759
|
-
}
|
|
1760
|
-
/** Index just past the last `\n\n` in `buf`, or -1 if there is no complete event. */
|
|
1761
|
-
function lastEventBoundary(buf) {
|
|
1762
|
-
for (let i = buf.length - 2; i >= 0; i--) if (buf[i] === 10 && buf[i + 1] === 10) return i + 2;
|
|
1763
|
-
return -1;
|
|
1764
|
-
}
|
|
1765
|
-
/** Count of `\n\n` terminators (= complete SSE events) in `buf`. */
|
|
1766
|
-
function countEvents(buf) {
|
|
1767
|
-
let n = 0;
|
|
1768
|
-
for (let i = 0; i + 1 < buf.length; i++) if (buf[i] === 10 && buf[i + 1] === 10) {
|
|
1769
|
-
n++;
|
|
1770
|
-
i++;
|
|
1771
|
-
}
|
|
1772
|
-
return n;
|
|
1773
|
-
}
|
|
1774
|
-
function resumeUrl(gateway, runId, from) {
|
|
1775
|
-
return `https://workers-binding.ai/ai-gateway/gateways/${gateway}/run/${runId}/resume?from=${from}`;
|
|
1776
|
-
}
|
|
1777
|
-
function createResumableStream(options) {
|
|
1778
|
-
const { binding, gateway, runId } = options;
|
|
1779
|
-
const maxReconnects = options.maxReconnects ?? 5;
|
|
1780
|
-
const onExpired = options.onResumeExpired ?? "error";
|
|
1781
|
-
let emittedEvents = options.fromEvent ?? 0;
|
|
1782
|
-
let pending = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(0));
|
|
1783
|
-
let reconnects = 0;
|
|
1784
|
-
async function fetchResume(controller) {
|
|
1785
|
-
let res;
|
|
1786
|
-
try {
|
|
1787
|
-
res = await binding.fetch(resumeUrl(gateway, runId, emittedEvents), { method: "GET" });
|
|
1788
|
-
} catch (fetchErr) {
|
|
1789
|
-
controller.error(new GatewayDelegateError("dispatch", `Resume request threw at event ${emittedEvents}.`, fetchErr));
|
|
1790
|
-
return null;
|
|
1791
|
-
}
|
|
1792
|
-
if (res.status === 404) {
|
|
1793
|
-
if (onExpired === "accept-partial") {
|
|
1794
|
-
controller.close();
|
|
1795
|
-
return null;
|
|
1796
|
-
}
|
|
1797
|
-
controller.error(new GatewayDelegateError("resume-expired", `Resume buffer expired (404) at event ${emittedEvents}. The gateway buffer TTL (~5.5 min) elapsed; fall back to continuation or regeneration.`));
|
|
1798
|
-
return null;
|
|
1799
|
-
}
|
|
1800
|
-
if (!res.ok || !res.body) {
|
|
1801
|
-
controller.error(new GatewayDelegateError("dispatch", `Resume failed (${res.status}) at event ${emittedEvents}.`));
|
|
1802
|
-
return null;
|
|
1803
|
-
}
|
|
1804
|
-
return res.body;
|
|
1805
|
-
}
|
|
1806
|
-
return new ReadableStream({ async start(controller) {
|
|
1807
|
-
let current;
|
|
1808
|
-
if (options.initial) current = options.initial;
|
|
1809
|
-
else {
|
|
1810
|
-
const body = await fetchResume(controller);
|
|
1811
|
-
if (!body) return;
|
|
1812
|
-
current = body;
|
|
1813
|
-
}
|
|
1814
|
-
for (;;) {
|
|
1815
|
-
const reader = current.getReader();
|
|
1816
|
-
try {
|
|
1817
|
-
for (;;) {
|
|
1818
|
-
const { done, value } = await reader.read();
|
|
1819
|
-
if (done) {
|
|
1820
|
-
if (pending.length > 0) {
|
|
1821
|
-
controller.enqueue(pending);
|
|
1822
|
-
pending = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(0));
|
|
1823
|
-
}
|
|
1824
|
-
controller.close();
|
|
1825
|
-
return;
|
|
1826
|
-
}
|
|
1827
|
-
if (!value || value.length === 0) continue;
|
|
1828
|
-
pending = concat(pending, value);
|
|
1829
|
-
const boundary = lastEventBoundary(pending);
|
|
1830
|
-
if (boundary > 0) {
|
|
1831
|
-
const complete = pending.slice(0, boundary);
|
|
1832
|
-
controller.enqueue(complete);
|
|
1833
|
-
emittedEvents += countEvents(complete);
|
|
1834
|
-
options.onProgress?.(emittedEvents);
|
|
1835
|
-
pending = pending.slice(boundary);
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
} catch (err) {
|
|
1839
|
-
try {
|
|
1840
|
-
reader.releaseLock();
|
|
1841
|
-
} catch {}
|
|
1842
|
-
if (reconnects >= maxReconnects) {
|
|
1843
|
-
controller.error(new GatewayDelegateError("resume-expired", `Exceeded ${maxReconnects} reconnect attempts at event ${emittedEvents}.`, err));
|
|
1844
|
-
return;
|
|
1845
|
-
}
|
|
1846
|
-
pending = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(0));
|
|
1847
|
-
reconnects++;
|
|
1848
|
-
options.onReconnect?.(emittedEvents, reconnects);
|
|
1849
|
-
const body = await fetchResume(controller);
|
|
1850
|
-
if (!body) return;
|
|
1851
|
-
current = body;
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
} });
|
|
1855
|
-
}
|
|
1856
|
-
//#endregion
|
|
1857
1789
|
//#region src/gateway-delegate.ts
|
|
1858
1790
|
/**
|
|
1859
1791
|
* Parse a `vendor/model` slug. The first segment is the resolver key (which
|
|
@@ -1921,31 +1853,6 @@ function selectTransport(opts, resumeExplicitlyTrue, runCatalog = true, gatewayA
|
|
|
1921
1853
|
warnings
|
|
1922
1854
|
};
|
|
1923
1855
|
}
|
|
1924
|
-
var GatewayDelegateError = class extends Error {
|
|
1925
|
-
constructor(kind, message, cause) {
|
|
1926
|
-
super(message);
|
|
1927
|
-
_defineProperty(this, "kind", void 0);
|
|
1928
|
-
_defineProperty(this, "cause", void 0);
|
|
1929
|
-
this.name = "GatewayDelegateError";
|
|
1930
|
-
this.kind = kind;
|
|
1931
|
-
this.cause = cause;
|
|
1932
|
-
}
|
|
1933
|
-
};
|
|
1934
|
-
const STRIP_HEADERS_BASE = new Set(["content-length", "host"]);
|
|
1935
|
-
function asText(body) {
|
|
1936
|
-
if (typeof body === "string") return body;
|
|
1937
|
-
if (body instanceof Uint8Array) return new TextDecoder().decode(body);
|
|
1938
|
-
if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);
|
|
1939
|
-
return "{}";
|
|
1940
|
-
}
|
|
1941
|
-
function headersToObject(h) {
|
|
1942
|
-
const out = {};
|
|
1943
|
-
if (!h) return out;
|
|
1944
|
-
if (h instanceof Headers) for (const [k, v] of h) out[k] = v;
|
|
1945
|
-
else if (Array.isArray(h)) for (const [k, v] of h) out[k] = v;
|
|
1946
|
-
else Object.assign(out, h);
|
|
1947
|
-
return out;
|
|
1948
|
-
}
|
|
1949
1856
|
function normalizeGateway(gateway) {
|
|
1950
1857
|
if (!gateway) throw new GatewayDelegateError("config", "A gateway is required for the delegate (resume needs a gateway). Pass `gateway: \"<gateway-id>\"` to createGatewayDelegate or per call.");
|
|
1951
1858
|
if (typeof gateway === "string") return {
|
|
@@ -2005,6 +1912,49 @@ function createGatewayDelegate(config) {
|
|
|
2005
1912
|
};
|
|
2006
1913
|
}));
|
|
2007
1914
|
}
|
|
1915
|
+
if (options.fallback?.mode === "server") {
|
|
1916
|
+
const resolved = [slug, ...options.fallback.models].map((s) => {
|
|
1917
|
+
const parsed = parseSlug(s);
|
|
1918
|
+
return {
|
|
1919
|
+
slug: s,
|
|
1920
|
+
modelId: parsed.modelId,
|
|
1921
|
+
info: resolveProvider(s, parsed)
|
|
1922
|
+
};
|
|
1923
|
+
});
|
|
1924
|
+
if (resolved.some((r) => r.info.gatewayProviderId !== resolved[0].info.gatewayProviderId)) {
|
|
1925
|
+
const resumeExplicitlyTrue = options.resume === true;
|
|
1926
|
+
const effectiveOptions = {
|
|
1927
|
+
...options,
|
|
1928
|
+
resume: options.resume ?? defaultResume,
|
|
1929
|
+
onResumeExpired: options.onResumeExpired ?? config.onResumeExpired
|
|
1930
|
+
};
|
|
1931
|
+
const primaryInfo = resolved[0].info;
|
|
1932
|
+
const selection = selectTransport(effectiveOptions, resumeExplicitlyTrue, primaryInfo.runCatalog, primaryInfo.gatewayPath !== false);
|
|
1933
|
+
for (const w of selection.warnings) console.warn(w);
|
|
1934
|
+
const { id: gatewayId, options: gatewayOptions } = normalizeGateway(options.gateway ?? config.gateway);
|
|
1935
|
+
const legs = resolved.map((r) => {
|
|
1936
|
+
if (r.info.gatewayPath === false) throw new GatewayDelegateError("config", `Server-side fallback cannot use "${r.slug}": it is on the unified run catalog but has no native gateway path.`);
|
|
1937
|
+
const wire = r.info.wireFormat;
|
|
1938
|
+
const plugin = plugins.get(wire);
|
|
1939
|
+
if (!plugin) throw new GatewayDelegateError("config", `No provider plugin for wire format "${wire}" (needed by "${r.slug}" for server-side fallback). Registered: ${[...plugins.keys()].join(", ") || "<none>"}.`);
|
|
1940
|
+
return {
|
|
1941
|
+
slug: r.slug,
|
|
1942
|
+
modelId: r.modelId,
|
|
1943
|
+
info: r.info,
|
|
1944
|
+
plugin
|
|
1945
|
+
};
|
|
1946
|
+
});
|
|
1947
|
+
return makeServerFallbackModel({
|
|
1948
|
+
binding: config.binding,
|
|
1949
|
+
gatewayId,
|
|
1950
|
+
gatewayOptions,
|
|
1951
|
+
legs,
|
|
1952
|
+
opts: effectiveOptions,
|
|
1953
|
+
selection,
|
|
1954
|
+
callOptions: options
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
2008
1958
|
return buildOne(slug, options).model;
|
|
2009
1959
|
};
|
|
2010
1960
|
}
|
|
@@ -2029,9 +1979,27 @@ function mergeMetadata(base, override) {
|
|
|
2029
1979
|
...override
|
|
2030
1980
|
};
|
|
2031
1981
|
}
|
|
2032
|
-
/**
|
|
2033
|
-
|
|
2034
|
-
|
|
1982
|
+
/**
|
|
1983
|
+
* Build the `cf-aig-*` request controls for a gateway-path entry. First-class
|
|
1984
|
+
* call options (`cacheTtl`, `skipCache`, `collectLog`, `metadata`, `byokAlias`,
|
|
1985
|
+
* `zdr`) are layered over the binding-style gateway options (`cacheKey`,
|
|
1986
|
+
* `eventId`, `requestTimeoutMs`, `retries`) so the gateway path reaches parity
|
|
1987
|
+
* with the run path, which forwards the whole `GatewayOptions` to `binding.run`.
|
|
1988
|
+
*/
|
|
1989
|
+
function buildGatewayControls(gatewayOptions, opts) {
|
|
1990
|
+
const metadata = mergeMetadata(gatewayOptions.metadata, opts.metadata);
|
|
1991
|
+
return {
|
|
1992
|
+
cacheTtl: opts.cacheTtl,
|
|
1993
|
+
skipCache: opts.skipCache,
|
|
1994
|
+
...gatewayOptions.cacheKey !== void 0 ? { cacheKey: gatewayOptions.cacheKey } : {},
|
|
1995
|
+
...metadata ? { metadata } : {},
|
|
1996
|
+
...opts.collectLog !== void 0 ? { collectLog: opts.collectLog } : {},
|
|
1997
|
+
...gatewayOptions.eventId !== void 0 ? { eventId: gatewayOptions.eventId } : {},
|
|
1998
|
+
...gatewayOptions.requestTimeoutMs !== void 0 ? { requestTimeoutMs: gatewayOptions.requestTimeoutMs } : {},
|
|
1999
|
+
...gatewayOptions.retries ? { retries: gatewayOptions.retries } : {},
|
|
2000
|
+
...opts.byokAlias !== void 0 ? { byokAlias: opts.byokAlias } : {},
|
|
2001
|
+
...opts.zdr !== void 0 ? { zdr: opts.zdr } : {}
|
|
2002
|
+
};
|
|
2035
2003
|
}
|
|
2036
2004
|
function makeRunFetch(binding, slug, gatewayOptions, opts, selection, callOptions) {
|
|
2037
2005
|
return (async (_input, init) => {
|
|
@@ -2041,10 +2009,12 @@ function makeRunFetch(binding, slug, gatewayOptions, opts, selection, callOption
|
|
|
2041
2009
|
const mergedMeta = mergeMetadata(gatewayOptions.metadata, opts.metadata);
|
|
2042
2010
|
if (mergedMeta) mergedGateway.metadata = mergedMeta;
|
|
2043
2011
|
if (opts.collectLog !== void 0) mergedGateway.collectLog = opts.collectLog;
|
|
2012
|
+
const extraHeaders = { ...opts.extraHeaders };
|
|
2013
|
+
if (opts.zdr !== void 0) extraHeaders["cf-aig-zdr"] = String(opts.zdr);
|
|
2044
2014
|
const runOptions = {
|
|
2045
2015
|
gateway: mergedGateway,
|
|
2046
2016
|
returnRawResponse: true,
|
|
2047
|
-
...
|
|
2017
|
+
...Object.keys(extraHeaders).length > 0 ? { extraHeaders } : {},
|
|
2048
2018
|
...init?.signal ? { signal: init.signal } : {}
|
|
2049
2019
|
};
|
|
2050
2020
|
const resp = await binding.run(slug, body, runOptions);
|
|
@@ -2068,31 +2038,24 @@ function makeRunFetch(binding, slug, gatewayOptions, opts, selection, callOption
|
|
|
2068
2038
|
});
|
|
2069
2039
|
}
|
|
2070
2040
|
function makeGatewayFetch(binding, info, gatewayId, gatewayOptions, opts, selection, callOptions) {
|
|
2071
|
-
const strip = new Set(STRIP_HEADERS_BASE);
|
|
2072
|
-
if (!opts.byok) for (const h of info.authHeaders) strip.add(h.toLowerCase());
|
|
2073
2041
|
return (async (input, init) => {
|
|
2074
2042
|
const rawUrl = typeof input === "string" ? input : input.toString();
|
|
2075
2043
|
const endpoint = info.transformEndpoint ? info.transformEndpoint(rawUrl) : new URL(rawUrl).pathname.replace(/^\//, "") + (new URL(rawUrl).search || "");
|
|
2076
2044
|
const body = JSON.parse(asText(init?.body));
|
|
2077
|
-
const
|
|
2078
|
-
|
|
2079
|
-
if (opts.extraHeaders) Object.assign(headers, opts.extraHeaders);
|
|
2080
|
-
if (opts.cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(opts.cacheTtl);
|
|
2081
|
-
if (opts.skipCache) headers["cf-aig-skip-cache"] = "true";
|
|
2082
|
-
const metadata = mergeMetadata(gatewayOptions.metadata, opts.metadata);
|
|
2083
|
-
if (metadata) headers["cf-aig-metadata"] = serializeMetadata(metadata);
|
|
2084
|
-
if (opts.collectLog !== void 0) headers["cf-aig-collect-log"] = String(opts.collectLog);
|
|
2085
|
-
const primary = {
|
|
2086
|
-
provider: info.gatewayProviderId,
|
|
2045
|
+
const primary = buildGatewayEntry({
|
|
2046
|
+
providerId: info.gatewayProviderId,
|
|
2087
2047
|
endpoint,
|
|
2088
|
-
headers,
|
|
2089
|
-
|
|
2090
|
-
|
|
2048
|
+
initHeaders: init?.headers,
|
|
2049
|
+
body,
|
|
2050
|
+
...opts.byok ? {} : { stripAuthHeaders: info.authHeaders },
|
|
2051
|
+
...opts.extraHeaders ? { extraHeaders: opts.extraHeaders } : {},
|
|
2052
|
+
cache: buildGatewayControls(gatewayOptions, opts)
|
|
2053
|
+
});
|
|
2091
2054
|
const entries = [primary];
|
|
2092
2055
|
if (opts.fallback?.mode === "server") for (const fb of opts.fallback.models) {
|
|
2093
2056
|
const fbParsed = parseSlug(fb);
|
|
2094
2057
|
const fbInfo = resolveProvider(fb, fbParsed);
|
|
2095
|
-
if (fbInfo.gatewayProviderId !== info.gatewayProviderId) throw new GatewayDelegateError("
|
|
2058
|
+
if (fbInfo.gatewayProviderId !== info.gatewayProviderId) throw new GatewayDelegateError("dispatch", `Internal: cross-vendor server fallback (${info.gatewayProviderId} → ${fbInfo.gatewayProviderId}) reached the same-vendor gateway fetch. This should have been routed through makeServerFallbackModel.`);
|
|
2096
2059
|
entries.push({
|
|
2097
2060
|
...primary,
|
|
2098
2061
|
query: {
|
|
@@ -2109,6 +2072,92 @@ function makeGatewayFetch(binding, info, gatewayId, gatewayOptions, opts, select
|
|
|
2109
2072
|
return resp;
|
|
2110
2073
|
});
|
|
2111
2074
|
}
|
|
2075
|
+
/** Sentinel thrown by the capture fetch to stop a leg before it hits the network. */
|
|
2076
|
+
var ServerFallbackCaptureStop = class extends Error {};
|
|
2077
|
+
/**
|
|
2078
|
+
* Cross-vendor server-side fallback via the gateway universal endpoint.
|
|
2079
|
+
*
|
|
2080
|
+
* Unlike same-vendor fallback (which just swaps `model` in one body), legs here
|
|
2081
|
+
* are DIFFERENT providers with possibly different wire formats, so each leg's own
|
|
2082
|
+
* `@ai-sdk` model must shape its native request. Mirrors ai-gateway-provider's
|
|
2083
|
+
* `processModelRequest`: run each leg's builder with a capture `fetch` that
|
|
2084
|
+
* sentinel-throws before the network, reshape each captured request into a
|
|
2085
|
+
* `{ provider, endpoint, headers, query }` entry, dispatch all N as one
|
|
2086
|
+
* `env.AI.gateway(id).run([...])`, then read `cf-aig-step` to find the leg the
|
|
2087
|
+
* gateway actually served and feed the raw response back into that leg's model.
|
|
2088
|
+
*/
|
|
2089
|
+
function makeServerFallbackModel(params) {
|
|
2090
|
+
const { binding, gatewayId, gatewayOptions, legs, opts, selection, callOptions } = params;
|
|
2091
|
+
const first = legs[0];
|
|
2092
|
+
const refModel = first.plugin.create({
|
|
2093
|
+
modelId: first.modelId,
|
|
2094
|
+
fetch: (async () => {
|
|
2095
|
+
throw new ServerFallbackCaptureStop();
|
|
2096
|
+
}),
|
|
2097
|
+
...first.info.baseURL ? { baseURL: first.info.baseURL } : {}
|
|
2098
|
+
});
|
|
2099
|
+
const cache = buildGatewayControls(gatewayOptions, opts);
|
|
2100
|
+
const dispatch = async (method, options) => {
|
|
2101
|
+
const entries = [];
|
|
2102
|
+
for (const leg of legs) {
|
|
2103
|
+
let captured;
|
|
2104
|
+
const captureFetch = (async (input, init) => {
|
|
2105
|
+
captured = {
|
|
2106
|
+
url: typeof input === "string" ? input : input.toString(),
|
|
2107
|
+
init
|
|
2108
|
+
};
|
|
2109
|
+
throw new ServerFallbackCaptureStop();
|
|
2110
|
+
});
|
|
2111
|
+
const model = leg.plugin.create({
|
|
2112
|
+
modelId: leg.modelId,
|
|
2113
|
+
fetch: captureFetch,
|
|
2114
|
+
...leg.info.baseURL ? { baseURL: leg.info.baseURL } : {}
|
|
2115
|
+
});
|
|
2116
|
+
try {
|
|
2117
|
+
await model[method](options);
|
|
2118
|
+
} catch (e) {
|
|
2119
|
+
if (!(e instanceof ServerFallbackCaptureStop)) throw e;
|
|
2120
|
+
}
|
|
2121
|
+
if (!captured) throw new GatewayDelegateError("dispatch", `Server-side fallback leg "${leg.slug}" produced no request to dispatch.`);
|
|
2122
|
+
const url = captured.url;
|
|
2123
|
+
const endpoint = leg.info.transformEndpoint ? leg.info.transformEndpoint(url) : new URL(url).pathname.replace(/^\//, "") + (new URL(url).search || "");
|
|
2124
|
+
const body = JSON.parse(asText(captured.init?.body));
|
|
2125
|
+
entries.push(buildGatewayEntry({
|
|
2126
|
+
providerId: leg.info.gatewayProviderId,
|
|
2127
|
+
endpoint,
|
|
2128
|
+
initHeaders: captured.init?.headers,
|
|
2129
|
+
body,
|
|
2130
|
+
...opts.byok ? {} : { stripAuthHeaders: leg.info.authHeaders },
|
|
2131
|
+
...opts.extraHeaders ? { extraHeaders: opts.extraHeaders } : {},
|
|
2132
|
+
cache
|
|
2133
|
+
}));
|
|
2134
|
+
}
|
|
2135
|
+
const gw = binding.gateway(gatewayId);
|
|
2136
|
+
const abortSignal = options.abortSignal;
|
|
2137
|
+
const runOptions = {};
|
|
2138
|
+
if (abortSignal) runOptions.signal = abortSignal;
|
|
2139
|
+
const resp = await gw.run(entries, runOptions);
|
|
2140
|
+
fireDispatch(resp, selection, callOptions);
|
|
2141
|
+
const winner = legs[Number.parseInt(resp.headers.get("cf-aig-step") ?? "0", 10)] ?? first;
|
|
2142
|
+
return winner.plugin.create({
|
|
2143
|
+
modelId: winner.modelId,
|
|
2144
|
+
fetch: (async () => resp),
|
|
2145
|
+
...winner.info.baseURL ? { baseURL: winner.info.baseURL } : {}
|
|
2146
|
+
})[method](options);
|
|
2147
|
+
};
|
|
2148
|
+
return {
|
|
2149
|
+
specificationVersion: "v3",
|
|
2150
|
+
provider: refModel.provider,
|
|
2151
|
+
modelId: refModel.modelId,
|
|
2152
|
+
supportedUrls: refModel.supportedUrls,
|
|
2153
|
+
doGenerate(options) {
|
|
2154
|
+
return dispatch("doGenerate", options);
|
|
2155
|
+
},
|
|
2156
|
+
doStream(options) {
|
|
2157
|
+
return dispatch("doStream", options);
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2112
2161
|
//#endregion
|
|
2113
2162
|
//#region src/index.ts
|
|
2114
2163
|
/**
|