ai 7.0.123 → 7.0.126

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 (35) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.d.ts +232 -34
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +415 -100
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +199 -8
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +9 -3
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +25 -0
  11. package/docs/03-ai-sdk-core/36-transcription.mdx +38 -1
  12. package/docs/03-ai-sdk-core/37-speech.mdx +23 -0
  13. package/docs/03-ai-sdk-core/60-telemetry.mdx +49 -1
  14. package/docs/07-reference/01-ai-sdk-core/11-stream-transcribe.mdx +7 -0
  15. package/docs/07-reference/01-ai-sdk-core/11-transcribe.mdx +7 -0
  16. package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +7 -0
  17. package/docs/07-reference/01-ai-sdk-core/17-create-agent-ui-stream.mdx +7 -0
  18. package/docs/07-reference/01-ai-sdk-core/18-create-agent-ui-stream-response.mdx +7 -0
  19. package/docs/07-reference/01-ai-sdk-core/18-pipe-agent-ui-stream-to-response.mdx +7 -0
  20. package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +60 -3
  21. package/package.json +12 -12
  22. package/src/agent/create-agent-ui-stream-response.ts +16 -4
  23. package/src/agent/create-agent-ui-stream.ts +16 -2
  24. package/src/agent/pipe-agent-ui-stream-to-response.ts +16 -4
  25. package/src/generate-speech/generate-speech.ts +163 -38
  26. package/src/generate-speech/index.ts +4 -0
  27. package/src/generate-speech/speech-events.ts +91 -0
  28. package/src/telemetry/create-telemetry-dispatcher.ts +18 -5
  29. package/src/telemetry/telemetry.ts +38 -6
  30. package/src/telemetry/tracing-channel.ts +3 -0
  31. package/src/transcribe/index.ts +6 -0
  32. package/src/transcribe/stream-transcribe.ts +182 -31
  33. package/src/transcribe/transcribe.ts +186 -33
  34. package/src/transcribe/transcription-events.ts +111 -0
  35. package/src/ui/chat.ts +36 -5
@@ -1,5 +1,6 @@
1
1
  import type { JSONObject } from '@ai-sdk/provider';
