@ai-sdk/google 4.0.101 → 4.0.103

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.
@@ -878,7 +878,7 @@ import { generateText } from 'ai';
878
878
  const { text, sources, providerMetadata } = await generateText({
879
879
  model: google('gemini-4-argon'),
880
880
  prompt: `Based on the document: https://ai.google.dev/gemini-api/docs/url-context.
881
- Answer this question: How many links we can consume in one request?`,
881
+ Answer this question: How many links can we consume in one request?`,
882
882
  tools: {
883
883
  url_context: google.tools.urlContext({}),
884
884
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.101",
3
+ "version": "4.0.103",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.31",
39
- "@ai-sdk/provider-utils": "5.0.64"
38
+ "@ai-sdk/provider": "4.0.32",
39
+ "@ai-sdk/provider-utils": "5.0.66"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -1,5 +1,9 @@
1
1
  import type { ParseResult } from '@ai-sdk/provider-utils';
2
2
 
3
+ export type GoogleStreamPart<T> =
4
+ | ParseResult<T>
5
+ | { type: 'continuation-start' };
6
+
3
7
  /**
4
8
  * Concatenates the event streams of a long decode response that spans several
5
9
  * requests (Gemini 4 long decode).
@@ -7,7 +11,9 @@ import type { ParseResult } from '@ai-sdk/provider-utils';
7
11
  * Events of the current stream are passed through unchanged. When an event
8
12
  * signals that the per-request cap was reached while budget remains
9
13
  * (`getContinuationToken` returns a token), the next request is sent with that
10
- * token once the current stream ends, and its events are appended. If the
14
+ * token once the current stream ends, and its events are appended after a
15
+ * `continuation-start` marker. The marker orders per-response state changes
16
+ * with buffered events, including when the new stream is empty. If the
11
17
  * continuation request fails, the error is emitted as a failed parse result
12
18
  * and the stream ends.
13
19
  */
@@ -21,11 +27,11 @@ export function continueGoogleStream<T>({
21
27
  continueStream: (
22
28
  continuationToken: string,
23
29
  ) => Promise<ReadableStream<ParseResult<T>>>;
24
- }): ReadableStream<ParseResult<T>> {
30
+ }): ReadableStream<GoogleStreamPart<T>> {
25
31
  let reader = stream.getReader();
26
32
  let pendingContinuationToken: string | undefined;
27
33
 
28
- return new ReadableStream<ParseResult<T>>({
34
+ return new ReadableStream<GoogleStreamPart<T>>({
29
35
  async pull(controller) {
30
36
  while (true) {
31
37
  const { done, value } = await reader.read();
@@ -51,6 +57,8 @@ export function continueGoogleStream<T>({
51
57
 
52
58
  try {
53
59
  reader = (await continueStream(continuationToken)).getReader();
60
+ controller.enqueue({ type: 'continuation-start' });
61
+ return;
54
62
  } catch (error) {
55
63
  // Surfaced as an `error` stream part, followed by the `finish` part
56
64
  // with the last continuation token so the caller can resume.
@@ -78,35 +78,38 @@ function assertSupportedBatchRequests(
78
78
  }
79
79
  }
80
80
 
81
- const googleRpcStatusSchema = z.object({
82
- code: z.union([z.number(), z.string()]).nullish(),
83
- message: z.string().nullish(),
84
- status: z.string().nullish(),
85
- });
86
-
87
- const googleBatchStatsSchema = z.object({
88
- requestCount: z.union([z.string(), z.number()]).nullish(),
89
- successfulRequestCount: z.union([z.string(), z.number()]).nullish(),
90
- failedRequestCount: z.union([z.string(), z.number()]).nullish(),
91
- pendingRequestCount: z.union([z.string(), z.number()]).nullish(),
92
- });
93
-
94
- const googleBatchOutputSchema = z.object({
95
- responsesFile: z.string().nullish(),
96
- inlinedResponses: z
97
- .object({
98
- inlinedResponses: z.array(
99
- z.object({
100
- metadata: z.object({
101
- key: z.string(),
81
+ const googleRpcStatusSchema = () =>
82
+ z.object({
83
+ code: z.union([z.number(), z.string()]).nullish(),
84
+ message: z.string().nullish(),
85
+ status: z.string().nullish(),
86
+ });
87
+
88
+ const googleBatchStatsSchema = () =>
89
+ z.object({
90
+ requestCount: z.union([z.string(), z.number()]).nullish(),
91
+ successfulRequestCount: z.union([z.string(), z.number()]).nullish(),
92
+ failedRequestCount: z.union([z.string(), z.number()]).nullish(),
93
+ pendingRequestCount: z.union([z.string(), z.number()]).nullish(),
94
+ });
95
+
96
+ const googleBatchOutputSchema = () =>
97
+ z.object({
98
+ responsesFile: z.string().nullish(),
99
+ inlinedResponses: z
100
+ .object({
101
+ inlinedResponses: z.array(
102
+ z.object({
103
+ metadata: z.object({
104
+ key: z.string(),
105
+ }),
106
+ response: z.unknown().nullish(),
107
+ error: googleRpcStatusSchema().nullish(),
102
108
  }),
103
- response: z.unknown().nullish(),
104
- error: googleRpcStatusSchema.nullish(),
105
- }),
106
- ),
107
- })
108
- .nullish(),
109
- });
109
+ ),
110
+ })
111
+ .nullish(),
112
+ });
110
113
 
111
114
  const googleBatchOperationZodSchema = () =>
112
115
  z.object({
@@ -115,13 +118,13 @@ const googleBatchOperationZodSchema = () =>
115
118
  .object({
116
119
  state: z.string().nullish(),
117
120
  createTime: z.string().nullish(),
118
- batchStats: googleBatchStatsSchema.nullish(),
119
- output: googleBatchOutputSchema.nullish(),
121
+ batchStats: googleBatchStatsSchema().nullish(),
122
+ output: googleBatchOutputSchema().nullish(),
120
123
  })
121
124
  .nullish(),
122
125
  done: z.boolean().nullish(),
123
- error: googleRpcStatusSchema.nullish(),
124
- response: googleBatchOutputSchema.nullish(),
126
+ error: googleRpcStatusSchema().nullish(),
127
+ response: googleBatchOutputSchema().nullish(),
125
128
  });
126
129
 
127
130
  const googleBatchOperationSchema = lazySchema(() =>
@@ -159,7 +162,7 @@ const googleBatchResultLineSchema = lazySchema(() =>
159
162
  z.object({
160
163
  key: z.string(),
161
164
  response: z.unknown().nullish(),
162
- error: googleRpcStatusSchema.nullish(),
165
+ error: googleRpcStatusSchema().nullish(),
163
166
  }),
164
167
  ),
165
168
  );
@@ -872,7 +875,7 @@ function parseCount(value: string | number | null | undefined) {
872
875
  }
873
876
 
874
877
  function convertGoogleRpcError(
875
- error: InferSchema<typeof googleRpcStatusSchema>,
878
+ error: InferSchema<ReturnType<typeof googleRpcStatusSchema>>,
876
879
  fallbackMessage: string,
877
880
  ): BatchV4Error {
878
881
  return {
@@ -11,21 +11,22 @@ export type GoogleEmbeddingModelId =
11
11
  | 'gemini-embedding-2-preview'
12
12
  | (string & {});
13
13
 
14
- const googleEmbeddingContentPartSchema = z.union([
15
- z.object({ text: z.string() }),
16
- z.object({
17
- inlineData: z.object({
18
- mimeType: z.string(),
19
- data: z.string(),
14
+ const googleEmbeddingContentPartSchema = () =>
15
+ z.union([
16
+ z.object({ text: z.string() }),
17
+ z.object({
18
+ inlineData: z.object({
19
+ mimeType: z.string(),
20
+ data: z.string(),
21
+ }),
20
22
  }),
21
- }),
22
- z.object({
23
- fileData: z.object({
24
- fileUri: z.string(),
25
- mimeType: z.string(),
23
+ z.object({
24
+ fileData: z.object({
25
+ fileUri: z.string(),
26
+ mimeType: z.string(),
27
+ }),
26
28
  }),
27
- }),
28
- ]);
29
+ ]);
29
30
 
30
31
  export const googleEmbeddingModelOptions = lazySchema(() =>
31
32
  zodSchema(
@@ -71,7 +72,7 @@ export const googleEmbeddingModelOptions = lazySchema(() =>
71
72
  * the case of a single embedding, the array length must be 1.
72
73
  */
73
74
  content: z
74
- .array(z.array(googleEmbeddingContentPartSchema).min(1).nullable())
75
+ .array(z.array(googleEmbeddingContentPartSchema()).min(1).nullable())
75
76
  .optional(),
76
77
  }),
77
78
  ),
@@ -13,6 +13,7 @@ import {
13
13
  lazySchema,
14
14
  parseProviderOptions,
15
15
  zodSchema,
16
+ encodePathSegment,
16
17
  getFromApi,
17
18
  type FetchFunction,
18
19
  } from '@ai-sdk/provider-utils';
@@ -34,17 +35,6 @@ interface GoogleFilesConfig {
34
35
  fetch?: FetchFunction;
35
36
  }
36
37
 
37
- function encodePathSegment(value: string): string {
38
- const encodedValue = encodeURIComponent(value);
39
-
40
- // URL parsing normalizes both literal and percent-encoded dot segments.
41
- return encodedValue === '.'
42
- ? '%252E'
43
- : encodedValue === '..'
44
- ? '%252E%252E'
45
- : encodedValue;
46
- }
47
-
48
38
  export class GoogleFiles implements FilesV4 {
49
39
  readonly specificationVersion = 'v4';
50
40
 
@@ -18,7 +18,7 @@ export const googleImageModelOptionsSchema = lazySchema(() =>
18
18
  * dedicated escape hatch for grounding image generation the same way
19
19
  * `generateText` does.
20
20
  */
21
- googleSearch: googleSearchToolArgsBaseSchema.optional(),
21
+ googleSearch: googleSearchToolArgsBaseSchema().optional(),
22
22
  }),
23
23
  ),
24
24
  );
@@ -33,11 +33,13 @@ import {
33
33
  zodSchema,
34
34
  type FetchFunction,
35
35
  type InferSchema,
36
- type ParseResult,
37
36
  type Resolvable,
38
37
  } from '@ai-sdk/provider-utils';
39
38
  import { z } from 'zod/v4';
40
- import { continueGoogleStream } from './continue-google-stream';
39
+ import {
40
+ continueGoogleStream,
41
+ type GoogleStreamPart,
42
+ } from './continue-google-stream';
41
43
  import {
42
44
  addGoogleUsageMetadata,
43
45
  convertGoogleUsage,
@@ -926,8 +928,9 @@ export class GoogleLanguageModel implements LanguageModelV4 {
926
928
  },
927
929
  });
928
930
 
931
+ // A clean end without the provider's completion signal is interrupted.
929
932
  let finishReason: LanguageModelV4FinishReason = {
930
- unified: 'other',
933
+ unified: 'interrupted',
931
934
  raw: undefined,
932
935
  };
933
936
  let usage: GoogleUsageMetadata | undefined = undefined;
@@ -1005,7 +1008,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1005
1008
  const result = {
1006
1009
  stream: response.pipeThrough(
1007
1010
  new TransformStream<
1008
- ParseResult<ChunkSchema>,
1011
+ GoogleStreamPart<ChunkSchema>,
1009
1012
  LanguageModelV4StreamPart
1010
1013
  >({
1011
1014
  start(controller) {
@@ -1013,11 +1016,21 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1013
1016
  },
1014
1017
 
1015
1018
  transform(chunk, controller) {
1019
+ if ('type' in chunk) {
1020
+ // Reset in stream order, after the prior response's buffered
1021
+ // CONTINUATION chunk, rather than when the request starts.
1022
+ finishReason = { unified: 'interrupted', raw: undefined };
1023
+ return;
1024
+ }
1025
+
1016
1026
  if (options.includeRawChunks) {
1017
1027
  controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });
1018
1028
  }
1019
1029
 
1020
1030
  if (!chunk.success) {
1031
+ if (finishReason.unified === 'interrupted') {
1032
+ finishReason = { unified: 'error', raw: undefined };
1033
+ }
1021
1034
  controller.enqueue({ type: 'error', error: chunk.error });
1022
1035
  return;
1023
1036
  }
@@ -1889,14 +1902,15 @@ export const getGroundingMetadataSchema = () =>
1889
1902
  .nullish(),
1890
1903
  });
1891
1904
 
1892
- const partialArgSchema = z.object({
1893
- jsonPath: z.string(),
1894
- stringValue: z.string().nullish(),
1895
- numberValue: z.number().nullish(),
1896
- boolValue: z.boolean().nullish(),
1897
- nullValue: z.unknown().nullish(),
1898
- willContinue: z.boolean().nullish(),
1899
- });
1905
+ const partialArgSchema = () =>
1906
+ z.object({
1907
+ jsonPath: z.string(),
1908
+ stringValue: z.string().nullish(),
1909
+ numberValue: z.number().nullish(),
1910
+ boolValue: z.boolean().nullish(),
1911
+ nullValue: z.unknown().nullish(),
1912
+ willContinue: z.boolean().nullish(),
1913
+ });
1900
1914
 
1901
1915
  const getContentSchema = () =>
1902
1916
  z.object({
@@ -1909,7 +1923,7 @@ const getContentSchema = () =>
1909
1923
  id: z.string().nullish(),
1910
1924
  name: z.string().nullish(),
1911
1925
  args: z.unknown().nullish(),
1912
- partialArgs: z.array(partialArgSchema).nullish(),
1926
+ partialArgs: z.array(partialArgSchema()).nullish(),
1913
1927
  willContinue: z.boolean().nullish(),
1914
1928
  }),
1915
1929
  thoughtSignature: z.string().nullish(),
@@ -1971,35 +1985,37 @@ const getSafetyRatingSchema = () =>
1971
1985
  blocked: z.boolean().nullish(),
1972
1986
  });
1973
1987
 
1974
- const tokenDetailsSchema = z
1975
- .array(
1976
- z
1977
- .object({
1978
- modality: z.string(),
1979
- tokenCount: z.number(),
1980
- })
1981
- .loose(),
1982
- )
1983
- .nullish();
1984
-
1985
- const usageSchema = z
1986
- .object({
1987
- cachedContentTokenCount: z.number().nullish(),
1988
- thoughtsTokenCount: z.number().nullish(),
1989
- promptTokenCount: z.number().nullish(),
1990
- candidatesTokenCount: z.number().nullish(),
1991
- toolUsePromptTokenCount: z.number().nullish(),
1992
- totalTokenCount: z.number().nullish(),
1993
- // https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
1994
- trafficType: z.string().nullish(),
1995
- serviceTier: z.string().nullish(),
1996
- // https://ai.google.dev/api/generate-content#Modality
1997
- promptTokensDetails: tokenDetailsSchema,
1998
- cacheTokensDetails: tokenDetailsSchema,
1999
- candidatesTokensDetails: tokenDetailsSchema,
2000
- toolUsePromptTokensDetails: tokenDetailsSchema,
2001
- })
2002
- .loose();
1988
+ const tokenDetailsSchema = () =>
1989
+ z
1990
+ .array(
1991
+ z
1992
+ .object({
1993
+ modality: z.string(),
1994
+ tokenCount: z.number(),
1995
+ })
1996
+ .loose(),
1997
+ )
1998
+ .nullish();
1999
+
2000
+ const usageSchema = () =>
2001
+ z
2002
+ .object({
2003
+ cachedContentTokenCount: z.number().nullish(),
2004
+ thoughtsTokenCount: z.number().nullish(),
2005
+ promptTokenCount: z.number().nullish(),
2006
+ candidatesTokenCount: z.number().nullish(),
2007
+ toolUsePromptTokenCount: z.number().nullish(),
2008
+ totalTokenCount: z.number().nullish(),
2009
+ // https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest/v1/GenerateContentResponse#TrafficType
2010
+ trafficType: z.string().nullish(),
2011
+ serviceTier: z.string().nullish(),
2012
+ // https://ai.google.dev/api/generate-content#Modality
2013
+ promptTokensDetails: tokenDetailsSchema(),
2014
+ cacheTokensDetails: tokenDetailsSchema(),
2015
+ candidatesTokensDetails: tokenDetailsSchema(),
2016
+ toolUsePromptTokensDetails: tokenDetailsSchema(),
2017
+ })
2018
+ .loose();
2003
2019
 
2004
2020
  // https://ai.google.dev/api/generate-content#UrlRetrievalMetadata
2005
2021
  export const getUrlContextMetadataSchema = () =>
@@ -2031,7 +2047,7 @@ export const responseSchema = lazySchema(() =>
2031
2047
  }),
2032
2048
  )
2033
2049
  .nullish(),
2034
- usageMetadata: usageSchema.nullish(),
2050
+ usageMetadata: usageSchema().nullish(),
2035
2051
  promptFeedback: z
2036
2052
  .object({
2037
2053
  blockReason: z.string().nullish(),
@@ -2086,7 +2102,7 @@ const chunkSchema = lazySchema(() =>
2086
2102
  }),
2087
2103
  )
2088
2104
  .nullish(),
2089
- usageMetadata: usageSchema.nullish(),
2105
+ usageMetadata: usageSchema().nullish(),
2090
2106
  promptFeedback: z
2091
2107
  .object({
2092
2108
  blockReason: z.string().nullish(),
@@ -13,25 +13,28 @@ export type GoogleSpeechModelId =
13
13
  | 'gemini-3.8-flash-lite-tts'
14
14
  | (string & {});
15
15
 
16
- const prebuiltVoiceConfigSchema = z.object({
17
- voiceName: z.string(),
18
- });
16
+ const prebuiltVoiceConfigSchema = () =>
17
+ z.object({
18
+ voiceName: z.string(),
19
+ });
19
20
 
20
- const voiceConfigSchema = z.object({
21
- prebuiltVoiceConfig: prebuiltVoiceConfigSchema,
22
- voice: z.never().optional(),
23
- });
21
+ const voiceConfigSchema = () =>
22
+ z.object({
23
+ prebuiltVoiceConfig: prebuiltVoiceConfigSchema(),
24
+ voice: z.never().optional(),
25
+ });
24
26
 
25
- const speechMetadataSchema = z.object({
26
- speaker: z.string().min(1).optional(),
27
- style: z.string().optional(),
28
- });
27
+ const speechMetadataSchema = () =>
28
+ z.object({
29
+ speaker: z.string().min(1).optional(),
30
+ style: z.string().optional(),
31
+ });
29
32
 
30
33
  export const googleSpeechProviderOptionsSchema = lazySchema(() =>
31
34
  zodSchema(
32
35
  z.object({
33
36
  /** Turn-level directions for the top-level text, for Gemini 3.8 TTS. */
34
- speechMetadata: speechMetadataSchema.optional(),
37
+ speechMetadata: speechMetadataSchema().optional(),
35
38
 
36
39
  /**
37
40
  * Structured transcript for Gemini 3.8 TTS. Replaces the top-level text;
@@ -42,7 +45,7 @@ export const googleSpeechProviderOptionsSchema = lazySchema(() =>
42
45
  .array(
43
46
  z.object({
44
47
  text: z.string(),
45
- speechMetadata: speechMetadataSchema.optional(),
48
+ speechMetadata: speechMetadataSchema().optional(),
46
49
  }),
47
50
  )
48
51
  .min(1)
@@ -61,7 +64,7 @@ export const googleSpeechProviderOptionsSchema = lazySchema(() =>
61
64
  speakerVoiceConfigs: z.array(
62
65
  z.object({
63
66
  speaker: z.string(),
64
- voiceConfig: voiceConfigSchema,
67
+ voiceConfig: voiceConfigSchema(),
65
68
  }),
66
69
  ),
67
70
  })
@@ -9,6 +9,9 @@ import {
9
9
  type SharedV4Warning,
10
10
  } from '@ai-sdk/provider';
11
11
  import {
12
+ lazySchema,
13
+ zodSchema,
14
+ type InferSchema,
12
15
  combineHeaders,
13
16
  convertUint8ArrayToBase64,
14
17
  createJsonResponseHandler,
@@ -265,7 +268,7 @@ export class GoogleVideoModel implements VideoModelV4 {
265
268
  }
266
269
 
267
270
  private async buildCompletedResult(
268
- finalOperation: z.infer<typeof googleOperationSchema>,
271
+ finalOperation: InferSchema<typeof googleOperationSchema>,
269
272
  responseHeaders: Record<string, string> | undefined,
270
273
  warnings: SharedV4Warning[],
271
274
  currentDate: Date,
@@ -441,33 +444,37 @@ export class GoogleVideoModel implements VideoModelV4 {
441
444
  }
442
445
  }
443
446
 
444
- const googleOperationSchema = z.object({
445
- name: z.string().nullish(),
446
- done: z.boolean().nullish(),
447
- error: z
448
- .object({
449
- code: z.number().nullish(),
450
- message: z.string(),
451
- status: z.string().nullish(),
452
- })
453
- .nullish(),
454
- response: z
455
- .object({
456
- generateVideoResponse: z
447
+ const googleOperationSchema = lazySchema(() =>
448
+ zodSchema(
449
+ z.object({
450
+ name: z.string().nullish(),
451
+ done: z.boolean().nullish(),
452
+ error: z
457
453
  .object({
458
- generatedSamples: z
459
- .array(
460
- z.object({
461
- video: z
462
- .object({
463
- uri: z.string().nullish(),
464
- })
465
- .nullish(),
466
- }),
467
- )
454
+ code: z.number().nullish(),
455
+ message: z.string(),
456
+ status: z.string().nullish(),
457
+ })
458
+ .nullish(),
459
+ response: z
460
+ .object({
461
+ generateVideoResponse: z
462
+ .object({
463
+ generatedSamples: z
464
+ .array(
465
+ z.object({
466
+ video: z
467
+ .object({
468
+ uri: z.string().nullish(),
469
+ })
470
+ .nullish(),
471
+ }),
472
+ )
473
+ .nullish(),
474
+ })
468
475
  .nullish(),
469
476
  })
470
477
  .nullish(),
471
- })
472
- .nullish(),
473
- });
478
+ }),
479
+ ),
480
+ );
@@ -152,6 +152,7 @@ export function buildGoogleInteractionsStreamTransform({
152
152
  let serviceTier: string | undefined = headerServiceTier;
153
153
  let finishStatus: GoogleInteractionsStatus | string | undefined;
154
154
  let hasFunctionCall = false;
155
+ let hasCompletionEvent = false;
155
156
 
156
157
  /*
157
158
  * Per-index open step slots. The Interactions API frames concurrent steps
@@ -838,6 +839,7 @@ export function buildGoogleInteractionsStreamTransform({
838
839
  }
839
840
 
840
841
  case 'interaction.completed': {
842
+ hasCompletionEvent = true;
841
843
  const event = value as Extract<
842
844
  GoogleInteractionsEvent,
843
845
  { event_type: 'interaction.completed' }
@@ -893,12 +895,17 @@ export function buildGoogleInteractionsStreamTransform({
893
895
  },
894
896
 
895
897
  flush(controller) {
898
+ const isInterrupted =
899
+ !hasCompletionEvent &&
900
+ (finishStatus == null || finishStatus === 'in_progress');
896
901
  const finishReason: LanguageModelV4FinishReason = {
897
- unified: mapGoogleInteractionsFinishReason({
898
- status: finishStatus,
899
- hasFunctionCall,
900
- }),
901
- raw: finishStatus,
902
+ unified: isInterrupted
903
+ ? 'interrupted'
904
+ : mapGoogleInteractionsFinishReason({
905
+ status: finishStatus,
906
+ hasFunctionCall,
907
+ }),
908
+ raw: isInterrupted ? undefined : finishStatus,
902
909
  };
903
910
 
904
911
  const outputTokensByModality =