@mastra/schema-compat 0.10.2-alpha.2 → 0.10.2-alpha.3
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +6 -0
- package/dist/_tsup-dts-rollup.d.cts +55 -26
- package/dist/_tsup-dts-rollup.d.ts +55 -26
- package/dist/index.cjs +178 -240
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +174 -242
- package/package.json +3 -3
- package/src/index.ts +7 -0
- package/src/provider-compats/anthropic.ts +27 -32
- package/src/provider-compats/deepseek.ts +15 -21
- package/src/provider-compats/google.ts +34 -33
- package/src/provider-compats/meta.ts +17 -24
- package/src/provider-compats/openai-reasoning.ts +51 -50
- package/src/provider-compats/openai.ts +28 -33
- package/src/schema-compatibility.test.ts +161 -40
- package/src/schema-compatibility.ts +83 -91
- package/src/utils.test.ts +129 -23
- package/src/utils.ts +8 -21
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Schema, LanguageModelV1 } from 'ai';
|
|
2
2
|
import type { JSONSchema7 } from 'json-schema';
|
|
3
|
-
import { z } from 'zod';
|
|
3
|
+
import { z, ZodOptional, ZodObject, ZodArray, ZodUnion, ZodString, ZodNumber, ZodDate, ZodDefault } from 'zod';
|
|
4
|
+
import type { ZodTypeAny } from 'zod';
|
|
4
5
|
import type { Targets } from 'zod-to-json-schema';
|
|
5
6
|
import { convertZodSchemaToAISDKSchema } from './utils';
|
|
6
7
|
|
|
@@ -26,6 +27,15 @@ export const ALL_NUMBER_CHECKS = [
|
|
|
26
27
|
*/
|
|
27
28
|
export const ALL_ARRAY_CHECKS = ['min', 'max', 'length'] as const;
|
|
28
29
|
|
|
30
|
+
export const isOptional = (v: ZodTypeAny): v is ZodOptional<any> => v instanceof ZodOptional;
|
|
31
|
+
export const isObj = (v: ZodTypeAny): v is ZodObject<any, any, any> => v instanceof ZodObject;
|
|
32
|
+
export const isArr = (v: ZodTypeAny): v is ZodArray<any, any> => v instanceof ZodArray;
|
|
33
|
+
export const isUnion = (v: ZodTypeAny): v is ZodUnion<[ZodTypeAny, ...ZodTypeAny[]]> => v instanceof ZodUnion;
|
|
34
|
+
export const isString = (v: ZodTypeAny): v is ZodString => v instanceof ZodString;
|
|
35
|
+
export const isNumber = (v: ZodTypeAny): v is ZodNumber => v instanceof ZodNumber;
|
|
36
|
+
export const isDate = (v: ZodTypeAny): v is ZodDate => v instanceof ZodDate;
|
|
37
|
+
export const isDefault = (v: ZodTypeAny): v is ZodDefault<any> => v instanceof ZodDefault;
|
|
38
|
+
|
|
29
39
|
/**
|
|
30
40
|
* Zod types that are not supported by most AI model providers and should be avoided.
|
|
31
41
|
* @constant
|
|
@@ -214,30 +224,7 @@ export abstract class SchemaCompatLayer {
|
|
|
214
224
|
* @returns The processed Zod type
|
|
215
225
|
* @abstract
|
|
216
226
|
*/
|
|
217
|
-
abstract processZodType
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Applies compatibility transformations to a Zod object schema.
|
|
221
|
-
*
|
|
222
|
-
* @param zodSchema - The Zod object schema to transform
|
|
223
|
-
* @returns Object containing the transformed schema
|
|
224
|
-
* @private
|
|
225
|
-
*/
|
|
226
|
-
private applyZodSchemaCompatibility(zodSchema: z.AnyZodObject): {
|
|
227
|
-
schema: z.AnyZodObject;
|
|
228
|
-
} {
|
|
229
|
-
const newSchema = z.object(
|
|
230
|
-
Object.entries<z.ZodTypeAny>(zodSchema.shape || {}).reduce(
|
|
231
|
-
(acc, [key, value]) => ({
|
|
232
|
-
...acc,
|
|
233
|
-
[key]: this.processZodType<any>(value),
|
|
234
|
-
}),
|
|
235
|
-
{},
|
|
236
|
-
),
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
return { schema: newSchema };
|
|
240
|
-
}
|
|
227
|
+
abstract processZodType(value: ZodTypeAny): ZodTypeAny;
|
|
241
228
|
|
|
242
229
|
/**
|
|
243
230
|
* Default handler for Zod object types. Recursively processes all properties in the object.
|
|
@@ -245,22 +232,25 @@ export abstract class SchemaCompatLayer {
|
|
|
245
232
|
* @param value - The Zod object to process
|
|
246
233
|
* @returns The processed Zod object
|
|
247
234
|
*/
|
|
248
|
-
public defaultZodObjectHandler<
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
235
|
+
public defaultZodObjectHandler(value: ZodObject<any, any, any>): ZodObject<any, any, any> {
|
|
236
|
+
const processedShape = Object.entries(value.shape).reduce<Record<string, ZodTypeAny>>((acc, [key, propValue]) => {
|
|
237
|
+
acc[key] = this.processZodType(propValue as ZodTypeAny);
|
|
238
|
+
return acc;
|
|
239
|
+
}, {});
|
|
240
|
+
|
|
241
|
+
let result: ZodObject<any, any, any> = z.object(processedShape);
|
|
242
|
+
|
|
243
|
+
if (value._def.unknownKeys === 'strict') {
|
|
244
|
+
result = result.strict();
|
|
245
|
+
}
|
|
246
|
+
if (value._def.catchall && !(value._def.catchall instanceof z.ZodNever)) {
|
|
247
|
+
result = result.catchall(value._def.catchall);
|
|
248
|
+
}
|
|
249
|
+
|
|
260
250
|
if (value.description) {
|
|
261
251
|
result = result.describe(value.description);
|
|
262
252
|
}
|
|
263
|
-
return result
|
|
253
|
+
return result;
|
|
264
254
|
}
|
|
265
255
|
|
|
266
256
|
/**
|
|
@@ -314,40 +304,46 @@ export abstract class SchemaCompatLayer {
|
|
|
314
304
|
* @param handleChecks - Array constraints to convert to descriptions vs keep as validation
|
|
315
305
|
* @returns The processed Zod array
|
|
316
306
|
*/
|
|
317
|
-
public defaultZodArrayHandler
|
|
318
|
-
value:
|
|
307
|
+
public defaultZodArrayHandler(
|
|
308
|
+
value: ZodArray<any, any>,
|
|
319
309
|
handleChecks: readonly ArrayCheckType[] = ALL_ARRAY_CHECKS,
|
|
320
|
-
):
|
|
321
|
-
const
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
if (zodArray.minLength?.value !== undefined && handleChecks.includes('min')) {
|
|
325
|
-
constraints.minLength = zodArray.minLength.value;
|
|
326
|
-
}
|
|
327
|
-
if (zodArray.maxLength?.value !== undefined && handleChecks.includes('max')) {
|
|
328
|
-
constraints.maxLength = zodArray.maxLength.value;
|
|
329
|
-
}
|
|
330
|
-
if (zodArray.exactLength?.value !== undefined && handleChecks.includes('length')) {
|
|
331
|
-
constraints.exactLength = zodArray.exactLength.value;
|
|
332
|
-
}
|
|
333
|
-
const processedType =
|
|
334
|
-
arrayType._def.typeName === 'ZodObject' ? this.processZodType<T>(arrayType as z.ZodTypeAny) : arrayType;
|
|
310
|
+
): ZodArray<any, any> {
|
|
311
|
+
const zodArrayDef = value._def;
|
|
312
|
+
const processedType = this.processZodType(zodArrayDef.type);
|
|
313
|
+
|
|
335
314
|
let result = z.array(processedType);
|
|
336
|
-
|
|
337
|
-
|
|
315
|
+
|
|
316
|
+
const constraints: ArrayConstraints = {};
|
|
317
|
+
|
|
318
|
+
if (zodArrayDef.minLength?.value !== undefined) {
|
|
319
|
+
if (handleChecks.includes('min')) {
|
|
320
|
+
constraints.minLength = zodArrayDef.minLength.value;
|
|
321
|
+
} else {
|
|
322
|
+
result = result.min(zodArrayDef.minLength.value);
|
|
323
|
+
}
|
|
338
324
|
}
|
|
339
|
-
|
|
340
|
-
|
|
325
|
+
|
|
326
|
+
if (zodArrayDef.maxLength?.value !== undefined) {
|
|
327
|
+
if (handleChecks.includes('max')) {
|
|
328
|
+
constraints.maxLength = zodArrayDef.maxLength.value;
|
|
329
|
+
} else {
|
|
330
|
+
result = result.max(zodArrayDef.maxLength.value);
|
|
331
|
+
}
|
|
341
332
|
}
|
|
342
|
-
|
|
343
|
-
|
|
333
|
+
|
|
334
|
+
if (zodArrayDef.exactLength?.value !== undefined) {
|
|
335
|
+
if (handleChecks.includes('length')) {
|
|
336
|
+
constraints.exactLength = zodArrayDef.exactLength.value;
|
|
337
|
+
} else {
|
|
338
|
+
result = result.length(zodArrayDef.exactLength.value);
|
|
339
|
+
}
|
|
344
340
|
}
|
|
345
341
|
|
|
346
342
|
const description = this.mergeParameterDescription(value.description, constraints);
|
|
347
343
|
if (description) {
|
|
348
344
|
result = result.describe(description);
|
|
349
345
|
}
|
|
350
|
-
return result
|
|
346
|
+
return result;
|
|
351
347
|
}
|
|
352
348
|
|
|
353
349
|
/**
|
|
@@ -357,15 +353,14 @@ export abstract class SchemaCompatLayer {
|
|
|
357
353
|
* @returns The processed Zod union
|
|
358
354
|
* @throws Error if union has fewer than 2 options
|
|
359
355
|
*/
|
|
360
|
-
public defaultZodUnionHandler
|
|
361
|
-
const
|
|
362
|
-
const processedOptions = zodUnion._def.options.map((option: z.ZodTypeAny) => this.processZodType<T>(option));
|
|
356
|
+
public defaultZodUnionHandler(value: ZodUnion<[ZodTypeAny, ...ZodTypeAny[]]>): ZodTypeAny {
|
|
357
|
+
const processedOptions = value._def.options.map((option: ZodTypeAny) => this.processZodType(option));
|
|
363
358
|
if (processedOptions.length < 2) throw new Error('Union must have at least 2 options');
|
|
364
|
-
let result = z.union(processedOptions as [
|
|
359
|
+
let result = z.union(processedOptions as [ZodTypeAny, ZodTypeAny, ...ZodTypeAny[]]);
|
|
365
360
|
if (value.description) {
|
|
366
361
|
result = result.describe(value.description);
|
|
367
362
|
}
|
|
368
|
-
return result
|
|
363
|
+
return result;
|
|
369
364
|
}
|
|
370
365
|
|
|
371
366
|
/**
|
|
@@ -375,13 +370,12 @@ export abstract class SchemaCompatLayer {
|
|
|
375
370
|
* @param handleChecks - String constraints to convert to descriptions vs keep as validation
|
|
376
371
|
* @returns The processed Zod string
|
|
377
372
|
*/
|
|
378
|
-
public defaultZodStringHandler
|
|
379
|
-
value:
|
|
373
|
+
public defaultZodStringHandler(
|
|
374
|
+
value: ZodString,
|
|
380
375
|
handleChecks: readonly StringCheckType[] = ALL_STRING_CHECKS,
|
|
381
|
-
):
|
|
382
|
-
const zodString = value as z.ZodString;
|
|
376
|
+
): ZodString {
|
|
383
377
|
const constraints: StringConstraints = {};
|
|
384
|
-
const checks =
|
|
378
|
+
const checks = value._def.checks || [];
|
|
385
379
|
type ZodStringCheck = (typeof checks)[number];
|
|
386
380
|
const newChecks: ZodStringCheck[] = [];
|
|
387
381
|
for (const check of checks) {
|
|
@@ -437,7 +431,7 @@ export abstract class SchemaCompatLayer {
|
|
|
437
431
|
if (description) {
|
|
438
432
|
result = result.describe(description);
|
|
439
433
|
}
|
|
440
|
-
return result
|
|
434
|
+
return result;
|
|
441
435
|
}
|
|
442
436
|
|
|
443
437
|
/**
|
|
@@ -447,13 +441,12 @@ export abstract class SchemaCompatLayer {
|
|
|
447
441
|
* @param handleChecks - Number constraints to convert to descriptions vs keep as validation
|
|
448
442
|
* @returns The processed Zod number
|
|
449
443
|
*/
|
|
450
|
-
public defaultZodNumberHandler
|
|
451
|
-
value:
|
|
444
|
+
public defaultZodNumberHandler(
|
|
445
|
+
value: ZodNumber,
|
|
452
446
|
handleChecks: readonly NumberCheckType[] = ALL_NUMBER_CHECKS,
|
|
453
|
-
):
|
|
454
|
-
const zodNumber = value as z.ZodNumber;
|
|
447
|
+
): ZodNumber {
|
|
455
448
|
const constraints: NumberConstraints = {};
|
|
456
|
-
const checks =
|
|
449
|
+
const checks = value._def.checks || [];
|
|
457
450
|
type ZodNumberCheck = (typeof checks)[number];
|
|
458
451
|
const newChecks: ZodNumberCheck[] = [];
|
|
459
452
|
for (const check of checks) {
|
|
@@ -501,7 +494,7 @@ export abstract class SchemaCompatLayer {
|
|
|
501
494
|
if (description) {
|
|
502
495
|
result = result.describe(description);
|
|
503
496
|
}
|
|
504
|
-
return result
|
|
497
|
+
return result;
|
|
505
498
|
}
|
|
506
499
|
|
|
507
500
|
/**
|
|
@@ -510,10 +503,9 @@ export abstract class SchemaCompatLayer {
|
|
|
510
503
|
* @param value - The Zod date to process
|
|
511
504
|
* @returns A Zod string schema representing the date in ISO format
|
|
512
505
|
*/
|
|
513
|
-
public defaultZodDateHandler
|
|
514
|
-
const zodDate = value as z.ZodDate;
|
|
506
|
+
public defaultZodDateHandler(value: ZodDate): ZodString {
|
|
515
507
|
const constraints: DateConstraints = {};
|
|
516
|
-
const checks =
|
|
508
|
+
const checks = value._def.checks || [];
|
|
517
509
|
type ZodDateCheck = (typeof checks)[number];
|
|
518
510
|
const newChecks: ZodDateCheck[] = [];
|
|
519
511
|
for (const check of checks) {
|
|
@@ -542,7 +534,7 @@ export abstract class SchemaCompatLayer {
|
|
|
542
534
|
if (description) {
|
|
543
535
|
result = result.describe(description);
|
|
544
536
|
}
|
|
545
|
-
return result
|
|
537
|
+
return result;
|
|
546
538
|
}
|
|
547
539
|
|
|
548
540
|
/**
|
|
@@ -552,14 +544,14 @@ export abstract class SchemaCompatLayer {
|
|
|
552
544
|
* @param handleTypes - Types that should be processed vs passed through
|
|
553
545
|
* @returns The processed Zod optional
|
|
554
546
|
*/
|
|
555
|
-
public defaultZodOptionalHandler
|
|
556
|
-
value:
|
|
547
|
+
public defaultZodOptionalHandler(
|
|
548
|
+
value: ZodOptional<any>,
|
|
557
549
|
handleTypes: readonly AllZodType[] = SUPPORTED_ZOD_TYPES,
|
|
558
|
-
):
|
|
550
|
+
): ZodTypeAny {
|
|
559
551
|
if (handleTypes.includes(value._def.innerType._def.typeName as AllZodType)) {
|
|
560
552
|
return this.processZodType(value._def.innerType).optional();
|
|
561
553
|
} else {
|
|
562
|
-
return value
|
|
554
|
+
return value;
|
|
563
555
|
}
|
|
564
556
|
}
|
|
565
557
|
|
|
@@ -569,10 +561,10 @@ export abstract class SchemaCompatLayer {
|
|
|
569
561
|
* @param zodSchema - The Zod object schema to process
|
|
570
562
|
* @returns An AI SDK Schema with provider-specific compatibility applied
|
|
571
563
|
*/
|
|
572
|
-
public processToAISDKSchema(zodSchema: z.
|
|
573
|
-
const
|
|
564
|
+
public processToAISDKSchema(zodSchema: z.ZodSchema): Schema {
|
|
565
|
+
const processedSchema = this.processZodType(zodSchema);
|
|
574
566
|
|
|
575
|
-
return convertZodSchemaToAISDKSchema(
|
|
567
|
+
return convertZodSchemaToAISDKSchema(processedSchema, this.getSchemaTarget());
|
|
576
568
|
}
|
|
577
569
|
|
|
578
570
|
/**
|
|
@@ -581,7 +573,7 @@ export abstract class SchemaCompatLayer {
|
|
|
581
573
|
* @param zodSchema - The Zod object schema to process
|
|
582
574
|
* @returns A JSONSchema7 object with provider-specific compatibility applied
|
|
583
575
|
*/
|
|
584
|
-
public processToJSONSchema(zodSchema: z.
|
|
576
|
+
public processToJSONSchema(zodSchema: z.ZodSchema): JSONSchema7 {
|
|
585
577
|
return this.processToAISDKSchema(zodSchema).jsonSchema;
|
|
586
578
|
}
|
|
587
579
|
}
|
package/src/utils.test.ts
CHANGED
|
@@ -31,6 +31,12 @@ class MockSchemaCompatibility extends SchemaCompatLayer {
|
|
|
31
31
|
if (value._def.typeName === 'ZodString') {
|
|
32
32
|
return z.string().describe('processed string');
|
|
33
33
|
}
|
|
34
|
+
if (value instanceof z.ZodObject) {
|
|
35
|
+
return this.defaultZodObjectHandler(value);
|
|
36
|
+
}
|
|
37
|
+
if (value instanceof z.ZodArray) {
|
|
38
|
+
return this.defaultZodArrayHandler(value);
|
|
39
|
+
}
|
|
34
40
|
return value;
|
|
35
41
|
}
|
|
36
42
|
}
|
|
@@ -97,6 +103,13 @@ describe('Builder Functions', () => {
|
|
|
97
103
|
expect(result).toHaveProperty('jsonSchema');
|
|
98
104
|
expect(result.jsonSchema).toHaveProperty('properties');
|
|
99
105
|
});
|
|
106
|
+
|
|
107
|
+
it('should handle array schemas', () => {
|
|
108
|
+
const zodSchema = z.array(z.string());
|
|
109
|
+
const result = convertZodSchemaToAISDKSchema(zodSchema);
|
|
110
|
+
expect(result.jsonSchema.type).toBe('array');
|
|
111
|
+
expect((result.jsonSchema.items as any)?.type).toBe('string');
|
|
112
|
+
});
|
|
100
113
|
});
|
|
101
114
|
|
|
102
115
|
describe('convertSchemaToZod', () => {
|
|
@@ -158,6 +171,20 @@ describe('Builder Functions', () => {
|
|
|
158
171
|
const parseResult = result.safeParse(validData);
|
|
159
172
|
expect(parseResult.success).toBe(true);
|
|
160
173
|
});
|
|
174
|
+
|
|
175
|
+
it('should convert AI SDK array schema to Zod', () => {
|
|
176
|
+
const aiSchema: Schema = jsonSchema({
|
|
177
|
+
type: 'array',
|
|
178
|
+
items: {
|
|
179
|
+
type: 'string',
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const result = convertSchemaToZod(aiSchema);
|
|
184
|
+
|
|
185
|
+
expect(result).toBeInstanceOf(z.ZodArray);
|
|
186
|
+
expect((result as z.ZodArray<any>).element).toBeInstanceOf(z.ZodString);
|
|
187
|
+
});
|
|
161
188
|
});
|
|
162
189
|
|
|
163
190
|
describe('applyCompatLayer', () => {
|
|
@@ -275,29 +302,6 @@ describe('Builder Functions', () => {
|
|
|
275
302
|
expect(result).toHaveProperty('validate');
|
|
276
303
|
});
|
|
277
304
|
|
|
278
|
-
it('should convert non-object AI SDK schema correctly', () => {
|
|
279
|
-
const stringSchema: Schema = jsonSchema({ type: 'string' });
|
|
280
|
-
|
|
281
|
-
const result = applyCompatLayer({
|
|
282
|
-
schema: stringSchema,
|
|
283
|
-
compatLayers: [mockCompatibility],
|
|
284
|
-
mode: 'aiSdkSchema',
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
expect(result).toHaveProperty('jsonSchema');
|
|
288
|
-
expect(result).toHaveProperty('validate');
|
|
289
|
-
|
|
290
|
-
// Verify the schema structure shows the string was wrapped in a 'value' property
|
|
291
|
-
const resultSchema = (result as Schema).jsonSchema;
|
|
292
|
-
expect(resultSchema).toHaveProperty('type', 'object');
|
|
293
|
-
expect(resultSchema).toHaveProperty('properties');
|
|
294
|
-
expect(resultSchema.properties).toHaveProperty('value');
|
|
295
|
-
|
|
296
|
-
// Verify the string property has the description added by the mock compatibility layer
|
|
297
|
-
const valueProperty = resultSchema.properties!.value as any;
|
|
298
|
-
expect(valueProperty).toHaveProperty('description', 'processed string');
|
|
299
|
-
});
|
|
300
|
-
|
|
301
305
|
it('should handle complex schema with multiple compatLayers', () => {
|
|
302
306
|
const compat1 = new MockSchemaCompatibility(mockModel, false);
|
|
303
307
|
const compat2 = new MockSchemaCompatibility(mockModel, true);
|
|
@@ -324,5 +328,107 @@ describe('Builder Functions', () => {
|
|
|
324
328
|
expect(compat1.processZodType).not.toHaveBeenCalled();
|
|
325
329
|
expect(compat2.processZodType).toHaveBeenCalled();
|
|
326
330
|
});
|
|
331
|
+
|
|
332
|
+
it('should process Zod array schema with compatibility', () => {
|
|
333
|
+
const arraySchema = z.array(z.string());
|
|
334
|
+
|
|
335
|
+
const result = applyCompatLayer({
|
|
336
|
+
schema: arraySchema,
|
|
337
|
+
compatLayers: [mockCompatibility],
|
|
338
|
+
mode: 'aiSdkSchema',
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result.jsonSchema.type).toBe('array');
|
|
342
|
+
if (
|
|
343
|
+
result.jsonSchema.items &&
|
|
344
|
+
!Array.isArray(result.jsonSchema.items) &&
|
|
345
|
+
typeof result.jsonSchema.items === 'object'
|
|
346
|
+
) {
|
|
347
|
+
expect(result.jsonSchema.items.type).toBe('string');
|
|
348
|
+
expect(result.jsonSchema.items.description).toBe('processed string');
|
|
349
|
+
} else {
|
|
350
|
+
expect.fail('items is not a single schema object');
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should process AI SDK array schema with compatibility', () => {
|
|
355
|
+
const aiSchema: Schema = jsonSchema({
|
|
356
|
+
type: 'array',
|
|
357
|
+
items: {
|
|
358
|
+
type: 'string',
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const result = applyCompatLayer({
|
|
363
|
+
schema: aiSchema,
|
|
364
|
+
compatLayers: [mockCompatibility],
|
|
365
|
+
mode: 'aiSdkSchema',
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
expect(result.jsonSchema.type).toBe('array');
|
|
369
|
+
if (
|
|
370
|
+
result.jsonSchema.items &&
|
|
371
|
+
!Array.isArray(result.jsonSchema.items) &&
|
|
372
|
+
typeof result.jsonSchema.items === 'object'
|
|
373
|
+
) {
|
|
374
|
+
expect(result.jsonSchema.items.type).toBe('string');
|
|
375
|
+
expect(result.jsonSchema.items.description).toBe('processed string');
|
|
376
|
+
} else {
|
|
377
|
+
expect.fail('items is not a single schema object');
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should handle a complex array of objects schema', () => {
|
|
382
|
+
const complexArraySchema = z.array(
|
|
383
|
+
z.object({
|
|
384
|
+
id: z.string(),
|
|
385
|
+
user: z.object({
|
|
386
|
+
name: z.string(),
|
|
387
|
+
}),
|
|
388
|
+
}),
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const result = applyCompatLayer({
|
|
392
|
+
schema: complexArraySchema,
|
|
393
|
+
compatLayers: [mockCompatibility],
|
|
394
|
+
mode: 'aiSdkSchema',
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const { jsonSchema } = result;
|
|
398
|
+
expect(jsonSchema.type).toBe('array');
|
|
399
|
+
|
|
400
|
+
const items = jsonSchema.items;
|
|
401
|
+
if (items && !Array.isArray(items) && typeof items === 'object') {
|
|
402
|
+
expect(items.type).toBe('object');
|
|
403
|
+
expect(items.properties).toHaveProperty('id');
|
|
404
|
+
expect(items.properties).toHaveProperty('user');
|
|
405
|
+
|
|
406
|
+
const idProperty = items.properties!.id as any;
|
|
407
|
+
expect(idProperty.description).toBe('processed string');
|
|
408
|
+
|
|
409
|
+
const userProperty = items.properties!.user as any;
|
|
410
|
+
expect(userProperty.type).toBe('object');
|
|
411
|
+
expect(userProperty.properties).toHaveProperty('name');
|
|
412
|
+
|
|
413
|
+
const nameProperty = userProperty.properties.name as any;
|
|
414
|
+
expect(nameProperty.description).toBe('processed string');
|
|
415
|
+
} else {
|
|
416
|
+
expect.fail('items is not a single schema object');
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should handle a scalar zod schema', () => {
|
|
421
|
+
const scalarSchema = z.string().email();
|
|
422
|
+
|
|
423
|
+
const result = applyCompatLayer({
|
|
424
|
+
schema: scalarSchema,
|
|
425
|
+
compatLayers: [mockCompatibility],
|
|
426
|
+
mode: 'aiSdkSchema',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const { jsonSchema } = result;
|
|
430
|
+
expect(jsonSchema.type).toBe('string');
|
|
431
|
+
expect(jsonSchema.description).toBe('processed string');
|
|
432
|
+
});
|
|
327
433
|
});
|
|
328
434
|
});
|
package/src/utils.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsonSchema } from 'ai';
|
|
2
2
|
import type { Schema } from 'ai';
|
|
3
3
|
import type { JSONSchema7 } from 'json-schema';
|
|
4
|
-
import type { ZodSchema } from 'zod';
|
|
5
|
-
import { z } from 'zod';
|
|
4
|
+
import type { z, ZodSchema } from 'zod';
|
|
6
5
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
7
6
|
import type { JSONSchema as ZodFromJSONSchema_JSONSchema } from 'zod-from-json-schema';
|
|
8
7
|
import type { Targets } from 'zod-to-json-schema';
|
|
@@ -118,7 +117,7 @@ export function convertSchemaToZod(schema: Schema | z.ZodSchema): z.ZodType {
|
|
|
118
117
|
* @returns Processed schema as an AI SDK Schema
|
|
119
118
|
*/
|
|
120
119
|
export function applyCompatLayer(options: {
|
|
121
|
-
schema: Schema | z.
|
|
120
|
+
schema: Schema | z.ZodSchema;
|
|
122
121
|
compatLayers: SchemaCompatLayer[];
|
|
123
122
|
mode: 'aiSdkSchema';
|
|
124
123
|
}): Schema;
|
|
@@ -133,7 +132,7 @@ export function applyCompatLayer(options: {
|
|
|
133
132
|
* @returns Processed schema as a JSONSchema7
|
|
134
133
|
*/
|
|
135
134
|
export function applyCompatLayer(options: {
|
|
136
|
-
schema: Schema | z.
|
|
135
|
+
schema: Schema | z.ZodSchema;
|
|
137
136
|
compatLayers: SchemaCompatLayer[];
|
|
138
137
|
mode: 'jsonSchema';
|
|
139
138
|
}): JSONSchema7;
|
|
@@ -178,29 +177,17 @@ export function applyCompatLayer({
|
|
|
178
177
|
compatLayers,
|
|
179
178
|
mode,
|
|
180
179
|
}: {
|
|
181
|
-
schema: Schema | z.
|
|
180
|
+
schema: Schema | z.ZodSchema;
|
|
182
181
|
compatLayers: SchemaCompatLayer[];
|
|
183
182
|
mode: 'jsonSchema' | 'aiSdkSchema';
|
|
184
183
|
}): JSONSchema7 | Schema {
|
|
185
|
-
let zodSchema: z.
|
|
184
|
+
let zodSchema: z.ZodSchema;
|
|
186
185
|
|
|
187
186
|
if (!isZodType(schema)) {
|
|
188
|
-
// Convert
|
|
189
|
-
|
|
190
|
-
if (convertedSchema instanceof z.ZodObject) {
|
|
191
|
-
zodSchema = convertedSchema;
|
|
192
|
-
} else {
|
|
193
|
-
// If it's not an object schema, wrap it in an object
|
|
194
|
-
zodSchema = z.object({ value: convertedSchema });
|
|
195
|
-
}
|
|
187
|
+
// Convert non-zod schema to Zod
|
|
188
|
+
zodSchema = convertSchemaToZod(schema);
|
|
196
189
|
} else {
|
|
197
|
-
|
|
198
|
-
if (schema instanceof z.ZodObject) {
|
|
199
|
-
zodSchema = schema;
|
|
200
|
-
} else {
|
|
201
|
-
// Wrap non-object schemas in an object
|
|
202
|
-
zodSchema = z.object({ value: schema });
|
|
203
|
-
}
|
|
190
|
+
zodSchema = schema;
|
|
204
191
|
}
|
|
205
192
|
|
|
206
193
|
for (const compat of compatLayers) {
|