@ai-sdk/google 4.0.112 → 4.0.114

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.
@@ -1594,6 +1594,14 @@ await generateText({
1594
1594
 
1595
1595
  The following optional provider options are available:
1596
1596
 
1597
+ - **labels** _Record<string, string>_
1598
+
1599
+ User-defined metadata sent in the top-level `labels` field of the Interactions
1600
+ request. For example, set `{ safety_identifier: 'opaque_user_123' }` to attach
1601
+ an opaque end-user identifier. Other labels are preserved unchanged.
1602
+ Vertex AI Interactions models also read labels from `providerOptions.google`,
1603
+ not `providerOptions.googleVertex` or `providerOptions.vertex`.
1604
+
1597
1605
  - **previousInteractionId** _string_
1598
1606
 
1599
1607
  Server-side interaction id from a prior turn. When set, the server pulls
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.112",
3
+ "version": "4.0.114",
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.36",
39
- "@ai-sdk/provider-utils": "5.0.75"
38
+ "@ai-sdk/provider": "4.0.37",
39
+ "@ai-sdk/provider-utils": "5.0.76"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -61,6 +61,7 @@ export class GoogleJSONAccumulator {
61
61
  if (!rawPath || rawPath === '$') continue;
62
62
 
63
63
  const segments = parsePath(rawPath);
64
+ if (!hasContiguousIndexes(this.accumulatedArgs, segments)) continue;
64
65
 
65
66
  const existingValue = getNestedValue(this.accumulatedArgs, segments);
66
67
  const isStringContinuation =
@@ -386,6 +387,39 @@ function getNestedValue(
386
387
  return current;
387
388
  }
388
389
 
390
+ /**
391
+ * Checks that every array index on the path is an existing element or the next
392
+ * one. Arrays are streamed in order, and a sparse index would make `finalize()`
393
+ * serialize one `null` per hole, so a tiny response could allocate without bound.
394
+ * A missing or `null` parent is replaced with a new array, so it starts at 0.
395
+ *
396
+ * Input: ({items:["a"]}, ["items",1]) or ({items:["a"]}, ["items",5])
397
+ * Output: true or false
398
+ */
399
+ function hasContiguousIndexes(
400
+ obj: Record<string, unknown>,
401
+ segments: Array<string | number>,
402
+ ): boolean {
403
+ let current: unknown = obj;
404
+ for (const pathSegment of segments) {
405
+ if (typeof pathSegment === 'number') {
406
+ const length = Array.isArray(current)
407
+ ? current.length
408
+ : current == null
409
+ ? 0
410
+ : undefined;
411
+ if (length !== undefined && pathSegment > length) return false;
412
+ }
413
+ current =
414
+ current != null &&
415
+ typeof current === 'object' &&
416
+ hasOwnProperty(current as Record<string | number, unknown>, pathSegment)
417
+ ? (current as Record<string | number, unknown>)[pathSegment]
418
+ : undefined;
419
+ }
420
+ return true;
421
+ }
422
+
389
423
  /**
390
424
  * Sets a value at a nested path, creating intermediate objects or arrays as needed.
391
425
  *
@@ -24,9 +24,9 @@ import {
24
24
  lazySchema,
25
25
  mapReasoningToProviderBudget,
26
26
  mapReasoningToProviderEffort,
27
- parseProviderOptions,
28
27
  postJsonToApi,
29
28
  resolve,
29
+ resolveProviderOptions,
30
30
  serializeModelOptions,
31
31
  WORKFLOW_SERIALIZE,
32
32
  WORKFLOW_DESERIALIZE,
@@ -179,25 +179,15 @@ export class GoogleLanguageModel implements LanguageModelV4 {
179
179
  ? (['googleVertex', 'vertex'] as const)
180
180
  : (['google'] as const);
181
181
 
182
- let googleOptions: GoogleLanguageModelOptions | undefined;
183
- for (const name of providerOptionsNames) {
184
- googleOptions = await parseProviderOptions({
185
- provider: name,
182
+ // The first present namespace wins as a whole. A Vertex provider also
183
+ // falls back to the `google` key (e.g. via the AI Gateway).
184
+ const { options: googleOptions } =
185
+ await resolveProviderOptions<GoogleLanguageModelOptions>({
186
186
  providerOptions,
187
+ namespaces: [...providerOptionsNames, 'google'],
188
+ resolution: 'first',
187
189
  schema: googleLanguageModelOptions,
188
190
  });
189
- if (googleOptions != null) break;
190
- }
191
-
192
- // Cross-namespace fallback: a Vertex provider may receive options under
193
- // the `google` key (e.g. via the AI Gateway).
194
- if (googleOptions == null && !providerOptionsNames.includes('google')) {
195
- googleOptions = await parseProviderOptions({
196
- provider: 'google',
197
- providerOptions,
198
- schema: googleLanguageModelOptions,
199
- });
200
- }
201
191
 
202
192
  // Add warning if Vertex rag tools are used with a non-Vertex Google provider
203
193
  const isVertexProvider = config.provider.startsWith('google.vertex.');
@@ -468,7 +458,13 @@ export class GoogleLanguageModel implements LanguageModelV4 {
468
458
  options: LanguageModelV4CallOptions,
469
459
  { isStreaming = false }: { isStreaming?: boolean } = {},
470
460
  ) {
471
- const { responseFormat, tools, toolChoice } = options;
461
+ const { responseFormat, toolChoice } = options;
462
+ // Enforce the caller's tool choice before the JSON response fallback can
463
+ // replace it with a forced call to the synthetic response tool.
464
+ const tools =
465
+ toolChoice?.type === 'none'
466
+ ? options.tools?.filter(tool => tool.type === 'function')
467
+ : options.tools;
472
468
  const entry = getGoogleLanguageModelEntry(this.modelId);
473
469
  const { structuredOutput } = entry.capabilities.language;
474
470
  let jsonResponseTool: LanguageModelV4FunctionTool | undefined;
@@ -48,6 +48,13 @@ export function prepareTools({
48
48
  };
49
49
  toolWarnings: SharedV4Warning[];
50
50
  } {
51
+ // NONE only disables function calling in the Google API. Provider tools
52
+ // must be omitted so they cannot execute server-side. Keep function
53
+ // declarations for conversation history containing function calls.
54
+ if (toolChoice?.type === 'none') {
55
+ tools = tools?.filter(tool => tool.type === 'function');
56
+ }
57
+
51
58
  // when the tools array is empty, change it to undefined to prevent errors:
52
59
  tools = tools?.length ? tools : undefined;
53
60
 
@@ -76,6 +76,8 @@ export type GoogleInteractionsVideoOptions = {
76
76
  export const googleInteractionsLanguageModelOptions = lazySchema(() =>
77
77
  zodSchema(
78
78
  z.object({
79
+ /** User-defined request metadata, including `safety_identifier`. */
80
+ labels: z.record(z.string(), z.string()).nullish(),
79
81
  previousInteractionId: z.string().nullish(),
80
82
  store: z.boolean().nullish(),
81
83
 
@@ -447,6 +447,7 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
447
447
  const args: GoogleInteractionsRequestBody = pruneUndefined({
448
448
  ...(isAgent ? { agent: this.agent } : { model: this.modelId }),
449
449
  input,
450
+ labels: googleOptions?.labels ?? undefined,
450
451
  system_instruction: systemInstruction,
451
452
  tools: toolsForBody,
452
453
  response_format:
@@ -577,6 +577,7 @@ export type GoogleInteractionsRequestBody = {
577
577
  model?: string;
578
578
  agent?: string;
579
579
  input: GoogleInteractionsInput;
580
+ labels?: Record<string, string>;
580
581
  system_instruction?: string;
581
582
  tools?: Array<GoogleInteractionsTool>;
582
583
  response_format?: Array<GoogleInteractionsResponseFormatEntry>;
@@ -9,3 +9,8 @@ export {
9
9
  } from '../interactions/google-interactions-language-model';
10
10
  export type { GoogleInteractionsModelId } from '../interactions/google-interactions-language-model-options';
11
11
  export type { GoogleInteractionsAgentName } from '../interactions/google-interactions-agent';
12
+
13
+ export {
14
+ getGoogleLanguageModelEntry,
15
+ googleProviderOptions,
16
+ } from '../google-model-catalog';