@ai-sdk/google 4.0.54 → 4.0.56

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.54",
3
+ "version": "4.0.56",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -9,13 +9,16 @@ export type GoogleTokenDetail = {
9
9
  export type GoogleUsageMetadata = {
10
10
  promptTokenCount?: number | null;
11
11
  candidatesTokenCount?: number | null;
12
+ toolUsePromptTokenCount?: number | null;
12
13
  totalTokenCount?: number | null;
13
14
  cachedContentTokenCount?: number | null;
14
15
  thoughtsTokenCount?: number | null;
15
16
  trafficType?: string | null;
16
17
  serviceTier?: string | null;
17
18
  promptTokensDetails?: GoogleTokenDetail[] | null;
19
+ cacheTokensDetails?: GoogleTokenDetail[] | null;
18
20
  candidatesTokensDetails?: GoogleTokenDetail[] | null;
21
+ toolUsePromptTokensDetails?: GoogleTokenDetail[] | null;
19
22
  };
20
23
 
21
24
  export function convertGoogleUsage(
@@ -62,6 +62,8 @@ const configurableSafetySettingCategories = [
62
62
  'HARM_CATEGORY_SEXUALLY_EXPLICIT',
63
63
  ] as const;
64
64
 
65
+ const gemini25ModelPattern = /(^|\/)gemini-2\.5(?:[.-]|$)/i;
66
+
65
67
  export type GoogleLanguageModelConfig = {
66
68
  provider: string;
67
69
  baseURL: string;
@@ -258,6 +260,22 @@ export class GoogleLanguageModel implements LanguageModelV4 {
258
260
  }
259
261
 
260
262
  const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
263
+ const isGemini25DeveloperApiModel =
264
+ !isVertexProvider && gemini25ModelPattern.test(this.modelId);
265
+
266
+ if (isGemini25DeveloperApiModel && frequencyPenalty != null) {
267
+ warnings.push({
268
+ type: 'unsupported',
269
+ feature: 'frequencyPenalty',
270
+ });
271
+ }
272
+ if (isGemini25DeveloperApiModel && presencePenalty != null) {
273
+ warnings.push({
274
+ type: 'unsupported',
275
+ feature: 'presencePenalty',
276
+ });
277
+ }
278
+
261
279
  const { usesGemini3Features } = getGoogleModelCapabilities(this.modelId);
262
280
 
263
281
  const { contents, systemInstruction } = convertToGoogleMessages(prompt, {
@@ -331,8 +349,12 @@ export class GoogleLanguageModel implements LanguageModelV4 {
331
349
  temperature,
332
350
  topK,
333
351
  topP,
334
- frequencyPenalty,
335
- presencePenalty,
352
+ frequencyPenalty: isGemini25DeveloperApiModel
353
+ ? undefined
354
+ : frequencyPenalty,
355
+ presencePenalty: isGemini25DeveloperApiModel
356
+ ? undefined
357
+ : presencePenalty,
336
358
  stopSequences,
337
359
  seed,
338
360
 
@@ -388,11 +410,16 @@ export class GoogleLanguageModel implements LanguageModelV4 {
388
410
  Object.fromEntries(
389
411
  providerOptionsNames.map(name => [name, payload]),
390
412
  ) as SharedV4ProviderMetadata;
391
- const candidate = response.candidates[0];
413
+ const candidate = response.candidates?.[0];
414
+ const promptBlockReason = response.promptFeedback?.blockReason;
415
+ const isPromptBlocked =
416
+ candidate?.finishReason == null && promptBlockReason != null;
417
+ const rawFinishReason =
418
+ candidate?.finishReason ?? promptBlockReason ?? undefined;
392
419
  const content: Array<LanguageModelV4Content> = [];
393
420
 
394
421
  // map ordered parts to content:
395
- const parts = candidate.content?.parts ?? [];
422
+ const parts = candidate?.content?.parts ?? [];
396
423
 
397
424
  const usageMetadata = response.usageMetadata;
398
425
 
@@ -517,7 +544,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
517
544
 
518
545
  const sources =
519
546
  extractSources({
520
- groundingMetadata: candidate.groundingMetadata,
547
+ groundingMetadata: candidate?.groundingMetadata,
521
548
  generateId: this.config.generateId,
522
549
  }) ?? [];
523
550
  for (const source of sources) {
@@ -527,24 +554,26 @@ export class GoogleLanguageModel implements LanguageModelV4 {
527
554
  return {
528
555
  content,
529
556
  finishReason: {
530
- unified: mapGoogleFinishReason({
531
- finishReason: candidate.finishReason,
532
- // Only count client-executed tool calls for finish reason determination.
533
- hasToolCalls: content.some(
534
- part => part.type === 'tool-call' && !part.providerExecuted,
535
- ),
536
- }),
537
- raw: candidate.finishReason ?? undefined,
557
+ unified: isPromptBlocked
558
+ ? 'content-filter'
559
+ : mapGoogleFinishReason({
560
+ finishReason: rawFinishReason,
561
+ // Only count client-executed tool calls for finish reason determination.
562
+ hasToolCalls: content.some(
563
+ part => part.type === 'tool-call' && !part.providerExecuted,
564
+ ),
565
+ }),
566
+ raw: rawFinishReason,
538
567
  },
539
568
  usage: convertGoogleUsage(usageMetadata),
540
569
  warnings,
541
570
  providerMetadata: wrapProviderMetadata({
542
571
  promptFeedback: response.promptFeedback ?? null,
543
- groundingMetadata: candidate.groundingMetadata ?? null,
544
- urlContextMetadata: candidate.urlContextMetadata ?? null,
545
- safetyRatings: candidate.safetyRatings ?? null,
572
+ groundingMetadata: candidate?.groundingMetadata ?? null,
573
+ urlContextMetadata: candidate?.urlContextMetadata ?? null,
574
+ safetyRatings: candidate?.safetyRatings ?? null,
546
575
  usageMetadata: usageMetadata ?? null,
547
- finishMessage: candidate.finishMessage ?? null,
576
+ finishMessage: candidate?.finishMessage ?? null,
548
577
  serviceTier: usageMetadata?.serviceTier ?? null,
549
578
  } satisfies GoogleProviderMetadata),
550
579
  response: {
@@ -735,6 +764,22 @@ export class GoogleLanguageModel implements LanguageModelV4 {
735
764
 
736
765
  // sometimes the API returns an empty candidates array
737
766
  if (candidate == null) {
767
+ const promptBlockReason = value.promptFeedback?.blockReason;
768
+ if (promptBlockReason != null) {
769
+ finishReason = {
770
+ unified: 'content-filter',
771
+ raw: promptBlockReason,
772
+ };
773
+ providerMetadata = wrapProviderMetadata({
774
+ promptFeedback: value.promptFeedback ?? null,
775
+ groundingMetadata: lastGroundingMetadata,
776
+ urlContextMetadata: lastUrlContextMetadata,
777
+ safetyRatings: null,
778
+ usageMetadata: usageMetadata ?? null,
779
+ finishMessage: null,
780
+ serviceTier: usage?.serviceTier ?? null,
781
+ } satisfies GoogleProviderMetadata);
782
+ }
738
783
  return;
739
784
  }
740
785
 
@@ -1110,13 +1155,21 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1110
1155
  }
1111
1156
  }
1112
1157
 
1113
- if (candidate.finishReason != null) {
1158
+ const promptBlockReason = value.promptFeedback?.blockReason;
1159
+ const isPromptBlocked =
1160
+ candidate.finishReason == null && promptBlockReason != null;
1161
+ const rawFinishReason =
1162
+ candidate.finishReason ?? promptBlockReason ?? undefined;
1163
+
1164
+ if (rawFinishReason != null) {
1114
1165
  finishReason = {
1115
- unified: mapGoogleFinishReason({
1116
- finishReason: candidate.finishReason,
1117
- hasToolCalls,
1118
- }),
1119
- raw: candidate.finishReason,
1166
+ unified: isPromptBlocked
1167
+ ? 'content-filter'
1168
+ : mapGoogleFinishReason({
1169
+ finishReason: rawFinishReason,
1170
+ hasToolCalls,
1171
+ }),
1172
+ raw: rawFinishReason,
1120
1173
  };
1121
1174
 
1122
1175
  providerMetadata = wrapProviderMetadata({
@@ -1552,26 +1605,33 @@ const getSafetyRatingSchema = () =>
1552
1605
 
1553
1606
  const tokenDetailsSchema = z
1554
1607
  .array(
1555
- z.object({
1556
- modality: z.string(),
1557
- tokenCount: z.number(),
1558
- }),
1608
+ z
1609
+ .object({
1610
+ modality: z.string(),
1611
+ tokenCount: z.number(),
1612
+ })
1613
+ .loose(),
1559
1614
  )
1560
1615
  .nullish();
1561
1616
 
1562
- const usageSchema = z.object({
1563
- cachedContentTokenCount: z.number().nullish(),
1564
- thoughtsTokenCount: z.number().nullish(),
1565
- promptTokenCount: z.number().nullish(),
1566
- candidatesTokenCount: z.number().nullish(),
1567
- totalTokenCount: z.number().nullish(),
1568
- // https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
1569
- trafficType: z.string().nullish(),
1570
- serviceTier: z.string().nullish(),
1571
- // https://ai.google.dev/api/generate-content#Modality
1572
- promptTokensDetails: tokenDetailsSchema,
1573
- candidatesTokensDetails: tokenDetailsSchema,
1574
- });
1617
+ const usageSchema = z
1618
+ .object({
1619
+ cachedContentTokenCount: z.number().nullish(),
1620
+ thoughtsTokenCount: z.number().nullish(),
1621
+ promptTokenCount: z.number().nullish(),
1622
+ candidatesTokenCount: z.number().nullish(),
1623
+ toolUsePromptTokenCount: z.number().nullish(),
1624
+ totalTokenCount: z.number().nullish(),
1625
+ // https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
1626
+ trafficType: z.string().nullish(),
1627
+ serviceTier: z.string().nullish(),
1628
+ // https://ai.google.dev/api/generate-content#Modality
1629
+ promptTokensDetails: tokenDetailsSchema,
1630
+ cacheTokensDetails: tokenDetailsSchema,
1631
+ candidatesTokensDetails: tokenDetailsSchema,
1632
+ toolUsePromptTokensDetails: tokenDetailsSchema,
1633
+ })
1634
+ .loose();
1575
1635
 
1576
1636
  // https://ai.google.dev/api/generate-content#UrlRetrievalMetadata
1577
1637
  export const getUrlContextMetadataSchema = () =>
@@ -1590,16 +1650,18 @@ export const responseSchema = lazySchema(() =>
1590
1650
  zodSchema(
1591
1651
  z.object({
1592
1652
  responseId: z.string().nullish(),
1593
- candidates: z.array(
1594
- z.object({
1595
- content: getContentSchema().nullish().or(z.object({}).strict()),
1596
- finishReason: z.string().nullish(),
1597
- finishMessage: z.string().nullish(),
1598
- safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1599
- groundingMetadata: getGroundingMetadataSchema().nullish(),
1600
- urlContextMetadata: getUrlContextMetadataSchema().nullish(),
1601
- }),
1602
- ),
1653
+ candidates: z
1654
+ .array(
1655
+ z.object({
1656
+ content: getContentSchema().nullish().or(z.object({}).strict()),
1657
+ finishReason: z.string().nullish(),
1658
+ finishMessage: z.string().nullish(),
1659
+ safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1660
+ groundingMetadata: getGroundingMetadataSchema().nullish(),
1661
+ urlContextMetadata: getUrlContextMetadataSchema().nullish(),
1662
+ }),
1663
+ )
1664
+ .nullish(),
1603
1665
  usageMetadata: usageSchema.nullish(),
1604
1666
  promptFeedback: z
1605
1667
  .object({
@@ -1611,16 +1673,20 @@ export const responseSchema = lazySchema(() =>
1611
1673
  ),
1612
1674
  );
1613
1675
 
1676
+ type CandidateSchema = NonNullable<
1677
+ InferSchema<typeof responseSchema>['candidates']
1678
+ >[number];
1679
+
1614
1680
  export type GroundingMetadataSchema = NonNullable<
1615
- InferSchema<typeof responseSchema>['candidates'][number]['groundingMetadata']
1681
+ CandidateSchema['groundingMetadata']
1616
1682
  >;
1617
1683
 
1618
1684
  export type UrlContextMetadataSchema = NonNullable<
1619
- InferSchema<typeof responseSchema>['candidates'][number]['urlContextMetadata']
1685
+ CandidateSchema['urlContextMetadata']
1620
1686
  >;
1621
1687
 
1622
1688
  export type SafetyRatingSchema = NonNullable<
1623
- InferSchema<typeof responseSchema>['candidates'][number]['safetyRatings']
1689
+ CandidateSchema['safetyRatings']
1624
1690
  >[number];
1625
1691
 
1626
1692
  export type PromptFeedbackSchema = NonNullable<