@mastra/schema-compat 0.0.0-fix-message-list-args-missing-20250807205055 → 0.0.0-vector-query-tool-provider-options-20250828222356
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 +1 -1
- package/CHANGELOG.md +54 -1
- package/README.md +0 -4
- package/dist/chunk-MKYBUMTK.js +27 -0
- package/dist/chunk-MKYBUMTK.js.map +1 -0
- package/dist/chunk-V7Y3FXBJ.cjs +33 -0
- package/dist/chunk-V7Y3FXBJ.cjs.map +1 -0
- package/dist/index.cjs +842 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +841 -84
- package/dist/index.js.map +1 -1
- package/dist/provider-compats/anthropic.d.ts +6 -4
- package/dist/provider-compats/anthropic.d.ts.map +1 -1
- package/dist/provider-compats/deepseek.d.ts +6 -4
- package/dist/provider-compats/deepseek.d.ts.map +1 -1
- package/dist/provider-compats/google.d.ts +6 -4
- package/dist/provider-compats/google.d.ts.map +1 -1
- package/dist/provider-compats/meta.d.ts +6 -4
- package/dist/provider-compats/meta.d.ts.map +1 -1
- package/dist/provider-compats/openai-reasoning.d.ts +6 -4
- package/dist/provider-compats/openai-reasoning.d.ts.map +1 -1
- package/dist/provider-compats/openai.d.ts +6 -4
- package/dist/provider-compats/openai.d.ts.map +1 -1
- package/dist/schema-compatibility-v3.d.ts +319 -0
- package/dist/schema-compatibility-v3.d.ts.map +1 -0
- package/dist/schema-compatibility-v4.d.ts +310 -0
- package/dist/schema-compatibility-v4.d.ts.map +1 -0
- package/dist/schema-compatibility.d.ts +76 -133
- package/dist/schema-compatibility.d.ts.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils-test-suite.d.ts +2 -0
- package/dist/utils-test-suite.d.ts.map +1 -0
- package/dist/utils.d.ts +17 -5
- package/dist/utils.d.ts.map +1 -1
- package/dist/zod-to-json.cjs +12 -0
- package/dist/zod-to-json.cjs.map +1 -0
- package/dist/zod-to-json.d.ts +6 -0
- package/dist/zod-to-json.d.ts.map +1 -0
- package/dist/zod-to-json.js +3 -0
- package/dist/zod-to-json.js.map +1 -0
- package/dist/zodTypes.d.ts +21 -0
- package/dist/zodTypes.d.ts.map +1 -0
- package/package.json +16 -6
- package/src/index.ts +4 -3
- package/src/provider-compats/anthropic.ts +29 -11
- package/src/provider-compats/deepseek.ts +14 -9
- package/src/provider-compats/google.ts +18 -32
- package/src/provider-compats/meta.ts +15 -10
- package/src/provider-compats/openai-reasoning.ts +18 -24
- package/src/provider-compats/openai.ts +22 -12
- package/src/schema-compatibility-v3.ts +664 -0
- package/src/schema-compatibility-v4.test.ts +476 -0
- package/src/schema-compatibility-v4.ts +706 -0
- package/src/schema-compatibility.test.ts +12 -28
- package/src/schema-compatibility.ts +262 -385
- package/src/types.ts +5 -0
- package/src/utils-test-suite.ts +467 -0
- package/src/utils-v3.test.ts +9 -0
- package/src/utils-v4.test.ts +9 -0
- package/src/utils.ts +30 -24
- package/src/zod-to-json.ts +28 -0
- package/src/zodTypes.ts +56 -0
- package/tsup.config.ts +8 -3
- package/src/utils.test.ts +0 -460
package/src/types.ts
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { jsonSchema } from 'ai';
|
|
2
|
+
import type { Schema } from 'ai';
|
|
3
|
+
import { MockLanguageModelV1 } from 'ai/test';
|
|
4
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import type { ZodType as ZodTypeV3 } from 'zod/v3';
|
|
7
|
+
import type { ZodType as ZodTypeV4 } from 'zod/v4';
|
|
8
|
+
import { SchemaCompatLayer } from './schema-compatibility';
|
|
9
|
+
import type { ModelInformation } from './types';
|
|
10
|
+
import { convertZodSchemaToAISDKSchema, convertSchemaToZod, applyCompatLayer, isZodType } from './utils';
|
|
11
|
+
|
|
12
|
+
type ZodType = ZodTypeV3 | ZodTypeV4;
|
|
13
|
+
|
|
14
|
+
const mockModel = new MockLanguageModelV1({
|
|
15
|
+
modelId: 'test-model',
|
|
16
|
+
defaultObjectGenerationMode: 'json',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
class MockSchemaCompatibility extends SchemaCompatLayer {
|
|
20
|
+
constructor(
|
|
21
|
+
model: ModelInformation,
|
|
22
|
+
private shouldApplyValue: boolean = true,
|
|
23
|
+
) {
|
|
24
|
+
super(model);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
shouldApply(): boolean {
|
|
28
|
+
return this.shouldApplyValue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getSchemaTarget() {
|
|
32
|
+
return 'jsonSchema7' as const;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
processZodType(value: ZodType): any {
|
|
36
|
+
if (value.constructor.name === 'ZodString') {
|
|
37
|
+
return z.string().describe('processed string');
|
|
38
|
+
}
|
|
39
|
+
if (value instanceof z.ZodObject) {
|
|
40
|
+
return this.defaultZodObjectHandler(value);
|
|
41
|
+
}
|
|
42
|
+
if (value instanceof z.ZodArray) {
|
|
43
|
+
return this.defaultZodArrayHandler(value);
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function runTestSuite() {
|
|
50
|
+
describe('Builder Functions', () => {
|
|
51
|
+
describe('convertZodSchemaToAISDKSchema', () => {
|
|
52
|
+
it('should convert simple Zod schema to AI SDK schema', () => {
|
|
53
|
+
const zodSchema = z.object({
|
|
54
|
+
name: z.string(),
|
|
55
|
+
age: z.number(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const result = convertZodSchemaToAISDKSchema(zodSchema);
|
|
59
|
+
|
|
60
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
61
|
+
expect(result).toHaveProperty('validate');
|
|
62
|
+
expect(typeof result.validate).toBe('function');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should create schema with validation function', () => {
|
|
66
|
+
const zodSchema = z.object({
|
|
67
|
+
email: z.string().email(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = convertZodSchemaToAISDKSchema(zodSchema);
|
|
71
|
+
|
|
72
|
+
expect(result.validate).toBeDefined();
|
|
73
|
+
|
|
74
|
+
const validResult = result.validate!({ email: 'test@example.com' });
|
|
75
|
+
expect(validResult.success).toBe(true);
|
|
76
|
+
if (validResult.success) {
|
|
77
|
+
expect(validResult.value).toEqual({ email: 'test@example.com' });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const invalidResult = result.validate!({ email: 'invalid-email' });
|
|
81
|
+
expect(invalidResult.success).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle custom targets', () => {
|
|
85
|
+
const zodSchema = z.object({
|
|
86
|
+
name: z.string(),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const result = convertZodSchemaToAISDKSchema(zodSchema, 'openApi3');
|
|
90
|
+
|
|
91
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
92
|
+
expect(result).toHaveProperty('validate');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should handle complex nested schemas', () => {
|
|
96
|
+
const zodSchema = z.object({
|
|
97
|
+
user: z.object({
|
|
98
|
+
name: z.string(),
|
|
99
|
+
preferences: z.object({
|
|
100
|
+
theme: z.enum(['light', 'dark']),
|
|
101
|
+
notifications: z.boolean(),
|
|
102
|
+
}),
|
|
103
|
+
}),
|
|
104
|
+
tags: z.array(z.string()),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const result = convertZodSchemaToAISDKSchema(zodSchema);
|
|
108
|
+
|
|
109
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
110
|
+
expect(result.jsonSchema).toHaveProperty('properties');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should handle array schemas', () => {
|
|
114
|
+
const zodSchema = z.array(z.string());
|
|
115
|
+
const result = convertZodSchemaToAISDKSchema(zodSchema);
|
|
116
|
+
expect(result.jsonSchema.type).toBe('array');
|
|
117
|
+
expect((result.jsonSchema.items as any)?.type).toBe('string');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('convertSchemaToZod', () => {
|
|
122
|
+
it('should return Zod schema unchanged', () => {
|
|
123
|
+
const zodSchema = z.object({
|
|
124
|
+
name: z.string(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = convertSchemaToZod(zodSchema);
|
|
128
|
+
|
|
129
|
+
expect(result).toBe(zodSchema);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should convert AI SDK schema to Zod', () => {
|
|
133
|
+
const aiSchema: Schema = jsonSchema({
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
name: { type: 'string' },
|
|
137
|
+
age: { type: 'number' },
|
|
138
|
+
},
|
|
139
|
+
required: ['name'],
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const result = convertSchemaToZod(aiSchema);
|
|
143
|
+
|
|
144
|
+
expect(isZodType(result)).toBe(true);
|
|
145
|
+
const parseResult = result.safeParse({ name: 'John', age: 30 });
|
|
146
|
+
expect(parseResult.success).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should handle complex JSON schema conversion', () => {
|
|
150
|
+
const complexSchema: Schema = jsonSchema({
|
|
151
|
+
type: 'object',
|
|
152
|
+
properties: {
|
|
153
|
+
user: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
properties: {
|
|
156
|
+
name: { type: 'string' },
|
|
157
|
+
email: { type: 'string', format: 'email' },
|
|
158
|
+
},
|
|
159
|
+
required: ['name'],
|
|
160
|
+
},
|
|
161
|
+
tags: {
|
|
162
|
+
type: 'array',
|
|
163
|
+
items: { type: 'string' },
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
required: ['user'],
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const result = convertSchemaToZod(complexSchema);
|
|
170
|
+
|
|
171
|
+
expect(isZodType(result)).toBe(true);
|
|
172
|
+
|
|
173
|
+
const validData = {
|
|
174
|
+
user: { name: 'John', email: 'john@example.com' },
|
|
175
|
+
tags: ['tag1', 'tag2'],
|
|
176
|
+
};
|
|
177
|
+
const parseResult = result.safeParse(validData);
|
|
178
|
+
expect(parseResult.success).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should convert AI SDK array schema to Zod', () => {
|
|
182
|
+
const aiSchema: Schema = jsonSchema({
|
|
183
|
+
type: 'array',
|
|
184
|
+
items: {
|
|
185
|
+
type: 'string',
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const result = convertSchemaToZod(aiSchema);
|
|
190
|
+
|
|
191
|
+
expect(result.constructor.name).toBe('ZodArray');
|
|
192
|
+
// @ts-expect-error - this is a test so we can ignore the type error
|
|
193
|
+
expect(result.element.constructor.name).toBe('ZodString');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('applyCompatLayer', () => {
|
|
198
|
+
let mockCompatibility: MockSchemaCompatibility;
|
|
199
|
+
|
|
200
|
+
beforeEach(() => {
|
|
201
|
+
mockCompatibility = new MockSchemaCompatibility({
|
|
202
|
+
modelId: mockModel.modelId,
|
|
203
|
+
supportsStructuredOutputs: mockModel.supportsStructuredOutputs ?? false,
|
|
204
|
+
provider: mockModel.provider,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should process Zod object schema with compatibility', () => {
|
|
209
|
+
const zodSchema = z.object({
|
|
210
|
+
name: z.string(),
|
|
211
|
+
age: z.number(),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const result = applyCompatLayer({
|
|
215
|
+
schema: zodSchema,
|
|
216
|
+
compatLayers: [mockCompatibility],
|
|
217
|
+
mode: 'aiSdkSchema',
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
221
|
+
expect(result).toHaveProperty('validate');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should process AI SDK schema with compatibility', () => {
|
|
225
|
+
const aiSchema: Schema = jsonSchema({
|
|
226
|
+
type: 'object',
|
|
227
|
+
properties: {
|
|
228
|
+
name: { type: 'string' },
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const result = applyCompatLayer({
|
|
233
|
+
schema: aiSchema,
|
|
234
|
+
compatLayers: [mockCompatibility],
|
|
235
|
+
mode: 'jsonSchema',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(typeof result).toBe('object');
|
|
239
|
+
expect(result).toHaveProperty('type');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should handle object schema with string property', () => {
|
|
243
|
+
const stringSchema = z.object({ value: z.string() });
|
|
244
|
+
|
|
245
|
+
const result = applyCompatLayer({
|
|
246
|
+
schema: stringSchema,
|
|
247
|
+
compatLayers: [mockCompatibility],
|
|
248
|
+
mode: 'aiSdkSchema',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
252
|
+
expect(result).toHaveProperty('validate');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should return processed schema when compatibility applies', () => {
|
|
256
|
+
const zodSchema = z.object({
|
|
257
|
+
name: z.string(),
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const result = applyCompatLayer({
|
|
261
|
+
schema: zodSchema,
|
|
262
|
+
compatLayers: [mockCompatibility],
|
|
263
|
+
mode: 'aiSdkSchema',
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
267
|
+
expect(result).toHaveProperty('validate');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should return fallback when no compatibility applies', () => {
|
|
271
|
+
const nonApplyingCompatibility = new MockSchemaCompatibility(
|
|
272
|
+
{
|
|
273
|
+
modelId: mockModel.modelId,
|
|
274
|
+
supportsStructuredOutputs: mockModel.supportsStructuredOutputs ?? false,
|
|
275
|
+
provider: mockModel.provider,
|
|
276
|
+
},
|
|
277
|
+
false,
|
|
278
|
+
);
|
|
279
|
+
const zodSchema = z.object({
|
|
280
|
+
name: z.string(),
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const result = applyCompatLayer({
|
|
284
|
+
schema: zodSchema,
|
|
285
|
+
compatLayers: [nonApplyingCompatibility],
|
|
286
|
+
mode: 'aiSdkSchema',
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
290
|
+
expect(result).toHaveProperty('validate');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should handle jsonSchema mode', () => {
|
|
294
|
+
const zodSchema = z.object({
|
|
295
|
+
name: z.string(),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const result = applyCompatLayer({
|
|
299
|
+
schema: zodSchema,
|
|
300
|
+
compatLayers: [mockCompatibility],
|
|
301
|
+
mode: 'jsonSchema',
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
expect(typeof result).toBe('object');
|
|
305
|
+
expect(result).toHaveProperty('type');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should handle empty compatLayers array', () => {
|
|
309
|
+
const zodSchema = z.object({
|
|
310
|
+
name: z.string(),
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const result = applyCompatLayer({
|
|
314
|
+
schema: zodSchema,
|
|
315
|
+
compatLayers: [],
|
|
316
|
+
mode: 'aiSdkSchema',
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
320
|
+
expect(result).toHaveProperty('validate');
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should handle complex schema with multiple compatLayers', () => {
|
|
324
|
+
const compat1 = new MockSchemaCompatibility(
|
|
325
|
+
{
|
|
326
|
+
modelId: mockModel.modelId,
|
|
327
|
+
supportsStructuredOutputs: mockModel.supportsStructuredOutputs ?? true,
|
|
328
|
+
provider: mockModel.provider,
|
|
329
|
+
},
|
|
330
|
+
false,
|
|
331
|
+
);
|
|
332
|
+
const compat2 = new MockSchemaCompatibility(
|
|
333
|
+
{
|
|
334
|
+
modelId: mockModel.modelId,
|
|
335
|
+
supportsStructuredOutputs: mockModel.supportsStructuredOutputs ?? false,
|
|
336
|
+
provider: mockModel.provider,
|
|
337
|
+
},
|
|
338
|
+
true,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
vi.spyOn(compat1, 'processZodType');
|
|
342
|
+
vi.spyOn(compat2, 'processZodType');
|
|
343
|
+
|
|
344
|
+
const zodSchema = z.object({
|
|
345
|
+
name: z.string(),
|
|
346
|
+
settings: z.object({
|
|
347
|
+
theme: z.string(),
|
|
348
|
+
notifications: z.boolean(),
|
|
349
|
+
}),
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const result = applyCompatLayer({
|
|
353
|
+
schema: zodSchema,
|
|
354
|
+
compatLayers: [compat1, compat2],
|
|
355
|
+
mode: 'aiSdkSchema',
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
expect(result).toHaveProperty('jsonSchema');
|
|
359
|
+
expect(result).toHaveProperty('validate');
|
|
360
|
+
expect(compat1.processZodType).not.toHaveBeenCalled();
|
|
361
|
+
expect(compat2.processZodType).toHaveBeenCalled();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should process Zod array schema with compatibility', () => {
|
|
365
|
+
const arraySchema = z.array(z.string());
|
|
366
|
+
|
|
367
|
+
const result = applyCompatLayer({
|
|
368
|
+
schema: arraySchema,
|
|
369
|
+
compatLayers: [mockCompatibility],
|
|
370
|
+
mode: 'aiSdkSchema',
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(result.jsonSchema.type).toBe('array');
|
|
374
|
+
if (
|
|
375
|
+
result.jsonSchema.items &&
|
|
376
|
+
!Array.isArray(result.jsonSchema.items) &&
|
|
377
|
+
typeof result.jsonSchema.items === 'object'
|
|
378
|
+
) {
|
|
379
|
+
expect(result.jsonSchema.items.type).toBe('string');
|
|
380
|
+
expect(result.jsonSchema.items.description).toBe('processed string');
|
|
381
|
+
} else {
|
|
382
|
+
expect.fail('items is not a single schema object');
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should process AI SDK array schema with compatibility', () => {
|
|
387
|
+
const aiSchema: Schema = jsonSchema({
|
|
388
|
+
type: 'array',
|
|
389
|
+
items: {
|
|
390
|
+
type: 'string',
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const result = applyCompatLayer({
|
|
395
|
+
schema: aiSchema,
|
|
396
|
+
compatLayers: [mockCompatibility],
|
|
397
|
+
mode: 'aiSdkSchema',
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
expect(result.jsonSchema.type).toBe('array');
|
|
401
|
+
if (
|
|
402
|
+
result.jsonSchema.items &&
|
|
403
|
+
!Array.isArray(result.jsonSchema.items) &&
|
|
404
|
+
typeof result.jsonSchema.items === 'object'
|
|
405
|
+
) {
|
|
406
|
+
expect(result.jsonSchema.items.type).toBe('string');
|
|
407
|
+
expect(result.jsonSchema.items.description).toBe('processed string');
|
|
408
|
+
} else {
|
|
409
|
+
expect.fail('items is not a single schema object');
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should handle a complex array of objects schema', () => {
|
|
414
|
+
const complexArraySchema = z.array(
|
|
415
|
+
z.object({
|
|
416
|
+
id: z.string(),
|
|
417
|
+
user: z.object({
|
|
418
|
+
name: z.string(),
|
|
419
|
+
}),
|
|
420
|
+
}),
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const result = applyCompatLayer({
|
|
424
|
+
schema: complexArraySchema,
|
|
425
|
+
compatLayers: [mockCompatibility],
|
|
426
|
+
mode: 'aiSdkSchema',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const { jsonSchema } = result;
|
|
430
|
+
expect(jsonSchema.type).toBe('array');
|
|
431
|
+
|
|
432
|
+
const items = jsonSchema.items;
|
|
433
|
+
if (items && !Array.isArray(items) && typeof items === 'object') {
|
|
434
|
+
expect(items.type).toBe('object');
|
|
435
|
+
expect(items.properties).toHaveProperty('id');
|
|
436
|
+
expect(items.properties).toHaveProperty('user');
|
|
437
|
+
|
|
438
|
+
const idProperty = items.properties!.id as any;
|
|
439
|
+
expect(idProperty.description).toBe('processed string');
|
|
440
|
+
|
|
441
|
+
const userProperty = items.properties!.user as any;
|
|
442
|
+
expect(userProperty.type).toBe('object');
|
|
443
|
+
expect(userProperty.properties).toHaveProperty('name');
|
|
444
|
+
|
|
445
|
+
const nameProperty = userProperty.properties.name as any;
|
|
446
|
+
expect(nameProperty.description).toBe('processed string');
|
|
447
|
+
} else {
|
|
448
|
+
expect.fail('items is not a single schema object');
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should handle a scalar zod schema', () => {
|
|
453
|
+
const scalarSchema = z.string().email();
|
|
454
|
+
|
|
455
|
+
const result = applyCompatLayer({
|
|
456
|
+
schema: scalarSchema,
|
|
457
|
+
compatLayers: [mockCompatibility],
|
|
458
|
+
mode: 'aiSdkSchema',
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const { jsonSchema } = result;
|
|
462
|
+
expect(jsonSchema.type).toBe('string');
|
|
463
|
+
expect(jsonSchema.description).toBe('processed string');
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { jsonSchema } from 'ai';
|
|
2
2
|
import type { Schema } from 'ai';
|
|
3
3
|
import type { JSONSchema7 } from 'json-schema';
|
|
4
|
-
import
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import type { ZodSchema as ZodSchemaV3, ZodType as ZodTypeV3 } from 'zod/v3';
|
|
6
|
+
import type { ZodType as ZodSchemaV4, ZodType as ZodTypeV4 } from 'zod/v4';
|
|
5
7
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
6
|
-
import
|
|
8
|
+
import { convertJsonSchemaToZod as convertJsonSchemaToZodV3 } from 'zod-from-json-schema-v3';
|
|
9
|
+
import type { JSONSchema } from 'zod-from-json-schema-v3';
|
|
7
10
|
import type { Targets } from 'zod-to-json-schema';
|
|
8
|
-
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
9
11
|
import type { SchemaCompatLayer } from './schema-compatibility';
|
|
12
|
+
import { zodToJsonSchema } from './zod-to-json';
|
|
13
|
+
|
|
14
|
+
type ZodSchema = ZodSchemaV3 | ZodSchemaV4;
|
|
15
|
+
type ZodType = ZodTypeV3 | ZodTypeV4;
|
|
10
16
|
|
|
11
17
|
/**
|
|
12
18
|
* Converts a Zod schema to an AI SDK Schema with validation support.
|
|
@@ -33,18 +39,14 @@ import type { SchemaCompatLayer } from './schema-compatibility';
|
|
|
33
39
|
*/
|
|
34
40
|
// mirrors https://github.com/vercel/ai/blob/main/packages/ui-utils/src/zod-schema.ts#L21 but with a custom target
|
|
35
41
|
export function convertZodSchemaToAISDKSchema(zodSchema: ZodSchema, target: Targets = 'jsonSchema7') {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
validate: value => {
|
|
43
|
-
const result = zodSchema.safeParse(value);
|
|
44
|
-
return result.success ? { success: true, value: result.data } : { success: false, error: result.error };
|
|
45
|
-
},
|
|
42
|
+
const jsonSchemaToUse = zodToJsonSchema(zodSchema, target) as JSONSchema7;
|
|
43
|
+
|
|
44
|
+
return jsonSchema(jsonSchemaToUse, {
|
|
45
|
+
validate: value => {
|
|
46
|
+
const result = zodSchema.safeParse(value);
|
|
47
|
+
return result.success ? { success: true, value: result.data } : { success: false, error: result.error };
|
|
46
48
|
},
|
|
47
|
-
);
|
|
49
|
+
});
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -54,7 +56,7 @@ export function convertZodSchemaToAISDKSchema(zodSchema: ZodSchema, target: Targ
|
|
|
54
56
|
* @returns True if the value is a Zod type, false otherwise
|
|
55
57
|
* @internal
|
|
56
58
|
*/
|
|
57
|
-
function isZodType(value: unknown): value is
|
|
59
|
+
export function isZodType(value: unknown): value is ZodType {
|
|
58
60
|
// Check if it's a Zod schema by looking for common Zod properties and methods
|
|
59
61
|
return (
|
|
60
62
|
typeof value === 'object' &&
|
|
@@ -92,13 +94,18 @@ function isZodType(value: unknown): value is z.ZodType {
|
|
|
92
94
|
* const zodSchema = convertSchemaToZod(aiSchema);
|
|
93
95
|
* ```
|
|
94
96
|
*/
|
|
95
|
-
export function convertSchemaToZod(schema: Schema |
|
|
97
|
+
export function convertSchemaToZod(schema: Schema | ZodSchema): ZodType {
|
|
96
98
|
if (isZodType(schema)) {
|
|
97
99
|
return schema;
|
|
98
100
|
} else {
|
|
99
|
-
const jsonSchemaToConvert = ('jsonSchema' in schema ? schema.jsonSchema : schema) as
|
|
101
|
+
const jsonSchemaToConvert = ('jsonSchema' in schema ? schema.jsonSchema : schema) as JSONSchema;
|
|
100
102
|
try {
|
|
101
|
-
|
|
103
|
+
if ('toJSONSchema' in z) {
|
|
104
|
+
// @ts-expect-error - zod type issue
|
|
105
|
+
return convertJsonSchemaToZod(jsonSchemaToConvert);
|
|
106
|
+
} else {
|
|
107
|
+
return convertJsonSchemaToZodV3(jsonSchemaToConvert);
|
|
108
|
+
}
|
|
102
109
|
} catch (e: unknown) {
|
|
103
110
|
const errorMessage = `[Schema Builder] Failed to convert schema parameters to Zod. Original schema: ${JSON.stringify(jsonSchemaToConvert)}`;
|
|
104
111
|
console.error(errorMessage, e);
|
|
@@ -117,7 +124,7 @@ export function convertSchemaToZod(schema: Schema | z.ZodSchema): z.ZodType {
|
|
|
117
124
|
* @returns Processed schema as an AI SDK Schema
|
|
118
125
|
*/
|
|
119
126
|
export function applyCompatLayer(options: {
|
|
120
|
-
schema: Schema |
|
|
127
|
+
schema: Schema | ZodSchema;
|
|
121
128
|
compatLayers: SchemaCompatLayer[];
|
|
122
129
|
mode: 'aiSdkSchema';
|
|
123
130
|
}): Schema;
|
|
@@ -132,7 +139,7 @@ export function applyCompatLayer(options: {
|
|
|
132
139
|
* @returns Processed schema as a JSONSchema7
|
|
133
140
|
*/
|
|
134
141
|
export function applyCompatLayer(options: {
|
|
135
|
-
schema: Schema |
|
|
142
|
+
schema: Schema | ZodSchema;
|
|
136
143
|
compatLayers: SchemaCompatLayer[];
|
|
137
144
|
mode: 'jsonSchema';
|
|
138
145
|
}): JSONSchema7;
|
|
@@ -177,11 +184,11 @@ export function applyCompatLayer({
|
|
|
177
184
|
compatLayers,
|
|
178
185
|
mode,
|
|
179
186
|
}: {
|
|
180
|
-
schema: Schema |
|
|
187
|
+
schema: Schema | ZodSchema;
|
|
181
188
|
compatLayers: SchemaCompatLayer[];
|
|
182
189
|
mode: 'jsonSchema' | 'aiSdkSchema';
|
|
183
190
|
}): JSONSchema7 | Schema {
|
|
184
|
-
let zodSchema:
|
|
191
|
+
let zodSchema: ZodSchema;
|
|
185
192
|
|
|
186
193
|
if (!isZodType(schema)) {
|
|
187
194
|
// Convert non-zod schema to Zod
|
|
@@ -195,10 +202,9 @@ export function applyCompatLayer({
|
|
|
195
202
|
return mode === 'jsonSchema' ? compat.processToJSONSchema(zodSchema) : compat.processToAISDKSchema(zodSchema);
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
|
-
|
|
199
205
|
// If no compatibility applied, convert back to appropriate format
|
|
200
206
|
if (mode === 'jsonSchema') {
|
|
201
|
-
return zodToJsonSchema(zodSchema,
|
|
207
|
+
return zodToJsonSchema(zodSchema, 'jsonSchema7') as JSONSchema7;
|
|
202
208
|
} else {
|
|
203
209
|
return convertZodSchemaToAISDKSchema(zodSchema);
|
|
204
210
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { JSONSchema7 } from 'json-schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ZodSchema as ZodSchemaV3 } from 'zod/v3';
|
|
4
|
+
import type { ZodType as ZodSchemaV4 } from 'zod/v4';
|
|
5
|
+
import type { Targets } from 'zod-to-json-schema';
|
|
6
|
+
import zodToJsonSchemaOriginal from 'zod-to-json-schema';
|
|
7
|
+
|
|
8
|
+
export function zodToJsonSchema(zodSchema: ZodSchemaV3 | ZodSchemaV4, target: Targets = 'jsonSchema7') {
|
|
9
|
+
if ('toJSONSchema' in z) {
|
|
10
|
+
// Use dynamic property access to avoid import errors in Zod v3
|
|
11
|
+
return (z as any)['toJSONSchema'](zodSchema, {
|
|
12
|
+
unrepresentable: 'any',
|
|
13
|
+
override: (ctx: any) => {
|
|
14
|
+
// Safe access to handle cases where _zod might be undefined
|
|
15
|
+
const def = ctx.zodSchema?._zod?.def;
|
|
16
|
+
if (def && def.type === 'date') {
|
|
17
|
+
ctx.jsonSchema.type = 'string';
|
|
18
|
+
ctx.jsonSchema.format = 'date-time';
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
}) as JSONSchema7;
|
|
22
|
+
} else {
|
|
23
|
+
return zodToJsonSchemaOriginal(zodSchema as ZodSchemaV3, {
|
|
24
|
+
$refStrategy: 'none',
|
|
25
|
+
target,
|
|
26
|
+
}) as JSONSchema7;
|
|
27
|
+
}
|
|
28
|
+
}
|