2
2
  import {
3
+ createIdGenerator,
3
4
  detectMediaType,
4
5
  withUserAgentSuffix,
5
6
  type ProviderOptions,
@@ -7,16 +8,28 @@ import {
7
8
  import { NoSpeechGeneratedError } from '../error/no-speech-generated-error';
8
9
  import { logWarnings } from '../logger/log-warnings';
9
10
  import { resolveSpeechModel } from '../model/resolve-model';
11
+ import { createTelemetryDispatcher } from '../telemetry/create-telemetry-dispatcher';
12
+ import type { TelemetryOptions } from '../telemetry/telemetry-options';
10
13
  import type { SpeechModel } from '../types/speech-model';
11
14
  import type { SpeechModelResponseMetadata } from '../types/speech-model-response-metadata';
12
15
  import type { Warning } from '../types/warning';
13
16
  import { prepareRetries } from '../util/prepare-retries';
17
+ import { notify } from '../util/notify';
14
18
  import { VERSION } from '../version';
15
19
  import type { SpeechResult } from './generate-speech-result';
16
20
  import {
17
21
  DefaultGeneratedAudioFile,
18
22
  type GeneratedAudioFile,
19
23
  } from './generated-audio-file';
24
+ import type {
25
+ GenerateSpeechEndEvent,
26
+ GenerateSpeechStartEvent,
27
+ } from './speech-events';
28
+
29
+ const originalGenerateCallId = createIdGenerator({
30
+ prefix: 'call',
31
+ size: 24,
32
+ });
20
33
  /**
21
34
  * Generates speech audio using a speech model.
22
35
  *
@@ -32,6 +45,7 @@ import {
32
45
  * @param maxRetries - Maximum number of retries. Set to 0 to disable retries. Default: 2.
33
46
  * @param abortSignal - An optional abort signal that can be used to cancel the call.
34
47
  * @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
48
+ * @param telemetry - Optional telemetry configuration.
35
49
  *
36
50
  * @returns A result object that contains the generated audio data.
37
51
  */
@@ -47,6 +61,8 @@ export async function generateSpeech({
47
61
  maxRetries: maxRetriesArg,
48
62
  abortSignal,
49
63
  headers,
64
+ telemetry,
65
+ _internal: { generateCallId = originalGenerateCallId } = {},
50
66
  }: {
51
67
  /**
52
68
  * The speech model to use.
@@ -115,6 +131,18 @@ export async function generateSpeech({
115
131
  * Only applicable for HTTP-based providers.
116
132
  */
117
133
  headers?: Record<string, string>;
134
+
135
+ /**
136
+ * Optional telemetry configuration.
137
+ */
138
+ telemetry?: TelemetryOptions;
139
+
140
+ /**
141
+ * Internal. For test use only. May change without notice.
142
+ */
143
+ _internal?: {
144
+ generateCallId?: () => string;
145
+ };
118
146
  }): Promise<SpeechResult> {
119
147
  const resolvedModel = resolveSpeechModel(model);
120
148
  if (!resolvedModel) {
@@ -126,55 +154,152 @@ export async function generateSpeech({
126
154
  `ai/${VERSION}`,
127
155
  );
128
156
 
129
- const { retry } = prepareRetries({
157
+ const { maxRetries, retry } = prepareRetries({
130
158
  maxRetries: maxRetriesArg,
131
159
  abortSignal,
132
160
  });
133
161
 
134
- const result = await retry(() =>
135
- resolvedModel.doGenerate({
136
- text,
137
- voice,
138
- outputFormat,
139
- instructions,
140
- speed,
141
- language,
142
- abortSignal,
143
- headers: headersWithUserAgent,
144
- providerOptions,
145
- }),
146
- );
147
-
148
- if (!result.audio || result.audio.length === 0) {
149
- throw new NoSpeechGeneratedError({ responses: [result.response] });
150
- }
162
+ const callId = generateCallId();
163
+ const telemetryDispatcher = createTelemetryDispatcher({ telemetry });
164
+ const runInTracingChannelSpan =
165
+ telemetryDispatcher.runInTracingChannelSpan ??
166
+ (async <T>({ execute }: { execute: () => PromiseLike<T> }) =>
167
+ await execute());
151
168
 
152
- logWarnings({
153
- warnings: result.warnings,
169
+ const startEvent: GenerateSpeechStartEvent = {
170
+ callId,
171
+ operationId: 'ai.generateSpeech',
154
172
  provider: resolvedModel.provider,
155
- model: resolvedModel.modelId,
156
- });
173
+ modelId: resolvedModel.modelId,
174
+ text,
175
+ voice,
176
+ outputFormat,
177
+ instructions,
178
+ speed,
179
+ language,
180
+ maxRetries,
181
+ headers,
182
+ providerOptions,
183
+ };
157
184
 
158
- const detectedMediaType = detectMediaType({
159
- data: result.audio,
160
- topLevelType: 'audio',
161
- });
185
+ return await runInTracingChannelSpan({
186
+ type: 'generateSpeech',
187
+ event: startEvent,
188
+ execute: async () => {
189
+ await notify({
190
+ event: startEvent,
191
+ callbacks: [telemetryDispatcher.onStart],
192
+ });
193
+
194
+ try {
195
+ const result = await retry(() =>
196
+ resolvedModel.doGenerate({
197
+ text,
198
+ voice,
199
+ outputFormat,
200
+ instructions,
201
+ speed,
202
+ language,
203
+ abortSignal,
204
+ headers: headersWithUserAgent,
205
+ providerOptions,
206
+ }),
207
+ );
208
+
209
+ if (!result.audio || result.audio.length === 0) {
210
+ throw new NoSpeechGeneratedError({ responses: [result.response] });
211
+ }
162
212
 
163
- return new DefaultSpeechResult({
164
- audio: new DefaultGeneratedAudioFile({
165
- data: result.audio,
166
- mediaType:
167
- detectedMediaType ??
168
- getResponseAudioMediaType(result.response.headers) ??
169
- getOutputFormatMediaType(outputFormat) ??
170
- 'audio/mp3',
171
- }),
172
- warnings: result.warnings,
173
- responses: [result.response],
174
- providerMetadata: result.providerMetadata,
213
+ logWarnings({
214
+ warnings: result.warnings,
215
+ provider: resolvedModel.provider,
216
+ model: resolvedModel.modelId,
217
+ });
218
+
219
+ const detectedMediaType = detectMediaType({
220
+ data: result.audio,
221
+ topLevelType: 'audio',
222
+ });
223
+
224
+ const audio = new DefaultGeneratedAudioFile({
225
+ data: result.audio,
226
+ mediaType:
227
+ detectedMediaType ??
228
+ getResponseAudioMediaType(result.response.headers) ??
229
+ getOutputFormatMediaType(outputFormat) ??
230
+ 'audio/mp3',
231
+ });
232
+
233
+ if (telemetryDispatcher.onEnd != null) {
234
+ const endEvent: GenerateSpeechEndEvent = {
235
+ callId,
236
+ operationId: 'ai.generateSpeech',
237
+ provider: resolvedModel.provider,
238
+ modelId: resolvedModel.modelId,
239
+ text,
240
+ audio: {
241
+ byteLength: getBase64OrBinaryByteLength(result.audio),
242
+ mediaType: audio.mediaType,
243
+ format: audio.format,
244
+ },
245
+ usage: result.usage,
246
+ warnings: result.warnings,
247
+ providerMetadata: result.providerMetadata,
248
+ response: getTelemetryResponseMetadata(result.response),
249
+ };
250
+
251
+ await notify({
252
+ event: endEvent,
253
+ callbacks: [telemetryDispatcher.onEnd],
254
+ });
255
+ }
256
+
257
+ return new DefaultSpeechResult({
258
+ audio,
259
+ warnings: result.warnings,
260
+ responses: [result.response],
261
+ providerMetadata: result.providerMetadata,
262
+ });
263
+ } catch (error) {
264
+ await telemetryDispatcher.onError?.({ callId, error });
265
+ throw error;
266
+ }
267
+ },
175
268
  });
176
269
  }
177
270
 
271
+ function getBase64OrBinaryByteLength(audio: string | Uint8Array): number {
272
+ if (audio instanceof Uint8Array) {
273
+ return audio.byteLength;
274
+ }
275
+
276
+ let characterCount = 0;
277
+ let lastCharacter = '';
278
+ let secondLastCharacter = '';
279
+ for (const character of audio) {
280
+ if (/\s/.test(character)) {
281
+ continue;
282
+ }
283
+ characterCount++;
284
+ secondLastCharacter = lastCharacter;
285
+ lastCharacter = character;
286
+ }
287
+
288
+ const padding =
289
+ lastCharacter === '=' ? (secondLastCharacter === '=' ? 2 : 1) : 0;
290
+ return Math.floor((characterCount * 3) / 4) - padding;
291
+ }
292
+
293
+ function getTelemetryResponseMetadata(
294
+ response: SpeechModelResponseMetadata,
295
+ ): SpeechModelResponseMetadata {
296
+ return {
297
+ timestamp: response.timestamp,
298
+ modelId: response.modelId,
299
+ headers: response.headers,
300
+ };
301
+ }
302
+
178
303
  function getResponseAudioMediaType(
179
304
  headers: Record<string, string> | undefined,
180
305
  ): string | undefined {
@@ -4,6 +4,10 @@ import { generateSpeech } from './generate-speech';
4
4
  export { generateSpeech } from './generate-speech';
5
5
  export type { SpeechResult } from './generate-speech-result';
6
6
  export type { GeneratedAudioFile } from './generated-audio-file';
7
+ export type {
8
+ GenerateSpeechEndEvent,
9
+ GenerateSpeechStartEvent,
10
+ } from './speech-events';
7
11
 
8
12
  // deprecated exports
9
13
 
@@ -0,0 +1,91 @@
1
+ import type { JSONObject } from '@ai-sdk/provider';
2
+ import type { ProviderOptions } from '@ai-sdk/provider-utils';
3
+ import type { ProviderMetadata } from '../types';
4
+ import type { SpeechModelResponseMetadata } from '../types/speech-model-response-metadata';
5
+ import type { Warning } from '../types/warning';
6
+
7
+ /**
8
+ * Event passed to telemetry integrations when speech generation begins.
9
+ */
10
+ export type GenerateSpeechStartEvent = {
11
+ /** Unique identifier for this speech generation call. */
12
+ readonly callId: string;
13
+
14
+ /** Identifies the operation type (`ai.generateSpeech`). */
15
+ readonly operationId: 'ai.generateSpeech';
16
+
17
+ /** The provider identifier. */
18
+ readonly provider: string;
19
+
20
+ /** The specific model identifier. */
21
+ readonly modelId: string;
22
+
23
+ /** The text to convert to speech. */
24
+ readonly text: string;
25
+
26
+ /** The requested voice. */
27
+ readonly voice: string | undefined;
28
+
29
+ /** The requested output format. */
30
+ readonly outputFormat: string | undefined;
31
+
32
+ /** Additional speech instructions. */
33
+ readonly instructions: string | undefined;
34
+
35
+ /** The requested speech speed. */
36
+ readonly speed: number | undefined;
37
+
38
+ /** The requested language. */
39
+ readonly language: string | undefined;
40
+
41
+ /** Maximum number of retries for failed requests. */
42
+ readonly maxRetries: number;
43
+
44
+ /** Additional HTTP headers sent with the request. */
45
+ readonly headers: Record<string, string> | undefined;
46
+
47
+ /** Additional provider-specific options. */
48
+ readonly providerOptions: ProviderOptions;
49
+ };
50
+
51
+ /**
52
+ * Event passed to telemetry integrations when speech generation completes.
53
+ *
54
+ * Raw audio is intentionally not included. Integrations receive only its size,
55
+ * media type, and format.
56
+ */
57
+ export type GenerateSpeechEndEvent = {
58
+ /** Unique identifier for this speech generation call. */
59
+ readonly callId: string;
60
+
61
+ /** Identifies the operation type (`ai.generateSpeech`). */
62
+ readonly operationId: 'ai.generateSpeech';
63
+
64
+ /** The provider identifier. */
65
+ readonly provider: string;
66
+
67
+ /** The specific model identifier. */
68
+ readonly modelId: string;
69
+
70
+ /** The text that was converted to speech. */
71
+ readonly text: string;
72
+
73
+ /** Metadata about the generated audio. */
74
+ readonly audio: {
75
+ readonly byteLength: number;
76
+ readonly mediaType: string;
77
+ readonly format: string;
78
+ };
79
+
80
+ /** Usage reported by the provider, when available. */
81
+ readonly usage: JSONObject | undefined;
82
+
83
+ /** Warnings from the speech model. */
84
+ readonly warnings: Array<Warning>;
85
+
86
+ /** Optional provider-specific metadata. */
87
+ readonly providerMetadata: ProviderMetadata | undefined;
88
+
89
+ /** Response metadata from the provider. */
90
+ readonly response: SpeechModelResponseMetadata;
91
+ };
@@ -142,7 +142,7 @@ export function createTelemetryDispatcher({
142
142
 
143
143
  const mergeTelemetryCallback = <KEY extends TelemetryCallbackKey>(
144
144
  key: KEY,
145
- ): Callback<TelemetryEvent<KEY>> => {
145
+ ): Callback<TelemetryEvent<KEY>> | undefined => {
146
146
  const integrationCallbacks = (
147
147
  integrations
148
148
  .map(integration => integration[key]?.bind(integration))
@@ -157,6 +157,10 @@ export function createTelemetryDispatcher({
157
157
  >,
158
158
  );
159
159
 
160
+ if (integrationCallbacks.length === 0) {
161
+ return undefined;
162
+ }
163
+
160
164
  const mergedIntegrationCallback = mergeCallbacks(...integrationCallbacks);
161
165
 
162
166
  return async (event: TelemetryEvent<KEY>) => {
@@ -164,6 +168,9 @@ export function createTelemetryDispatcher({
164
168
  };
165
169
  };
166
170
 
171
+ const onStepEnd = mergeTelemetryCallback('onStepEnd');
172
+ const onStepFinish = mergeTelemetryCallback('onStepFinish');
173
+
167
174
  const executeLanguageModelCallWrappers = integrations
168
175
  .map(integration => integration.executeLanguageModelCall?.bind(integration))
169
176
  .filter(Boolean) as Array<
@@ -205,10 +212,10 @@ export function createTelemetryDispatcher({
205
212
  // deprecated `onStepFinish` callback so integrations that still implement
206
213
  // only `onStepFinish` keep receiving step-end events during the deprecation
207
214
  // window.
208
- onStepEnd: mergeCallbacks(
209
- mergeTelemetryCallback('onStepEnd'),
210
- mergeTelemetryCallback('onStepFinish'),
211
- ),
215
+ onStepEnd:
216
+ onStepEnd == null && onStepFinish == null
217
+ ? undefined
218
+ : mergeCallbacks(onStepEnd, onStepFinish),
212
219
  onObjectStepStart: mergeTelemetryCallback('onObjectStepStart'),
213
220
  onObjectStepEnd: mergeTelemetryCallback('onObjectStepEnd'),
214
221
  onEmbedStart: mergeTelemetryCallback('onEmbedStart'),
@@ -227,6 +234,12 @@ export function createTelemetryDispatcher({
227
234
  experimental_onEvaluateEnd: mergeTelemetryCallback(
228
235
  'experimental_onEvaluateEnd',
229
236
  ),
237
+ experimental_onStreamTranscriptionStart: mergeTelemetryCallback(
238
+ 'experimental_onStreamTranscriptionStart',
239
+ ),
240
+ experimental_onStreamTranscriptionEnd: mergeTelemetryCallback(
241
+ 'experimental_onStreamTranscriptionEnd',
242
+ ),
230
243
  onEnd: mergeTelemetryCallback('onEnd'),
231
244
  onAbort: mergeTelemetryCallback('onAbort'),
232
245
  onError: mergeTelemetryCallback('onError'),
@@ -40,6 +40,16 @@ import type {
40
40
  RerankingModelCallEndEvent,
41
41
  RerankingModelCallStartEvent,
42
42
  } from '../rerank/rerank-events';
43
+ import type {
44
+ GenerateSpeechEndEvent,
45
+ GenerateSpeechStartEvent,
46
+ } from '../generate-speech/speech-events';
47
+ import type {
48
+ StreamTranscriptionEndEvent,
49
+ StreamTranscriptionStartEvent,
50
+ TranscriptionEndEvent,
51
+ TranscriptionStartEvent,
52
+ } from '../transcribe/transcription-events';
43
53
  import type { Callback } from '../util/callback';
44
54
  import type { TelemetryOptions } from '../telemetry/telemetry-options';
45
55
  import type { TelemetryTracingEventType } from './tracing-channel';
@@ -51,17 +61,25 @@ export type InferTelemetryEvent<EVENT> = EVENT &
51
61
  'integrations' | 'isEnabled' | 'includeRuntimeContext'
52
62
  >;
53
63
 
64
+ type BivariantCallback<EVENT> = {
65
+ bivarianceHack(event: EVENT): PromiseLike<void> | void;
66
+ }['bivarianceHack'];
67
+
54
68
  type OperationStartEvent =
55
69
  | GenerateTextStartEvent
56
70
  | GenerateObjectStartEvent
57
71
  | EmbedStartEvent
58
- | RerankStartEvent;
72
+ | RerankStartEvent
73
+ | GenerateSpeechStartEvent
74
+ | TranscriptionStartEvent;
59
75
 
60
76
  type OperationEndEvent =
61
77
  | GenerateTextEndEvent<ToolSet>
62
78
  | GenerateObjectEndEvent<unknown>
63
79
  | EmbedEndEvent
64
- | RerankEndEvent;
80
+ | RerankEndEvent
81
+ | GenerateSpeechEndEvent
82
+ | TranscriptionEndEvent;
65
83
 
66
84
  export interface TelemetryDispatcher {
67
85
  /**
@@ -102,6 +120,8 @@ export interface TelemetryDispatcher {
102
120
  experimental_onEvaluationModelCallStart?: Callback<EvaluationModelCallStartEvent>;
103
121
  experimental_onEvaluationModelCallEnd?: Callback<EvaluationModelCallEndEvent>;
104
122
  experimental_onEvaluateEnd?: Callback<EvaluateEndEvent>;
123
+ experimental_onStreamTranscriptionStart?: Callback<StreamTranscriptionStartEvent>;
124
+ experimental_onStreamTranscriptionEnd?: Callback<StreamTranscriptionEndEvent>;
105
125
  onEnd?: Callback<OperationEndEvent>;
106
126
  onAbort?: Callback<GenerateTextAbortEvent<ToolSet>>;
107
127
  onError?: Callback<unknown>;
@@ -117,11 +137,12 @@ export interface Telemetry {
117
137
  /**
118
138
  * Called when an operation begins. Fired for text generation
119
139
  * (generateText/streamText), object generation (generateObject/streamObject),
120
- * embedding (embed/embedMany), and reranking operations.
140
+ * embedding (embed/embedMany), reranking, speech generation, and
141
+ * non-streaming transcription operations.
121
142
  *
122
143
  * Use the `operationId` field to distinguish between operation types.
123
144
  */
124
- onStart?: Callback<InferTelemetryEvent<OperationStartEvent>>;
145
+ onStart?: BivariantCallback<InferTelemetryEvent<OperationStartEvent>>;
125
146
 
126
147
  /**
127
148
  * Called when an individual step (single LLM invocation) begins.
@@ -248,14 +269,25 @@ export interface Telemetry {
248
269
  /** Called when an experimental evaluation operation completes. */
249
270
  experimental_onEvaluateEnd?: Callback<InferTelemetryEvent<EvaluateEndEvent>>;
250
271
 
272
+ /** Called when an experimental streaming transcription operation begins. */
273
+ experimental_onStreamTranscriptionStart?: Callback<
274
+ InferTelemetryEvent<StreamTranscriptionStartEvent>
275
+ >;
276
+
277
+ /** Called when an experimental streaming transcription operation completes. */
278
+ experimental_onStreamTranscriptionEnd?: Callback<
279
+ InferTelemetryEvent<StreamTranscriptionEndEvent>
280
+ >;
281
+
251
282
  /**
252
283
  * Called when an operation completes. Fired for text generation
253
284
  * (generateText/streamText), object generation (generateObject/streamObject),
254
- * embedding (embed/embedMany), and reranking operations.
285
+ * embedding (embed/embedMany), reranking, speech generation, and
286
+ * non-streaming transcription operations.
255
287
  *
256
288
  * Use the event shape or `operationId` to distinguish between operation types.
257
289
  */
258
- onEnd?: Callback<InferTelemetryEvent<OperationEndEvent>>;
290
+ onEnd?: BivariantCallback<InferTelemetryEvent<OperationEndEvent>>;
259
291
 
260
292
  /**
261
293
  * Called when a streaming text generation operation is aborted before it
@@ -9,6 +9,9 @@ export type TelemetryTracingEventType =
9
9
  | 'embed'
10
10
  | 'embedMany'
11
11
  | 'rerank'
12
+ | 'generateSpeech'
13
+ | 'transcribe'
14
+ | 'streamTranscribe'
12
15
  | 'experimental_evaluate';
13
16
 
14
17
  export type TelemetryTracingChannelMessage<EVENT = unknown> = {
@@ -8,6 +8,12 @@ export type {
8
8
  TranscriptionStreamPart,
9
9
  } from './stream-transcribe-result';
10
10
  export type { TranscriptionResult } from './transcribe-result';
11
+ export type {
12
+ StreamTranscriptionEndEvent as Experimental_StreamTranscriptionEndEvent,
13
+ StreamTranscriptionStartEvent as Experimental_StreamTranscriptionStartEvent,
14
+ TranscriptionEndEvent,
15
+ TranscriptionStartEvent,
16
+ } from './transcription-events';
11
17
 
12
18
  // deprecated exports
13
19