@ai-sdk/assemblyai 3.0.3 → 3.0.5
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 +39 -0
- package/README.md +1 -1
- package/dist/index.d.ts +36 -2
- package/dist/index.js +245 -26
- package/dist/index.js.map +1 -1
- package/docs/100-assemblyai.mdx +134 -9
- package/package.json +3 -3
- package/src/assemblyai-api-types.ts +84 -1
- package/src/assemblyai-provider.ts +1 -1
- package/src/assemblyai-transcription-model-options.ts +83 -1
- package/src/assemblyai-transcription-model.ts +244 -18
- package/src/assemblyai-transcription-settings.ts +15 -1
|
@@ -19,8 +19,12 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
19
19
|
*/
|
|
20
20
|
autoHighlights: z.boolean().nullish(),
|
|
21
21
|
/**
|
|
22
|
-
* Boost parameter for
|
|
22
|
+
* Boost parameter for word boost (used with `wordBoost`).
|
|
23
23
|
* Allowed values: 'low', 'default', 'high'.
|
|
24
|
+
*
|
|
25
|
+
* @deprecated Only applies to the deprecated `wordBoost` option. Use
|
|
26
|
+
* `keytermsPrompt` instead, which works with the recommended `universal-*`
|
|
27
|
+
* models.
|
|
24
28
|
*/
|
|
25
29
|
boostParam: z.string().nullish(),
|
|
26
30
|
/**
|
|
@@ -46,6 +50,11 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
46
50
|
* Whether to include filler words (um, uh, etc.) in the transcription.
|
|
47
51
|
*/
|
|
48
52
|
disfluencies: z.boolean().nullish(),
|
|
53
|
+
/**
|
|
54
|
+
* Enable a domain-specific model to improve accuracy for specialized
|
|
55
|
+
* terminology. Currently supports `'medical-v1'` (Medical Mode).
|
|
56
|
+
*/
|
|
57
|
+
domain: z.string().nullish(),
|
|
49
58
|
/**
|
|
50
59
|
* Whether to enable entity detection.
|
|
51
60
|
*/
|
|
@@ -62,6 +71,13 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
62
71
|
* Whether to enable IAB categories detection.
|
|
63
72
|
*/
|
|
64
73
|
iabCategories: z.boolean().nullish(),
|
|
74
|
+
/**
|
|
75
|
+
* Domain-specific keyterms to boost recognition for (max 6 words per phrase).
|
|
76
|
+
* Replaces `wordBoost` for newer models: supported by `universal-3-pro` /
|
|
77
|
+
* `universal-3-5-pro` and `slam-1` (and `universal-2` when metaphone is
|
|
78
|
+
* enabled for the account).
|
|
79
|
+
*/
|
|
80
|
+
keytermsPrompt: z.array(z.string()).nullish(),
|
|
65
81
|
/**
|
|
66
82
|
* Language code for the transcription.
|
|
67
83
|
*/
|
|
@@ -74,10 +90,30 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
74
90
|
* Whether to enable language detection.
|
|
75
91
|
*/
|
|
76
92
|
languageDetection: z.boolean().nullish(),
|
|
93
|
+
/**
|
|
94
|
+
* Options for automatic language detection.
|
|
95
|
+
*/
|
|
96
|
+
languageDetectionOptions: z
|
|
97
|
+
.object({
|
|
98
|
+
/** List of languages expected in the audio file. */
|
|
99
|
+
expectedLanguages: z.array(z.string()).nullish(),
|
|
100
|
+
/** Fallback language if the detected language is not expected. */
|
|
101
|
+
fallbackLanguage: z.string().nullish(),
|
|
102
|
+
/** Whether code switching should be detected. */
|
|
103
|
+
codeSwitching: z.boolean().nullish(),
|
|
104
|
+
/** Confidence threshold for code switching detection (0-1). */
|
|
105
|
+
codeSwitchingConfidenceThreshold: z.number().min(0).max(1).nullish(),
|
|
106
|
+
})
|
|
107
|
+
.nullish(),
|
|
77
108
|
/**
|
|
78
109
|
* Whether to process audio as multichannel.
|
|
79
110
|
*/
|
|
80
111
|
multichannel: z.boolean().nullish(),
|
|
112
|
+
/**
|
|
113
|
+
* Provide natural-language context (up to 1,500 words) to steer the model.
|
|
114
|
+
* Only supported by `universal-3-pro` / `universal-3-5-pro` and `slam-1`.
|
|
115
|
+
*/
|
|
116
|
+
prompt: z.string().nullish(),
|
|
81
117
|
/**
|
|
82
118
|
* Whether to add punctuation to the transcription.
|
|
83
119
|
*/
|
|
@@ -90,6 +126,17 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
90
126
|
* Whether to redact PII in the audio file.
|
|
91
127
|
*/
|
|
92
128
|
redactPiiAudio: z.boolean().nullish(),
|
|
129
|
+
/**
|
|
130
|
+
* Options for PII-redacted audio files. Requires `redactPiiAudio`.
|
|
131
|
+
*/
|
|
132
|
+
redactPiiAudioOptions: z
|
|
133
|
+
.object({
|
|
134
|
+
/** Return redacted audio even for files without detected speech. */
|
|
135
|
+
returnRedactedNoSpeechAudio: z.boolean().nullish(),
|
|
136
|
+
/** Redaction method; set to `'silence'` to replace PII with silence. */
|
|
137
|
+
overrideAudioRedactionMethod: z.enum(['silence']).nullish(),
|
|
138
|
+
})
|
|
139
|
+
.nullish(),
|
|
93
140
|
/**
|
|
94
141
|
* Audio format for PII redaction.
|
|
95
142
|
*/
|
|
@@ -98,10 +145,26 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
98
145
|
* List of PII types to redact.
|
|
99
146
|
*/
|
|
100
147
|
redactPiiPolicies: z.array(z.string()).nullish(),
|
|
148
|
+
/**
|
|
149
|
+
* Return the original unredacted transcript alongside the redacted one.
|
|
150
|
+
* Requires `redactPii`.
|
|
151
|
+
*/
|
|
152
|
+
redactPiiReturnUnredacted: z.boolean().nullish(),
|
|
101
153
|
/**
|
|
102
154
|
* Substitution method for redacted PII.
|
|
103
155
|
*/
|
|
104
156
|
redactPiiSub: z.string().nullish(),
|
|
157
|
+
/**
|
|
158
|
+
* Map of user-defined labels to exact terms to redact, e.g.
|
|
159
|
+
* `{ INTERNAL_TOOL: ['Bearclaw'] }`. Applied on top of standard PII redaction
|
|
160
|
+
* using `redactPiiSub`. Requires `redactPii`.
|
|
161
|
+
*/
|
|
162
|
+
redactStaticEntities: z.record(z.string(), z.array(z.string())).nullish(),
|
|
163
|
+
/**
|
|
164
|
+
* Remove inline annotations from rich transcripts. `'all'` removes all inline
|
|
165
|
+
* annotations; `'speaker'` removes only speaker cues. Universal-3 Pro models.
|
|
166
|
+
*/
|
|
167
|
+
removeAudioTags: z.enum(['all', 'speaker']).nullish(),
|
|
105
168
|
/**
|
|
106
169
|
* Whether to enable sentiment analysis.
|
|
107
170
|
*/
|
|
@@ -110,6 +173,17 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
110
173
|
* Whether to identify different speakers in the audio.
|
|
111
174
|
*/
|
|
112
175
|
speakerLabels: z.boolean().nullish(),
|
|
176
|
+
/**
|
|
177
|
+
* Options for speaker diarization, e.g. a range of possible speakers.
|
|
178
|
+
*/
|
|
179
|
+
speakerOptions: z
|
|
180
|
+
.object({
|
|
181
|
+
/** Minimum number of speakers expected in the audio file. */
|
|
182
|
+
minSpeakersExpected: z.number().int().nullish(),
|
|
183
|
+
/** Maximum number of speakers expected in the audio file. */
|
|
184
|
+
maxSpeakersExpected: z.number().int().nullish(),
|
|
185
|
+
})
|
|
186
|
+
.nullish(),
|
|
113
187
|
/**
|
|
114
188
|
* Number of speakers expected in the audio.
|
|
115
189
|
*/
|
|
@@ -130,6 +204,10 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
130
204
|
* Type of summary to generate.
|
|
131
205
|
*/
|
|
132
206
|
summaryType: z.string().nullish(),
|
|
207
|
+
/**
|
|
208
|
+
* Sampling temperature (0-1) controlling randomness. Universal-3 Pro models.
|
|
209
|
+
*/
|
|
210
|
+
temperature: z.number().min(0).max(1).nullish(),
|
|
133
211
|
/**
|
|
134
212
|
* Name of the authentication header for webhook requests.
|
|
135
213
|
*/
|
|
@@ -144,6 +222,10 @@ export const assemblyaiTranscriptionModelOptionsSchema = z.object({
|
|
|
144
222
|
webhookUrl: z.string().nullish(),
|
|
145
223
|
/**
|
|
146
224
|
* List of words to boost recognition for.
|
|
225
|
+
*
|
|
226
|
+
* @deprecated `wordBoost` is rejected by `universal-3-pro` /
|
|
227
|
+
* `universal-3-5-pro` and `slam-1` (it only works on `universal-2`/`best`).
|
|
228
|
+
* Use `keytermsPrompt` instead.
|
|
147
229
|
*/
|
|
148
230
|
wordBoost: z.array(z.string()).nullish(),
|
|
149
231
|
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
TranscriptionModelV4,
|
|
3
|
+
SharedV4Warning,
|
|
4
|
+
SharedV4ProviderMetadata,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
2
6
|
import {
|
|
3
7
|
combineHeaders,
|
|
4
8
|
createJsonResponseHandler,
|
|
@@ -66,9 +70,42 @@ export class AssemblyAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
66
70
|
schema: assemblyaiTranscriptionModelOptionsSchema,
|
|
67
71
|
});
|
|
68
72
|
|
|
69
|
-
const body: Omit<AssemblyAITranscriptionAPITypes, 'audio_url'> = {
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
const body: Omit<AssemblyAITranscriptionAPITypes, 'audio_url'> = {};
|
|
74
|
+
|
|
75
|
+
// The legacy `best` model is selected via the deprecated singular
|
|
76
|
+
// `speech_model` parameter. All other models (e.g. `universal-2`,
|
|
77
|
+
// `universal-3-pro`, `universal-3-5-pro`) are only accessible via the
|
|
78
|
+
// `speech_models` array and are rejected by `speech_model`.
|
|
79
|
+
// See https://www.assemblyai.com/docs/pre-recorded-audio/select-the-speech-model
|
|
80
|
+
if (this.modelId === 'best') {
|
|
81
|
+
body.speech_model = this.modelId as 'best';
|
|
82
|
+
warnings.push({
|
|
83
|
+
type: 'deprecated',
|
|
84
|
+
setting: `model '${this.modelId}'`,
|
|
85
|
+
message:
|
|
86
|
+
"The 'best' model is a legacy AssemblyAI model. Use 'universal-3-5-pro' instead. See documentation: https://www.assemblyai.com/docs/pre-recorded-audio/select-the-speech-model",
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
body.speech_models = [this.modelId];
|
|
90
|
+
|
|
91
|
+
// Forward-looking nudge: universal-3-5-pro is AssemblyAI's latest
|
|
92
|
+
// flagship and is set to replace universal-3-pro. Not a deprecation —
|
|
93
|
+
// both models still work — so this is an informational warning only.
|
|
94
|
+
if (
|
|
95
|
+
this.modelId === 'universal-3-pro' ||
|
|
96
|
+
this.modelId === 'universal-2'
|
|
97
|
+
) {
|
|
98
|
+
const docsUrl =
|
|
99
|
+
'https://www.assemblyai.com/docs/pre-recorded-audio/select-the-speech-model';
|
|
100
|
+
warnings.push({
|
|
101
|
+
type: 'other',
|
|
102
|
+
message:
|
|
103
|
+
this.modelId === 'universal-3-pro'
|
|
104
|
+
? `'universal-3-5-pro' is AssemblyAI's latest flagship model and is set to replace 'universal-3-pro'. See ${docsUrl}`
|
|
105
|
+
: `'universal-3-5-pro' is AssemblyAI's latest flagship model. See ${docsUrl}`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
72
109
|
|
|
73
110
|
// Add provider-specific options
|
|
74
111
|
if (assemblyaiOptions) {
|
|
@@ -118,6 +155,103 @@ export class AssemblyAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
118
155
|
assemblyaiOptions.webhookAuthHeaderValue ?? undefined;
|
|
119
156
|
body.webhook_url = assemblyaiOptions.webhookUrl ?? undefined;
|
|
120
157
|
body.word_boost = assemblyaiOptions.wordBoost ?? undefined;
|
|
158
|
+
body.keyterms_prompt = assemblyaiOptions.keytermsPrompt ?? undefined;
|
|
159
|
+
body.prompt = assemblyaiOptions.prompt ?? undefined;
|
|
160
|
+
body.temperature = assemblyaiOptions.temperature ?? undefined;
|
|
161
|
+
body.remove_audio_tags = assemblyaiOptions.removeAudioTags ?? undefined;
|
|
162
|
+
body.domain = assemblyaiOptions.domain ?? undefined;
|
|
163
|
+
body.redact_pii_return_unredacted =
|
|
164
|
+
assemblyaiOptions.redactPiiReturnUnredacted ?? undefined;
|
|
165
|
+
body.redact_static_entities =
|
|
166
|
+
assemblyaiOptions.redactStaticEntities ?? undefined;
|
|
167
|
+
|
|
168
|
+
if (assemblyaiOptions.speakerOptions) {
|
|
169
|
+
body.speaker_options = {
|
|
170
|
+
min_speakers_expected:
|
|
171
|
+
assemblyaiOptions.speakerOptions.minSpeakersExpected ?? undefined,
|
|
172
|
+
max_speakers_expected:
|
|
173
|
+
assemblyaiOptions.speakerOptions.maxSpeakersExpected ?? undefined,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (assemblyaiOptions.languageDetectionOptions) {
|
|
178
|
+
body.language_detection_options = {
|
|
179
|
+
expected_languages:
|
|
180
|
+
assemblyaiOptions.languageDetectionOptions.expectedLanguages ??
|
|
181
|
+
undefined,
|
|
182
|
+
fallback_language:
|
|
183
|
+
assemblyaiOptions.languageDetectionOptions.fallbackLanguage ??
|
|
184
|
+
undefined,
|
|
185
|
+
code_switching:
|
|
186
|
+
assemblyaiOptions.languageDetectionOptions.codeSwitching ??
|
|
187
|
+
undefined,
|
|
188
|
+
code_switching_confidence_threshold:
|
|
189
|
+
assemblyaiOptions.languageDetectionOptions
|
|
190
|
+
.codeSwitchingConfidenceThreshold ?? undefined,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (assemblyaiOptions.redactPiiAudioOptions) {
|
|
195
|
+
body.redact_pii_audio_options = {
|
|
196
|
+
return_redacted_no_speech_audio:
|
|
197
|
+
assemblyaiOptions.redactPiiAudioOptions
|
|
198
|
+
.returnRedactedNoSpeechAudio ?? undefined,
|
|
199
|
+
override_audio_redaction_method:
|
|
200
|
+
assemblyaiOptions.redactPiiAudioOptions
|
|
201
|
+
.overrideAudioRedactionMethod ?? undefined,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const deprecatedBoostOptions: string[] = [];
|
|
206
|
+
if (assemblyaiOptions.wordBoost != null) {
|
|
207
|
+
deprecatedBoostOptions.push('wordBoost');
|
|
208
|
+
}
|
|
209
|
+
if (assemblyaiOptions.boostParam != null) {
|
|
210
|
+
deprecatedBoostOptions.push('boostParam');
|
|
211
|
+
}
|
|
212
|
+
if (deprecatedBoostOptions.length > 0) {
|
|
213
|
+
warnings.push({
|
|
214
|
+
type: 'deprecated',
|
|
215
|
+
setting: deprecatedBoostOptions.join(', '),
|
|
216
|
+
message:
|
|
217
|
+
"'wordBoost' and 'boostParam' are deprecated and are rejected by 'universal-3-pro' / 'universal-3-5-pro' and 'slam-1'. Use 'keytermsPrompt' instead.",
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// The following options only take effect alongside a prerequisite
|
|
222
|
+
// option; without it AssemblyAI either rejects the request (400) or
|
|
223
|
+
// silently ignores the option. Warn rather than mutate user input.
|
|
224
|
+
if (
|
|
225
|
+
(assemblyaiOptions.redactPiiReturnUnredacted != null ||
|
|
226
|
+
assemblyaiOptions.redactStaticEntities != null) &&
|
|
227
|
+
!assemblyaiOptions.redactPii
|
|
228
|
+
) {
|
|
229
|
+
warnings.push({
|
|
230
|
+
type: 'other',
|
|
231
|
+
message:
|
|
232
|
+
"'redactPiiReturnUnredacted' and 'redactStaticEntities' require 'redactPii' to be enabled; AssemblyAI rejects the request otherwise.",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (
|
|
236
|
+
assemblyaiOptions.redactPiiAudioOptions != null &&
|
|
237
|
+
!assemblyaiOptions.redactPiiAudio
|
|
238
|
+
) {
|
|
239
|
+
warnings.push({
|
|
240
|
+
type: 'other',
|
|
241
|
+
message:
|
|
242
|
+
"'redactPiiAudioOptions' only applies when 'redactPiiAudio' is enabled; it is otherwise ignored.",
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (
|
|
246
|
+
assemblyaiOptions.languageCode != null &&
|
|
247
|
+
assemblyaiOptions.languageDetection
|
|
248
|
+
) {
|
|
249
|
+
warnings.push({
|
|
250
|
+
type: 'other',
|
|
251
|
+
message:
|
|
252
|
+
"'languageDetection' cannot be combined with an explicit 'languageCode'; AssemblyAI rejects requests that set both.",
|
|
253
|
+
});
|
|
254
|
+
}
|
|
121
255
|
}
|
|
122
256
|
|
|
123
257
|
return {
|
|
@@ -137,17 +271,22 @@ export class AssemblyAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
137
271
|
abortSignal?: AbortSignal,
|
|
138
272
|
): Promise<{
|
|
139
273
|
transcript: z.infer<typeof assemblyaiTranscriptionResponseSchema>;
|
|
274
|
+
rawTranscript: unknown;
|
|
140
275
|
responseHeaders: Record<string, string>;
|
|
141
276
|
}> {
|
|
142
277
|
const pollingInterval =
|
|
143
278
|
this.config.pollingInterval ?? this.POLLING_INTERVAL_MS;
|
|
144
279
|
|
|
280
|
+
// Honor a caller-provided fetch (proxy, auth injection, tests) for the
|
|
281
|
+
// polling GETs, matching the upload/submit calls that use config.fetch.
|
|
282
|
+
const fetchImpl = this.config.fetch ?? globalThis.fetch;
|
|
283
|
+
|
|
145
284
|
while (true) {
|
|
146
285
|
if (abortSignal?.aborted) {
|
|
147
286
|
throw new Error('Transcription request was aborted');
|
|
148
287
|
}
|
|
149
288
|
|
|
150
|
-
const response = await
|
|
289
|
+
const response = await fetchImpl(
|
|
151
290
|
this.config.url({
|
|
152
291
|
path: `/v2/transcript/${transcriptId}`,
|
|
153
292
|
modelId: this.modelId,
|
|
@@ -173,13 +312,14 @@ export class AssemblyAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
173
312
|
});
|
|
174
313
|
}
|
|
175
314
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
315
|
+
const rawTranscript = await response.json();
|
|
316
|
+
const transcript =
|
|
317
|
+
assemblyaiTranscriptionResponseSchema.parse(rawTranscript);
|
|
179
318
|
|
|
180
319
|
if (transcript.status === 'completed') {
|
|
181
320
|
return {
|
|
182
321
|
transcript,
|
|
322
|
+
rawTranscript,
|
|
183
323
|
responseHeaders: extractResponseHeaders(response),
|
|
184
324
|
};
|
|
185
325
|
}
|
|
@@ -240,29 +380,70 @@ export class AssemblyAITranscriptionModel implements TranscriptionModelV4 {
|
|
|
240
380
|
fetch: this.config.fetch,
|
|
241
381
|
});
|
|
242
382
|
|
|
243
|
-
const { transcript, responseHeaders } =
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
383
|
+
const { transcript, rawTranscript, responseHeaders } =
|
|
384
|
+
await this.waitForCompletion(
|
|
385
|
+
submitResponse.id,
|
|
386
|
+
options.headers,
|
|
387
|
+
options.abortSignal,
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
// Surface diarization and audio-intelligence results that the AI SDK's
|
|
391
|
+
// `segments` shape can't represent, keyed under `assemblyai`. Presence is
|
|
392
|
+
// gated on the parsed transcript, but values are taken from the raw
|
|
393
|
+
// response so no fields are stripped by the schema.
|
|
394
|
+
//
|
|
395
|
+
// NOTE: timings inside these objects (e.g. `utterances[].start`) are in
|
|
396
|
+
// milliseconds, matching the AssemblyAI API — unlike the top-level
|
|
397
|
+
// `segments`, whose `startSecond`/`endSecond` are in seconds.
|
|
398
|
+
const raw = (rawTranscript ?? {}) as Record<string, unknown>;
|
|
399
|
+
const assemblyaiMetadata: Record<string, unknown> = {};
|
|
400
|
+
if (transcript.utterances != null) {
|
|
401
|
+
assemblyaiMetadata.utterances = raw.utterances;
|
|
402
|
+
}
|
|
403
|
+
if (transcript.sentiment_analysis_results != null) {
|
|
404
|
+
assemblyaiMetadata.sentimentAnalysisResults =
|
|
405
|
+
raw.sentiment_analysis_results;
|
|
406
|
+
}
|
|
407
|
+
if (transcript.entities != null) {
|
|
408
|
+
assemblyaiMetadata.entities = raw.entities;
|
|
409
|
+
}
|
|
410
|
+
if (transcript.content_safety_labels != null) {
|
|
411
|
+
assemblyaiMetadata.contentSafetyLabels = raw.content_safety_labels;
|
|
412
|
+
}
|
|
413
|
+
if (transcript.iab_categories_result != null) {
|
|
414
|
+
assemblyaiMetadata.iabCategoriesResult = raw.iab_categories_result;
|
|
415
|
+
}
|
|
416
|
+
if (transcript.auto_highlights_result != null) {
|
|
417
|
+
assemblyaiMetadata.autoHighlightsResult = raw.auto_highlights_result;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const lastWordEndMs = transcript.words?.at(-1)?.end;
|
|
248
421
|
|
|
249
422
|
return {
|
|
250
423
|
text: transcript.text ?? '',
|
|
424
|
+
// AssemblyAI returns word timings in milliseconds; the AI SDK reports
|
|
425
|
+
// segment timings in seconds.
|
|
251
426
|
segments:
|
|
252
427
|
transcript.words?.map(word => ({
|
|
253
428
|
text: word.text,
|
|
254
|
-
startSecond: word.start,
|
|
255
|
-
endSecond: word.end,
|
|
429
|
+
startSecond: word.start / 1000,
|
|
430
|
+
endSecond: word.end / 1000,
|
|
256
431
|
})) ?? [],
|
|
257
432
|
language: transcript.language_code ?? undefined,
|
|
258
433
|
durationInSeconds:
|
|
259
|
-
transcript.audio_duration ??
|
|
434
|
+
transcript.audio_duration ??
|
|
435
|
+
(lastWordEndMs != null ? lastWordEndMs / 1000 : undefined),
|
|
260
436
|
warnings,
|
|
437
|
+
...(Object.keys(assemblyaiMetadata).length > 0 && {
|
|
438
|
+
providerMetadata: {
|
|
439
|
+
assemblyai: assemblyaiMetadata,
|
|
440
|
+
} as SharedV4ProviderMetadata,
|
|
441
|
+
}),
|
|
261
442
|
response: {
|
|
262
443
|
timestamp: currentDate,
|
|
263
444
|
modelId: this.modelId,
|
|
264
445
|
headers: responseHeaders, // Headers from final GET request
|
|
265
|
-
body:
|
|
446
|
+
body: rawTranscript, // Full raw response from final GET request
|
|
266
447
|
},
|
|
267
448
|
};
|
|
268
449
|
}
|
|
@@ -277,20 +458,65 @@ const assemblyaiSubmitResponseSchema = z.object({
|
|
|
277
458
|
status: z.enum(['queued', 'processing', 'completed', 'error']),
|
|
278
459
|
});
|
|
279
460
|
|
|
461
|
+
const assemblyaiWordSchema = z.object({
|
|
462
|
+
start: z.number(),
|
|
463
|
+
end: z.number(),
|
|
464
|
+
text: z.string(),
|
|
465
|
+
confidence: z.number().nullish(),
|
|
466
|
+
// Speaker label (e.g. 'A', 'B') when speaker diarization is enabled, else null.
|
|
467
|
+
speaker: z.string().nullish(),
|
|
468
|
+
channel: z.string().nullish(),
|
|
469
|
+
});
|
|
470
|
+
|
|
280
471
|
const assemblyaiTranscriptionResponseSchema = z.object({
|
|
281
472
|
id: z.string(),
|
|
282
473
|
status: z.enum(['queued', 'processing', 'completed', 'error']),
|
|
283
474
|
text: z.string().nullish(),
|
|
284
475
|
language_code: z.string().nullish(),
|
|
285
|
-
|
|
476
|
+
speech_model_used: z.string().nullish(),
|
|
477
|
+
words: z.array(assemblyaiWordSchema).nullish(),
|
|
478
|
+
// Speaker-diarized utterances (present when `speaker_labels` is enabled).
|
|
479
|
+
utterances: z
|
|
286
480
|
.array(
|
|
287
481
|
z.object({
|
|
288
482
|
start: z.number(),
|
|
289
483
|
end: z.number(),
|
|
290
484
|
text: z.string(),
|
|
485
|
+
confidence: z.number().nullish(),
|
|
486
|
+
speaker: z.string().nullish(),
|
|
487
|
+
channel: z.string().nullish(),
|
|
488
|
+
words: z.array(assemblyaiWordSchema).nullish(),
|
|
489
|
+
}),
|
|
490
|
+
)
|
|
491
|
+
.nullish(),
|
|
492
|
+
// Audio-intelligence results, present only when the matching feature is
|
|
493
|
+
// enabled. Kept intentionally permissive (the full structures are also
|
|
494
|
+
// available on the raw `response.body`).
|
|
495
|
+
sentiment_analysis_results: z
|
|
496
|
+
.array(
|
|
497
|
+
z.object({
|
|
498
|
+
text: z.string(),
|
|
499
|
+
start: z.number().nullish(),
|
|
500
|
+
end: z.number().nullish(),
|
|
501
|
+
sentiment: z.string(),
|
|
502
|
+
confidence: z.number().nullish(),
|
|
503
|
+
speaker: z.string().nullish(),
|
|
504
|
+
}),
|
|
505
|
+
)
|
|
506
|
+
.nullish(),
|
|
507
|
+
entities: z
|
|
508
|
+
.array(
|
|
509
|
+
z.object({
|
|
510
|
+
entity_type: z.string(),
|
|
511
|
+
text: z.string(),
|
|
512
|
+
start: z.number().nullish(),
|
|
513
|
+
end: z.number().nullish(),
|
|
291
514
|
}),
|
|
292
515
|
)
|
|
293
516
|
.nullish(),
|
|
517
|
+
content_safety_labels: z.record(z.string(), z.any()).nullish(),
|
|
518
|
+
iab_categories_result: z.record(z.string(), z.any()).nullish(),
|
|
519
|
+
auto_highlights_result: z.record(z.string(), z.any()).nullish(),
|
|
294
520
|
audio_duration: z.number().nullish(),
|
|
295
521
|
error: z.string().nullish(),
|
|
296
522
|
});
|
|
@@ -1 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Legacy AssemblyAI speech model, sent via the deprecated singular
|
|
3
|
+
* `speech_model` request parameter.
|
|
4
|
+
*
|
|
5
|
+
* @deprecated Use `universal-3-5-pro` instead.
|
|
6
|
+
* @see https://www.assemblyai.com/docs/pre-recorded-audio/select-the-speech-model
|
|
7
|
+
*/
|
|
8
|
+
export type AssemblyAIDeprecatedTranscriptionModelId = 'best';
|
|
9
|
+
|
|
10
|
+
export type AssemblyAITranscriptionModelId =
|
|
11
|
+
| 'universal-2'
|
|
12
|
+
| 'universal-3-pro'
|
|
13
|
+
| 'universal-3-5-pro'
|
|
14
|
+
| AssemblyAIDeprecatedTranscriptionModelId
|
|
15
|
+
| (string & {});
|