ai 7.0.123 → 7.0.124
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/CHANGELOG.md +14 -0
- package/dist/index.d.ts +191 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +383 -91
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +199 -8
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +9 -3
- package/dist/internal/index.js.map +1 -1
- package/docs/03-ai-sdk-core/36-transcription.mdx +36 -0
- package/docs/03-ai-sdk-core/37-speech.mdx +21 -0
- package/docs/03-ai-sdk-core/60-telemetry.mdx +49 -1
- package/docs/07-reference/01-ai-sdk-core/11-stream-transcribe.mdx +7 -0
- package/docs/07-reference/01-ai-sdk-core/11-transcribe.mdx +7 -0
- package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +7 -0
- package/package.json +12 -12
- package/src/generate-speech/generate-speech.ts +163 -38
- package/src/generate-speech/index.ts +4 -0
- package/src/generate-speech/speech-events.ts +91 -0
- package/src/telemetry/create-telemetry-dispatcher.ts +18 -5
- package/src/telemetry/telemetry.ts +38 -6
- package/src/telemetry/tracing-channel.ts +3 -0
- package/src/transcribe/index.ts +6 -0
- package/src/transcribe/stream-transcribe.ts +182 -31
- package/src/transcribe/transcribe.ts +186 -33
- package/src/transcribe/transcription-events.ts +111 -0
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import type { JSONObject } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
|
+
createIdGenerator,
|
|
3
4
|
detectMediaType,
|
|
4
5
|
withUserAgentSuffix,
|
|
5
6
|
type ProviderOptions,
|
|
6
7
|
} from '@ai-sdk/provider-utils';
|
|
7
8
|
import { NoTranscriptGeneratedError } from '../error/no-transcript-generated-error';
|
|
8
9
|
import { logWarnings } from '../logger/log-warnings';
|
|
10
|
+
import { resolveTranscriptionModel } from '../model/resolve-model';
|
|
9
11
|
import type { DataContent } from '../prompt';
|
|
10
12
|
import { convertDataContentToUint8Array } from '../prompt/data-content';
|
|
13
|
+
import { createTelemetryDispatcher } from '../telemetry/create-telemetry-dispatcher';
|
|
14
|
+
import type { TelemetryOptions } from '../telemetry/telemetry-options';
|
|
11
15
|
import type { TranscriptionModel } from '../types/transcription-model';
|
|
12
16
|
import type { TranscriptionModelResponseMetadata } from '../types/transcription-model-response-metadata';
|
|
13
17
|
import { createDownload } from '../util/download/create-download';
|
|
18
|
+
import { notify } from '../util/notify';
|
|
14
19
|
import { prepareRetries } from '../util/prepare-retries';
|
|
15
20
|
import type { TranscriptionResult } from './transcribe-result';
|
|
16
21
|
import { VERSION } from '../version';
|
|
17
|
-
import { resolveTranscriptionModel } from '../model/resolve-model';
|
|
18
22
|
import type { Warning } from '../types';
|
|
23
|
+
import type {
|
|
24
|
+
TranscriptionEndEvent,
|
|
25
|
+
TranscriptionStartEvent,
|
|
26
|
+
} from './transcription-events';
|
|
27
|
+
|
|
28
|
+
const originalGenerateCallId = createIdGenerator({
|
|
29
|
+
prefix: 'call',
|
|
30
|
+
size: 24,
|
|
31
|
+
});
|
|
19
32
|
/**
|
|
20
33
|
* Generates transcripts using a transcription model.
|
|
21
34
|
*
|
|
@@ -26,6 +39,7 @@ import type { Warning } from '../types';
|
|
|
26
39
|
* @param maxRetries - Maximum number of retries. Set to 0 to disable retries. Default: 2.
|
|
27
40
|
* @param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
28
41
|
* @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
42
|
+
* @param telemetry - Optional telemetry configuration.
|
|
29
43
|
*
|
|
30
44
|
* @returns A result object that contains the generated transcript.
|
|
31
45
|
*/
|
|
@@ -38,7 +52,9 @@ export async function transcribe({
|
|
|
38
52
|
maxRetries: maxRetriesArg,
|
|
39
53
|
abortSignal,
|
|
40
54
|
headers,
|
|
55
|
+
telemetry,
|
|
41
56
|
download: downloadFn = defaultDownload,
|
|
57
|
+
_internal: { generateCallId = originalGenerateCallId } = {},
|
|
42
58
|
}: {
|
|
43
59
|
/**
|
|
44
60
|
* The transcription model to use.
|
|
@@ -84,6 +100,11 @@ export async function transcribe({
|
|
|
84
100
|
*/
|
|
85
101
|
headers?: Record<string, string>;
|
|
86
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Optional telemetry configuration.
|
|
105
|
+
*/
|
|
106
|
+
telemetry?: TelemetryOptions;
|
|
107
|
+
|
|
87
108
|
/**
|
|
88
109
|
* Custom download function for fetching audio from URLs.
|
|
89
110
|
* Use `createDownload()` from `ai` to create a download function with custom size limits.
|
|
@@ -94,13 +115,20 @@ export async function transcribe({
|
|
|
94
115
|
url: URL;
|
|
95
116
|
abortSignal?: AbortSignal;
|
|
96
117
|
}) => Promise<{ data: Uint8Array; mediaType: string | undefined }>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Internal. For test use only. May change without notice.
|
|
121
|
+
*/
|
|
122
|
+
_internal?: {
|
|
123
|
+
generateCallId?: () => string;
|
|
124
|
+
};
|
|
97
125
|
}): Promise<TranscriptionResult> {
|
|
98
126
|
const resolvedModel = resolveTranscriptionModel(model);
|
|
99
127
|
if (!resolvedModel) {
|
|
100
128
|
throw new Error('Model could not be resolved');
|
|
101
129
|
}
|
|
102
130
|
|
|
103
|
-
const { retry } = prepareRetries({
|
|
131
|
+
const { maxRetries, retry } = prepareRetries({
|
|
104
132
|
maxRetries: maxRetriesArg,
|
|
105
133
|
abortSignal,
|
|
106
134
|
});
|
|
@@ -110,44 +138,169 @@ export async function transcribe({
|
|
|
110
138
|
`ai/${VERSION}`,
|
|
111
139
|
);
|
|
112
140
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
resolvedModel.doGenerate({
|
|
120
|
-
audio: audioData,
|
|
121
|
-
abortSignal,
|
|
122
|
-
headers: headersWithUserAgent,
|
|
123
|
-
providerOptions,
|
|
124
|
-
mediaType:
|
|
125
|
-
detectMediaType({
|
|
126
|
-
data: audioData,
|
|
127
|
-
topLevelType: 'audio',
|
|
128
|
-
}) ?? 'audio/wav',
|
|
129
|
-
}),
|
|
130
|
-
);
|
|
141
|
+
const callId = generateCallId();
|
|
142
|
+
const telemetryDispatcher = createTelemetryDispatcher({ telemetry });
|
|
143
|
+
const runInTracingChannelSpan =
|
|
144
|
+
telemetryDispatcher.runInTracingChannelSpan ??
|
|
145
|
+
(async <T>({ execute }: { execute: () => PromiseLike<T> }) =>
|
|
146
|
+
await execute());
|
|
131
147
|
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
const createStartEvent = ({
|
|
149
|
+
byteLength,
|
|
150
|
+
mediaType,
|
|
151
|
+
}: {
|
|
152
|
+
byteLength: number | undefined;
|
|
153
|
+
mediaType: string | undefined;
|
|
154
|
+
}): TranscriptionStartEvent => ({
|
|
155
|
+
callId,
|
|
156
|
+
operationId: 'ai.transcribe',
|
|
134
157
|
provider: resolvedModel.provider,
|
|
135
|
-
|
|
158
|
+
modelId: resolvedModel.modelId,
|
|
159
|
+
audio: {
|
|
160
|
+
byteLength,
|
|
161
|
+
mediaType,
|
|
162
|
+
},
|
|
163
|
+
inputAudioFormat: undefined,
|
|
164
|
+
maxRetries,
|
|
165
|
+
headers,
|
|
166
|
+
providerOptions,
|
|
136
167
|
});
|
|
137
168
|
|
|
138
|
-
|
|
139
|
-
|
|
169
|
+
const executeWithStartEvent = async <T>({
|
|
170
|
+
startEvent,
|
|
171
|
+
execute,
|
|
172
|
+
}: {
|
|
173
|
+
startEvent: TranscriptionStartEvent;
|
|
174
|
+
execute: () => PromiseLike<T>;
|
|
175
|
+
}) =>
|
|
176
|
+
await runInTracingChannelSpan({
|
|
177
|
+
type: 'transcribe',
|
|
178
|
+
event: startEvent,
|
|
179
|
+
execute: async () => {
|
|
180
|
+
await notify({
|
|
181
|
+
event: startEvent,
|
|
182
|
+
callbacks: [telemetryDispatcher.onStart],
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return await execute();
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
let preparationError: unknown;
|
|
190
|
+
let preparationFailed = false;
|
|
191
|
+
let audioData: Uint8Array;
|
|
192
|
+
let downloadedMediaType: string | undefined;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
if (audio instanceof URL) {
|
|
196
|
+
const downloadResult = await downloadFn({ url: audio, abortSignal });
|
|
197
|
+
audioData = downloadResult.data;
|
|
198
|
+
downloadedMediaType = downloadResult.mediaType;
|
|
199
|
+
} else {
|
|
200
|
+
audioData = convertDataContentToUint8Array(audio);
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
preparationFailed = true;
|
|
204
|
+
preparationError = error;
|
|
205
|
+
audioData = new Uint8Array();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (preparationFailed) {
|
|
209
|
+
const startEvent = createStartEvent({
|
|
210
|
+
byteLength: undefined,
|
|
211
|
+
mediaType: undefined,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
return await executeWithStartEvent({
|
|
216
|
+
startEvent,
|
|
217
|
+
execute: () => {
|
|
218
|
+
throw preparationError;
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
await telemetryDispatcher.onError?.({ callId, error });
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
140
225
|
}
|
|
141
226
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
227
|
+
const mediaType =
|
|
228
|
+
downloadedMediaType ??
|
|
229
|
+
detectMediaType({
|
|
230
|
+
data: audioData,
|
|
231
|
+
topLevelType: 'audio',
|
|
232
|
+
}) ??
|
|
233
|
+
'audio/wav';
|
|
234
|
+
const startEvent = createStartEvent({
|
|
235
|
+
byteLength: audioData.byteLength,
|
|
236
|
+
mediaType,
|
|
150
237
|
});
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
return await executeWithStartEvent({
|
|
241
|
+
startEvent,
|
|
242
|
+
execute: async () => {
|
|
243
|
+
const result = await retry(() =>
|
|
244
|
+
resolvedModel.doGenerate({
|
|
245
|
+
audio: audioData,
|
|
246
|
+
abortSignal,
|
|
247
|
+
headers: headersWithUserAgent,
|
|
248
|
+
providerOptions,
|
|
249
|
+
mediaType,
|
|
250
|
+
}),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
logWarnings({
|
|
254
|
+
warnings: result.warnings,
|
|
255
|
+
provider: resolvedModel.provider,
|
|
256
|
+
model: resolvedModel.modelId,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (!result.text) {
|
|
260
|
+
throw new NoTranscriptGeneratedError({
|
|
261
|
+
responses: [result.response],
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const endEvent: TranscriptionEndEvent = {
|
|
266
|
+
callId,
|
|
267
|
+
operationId: 'ai.transcribe',
|
|
268
|
+
provider: resolvedModel.provider,
|
|
269
|
+
modelId: resolvedModel.modelId,
|
|
270
|
+
audio: {
|
|
271
|
+
byteLength: audioData.byteLength,
|
|
272
|
+
mediaType,
|
|
273
|
+
},
|
|
274
|
+
text: result.text,
|
|
275
|
+
segments: result.segments,
|
|
276
|
+
language: result.language,
|
|
277
|
+
durationInSeconds: result.durationInSeconds,
|
|
278
|
+
usage: result.usage,
|
|
279
|
+
warnings: result.warnings,
|
|
280
|
+
providerMetadata: result.providerMetadata,
|
|
281
|
+
response: result.response,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
await notify({
|
|
285
|
+
event: endEvent,
|
|
286
|
+
callbacks: [telemetryDispatcher.onEnd],
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
return new DefaultTranscriptionResult({
|
|
290
|
+
text: result.text,
|
|
291
|
+
segments: result.segments,
|
|
292
|
+
language: result.language,
|
|
293
|
+
durationInSeconds: result.durationInSeconds,
|
|
294
|
+
warnings: result.warnings,
|
|
295
|
+
responses: [result.response],
|
|
296
|
+
providerMetadata: result.providerMetadata,
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
} catch (error) {
|
|
301
|
+
await telemetryDispatcher.onError?.({ callId, error });
|
|
302
|
+
throw error;
|
|
303
|
+
}
|
|
151
304
|
}
|
|
152
305
|
|
|
153
306
|
class DefaultTranscriptionResult implements TranscriptionResult {
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { JSONObject, SharedV4AudioFormat } from '@ai-sdk/provider';
|
|
2
|
+
import type { ProviderOptions } from '@ai-sdk/provider-utils';
|
|
3
|
+
import type { ProviderMetadata } from '../types';
|
|
4
|
+
import type { TranscriptionModelResponseMetadata } from '../types/transcription-model-response-metadata';
|
|
5
|
+
import type { Warning } from '../types/warning';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Event passed to telemetry integrations when transcription begins.
|
|
9
|
+
*/
|
|
10
|
+
type TranscriptionStartEventFor<OPERATION_ID extends string> = {
|
|
11
|
+
/** Unique identifier for this transcription call. */
|
|
12
|
+
readonly callId: string;
|
|
13
|
+
|
|
14
|
+
/** Identifies the operation type (`ai.transcribe` or `ai.streamTranscribe`). */
|
|
15
|
+
readonly operationId: OPERATION_ID;
|
|
16
|
+
|
|
17
|
+
/** The provider identifier. */
|
|
18
|
+
readonly provider: string;
|
|
19
|
+
|
|
20
|
+
/** The specific model identifier. */
|
|
21
|
+
readonly modelId: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Metadata about the input audio. The byte length is known immediately for
|
|
25
|
+
* non-streaming transcription and is reported on the end event for streams.
|
|
26
|
+
*/
|
|
27
|
+
readonly audio: {
|
|
28
|
+
readonly byteLength: number | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The media type, when it is known before input preparation completes.
|
|
31
|
+
* URL downloads and invalid inline inputs can begin without this metadata.
|
|
32
|
+
*/
|
|
33
|
+
readonly mediaType: string | undefined;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** The raw input format for streaming transcription. */
|
|
37
|
+
readonly inputAudioFormat: SharedV4AudioFormat | undefined;
|
|
38
|
+
|
|
39
|
+
/** Maximum number of retries for non-streaming transcription. */
|
|
40
|
+
readonly maxRetries: number | undefined;
|
|
41
|
+
|
|
42
|
+
/** Additional HTTP or WebSocket headers sent with the request. */
|
|
43
|
+
readonly headers: Record<string, string> | undefined;
|
|
44
|
+
|
|
45
|
+
/** Additional provider-specific options. */
|
|
46
|
+
readonly providerOptions: ProviderOptions;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type TranscriptionStartEvent =
|
|
50
|
+
TranscriptionStartEventFor<'ai.transcribe'>;
|
|
51
|
+
|
|
52
|
+
export type StreamTranscriptionStartEvent =
|
|
53
|
+
TranscriptionStartEventFor<'ai.streamTranscribe'>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Event passed to telemetry integrations when transcription completes.
|
|
57
|
+
*
|
|
58
|
+
* Raw audio is intentionally not included.
|
|
59
|
+
*/
|
|
60
|
+
type TranscriptionEndEventFor<OPERATION_ID extends string> = {
|
|
61
|
+
/** Unique identifier for this transcription call. */
|
|
62
|
+
readonly callId: string;
|
|
63
|
+
|
|
64
|
+
/** Identifies the operation type (`ai.transcribe` or `ai.streamTranscribe`). */
|
|
65
|
+
readonly operationId: OPERATION_ID;
|
|
66
|
+
|
|
67
|
+
/** The provider identifier. */
|
|
68
|
+
readonly provider: string;
|
|
69
|
+
|
|
70
|
+
/** The specific model identifier. */
|
|
71
|
+
readonly modelId: string;
|
|
72
|
+
|
|
73
|
+
/** Metadata about the input audio. */
|
|
74
|
+
readonly audio: {
|
|
75
|
+
readonly byteLength: number;
|
|
76
|
+
readonly mediaType: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** The complete transcript. */
|
|
80
|
+
readonly text: string;
|
|
81
|
+
|
|
82
|
+
/** Transcript segments with timing information. */
|
|
83
|
+
readonly segments: Array<{
|
|
84
|
+
readonly text: string;
|
|
85
|
+
readonly startSecond: number;
|
|
86
|
+
readonly endSecond: number;
|
|
87
|
+
}>;
|
|
88
|
+
|
|
89
|
+
/** The detected language. */
|
|
90
|
+
readonly language: string | undefined;
|
|
91
|
+
|
|
92
|
+
/** The duration of the input audio, when available. */
|
|
93
|
+
readonly durationInSeconds: number | undefined;
|
|
94
|
+
|
|
95
|
+
/** Usage reported by the provider, when available. */
|
|
96
|
+
readonly usage: JSONObject | undefined;
|
|
97
|
+
|
|
98
|
+
/** Warnings from the transcription model. */
|
|
99
|
+
readonly warnings: Array<Warning>;
|
|
100
|
+
|
|
101
|
+
/** Optional provider-specific metadata. */
|
|
102
|
+
readonly providerMetadata: ProviderMetadata | undefined;
|
|
103
|
+
|
|
104
|
+
/** Response metadata from the provider. */
|
|
105
|
+
readonly response: TranscriptionModelResponseMetadata;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type TranscriptionEndEvent = TranscriptionEndEventFor<'ai.transcribe'>;
|
|
109
|
+
|
|
110
|
+
export type StreamTranscriptionEndEvent =
|
|
111
|
+
TranscriptionEndEventFor<'ai.streamTranscribe'>;
|