@ai-sdk/google-vertex 5.0.38 → 5.0.39

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.
@@ -1,14 +1,17 @@
1
1
  import {
2
2
  AISDKError,
3
3
  type Experimental_VideoModelV4,
4
+ type Experimental_VideoModelV4CallOptions as VideoModelV4CallOptions,
4
5
  type Experimental_VideoModelV4File,
6
+ type Experimental_VideoModelV4OperationStartResult as VideoModelV4OperationStartResult,
7
+ type Experimental_VideoModelV4OperationStatusResult as VideoModelV4OperationStatusResult,
8
+ type SharedV4ProviderMetadata,
5
9
  type SharedV4Warning,
6
10
  } from '@ai-sdk/provider';
7
11
  import {
8
12
  combineHeaders,
9
13
  convertUint8ArrayToBase64,
10
14
  createJsonResponseHandler,
11
- delay,
12
15
  parseProviderOptions,
13
16
  postJsonToApi,
14
17
  resolve,
@@ -23,6 +26,8 @@ import {
23
26
  } from './google-vertex-video-model-options';
24
27
  import type { GoogleVertexVideoModelId } from './google-vertex-video-settings';
25
28
 
29
+ type VideoModelV4 = Experimental_VideoModelV4;
30
+
26
31
  interface GoogleVertexVideoModelConfig {
27
32
  provider: string;
28
33
  baseURL: string;
@@ -35,27 +40,27 @@ interface GoogleVertexVideoModelConfig {
35
40
  }
36
41
 
37
42
  function getFirstFrameImage(
38
- options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
43
+ options: VideoModelV4CallOptions,
39
44
  ): Experimental_VideoModelV4File | undefined {
40
45
  return options.frameImages?.find(frame => frame.frameType === 'first_frame')
41
46
  ?.image;
42
47
  }
43
48
 
44
49
  function resolveStartImage(
45
- options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
50
+ options: VideoModelV4CallOptions,
46
51
  ): Experimental_VideoModelV4File | undefined {
47
52
  return getFirstFrameImage(options) ?? options.image;
48
53
  }
49
54
 
50
55
  function getLastFrameImage(
51
- options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
56
+ options: VideoModelV4CallOptions,
52
57
  ): Experimental_VideoModelV4File | undefined {
53
58
  return options.frameImages?.find(frame => frame.frameType === 'last_frame')
54
59
  ?.image;
55
60
  }
56
61
 
57
62
  function getInputReferences(
58
- options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
63
+ options: VideoModelV4CallOptions,
59
64
  ): Array<Experimental_VideoModelV4File> | undefined {
60
65
  if (options.frameImages != null && options.frameImages.length > 0) {
61
66
  return undefined;
@@ -123,10 +128,14 @@ export class GoogleVertexVideoModel implements Experimental_VideoModelV4 {
123
128
  private readonly config: GoogleVertexVideoModelConfig,
124
129
  ) {}
125
130
 
126
- async doGenerate(
127
- options: Parameters<Experimental_VideoModelV4['doGenerate']>[0],
128
- ): Promise<Awaited<ReturnType<Experimental_VideoModelV4['doGenerate']>>> {
129
- const currentDate = this.config._internal?.currentDate?.() ?? new Date();
131
+ private async buildRequest(
132
+ options: Parameters<NonNullable<VideoModelV4['doStart']>>[0],
133
+ ): Promise<{
134
+ instances: Array<Record<string, unknown>>;
135
+ parameters: Record<string, unknown>;
136
+ warnings: SharedV4Warning[];
137
+ googleVertexOptions: GoogleVertexVideoModelOptions | undefined;
138
+ }> {
130
139
  const warnings: SharedV4Warning[] = [];
131
140
 
132
141
  const googleVertexOptions = ((await parseProviderOptions({
@@ -241,85 +250,33 @@ export class GoogleVertexVideoModel implements Experimental_VideoModelV4 {
241
250
  }
242
251
  }
243
252
 
244
- const { value: operation } = await postJsonToApi({
245
- url: `${this.config.baseURL}/models/${this.modelId}:predictLongRunning`,
246
- headers: combineHeaders(
247
- await resolve(this.config.headers),
248
- options.headers,
249
- ),
250
- body: {
251
- instances,
252
- parameters,
253
- },
254
- successfulResponseHandler: createJsonResponseHandler(
255
- googleVertexOperationSchema,
256
- ),
257
- failedResponseHandler: googleVertexFailedResponseHandler,
258
- abortSignal: options.abortSignal,
259
- fetch: this.config.fetch,
260
- });
261
-
262
- const operationName = operation.name;
263
- if (!operationName) {
264
- throw new AISDKError({
265
- name: 'VERTEX_VIDEO_GENERATION_ERROR',
266
- message: 'No operation name returned from API',
267
- });
268
- }
269
-
270
- const pollIntervalMs = googleVertexOptions?.pollIntervalMs ?? 10000; // 10 seconds
271
- const pollTimeoutMs = googleVertexOptions?.pollTimeoutMs ?? 600000; // 10 minutes
272
-
273
- const startTime = Date.now();
274
- let finalOperation = operation;
275
- let responseHeaders: Record<string, string> | undefined;
276
-
277
- while (!finalOperation.done) {
278
- if (Date.now() - startTime > pollTimeoutMs) {
279
- throw new AISDKError({
280
- name: 'VERTEX_VIDEO_GENERATION_TIMEOUT',
281
- message: `Video generation timed out after ${pollTimeoutMs}ms`,
282
- });
283
- }
284
-
285
- await delay(pollIntervalMs);
286
-
287
- if (options.abortSignal?.aborted) {
288
- throw new AISDKError({
289
- name: 'VERTEX_VIDEO_GENERATION_ABORTED',
290
- message: 'Video generation request was aborted',
291
- });
292
- }
293
-
294
- const { value: statusOperation, responseHeaders: pollHeaders } =
295
- await postJsonToApi({
296
- url: `${this.config.baseURL}/models/${this.modelId}:fetchPredictOperation`,
297
- headers: combineHeaders(
298
- await resolve(this.config.headers),
299
- options.headers,
300
- ),
301
- body: {
302
- operationName,
303
- },
304
- successfulResponseHandler: createJsonResponseHandler(
305
- googleVertexOperationSchema,
306
- ),
307
- failedResponseHandler: googleVertexFailedResponseHandler,
308
- abortSignal: options.abortSignal,
309
- fetch: this.config.fetch,
310
- });
311
-
312
- finalOperation = statusOperation;
313
- responseHeaders = pollHeaders;
314
- }
315
-
316
- if (finalOperation.error) {
317
- throw new AISDKError({
318
- name: 'VERTEX_VIDEO_GENERATION_FAILED',
319
- message: `Video generation failed: ${finalOperation.error.message}`,
320
- });
321
- }
253
+ return { instances, parameters, warnings, googleVertexOptions };
254
+ }
322
255
 
256
+ private buildCompletedResult({
257
+ finalOperation,
258
+ responseHeaders,
259
+ warnings,
260
+ currentDate,
261
+ }: {
262
+ finalOperation: VertexOperation;
263
+ responseHeaders: Record<string, string> | undefined;
264
+ warnings: SharedV4Warning[];
265
+ currentDate: Date;
266
+ }): {
267
+ status: 'completed';
268
+ videos: Array<
269
+ | { type: 'base64'; data: string; mediaType: string }
270
+ | { type: 'url'; url: string; mediaType: string }
271
+ >;
272
+ warnings: SharedV4Warning[];
273
+ providerMetadata: SharedV4ProviderMetadata;
274
+ response: {
275
+ timestamp: Date;
276
+ modelId: string;
277
+ headers: Record<string, string> | undefined;
278
+ };
279
+ } {
323
280
  const response = finalOperation.response;
324
281
  if (!response?.videos || response.videos.length === 0) {
325
282
  throw new AISDKError({
@@ -328,7 +285,6 @@ export class GoogleVertexVideoModel implements Experimental_VideoModelV4 {
328
285
  });
329
286
  }
330
287
 
331
- // Process videos - Vertex returns base64 encoded videos or GCS URIs
332
288
  const videos: Array<
333
289
  | { type: 'base64'; data: string; mediaType: string }
334
290
  | { type: 'url'; url: string; mediaType: string }
@@ -369,6 +325,7 @@ export class GoogleVertexVideoModel implements Experimental_VideoModelV4 {
369
325
  }
370
326
 
371
327
  return {
328
+ status: 'completed',
372
329
  videos,
373
330
  warnings,
374
331
  response: {
@@ -387,6 +344,105 @@ export class GoogleVertexVideoModel implements Experimental_VideoModelV4 {
387
344
  })(),
388
345
  };
389
346
  }
347
+
348
+ async doStart(
349
+ options: Parameters<NonNullable<VideoModelV4['doStart']>>[0],
350
+ ): Promise<VideoModelV4OperationStartResult> {
351
+ const currentDate = this.config._internal?.currentDate?.() ?? new Date();
352
+
353
+ const { instances, parameters, warnings } =
354
+ await this.buildRequest(options);
355
+
356
+ const { value: operation, responseHeaders } = await postJsonToApi({
357
+ url: `${this.config.baseURL}/models/${this.modelId}:predictLongRunning`,
358
+ headers: combineHeaders(
359
+ await resolve(this.config.headers),
360
+ options.headers,
361
+ ),
362
+ body: {
363
+ instances,
364
+ parameters,
365
+ },
366
+ successfulResponseHandler: createJsonResponseHandler(
367
+ googleVertexOperationSchema,
368
+ ),
369
+ failedResponseHandler: googleVertexFailedResponseHandler,
370
+ abortSignal: options.abortSignal,
371
+ fetch: this.config.fetch,
372
+ });
373
+
374
+ const operationName = operation.name;
375
+ if (!operationName) {
376
+ throw new AISDKError({
377
+ name: 'VERTEX_VIDEO_GENERATION_ERROR',
378
+ message: 'No operation name returned from API',
379
+ });
380
+ }
381
+
382
+ return {
383
+ operation: { operationName },
384
+ warnings,
385
+ response: {
386
+ timestamp: currentDate,
387
+ modelId: this.modelId,
388
+ headers: responseHeaders,
389
+ },
390
+ };
391
+ }
392
+
393
+ async doStatus(
394
+ options: Parameters<NonNullable<VideoModelV4['doStatus']>>[0],
395
+ ): Promise<VideoModelV4OperationStatusResult> {
396
+ const currentDate = this.config._internal?.currentDate?.() ?? new Date();
397
+ const { operationName } = options.operation as { operationName: string };
398
+
399
+ const { value: statusOperation, responseHeaders } = await postJsonToApi({
400
+ url: `${this.config.baseURL}/models/${this.modelId}:fetchPredictOperation`,
401
+ headers: combineHeaders(
402
+ await resolve(this.config.headers),
403
+ options.headers,
404
+ ),
405
+ body: {
406
+ operationName,
407
+ },
408
+ successfulResponseHandler: createJsonResponseHandler(
409
+ googleVertexOperationSchema,
410
+ ),
411
+ failedResponseHandler: googleVertexFailedResponseHandler,
412
+ abortSignal: options.abortSignal,
413
+ fetch: this.config.fetch,
414
+ });
415
+
416
+ if (!statusOperation.done) {
417
+ return {
418
+ status: 'pending' as const,
419
+ response: {
420
+ timestamp: currentDate,
421
+ modelId: this.modelId,
422
+ headers: responseHeaders,
423
+ },
424
+ };
425
+ }
426
+
427
+ if (statusOperation.error) {
428
+ return {
429
+ status: 'error' as const,
430
+ error: `Video generation failed: ${statusOperation.error.message}`,
431
+ response: {
432
+ timestamp: currentDate,
433
+ modelId: this.modelId,
434
+ headers: responseHeaders,
435
+ },
436
+ };
437
+ }
438
+
439
+ return this.buildCompletedResult({
440
+ finalOperation: statusOperation,
441
+ responseHeaders,
442
+ warnings: [],
443
+ currentDate,
444
+ });
445
+ }
390
446
  }
391
447
 
392
448
  const googleVertexOperationSchema = z.object({
@@ -414,3 +470,5 @@ const googleVertexOperationSchema = z.object({
414
470
  })
415
471
  .nullish(),
416
472
  });
473
+
474
+ type VertexOperation = z.infer<typeof googleVertexOperationSchema>;