ai 7.0.148 → 7.0.150

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/index.d.ts +168 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +325 -35
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +2 -1
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +4 -1
  9. package/dist/internal/index.js.map +1 -1
  10. package/dist/test/index.d.ts +17 -2
  11. package/dist/test/index.d.ts.map +1 -1
  12. package/dist/test/index.js +12 -1
  13. package/dist/test/index.js.map +1 -1
  14. package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +7 -0
  15. package/docs/03-ai-sdk-core/18-code-mode.mdx +3 -1
  16. package/docs/03-ai-sdk-core/36-transcription.mdx +40 -39
  17. package/docs/03-ai-sdk-core/37-speech.mdx +66 -0
  18. package/docs/03-ai-sdk-core/60-telemetry.mdx +25 -2
  19. package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +1 -1
  20. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +4 -4
  21. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +7 -7
  22. package/docs/07-reference/01-ai-sdk-core/12-stream-speech.mdx +195 -0
  23. package/docs/07-reference/01-ai-sdk-core/index.mdx +5 -0
  24. package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +1 -1
  25. package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +1 -1
  26. package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -1
  27. package/docs/07-reference/05-ai-sdk-errors/ai-tool-choice-violation-error.mdx +12 -7
  28. package/package.json +12 -12
  29. package/src/error/no-speech-generated-error.ts +5 -1
  30. package/src/error/tool-choice-violation-error.ts +6 -4
  31. package/src/generate-speech/index.ts +7 -0
  32. package/src/generate-speech/is-streaming-speech-model.ts +17 -0
  33. package/src/generate-speech/stream-speech-result.ts +95 -0
  34. package/src/generate-speech/stream-speech.ts +569 -0
  35. package/src/generate-text/collect-tool-approvals.ts +10 -0
  36. package/src/generate-text/generate-text.ts +24 -12
  37. package/src/generate-text/stream-language-model-call.ts +37 -12
  38. package/src/generate-text/stream-text.ts +4 -1
  39. package/src/test/mock-streaming-speech-model-v4.ts +28 -0
  40. package/src/types/language-model.ts +2 -0
  41. package/src/ui/chat.ts +40 -12
  42. package/src/ui-message-stream/ui-message-chunks.ts +1 -0
@@ -1411,7 +1411,7 @@ retain the content their contracts require.
1411
1411
  },
1412
1412
  {
1413
1413
  name: 'finishReason',
1414
- type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other"',
1414
+ type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "interrupted" | "other"',
1415
1415
  description:
1416
1416
  'The unified finish reason why the generation finished.',
1417
1417
  },
@@ -1746,7 +1746,7 @@ retain the content their contracts require.
1746
1746
  parameters: [
1747
1747
  {
1748
1748
  name: 'finishReason',
1749
- type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "other"',
1749
+ type: '"stop" | "length" | "content-filter" | "tool-calls" | "error" | "interrupted" | "other"',
1750
1750
  description:
1751
1751
  'The unified finish reason why the generation finished.',
1752
1752
  },
@@ -2674,9 +2674,9 @@ retain the content their contracts require.
2674
2674
  },
2675
2675
  {
2676
2676
  name: 'finishReason',
2677
- type: "PromiseLike<'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'>",
2677
+ type: "PromiseLike<'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'>",
2678
2678
  description:
2679
- 'The reason why the generation finished. Automatically consumes the stream.',
2679
+ 'The reason why the generation finished. A stream that ends cleanly without its provider completion signal can report "interrupted". Partial output is preserved; interruption alone does not emit an error. Streams that produce no output and no finish chunk still reject with NoOutputGeneratedError. OpenAI-compatible retains its existing error behavior. Automatically consumes the stream.',
2680
2680
  },
2681
2681
  {
2682
2682
  name: 'rawFinishReason',
@@ -3165,7 +3165,7 @@ retain the content their contracts require.
3165
3165
  },
3166
3166
  {
3167
3167
  name: 'finishReason',
3168
- type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
3168
+ type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
3169
3169
  description: 'The reason the model finished generating the text.',
3170
3170
  },
3171
3171
  {
@@ -3873,7 +3873,7 @@ retain the content their contracts require.
3873
3873
  },
