@ai-sdk/google 3.0.142 → 3.0.144
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 +19 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +98 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -13
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +97 -12
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +97 -12
- package/dist/internal/index.mjs.map +1 -1
- package/docs/15-google-generative-ai.mdx +18 -0
- package/package.json +2 -2
- package/src/convert-json-schema-to-openapi-schema.ts +85 -0
- package/src/google-generative-ai-language-model.ts +8 -2
- package/src/google-json-accumulator.ts +49 -0
- package/src/interactions/google-interactions-language-model-options.ts +2 -0
- package/src/interactions/google-interactions-language-model.ts +1 -0
- package/src/interactions/google-interactions-prompt.ts +1 -0
|
@@ -1210,6 +1210,16 @@ The errors that you get in this case look like this:
|
|
|
1210
1210
|
`GenerateContentRequest.generation_config.response_schema.properties[occupation].type: must be specified`
|
|
1211
1211
|
|
|
1212
1212
|
By default, structured outputs are enabled (and for tool calling they are required).
|
|
1213
|
+
|
|
1214
|
+
The Google provider bounds schema conversion to prevent excessive resource use
|
|
1215
|
+
when expanding local references. Input inspection and conversion each have a
|
|
1216
|
+
separate budget of 10,000 visited JSON values and a conservative
|
|
1217
|
+
1,000,000-character serialized-size allowance, with a depth limit of 100.
|
|
1218
|
+
String-size accounting reserves six characters per UTF-16 code unit for JSON
|
|
1219
|
+
escaping. Schemas that exceed these limits throw `UnsupportedFunctionalityError`
|
|
1220
|
+
before a model request is sent. Reduce the schema's size, nesting, or repeated
|
|
1221
|
+
references to stay within these limits.
|
|
1222
|
+
|
|
1213
1223
|
You can disable structured outputs for object generation as a workaround:
|
|
1214
1224
|
|
|
1215
1225
|
```ts highlight="3,8"
|
|
@@ -1334,6 +1344,14 @@ await generateText({
|
|
|
1334
1344
|
|
|
1335
1345
|
The following optional provider options are available:
|
|
1336
1346
|
|
|
1347
|
+
- **labels** _Record<string, string>_
|
|
1348
|
+
|
|
1349
|
+
User-defined metadata sent in the top-level `labels` field of the Interactions
|
|
1350
|
+
request. For example, set `{ safety_identifier: 'opaque_user_123' }` to attach
|
|
1351
|
+
an opaque end-user identifier. Other labels are preserved unchanged.
|
|
1352
|
+
Vertex AI Interactions models also read labels from `providerOptions.google`,
|
|
1353
|
+
not `providerOptions.googleVertex` or `providerOptions.vertex`.
|
|
1354
|
+
|
|
1337
1355
|
- **previousInteractionId** _string_
|
|
1338
1356
|
|
|
1339
1357
|
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": "3.0.
|
|
3
|
+
"version": "3.0.144",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@ai-sdk/provider": "3.0.19",
|
|
40
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
40
|
+
"@ai-sdk/provider-utils": "4.0.66"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "20.17.24",
|
|
@@ -12,8 +12,14 @@ type ReferenceContext = {
|
|
|
12
12
|
definitions: Record<string, JSONSchema7Definition> | undefined;
|
|
13
13
|
dollarDefinitions: Record<string, JSONSchema7Definition> | undefined;
|
|
14
14
|
resolvingReferences: ReadonlySet<string>;
|
|
15
|
+
budget: { nodes: number; size: number };
|
|
16
|
+
depth: number;
|
|
15
17
|
};
|
|
16
18
|
|
|
19
|
+
const maxSchemaDepth = 100;
|
|
20
|
+
const maxSchemaNodes = 10_000;
|
|
21
|
+
const maxSchemaSize = 1_000_000;
|
|
22
|
+
|
|
17
23
|
const recursiveReferenceFunctionalityPrefix =
|
|
18
24
|
'recursive JSON Schema reference:';
|
|
19
25
|
|
|
@@ -33,6 +39,12 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
33
39
|
jsonSchema: JSONSchema7Definition | undefined,
|
|
34
40
|
isRoot = true,
|
|
35
41
|
): unknown {
|
|
42
|
+
// Bound the input too, including metadata copied while resolving references.
|
|
43
|
+
consumeSchemaBudget(jsonSchema, {
|
|
44
|
+
nodes: maxSchemaNodes,
|
|
45
|
+
size: maxSchemaSize,
|
|
46
|
+
});
|
|
47
|
+
|
|
36
48
|
const rootSchema =
|
|
37
49
|
typeof jsonSchema === 'object'
|
|
38
50
|
? (jsonSchema as JSONSchema7WithDefinitions)
|
|
@@ -42,6 +54,8 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
42
54
|
definitions: rootSchema?.definitions,
|
|
43
55
|
dollarDefinitions: rootSchema?.$defs,
|
|
44
56
|
resolvingReferences: new Set(),
|
|
57
|
+
budget: { nodes: maxSchemaNodes, size: maxSchemaSize },
|
|
58
|
+
depth: 0,
|
|
45
59
|
});
|
|
46
60
|
}
|
|
47
61
|
|
|
@@ -54,10 +68,34 @@ function convertJSONSchemaDefinition(
|
|
|
54
68
|
return undefined;
|
|
55
69
|
}
|
|
56
70
|
|
|
71
|
+
// Every visit is charged, even when the same definition was already expanded.
|
|
72
|
+
// Reserve space for emitted schema keywords and container punctuation.
|
|
73
|
+
referenceContext.budget.nodes--;
|
|
74
|
+
referenceContext.budget.size -= 128;
|
|
75
|
+
checkSchemaBudget(referenceContext.budget, referenceContext.depth);
|
|
76
|
+
referenceContext = {
|
|
77
|
+
...referenceContext,
|
|
78
|
+
depth: referenceContext.depth + 1,
|
|
79
|
+
};
|
|
80
|
+
|
|
57
81
|
if (typeof jsonSchema === 'boolean') {
|
|
58
82
|
return { type: 'boolean', properties: {} };
|
|
59
83
|
}
|
|
60
84
|
|
|
85
|
+
consumeSchemaBudget(
|
|
86
|
+
[
|
|
87
|
+
jsonSchema.$ref,
|
|
88
|
+
jsonSchema.type,
|
|
89
|
+
jsonSchema.description,
|
|
90
|
+
jsonSchema.required,
|
|
91
|
+
jsonSchema.format,
|
|
92
|
+
jsonSchema.const,
|
|
93
|
+
jsonSchema.enum,
|
|
94
|
+
jsonSchema.minLength,
|
|
95
|
+
],
|
|
96
|
+
referenceContext.budget,
|
|
97
|
+
);
|
|
98
|
+
|
|
61
99
|
if (jsonSchema.$ref != null) {
|
|
62
100
|
return convertJSONSchemaReference({
|
|
63
101
|
jsonSchema,
|
|
@@ -141,6 +179,7 @@ function convertJSONSchemaDefinition(
|
|
|
141
179
|
if (properties != null) {
|
|
142
180
|
result.properties = Object.entries(properties).reduce(
|
|
143
181
|
(acc, [key, value]) => {
|
|
182
|
+
consumeSchemaBudget(key, referenceContext.budget);
|
|
144
183
|
acc[key] = convertJSONSchemaDefinition(value, false, referenceContext);
|
|
145
184
|
return acc;
|
|
146
185
|
},
|
|
@@ -209,6 +248,52 @@ function convertJSONSchemaDefinition(
|
|
|
209
248
|
return result;
|
|
210
249
|
}
|
|
211
250
|
|
|
251
|
+
function checkSchemaBudget(
|
|
252
|
+
budget: ReferenceContext['budget'],
|
|
253
|
+
depth: number,
|
|
254
|
+
): void {
|
|
255
|
+
if (budget.nodes < 0 || budget.size < 0 || depth > maxSchemaDepth) {
|
|
256
|
+
throw new UnsupportedFunctionalityError({
|
|
257
|
+
functionality: 'JSON Schema conversion exceeding resource limits',
|
|
258
|
+
message:
|
|
259
|
+
'Google schema conversion exceeded its node, size, or depth limit.',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function consumeSchemaBudget(
|
|
265
|
+
value: unknown,
|
|
266
|
+
budget: ReferenceContext['budget'],
|
|
267
|
+
depth = 0,
|
|
268
|
+
): void {
|
|
269
|
+
if (value === undefined) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
budget.nodes--;
|
|
274
|
+
// A JSON string can require six characters per UTF-16 code unit when escaped.
|
|
275
|
+
// Other values reserve room for numbers, delimiters, and separators.
|
|
276
|
+
budget.size -= typeof value === 'string' ? value.length * 6 + 2 : 32;
|
|
277
|
+
checkSchemaBudget(budget, depth);
|
|
278
|
+
|
|
279
|
+
if (Array.isArray(value)) {
|
|
280
|
+
for (const item of value) {
|
|
281
|
+
consumeSchemaBudget(item, budget, depth + 1);
|
|
282
|
+
}
|
|
283
|
+
} else if (value !== null && typeof value === 'object') {
|
|
284
|
+
for (const key in value) {
|
|
285
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
286
|
+
consumeSchemaBudget(key, budget, depth + 1);
|
|
287
|
+
consumeSchemaBudget(
|
|
288
|
+
(value as Record<string, unknown>)[key],
|
|
289
|
+
budget,
|
|
290
|
+
depth + 1,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
212
297
|
type EnumValues = NonNullable<JSONSchema7['enum']>;
|
|
213
298
|
type EnumType = 'string' | 'number' | 'integer' | 'boolean';
|
|
214
299
|
type GoogleEnumSchema = {
|
|
@@ -657,7 +657,10 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
657
657
|
safetyRatings: candidate?.safetyRatings ?? null,
|
|
658
658
|
usageMetadata: usageMetadata ?? null,
|
|
659
659
|
finishMessage: candidate?.finishMessage ?? null,
|
|
660
|
-
serviceTier:
|
|
660
|
+
serviceTier:
|
|
661
|
+
usageMetadata?.serviceTier ??
|
|
662
|
+
responseHeaders?.['x-gemini-service-tier'] ??
|
|
663
|
+
null,
|
|
661
664
|
...(candidate?.continuationToken != null &&
|
|
662
665
|
rawFinishReason === 'MAX_TOKENS'
|
|
663
666
|
? { continuationToken: candidate.continuationToken }
|
|
@@ -1351,7 +1354,10 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
|
|
|
1351
1354
|
safetyRatings: lastSafetyRatings,
|
|
1352
1355
|
usageMetadata: usage ?? null,
|
|
1353
1356
|
finishMessage: lastFinishMessage,
|
|
1354
|
-
serviceTier:
|
|
1357
|
+
serviceTier:
|
|
1358
|
+
usage?.serviceTier ??
|
|
1359
|
+
responseHeaders?.['x-gemini-service-tier'] ??
|
|
1360
|
+
null,
|
|
1355
1361
|
// Only surface the token when the response stopped early
|
|
1356
1362
|
// (budget used up, or continuing failed), not for the
|
|
1357
1363
|
// checkpoint tokens of a stream that finished normally.
|
|
@@ -61,6 +61,7 @@ export class GoogleJSONAccumulator {
|
|
|
61
61
|
if (!rawPath || rawPath === '$') continue;
|
|
62
62
|
|
|
63
63
|
const segments = parsePath(rawPath);
|
|
64
|
+
if (!normalizeAndCheckIndexes(this.accumulatedArgs, segments)) continue;
|
|
64
65
|
|
|
65
66
|
const existingValue = getNestedValue(this.accumulatedArgs, segments);
|
|
66
67
|
const isStringContinuation =
|
|
@@ -374,6 +375,54 @@ function getNestedValue(
|
|
|
374
375
|
return current;
|
|
375
376
|
}
|
|
376
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Normalizes dotted array indexes in place and checks that every array index
|
|
380
|
+
* is an existing element or the next one. Arrays are streamed in order, and a
|
|
381
|
+
* sparse index would make `finalize()` serialize one `null` per hole, so a tiny
|
|
382
|
+
* response could allocate without bound.
|
|
383
|
+
* A missing or `null` parent is replaced with a new array, so it starts at 0.
|
|
384
|
+
*
|
|
385
|
+
* Input: ({items:["a"]}, ["items",1]) or ({items:["a"]}, ["items",5])
|
|
386
|
+
* Output: true or false
|
|
387
|
+
*/
|
|
388
|
+
function normalizeAndCheckIndexes(
|
|
389
|
+
obj: Record<string, unknown>,
|
|
390
|
+
segments: Array<string | number>,
|
|
391
|
+
): boolean {
|
|
392
|
+
let current: unknown = obj;
|
|
393
|
+
for (let i = 0; i < segments.length; i++) {
|
|
394
|
+
let pathSegment = segments[i];
|
|
395
|
+
if (Array.isArray(current) && typeof pathSegment === 'string') {
|
|
396
|
+
const index = Number(pathSegment);
|
|
397
|
+
// Only canonical array-index keys affect length. Keep object keys as strings.
|
|
398
|
+
if (
|
|
399
|
+
Number.isInteger(index) &&
|
|
400
|
+
index >= 0 &&
|
|
401
|
+
index < 2 ** 32 - 1 &&
|
|
402
|
+
String(index) === pathSegment
|
|
403
|
+
) {
|
|
404
|
+
pathSegment = index;
|
|
405
|
+
segments[i] = index;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (typeof pathSegment === 'number') {
|
|
409
|
+
const length = Array.isArray(current)
|
|
410
|
+
? current.length
|
|
411
|
+
: current == null
|
|
412
|
+
? 0
|
|
413
|
+
: undefined;
|
|
414
|
+
if (length !== undefined && pathSegment > length) return false;
|
|
415
|
+
}
|
|
416
|
+
current =
|
|
417
|
+
current != null &&
|
|
418
|
+
typeof current === 'object' &&
|
|
419
|
+
hasOwnProperty(current as Record<string | number, unknown>, pathSegment)
|
|
420
|
+
? (current as Record<string | number, unknown>)[pathSegment]
|
|
421
|
+
: undefined;
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
|
|
377
426
|
/**
|
|
378
427
|
* Sets a value at a nested path, creating intermediate objects or arrays as needed.
|
|
379
428
|
*
|
|
@@ -53,6 +53,8 @@ export type GoogleInteractionsModelId =
|
|
|
53
53
|
export const googleInteractionsLanguageModelOptions = lazySchema(() =>
|
|
54
54
|
zodSchema(
|
|
55
55
|
z.object({
|
|
56
|
+
/** User-defined request metadata, including `safety_identifier`. */
|
|
57
|
+
labels: z.record(z.string(), z.string()).nullish(),
|
|
56
58
|
previousInteractionId: z.string().nullish(),
|
|
57
59
|
store: z.boolean().nullish(),
|
|
58
60
|
|
|
@@ -420,6 +420,7 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV3 {
|
|
|
420
420
|
const args: GoogleInteractionsRequestBody = pruneUndefined({
|
|
421
421
|
...(isAgent ? { agent: this.agent } : { model: this.modelId }),
|
|
422
422
|
input,
|
|
423
|
+
labels: opts?.labels ?? undefined,
|
|
423
424
|
system_instruction: systemInstruction,
|
|
424
425
|
tools: toolsForBody,
|
|
425
426
|
response_format:
|
|
@@ -546,6 +546,7 @@ export type GoogleInteractionsRequestBody = {
|
|
|
546
546
|
model?: string;
|
|
547
547
|
agent?: string;
|
|
548
548
|
input: GoogleInteractionsInput;
|
|
549
|
+
labels?: Record<string, string>;
|
|
549
550
|
system_instruction?: string;
|
|
550
551
|
tools?: Array<GoogleInteractionsTool>;
|
|
551
552
|
response_format?: Array<GoogleInteractionsResponseFormatEntry>;
|