@ai-sdk/google 4.0.2 → 4.0.4

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # @ai-sdk/google
2
2
 
3
+ ## 4.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - dc1eb8d: Support Gemini Interactions video output. Parse video output blocks from the Google Interactions API into file parts (buffered and streaming), and surface the per-modality output token breakdown via `providerMetadata.google.outputTokensByModality`.
8
+
9
+ ## 4.0.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 0274f34: feat (video): add first-class `frameImages` and `inputReferences` call options for video generation
14
+ - Updated dependencies [0274f34]
15
+ - @ai-sdk/provider@4.0.1
16
+ - @ai-sdk/provider-utils@5.0.2
17
+
3
18
  ## 4.0.2
4
19
 
5
20
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -387,6 +387,13 @@ type GoogleInteractionsProviderMetadata = {
387
387
  * Service tier used for this interaction (passthrough for observability).
388
388
  */
389
389
  serviceTier?: string;
390
+ /**
391
+ * Output token counts keyed by modality (e.g. `{ video: 57920 }`), sourced
392
+ * from the Interactions API `output_tokens_by_modality`. Present only when
393
+ * the response reports a breakdown. Preview surface for per-modality billing;
394
+ * may be promoted to a first-class usage field later.
395
+ */
396
+ outputTokensByModality?: Record<string, number>;
390
397
  /**
391
398
  * Per-block signature hash for backend validation. Set by the SDK on output
392
399
  * reasoning / tool-call parts and round-tripped on input parts.
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from "@ai-sdk/provider-utils";
8
8
 
9
9
  // src/version.ts
10
- var VERSION = true ? "4.0.2" : "0.0.0-test";
10
+ var VERSION = true ? "4.0.4" : "0.0.0-test";
11
11
 
12
12
  // src/google-embedding-model.ts
13
13
  import {
@@ -3360,6 +3360,67 @@ var googleVideoModelOptionsSchema = lazySchema15(
3360
3360
  );
3361
3361
 
3362
3362
  // src/google-video-model.ts
3363
+ function getFirstFrameImage(options) {
3364
+ var _a, _b;
3365
+ return (_b = (_a = options.frameImages) == null ? void 0 : _a.find((frame) => frame.frameType === "first_frame")) == null ? void 0 : _b.image;
3366
+ }
3367
+ function resolveStartImage(options) {
3368
+ var _a;
3369
+ return (_a = getFirstFrameImage(options)) != null ? _a : options.image;
3370
+ }
3371
+ function getLastFrameImage(options) {
3372
+ var _a, _b;
3373
+ return (_b = (_a = options.frameImages) == null ? void 0 : _a.find((frame) => frame.frameType === "last_frame")) == null ? void 0 : _b.image;
3374
+ }
3375
+ function getInputReferences(options) {
3376
+ if (options.frameImages != null && options.frameImages.length > 0) {
3377
+ return void 0;
3378
+ }
3379
+ return options.inputReferences != null && options.inputReferences.length > 0 ? options.inputReferences : void 0;
3380
+ }
3381
+ function convertFileToGoogleImage(file, warnings) {
3382
+ if (file.type === "url") {
3383
+ if (file.url.startsWith("gs://")) {
3384
+ return {
3385
+ gcsUri: file.url,
3386
+ mimeType: "image/png"
3387
+ };
3388
+ }
3389
+ warnings.push({
3390
+ type: "unsupported",
3391
+ feature: "URL-based image input",
3392
+ details: "Google Generative AI video models require base64-encoded images or GCS URIs. URL will be ignored."
3393
+ });
3394
+ return void 0;
3395
+ }
3396
+ const base64Data = typeof file.data === "string" ? file.data : convertUint8ArrayToBase64(file.data);
3397
+ return {
3398
+ inlineData: {
3399
+ mimeType: file.mediaType || "image/png",
3400
+ data: base64Data
3401
+ }
3402
+ };
3403
+ }
3404
+ function convertProviderReferenceImage(refImg) {
3405
+ if (refImg.bytesBase64Encoded) {
3406
+ return {
3407
+ inlineData: {
3408
+ mimeType: "image/png",
3409
+ data: refImg.bytesBase64Encoded
3410
+ }
3411
+ };
3412
+ }
3413
+ if (refImg.gcsUri) {
3414
+ return {
3415
+ gcsUri: refImg.gcsUri
3416
+ };
3417
+ }
3418
+ return refImg;
3419
+ }
3420
+ function convertInputReferenceImage(file, warnings) {
3421
+ const image = convertFileToGoogleImage(file, warnings);
3422
+ return image != null ? { image, referenceType: "asset" } : void 0;
3423
+ }
3363
3424
  var GoogleVideoModel = class {
3364
3425
  constructor(modelId, config) {
3365
3426
  this.modelId = modelId;
@@ -3386,39 +3447,30 @@ var GoogleVideoModel = class {
3386
3447
  if (options.prompt != null) {
3387
3448
  instance.prompt = options.prompt;
3388
3449
  }
3389
- if (options.image != null) {
3390
- if (options.image.type === "url") {
3391
- warnings.push({
3392
- type: "unsupported",
3393
- feature: "URL-based image input",
3394
- details: "Google Generative AI video models require base64-encoded images. URL will be ignored."
3395
- });
3396
- } else {
3397
- const base64Data = typeof options.image.data === "string" ? options.image.data : convertUint8ArrayToBase64(options.image.data);
3398
- instance.image = {
3399
- inlineData: {
3400
- mimeType: options.image.mediaType || "image/png",
3401
- data: base64Data
3402
- }
3403
- };
3450
+ const startImage = resolveStartImage(options);
3451
+ if (startImage != null) {
3452
+ const image = convertFileToGoogleImage(startImage, warnings);
3453
+ if (image != null) {
3454
+ instance.image = image;
3404
3455
  }
3405
3456
  }
3406
- if ((googleOptions == null ? void 0 : googleOptions.referenceImages) != null) {
3407
- instance.referenceImages = googleOptions.referenceImages.map((refImg) => {
3408
- if (refImg.bytesBase64Encoded) {
3409
- return {
3410
- inlineData: {
3411
- mimeType: "image/png",
3412
- data: refImg.bytesBase64Encoded
3413
- }
3414
- };
3415
- } else if (refImg.gcsUri) {
3416
- return {
3417
- gcsUri: refImg.gcsUri
3418
- };
3419
- }
3420
- return refImg;
3457
+ const lastFrameImage = getLastFrameImage(options);
3458
+ if (lastFrameImage != null) {
3459
+ const lastFrame = convertFileToGoogleImage(lastFrameImage, warnings);
3460
+ if (lastFrame != null) {
3461
+ instance.lastFrame = lastFrame;
3462
+ }
3463
+ }
3464
+ const inputReferences = getInputReferences(options);
3465
+ if (inputReferences != null) {
3466
+ instance.referenceImages = inputReferences.flatMap((reference) => {
3467
+ const converted = convertInputReferenceImage(reference, warnings);
3468
+ return converted != null ? [converted] : [];
3421
3469
  });
3470
+ } else if ((googleOptions == null ? void 0 : googleOptions.referenceImages) != null) {
3471
+ instance.referenceImages = googleOptions.referenceImages.map(
3472
+ (refImg) => convertProviderReferenceImage(refImg)
3473
+ );
3422
3474
  }
3423
3475
  const parameters = {
3424
3476
  sampleCount: options.n
@@ -3917,6 +3969,19 @@ function convertGoogleInteractionsUsage(usage) {
3917
3969
  raw: usage
3918
3970
  };
3919
3971
  }
3972
+ function getGoogleInteractionsOutputTokensByModality(usage) {
3973
+ const byModality = usage == null ? void 0 : usage.output_tokens_by_modality;
3974
+ if (byModality == null) {
3975
+ return void 0;
3976
+ }
3977
+ const result = {};
3978
+ for (const entry of byModality) {
3979
+ if ((entry == null ? void 0 : entry.modality) != null && entry.tokens != null) {
3980
+ result[entry.modality] = entry.tokens;
3981
+ }
3982
+ }
3983
+ return Object.keys(result).length > 0 ? result : void 0;
3984
+ }
3920
3985
 
3921
3986
  // src/interactions/extract-google-interactions-sources.ts
3922
3987
  var KNOWN_DOC_EXTENSIONS = {
@@ -4172,7 +4237,7 @@ function buildGoogleInteractionsStreamTransform({
4172
4237
  controller.enqueue({ type: "stream-start", warnings });
4173
4238
  },
4174
4239
  transform(chunk, controller) {
4175
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4240
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4176
4241
  if (includeRawChunks) {
4177
4242
  controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
4178
4243
  }
@@ -4355,9 +4420,31 @@ function buildGoogleInteractionsStreamTransform({
4355
4420
  }
4356
4421
  break;
4357
4422
  }
4423
+ if (dtype === "video" && (open.kind === "pending_model_output" || open.kind === "text")) {
4424
+ const videoDelta = event.delta;
4425
+ const google2 = {};
4426
+ if (interactionId != null) google2.interactionId = interactionId;
4427
+ const providerMetadata = Object.keys(google2).length > 0 ? { google: google2 } : void 0;
4428
+ if ((videoDelta == null ? void 0 : videoDelta.data) != null && videoDelta.data.length > 0) {
4429
+ controller.enqueue({
4430
+ type: "file",
4431
+ mediaType: (_m = videoDelta.mime_type) != null ? _m : "video/mp4",
4432
+ data: { type: "data", data: videoDelta.data },
4433
+ ...providerMetadata ? { providerMetadata } : {}
4434
+ });
4435
+ } else if ((videoDelta == null ? void 0 : videoDelta.uri) != null && videoDelta.uri.length > 0) {
4436
+ controller.enqueue({
4437
+ type: "file",
4438
+ mediaType: (_n = videoDelta.mime_type) != null ? _n : "video/mp4",
4439
+ data: { type: "url", url: new URL(videoDelta.uri) },
4440
+ ...providerMetadata ? { providerMetadata } : {}
4441
+ });
4442
+ }
4443
+ break;
4444
+ }
4358
4445
  const delta = event.delta;
4359
4446
  if (open.kind === "text" && (delta == null ? void 0 : delta.type) === "text") {
4360
- const text = (_m = delta.text) != null ? _m : "";
4447
+ const text = (_o = delta.text) != null ? _o : "";
4361
4448
  if (text.length > 0) {
4362
4449
  controller.enqueue({
4363
4450
  type: "text-delta",
@@ -4460,14 +4547,14 @@ function buildGoogleInteractionsStreamTransform({
4460
4547
  if (open.data != null && open.data.length > 0) {
4461
4548
  controller.enqueue({
4462
4549
  type: "file",
4463
- mediaType: (_n = open.mimeType) != null ? _n : "image/png",
4550
+ mediaType: (_p = open.mimeType) != null ? _p : "image/png",
4464
4551
  data: { type: "data", data: open.data },
4465
4552
  ...providerMetadata ? { providerMetadata } : {}
4466
4553
  });
4467
4554
  } else if (open.uri != null && open.uri.length > 0) {
4468
4555
  controller.enqueue({
4469
4556
  type: "file",
4470
- mediaType: (_o = open.mimeType) != null ? _o : "image/png",
4557
+ mediaType: (_q = open.mimeType) != null ? _q : "image/png",
4471
4558
  data: { type: "url", url: new URL(open.uri) },
4472
4559
  ...providerMetadata ? { providerMetadata } : {}
4473
4560
  });
@@ -4494,7 +4581,7 @@ function buildGoogleInteractionsStreamTransform({
4494
4581
  type: "tool-call",
4495
4582
  toolCallId: open.toolCallId,
4496
4583
  toolName: open.toolName,
4497
- input: JSON.stringify((_p = open.arguments) != null ? _p : {}),
4584
+ input: JSON.stringify((_r = open.arguments) != null ? _r : {}),
4498
4585
  providerExecuted: true
4499
4586
  });
4500
4587
  open.callEmitted = true;
@@ -4503,7 +4590,7 @@ function buildGoogleInteractionsStreamTransform({
4503
4590
  type: "tool-result",
4504
4591
  toolCallId: open.callId,
4505
4592
  toolName: open.toolName,
4506
- result: (_q = open.result) != null ? _q : null
4593
+ result: (_s = open.result) != null ? _s : null
4507
4594
  });
4508
4595
  open.resultEmitted = true;
4509
4596
  const sources = builtinToolResultToSources({
@@ -4557,7 +4644,7 @@ function buildGoogleInteractionsStreamTransform({
4557
4644
  case "error": {
4558
4645
  const event = value;
4559
4646
  finishStatus = "failed";
4560
- const errorPayload = (_r = event.error) != null ? _r : {
4647
+ const errorPayload = (_t = event.error) != null ? _t : {
4561
4648
  message: "Unknown interaction error"
4562
4649
  };
4563
4650
  controller.enqueue({ type: "error", error: errorPayload });
@@ -4575,10 +4662,12 @@ function buildGoogleInteractionsStreamTransform({
4575
4662
  }),
4576
4663
  raw: finishStatus
4577
4664
  };
4665
+ const outputTokensByModality = getGoogleInteractionsOutputTokensByModality(usage);
4578
4666
  const providerMetadata = {
4579
4667
  google: {
4580
4668
  ...interactionId != null ? { interactionId } : {},
4581
- ...serviceTier != null ? { serviceTier } : {}
4669
+ ...serviceTier != null ? { serviceTier } : {},
4670
+ ...outputTokensByModality != null ? { outputTokensByModality } : {}
4582
4671
  }
4583
4672
  };
4584
4673
  controller.enqueue({
@@ -5065,9 +5154,16 @@ var contentBlockSchema = () => {
5065
5154
  resolution: z20.enum(["low", "medium", "high", "ultra_high"]).nullish(),
5066
5155
  uri: z20.string().nullish()
5067
5156
  }).loose();
5157
+ const videoContent = z20.object({
5158
+ type: z20.literal("video"),
5159
+ data: z20.string().nullish(),
5160
+ mime_type: z20.string().nullish(),
5161
+ uri: z20.string().nullish()
5162
+ }).loose();
5068
5163
  return z20.union([
5069
5164
  textContent,
5070
5165
  imageContent,
5166
+ videoContent,
5071
5167
  z20.object({ type: z20.string() }).loose()
5072
5168
  ]);
5073
5169
  };
@@ -5214,6 +5310,12 @@ var googleInteractionsEventSchema = lazySchema18(
5214
5310
  resolution: z20.enum(["low", "medium", "high", "ultra_high"]).nullish(),
5215
5311
  uri: z20.string().nullish()
5216
5312
  }).loose();
5313
+ const stepDeltaVideo = z20.object({
5314
+ type: z20.literal("video"),
5315
+ data: z20.string().nullish(),
5316
+ mime_type: z20.string().nullish(),
5317
+ uri: z20.string().nullish()
5318
+ }).loose();
5217
5319
  const stepDeltaBuiltinToolCall = z20.object({
5218
5320
  type: z20.enum(BUILTIN_TOOL_CALL_STEP_TYPES),
5219
5321
  id: z20.string().nullish(),
@@ -5236,6 +5338,7 @@ var googleInteractionsEventSchema = lazySchema18(
5236
5338
  const stepDeltaUnion = z20.union([
5237
5339
  stepDeltaText,
5238
5340
  stepDeltaImage,
5341
+ stepDeltaVideo,
5239
5342
  stepDeltaThoughtSummary,
5240
5343
  stepDeltaThoughtSignature,
5241
5344
  stepDeltaArgumentsDelta,
@@ -5527,7 +5630,7 @@ function parseGoogleInteractionsOutputs({
5527
5630
  generateId: generateId3,
5528
5631
  interactionId
5529
5632
  }) {
5530
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
5633
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
5531
5634
  const content = [];
5532
5635
  let hasFunctionCall = false;
5533
5636
  if (steps == null) {
@@ -5575,6 +5678,23 @@ function parseGoogleInteractionsOutputs({
5575
5678
  ...googleProviderMetadata({ interactionId })
5576
5679
  });
5577
5680
  }
5681
+ } else if (blockType === "video") {
5682
+ const video = block;
5683
+ if (video.data != null && video.data.length > 0) {
5684
+ content.push({
5685
+ type: "file",
5686
+ mediaType: (_e = video.mime_type) != null ? _e : "video/mp4",
5687
+ data: { type: "data", data: video.data },
5688
+ ...googleProviderMetadata({ interactionId })
5689
+ });
5690
+ } else if (video.uri != null && video.uri.length > 0) {
5691
+ content.push({
5692
+ type: "file",
5693
+ mediaType: (_f = video.mime_type) != null ? _f : "video/mp4",
5694
+ data: { type: "url", url: new URL(video.uri) },
5695
+ ...googleProviderMetadata({ interactionId })
5696
+ });
5697
+ }
5578
5698
  }
5579
5699
  }
5580
5700
  break;
@@ -5602,7 +5722,7 @@ function parseGoogleInteractionsOutputs({
5602
5722
  type: "tool-call",
5603
5723
  toolCallId: call.id,
5604
5724
  toolName: call.name,
5605
- input: JSON.stringify((_e = call.arguments) != null ? _e : {}),
5725
+ input: JSON.stringify((_g = call.arguments) != null ? _g : {}),
5606
5726
  ...googleProviderMetadata({
5607
5727
  signature: call.signature,
5608
5728
  interactionId
@@ -5613,23 +5733,23 @@ function parseGoogleInteractionsOutputs({
5613
5733
  default: {
5614
5734
  if (BUILTIN_TOOL_CALL_TYPES2.has(type)) {
5615
5735
  const call = step;
5616
- const toolName = type === "mcp_server_tool_call" ? (_f = call.name) != null ? _f : "mcp_server_tool" : builtinToolNameFromCallType2(type);
5617
- const input = JSON.stringify((_g = call.arguments) != null ? _g : {});
5736
+ const toolName = type === "mcp_server_tool_call" ? (_h = call.name) != null ? _h : "mcp_server_tool" : builtinToolNameFromCallType2(type);
5737
+ const input = JSON.stringify((_i = call.arguments) != null ? _i : {});
5618
5738
  content.push({
5619
5739
  type: "tool-call",
5620
- toolCallId: (_h = call.id) != null ? _h : generateId3(),
5740
+ toolCallId: (_j = call.id) != null ? _j : generateId3(),
5621
5741
  toolName,
5622
5742
  input,
5623
5743
  providerExecuted: true
5624
5744
  });
5625
5745
  } else if (BUILTIN_TOOL_RESULT_TYPES2.has(type)) {
5626
5746
  const result = step;
5627
- const toolName = type === "mcp_server_tool_result" ? (_i = result.name) != null ? _i : "mcp_server_tool" : builtinToolNameFromResultType2(type);
5747
+ const toolName = type === "mcp_server_tool_result" ? (_k = result.name) != null ? _k : "mcp_server_tool" : builtinToolNameFromResultType2(type);
5628
5748
  content.push({
5629
5749
  type: "tool-result",
5630
- toolCallId: (_j = result.call_id) != null ? _j : generateId3(),
5750
+ toolCallId: (_l = result.call_id) != null ? _l : generateId3(),
5631
5751
  toolName,
5632
- result: (_k = result.result) != null ? _k : null
5752
+ result: (_m = result.result) != null ? _m : null
5633
5753
  });
5634
5754
  const sources = builtinToolResultToSources({
5635
5755
  block: step,
@@ -6542,10 +6662,14 @@ var GoogleInteractionsLanguageModel = class _GoogleInteractionsLanguageModel {
6542
6662
  raw: response.status
6543
6663
  };
6544
6664
  const serviceTier = (_e = (_d = response.service_tier) != null ? _d : responseHeaders == null ? void 0 : responseHeaders["x-gemini-service-tier"]) != null ? _e : void 0;
6665
+ const outputTokensByModality = getGoogleInteractionsOutputTokensByModality(
6666
+ response.usage
6667
+ );
6545
6668
  const providerMetadata = {
6546
6669
  google: {
6547
6670
  ...interactionId != null ? { interactionId } : {},
6548
- ...serviceTier != null ? { serviceTier } : {}
6671
+ ...serviceTier != null ? { serviceTier } : {},
6672
+ ...outputTokensByModality != null ? { outputTokensByModality } : {}
6549
6673
  }
6550
6674
  };
6551
6675
  let timestamp;