3874
3874
  {
3875
3875
  name: 'finishReason',
3876
- type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
3876
+ type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
3877
3877
  description: 'The reason the model finished generating the text.',
3878
3878
  },
3879
3879
  {
@@ -3911,7 +3911,7 @@ retain the content their contracts require.
3911
3911
  },
3912
3912
  {
3913
3913
  name: 'finishReason',
3914
- type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
3914
+ type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
3915
3915
  description: 'The reason the model finished generating the text.',
3916
3916
  },
3917
3917
  {
@@ -0,0 +1,195 @@
1
+ ---
2
+ title: experimental_streamSpeech
3
+ description: API Reference for experimental_streamSpeech.
4
+ ---
5
+
6
+ # `experimental_streamSpeech()`
7
+
8
+ <Note type="warning">
9
+ `experimental_streamSpeech` is an experimental feature.
10
+ </Note>
11
+
12
+ Streams speech audio from text, or from an array or stream of text deltas and
13
+ text parts, using a speech model with streaming support.
14
+
15
+ ```ts
16
+ import { experimental_streamSpeech as streamSpeech } from 'ai';
17
+ import { elevenLabs } from '@ai-sdk/elevenlabs';
18
+
19
+ const result = streamSpeech({
20
+ model: elevenLabs.speech('eleven_flash_v2_5'),
21
+ text: 'Hello, world!',
22
+ });
23
+
24
+ for await (const chunk of result.audioStream) {
25
+ // Uint8Array audio bytes
26
+ }
27
+ ```
28
+
29
+ ## Import
30
+
31
+ <Snippet
32
+ text={`import { experimental_streamSpeech as streamSpeech } from "ai"`}
33
+ prompt={false}
34
+ />
35
+
36
+ ## API Signature
37
+
38
+ ### Parameters
39
+
40
+ <PropertiesTable
41
+ content={[
42
+ {
43
+ name: 'model',
44
+ type: 'SpeechModel',
45
+ description:
46
+ 'The speech model to use. The model must implement the experimental streaming speech extension (`Experimental_StreamingSpeechModelV4`).',
47
+ },
48
+ {
49
+ name: 'text',
50
+ type: 'string | Iterable<string | Experimental_SpeechTextPart> | AsyncIterable<string | Experimental_SpeechTextPart> | ReadableStream<string | Experimental_SpeechTextPart>',
51
+ description:
52
+ "The text to convert to speech: a complete string, or an array, iterable, or stream of text deltas (e.g. the `textStream` of `streamText`) and/or text parts `{ type: 'text', text, voice?, providerOptions? }`. A part's `voice` selects its speaker on providers that support several voices per stream; single-voice providers error the stream when a part's voice differs from the call's voice. The end of the stream ends the input.",
53
+ },
54
+ {
55
+ name: 'voice',
56
+ type: 'string',
57
+ isOptional: true,
58
+ description: 'The voice to use for speech generation.',
59
+ },
60
+ {
61
+ name: 'outputFormat',
62
+ type: '{ type: string; rate?: number }',
63
+ isOptional: true,
64
+ description:
65
+ "The format of the output audio chunks, e.g. `{ type: 'audio/pcm', rate: 24000 }` or `{ type: 'audio/mpeg' }`. Supported formats are provider-specific.",
66
+ },
67
+ {
68
+ name: 'instructions',
69
+ type: 'string',
70
+ isOptional: true,
71
+ description: 'Instructions for the speech generation.',
72
+ },
73
+ {
74
+ name: 'speed',
75
+ type: 'number',
76
+ isOptional: true,
77
+ description: 'The speed of the speech generation.',
78
+ },
79
+ {
80
+ name: 'language',
81
+ type: 'string',
82
+ isOptional: true,
83
+ description:
84
+ 'The language for speech generation (ISO 639-1 code), or "auto". Provider support varies.',
85
+ },
86
+ {
87
+ name: 'providerOptions',
88
+ type: 'Record<string, JSONObject>',
89
+ isOptional: true,
90
+ description: 'Additional provider-specific options.',
91
+ },
92
+ {
93
+ name: 'abortSignal',
94
+ type: 'AbortSignal',
95
+ isOptional: true,
96
+ description: 'An optional abort signal to cancel the call.',
97
+ },
98
+ {
99
+ name: 'headers',
100
+ type: 'Record<string, string>',
101
+ isOptional: true,
102
+ description:
103
+ 'Additional HTTP/WebSocket headers, if supported by the provider.',
104
+ },
105
+ {
106
+ name: 'includeRawChunks',
107
+ type: 'boolean',
108
+ isOptional: true,
109
+ description:
110
+ 'When true, the provider includes raw provider chunks in `fullStream` as `raw` parts.',
111
+ },
112
+ {
113
+ name: 'onError',
114
+ type: '(event: { error: unknown }) => void | PromiseLike<void>',
115
+ isOptional: true,
116
+ description:
117
+ 'Called for each `error` part the provider reports. Defaults to `console.error`.',
118
+ },
119
+ ]}
120
+ />
121
+
122
+ ### Returns
123
+
124
+ <PropertiesTable
125
+ content={[
126
+ {
127
+ name: 'audioStream',
128
+ type: 'AsyncIterableStream<Uint8Array>',
129
+ description:
130
+ 'Audio bytes in generation order. Shares one live stream with `fullStream`: only one of them can be accessed, once, before any result promise. `error` parts are skipped (they are reported via `onError`); the stream errors only when the speech stream itself fails.',
131
+ },
132
+ {
133
+ name: 'fullStream',
134
+ type: 'AsyncIterableStream<Experimental_SpeechStreamPart>',
135
+ description:
136
+ 'A single-consumer live stream of `audio` (with `audio: Uint8Array` and `audioFormat`), `raw`, and `error` parts. Error parts are not fatal; the stream continues after them.',
137
+ },
138
+ {
139
+ name: 'usage',
140
+ type: 'Promise<{ inputCharacters?: number; inputTokens?: number; outputTokens?: number; raw?: JSONObject }>',
141
+ description: 'Usage information for the call, if known.',
142
+ },
143
+ {
144
+ name: 'warnings',
145
+ type: 'Promise<Warning[]>',
146
+ description:
147
+ 'Warnings for the call, e.g. unsupported settings. Resolves when the provider emits the stream start.',
148
+ },
149
+ {
150
+ name: 'responses',
151
+ type: 'Promise<Array<SpeechModelResponseMetadata>>',
152
+ description: 'Response metadata (timestamp, model ID, headers, body).',
153
+ },
154
+ {
155
+ name: 'providerMetadata',
156
+ type: 'Promise<Record<string, JSONObject>>',
157
+ description: 'Additional provider-specific metadata.',
158
+ },
159
+ ]}
160
+ />
161
+
162
+ <Note>
163
+ The result promises settle as the stream is consumed. If you cancel the stream
164
+ early (for example by breaking out of a `for await` loop) or abort the call,
165
+ pending result promises reject immediately and the provider is signalled to
166
+ close its connection. A stream that is not read stalls under backpressure and
167
+ keeps the provider call open.
168
+ </Note>
169
+
170
+ ## Errors
171
+
172
+ - `UnsupportedFunctionalityError` is thrown synchronously when the model does
173
+ not support streaming speech.
174
+ - `InvalidArgumentError` is thrown synchronously when `text` is not a string,
175
+ an iterable or async iterable, or a readable stream.
176
+ - Provider `error` parts are not fatal. They appear in `fullStream`, are
177
+ skipped by `audioStream`, and are passed to `onError`.
178
+ - If the stream ends without audio, the result rejects with
179
+ `NoSpeechGeneratedError`, whose `cause` is the last provider error part.
180
+ - If the stream stops after audio but before it finished, the result rejects
181
+ with the last provider error part, or with a `StreamProviderError`.
182
+ - If the provider call fails (for example an authentication error), the result
183
+ rejects with that error. Cancelling or aborting rejects with the
184
+ cancellation or abort reason.
185
+
186
+ ## Wire format (experimental)
187
+
188
+ Streaming speech over WebSocket is serialized with the experimental
189
+ speech-stream envelope defined in `@ai-sdk/provider-utils`
190
+ (`experimental_parseSpeechStreamClientFrame`,
191
+ `experimental_serializeSpeechStreamPart`, `experimental_parseSpeechStreamPart`):
192
+ the client sends one `speech-stream.start` TEXT frame, input text as
193
+ `speech-stream.text-delta` TEXT frames (each carrying its text part's `voice`
194
+ and `providerOptions`), and a `speech-stream.text-done` TEXT frame; each server TEXT frame is one JSON-serialized speech stream part with
195
+ base64 audio. AI Gateway implements the server side of this envelope.
@@ -98,6 +98,11 @@ AI SDK Core contains the following main functions:
98
98
  description: 'Generate speech audio from text.',
99
99
  href: '/docs/reference/ai-sdk-core/generate-speech',
100
100
  },
