@ai-sdk/google 4.0.68 → 4.0.70
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 +25 -0
- package/dist/index.js +173 -329
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +9 -4
- package/dist/internal/index.js +159 -322
- package/dist/internal/index.js.map +1 -1
- package/package.json +1 -1
- package/src/download-tool-result-files.ts +124 -0
- package/src/google-language-model.ts +32 -13
- package/src/google-prepare-tools.ts +3 -23
- package/src/realtime/google-realtime-event-mapper.ts +24 -3
- package/src/sanitize-response-json-schema.ts +65 -0
- package/src/convert-json-schema-to-openapi-schema.ts +0 -456
|
@@ -1,456 +0,0 @@
|
|
|
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
|
-
};
|
|
16
|
-
|
|
17
|
-
const recursiveReferenceFunctionalityPrefix =
|
|
18
|
-
'recursive JSON Schema reference:';
|
|
19
|
-
|
|
20
|
-
export function isRecursiveJSONSchemaReferenceError(
|
|
21
|
-
error: unknown,
|
|
22
|
-
): error is UnsupportedFunctionalityError {
|
|
23
|
-
return (
|
|
24
|
-
UnsupportedFunctionalityError.isInstance(error) &&
|
|
25
|
-
error.functionality.startsWith(recursiveReferenceFunctionalityPrefix)
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Converts JSON Schema 7 to OpenAPI Schema 3.0
|
|
31
|
-
*/
|
|
32
|
-
export function convertJSONSchemaToOpenAPISchema(
|
|
33
|
-
jsonSchema: JSONSchema7Definition | undefined,
|
|
34
|
-
isRoot = true,
|
|
35
|
-
): unknown {
|
|
36
|
-
const rootSchema =
|
|
37
|
-
typeof jsonSchema === 'object'
|
|
38
|
-
? (jsonSchema as JSONSchema7WithDefinitions)
|
|
39
|
-
: undefined;
|
|
40
|
-
|
|
41
|
-
return convertJSONSchemaDefinition(jsonSchema, isRoot, {
|
|
42
|
-
definitions: rootSchema?.definitions,
|
|
43
|
-
dollarDefinitions: rootSchema?.$defs,
|
|
44
|
-
resolvingReferences: new Set(),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function convertJSONSchemaDefinition(
|
|
49
|
-
jsonSchema: JSONSchema7Definition | undefined,
|
|
50
|
-
isRoot: boolean,
|
|
51
|
-
referenceContext: ReferenceContext,
|
|
52
|
-
): unknown {
|
|
53
|
-
if (jsonSchema == null) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (typeof jsonSchema === 'boolean') {
|
|
58
|
-
return { type: 'boolean', properties: {} };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (jsonSchema.$ref != null) {
|
|
62
|
-
return convertJSONSchemaReference({
|
|
63
|
-
jsonSchema,
|
|
64
|
-
reference: jsonSchema.$ref,
|
|
65
|
-
isRoot,
|
|
66
|
-
referenceContext,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Handle empty object schemas: undefined at root, preserved when nested
|
|
71
|
-
if (isEmptyObjectSchema(jsonSchema)) {
|
|
72
|
-
if (isRoot) {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (jsonSchema.description) {
|
|
77
|
-
return { type: 'object', description: jsonSchema.description };
|
|
78
|
-
}
|
|
79
|
-
return { type: 'object' };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const {
|
|
83
|
-
type,
|
|
84
|
-
description,
|
|
85
|
-
required,
|
|
86
|
-
properties,
|
|
87
|
-
items,
|
|
88
|
-
allOf,
|
|
89
|
-
anyOf,
|
|
90
|
-
oneOf,
|
|
91
|
-
format,
|
|
92
|
-
const: constValue,
|
|
93
|
-
minLength,
|
|
94
|
-
minItems,
|
|
95
|
-
maxItems,
|
|
96
|
-
enum: enumValues,
|
|
97
|
-
} = jsonSchema;
|
|
98
|
-
|
|
99
|
-
const result: Record<string, unknown> = {};
|
|
100
|
-
|
|
101
|
-
if (description) result.description = description;
|
|
102
|
-
if (required) result.required = required;
|
|
103
|
-
if (format) result.format = format;
|
|
104
|
-
|
|
105
|
-
// Handle type
|
|
106
|
-
if (type) {
|
|
107
|
-
if (Array.isArray(type)) {
|
|
108
|
-
const hasNull = type.includes('null');
|
|
109
|
-
const nonNullTypes = type.filter(t => t !== 'null');
|
|
110
|
-
|
|
111
|
-
if (nonNullTypes.length === 0) {
|
|
112
|
-
// Only null type
|
|
113
|
-
result.type = 'null';
|
|
114
|
-
} else {
|
|
115
|
-
// One or more non-null types: always use anyOf
|
|
116
|
-
result.anyOf = nonNullTypes.map(t => ({ type: t }));
|
|
117
|
-
if (hasNull) {
|
|
118
|
-
result.nullable = true;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
result.type = type;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const values =
|
|
127
|
-
enumValues ?? (constValue !== undefined ? [constValue] : undefined);
|
|
128
|
-
|
|
129
|
-
if (values !== undefined) {
|
|
130
|
-
addEnumToSchema({ values, type, result });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (properties != null) {
|
|
134
|
-
result.properties = Object.entries(properties).reduce(
|
|
135
|
-
(acc, [key, value]) => {
|
|
136
|
-
acc[key] = convertJSONSchemaDefinition(value, false, referenceContext);
|
|
137
|
-
return acc;
|
|
138
|
-
},
|
|
139
|
-
{} as Record<string, unknown>,
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (items) {
|
|
144
|
-
result.items = Array.isArray(items)
|
|
145
|
-
? items.map(item =>
|
|
146
|
-
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
147
|
-
)
|
|
148
|
-
: convertJSONSchemaDefinition(items, false, referenceContext);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (allOf) {
|
|
152
|
-
result.allOf = allOf.map(item =>
|
|
153
|
-
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
if (anyOf) {
|
|
157
|
-
// Handle cases where anyOf includes a null type
|
|
158
|
-
if (
|
|
159
|
-
anyOf.some(
|
|
160
|
-
schema => typeof schema === 'object' && schema?.type === 'null',
|
|
161
|
-
)
|
|
162
|
-
) {
|
|
163
|
-
const nonNullSchemas = anyOf.filter(
|
|
164
|
-
schema => !(typeof schema === 'object' && schema?.type === 'null'),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
if (nonNullSchemas.length === 1) {
|
|
168
|
-
// If there's only one non-null schema, convert it and make it nullable
|
|
169
|
-
const converted = convertJSONSchemaDefinition(
|
|
170
|
-
nonNullSchemas[0],
|
|
171
|
-
false,
|
|
172
|
-
referenceContext,
|
|
173
|
-
);
|
|
174
|
-
if (typeof converted === 'object') {
|
|
175
|
-
result.nullable = true;
|
|
176
|
-
Object.assign(result, converted);
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
// If there are multiple non-null schemas, keep them in anyOf
|
|
180
|
-
result.anyOf = nonNullSchemas.map(item =>
|
|
181
|
-
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
182
|
-
);
|
|
183
|
-
result.nullable = true;
|
|
184
|
-
}
|
|
185
|
-
} else {
|
|
186
|
-
result.anyOf = anyOf.map(item =>
|
|
187
|
-
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (oneOf) {
|
|
192
|
-
result.oneOf = oneOf.map(item =>
|
|
193
|
-
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (minLength !== undefined) {
|
|
198
|
-
result.minLength = minLength;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (minItems !== undefined) {
|
|
202
|
-
result.minItems = minItems;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (maxItems !== undefined) {
|
|
206
|
-
result.maxItems = maxItems;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function convertJSONSchemaReference({
|
|
213
|
-
jsonSchema,
|
|
214
|
-
reference,
|
|
215
|
-
isRoot,
|
|
216
|
-
referenceContext,
|
|
217
|
-
}: {
|
|
218
|
-
jsonSchema: JSONSchema7;
|
|
219
|
-
reference: string;
|
|
220
|
-
isRoot: boolean;
|
|
221
|
-
referenceContext: ReferenceContext;
|
|
222
|
-
}): unknown {
|
|
223
|
-
const { definition, referenceKey } = getReferencedDefinition(
|
|
224
|
-
reference,
|
|
225
|
-
referenceContext,
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
if (referenceContext.resolvingReferences.has(referenceKey)) {
|
|
229
|
-
throw new UnsupportedFunctionalityError({
|
|
230
|
-
functionality: `${recursiveReferenceFunctionalityPrefix} ${reference}`,
|
|
231
|
-
message:
|
|
232
|
-
'Google schema conversion does not support recursive JSON Schema references.',
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const resolvingReferences = new Set(referenceContext.resolvingReferences);
|
|
237
|
-
resolvingReferences.add(referenceKey);
|
|
238
|
-
|
|
239
|
-
// Inline references instead of emitting Google's `ref` / `defs` fields.
|
|
240
|
-
// Those fields are supported by Vertex AI's Schema representation but are
|
|
241
|
-
// rejected by the Gemini Developer API representation used by this shared
|
|
242
|
-
// converter.
|
|
243
|
-
const { $ref: _reference, ...siblingSchema } = jsonSchema;
|
|
244
|
-
const resolvedSchema =
|
|
245
|
-
typeof definition === 'boolean'
|
|
246
|
-
? definition
|
|
247
|
-
? siblingSchema
|
|
248
|
-
: false
|
|
249
|
-
: { ...definition, ...siblingSchema };
|
|
250
|
-
|
|
251
|
-
return convertJSONSchemaDefinition(resolvedSchema, isRoot, {
|
|
252
|
-
...referenceContext,
|
|
253
|
-
resolvingReferences,
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function getReferencedDefinition(
|
|
258
|
-
reference: string,
|
|
259
|
-
referenceContext: ReferenceContext,
|
|
260
|
-
): {
|
|
261
|
-
definition: JSONSchema7Definition;
|
|
262
|
-
referenceKey: string;
|
|
263
|
-
} {
|
|
264
|
-
const definitionSources = [
|
|
265
|
-
{
|
|
266
|
-
prefix: '#/$defs/',
|
|
267
|
-
definitions: referenceContext.dollarDefinitions,
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
prefix: '#/definitions/',
|
|
271
|
-
definitions: referenceContext.definitions,
|
|
272
|
-
},
|
|
273
|
-
];
|
|
274
|
-
|
|
275
|
-
const source = definitionSources.find(({ prefix }) =>
|
|
276
|
-
reference.startsWith(prefix),
|
|
277
|
-
);
|
|
278
|
-
const encodedDefinitionName = source
|
|
279
|
-
? reference.slice(source.prefix.length)
|
|
280
|
-
: undefined;
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
source == null ||
|
|
284
|
-
encodedDefinitionName == null ||
|
|
285
|
-
encodedDefinitionName.length === 0 ||
|
|
286
|
-
encodedDefinitionName.includes('/')
|
|
287
|
-
) {
|
|
288
|
-
throwUnsupportedReference(reference);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
let decodedDefinitionName: string;
|
|
292
|
-
try {
|
|
293
|
-
decodedDefinitionName = decodeURIComponent(encodedDefinitionName);
|
|
294
|
-
} catch {
|
|
295
|
-
throwUnsupportedReference(reference);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
decodedDefinitionName.includes('/') ||
|
|
300
|
-
/~(?![01])/u.test(decodedDefinitionName) ||
|
|
301
|
-
source.definitions == null
|
|
302
|
-
) {
|
|
303
|
-
throwUnsupportedReference(reference);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const definitionName = decodedDefinitionName.replace(/~[01]/g, match =>
|
|
307
|
-
match === '~1' ? '/' : '~',
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
if (
|
|
311
|
-
!Object.prototype.hasOwnProperty.call(source.definitions, definitionName)
|
|
312
|
-
) {
|
|
313
|
-
throwUnsupportedReference(reference);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return {
|
|
317
|
-
definition: source.definitions[definitionName],
|
|
318
|
-
referenceKey: `${source.prefix}${definitionName}`,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function throwUnsupportedReference(reference: string): never {
|
|
323
|
-
throw new UnsupportedFunctionalityError({
|
|
324
|
-
functionality: `JSON Schema reference: ${reference}`,
|
|
325
|
-
message:
|
|
326
|
-
'Google schema conversion only supports references to direct children of root-level $defs or definitions.',
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
type EnumValues = NonNullable<JSONSchema7['enum']>;
|
|
331
|
-
type EnumType = 'string' | 'number' | 'integer' | 'boolean';
|
|
332
|
-
type GoogleEnumSchema = {
|
|
333
|
-
type?: JSONSchema7['type'];
|
|
334
|
-
enum?: JSONSchema7['enum'];
|
|
335
|
-
format?: JSONSchema7['format'];
|
|
336
|
-
anyOf?: JSONSchema7['anyOf'];
|
|
337
|
-
nullable?: boolean;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
function addEnumToSchema({
|
|
341
|
-
values,
|
|
342
|
-
type,
|
|
343
|
-
result,
|
|
344
|
-
}: {
|
|
345
|
-
values: EnumValues;
|
|
346
|
-
type: JSONSchema7['type'];
|
|
347
|
-
result: GoogleEnumSchema;
|
|
348
|
-
}) {
|
|
349
|
-
const nullable =
|
|
350
|
-
(Array.isArray(type) && type.includes('null')) ||
|
|
351
|
-
(type === undefined && values.includes(null));
|
|
352
|
-
|
|
353
|
-
// Gemini uses nullable instead of a null enum member.
|
|
354
|
-
const enumValues = nullable ? values.filter(value => value !== null) : values;
|
|
355
|
-
|
|
356
|
-
if (values.length > 0 && values.every(value => value === null)) {
|
|
357
|
-
const typeAllowsNull =
|
|
358
|
-
type === undefined ||
|
|
359
|
-
type === 'null' ||
|
|
360
|
-
(Array.isArray(type) && type.includes('null'));
|
|
361
|
-
|
|
362
|
-
if (typeAllowsNull) {
|
|
363
|
-
result.type = 'null';
|
|
364
|
-
if (Array.isArray(type)) {
|
|
365
|
-
delete result.anyOf;
|
|
366
|
-
}
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const enumType = getEnumType({ values: enumValues, type });
|
|
372
|
-
|
|
373
|
-
if (enumType === undefined) {
|
|
374
|
-
throw new UnsupportedFunctionalityError({
|
|
375
|
-
functionality: 'JSON Schema enum with mixed or unsupported values',
|
|
376
|
-
message:
|
|
377
|
-
'Google does not support this JSON Schema enum. Enum values must share one supported primitive type and match the schema type.',
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
result.type = enumType;
|
|
382
|
-
|
|
383
|
-
// The earlier type-array conversion created anyOf. The enum gives us one
|
|
384
|
-
// concrete value type, so store that type directly.
|
|
385
|
-
if (Array.isArray(type)) {
|
|
386
|
-
delete result.anyOf;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if (nullable) {
|
|
390
|
-
result.nullable = true;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (enumType === 'string') {
|
|
394
|
-
result.enum = enumValues;
|
|
395
|
-
} else {
|
|
396
|
-
result.format = 'enum';
|
|
397
|
-
result.enum = enumValues.map(String);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function getEnumType({
|
|
402
|
-
values,
|
|
403
|
-
type,
|
|
404
|
-
}: {
|
|
405
|
-
values: EnumValues;
|
|
406
|
-
type: JSONSchema7['type'];
|
|
407
|
-
}): EnumType | undefined {
|
|
408
|
-
if (values.length === 0) {
|
|
409
|
-
return undefined;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const typeAllows = (enumType: EnumType) =>
|
|
413
|
-
type === undefined ||
|
|
414
|
-
type === enumType ||
|
|
415
|
-
(Array.isArray(type) && type.includes(enumType));
|
|
416
|
-
|
|
417
|
-
if (
|
|
418
|
-
typeAllows('string') &&
|
|
419
|
-
values.every(value => typeof value === 'string')
|
|
420
|
-
) {
|
|
421
|
-
return 'string';
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (
|
|
425
|
-
(typeAllows('number') || typeAllows('integer')) &&
|
|
426
|
-
values.every(value => typeof value === 'number' && Number.isFinite(value))
|
|
427
|
-
) {
|
|
428
|
-
if (typeAllows('number')) {
|
|
429
|
-
return 'number';
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (values.every(value => Number.isInteger(value))) {
|
|
433
|
-
return 'integer';
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
typeAllows('boolean') &&
|
|
439
|
-
values.every(value => typeof value === 'boolean')
|
|
440
|
-
) {
|
|
441
|
-
return 'boolean';
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return undefined;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function isEmptyObjectSchema(jsonSchema: JSONSchema7Definition): boolean {
|
|
448
|
-
return (
|
|
449
|
-
jsonSchema != null &&
|
|
450
|
-
typeof jsonSchema === 'object' &&
|
|
451
|
-
jsonSchema.type === 'object' &&
|
|
452
|
-
(jsonSchema.properties == null ||
|
|
453
|
-
Object.keys(jsonSchema.properties).length === 0) &&
|
|
454
|
-
!jsonSchema.additionalProperties
|
|
455
|
-
);
|
|
456
|
-
}
|