@ai-sdk/google 3.0.109 → 3.0.111

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": "3.0.109",
3
+ "version": "3.0.111",
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.15",
40
- "@ai-sdk/provider-utils": "4.0.45"
40
+ "@ai-sdk/provider-utils": "4.0.46"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
@@ -1,4 +1,18 @@
1
- import type { JSONSchema7Definition } from '@ai-sdk/provider';
1
+ import {
2
+ UnsupportedFunctionalityError,
3
+ type JSONSchema7,
4
+ type JSONSchema7Definition,
5
+ } from '@ai-sdk/provider';
6
+
7
+ type JSONSchema7WithDefinitions = JSONSchema7 & {
8
+ $defs?: Record<string, JSONSchema7Definition>;
9
+ };
10
+
11
+ type ReferenceContext = {
12
+ definitions: Record<string, JSONSchema7Definition> | undefined;
13
+ dollarDefinitions: Record<string, JSONSchema7Definition> | undefined;
14
+ resolvingReferences: ReadonlySet<string>;
15
+ };
2
16
 
3
17
  /**
4
18
  * Converts JSON Schema 7 to OpenAPI Schema 3.0
@@ -7,26 +21,52 @@ export function convertJSONSchemaToOpenAPISchema(
7
21
  jsonSchema: JSONSchema7Definition | undefined,
8
22
  isRoot = true,
9
23
  ): unknown {
10
- // Handle empty object schemas: undefined at root, preserved when nested
24
+ const rootSchema =
25
+ typeof jsonSchema === 'object'
26
+ ? (jsonSchema as JSONSchema7WithDefinitions)
27
+ : undefined;
28
+
29
+ return convertJSONSchemaDefinition(jsonSchema, isRoot, {
30
+ definitions: rootSchema?.definitions,
31
+ dollarDefinitions: rootSchema?.$defs,
32
+ resolvingReferences: new Set(),
33
+ });
34
+ }
35
+
36
+ function convertJSONSchemaDefinition(
37
+ jsonSchema: JSONSchema7Definition | undefined,
38
+ isRoot: boolean,
39
+ referenceContext: ReferenceContext,
40
+ ): unknown {
11
41
  if (jsonSchema == null) {
12
42
  return undefined;
13
43
  }
14
44
 
45
+ if (typeof jsonSchema === 'boolean') {
46
+ return { type: 'boolean', properties: {} };
47
+ }
48
+
49
+ if (jsonSchema.$ref != null) {
50
+ return convertJSONSchemaReference({
51
+ jsonSchema,
52
+ reference: jsonSchema.$ref,
53
+ isRoot,
54
+ referenceContext,
55
+ });
56
+ }
57
+
58
+ // Handle empty object schemas: undefined at root, preserved when nested
15
59
  if (isEmptyObjectSchema(jsonSchema)) {
16
60
  if (isRoot) {
17
61
  return undefined;
18
62
  }
19
63
 
20
- if (typeof jsonSchema === 'object' && jsonSchema.description) {
64
+ if (jsonSchema.description) {
21
65
  return { type: 'object', description: jsonSchema.description };
22
66
  }
23
67
  return { type: 'object' };
24
68
  }
25
69
 
26
- if (typeof jsonSchema === 'boolean') {
27
- return { type: 'boolean', properties: {} };
28
- }
29
-
30
70
  const {
31
71
  type,
32
72
  description,
@@ -81,7 +121,7 @@ export function convertJSONSchemaToOpenAPISchema(
81
121
  if (properties != null) {
82
122
  result.properties = Object.entries(properties).reduce(
83
123
  (acc, [key, value]) => {
84
- acc[key] = convertJSONSchemaToOpenAPISchema(value, false);
124
+ acc[key] = convertJSONSchemaDefinition(value, false, referenceContext);
85
125
  return acc;
86
126
  },
87
127
  {} as Record<string, unknown>,
@@ -90,13 +130,15 @@ export function convertJSONSchemaToOpenAPISchema(
90
130
 
91
131
  if (items) {
92
132
  result.items = Array.isArray(items)
93
- ? items.map(item => convertJSONSchemaToOpenAPISchema(item, false))
94
- : convertJSONSchemaToOpenAPISchema(items, false);
133
+ ? items.map(item =>
134
+ convertJSONSchemaDefinition(item, false, referenceContext),
135
+ )
136
+ : convertJSONSchemaDefinition(items, false, referenceContext);
95
137
  }
96
138
 
97
139
  if (allOf) {
98
140
  result.allOf = allOf.map(item =>
99
- convertJSONSchemaToOpenAPISchema(item, false),
141
+ convertJSONSchemaDefinition(item, false, referenceContext),
100
142
  );
101
143
  }
102
144
  if (anyOf) {
@@ -112,9 +154,10 @@ export function convertJSONSchemaToOpenAPISchema(
112
154
 
113
155
  if (nonNullSchemas.length === 1) {
114
156
  // If there's only one non-null schema, convert it and make it nullable
115
- const converted = convertJSONSchemaToOpenAPISchema(
157
+ const converted = convertJSONSchemaDefinition(
116
158
  nonNullSchemas[0],
117
159
  false,
160
+ referenceContext,
118
161
  );
119
162
  if (typeof converted === 'object') {
120
163
  result.nullable = true;
@@ -123,19 +166,19 @@ export function convertJSONSchemaToOpenAPISchema(
123
166
  } else {
124
167
  // If there are multiple non-null schemas, keep them in anyOf
125
168
  result.anyOf = nonNullSchemas.map(item =>
126
- convertJSONSchemaToOpenAPISchema(item, false),
169
+ convertJSONSchemaDefinition(item, false, referenceContext),
127
170
  );
128
171
  result.nullable = true;
129
172
  }
130
173
  } else {
131
174
  result.anyOf = anyOf.map(item =>
132
- convertJSONSchemaToOpenAPISchema(item, false),
175
+ convertJSONSchemaDefinition(item, false, referenceContext),
133
176
  );
134
177
  }
135
178
  }
136
179
  if (oneOf) {
137
180
  result.oneOf = oneOf.map(item =>
138
- convertJSONSchemaToOpenAPISchema(item, false),
181
+ convertJSONSchemaDefinition(item, false, referenceContext),
139
182
  );
140
183
  }
141
184
 
@@ -146,6 +189,123 @@ export function convertJSONSchemaToOpenAPISchema(
146
189
  return result;
147
190
  }
148
191
 
192
+ function convertJSONSchemaReference({
193
+ jsonSchema,
194
+ reference,
195
+ isRoot,
196
+ referenceContext,
197
+ }: {
198
+ jsonSchema: JSONSchema7;
199
+ reference: string;
200
+ isRoot: boolean;
201
+ referenceContext: ReferenceContext;
202
+ }): unknown {
203
+ const { definition, referenceKey } = getReferencedDefinition(
204
+ reference,
205
+ referenceContext,
206
+ );
207
+
208
+ if (referenceContext.resolvingReferences.has(referenceKey)) {
209
+ throw new UnsupportedFunctionalityError({
210
+ functionality: `recursive JSON Schema reference: ${reference}`,
211
+ message:
212
+ 'Google schema conversion does not support recursive JSON Schema references.',
213
+ });
214
+ }
215
+
216
+ const resolvingReferences = new Set(referenceContext.resolvingReferences);
217
+ resolvingReferences.add(referenceKey);
218
+
219
+ // Inline references instead of emitting Google's `ref` / `defs` fields.
220
+ // Those fields are supported by Vertex AI's Schema representation but are
221
+ // rejected by the Gemini Developer API representation used by this shared
222
+ // converter.
223
+ const { $ref: _reference, ...siblingSchema } = jsonSchema;
224
+ const resolvedSchema =
225
+ typeof definition === 'boolean'
226
+ ? definition
227
+ ? siblingSchema
228
+ : false
229
+ : { ...definition, ...siblingSchema };
230
+
231
+ return convertJSONSchemaDefinition(resolvedSchema, isRoot, {
232
+ ...referenceContext,
233
+ resolvingReferences,
234
+ });
235
+ }
236
+
237
+ function getReferencedDefinition(
238
+ reference: string,
239
+ referenceContext: ReferenceContext,
240
+ ): {
241
+ definition: JSONSchema7Definition;
242
+ referenceKey: string;
243
+ } {
244
+ const definitionSources = [
245
+ {
246
+ prefix: '#/$defs/',
247
+ definitions: referenceContext.dollarDefinitions,
248
+ },
249
+ {
250
+ prefix: '#/definitions/',
251
+ definitions: referenceContext.definitions,
252
+ },
253
+ ];
254
+
255
+ const source = definitionSources.find(({ prefix }) =>
256
+ reference.startsWith(prefix),
257
+ );
258
+ const encodedDefinitionName = source
259
+ ? reference.slice(source.prefix.length)
260
+ : undefined;
261
+
262
+ if (
263
+ source == null ||
264
+ encodedDefinitionName == null ||
265
+ encodedDefinitionName.length === 0 ||
266
+ encodedDefinitionName.includes('/')
267
+ ) {
268
+ throwUnsupportedReference(reference);
269
+ }
270
+
271
+ let decodedDefinitionName: string;
272
+ try {
273
+ decodedDefinitionName = decodeURIComponent(encodedDefinitionName);
274
+ } catch {
275
+ throwUnsupportedReference(reference);
276
+ }
277
+
278
+ if (
279
+ decodedDefinitionName.includes('/') ||
280
+ /~(?![01])/u.test(decodedDefinitionName) ||
281
+ source.definitions == null
282
+ ) {
283
+ throwUnsupportedReference(reference);
284
+ }
285
+
286
+ const definitionName = decodedDefinitionName.replace(/~[01]/g, match =>
287
+ match === '~1' ? '/' : '~',
288
+ );
289
+
290
+ if (
291
+ !Object.prototype.hasOwnProperty.call(source.definitions, definitionName)
292
+ ) {
293
+ throwUnsupportedReference(reference);
294
+ }
295
+
296
+ return {
297
+ definition: source.definitions[definitionName],
298
+ referenceKey: `${source.prefix}${definitionName}`,
299
+ };
300
+ }
301
+
302
+ function throwUnsupportedReference(reference: string): never {
303
+ throw new UnsupportedFunctionalityError({
304
+ functionality: `JSON Schema reference: ${reference}`,
305
+ message:
306
+ 'Google schema conversion only supports references to direct children of root-level $defs or definitions.',
307
+ });
308
+ }
149
309
  function isEmptyObjectSchema(jsonSchema: JSONSchema7Definition): boolean {
150
310
  return (
151
311
  jsonSchema != null &&