101
+ {
102
+ title: 'experimental_streamSpeech()',
103
+ description: 'Stream speech audio from text or a text stream.',
104
+ href: '/docs/reference/ai-sdk-core/stream-speech',
105
+ },
101
106
  {
102
107
  title: 'uploadFile()',
103
108
  description: 'Upload a file to a provider and get a provider reference.',
@@ -317,7 +317,7 @@ Allows you to easily create a conversational user interface for your chatbot app
317
317
  },
318
318
  {
319
319
  name: 'finishReason',
320
- type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
320
+ type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
321
321
  isOptional: true,
322
322
  description:
323
323
  'The reason why the model finished generating the response. Undefined if the finish reason was not provided by the model.',
@@ -179,7 +179,7 @@ outcomes and call `setOutcome` once.
179
179
  name: 'finishReason',
180
180
  type: 'FinishReason | undefined',
181
181
  description:
182
- "The reason why the generation finished. One of: 'stop', 'length', 'content-filter', 'tool-calls', 'error', or 'other'.",
182
+ "The reason why the generation finished. One of: 'stop', 'length', 'content-filter', 'tool-calls', 'error', 'interrupted', or 'other'.",
183
183
  },
184
184
  ],
185
185
  },
@@ -649,7 +649,7 @@ To see `streamUI` in action, check out [these examples](#examples).
649
649
  },
