ai 7.0.162 → 7.0.164
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 +78 -0
- package/dist/index.d.ts +166 -41
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +539 -112
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +43 -10
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +209 -24
- package/dist/internal/index.js.map +1 -1
- package/dist/test/index.d.ts +3 -1
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +2 -1
- package/dist/test/index.js.map +1 -1
- package/docs/02-foundations/02-providers-and-models.mdx +2 -0
- package/docs/03-agents/04-loop-control.mdx +34 -0
- package/docs/03-agents/06-memory.mdx +42 -0
- package/docs/03-ai-sdk-core/05-generating-text.mdx +1 -0
- package/docs/03-ai-sdk-core/16-mcp-tools.mdx +12 -0
- package/docs/03-ai-sdk-core/36-realtime.mdx +74 -1
- package/docs/03-ai-sdk-core/36-transcription.mdx +51 -4
- package/docs/03-ai-sdk-core/40-middleware.mdx +7 -2
- package/docs/03-ai-sdk-core/60-telemetry.mdx +37 -4
- package/docs/04-ai-sdk-ui/50-stream-protocol.mdx +13 -0
- package/docs/06-advanced/04-caching.mdx +13 -4
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +33 -28
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +58 -1
- package/docs/07-reference/01-ai-sdk-core/11-transcribe.mdx +17 -2
- package/docs/07-reference/01-ai-sdk-core/13-generate-video.mdx +8 -1
- package/docs/07-reference/01-ai-sdk-core/31-ui-message.mdx +23 -0
- package/docs/07-reference/02-ai-sdk-ui/05-use-realtime.mdx +37 -8
- package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +1 -49
- package/docs/08-migration-guides/23-migration-guide-7-0.mdx +36 -34
- package/package.json +12 -12
- package/src/generate-object/generate-object.ts +2 -1
- package/src/generate-object/stream-object.ts +7 -3
- package/src/generate-text/generate-text.ts +23 -3
- package/src/generate-text/is-output-chunk.ts +1 -0
- package/src/generate-text/language-model-events.ts +3 -0
- package/src/generate-text/output.ts +90 -37
- package/src/generate-text/smooth-stream.ts +5 -0
- package/src/generate-text/steering.ts +5 -4
- package/src/generate-text/stream-language-model-call.ts +16 -1
- package/src/generate-text/stream-text-result.ts +29 -1
- package/src/generate-text/stream-text.ts +25 -1
- package/src/generate-text/to-response-messages.ts +6 -2
- package/src/generate-video/generate-video-result.ts +11 -2
- package/src/generate-video/generate-video.ts +26 -2
- package/src/model/as-transcription-model-v4.ts +25 -0
- package/src/prompt/convert-to-language-model-prompt.ts +86 -14
- package/src/realtime/index.ts +3 -0
- package/src/realtime/realtime-attempt.ts +1 -0
- package/src/realtime/realtime-session.ts +142 -21
- package/src/telemetry/create-telemetry-dispatcher.ts +8 -1
- package/src/telemetry/filter-telemetry-event.ts +165 -0
- package/src/telemetry/get-request-origin.ts +25 -0
- package/src/telemetry/tracing-channel-publisher.ts +19 -4
- package/src/test/mock-transcription-model-v4.ts +4 -0
- package/src/transcribe/transcribe-result.ts +20 -0
- package/src/transcribe/transcribe.ts +107 -20
- package/src/transcribe/transcription-events.ts +28 -8
- package/src/types/language-model-request-metadata.ts +3 -0
- package/src/types/realtime-model.ts +9 -0
- package/src/ui/convert-to-model-messages.ts +36 -28
- package/src/ui/index.ts +1 -0
- package/src/ui/process-ui-message-stream.ts +10 -0
- package/src/ui/ui-messages.ts +20 -0
- package/src/ui/validate-ui-messages.ts +21 -0
- package/src/ui-message-stream/to-ui-message-chunk.ts +72 -1
- package/src/ui-message-stream/ui-message-chunks.ts +31 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { TelemetryOptions } from './telemetry-options';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Filters SDK-owned content fields on a copy. Only descend into SDK event
|
|
5
|
+
* containers; user context has its own inclusion policy.
|
|
6
|
+
*/
|
|
7
|
+
export function filterTelemetryEvent<EVENT>(
|
|
8
|
+
event: EVENT,
|
|
9
|
+
telemetry: Pick<TelemetryOptions, 'recordInputs' | 'recordOutputs'>,
|
|
10
|
+
): EVENT {
|
|
11
|
+
const hideInputs = telemetry.recordInputs === false;
|
|
12
|
+
const hideOutputs = telemetry.recordOutputs === false;
|
|
13
|
+
if (
|
|
14
|
+
(!hideInputs && !hideOutputs) ||
|
|
15
|
+
event == null ||
|
|
16
|
+
typeof event !== 'object'
|
|
17
|
+
) {
|
|
18
|
+
return event;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const filtered = Object.assign(
|
|
22
|
+
Object.create(Object.getPrototypeOf(event)),
|
|
23
|
+
event,
|
|
24
|
+
) as Record<string, unknown>;
|
|
25
|
+
const replace = (fields: Record<string, unknown>) => {
|
|
26
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
27
|
+
if (Object.hasOwn(event, key)) {
|
|
28
|
+
filtered[key] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const isSpeech = filtered.operationId === 'ai.generateSpeech';
|
|
34
|
+
if (hideInputs) {
|
|
35
|
+
replace({
|
|
36
|
+
instructions: undefined,
|
|
37
|
+
system: undefined,
|
|
38
|
+
prompt: undefined,
|
|
39
|
+
messages: [],
|
|
40
|
+
promptMessages: [],
|
|
41
|
+
experimental_steeringMessages: [],
|
|
42
|
+
tools: undefined,
|
|
43
|
+
stepTools: undefined,
|
|
44
|
+
schema: undefined,
|
|
45
|
+
schemaName: undefined,
|
|
46
|
+
schemaDescription: undefined,
|
|
47
|
+
...(typeof filtered.output === 'object' ? { output: undefined } : {}),
|
|
48
|
+
providerOptions: {},
|
|
49
|
+
value: '',
|
|
50
|
+
values: [],
|
|
51
|
+
query: '',
|
|
52
|
+
documents: [],
|
|
53
|
+
state: Array.isArray(filtered.state)
|
|
54
|
+
? []
|
|
55
|
+
: typeof filtered.state === 'string'
|
|
56
|
+
? ''
|
|
57
|
+
: {},
|
|
58
|
+
questions: {},
|
|
59
|
+
...(isSpeech ? { text: '' } : {}),
|
|
60
|
+
});
|
|
61
|
+
if (filtered.toolCall != null) {
|
|
62
|
+
filtered.toolCall = { ...filtered.toolCall, input: undefined };
|
|
63
|
+
}
|
|
64
|
+
if (filtered.toolOutput != null) {
|
|
65
|
+
filtered.toolOutput = { ...filtered.toolOutput, input: undefined };
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(filtered.ranking)) {
|
|
68
|
+
filtered.ranking = filtered.ranking.map(entry =>
|
|
69
|
+
'document' in entry
|
|
70
|
+
? { ...entry, document: typeof entry.document === 'string' ? '' : {} }
|
|
71
|
+
: entry,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (hideOutputs) {
|
|
77
|
+
replace({
|
|
78
|
+
content: [],
|
|
79
|
+
...(!isSpeech ? { text: '' } : {}),
|
|
80
|
+
reasoning: Array.isArray(filtered.reasoning) ? [] : undefined,
|
|
81
|
+
reasoningText: undefined,
|
|
82
|
+
files: [],
|
|
83
|
+
sources: [],
|
|
84
|
+
toolCalls: [],
|
|
85
|
+
staticToolCalls: [],
|
|
86
|
+
dynamicToolCalls: [],
|
|
87
|
+
toolResults: [],
|
|
88
|
+
staticToolResults: [],
|
|
89
|
+
dynamicToolResults: [],
|
|
90
|
+
responseMessages: [],
|
|
91
|
+
object: undefined,
|
|
92
|
+
objectText: '',
|
|
93
|
+
embedding: [],
|
|
94
|
+
embeddings: [],
|
|
95
|
+
ranking: [],
|
|
96
|
+
answers: {},
|
|
97
|
+
segments: [],
|
|
98
|
+
});
|
|
99
|
+
if (filtered.toolOutput != null) {
|
|
100
|
+
filtered.toolOutput = {
|
|
101
|
+
...filtered.toolOutput,
|
|
102
|
+
output: undefined,
|
|
103
|
+
error: undefined,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Raw provider payloads and errors can contain either inputs or outputs.
|
|
109
|
+
replace({
|
|
110
|
+
providerMetadata: undefined,
|
|
111
|
+
error:
|
|
112
|
+
filtered.error == null
|
|
113
|
+
? filtered.error
|
|
114
|
+
: new Error('AI SDK operation failed'),
|
|
115
|
+
});
|
|
116
|
+
for (const key of ['toolCall', 'toolOutput'] as const) {
|
|
117
|
+
const toolEvent = filtered[key];
|
|
118
|
+
if (toolEvent != null && typeof toolEvent === 'object') {
|
|
119
|
+
filtered[key] = {
|
|
120
|
+
...toolEvent,
|
|
121
|
+
providerMetadata: undefined,
|
|
122
|
+
...('error' in toolEvent
|
|
123
|
+
? {
|
|
124
|
+
error:
|
|
125
|
+
toolEvent.error == null
|
|
126
|
+
? toolEvent.error
|
|
127
|
+
: new Error('AI SDK operation failed'),
|
|
128
|
+
}
|
|
129
|
+
: {}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for (const key of ['request', 'response'] as const) {
|
|
134
|
+
const filterMetadata = (metadata: unknown) => {
|
|
135
|
+
if (metadata == null || typeof metadata !== 'object') {
|
|
136
|
+
return metadata;
|
|
137
|
+
}
|
|
138
|
+
const filteredMetadata: Record<string, unknown> = { ...metadata };
|
|
139
|
+
delete filteredMetadata.body;
|
|
140
|
+
if (key === 'request' ? hideInputs : hideOutputs) {
|
|
141
|
+
if ('messages' in metadata) filteredMetadata.messages = [];
|
|
142
|
+
if ('instructions' in metadata)
|
|
143
|
+
filteredMetadata.instructions = undefined;
|
|
144
|
+
}
|
|
145
|
+
return filteredMetadata;
|
|
146
|
+
};
|
|
147
|
+
const metadata = filtered[key];
|
|
148
|
+
if (metadata != null) {
|
|
149
|
+
filtered[key] = Array.isArray(metadata)
|
|
150
|
+
? metadata.map(filterMetadata)
|
|
151
|
+
: filterMetadata(metadata);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (Array.isArray(filtered.steps)) {
|
|
156
|
+
filtered.steps = filtered.steps.map(step =>
|
|
157
|
+
filterTelemetryEvent(step, telemetry),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (Object.hasOwn(event, 'finalStep')) {
|
|
161
|
+
filtered.finalStep = filterTelemetryEvent(filtered.finalStep, telemetry);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return filtered as EVENT;
|
|
165
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LanguageModelV4GenerateResult } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
/** Remove credentials, paths, queries, and fragments before emitting telemetry. */
|
|
4
|
+
export function getRequestOrigin(url: string | undefined): string | undefined {
|
|
5
|
+
if (url == null) return undefined;
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const endpoint = new URL(url);
|
|
9
|
+
return endpoint.protocol === 'http:' || endpoint.protocol === 'https:'
|
|
10
|
+
? endpoint.origin
|
|
11
|
+
: undefined;
|
|
12
|
+
} catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Preserve request metadata while keeping unknown or sensitive URL fields out of results. */
|
|
18
|
+
export function sanitizeRequestMetadata(
|
|
19
|
+
request: LanguageModelV4GenerateResult['request'],
|
|
20
|
+
): LanguageModelV4GenerateResult['request'] {
|
|
21
|
+
if (request == null || !('url' in request)) return request;
|
|
22
|
+
const { url, ...rest } = request;
|
|
23
|
+
const origin = getRequestOrigin(url);
|
|
24
|
+
return { ...rest, ...(origin != null ? { url: origin } : {}) };
|
|
25
|
+
}
|
|
@@ -66,6 +66,7 @@ function loadBuiltinModule<T>(id: string): T | undefined {
|
|
|
66
66
|
export async function runWithTracingChannelSpan<T>(
|
|
67
67
|
message: TelemetryTracingChannelMessage,
|
|
68
68
|
execute: () => PromiseLike<T>,
|
|
69
|
+
recordResult = true,
|
|
69
70
|
): Promise<T> {
|
|
70
71
|
const diagnosticsChannel = await loadDiagnosticsChannel();
|
|
71
72
|
const tracingChannel = diagnosticsChannel?.tracingChannel?.(
|
|
@@ -102,11 +103,21 @@ export async function runWithTracingChannelSpan<T>(
|
|
|
102
103
|
},
|
|
103
104
|
);
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
// tracePromise publishes its resolved value on the channel. Keep the
|
|
107
|
+
// application's result separately when content recording is disabled.
|
|
108
|
+
return recordResult
|
|
109
|
+
? executePromise
|
|
110
|
+
: executePromise.then(
|
|
111
|
+
() => undefined,
|
|
112
|
+
() => {
|
|
113
|
+
throw new Error('AI SDK operation failed');
|
|
114
|
+
},
|
|
115
|
+
);
|
|
106
116
|
};
|
|
107
117
|
|
|
108
118
|
try {
|
|
109
|
-
|
|
119
|
+
await tracingChannel.tracePromise(tracedExecute, message);
|
|
120
|
+
return executionResult as T;
|
|
110
121
|
} catch {
|
|
111
122
|
if (hasExecutionError) {
|
|
112
123
|
throw executionError;
|
|
@@ -131,9 +142,11 @@ export async function runWithTracingChannelSpan<T>(
|
|
|
131
142
|
export function openTelemetryChannelSpanContext({
|
|
132
143
|
message,
|
|
133
144
|
completion,
|
|
145
|
+
recordResult = true,
|
|
134
146
|
}: {
|
|
135
147
|
message: TelemetryTracingChannelMessage;
|
|
136
148
|
completion: PromiseLike<unknown>;
|
|
149
|
+
recordResult?: boolean;
|
|
137
150
|
}): TracingChannelContext | undefined {
|
|
138
151
|
if (!isNodeRuntime()) {
|
|
139
152
|
Promise.resolve(completion).catch(() => {});
|
|
@@ -186,11 +199,13 @@ export function openTelemetryChannelSpanContext({
|
|
|
186
199
|
asyncEndPublished = true;
|
|
187
200
|
|
|
188
201
|
if (error !== undefined) {
|
|
189
|
-
context.error =
|
|
202
|
+
context.error = recordResult
|
|
203
|
+
? error
|
|
204
|
+
: new Error('AI SDK operation failed');
|
|
190
205
|
safePublish(() => tracingChannel.error.publish(context));
|
|
191
206
|
}
|
|
192
207
|
|
|
193
|
-
if (result !== undefined) {
|
|
208
|
+
if (recordResult && result !== undefined) {
|
|
194
209
|
context.result = result;
|
|
195
210
|
}
|
|
196
211
|
|
|
@@ -8,21 +8,25 @@ export class MockTranscriptionModelV4 implements TranscriptionModelV4 {
|
|
|
8
8
|
|
|
9
9
|
doGenerate: TranscriptionModelV4['doGenerate'];
|
|
10
10
|
doStream: TranscriptionModelV4['doStream'];
|
|
11
|
+
doGenerateFromUrl: TranscriptionModelV4['doGenerateFromUrl'];
|
|
11
12
|
|
|
12
13
|
constructor({
|
|
13
14
|
provider = 'mock-provider',
|
|
14
15
|
modelId = 'mock-model-id',
|
|
15
16
|
doGenerate = notImplemented,
|
|
16
17
|
doStream,
|
|
18
|
+
doGenerateFromUrl,
|
|
17
19
|
}: {
|
|
18
20
|
provider?: TranscriptionModelV4['provider'];
|
|
19
21
|
modelId?: TranscriptionModelV4['modelId'];
|
|
20
22
|
doGenerate?: TranscriptionModelV4['doGenerate'];
|
|
21
23
|
doStream?: TranscriptionModelV4['doStream'];
|
|
24
|
+
doGenerateFromUrl?: TranscriptionModelV4['doGenerateFromUrl'];
|
|
22
25
|
} = {}) {
|
|
23
26
|
this.provider = provider;
|
|
24
27
|
this.modelId = modelId;
|
|
25
28
|
this.doGenerate = doGenerate;
|
|
26
29
|
this.doStream = doStream;
|
|
30
|
+
this.doGenerateFromUrl = doGenerateFromUrl;
|
|
27
31
|
}
|
|
28
32
|
}
|
|
@@ -15,6 +15,7 @@ export interface TranscriptionResult {
|
|
|
15
15
|
/**
|
|
16
16
|
* Array of transcript segments with timing information.
|
|
17
17
|
* Each segment represents a portion of the transcribed text with start and end times.
|
|
18
|
+
* Segments retain the provider's behavior and may represent individual words.
|
|
18
19
|
*/
|
|
19
20
|
readonly segments: Array<{
|
|
20
21
|
/**
|
|
@@ -31,6 +32,25 @@ export interface TranscriptionResult {
|
|
|
31
32
|
readonly endSecond: number;
|
|
32
33
|
}>;
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Word-level timings returned by the provider, whether explicitly requested
|
|
37
|
+
* or returned by default. Undefined when the provider does not return them.
|
|
38
|
+
*/
|
|
39
|
+
readonly words?: Array<{
|
|
40
|
+
/**
|
|
41
|
+
* The text of this word.
|
|
42
|
+
*/
|
|
43
|
+
readonly text: string;
|
|
44
|
+
/**
|
|
45
|
+
* The start time of this word in seconds.
|
|
46
|
+
*/
|
|
47
|
+
readonly startSecond: number;
|
|
48
|
+
/**
|
|
49
|
+
* The end time of this word in seconds.
|
|
50
|
+
*/
|
|
51
|
+
readonly endSecond: number;
|
|
52
|
+
}>;
|
|
53
|
+
|
|
34
54
|
/**
|
|
35
55
|
* The detected language of the audio content, as an ISO-639-1 code (e.g., 'en' for English).
|
|
36
56
|
* May be undefined if the language couldn't be detected.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
JSONObject,
|
|
3
|
+
TranscriptionModelV4TimestampGranularity,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
2
5
|
import {
|
|
3
6
|
createIdGenerator,
|
|
4
7
|
detectMediaType,
|
|
8
|
+
getTopLevelMediaType,
|
|
9
|
+
isFullMediaType,
|
|
5
10
|
withUserAgentSuffix,
|
|
6
11
|
type ProviderOptions,
|
|
7
12
|
} from '@ai-sdk/provider-utils';
|
|
@@ -49,6 +54,7 @@ export async function transcribe({
|
|
|
49
54
|
model,
|
|
50
55
|
audio,
|
|
51
56
|
providerOptions = {},
|
|
57
|
+
timestampGranularities,
|
|
52
58
|
maxRetries: maxRetriesArg,
|
|
53
59
|
abortSignal,
|
|
54
60
|
headers,
|
|
@@ -82,6 +88,16 @@ export async function transcribe({
|
|
|
82
88
|
*/
|
|
83
89
|
providerOptions?: ProviderOptions;
|
|
84
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Word and segment timings to request.
|
|
93
|
+
*
|
|
94
|
+
* Supported adapters map this onto the provider's native control when the
|
|
95
|
+
* matching provider option is omitted. An explicit provider option wins.
|
|
96
|
+
* Supported adapters warn when they cannot return a requested granularity.
|
|
97
|
+
* An empty array requests no shared timing granularity; provider defaults may vary.
|
|
98
|
+
*/
|
|
99
|
+
timestampGranularities?: Array<TranscriptionModelV4TimestampGranularity>;
|
|
100
|
+
|
|
85
101
|
/**
|
|
86
102
|
* Maximum number of retries per transcript model call. Set to 0 to disable retries.
|
|
87
103
|
*
|
|
@@ -107,6 +123,8 @@ export async function transcribe({
|
|
|
107
123
|
|
|
108
124
|
/**
|
|
109
125
|
* Custom download function for fetching audio from URLs.
|
|
126
|
+
* Providing this option forces local downloading even when the provider
|
|
127
|
+
* supports native URL transcription.
|
|
110
128
|
* Use `createDownload()` from `ai` to create a download function with custom size limits.
|
|
111
129
|
*
|
|
112
130
|
* @default createDownload() (2 GiB limit)
|
|
@@ -164,6 +182,7 @@ export async function transcribe({
|
|
|
164
182
|
maxRetries,
|
|
165
183
|
headers,
|
|
166
184
|
providerOptions,
|
|
185
|
+
timestampGranularities,
|
|
167
186
|
});
|
|
168
187
|
|
|
169
188
|
const executeWithStartEvent = async <T>({
|
|
@@ -188,14 +207,22 @@ export async function transcribe({
|
|
|
188
207
|
|
|
189
208
|
let preparationError: unknown;
|
|
190
209
|
let preparationFailed = false;
|
|
191
|
-
let audioData: Uint8Array;
|
|
210
|
+
let audioData: Uint8Array | URL;
|
|
192
211
|
let downloadedMediaType: string | undefined;
|
|
193
212
|
|
|
194
213
|
try {
|
|
195
214
|
if (audio instanceof URL) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
215
|
+
if (
|
|
216
|
+
resolvedModel.doGenerateFromUrl != null &&
|
|
217
|
+
downloadFn === defaultDownload &&
|
|
218
|
+
(audio.protocol === 'https:' || audio.protocol === 'http:')
|
|
219
|
+
) {
|
|
220
|
+
audioData = audio;
|
|
221
|
+
} else {
|
|
222
|
+
const downloadResult = await downloadFn({ url: audio, abortSignal });
|
|
223
|
+
audioData = downloadResult.data;
|
|
224
|
+
downloadedMediaType = downloadResult.mediaType;
|
|
225
|
+
}
|
|
199
226
|
} else {
|
|
200
227
|
audioData = convertDataContentToUint8Array(audio);
|
|
201
228
|
}
|
|
@@ -224,15 +251,18 @@ export async function transcribe({
|
|
|
224
251
|
}
|
|
225
252
|
}
|
|
226
253
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
254
|
+
let mediaType =
|
|
255
|
+
audioData instanceof URL
|
|
256
|
+
? undefined
|
|
257
|
+
: (getAudioMediaType(downloadedMediaType) ??
|
|
258
|
+
detectMediaType({
|
|
259
|
+
data: audioData,
|
|
260
|
+
topLevelType: 'audio',
|
|
261
|
+
}) ??
|
|
262
|
+
'audio/wav');
|
|
263
|
+
let byteLength = audioData instanceof URL ? undefined : audioData.byteLength;
|
|
234
264
|
const startEvent = createStartEvent({
|
|
235
|
-
byteLength
|
|
265
|
+
byteLength,
|
|
236
266
|
mediaType,
|
|
237
267
|
});
|
|
238
268
|
|
|
@@ -240,15 +270,41 @@ export async function transcribe({
|
|
|
240
270
|
return await executeWithStartEvent({
|
|
241
271
|
startEvent,
|
|
242
272
|
execute: async () => {
|
|
243
|
-
const result = await retry(() =>
|
|
244
|
-
|
|
245
|
-
audio: audioData,
|
|
273
|
+
const result = await retry(async () => {
|
|
274
|
+
const callOptions = {
|
|
246
275
|
abortSignal,
|
|
247
276
|
headers: headersWithUserAgent,
|
|
248
277
|
providerOptions,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
278
|
+
...(timestampGranularities != null
|
|
279
|
+
? { timestampGranularities }
|
|
280
|
+
: {}),
|
|
281
|
+
};
|
|
282
|
+
if (audioData instanceof URL) {
|
|
283
|
+
const nativeResult = await resolvedModel.doGenerateFromUrl?.({
|
|
284
|
+
...callOptions,
|
|
285
|
+
audio: audioData,
|
|
286
|
+
});
|
|
287
|
+
if (nativeResult != null) return nativeResult;
|
|
288
|
+
|
|
289
|
+
// A capable provider can decline an unsupported URL (for example,
|
|
290
|
+
// a redirect). Keep the existing secure download and file path.
|
|
291
|
+
const downloaded = await downloadFn({
|
|
292
|
+
url: audioData,
|
|
293
|
+
abortSignal,
|
|
294
|
+
});
|
|
295
|
+
audioData = downloaded.data;
|
|
296
|
+
mediaType =
|
|
297
|
+
getAudioMediaType(downloaded.mediaType) ??
|
|
298
|
+
detectMediaType({ data: audioData, topLevelType: 'audio' }) ??
|
|
299
|
+
'audio/wav';
|
|
300
|
+
byteLength = audioData.byteLength;
|
|
301
|
+
}
|
|
302
|
+
return resolvedModel.doGenerate({
|
|
303
|
+
...callOptions,
|
|
304
|
+
audio: audioData,
|
|
305
|
+
mediaType: mediaType ?? 'audio/wav',
|
|
306
|
+
});
|
|
307
|
+
});
|
|
252
308
|
|
|
253
309
|
logWarnings({
|
|
254
310
|
warnings: result.warnings,
|
|
@@ -268,11 +324,12 @@ export async function transcribe({
|
|
|
268
324
|
provider: resolvedModel.provider,
|
|
269
325
|
modelId: resolvedModel.modelId,
|
|
270
326
|
audio: {
|
|
271
|
-
byteLength
|
|
327
|
+
byteLength,
|
|
272
328
|
mediaType,
|
|
273
329
|
},
|
|
274
330
|
text: result.text,
|
|
275
331
|
segments: result.segments,
|
|
332
|
+
words: result.words,
|
|
276
333
|
language: result.language,
|
|
277
334
|
durationInSeconds: result.durationInSeconds,
|
|
278
335
|
usage: result.usage,
|
|
@@ -289,6 +346,7 @@ export async function transcribe({
|
|
|
289
346
|
return new DefaultTranscriptionResult({
|
|
290
347
|
text: result.text,
|
|
291
348
|
segments: result.segments,
|
|
349
|
+
words: result.words,
|
|
292
350
|
language: result.language,
|
|
293
351
|
durationInSeconds: result.durationInSeconds,
|
|
294
352
|
warnings: result.warnings,
|
|
@@ -303,6 +361,24 @@ export async function transcribe({
|
|
|
303
361
|
}
|
|
304
362
|
}
|
|
305
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Normalizes a downloaded `Content-Type` header value and returns it only
|
|
366
|
+
* when it is a full `audio/<subtype>` media type. Generic or mislabeled types
|
|
367
|
+
* (e.g. `application/octet-stream`, `binary/octet-stream`, `text/plain`)
|
|
368
|
+
* return `undefined` so that the media type is detected from the audio bytes
|
|
369
|
+
* instead.
|
|
370
|
+
*/
|
|
371
|
+
function getAudioMediaType(
|
|
372
|
+
contentType: string | undefined,
|
|
373
|
+
): string | undefined {
|
|
374
|
+
const mediaType = contentType?.split(';')[0].trim().toLowerCase();
|
|
375
|
+
return mediaType != null &&
|
|
376
|
+
getTopLevelMediaType(mediaType) === 'audio' &&
|
|
377
|
+
isFullMediaType(mediaType)
|
|
378
|
+
? mediaType
|
|
379
|
+
: undefined;
|
|
380
|
+
}
|
|
381
|
+
|
|
306
382
|
class DefaultTranscriptionResult implements TranscriptionResult {
|
|
307
383
|
readonly text: string;
|
|
308
384
|
readonly segments: Array<{
|
|
@@ -310,6 +386,11 @@ class DefaultTranscriptionResult implements TranscriptionResult {
|
|
|
310
386
|
startSecond: number;
|
|
311
387
|
endSecond: number;
|
|
312
388
|
}>;
|
|
389
|
+
readonly words?: Array<{
|
|
390
|
+
text: string;
|
|
391
|
+
startSecond: number;
|
|
392
|
+
endSecond: number;
|
|
393
|
+
}>;
|
|
313
394
|
readonly language: string | undefined;
|
|
314
395
|
readonly durationInSeconds: number | undefined;
|
|
315
396
|
readonly warnings: Array<Warning>;
|
|
@@ -323,6 +404,11 @@ class DefaultTranscriptionResult implements TranscriptionResult {
|
|
|
323
404
|
startSecond: number;
|
|
324
405
|
endSecond: number;
|
|
325
406
|
}>;
|
|
407
|
+
words?: Array<{
|
|
408
|
+
text: string;
|
|
409
|
+
startSecond: number;
|
|
410
|
+
endSecond: number;
|
|
411
|
+
}>;
|
|
326
412
|
language: string | undefined;
|
|
327
413
|
durationInSeconds: number | undefined;
|
|
328
414
|
warnings: Array<Warning>;
|
|
@@ -331,6 +417,7 @@ class DefaultTranscriptionResult implements TranscriptionResult {
|
|
|
331
417
|
}) {
|
|
332
418
|
this.text = options.text;
|
|
333
419
|
this.segments = options.segments;
|
|
420
|
+
this.words = options.words;
|
|
334
421
|
this.language = options.language;
|
|
335
422
|
this.durationInSeconds = options.durationInSeconds;
|
|
336
423
|
this.warnings = options.warnings;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
JSONObject,
|
|
3
|
+
SharedV4AudioFormat,
|
|
4
|
+
TranscriptionModelV4TimestampGranularity,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
2
6
|
import type { ProviderOptions } from '@ai-sdk/provider-utils';
|
|
3
7
|
import type { ProviderMetadata } from '../types';
|
|
4
8
|
import type { TranscriptionModelResponseMetadata } from '../types/transcription-model-response-metadata';
|
|
@@ -21,8 +25,9 @@ type TranscriptionStartEventFor<OPERATION_ID extends string> = {
|
|
|
21
25
|
readonly modelId: string;
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
|
-
* Metadata about the input audio.
|
|
25
|
-
*
|
|
28
|
+
* Metadata about the input audio. Native URL transcription does not inspect
|
|
29
|
+
* the audio, so its byte length and media type are unknown. Streaming byte
|
|
30
|
+
* length is reported on the end event after consuming the stream.
|
|
26
31
|
*/
|
|
27
32
|
readonly audio: {
|
|
28
33
|
readonly byteLength: number | undefined;
|
|
@@ -47,7 +52,10 @@ type TranscriptionStartEventFor<OPERATION_ID extends string> = {
|
|
|
47
52
|
};
|
|
48
53
|
|
|
49
54
|
export type TranscriptionStartEvent =
|
|
50
|
-
TranscriptionStartEventFor<'ai.transcribe'
|
|
55
|
+
TranscriptionStartEventFor<'ai.transcribe'> & {
|
|
56
|
+
/** Requested shared timing granularities, when specified. */
|
|
57
|
+
readonly timestampGranularities?: Array<TranscriptionModelV4TimestampGranularity>;
|
|
58
|
+
};
|
|
51
59
|
|
|
52
60
|
export type StreamTranscriptionStartEvent =
|
|
53
61
|
TranscriptionStartEventFor<'ai.streamTranscribe'>;
|
|
@@ -72,14 +80,18 @@ type TranscriptionEndEventFor<OPERATION_ID extends string> = {
|
|
|
72
80
|
|
|
73
81
|
/** Metadata about the input audio. */
|
|
74
82
|
readonly audio: {
|
|
75
|
-
readonly byteLength:
|
|
76
|
-
|
|
83
|
+
readonly byteLength: OPERATION_ID extends 'ai.transcribe'
|
|
84
|
+
? number | undefined
|
|
85
|
+
: number;
|
|
86
|
+
readonly mediaType: OPERATION_ID extends 'ai.transcribe'
|
|
87
|
+
? string | undefined
|
|
88
|
+
: string;
|
|
77
89
|
};
|
|
78
90
|
|
|
79
91
|
/** The complete transcript. */
|
|
80
92
|
readonly text: string;
|
|
81
93
|
|
|
82
|
-
/** Transcript segments with timing information. */
|
|
94
|
+
/** Transcript segments with timing information, retaining provider behavior. */
|
|
83
95
|
readonly segments: Array<{
|
|
84
96
|
readonly text: string;
|
|
85
97
|
readonly startSecond: number;
|
|
@@ -105,7 +117,15 @@ type TranscriptionEndEventFor<OPERATION_ID extends string> = {
|
|
|
105
117
|
readonly response: TranscriptionModelResponseMetadata;
|
|
106
118
|
};
|
|
107
119
|
|
|
108
|
-
export type TranscriptionEndEvent =
|
|
120
|
+
export type TranscriptionEndEvent =
|
|
121
|
+
TranscriptionEndEventFor<'ai.transcribe'> & {
|
|
122
|
+
/** Word timings returned by the provider, whether requested or returned by default. */
|
|
123
|
+
readonly words?: Array<{
|
|
124
|
+
readonly text: string;
|
|
125
|
+
readonly startSecond: number;
|
|
126
|
+
readonly endSecond: number;
|
|
127
|
+
}>;
|
|
128
|
+
};
|
|
109
129
|
|
|
110
130
|
export type StreamTranscriptionEndEvent =
|
|
111
131
|
TranscriptionEndEventFor<'ai.streamTranscribe'>;
|
|
@@ -4,6 +4,9 @@ import type { ModelMessage } from '@ai-sdk/provider-utils';
|
|
|
4
4
|
* Metadata for a language model request.
|
|
5
5
|
*/
|
|
6
6
|
export type LanguageModelRequestMetadata = {
|
|
7
|
+
/** HTTP origin used for this request, without URL credentials, paths, queries or fragments. */
|
|
8
|
+
readonly url?: string;
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* The input messages that were sent to the model for this step.
|
|
9
12
|
*/
|
|
@@ -4,9 +4,12 @@ import type {
|
|
|
4
4
|
Experimental_RealtimeFactoryV4GetTokenResult as RealtimeFactoryV4GetTokenResult,
|
|
5
5
|
Experimental_RealtimeModelV4 as RealtimeModelV4,
|
|
6
6
|
Experimental_RealtimeModelV4ClientEvent as RealtimeModelV4ClientEvent,
|
|
7
|
+
Experimental_RealtimeModelV4ServerConnection as RealtimeModelV4ServerConnection,
|
|
7
8
|
Experimental_RealtimeModelV4ServerEvent as RealtimeModelV4ServerEvent,
|
|
8
9
|
Experimental_RealtimeModelV4SessionConfig as RealtimeModelV4SessionConfig,
|
|
10
|
+
Experimental_RealtimeModelV4SessionIntent as RealtimeModelV4SessionIntent,
|
|
9
11
|
Experimental_RealtimeModelV4ToolDefinition as RealtimeModelV4ToolDefinition,
|
|
12
|
+
Experimental_RealtimeModelV4Usage as RealtimeModelV4Usage,
|
|
10
13
|
} from '@ai-sdk/provider';
|
|
11
14
|
|
|
12
15
|
export type RealtimeFactory = RealtimeFactoryV4;
|
|
@@ -19,8 +22,14 @@ export type RealtimeModel = RealtimeModelV4;
|
|
|
19
22
|
|
|
20
23
|
export type RealtimeClientEvent = RealtimeModelV4ClientEvent;
|
|
21
24
|
|
|
25
|
+
export type RealtimeServerConnection = RealtimeModelV4ServerConnection;
|
|
26
|
+
|
|
22
27
|
export type RealtimeServerEvent = RealtimeModelV4ServerEvent;
|
|
23
28
|
|
|
24
29
|
export type RealtimeSessionConfig = RealtimeModelV4SessionConfig;
|
|
25
30
|
|
|
31
|
+
export type RealtimeSessionIntent = RealtimeModelV4SessionIntent;
|
|
32
|
+
|
|
26
33
|
export type RealtimeToolDefinition = RealtimeModelV4ToolDefinition;
|
|
34
|
+
|
|
35
|
+
export type RealtimeUsage = RealtimeModelV4Usage;
|