ai 7.0.74 → 7.0.76
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 +15 -0
- package/dist/index.d.ts +115 -3
- package/dist/index.js +265 -77
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +1 -1
- package/docs/03-ai-sdk-core/60-telemetry.mdx +6 -4
- package/package.json +2 -2
- package/src/generate-text/stream-text.ts +78 -6
- package/src/generate-video/generate-video.ts +95 -53
- package/src/generate-video/get-video-status.ts +66 -0
- package/src/generate-video/index.ts +4 -0
- package/src/generate-video/start-video.ts +214 -0
- package/src/ui/chat.ts +2 -2
- package/src/ui/process-ui-message-stream.ts +1 -1
package/dist/internal/index.js
CHANGED
|
@@ -491,10 +491,9 @@ For `embed` and `embedMany`, the integration records spans with `CLIENT` kind:
|
|
|
491
491
|
- `gen_ai.provider.name`: the provider
|
|
492
492
|
- `gen_ai.request.model`: the requested model ID
|
|
493
493
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
- **`embeddings {modelId}`** (inner span, `embedMany` only): one span per provider batch call, nested under the root span.
|
|
494
|
+
- **`embeddings {modelId}`** (inner span): one span per provider request,
|
|
495
|
+
nested under the root span. `embed` creates one inner span. `embedMany`
|
|
496
|
+
creates one inner span per provider batch call.
|
|
498
497
|
|
|
499
498
|
Initial attributes:
|
|
500
499
|
- `gen_ai.operation.name`: `"embeddings"`
|
|
@@ -504,6 +503,9 @@ For `embed` and `embedMany`, the integration records spans with `CLIENT` kind:
|
|
|
504
503
|
Attributes set on finish:
|
|
505
504
|
- `gen_ai.usage.input_tokens`: the number of tokens used
|
|
506
505
|
|
|
506
|
+
Usage is recorded only on inner provider-request spans. For `embedMany`, sum
|
|
507
|
+
the usage from the inner spans to get the total usage for the operation.
|
|
508
|
+
|
|
507
509
|
#### rerank
|
|
508
510
|
|
|
509
511
|
For `rerank`, the integration records spans with `CLIENT` kind:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.76",
|
|
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,7 +42,7 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.61",
|
|
46
46
|
"@ai-sdk/provider": "4.0.7",
|
|
47
47
|
"@ai-sdk/provider-utils": "5.0.28"
|
|
48
48
|
},
|
|
@@ -1160,6 +1160,35 @@ class DefaultStreamTextResult<
|
|
|
1160
1160
|
let stepMessagesForNextStep: Array<ModelMessage> | undefined;
|
|
1161
1161
|
let currentStepMessages: Array<ModelMessage> = [];
|
|
1162
1162
|
|
|
1163
|
+
// provider-assigned text/reasoning part IDs are only unique within a
|
|
1164
|
+
// single model call (e.g. Anthropic uses the content block index, which
|
|
1165
|
+
// restarts at 0 for every call), so colliding IDs are remapped to keep
|
|
1166
|
+
// them unique across the whole multi-step stream:
|
|
1167
|
+
const createPartIdReserver = () => {
|
|
1168
|
+
const usedIds = new Set<string>();
|
|
1169
|
+
|
|
1170
|
+
return (id: string) => {
|
|
1171
|
+
if (!usedIds.has(id)) {
|
|
1172
|
+
usedIds.add(id);
|
|
1173
|
+
return id;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
const generatedId = generateId();
|
|
1177
|
+
let uniqueId = generatedId;
|
|
1178
|
+
let suffix = 0;
|
|
1179
|
+
|
|
1180
|
+
while (usedIds.has(uniqueId)) {
|
|
1181
|
+
uniqueId = `${generatedId}-${++suffix}`;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
usedIds.add(uniqueId);
|
|
1185
|
+
return uniqueId;
|
|
1186
|
+
};
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
const reserveTextPartId = createPartIdReserver();
|
|
1190
|
+
const reserveReasoningPartId = createPartIdReserver();
|
|
1191
|
+
|
|
1163
1192
|
// Track provider-executed tool calls that support deferred results
|
|
1164
1193
|
// (e.g., code_execution in programmatic tool calling scenarios).
|
|
1165
1194
|
// These tools may not return their results in the same turn as their call.
|
|
@@ -2189,6 +2218,11 @@ class DefaultStreamTextResult<
|
|
|
2189
2218
|
modelId: model.modelId,
|
|
2190
2219
|
};
|
|
2191
2220
|
|
|
2221
|
+
// maps provider-assigned IDs to stream-unique IDs for the text and
|
|
2222
|
+
// reasoning parts that are active in this step
|
|
2223
|
+
const textPartIds = new Map<string, string>();
|
|
2224
|
+
const reasoningPartIds = new Map<string, string>();
|
|
2225
|
+
|
|
2192
2226
|
self.addStream(
|
|
2193
2227
|
streamWithToolResults.pipeThrough(
|
|
2194
2228
|
new TransformStream<
|
|
@@ -2228,11 +2262,6 @@ class DefaultStreamTextResult<
|
|
|
2228
2262
|
case 'file':
|
|
2229
2263
|
case 'custom':
|
|
2230
2264
|
case 'source':
|
|
2231
|
-
case 'text-start':
|
|
2232
|
-
case 'text-end':
|
|
2233
|
-
case 'reasoning-start':
|
|
2234
|
-
case 'reasoning-end':
|
|
2235
|
-
case 'reasoning-delta':
|
|
2236
2265
|
case 'reasoning-file':
|
|
2237
2266
|
case 'tool-input-start':
|
|
2238
2267
|
case 'tool-input-end':
|
|
@@ -2242,16 +2271,59 @@ class DefaultStreamTextResult<
|
|
|
2242
2271
|
break;
|
|
2243
2272
|
}
|
|
2244
2273
|
|
|
2274
|
+
case 'text-start': {
|
|
2275
|
+
const id = reserveTextPartId(chunk.id);
|
|
2276
|
+
textPartIds.set(chunk.id, id);
|
|
2277
|
+
controller.enqueue({ ...chunk, id });
|
|
2278
|
+
break;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2245
2281
|
case 'text-delta': {
|
|
2246
2282
|
if (
|
|
2247
2283
|
chunk.text.length > 0 ||
|
|
2248
2284
|
chunk.providerMetadata != null
|
|
2249
2285
|
) {
|
|
2250
|
-
controller.enqueue(
|
|
2286
|
+
controller.enqueue({
|
|
2287
|
+
...chunk,
|
|
2288
|
+
id: textPartIds.get(chunk.id) ?? chunk.id,
|
|
2289
|
+
});
|
|
2251
2290
|
}
|
|
2252
2291
|
break;
|
|
2253
2292
|
}
|
|
2254
2293
|
|
|
2294
|
+
case 'text-end': {
|
|
2295
|
+
controller.enqueue({
|
|
2296
|
+
...chunk,
|
|
2297
|
+
id: textPartIds.get(chunk.id) ?? chunk.id,
|
|
2298
|
+
});
|
|
2299
|
+
textPartIds.delete(chunk.id);
|
|
2300
|
+
break;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
case 'reasoning-start': {
|
|
2304
|
+
const id = reserveReasoningPartId(chunk.id);
|
|
2305
|
+
reasoningPartIds.set(chunk.id, id);
|
|
2306
|
+
controller.enqueue({ ...chunk, id });
|
|
2307
|
+
break;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
case 'reasoning-delta': {
|
|
2311
|
+
controller.enqueue({
|
|
2312
|
+
...chunk,
|
|
2313
|
+
id: reasoningPartIds.get(chunk.id) ?? chunk.id,
|
|
2314
|
+
});
|
|
2315
|
+
break;
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
case 'reasoning-end': {
|
|
2319
|
+
controller.enqueue({
|
|
2320
|
+
...chunk,
|
|
2321
|
+
id: reasoningPartIds.get(chunk.id) ?? chunk.id,
|
|
2322
|
+
});
|
|
2323
|
+
reasoningPartIds.delete(chunk.id);
|
|
2324
|
+
break;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2255
2327
|
case 'tool-call': {
|
|
2256
2328
|
controller.enqueue(chunk);
|
|
2257
2329
|
// store tool calls for onEnd callback and toolCalls promise:
|
|
@@ -292,59 +292,13 @@ export async function experimental_generateVideo({
|
|
|
292
292
|
abortSignal,
|
|
293
293
|
});
|
|
294
294
|
|
|
295
|
-
const {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
? [{ image: normalizedImage, frameType: frame.frameType }]
|
|
303
|
-
: [];
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
const normalizedInputReferences:
|
|
307
|
-
| Array<Experimental_VideoModelV4File>
|
|
308
|
-
| undefined = inputReferences?.flatMap(reference => {
|
|
309
|
-
const normalized = normalizeReferenceData(reference);
|
|
310
|
-
return normalized != null ? [normalized] : [];
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
const effectiveInputReferences =
|
|
314
|
-
normalizedFrameImages != null && normalizedFrameImages.length > 0
|
|
315
|
-
? undefined
|
|
316
|
-
: normalizedInputReferences;
|
|
317
|
-
|
|
318
|
-
const warnings: Array<Warning> = [];
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
normalizedFrameImages != null &&
|
|
322
|
-
normalizedFrameImages.length > 0 &&
|
|
323
|
-
normalizedInputReferences != null &&
|
|
324
|
-
normalizedInputReferences.length > 0
|
|
325
|
-
) {
|
|
326
|
-
warnings.push({
|
|
327
|
-
type: 'other',
|
|
328
|
-
message:
|
|
329
|
-
'inputReferences were ignored because frameImages were provided; ' +
|
|
330
|
-
'frameImages and inputReferences cannot be combined.',
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const firstFrameImage = normalizedFrameImages?.find(
|
|
335
|
-
frame => frame.frameType === 'first_frame',
|
|
336
|
-
)?.image;
|
|
337
|
-
|
|
338
|
-
if (image != null && firstFrameImage != null) {
|
|
339
|
-
warnings.push({
|
|
340
|
-
type: 'other',
|
|
341
|
-
message:
|
|
342
|
-
'prompt.image was ignored because a first_frame frameImage was provided; ' +
|
|
343
|
-
'the first_frame frameImage takes precedence as the start image.',
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const resolvedImage = firstFrameImage ?? image;
|
|
295
|
+
const {
|
|
296
|
+
prompt,
|
|
297
|
+
resolvedImage,
|
|
298
|
+
normalizedFrameImages,
|
|
299
|
+
effectiveInputReferences,
|
|
300
|
+
warnings,
|
|
301
|
+
} = normalizeVideoCallInputs({ promptArg, frameImages, inputReferences });
|
|
348
302
|
|
|
349
303
|
const maxVideosPerCallWithDefault =
|
|
350
304
|
maxVideosPerCall ?? (await invokeModelMaxVideosPerCall(model)) ?? 1;
|
|
@@ -737,6 +691,94 @@ function normalizePrompt(promptArg: GenerateVideoPrompt): {
|
|
|
737
691
|
};
|
|
738
692
|
}
|
|
739
693
|
|
|
694
|
+
/**
|
|
695
|
+
* Shared input normalization for `experimental_generateVideo` and
|
|
696
|
+
* `experimental_startVideo`: prompt/image plus the frameImages /
|
|
697
|
+
* inputReferences precedence rules and their warnings.
|
|
698
|
+
*/
|
|
699
|
+
export function normalizeVideoCallInputs({
|
|
700
|
+
promptArg,
|
|
701
|
+
frameImages,
|
|
702
|
+
inputReferences,
|
|
703
|
+
}: {
|
|
704
|
+
promptArg: GenerateVideoPrompt;
|
|
705
|
+
frameImages?: Array<{
|
|
706
|
+
image: DataContent;
|
|
707
|
+
frameType: Experimental_VideoModelV4FrameType;
|
|
708
|
+
}>;
|
|
709
|
+
inputReferences?: Array<
|
|
710
|
+
DataContent | { data: DataContent; mediaType?: string }
|
|
711
|
+
>;
|
|
712
|
+
}): {
|
|
713
|
+
prompt: string | undefined;
|
|
714
|
+
resolvedImage: Experimental_VideoModelV4File | undefined;
|
|
715
|
+
normalizedFrameImages: Array<Experimental_VideoModelV4FrameImage> | undefined;
|
|
716
|
+
effectiveInputReferences: Array<Experimental_VideoModelV4File> | undefined;
|
|
717
|
+
warnings: Array<Warning>;
|
|
718
|
+
} {
|
|
719
|
+
const { prompt, image } = normalizePrompt(promptArg);
|
|
720
|
+
|
|
721
|
+
const normalizedFrameImages:
|
|
722
|
+
| Array<Experimental_VideoModelV4FrameImage>
|
|
723
|
+
| undefined = frameImages?.flatMap(frame => {
|
|
724
|
+
const normalizedImage = normalizeImageData(frame.image);
|
|
725
|
+
return normalizedImage != null
|
|
726
|
+
? [{ image: normalizedImage, frameType: frame.frameType }]
|
|
727
|
+
: [];
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
const normalizedInputReferences:
|
|
731
|
+
| Array<Experimental_VideoModelV4File>
|
|
732
|
+
| undefined = inputReferences?.flatMap(reference => {
|
|
733
|
+
const normalized = normalizeReferenceData(reference);
|
|
734
|
+
return normalized != null ? [normalized] : [];
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
const effectiveInputReferences =
|
|
738
|
+
normalizedFrameImages != null && normalizedFrameImages.length > 0
|
|
739
|
+
? undefined
|
|
740
|
+
: normalizedInputReferences;
|
|
741
|
+
|
|
742
|
+
const warnings: Array<Warning> = [];
|
|
743
|
+
|
|
744
|
+
if (
|
|
745
|
+
normalizedFrameImages != null &&
|
|
746
|
+
normalizedFrameImages.length > 0 &&
|
|
747
|
+
normalizedInputReferences != null &&
|
|
748
|
+
normalizedInputReferences.length > 0
|
|
749
|
+
) {
|
|
750
|
+
warnings.push({
|
|
751
|
+
type: 'other',
|
|
752
|
+
message:
|
|
753
|
+
'inputReferences were ignored because frameImages were provided; ' +
|
|
754
|
+
'frameImages and inputReferences cannot be combined.',
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const firstFrameImage = normalizedFrameImages?.find(
|
|
759
|
+
frame => frame.frameType === 'first_frame',
|
|
760
|
+
)?.image;
|
|
761
|
+
|
|
762
|
+
if (image != null && firstFrameImage != null) {
|
|
763
|
+
warnings.push({
|
|
764
|
+
type: 'other',
|
|
765
|
+
message:
|
|
766
|
+
'prompt.image was ignored because a first_frame frameImage was provided; ' +
|
|
767
|
+
'the first_frame frameImage takes precedence as the start image.',
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const resolvedImage = firstFrameImage ?? image;
|
|
772
|
+
|
|
773
|
+
return {
|
|
774
|
+
prompt,
|
|
775
|
+
resolvedImage,
|
|
776
|
+
normalizedFrameImages,
|
|
777
|
+
effectiveInputReferences,
|
|
778
|
+
warnings,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
740
782
|
function detectFileMediaType(
|
|
741
783
|
data: Uint8Array,
|
|
742
784
|
restrictToImages: boolean,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Experimental_VideoModelV4OperationStatusResult,
|
|
3
|
+
JSONValue,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import { withUserAgentSuffix } from '@ai-sdk/provider-utils';
|
|
6
|
+
import { resolveVideoModel } from '../model/resolve-model';
|
|
7
|
+
import type { VideoModel } from '../types/video-model';
|
|
8
|
+
import { prepareRetries } from '../util/prepare-retries';
|
|
9
|
+
import { VERSION } from '../version';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The result of an `experimental_getVideoStatus` call: the spec-level status
|
|
13
|
+
* payload, discriminated by `status` (`pending` | `completed` | `error`).
|
|
14
|
+
*/
|
|
15
|
+
export type GetVideoStatusResult =
|
|
16
|
+
Experimental_VideoModelV4OperationStatusResult;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Checks the status of an asynchronous video generation started with
|
|
20
|
+
* `experimental_startVideo`.
|
|
21
|
+
*
|
|
22
|
+
* A single check — no polling loop. Poll by calling this on your own
|
|
23
|
+
* schedule, or skip polling entirely when the start used `webhookUrl` and
|
|
24
|
+
* your receiver fetches the result after the terminal notification arrives.
|
|
25
|
+
*
|
|
26
|
+
* @param model - The video model the operation was started on.
|
|
27
|
+
* @param operation - The opaque reference returned by `experimental_startVideo`.
|
|
28
|
+
* @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
29
|
+
* @param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
30
|
+
* @param maxRetries - Maximum number of retries for the status call. Set to 0 to disable retries. Default: 2.
|
|
31
|
+
*/
|
|
32
|
+
export async function experimental_getVideoStatus(
|
|
33
|
+
modelArg: VideoModel,
|
|
34
|
+
{
|
|
35
|
+
operation,
|
|
36
|
+
headers,
|
|
37
|
+
abortSignal,
|
|
38
|
+
maxRetries: maxRetriesArg,
|
|
39
|
+
}: {
|
|
40
|
+
operation: JSONValue;
|
|
41
|
+
headers?: Record<string, string>;
|
|
42
|
+
abortSignal?: AbortSignal;
|
|
43
|
+
maxRetries?: number;
|
|
44
|
+
},
|
|
45
|
+
): Promise<GetVideoStatusResult> {
|
|
46
|
+
const model = resolveVideoModel(modelArg);
|
|
47
|
+
|
|
48
|
+
if (model.doStatus == null) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Video model ${model.modelId} does not implement doStatus.`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { retry } = prepareRetries({
|
|
55
|
+
maxRetries: maxRetriesArg,
|
|
56
|
+
abortSignal,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return retry(() =>
|
|
60
|
+
model.doStatus!({
|
|
61
|
+
operation,
|
|
62
|
+
headers: withUserAgentSuffix(headers ?? {}, `ai/${VERSION}`),
|
|
63
|
+
abortSignal,
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export type { GenerateVideoPrompt } from './generate-video';
|
|
2
2
|
export { experimental_generateVideo } from './generate-video';
|
|
3
3
|
export type { GenerateVideoResult } from './generate-video-result';
|
|
4
|
+
export { experimental_startVideo } from './start-video';
|
|
5
|
+
export type { StartVideoResult } from './start-video';
|
|
6
|
+
export { experimental_getVideoStatus } from './get-video-status';
|
|
7
|
+
export type { GetVideoStatusResult } from './get-video-status';
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Experimental_VideoModelV4CallOptions,
|
|
3
|
+
Experimental_VideoModelV4FrameType,
|
|
4
|
+
JSONValue,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import {
|
|
7
|
+
generateId,
|
|
8
|
+
withUserAgentSuffix,
|
|
9
|
+
type DataContent,
|
|
10
|
+
type ProviderOptions,
|
|
11
|
+
} from '@ai-sdk/provider-utils';
|
|
12
|
+
import { resolveVideoModel } from '../model/resolve-model';
|
|
13
|
+
import type {
|
|
14
|
+
VideoModel,
|
|
15
|
+
VideoModelProviderMetadata,
|
|
16
|
+
} from '../types/video-model';
|
|
17
|
+
import type { VideoModelResponseMetadata } from '../types/video-model-response-metadata';
|
|
18
|
+
import type { Warning } from '../types/warning';
|
|
19
|
+
import { prepareRetries } from '../util/prepare-retries';
|
|
20
|
+
import { VERSION } from '../version';
|
|
21
|
+
import {
|
|
22
|
+
normalizeVideoCallInputs,
|
|
23
|
+
type GenerateVideoPrompt,
|
|
24
|
+
} from './generate-video';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The result of an `experimental_startVideo` call.
|
|
28
|
+
*/
|
|
29
|
+
export interface StartVideoResult {
|
|
30
|
+
/**
|
|
31
|
+
* JSON-serializable opaque reference to the started generation.
|
|
32
|
+
* Persist it and pass it to `experimental_getVideoStatus` to retrieve the
|
|
33
|
+
* status and result later — from any process.
|
|
34
|
+
*/
|
|
35
|
+
readonly operation: JSONValue;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Warnings for the call, e.g. unsupported settings.
|
|
39
|
+
*/
|
|
40
|
+
readonly warnings: Array<Warning>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Provider-specific metadata passed through from the provider.
|
|
44
|
+
* Carries the provider's own job identifiers (e.g. the AI Gateway's
|
|
45
|
+
* `providerMetadata.gateway.asyncJob.jobId` and, when `webhookUrl` was
|
|
46
|
+
* given, its `webhookSigningSecret`).
|
|
47
|
+
*/
|
|
48
|
+
readonly providerMetadata?: VideoModelProviderMetadata;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Response metadata from the provider.
|
|
52
|
+
*/
|
|
53
|
+
readonly response: VideoModelResponseMetadata;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Starts an asynchronous video generation and returns immediately with an
|
|
58
|
+
* opaque `operation` reference — without waiting for the video to finish.
|
|
59
|
+
*
|
|
60
|
+
* This is the fire-and-forget counterpart to `experimental_generateVideo`:
|
|
61
|
+
* use it to fan out many jobs, to submit from a process that will not stay
|
|
62
|
+
* alive, or together with `webhookUrl` so the provider notifies your endpoint
|
|
63
|
+
* at the terminal state. Check the outcome with `experimental_getVideoStatus`,
|
|
64
|
+
* or let your webhook receiver fetch the result.
|
|
65
|
+
*
|
|
66
|
+
* @param model - The video model to use. Must implement `doStart`.
|
|
67
|
+
* @param prompt - The prompt that should be used to generate the video.
|
|
68
|
+
* @param n - Number of videos to generate. Default: 1. Must not exceed the
|
|
69
|
+
* model's `maxVideosPerCall` — fan out with multiple `startVideo` calls.
|
|
70
|
+
* @param aspectRatio - Aspect ratio of the videos to generate. Must have the format `{width}:{height}`, or `'adaptive'`.
|
|
71
|
+
* @param resolution - Resolution of the videos to generate. Must have the format `{width}x${height}`.
|
|
72
|
+
* @param duration - Duration of the video in seconds.
|
|
73
|
+
* @param fps - Frames per second for the video.
|
|
74
|
+
* @param seed - Seed for the video generation.
|
|
75
|
+
* @param frameImages - Role-tagged image inputs for image-to-video and first-last-frame generation.
|
|
76
|
+
* @param inputReferences - Reference image or video inputs for reference-to-video generation.
|
|
77
|
+
* @param generateAudio - Whether the model should generate audio alongside the video.
|
|
78
|
+
* @param providerOptions - Additional provider-specific options that are passed through to the provider
|
|
79
|
+
* as body parameters.
|
|
80
|
+
* @param maxRetries - Maximum number of retries for the start call. Set to 0 to disable retries. Default: 2.
|
|
81
|
+
* @param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
82
|
+
* @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
83
|
+
* @param webhookUrl - A URL the provider should notify when the generation
|
|
84
|
+
* reaches a terminal state.
|
|
85
|
+
*
|
|
86
|
+
* @returns A result object that contains the opaque `operation` reference,
|
|
87
|
+
* warnings, provider metadata (including the provider's job id), and response
|
|
88
|
+
* metadata.
|
|
89
|
+
*/
|
|
90
|
+
export async function experimental_startVideo({
|
|
91
|
+
model: modelArg,
|
|
92
|
+
prompt: promptArg,
|
|
93
|
+
n = 1,
|
|
94
|
+
maxVideosPerCall,
|
|
95
|
+
aspectRatio,
|
|
96
|
+
resolution,
|
|
97
|
+
duration,
|
|
98
|
+
fps,
|
|
99
|
+
seed,
|
|
100
|
+
frameImages,
|
|
101
|
+
inputReferences,
|
|
102
|
+
generateAudio,
|
|
103
|
+
providerOptions,
|
|
104
|
+
maxRetries: maxRetriesArg,
|
|
105
|
+
abortSignal,
|
|
106
|
+
headers,
|
|
107
|
+
webhookUrl,
|
|
108
|
+
}: {
|
|
109
|
+
model: VideoModel;
|
|
110
|
+
prompt: GenerateVideoPrompt;
|
|
111
|
+
n?: number;
|
|
112
|
+
maxVideosPerCall?: number;
|
|
113
|
+
aspectRatio?: `${number}:${number}` | 'adaptive';
|
|
114
|
+
resolution?: `${number}x${number}`;
|
|
115
|
+
duration?: number;
|
|
116
|
+
fps?: number;
|
|
117
|
+
seed?: number;
|
|
118
|
+
frameImages?: Array<{
|
|
119
|
+
image: DataContent;
|
|
120
|
+
frameType: Experimental_VideoModelV4FrameType;
|
|
121
|
+
}>;
|
|
122
|
+
inputReferences?: Array<
|
|
123
|
+
DataContent | { data: DataContent; mediaType?: string }
|
|
124
|
+
>;
|
|
125
|
+
generateAudio?: boolean;
|
|
126
|
+
providerOptions?: ProviderOptions;
|
|
127
|
+
maxRetries?: number;
|
|
128
|
+
abortSignal?: AbortSignal;
|
|
129
|
+
headers?: Record<string, string>;
|
|
130
|
+
webhookUrl?: string;
|
|
131
|
+
}): Promise<StartVideoResult> {
|
|
132
|
+
const model = resolveVideoModel(modelArg);
|
|
133
|
+
|
|
134
|
+
if (model.doStart == null) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Video model ${model.modelId} does not implement doStart. ` +
|
|
137
|
+
'Use generateVideo for models without an asynchronous start/status flow.',
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Invalid n: expected a positive integer, received ${JSON.stringify(n)}.`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// A start yields one operation covering all n videos: refuse to silently
|
|
148
|
+
// exceed a known per-call limit instead of splitting into several starts.
|
|
149
|
+
const knownMaxVideosPerCall =
|
|
150
|
+
maxVideosPerCall ??
|
|
151
|
+
(typeof model.maxVideosPerCall === 'function'
|
|
152
|
+
? await model.maxVideosPerCall({ modelId: model.modelId })
|
|
153
|
+
: model.maxVideosPerCall);
|
|
154
|
+
if (knownMaxVideosPerCall != null && n > knownMaxVideosPerCall) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Video model ${model.modelId} supports at most ${knownMaxVideosPerCall} video(s) per call, ` +
|
|
157
|
+
`but ${n} were requested. Split the batch across multiple startVideo calls.`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const {
|
|
162
|
+
prompt,
|
|
163
|
+
resolvedImage,
|
|
164
|
+
normalizedFrameImages,
|
|
165
|
+
effectiveInputReferences,
|
|
166
|
+
warnings,
|
|
167
|
+
} = normalizeVideoCallInputs({ promptArg, frameImages, inputReferences });
|
|
168
|
+
|
|
169
|
+
const { retry } = prepareRetries({
|
|
170
|
+
maxRetries: maxRetriesArg,
|
|
171
|
+
abortSignal,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// `doStart` is billable: mint one idempotency token per logical start,
|
|
175
|
+
// outside the retry closure; a caller-supplied key wins.
|
|
176
|
+
const callerIdempotencyKey = Object.entries(headers ?? {}).find(
|
|
177
|
+
([key, value]) =>
|
|
178
|
+
key.toLowerCase() === 'idempotency-key' && value !== undefined,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const callOptions: Experimental_VideoModelV4CallOptions & {
|
|
182
|
+
webhookUrl?: string;
|
|
183
|
+
} = {
|
|
184
|
+
prompt,
|
|
185
|
+
n,
|
|
186
|
+
aspectRatio,
|
|
187
|
+
resolution,
|
|
188
|
+
duration,
|
|
189
|
+
fps,
|
|
190
|
+
seed,
|
|
191
|
+
image: resolvedImage,
|
|
192
|
+
frameImages: normalizedFrameImages,
|
|
193
|
+
inputReferences: effectiveInputReferences,
|
|
194
|
+
generateAudio,
|
|
195
|
+
providerOptions: providerOptions ?? {},
|
|
196
|
+
headers: {
|
|
197
|
+
...withUserAgentSuffix(headers ?? {}, `ai/${VERSION}`),
|
|
198
|
+
...(callerIdempotencyKey
|
|
199
|
+
? {}
|
|
200
|
+
: { 'idempotency-key': `aisdk_vid_${generateId()}` }),
|
|
201
|
+
},
|
|
202
|
+
abortSignal,
|
|
203
|
+
webhookUrl,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const startResult = await retry(() => model.doStart!(callOptions));
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
operation: startResult.operation,
|
|
210
|
+
warnings: [...warnings, ...startResult.warnings],
|
|
211
|
+
providerMetadata: startResult.providerMetadata,
|
|
212
|
+
response: startResult.response,
|
|
213
|
+
};
|
|
214
|
+
}
|
package/src/ui/chat.ts
CHANGED
|
@@ -191,7 +191,7 @@ export interface ChatInit<UI_MESSAGE extends UIMessage> {
|
|
|
191
191
|
*/
|
|
192
192
|
id?: string;
|
|
193
193
|
|
|
194
|
-
messageMetadataSchema?: FlexibleSchema<
|
|
194
|
+
messageMetadataSchema?: FlexibleSchema<UI_MESSAGE['metadata']>;
|
|
195
195
|
dataPartSchemas?: UIDataTypesToSchemas<InferUIMessageData<UI_MESSAGE>>;
|
|
196
196
|
|
|
197
197
|
messages?: UI_MESSAGE[];
|
|
@@ -246,7 +246,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
246
246
|
protected state: ChatState<UI_MESSAGE>;
|
|
247
247
|
|
|
248
248
|
private messageMetadataSchema:
|
|
249
|
-
| FlexibleSchema<
|
|
249
|
+
| FlexibleSchema<UI_MESSAGE['metadata']>
|
|
250
250
|
| undefined;
|
|
251
251
|
private dataPartSchemas:
|
|
252
252
|
| UIDataTypesToSchemas<InferUIMessageData<UI_MESSAGE>>
|
|
@@ -90,7 +90,7 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
90
90
|
}: {
|
|
91
91
|
// input stream is not fully typed yet:
|
|
92
92
|
stream: ReadableStream<UIMessageChunk>;
|
|
93
|
-
messageMetadataSchema?: FlexibleSchema<
|
|
93
|
+
messageMetadataSchema?: FlexibleSchema<UI_MESSAGE['metadata']>;
|
|
94
94
|
dataPartSchemas?: UIDataTypesToSchemas<InferUIMessageData<UI_MESSAGE>>;
|
|
95
95
|
onToolCall?: (options: {
|
|
96
96
|
toolCall: InferUIMessageToolCall<UI_MESSAGE>;
|