650
650
  {
651
651
  name: 'finishReason',
652
- type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other'",
652
+ type: "'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'interrupted' | 'other'",
653
653
  description: 'The reason the model finished generating the text.',
654
654
  },
655
655
  {
@@ -5,14 +5,16 @@ description: Learn how to fix AI SDK ToolChoiceViolationError
5
5
 
6
6
  # ToolChoiceViolationError
7
7
 
8
- This error occurs when a `generateText` response does not satisfy an enforced
9
- tool choice. It is thrown when `toolChoice` is set to `'required'` but the
10
- response contains no structured tool call, or when a specifically selected tool
11
- was not called.
8
+ This error occurs when a `generateText` or `streamText` response does not satisfy
9
+ an enforced tool choice. It is thrown when `toolChoice` is set to `'none'` but
10
+ the response contains a tool call, when `'required'` produces no structured tool
11
+ call, or when a specifically selected tool was not called. With `streamText`,
12
+ the error is emitted in the stream. Tool calls returned while `'none'` is selected
13
+ are not executed.
12
14
 
13
15
  The error does not automatically interpret text or reasoning as an executable
14
- tool call. You can inspect `content` to implement opt-in recovery, including
15
- schema validation before executing any recovered call.
16
+ tool call. When tool calls are allowed, you can inspect `content` to implement
17
+ opt-in recovery, including schema validation before executing any recovered call.
16
18
 
17
19
  ## Properties
18
20
 
@@ -30,7 +32,10 @@ You can check if an error is an instance of `ToolChoiceViolationError` using:
30
32
  ```typescript
31
33
  import { ToolChoiceViolationError } from 'ai';
32
34
 
33
- if (ToolChoiceViolationError.isInstance(error)) {
35
+ if (
36
+ ToolChoiceViolationError.isInstance(error) &&
37
+ error.toolChoice.type !== 'none'
38
+ ) {
34
39
  const serializedCall = error.content.find(part => part.type === 'text')?.text;
35
40
 
36
41
  // Parse and validate serializedCall before treating it as a tool call.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.148",
3
+ "version": "7.0.150",
4
4
  "type": "module",
5
5
  "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
6
6
  "license": "Apache-2.0",
@@ -42,20 +42,20 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.121",
46
- "@ai-sdk/provider": "4.0.31",
47
- "@ai-sdk/provider-utils": "5.0.65"
45
+ "@ai-sdk/gateway": "4.0.123",
46
+ "@ai-sdk/provider": "4.0.33",
47
+ "@ai-sdk/provider-utils": "5.0.67"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.121",
51
- "@ai-sdk/deepseek": "3.0.70",
52
- "@ai-sdk/google": "4.0.102",
53
- "@ai-sdk/groq": "4.0.67",
54
- "@ai-sdk/huggingface": "2.0.75",
55
- "@ai-sdk/moonshotai": "3.0.74",
56
- "@ai-sdk/openai": "4.0.99",
50
+ "@ai-sdk/amazon-bedrock": "5.0.123",
51
+ "@ai-sdk/deepseek": "3.0.72",
52
+ "@ai-sdk/google": "4.0.104",
53
+ "@ai-sdk/groq": "4.0.69",
54
+ "@ai-sdk/huggingface": "2.0.77",
55
+ "@ai-sdk/moonshotai": "3.0.76",
56
+ "@ai-sdk/openai": "4.0.101",
57
57
  "@ai-sdk/test-server": "2.0.4",
58
- "@ai-sdk/xai": "5.0.27",
58
+ "@ai-sdk/xai": "5.0.29",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -13,10 +13,14 @@ export class NoSpeechGeneratedError extends AISDKError {
13
13
 
14
14
  readonly responses: Array<SpeechModelResponseMetadata>;
15
15
 
16
- constructor(options: { responses: Array<SpeechModelResponseMetadata> }) {
16
+ constructor(options: {
17
+ responses: Array<SpeechModelResponseMetadata>;
18
+ cause?: unknown;
19
+ }) {
17
20
  super({
18
21
  name,
19
22
  message: 'No speech audio generated.',
23
+ ...(options.cause !== undefined && { cause: options.cause }),
20
24
  });
21
25
 
22
26
  this.responses = options.responses;
@@ -11,7 +11,7 @@ const symbol = Symbol.for(marker);
11
11
 
12
12
  type EnforcedToolChoice = Extract<
13
13
  LanguageModelV4ToolChoice,
14
- { type: 'required' } | { type: 'tool' }
14
+ { type: 'none' } | { type: 'required' } | { type: 'tool' }
15
15
  >;
16
16
 
17
17
  /**
@@ -54,9 +54,11 @@ export class ToolChoiceViolationError extends AISDKError {
54
54
  provider,
55
55
  modelId,
56
56
  content,
57
- message = toolChoice.type === 'required'
58
- ? 'Model response did not contain a tool call even though tool choice was required.'
59
- : `Model response did not contain a call to the required tool '${toolChoice.toolName}'.`,
57
+ message = toolChoice.type === 'none'
58
+ ? 'Model response contained a tool call even though tool choice was none.'
59
+ : toolChoice.type === 'required'
60
+ ? 'Model response did not contain a tool call even though tool choice was required.'
61
+ : `Model response did not contain a call to the required tool '${toolChoice.toolName}'.`,
60
62
  }: {
61
63
  toolChoice: EnforcedToolChoice;
62
64
  finishReason: FinishReason;
@@ -3,6 +3,13 @@ import { generateSpeech } from './generate-speech';
3
3
  import { logWarnings } from '../logger/log-warnings';
4
4
 
5
5
  export { generateSpeech } from './generate-speech';
6
+ export { streamSpeech as experimental_streamSpeech } from './stream-speech';
7
+ export type {
8
+ SpeechStreamPart as Experimental_SpeechStreamPart,
9
+ SpeechStreamUsage as Experimental_SpeechStreamUsage,
10
+ SpeechTextPart as Experimental_SpeechTextPart,
11
+ StreamSpeechResult as Experimental_StreamSpeechResult,
12
+ } from './stream-speech-result';
6
13
  export type { SpeechResult } from './generate-speech-result';
7
14
  export type { GeneratedAudioFile } from './generated-audio-file';
8
15
  export type {
@@ -0,0 +1,17 @@
1
+ import type {
2
+ Experimental_StreamingSpeechModelV4,
3
+ SpeechModelV4,
4
+ } from '@ai-sdk/provider';
5
+
6
+ /**
7
+ * Whether a resolved speech model implements the experimental streaming
8
+ * speech extension (`Experimental_StreamingSpeechModelV4`).
9
+ */
10
+ export function isStreamingSpeechModel(
11
+ model: SpeechModelV4,
12
+ ): model is Experimental_StreamingSpeechModelV4 {
13
+ return (
14
+ typeof (model as Partial<Experimental_StreamingSpeechModelV4>).doStream ===
15
+ 'function'
16
+ );
17
+ }
@@ -0,0 +1,95 @@
1
+ import type {
2
+ Experimental_SpeechModelV4StreamUsage,
3
+ JSONObject,
4
+ SharedV4AudioFormat,
5
+ SharedV4ProviderMetadata,
6
+ } from '@ai-sdk/provider';
7
+ import type { ProviderOptions } from '@ai-sdk/provider-utils';
8
+ import type { SpeechModelResponseMetadata } from '../types/speech-model-response-metadata';
9
+ import type { Warning } from '../types/warning';
10
+ import type { AsyncIterableStream } from '../util/async-iterable-stream';
11
+
12
+ /**
13
+ * A piece of text to synthesize. Pass a stream of these (or mix them with
14
+ * plain strings) to give parts their own voice, e.g. for dialogue.
15
+ */
16
+ export type SpeechTextPart = {
17
+ type: 'text';
18
+ text: string;
19
+
20
+ /**
21
+ * The voice that speaks this part (provider-specific, like the call's
22
+ * `voice`). Single-voice providers reject a voice that differs from the
23
+ * call's voice.
24
+ */
25
+ voice?: string;
26
+
27
+ /**
28
+ * Additional provider-specific options for this part.
29
+ */
30
+ providerOptions?: ProviderOptions;
31
+ };
32
+
33
+ export type SpeechStreamPart =
34
+ | {
35
+ /**
36
+ * Audio chunk in generation order. Chunks are not necessarily
37
+ * standalone files; concatenate them to reconstruct the audio. A chunk
38
+ * is empty only when it carries `providerMetadata` (e.g. a turn marker).
39
+ */
40
+ type: 'audio';
41
+ id?: string;
42
+ audio: Uint8Array;
43
+ audioFormat: SharedV4AudioFormat;
44
+ providerMetadata?: SharedV4ProviderMetadata;
45
+ }
46
+ | {
47
+ type: 'raw';
48
+ rawValue: unknown;
49
+ }
50
+ | {
51
+ type: 'error';
52
+ error: unknown;
53
+ };
54
+
55
+ export type SpeechStreamUsage = Experimental_SpeechModelV4StreamUsage;
56
+
57
+ export interface StreamSpeechResult {
58
+ /**
59
+ * Usage information reported for the call, if available.
60
+ */
61
+ readonly usage: PromiseLike<SpeechStreamUsage>;
62
+
63
+ /**
64
+ * Warnings for the call, e.g. unsupported settings.
65
+ */
66
+ readonly warnings: PromiseLike<Array<Warning>>;
67
+
68
+ /**
69
+ * Response metadata.
70
+ */
71
+ readonly responses: PromiseLike<Array<SpeechModelResponseMetadata>>;
72
+
73
+ /**
74
+ * Additional provider-specific metadata.
75
+ */
76
+ readonly providerMetadata: PromiseLike<Record<string, JSONObject>>;
77
+
78
+ /**
79
+ * Stream of audio bytes in generation order.
80
+ *
81
+ * `audioStream` and `fullStream` share one live stream: only one of them
82
+ * can be accessed, and only once. Access it before any result promise;
83
+ * accessing a result promise first consumes the stream internally.
84
+ */
85
+ readonly audioStream: AsyncIterableStream<Uint8Array>;
86
+
87
+ /**
88
+ * Full stream of speech parts (audio, raw, and error parts).
89
+ *
90
+ * `audioStream` and `fullStream` share one live stream: only one of them
91
+ * can be accessed, and only once. Access it before any result promise;
92
+ * accessing a result promise first consumes the stream internally.
93
+ */
94
+ readonly fullStream: AsyncIterableStream<SpeechStreamPart>;
95
+ }