workers-ai-provider 3.3.0 → 4.0.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/src/streaming.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type {
2
- LanguageModelV3FinishReason,
3
- LanguageModelV3StreamPart,
4
- LanguageModelV3Usage,
2
+ LanguageModelV4FinishReason,
3
+ LanguageModelV4StreamPart,
4
+ LanguageModelV4Usage,
5
5
  } from "@ai-sdk/provider";
6
6
  import { SSEDecoder } from "@cloudflare/gateway-core";
7
7
  import { generateId } from "ai";
@@ -15,16 +15,16 @@ import {
15
15
  } from "./utils";
16
16
 
17
17
  /**
18
- * Prepend a stream-start event to an existing LanguageModelV3 stream.
18
+ * Prepend a stream-start event to an existing LanguageModelV4 stream.
19
19
  * Uses pipeThrough for proper backpressure and error propagation.
20
20
  */
21
21
  export function prependStreamStart(
22
- source: ReadableStream<LanguageModelV3StreamPart>,
23
- warnings: LanguageModelV3StreamPart extends { type: "stream-start" } ? never : unknown,
24
- ): ReadableStream<LanguageModelV3StreamPart> {
22
+ source: ReadableStream<LanguageModelV4StreamPart>,
23
+ warnings: LanguageModelV4StreamPart extends { type: "stream-start" } ? never : unknown,
24
+ ): ReadableStream<LanguageModelV4StreamPart> {
25
25
  let sentStart = false;
26
26
  return source.pipeThrough(
27
- new TransformStream<LanguageModelV3StreamPart, LanguageModelV3StreamPart>({
27
+ new TransformStream<LanguageModelV4StreamPart, LanguageModelV4StreamPart>({
28
28
  transform(chunk, controller) {
29
29
  if (!sentStart) {
30
30
  sentStart = true;
@@ -59,7 +59,7 @@ function isNullFinalizationChunk(tc: Record<string, unknown>): boolean {
59
59
  }
60
60
 
61
61
  /**
62
- * Maps a Workers AI SSE stream into AI SDK V3 LanguageModelV3StreamPart events.
62
+ * Maps a Workers AI SSE stream into AI SDK LanguageModelV4StreamPart events.
63
63
  *
64
64
  * Uses a TransformStream pipeline for proper backpressure — chunks are emitted
65
65
  * one at a time as the downstream consumer pulls, not buffered eagerly.
@@ -74,7 +74,7 @@ export function getMappedStream(
74
74
  tools: Array<{ function: { name?: string } }> | undefined;
75
75
  toolChoice: unknown;
76
76
  },
77
- ): ReadableStream<LanguageModelV3StreamPart> {
77
+ ): ReadableStream<LanguageModelV4StreamPart> {
78
78
  const rawStream =
79
79
  response instanceof ReadableStream
80
80
  ? response
@@ -97,7 +97,7 @@ export function getMappedStream(
97
97
  let anyToolCallStarted = false;
98
98
 
99
99
  // State shared across the transform
100
- let usage: LanguageModelV3Usage = {
100
+ let usage: LanguageModelV4Usage = {
101
101
  outputTokens: { total: 0, text: undefined, reasoning: undefined },
102
102
  inputTokens: {
103
103
  total: 0,
@@ -109,7 +109,7 @@ export function getMappedStream(
109
109
  };
110
110
  let textId: string | null = null;
111
111
  let reasoningId: string | null = null;
112
- let finishReason: LanguageModelV3FinishReason | null = null;
112
+ let finishReason: LanguageModelV4FinishReason | null = null;
113
113
  let receivedDone = false;
114
114
  let receivedAnyData = false;
115
115
 
@@ -125,9 +125,9 @@ export function getMappedStream(
125
125
  // Step 1: Decode bytes into SSE lines
126
126
  const sseStream = rawStream.pipeThrough(new SSEDecoder());
127
127
 
128
- // Step 2: Transform SSE events into LanguageModelV3StreamPart
128
+ // Step 2: Transform SSE events into LanguageModelV4StreamPart
129
129
  return sseStream.pipeThrough(
130
- new TransformStream<string, LanguageModelV3StreamPart>({
130
+ new TransformStream<string, LanguageModelV4StreamPart>({
131
131
  transform(data, controller) {
132
132
  if (!data || data === "[DONE]") {
133
133
  if (data === "[DONE]") receivedDone = true;
@@ -315,12 +315,12 @@ export function getMappedStream(
315
315
 
316
316
  // Detect premature termination
317
317
  const effectiveFinishReason = salvagedToolCalls
318
- ? ({ unified: "tool-calls", raw: "stop" } as LanguageModelV3FinishReason)
318
+ ? ({ unified: "tool-calls", raw: "stop" } as LanguageModelV4FinishReason)
319
319
  : !receivedDone && receivedAnyData && !finishReason
320
320
  ? ({
321
321
  unified: "error",
322
322
  raw: "stream-truncated",
323
- } as LanguageModelV3FinishReason)
323
+ } as LanguageModelV4FinishReason)
324
324
  : (finishReason ?? { unified: "stop", raw: "stop" });
325
325
 
326
326
  controller.enqueue({
@@ -337,7 +337,7 @@ export function getMappedStream(
337
337
  */
338
338
  function closeToolCall(
339
339
  index: number,
340
- controller: TransformStreamDefaultController<LanguageModelV3StreamPart>,
340
+ controller: TransformStreamDefaultController<LanguageModelV4StreamPart>,
341
341
  ) {
342
342
  const tc = activeToolCalls.get(index);
343
343
  if (!tc || closedToolCalls.has(index)) return;
@@ -367,7 +367,7 @@ export function getMappedStream(
367
367
  */
368
368
  function emitToolCallDeltas(
369
369
  toolCalls: Record<string, unknown>[],
370
- controller: TransformStreamDefaultController<LanguageModelV3StreamPart>,
370
+ controller: TransformStreamDefaultController<LanguageModelV4StreamPart>,
371
371
  ) {
372
372
  for (const tc of toolCalls) {
373
373
  if (isNullFinalizationChunk(tc)) {
package/src/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { LanguageModelV3, LanguageModelV3ToolCall } from "@ai-sdk/provider";
1
+ import type { LanguageModelV4, LanguageModelV4ToolCall } from "@ai-sdk/provider";
2
2
  import {
3
3
  getToolNames,
4
4
  isForcedToolChoice,
@@ -332,8 +332,8 @@ export function buildJsonSchemaPayload(
332
332
  // ---------------------------------------------------------------------------
333
333
 
334
334
  export function prepareToolsAndToolChoice(
335
- tools: Parameters<LanguageModelV3["doGenerate"]>[0]["tools"],
336
- toolChoice: Parameters<LanguageModelV3["doGenerate"]>[0]["toolChoice"],
335
+ tools: Parameters<LanguageModelV4["doGenerate"]>[0]["tools"],
336
+ toolChoice: Parameters<LanguageModelV4["doGenerate"]>[0]["toolChoice"],
337
337
  ) {
338
338
  if (tools == null) {
339
339
  return { tool_choice: undefined, tools: undefined };
@@ -476,7 +476,7 @@ function mergePartialToolCalls(partialCalls: PartialToolCall[]) {
476
476
  return Object.values(mergedCallsByIndex);
477
477
  }
478
478
 
479
- function processToolCall(toolCall: FlatToolCall | OpenAIToolCall): LanguageModelV3ToolCall {
479
+ function processToolCall(toolCall: FlatToolCall | OpenAIToolCall): LanguageModelV4ToolCall {
480
480
  // OpenAI format: has function.name (the key discriminator)
481
481
  const fn =
482
482
  "function" in toolCall && typeof toolCall.function === "object" && toolCall.function
@@ -508,7 +508,7 @@ function processToolCall(toolCall: FlatToolCall | OpenAIToolCall): LanguageModel
508
508
  };
509
509
  }
510
510
 
511
- export function processToolCalls(output: Record<string, unknown>): LanguageModelV3ToolCall[] {
511
+ export function processToolCalls(output: Record<string, unknown>): LanguageModelV4ToolCall[] {
512
512
  if (output.tool_calls && Array.isArray(output.tool_calls)) {
513
513
  return output.tool_calls.map((toolCall: FlatToolCall | OpenAIToolCall) =>
514
514
  processToolCall(toolCall),
@@ -540,12 +540,12 @@ export function processPartialToolCalls(partialToolCalls: PartialToolCall[]) {
540
540
  *
541
541
  * The recovery logic (which JSON shapes count as a leaked call) lives in
542
542
  * `@cloudflare/gateway-core`; this wrapper only layers the framework id on each
543
- * neutral result so the existing `LanguageModelV3ToolCall` shape is preserved.
543
+ * neutral result so the existing `LanguageModelV4ToolCall` shape is preserved.
544
544
  */
545
545
  export function parseLeakedToolCalls(
546
546
  text: string,
547
547
  knownToolNames: Set<string>,
548
- ): LanguageModelV3ToolCall[] {
548
+ ): LanguageModelV4ToolCall[] {
549
549
  return coreParseLeakedToolCalls(text, knownToolNames).map((call) => ({
550
550
  input: call.input,
551
551
  toolCallId: createAISDKToolCallId(undefined),
@@ -584,7 +584,7 @@ export function salvageToolCallsFromText(
584
584
  tools: Array<{ function: { name?: string } }> | undefined;
585
585
  toolChoice: unknown;
586
586
  },
587
- ): LanguageModelV3ToolCall[] | null {
587
+ ): LanguageModelV4ToolCall[] | null {
588
588
  if (!isForcedToolChoice(context.toolChoice)) return null;
589
589
 
590
590
  // Never override real tool calls.
@@ -1,4 +1,4 @@
1
- import type { LanguageModelV3, SharedV3Warning, LanguageModelV3StreamPart } from "@ai-sdk/provider";
1
+ import type { LanguageModelV4, SharedV4Warning, LanguageModelV4StreamPart } from "@ai-sdk/provider";
2
2
  import { generateId } from "ai";
3
3
  import { convertToWorkersAIChatMessages } from "./convert-to-workersai-chat-messages";
4
4
  import { mapWorkersAIFinishReason } from "./map-workersai-finish-reason";
@@ -24,8 +24,33 @@ type WorkersAIChatConfig = {
24
24
  isBinding: boolean;
25
25
  };
26
26
 
27
- export class WorkersAIChatLanguageModel implements LanguageModelV3 {
28
- readonly specificationVersion = "v3";
27
+ /**
28
+ * Map the unified `reasoning` call option (spec v4) to Workers AI's
29
+ * `reasoning_effort`. Workers AI accepts "low" | "medium" | "high" | null
30
+ * (null disables reasoning), so `minimal` and `xhigh` are clamped to the
31
+ * nearest supported effort. `provider-default` (and absence) returns
32
+ * undefined so the settings-level `reasoning_effort` still applies.
33
+ */
34
+ function mapUnifiedReasoningEffort(
35
+ reasoning: Parameters<LanguageModelV4["doGenerate"]>[0]["reasoning"],
36
+ ): "low" | "medium" | "high" | null | undefined {
37
+ switch (reasoning) {
38
+ case undefined:
39
+ case "provider-default":
40
+ return undefined;
41
+ case "none":
42
+ return null;
43
+ case "minimal":
44
+ return "low";
45
+ case "xhigh":
46
+ return "high";
47
+ default:
48
+ return reasoning;
49
+ }
50
+ }
51
+
52
+ export class WorkersAIChatLanguageModel implements LanguageModelV4 {
53
+ readonly specificationVersion = "v4";
29
54
  readonly defaultObjectGenerationMode = "json";
30
55
 
31
56
  readonly supportedUrls: Record<string, RegExp[]> | PromiseLike<Record<string, RegExp[]>> = {};
@@ -59,10 +84,11 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
59
84
  frequencyPenalty,
60
85
  presencePenalty,
61
86
  seed,
62
- }: Parameters<LanguageModelV3["doGenerate"]>[0]) {
87
+ reasoning,
88
+ }: Parameters<LanguageModelV4["doGenerate"]>[0]) {
63
89
  const type = responseFormat?.type ?? "text";
64
90
 
65
- const warnings: SharedV3Warning[] = [];
91
+ const warnings: SharedV4Warning[] = [];
66
92
 
67
93
  if (frequencyPenalty != null) {
68
94
  warnings.push({ feature: "frequencyPenalty", type: "unsupported" });
@@ -72,6 +98,16 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
72
98
  warnings.push({ feature: "presencePenalty", type: "unsupported" });
73
99
  }
74
100
 
101
+ if (reasoning === "minimal" || reasoning === "xhigh") {
102
+ warnings.push({
103
+ type: "compatibility",
104
+ feature: "reasoning",
105
+ details:
106
+ `Workers AI supports reasoning_effort "low" | "medium" | "high"; ` +
107
+ `"${reasoning}" was mapped to "${reasoning === "minimal" ? "low" : "high"}".`,
108
+ });
109
+ }
110
+
75
111
  const baseArgs = {
76
112
  max_tokens: maxOutputTokens,
77
113
  model: this.modelId,
@@ -145,11 +181,20 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
145
181
  *
146
182
  * `reasoning_effort: null` is a valid value ("disable reasoning"), so we
147
183
  * check `!== undefined` rather than truthiness.
184
+ *
185
+ * The unified `reasoning` call option (spec v4) is mapped onto
186
+ * `reasoning_effort` between the two: an explicit per-call
187
+ * `providerOptions["workers-ai"].reasoning_effort` wins, then the unified
188
+ * option, then settings.
148
189
  */
149
190
  private buildRunInputs(
150
191
  args: ReturnType<typeof this.getArgs>["args"],
151
192
  messages: ReturnType<typeof convertToWorkersAIChatMessages>["messages"],
152
- options?: { stream?: boolean; providerOptions?: Record<string, unknown> },
193
+ options?: {
194
+ stream?: boolean;
195
+ providerOptions?: Record<string, unknown>;
196
+ reasoning?: Parameters<LanguageModelV4["doGenerate"]>[0]["reasoning"];
197
+ },
153
198
  ) {
154
199
  // The AI SDK types this as `Record<string, JSONObject>` but we defensively
155
200
  // accept anything and only treat it as a lookup if it's a plain object.
@@ -159,10 +204,13 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
159
204
  rawPerCall !== null && typeof rawPerCall === "object" && !Array.isArray(rawPerCall)
160
205
  ? (rawPerCall as Record<string, unknown>)
161
206
  : {};
207
+ const unifiedReasoningEffort = mapUnifiedReasoningEffort(options?.reasoning);
162
208
  const reasoningEffort =
163
209
  "reasoning_effort" in perCall
164
210
  ? perCall.reasoning_effort
165
- : this.settings.reasoning_effort;
211
+ : unifiedReasoningEffort !== undefined
212
+ ? unifiedReasoningEffort
213
+ : this.settings.reasoning_effort;
166
214
  const chatTemplateKwargs =
167
215
  "chat_template_kwargs" in perCall
168
216
  ? perCall.chat_template_kwargs
@@ -229,7 +277,7 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
229
277
  private extractContent(
230
278
  outputRecord: Record<string, unknown>,
231
279
  args: ReturnType<typeof this.getArgs>["args"],
232
- warnings: SharedV3Warning[],
280
+ warnings: SharedV4Warning[],
233
281
  ) {
234
282
  const choices = outputRecord.choices as
235
283
  | Array<{ message?: { reasoning_content?: string; reasoning?: string } }>
@@ -268,13 +316,14 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
268
316
  }
269
317
 
270
318
  async doGenerate(
271
- options: Parameters<LanguageModelV3["doGenerate"]>[0],
272
- ): Promise<Awaited<ReturnType<LanguageModelV3["doGenerate"]>>> {
319
+ options: Parameters<LanguageModelV4["doGenerate"]>[0],
320
+ ): Promise<Awaited<ReturnType<LanguageModelV4["doGenerate"]>>> {
273
321
  const { args, warnings } = this.getArgs(options);
274
322
  const { messages } = convertToWorkersAIChatMessages(options.prompt);
275
323
 
276
324
  const inputs = this.buildRunInputs(args, messages, {
277
325
  providerOptions: options.providerOptions,
326
+ reasoning: options.reasoning,
278
327
  });
279
328
  const runOptions = this.getRunOptions();
280
329
 
@@ -325,14 +374,15 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
325
374
  }
326
375
 
327
376
  async doStream(
328
- options: Parameters<LanguageModelV3["doStream"]>[0],
329
- ): Promise<Awaited<ReturnType<LanguageModelV3["doStream"]>>> {
377
+ options: Parameters<LanguageModelV4["doStream"]>[0],
378
+ ): Promise<Awaited<ReturnType<LanguageModelV4["doStream"]>>> {
330
379
  const { args, warnings } = this.getArgs(options);
331
380
  const { messages } = convertToWorkersAIChatMessages(options.prompt);
332
381
 
333
382
  const inputs = this.buildRunInputs(args, messages, {
334
383
  stream: true,
335
384
  providerOptions: options.providerOptions,
385
+ reasoning: options.reasoning,
336
386
  });
337
387
  const runOptions = this.getRunOptions();
338
388
 
@@ -381,11 +431,11 @@ export class WorkersAIChatLanguageModel implements LanguageModelV3 {
381
431
  let reasoningId: string | null = null;
382
432
 
383
433
  return {
384
- stream: new ReadableStream<LanguageModelV3StreamPart>({
434
+ stream: new ReadableStream<LanguageModelV4StreamPart>({
385
435
  start(controller) {
386
436
  controller.enqueue({
387
437
  type: "stream-start",
388
- warnings: warnings as SharedV3Warning[],
438
+ warnings: warnings as SharedV4Warning[],
389
439
  });
390
440
 
391
441
  if (reasoningContent) {
@@ -1,7 +1,7 @@
1
1
  import type {
2
- EmbeddingModelV3,
3
- EmbeddingModelV3CallOptions,
4
- EmbeddingModelV3Result,
2
+ EmbeddingModelV4,
3
+ EmbeddingModelV4CallOptions,
4
+ EmbeddingModelV4Result,
5
5
  } from "@ai-sdk/provider";
6
6
  import { TooManyEmbeddingValuesForCallError } from "@ai-sdk/provider";
7
7
  import { normalizeBindingError } from "./workersai-error";
@@ -24,8 +24,8 @@ export type WorkersAIEmbeddingSettings = {
24
24
  [key: string]: unknown;
25
25
  };
26
26
 
27
- export class WorkersAIEmbeddingModel implements EmbeddingModelV3 {
28
- readonly specificationVersion = "v3";
27
+ export class WorkersAIEmbeddingModel implements EmbeddingModelV4 {
28
+ readonly specificationVersion = "v4";
29
29
  readonly modelId: EmbeddingModels;
30
30
  private readonly config: WorkersAIEmbeddingConfig;
31
31
  private readonly settings: WorkersAIEmbeddingSettings;
@@ -56,7 +56,7 @@ export class WorkersAIEmbeddingModel implements EmbeddingModelV3 {
56
56
  async doEmbed({
57
57
  values,
58
58
  abortSignal,
59
- }: EmbeddingModelV3CallOptions): Promise<EmbeddingModelV3Result> {
59
+ }: EmbeddingModelV4CallOptions): Promise<EmbeddingModelV4Result> {
60
60
  if (values.length > this.maxEmbeddingsPerCall) {
61
61
  throw new TooManyEmbeddingValuesForCallError({
62
62
  maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
@@ -1,4 +1,4 @@
1
- import type { ImageModelV3, SharedV3Warning } from "@ai-sdk/provider";
1
+ import type { ImageModelV4, SharedV4Warning } from "@ai-sdk/provider";
2
2
  import { normalizeBindingError } from "./workersai-error";
3
3
  import type { WorkersAIImageSettings } from "./workersai-image-settings";
4
4
  import type { ImageGenerationModels } from "./workersai-models";
@@ -9,8 +9,8 @@ export type WorkersAIImageConfig = {
9
9
  gateway?: GatewayOptions;
10
10
  };
11
11
 
12
- export class WorkersAIImageModel implements ImageModelV3 {
13
- readonly specificationVersion = "v3";
12
+ export class WorkersAIImageModel implements ImageModelV4 {
13
+ readonly specificationVersion = "v4";
14
14
 
15
15
  get maxImagesPerCall(): number {
16
16
  return this.settings.maxImagesPerCall ?? 1;
@@ -33,12 +33,12 @@ export class WorkersAIImageModel implements ImageModelV3 {
33
33
  aspectRatio,
34
34
  seed,
35
35
  abortSignal,
36
- }: Parameters<ImageModelV3["doGenerate"]>[0]): Promise<
37
- Awaited<ReturnType<ImageModelV3["doGenerate"]>>
36
+ }: Parameters<ImageModelV4["doGenerate"]>[0]): Promise<
37
+ Awaited<ReturnType<ImageModelV4["doGenerate"]>>
38
38
  > {
39
39
  const { width, height } = getDimensionsFromSizeString(size);
40
40
 
41
- const warnings: Array<SharedV3Warning> = [];
41
+ const warnings: Array<SharedV4Warning> = [];
42
42
 
43
43
  if (aspectRatio != null) {
44
44
  warnings.push({
@@ -1,4 +1,4 @@
1
- import type { RerankingModelV3, SharedV3Warning } from "@ai-sdk/provider";
1
+ import type { RerankingModelV4, SharedV4Warning } from "@ai-sdk/provider";
2
2
  import { normalizeBindingError } from "./workersai-error";
3
3
  import type { WorkersAIRerankingSettings } from "./workersai-reranking-settings";
4
4
  import type { RerankingModels } from "./workersai-models";
@@ -10,7 +10,7 @@ export type WorkersAIRerankingConfig = {
10
10
  };
11
11
 
12
12
  /**
13
- * Workers AI reranking model implementing the AI SDK's `RerankingModelV3` interface.
13
+ * Workers AI reranking model implementing the AI SDK's `RerankingModelV4` interface.
14
14
  *
15
15
  * Supports BGE reranker models (`@cf/baai/bge-reranker-base`, `bge-reranker-v2-m3`).
16
16
  *
@@ -18,8 +18,8 @@ export type WorkersAIRerankingConfig = {
18
18
  * - Input: `{ query, contexts: [{ text }], top_k? }`
19
19
  * - Output: `{ response: [{ id, score }] }`
20
20
  */
21
- export class WorkersAIRerankingModel implements RerankingModelV3 {
22
- readonly specificationVersion = "v3";
21
+ export class WorkersAIRerankingModel implements RerankingModelV4 {
22
+ readonly specificationVersion = "v4";
23
23
 
24
24
  get provider(): string {
25
25
  return this.config.provider;
@@ -32,11 +32,11 @@ export class WorkersAIRerankingModel implements RerankingModelV3 {
32
32
  ) {}
33
33
 
34
34
  async doRerank(
35
- options: Parameters<RerankingModelV3["doRerank"]>[0],
36
- ): Promise<Awaited<ReturnType<RerankingModelV3["doRerank"]>>> {
35
+ options: Parameters<RerankingModelV4["doRerank"]>[0],
36
+ ): Promise<Awaited<ReturnType<RerankingModelV4["doRerank"]>>> {
37
37
  const { documents, query, topN, abortSignal } = options;
38
38
 
39
- const warnings: Array<SharedV3Warning> = [];
39
+ const warnings: Array<SharedV4Warning> = [];
40
40
 
41
41
  // Convert AI SDK documents to Workers AI contexts format
42
42
  const contexts = documentsToContexts(documents, warnings);
@@ -100,8 +100,8 @@ export class WorkersAIRerankingModel implements RerankingModelV3 {
100
100
  * - `{ type: 'object', values: JSONObject[] }` — JSON objects (stringified for Workers AI)
101
101
  */
102
102
  function documentsToContexts(
103
- documents: Parameters<RerankingModelV3["doRerank"]>[0]["documents"],
104
- warnings: Array<SharedV3Warning>,
103
+ documents: Parameters<RerankingModelV4["doRerank"]>[0]["documents"],
104
+ warnings: Array<SharedV4Warning>,
105
105
  ): Array<{ text: string }> {
106
106
  if (documents.type === "text") {
107
107
  return documents.values.map((text) => ({ text }));
@@ -1,4 +1,4 @@
1
- import type { SpeechModelV3, SharedV3Warning } from "@ai-sdk/provider";
1
+ import type { SpeechModelV4, SharedV4Warning } from "@ai-sdk/provider";
2
2
  import { apiCallErrorFromResponse, normalizeBindingError } from "./workersai-error";
3
3
  import type { WorkersAISpeechSettings } from "./workersai-speech-settings";
4
4
  import type { SpeechModels } from "./workersai-models";
@@ -10,13 +10,13 @@ export type WorkersAISpeechConfig = {
10
10
  };
11
11
 
12
12
  /**
13
- * Workers AI speech (text-to-speech) model implementing the AI SDK's `SpeechModelV3` interface.
13
+ * Workers AI speech (text-to-speech) model implementing the AI SDK's `SpeechModelV4` interface.
14
14
  *
15
15
  * Currently supports Deepgram Aura-1 (`@cf/deepgram/aura-1`).
16
16
  * The model accepts `{ text, voice?, speed? }` and returns raw audio bytes.
17
17
  */
18
- export class WorkersAISpeechModel implements SpeechModelV3 {
19
- readonly specificationVersion = "v3";
18
+ export class WorkersAISpeechModel implements SpeechModelV4 {
19
+ readonly specificationVersion = "v4";
20
20
 
21
21
  get provider(): string {
22
22
  return this.config.provider;
@@ -29,11 +29,11 @@ export class WorkersAISpeechModel implements SpeechModelV3 {
29
29
  ) {}
30
30
 
31
31
  async doGenerate(
32
- options: Parameters<SpeechModelV3["doGenerate"]>[0],
33
- ): Promise<Awaited<ReturnType<SpeechModelV3["doGenerate"]>>> {
32
+ options: Parameters<SpeechModelV4["doGenerate"]>[0],
33
+ ): Promise<Awaited<ReturnType<SpeechModelV4["doGenerate"]>>> {
34
34
  const { text, voice, speed, abortSignal } = options;
35
35
 
36
- const warnings: Array<SharedV3Warning> = [];
36
+ const warnings: Array<SharedV4Warning> = [];
37
37
 
38
38
  if (options.instructions) {
39
39
  warnings.push({
@@ -1,4 +1,4 @@
1
- import type { TranscriptionModelV3, SharedV3Warning } from "@ai-sdk/provider";
1
+ import type { TranscriptionModelV4, SharedV4Warning } from "@ai-sdk/provider";
2
2
  import type { WorkersAITranscriptionSettings } from "./workersai-transcription-settings";
3
3
  import type { TranscriptionModels } from "./workersai-models";
4
4
  import { createRunBinary, type CreateRunConfig } from "./utils";
@@ -21,14 +21,14 @@ export type WorkersAITranscriptionConfig = {
21
21
  };
22
22
 
23
23
  /**
24
- * Workers AI transcription model implementing the AI SDK's `TranscriptionModelV3` interface.
24
+ * Workers AI transcription model implementing the AI SDK's `TranscriptionModelV4` interface.
25
25
  *
26
26
  * Supports:
27
27
  * - Whisper models (`@cf/openai/whisper`, `whisper-tiny-en`, `whisper-large-v3-turbo`)
28
28
  * - Deepgram Nova-3 (`@cf/deepgram/nova-3`) — uses a different input/output format
29
29
  */
30
- export class WorkersAITranscriptionModel implements TranscriptionModelV3 {
31
- readonly specificationVersion = "v3";
30
+ export class WorkersAITranscriptionModel implements TranscriptionModelV4 {
31
+ readonly specificationVersion = "v4";
32
32
 
33
33
  get provider(): string {
34
34
  return this.config.provider;
@@ -41,11 +41,11 @@ export class WorkersAITranscriptionModel implements TranscriptionModelV3 {
41
41
  ) {}
42
42
 
43
43
  async doGenerate(
44
- options: Parameters<TranscriptionModelV3["doGenerate"]>[0],
45
- ): Promise<Awaited<ReturnType<TranscriptionModelV3["doGenerate"]>>> {
44
+ options: Parameters<TranscriptionModelV4["doGenerate"]>[0],
45
+ ): Promise<Awaited<ReturnType<TranscriptionModelV4["doGenerate"]>>> {
46
46
  const { audio, mediaType, abortSignal } = options;
47
47
 
48
- const warnings: Array<SharedV3Warning> = [];
48
+ const warnings: Array<SharedV4Warning> = [];
49
49
 
50
50
  // The AI SDK always converts audio to Uint8Array via
51
51
  // convertDataContentToUint8Array before calling doGenerate.
@@ -114,8 +114,8 @@ export class WorkersAITranscriptionModel implements TranscriptionModelV3 {
114
114
 
115
115
  private normalizeWhisperResponse(
116
116
  raw: Record<string, unknown>,
117
- warnings: Array<SharedV3Warning>,
118
- ): Awaited<ReturnType<TranscriptionModelV3["doGenerate"]>> {
117
+ warnings: Array<SharedV4Warning>,
118
+ ): Awaited<ReturnType<TranscriptionModelV4["doGenerate"]>> {
119
119
  const text = (raw.text as string) ?? "";
120
120
 
121
121
  // Build segments from Whisper's various formats
@@ -199,8 +199,8 @@ export class WorkersAITranscriptionModel implements TranscriptionModelV3 {
199
199
 
200
200
  private normalizeNova3Response(
201
201
  raw: Record<string, unknown>,
202
- warnings: Array<SharedV3Warning>,
203
- ): Awaited<ReturnType<TranscriptionModelV3["doGenerate"]>> {
202
+ warnings: Array<SharedV4Warning>,
203
+ ): Awaited<ReturnType<TranscriptionModelV4["doGenerate"]>> {
204
204
  // Nova-3 format: { results: { channels: [{ alternatives: [{ transcript, words }] }] } }
205
205
  const results = raw.results as Record<string, unknown> | undefined;
206
206
  const channels = results?.channels as