@mastra/schema-compat 0.10.6-alpha.0 → 0.10.6-alpha.1

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.
Files changed (68) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +0 -4
  4. package/dist/chunk-FTKGHMGD.js +27 -0
  5. package/dist/chunk-FTKGHMGD.js.map +1 -0
  6. package/dist/chunk-LNR4XKDU.cjs +33 -0
  7. package/dist/chunk-LNR4XKDU.cjs.map +1 -0
  8. package/dist/index.cjs +843 -84
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.ts +10 -8
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +842 -85
  13. package/dist/index.js.map +1 -1
  14. package/dist/provider-compats/anthropic.d.ts +7 -5
  15. package/dist/provider-compats/anthropic.d.ts.map +1 -1
  16. package/dist/provider-compats/deepseek.d.ts +7 -5
  17. package/dist/provider-compats/deepseek.d.ts.map +1 -1
  18. package/dist/provider-compats/google.d.ts +7 -5
  19. package/dist/provider-compats/google.d.ts.map +1 -1
  20. package/dist/provider-compats/meta.d.ts +7 -5
  21. package/dist/provider-compats/meta.d.ts.map +1 -1
  22. package/dist/provider-compats/openai-reasoning.d.ts +7 -5
  23. package/dist/provider-compats/openai-reasoning.d.ts.map +1 -1
  24. package/dist/provider-compats/openai.d.ts +7 -5
  25. package/dist/provider-compats/openai.d.ts.map +1 -1
  26. package/dist/schema-compatibility-v3.d.ts +319 -0
  27. package/dist/schema-compatibility-v3.d.ts.map +1 -0
  28. package/dist/schema-compatibility-v4.d.ts +310 -0
  29. package/dist/schema-compatibility-v4.d.ts.map +1 -0
  30. package/dist/schema-compatibility.d.ts +79 -131
  31. package/dist/schema-compatibility.d.ts.map +1 -1
  32. package/dist/types.d.ts +6 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/utils-test-suite.d.ts +2 -0
  35. package/dist/utils-test-suite.d.ts.map +1 -0
  36. package/dist/utils.d.ts +17 -5
  37. package/dist/utils.d.ts.map +1 -1
  38. package/dist/zod-to-json.cjs +12 -0
  39. package/dist/zod-to-json.cjs.map +1 -0
  40. package/dist/zod-to-json.d.ts +6 -0
  41. package/dist/zod-to-json.d.ts.map +1 -0
  42. package/dist/zod-to-json.js +3 -0
  43. package/dist/zod-to-json.js.map +1 -0
  44. package/dist/zodTypes.d.ts +21 -0
  45. package/dist/zodTypes.d.ts.map +1 -0
  46. package/package.json +16 -6
  47. package/src/index.ts +4 -3
  48. package/src/provider-compats/anthropic.ts +30 -13
  49. package/src/provider-compats/deepseek.ts +15 -10
  50. package/src/provider-compats/google.ts +19 -33
  51. package/src/provider-compats/meta.ts +16 -11
  52. package/src/provider-compats/openai-reasoning.ts +24 -26
  53. package/src/provider-compats/openai.ts +23 -14
  54. package/src/provider-compats.test.ts +120 -25
  55. package/src/schema-compatibility-v3.ts +664 -0
  56. package/src/schema-compatibility-v4.test.ts +476 -0
  57. package/src/schema-compatibility-v4.ts +706 -0
  58. package/src/schema-compatibility.test.ts +9 -9
  59. package/src/schema-compatibility.ts +266 -383
  60. package/src/types.ts +5 -0
  61. package/src/utils-test-suite.ts +467 -0
  62. package/src/utils-v3.test.ts +9 -0
  63. package/src/utils-v4.test.ts +9 -0
  64. package/src/utils.ts +30 -24
  65. package/src/zod-to-json.ts +27 -0
  66. package/src/zodTypes.ts +56 -0
  67. package/tsup.config.ts +8 -3
  68. package/src/utils.test.ts +0 -434
@@ -1,10 +1,13 @@
1
- import type { LanguageModelV1 } from 'ai';
2
- import type { ZodTypeAny } from 'zod';
1
+ import { z } from 'zod';
2
+ import type { ZodType as ZodTypeV3 } from 'zod/v3';
3
+ import type { ZodType as ZodTypeV4 } from 'zod/v4';
3
4
  import type { Targets } from 'zod-to-json-schema';
4
- import { SchemaCompatLayer, isArr, isObj, isOptional, isString, isUnion } from '../schema-compatibility';
5
+ import { SchemaCompatLayer } from '../schema-compatibility';
6
+ import type { ModelInformation } from '../types';
7
+ import { isOptional, isObj, isArr, isUnion, isString } from '../zodTypes';
5
8
 
6
9
  export class DeepSeekSchemaCompatLayer extends SchemaCompatLayer {
7
- constructor(model: LanguageModelV1) {
10
+ constructor(model: ModelInformation) {
8
11
  super(model);
9
12
  }
10
13
 
@@ -17,16 +20,18 @@ export class DeepSeekSchemaCompatLayer extends SchemaCompatLayer {
17
20
  return this.getModel().modelId.includes('deepseek') && !this.getModel().modelId.includes('r1');
18
21
  }
19
22
 
20
- processZodType(value: ZodTypeAny): ZodTypeAny {
21
- if (isOptional(value)) {
23
+ processZodType(value: ZodTypeV3): ZodTypeV3;
24
+ processZodType(value: ZodTypeV4): ZodTypeV4;
25
+ processZodType(value: ZodTypeV3 | ZodTypeV4): ZodTypeV3 | ZodTypeV4 {
26
+ if (isOptional(z)(value)) {
22
27
  return this.defaultZodOptionalHandler(value, ['ZodObject', 'ZodArray', 'ZodUnion', 'ZodString', 'ZodNumber']);
23
- } else if (isObj(value)) {
28
+ } else if (isObj(z)(value)) {
24
29
  return this.defaultZodObjectHandler(value);
25
- } else if (isArr(value)) {
30
+ } else if (isArr(z)(value)) {
26
31
  return this.defaultZodArrayHandler(value, ['min', 'max']);
27
- } else if (isUnion(value)) {
32
+ } else if (isUnion(z)(value)) {
28
33
  return this.defaultZodUnionHandler(value);
29
- } else if (isString(value)) {
34
+ } else if (isString(z)(value)) {
30
35
  return this.defaultZodStringHandler(value);
31
36
  }
32
37
 
@@ -1,21 +1,13 @@
1
- import type { LanguageModelV1 } from 'ai';
2
- import type { ZodTypeAny } from 'zod';
3
1
  import { z } from 'zod';
2
+ import type { ZodType as ZodTypeV3, ZodObject as ZodObjectV3 } from 'zod/v3';
3
+ import type { ZodType as ZodTypeV4, ZodObject as ZodObjectV4 } from 'zod/v4';
4
4
  import type { Targets } from 'zod-to-json-schema';
5
- import {
6
- SchemaCompatLayer,
7
- UNSUPPORTED_ZOD_TYPES,
8
- isArr,
9
- isNull,
10
- isNumber,
11
- isObj,
12
- isOptional,
13
- isString,
14
- isUnion,
15
- } from '../schema-compatibility';
5
+ import { SchemaCompatLayer } from '../schema-compatibility';
6
+ import type { ModelInformation } from '../types';
7
+ import { isOptional, isNull, isObj, isArr, isUnion, isString, isNumber } from '../zodTypes';
16
8
 
17
9
  export class GoogleSchemaCompatLayer extends SchemaCompatLayer {
18
- constructor(model: LanguageModelV1) {
10
+ constructor(model: ModelInformation) {
19
11
  super(model);
20
12
  }
21
13
 
@@ -26,38 +18,32 @@ export class GoogleSchemaCompatLayer extends SchemaCompatLayer {
26
18
  shouldApply(): boolean {
27
19
  return this.getModel().provider.includes('google') || this.getModel().modelId.includes('google');
28
20
  }
29
-
30
- processZodType(value: ZodTypeAny): ZodTypeAny {
31
- if (isOptional(value)) {
32
- return this.defaultZodOptionalHandler(value, [
33
- 'ZodObject',
34
- 'ZodArray',
35
- 'ZodUnion',
36
- 'ZodString',
37
- 'ZodNumber',
38
- ...UNSUPPORTED_ZOD_TYPES,
39
- ]);
40
- } else if (isNull(value)) {
21
+ processZodType(value: ZodTypeV3): ZodTypeV3;
22
+ processZodType(value: ZodTypeV4): ZodTypeV4;
23
+ processZodType(value: ZodTypeV3 | ZodTypeV4): ZodTypeV3 | ZodTypeV4 {
24
+ if (isOptional(z)(value)) {
25
+ return this.defaultZodOptionalHandler(value, ['ZodObject', 'ZodArray', 'ZodUnion', 'ZodString', 'ZodNumber']);
26
+ } else if (isNull(z)(value)) {
41
27
  // Google models don't support null, so we need to convert it to any and then refine it to null
42
28
  return z
43
29
  .any()
44
30
  .refine(v => v === null, { message: 'must be null' })
45
- .describe(value._def.description || 'must be null');
46
- } else if (isObj(value)) {
31
+ .describe(value.description || 'must be null');
32
+ } else if (isObj(z)(value)) {
47
33
  return this.defaultZodObjectHandler(value);
48
- } else if (isArr(value)) {
34
+ } else if (isArr(z)(value)) {
49
35
  return this.defaultZodArrayHandler(value, []);
50
- } else if (isUnion(value)) {
36
+ } else if (isUnion(z)(value)) {
51
37
  return this.defaultZodUnionHandler(value);
52
- } else if (isString(value)) {
38
+ } else if (isString(z)(value)) {
53
39
  // Google models support these properties but the model doesn't respect them, but it respects them when they're
54
40
  // added to the tool description
55
41
  return this.defaultZodStringHandler(value);
56
- } else if (isNumber(value)) {
42
+ } else if (isNumber(z)(value)) {
57
43
  // Google models support these properties but the model doesn't respect them, but it respects them when they're
58
44
  // added to the tool description
59
45
  return this.defaultZodNumberHandler(value);
60
46
  }
61
- return this.defaultUnsupportedZodTypeHandler(value);
47
+ return this.defaultUnsupportedZodTypeHandler(value as ZodObjectV4<any> | ZodObjectV3<any>);
62
48
  }
63
49
  }
@@ -1,10 +1,13 @@
1
- import type { LanguageModelV1 } from 'ai';
2
- import type { ZodTypeAny } from 'zod';
1
+ import { z } from 'zod';
2
+ import type { ZodType as ZodTypeV3 } from 'zod/v3';
3
+ import type { ZodType as ZodTypeV4 } from 'zod/v4';
3
4
  import type { Targets } from 'zod-to-json-schema';
4
- import { SchemaCompatLayer, isArr, isNumber, isObj, isOptional, isString, isUnion } from '../schema-compatibility';
5
+ import { SchemaCompatLayer } from '../schema-compatibility';
6
+ import type { ModelInformation } from '../types';
7
+ import { isOptional, isObj, isArr, isUnion, isNumber, isString } from '../zodTypes';
5
8
 
6
9
  export class MetaSchemaCompatLayer extends SchemaCompatLayer {
7
- constructor(model: LanguageModelV1) {
10
+ constructor(model: ModelInformation) {
8
11
  super(model);
9
12
  }
10
13
 
@@ -16,18 +19,20 @@ export class MetaSchemaCompatLayer extends SchemaCompatLayer {
16
19
  return this.getModel().modelId.includes('meta');
17
20
  }
18
21
 
19
- processZodType(value: ZodTypeAny): ZodTypeAny {
20
- if (isOptional(value)) {
22
+ processZodType(value: ZodTypeV3): ZodTypeV3;
23
+ processZodType(value: ZodTypeV4): ZodTypeV4;
24
+ processZodType(value: ZodTypeV3 | ZodTypeV4): ZodTypeV3 | ZodTypeV4 {
25
+ if (isOptional(z)(value)) {
21
26
  return this.defaultZodOptionalHandler(value, ['ZodObject', 'ZodArray', 'ZodUnion', 'ZodString', 'ZodNumber']);
22
- } else if (isObj(value)) {
27
+ } else if (isObj(z)(value)) {
23
28
  return this.defaultZodObjectHandler(value);
24
- } else if (isArr(value)) {
29
+ } else if (isArr(z)(value)) {
25
30
  return this.defaultZodArrayHandler(value, ['min', 'max']);
26
- } else if (isUnion(value)) {
31
+ } else if (isUnion(z)(value)) {
27
32
  return this.defaultZodUnionHandler(value);
28
- } else if (isNumber(value)) {
33
+ } else if (isNumber(z)(value)) {
29
34
  return this.defaultZodNumberHandler(value);
30
- } else if (isString(value)) {
35
+ } else if (isString(z)(value)) {
31
36
  return this.defaultZodStringHandler(value);
32
37
  }
33
38
 
@@ -1,21 +1,13 @@
1
- import type { LanguageModelV1 } from 'ai';
2
1
  import { z } from 'zod';
3
- import type { ZodTypeAny } from 'zod';
2
+ import type { ZodType as ZodTypeV3, ZodObject as ZodObjectV3 } from 'zod/v3';
3
+ import type { ZodType as ZodTypeV4, ZodObject as ZodObjectV4 } from 'zod/v4';
4
4
  import type { Targets } from 'zod-to-json-schema';
5
- import {
6
- SchemaCompatLayer,
7
- isArr,
8
- isDate,
9
- isDefault,
10
- isNumber,
11
- isObj,
12
- isOptional,
13
- isString,
14
- isUnion,
15
- } from '../schema-compatibility';
5
+ import { SchemaCompatLayer } from '../schema-compatibility';
6
+ import type { ModelInformation } from '../types';
7
+ import { isOptional, isObj, isArr, isUnion, isDefault, isNumber, isString, isDate } from '../zodTypes';
16
8
 
17
9
  export class OpenAIReasoningSchemaCompatLayer extends SchemaCompatLayer {
18
- constructor(model: LanguageModelV1) {
10
+ constructor(model: ModelInformation) {
19
11
  super(model);
20
12
  }
21
13
 
@@ -26,7 +18,11 @@ export class OpenAIReasoningSchemaCompatLayer extends SchemaCompatLayer {
26
18
  isReasoningModel(): boolean {
27
19
  // there isn't a good way to automatically detect reasoning models besides doing this.
28
20
  // in the future when o5 is released this compat wont apply and we'll want to come back and update this class + our tests
29
- return this.getModel().modelId.includes(`o3`) || this.getModel().modelId.includes(`o4`);
21
+ return (
22
+ this.getModel().modelId.includes(`o3`) ||
23
+ this.getModel().modelId.includes(`o4`) ||
24
+ this.getModel().modelId.includes(`o1`)
25
+ );
30
26
  }
31
27
 
32
28
  shouldApply(): boolean {
@@ -40,17 +36,19 @@ export class OpenAIReasoningSchemaCompatLayer extends SchemaCompatLayer {
40
36
  return false;
41
37
  }
42
38
 
43
- processZodType(value: ZodTypeAny): ZodTypeAny {
44
- if (isOptional(value)) {
39
+ processZodType(value: ZodTypeV3): ZodTypeV3;
40
+ processZodType(value: ZodTypeV4): ZodTypeV4;
41
+ processZodType(value: ZodTypeV3 | ZodTypeV4): ZodTypeV3 | ZodTypeV4 {
42
+ if (isOptional(z)(value)) {
45
43
  const innerZodType = this.processZodType(value._def.innerType);
46
44
  return innerZodType.nullable();
47
- } else if (isObj(value)) {
45
+ } else if (isObj(z)(value)) {
48
46
  return this.defaultZodObjectHandler(value, { passthrough: false });
49
- } else if (isArr(value)) {
47
+ } else if (isArr(z)(value)) {
50
48
  return this.defaultZodArrayHandler(value);
51
- } else if (isUnion(value)) {
49
+ } else if (isUnion(z)(value)) {
52
50
  return this.defaultZodUnionHandler(value);
53
- } else if (isDefault(value)) {
51
+ } else if (isDefault(z)(value)) {
54
52
  const defaultDef = value._def;
55
53
  const innerType = defaultDef.innerType;
56
54
  const defaultValue = defaultDef.defaultValue();
@@ -65,13 +63,13 @@ export class OpenAIReasoningSchemaCompatLayer extends SchemaCompatLayer {
65
63
  result = result.describe(description);
66
64
  }
67
65
  return result;
68
- } else if (isNumber(value)) {
66
+ } else if (isNumber(z)(value)) {
69
67
  return this.defaultZodNumberHandler(value);
70
- } else if (isString(value)) {
68
+ } else if (isString(z)(value)) {
71
69
  return this.defaultZodStringHandler(value);
72
- } else if (isDate(value)) {
70
+ } else if (isDate(z)(value)) {
73
71
  return this.defaultZodDateHandler(value);
74
- } else if (value._def.typeName === 'ZodAny') {
72
+ } else if (value.constructor.name === 'ZodAny') {
75
73
  // It's bad practice in the tool to use any, it's not reasonable for models that don't support that OOTB, to cast every single possible type
76
74
  // in the schema. Usually when it's "any" it could be a json object or a union of specific types.
77
75
  return z
@@ -82,6 +80,6 @@ export class OpenAIReasoningSchemaCompatLayer extends SchemaCompatLayer {
82
80
  );
83
81
  }
84
82
 
85
- return this.defaultUnsupportedZodTypeHandler(value);
83
+ return this.defaultUnsupportedZodTypeHandler(value as ZodObjectV4<any> | ZodObjectV3<any>);
86
84
  }
87
85
  }
@@ -1,11 +1,13 @@
1
- import type { LanguageModelV1 } from 'ai';
2
- import type { ZodTypeAny } from 'zod';
1
+ import { z } from 'zod';
2
+ import type { ZodType as ZodTypeV3, ZodObject as ZodObjectV3 } from 'zod/v3';
3
+ import type { ZodType as ZodTypeV4, ZodObject as ZodObjectV4 } from 'zod/v4';
3
4
  import type { Targets } from 'zod-to-json-schema';
4
- import { SchemaCompatLayer, isArr, isObj, isOptional, isString, isUnion } from '../schema-compatibility';
5
- import type { StringCheckType } from '../schema-compatibility';
5
+ import { SchemaCompatLayer } from '../schema-compatibility';
6
+ import type { ModelInformation } from '../types';
7
+ import { isOptional, isObj, isUnion, isArr, isString } from '../zodTypes';
6
8
 
7
9
  export class OpenAISchemaCompatLayer extends SchemaCompatLayer {
8
- constructor(model: LanguageModelV1) {
10
+ constructor(model: ModelInformation) {
9
11
  super(model);
10
12
  }
11
13
 
@@ -24,8 +26,10 @@ export class OpenAISchemaCompatLayer extends SchemaCompatLayer {
24
26
  return false;
25
27
  }
26
28
 
27
- processZodType(value: ZodTypeAny): ZodTypeAny {
28
- if (isOptional(value)) {
29
+ processZodType(value: ZodTypeV3): ZodTypeV3;
30
+ processZodType(value: ZodTypeV4): ZodTypeV4;
31
+ processZodType(value: ZodTypeV3 | ZodTypeV4): ZodTypeV3 | ZodTypeV4 {
32
+ if (isOptional(z)(value)) {
29
33
  return this.defaultZodOptionalHandler(value, [
30
34
  'ZodObject',
31
35
  'ZodArray',
@@ -35,22 +39,27 @@ export class OpenAISchemaCompatLayer extends SchemaCompatLayer {
35
39
  'ZodUndefined',
36
40
  'ZodTuple',
37
41
  ]);
38
- } else if (isObj(value)) {
42
+ } else if (isObj(z)(value)) {
39
43
  return this.defaultZodObjectHandler(value);
40
- } else if (isUnion(value)) {
44
+ } else if (isUnion(z)(value)) {
41
45
  return this.defaultZodUnionHandler(value);
42
- } else if (isArr(value)) {
46
+ } else if (isArr(z)(value)) {
43
47
  return this.defaultZodArrayHandler(value);
44
- } else if (isString(value)) {
48
+ } else if (isString(z)(value)) {
45
49
  const model = this.getModel();
46
- const checks: StringCheckType[] = ['emoji'];
50
+ const checks = ['emoji'] as const;
47
51
 
48
52
  if (model.modelId.includes('gpt-4o-mini')) {
49
- checks.push('regex');
53
+ return this.defaultZodStringHandler(value, ['emoji', 'regex']);
50
54
  }
55
+
51
56
  return this.defaultZodStringHandler(value, checks);
52
57
  }
53
58
 
54
- return this.defaultUnsupportedZodTypeHandler(value, ['ZodNever', 'ZodUndefined', 'ZodTuple']);
59
+ return this.defaultUnsupportedZodTypeHandler(value as ZodObjectV4<any> | ZodObjectV3<any>, [
60
+ 'ZodNever',
61
+ 'ZodUndefined',
62
+ 'ZodTuple',
63
+ ]);
55
64
  }
56
65
  }
@@ -40,22 +40,38 @@ describe('Provider Compatibility Classes', () => {
40
40
 
41
41
  describe('AnthropicSchemaCompatLayer', () => {
42
42
  it('should apply for Anthropic models', () => {
43
- const compat = new AnthropicSchemaCompatLayer(mockModels.anthropic);
43
+ const compat = new AnthropicSchemaCompatLayer({
44
+ modelId: mockModels.anthropic.modelId,
45
+ supportsStructuredOutputs: mockModels.anthropic.supportsStructuredOutputs ?? false,
46
+ provider: mockModels.anthropic.provider,
47
+ });
44
48
  expect(compat.shouldApply()).toBe(true);
45
49
  });
46
50
 
47
51
  it('should not apply for non-Anthropic models', () => {
48
- const compat = new AnthropicSchemaCompatLayer(mockModels.openai);
52
+ const compat = new AnthropicSchemaCompatLayer({
53
+ modelId: mockModels.openai.modelId,
54
+ supportsStructuredOutputs: mockModels.openai.supportsStructuredOutputs ?? false,
55
+ provider: mockModels.openai.provider,
56
+ });
49
57
  expect(compat.shouldApply()).toBe(false);
50
58
  });
51
59
 
52
60
  it('should return correct schema target', () => {
53
- const compat = new AnthropicSchemaCompatLayer(mockModels.anthropic);
61
+ const compat = new AnthropicSchemaCompatLayer({
62
+ modelId: mockModels.anthropic.modelId,
63
+ supportsStructuredOutputs: mockModels.anthropic.supportsStructuredOutputs ?? false,
64
+ provider: mockModels.anthropic.provider,
65
+ });
54
66
  expect(compat.getSchemaTarget()).toBe('jsonSchema7');
55
67
  });
56
68
 
57
69
  it('should process schemas correctly', () => {
58
- const compat = new AnthropicSchemaCompatLayer(mockModels.anthropic);
70
+ const compat = new AnthropicSchemaCompatLayer({
71
+ modelId: mockModels.anthropic.modelId,
72
+ supportsStructuredOutputs: mockModels.anthropic.supportsStructuredOutputs ?? false,
73
+ provider: mockModels.anthropic.provider,
74
+ });
59
75
  const schema = z.object({
60
76
  text: z.string().min(1).max(100),
61
77
  count: z.number().min(1),
@@ -79,17 +95,29 @@ describe('Provider Compatibility Classes', () => {
79
95
 
80
96
  describe('OpenAISchemaCompatLayer', () => {
81
97
  it('should apply for OpenAI models without structured outputs support', () => {
82
- const compat = new OpenAISchemaCompatLayer(mockModels.openai);
98
+ const compat = new OpenAISchemaCompatLayer({
99
+ modelId: mockModels.openai.modelId,
100
+ supportsStructuredOutputs: mockModels.openai.supportsStructuredOutputs ?? false,
101
+ provider: mockModels.openai.provider,
102
+ });
83
103
  expect(compat.shouldApply()).toBe(true);
84
104
  });
85
105
 
86
106
  it('should return correct schema target', () => {
87
- const compat = new OpenAISchemaCompatLayer(mockModels.openai);
107
+ const compat = new OpenAISchemaCompatLayer({
108
+ modelId: mockModels.openai.modelId,
109
+ supportsStructuredOutputs: mockModels.openai.supportsStructuredOutputs ?? false,
110
+ provider: mockModels.openai.provider,
111
+ });
88
112
  expect(compat.getSchemaTarget()).toBe('jsonSchema7');
89
113
  });
90
114
 
91
115
  it('should process complex schemas', () => {
92
- const compat = new OpenAISchemaCompatLayer(mockModels.openai);
116
+ const compat = new OpenAISchemaCompatLayer({
117
+ modelId: mockModels.openai.modelId,
118
+ supportsStructuredOutputs: mockModels.openai.supportsStructuredOutputs ?? false,
119
+ provider: mockModels.openai.provider,
120
+ });
93
121
  const schema = z.object({
94
122
  user: z.object({
95
123
  name: z.string().email(),
@@ -122,29 +150,49 @@ describe('Provider Compatibility Classes', () => {
122
150
 
123
151
  describe('OpenAIReasoningSchemaCompatLayer', () => {
124
152
  it('should have consistent behavior', () => {
125
- const compat = new OpenAIReasoningSchemaCompatLayer(mockModels.openaiReasoning);
153
+ const compat = new OpenAIReasoningSchemaCompatLayer({
154
+ modelId: mockModels.openaiReasoning.modelId,
155
+ supportsStructuredOutputs: mockModels.openaiReasoning.supportsStructuredOutputs ?? false,
156
+ provider: mockModels.openaiReasoning.provider,
157
+ });
126
158
  expect(compat.shouldApply()).toBe(true);
127
159
  });
128
160
 
129
161
  it('should return correct schema target', () => {
130
- const compat = new OpenAIReasoningSchemaCompatLayer(mockModels.openaiReasoning);
162
+ const compat = new OpenAIReasoningSchemaCompatLayer({
163
+ modelId: mockModels.openaiReasoning.modelId,
164
+ supportsStructuredOutputs: mockModels.openaiReasoning.supportsStructuredOutputs ?? false,
165
+ provider: mockModels.openaiReasoning.provider,
166
+ });
131
167
  expect(compat.getSchemaTarget()).toBe('openApi3');
132
168
  });
133
169
  });
134
170
 
135
171
  describe('GoogleSchemaCompatLayer', () => {
136
172
  it('should have consistent behavior', () => {
137
- const compat = new GoogleSchemaCompatLayer(mockModels.google);
173
+ const compat = new GoogleSchemaCompatLayer({
174
+ modelId: mockModels.google.modelId,
175
+ supportsStructuredOutputs: mockModels.google.supportsStructuredOutputs ?? false,
176
+ provider: mockModels.google.provider,
177
+ });
138
178
  expect(typeof compat.shouldApply()).toBe('boolean');
139
179
  });
140
180
 
141
181
  it('should return correct schema target', () => {
142
- const compat = new GoogleSchemaCompatLayer(mockModels.google);
182
+ const compat = new GoogleSchemaCompatLayer({
183
+ modelId: mockModels.google.modelId,
184
+ supportsStructuredOutputs: mockModels.google.supportsStructuredOutputs ?? false,
185
+ provider: mockModels.google.provider,
186
+ });
143
187
  expect(compat.getSchemaTarget()).toBe('jsonSchema7');
144
188
  });
145
189
 
146
190
  it('should handle date types correctly', () => {
147
- const compat = new GoogleSchemaCompatLayer(mockModels.google);
191
+ const compat = new GoogleSchemaCompatLayer({
192
+ modelId: mockModels.google.modelId,
193
+ supportsStructuredOutputs: mockModels.google.supportsStructuredOutputs ?? false,
194
+ provider: mockModels.google.provider,
195
+ });
148
196
  const schema = z.object({
149
197
  startDate: z.date(),
150
198
  endDate: z.date().optional(),
@@ -169,22 +217,38 @@ describe('Provider Compatibility Classes', () => {
169
217
 
170
218
  describe('DeepSeekSchemaCompatLayer', () => {
171
219
  it('should apply for DeepSeek models', () => {
172
- const compat = new DeepSeekSchemaCompatLayer(mockModels.deepseek);
220
+ const compat = new DeepSeekSchemaCompatLayer({
221
+ modelId: mockModels.deepseek.modelId,
222
+ supportsStructuredOutputs: mockModels.deepseek.supportsStructuredOutputs ?? false,
223
+ provider: mockModels.deepseek.provider,
224
+ });
173
225
  expect(compat.shouldApply()).toBe(true);
174
226
  });
175
227
 
176
228
  it('should not apply for non-DeepSeek models', () => {
177
- const compat = new DeepSeekSchemaCompatLayer(mockModels.openai);
229
+ const compat = new DeepSeekSchemaCompatLayer({
230
+ modelId: mockModels.openai.modelId,
231
+ supportsStructuredOutputs: mockModels.openai.supportsStructuredOutputs ?? false,
232
+ provider: mockModels.openai.provider,
233
+ });
178
234
  expect(compat.shouldApply()).toBe(false);
179
235
  });
180
236
 
181
237
  it('should return correct schema target', () => {
182
- const compat = new DeepSeekSchemaCompatLayer(mockModels.deepseek);
238
+ const compat = new DeepSeekSchemaCompatLayer({
239
+ modelId: mockModels.deepseek.modelId,
240
+ supportsStructuredOutputs: mockModels.deepseek.supportsStructuredOutputs ?? false,
241
+ provider: mockModels.deepseek.provider,
242
+ });
183
243
  expect(compat.getSchemaTarget()).toBe('jsonSchema7');
184
244
  });
185
245
 
186
246
  it('should handle string constraints', () => {
187
- const compat = new DeepSeekSchemaCompatLayer(mockModels.deepseek);
247
+ const compat = new DeepSeekSchemaCompatLayer({
248
+ modelId: mockModels.deepseek.modelId,
249
+ supportsStructuredOutputs: mockModels.deepseek.supportsStructuredOutputs ?? false,
250
+ provider: mockModels.deepseek.provider,
251
+ });
188
252
  const schema = z.object({
189
253
  email: z.string().email(),
190
254
  url: z.string().url(),
@@ -212,17 +276,29 @@ describe('Provider Compatibility Classes', () => {
212
276
 
213
277
  describe('MetaSchemaCompatLayer', () => {
214
278
  it('should have consistent behavior', () => {
215
- const compat = new MetaSchemaCompatLayer(mockModels.meta);
279
+ const compat = new MetaSchemaCompatLayer({
280
+ modelId: mockModels.meta.modelId,
281
+ supportsStructuredOutputs: mockModels.meta.supportsStructuredOutputs ?? false,
282
+ provider: mockModels.meta.provider,
283
+ });
216
284
  expect(typeof compat.shouldApply()).toBe('boolean');
217
285
  });
218
286
 
219
287
  it('should return correct schema target', () => {
220
- const compat = new MetaSchemaCompatLayer(mockModels.meta);
288
+ const compat = new MetaSchemaCompatLayer({
289
+ modelId: mockModels.meta.modelId,
290
+ supportsStructuredOutputs: mockModels.meta.supportsStructuredOutputs ?? false,
291
+ provider: mockModels.meta.provider,
292
+ });
221
293
  expect(compat.getSchemaTarget()).toBe('jsonSchema7');
222
294
  });
223
295
 
224
296
  it('should handle array and union types', () => {
225
- const compat = new MetaSchemaCompatLayer(mockModels.meta);
297
+ const compat = new MetaSchemaCompatLayer({
298
+ modelId: mockModels.meta.modelId,
299
+ supportsStructuredOutputs: mockModels.meta.supportsStructuredOutputs ?? false,
300
+ provider: mockModels.meta.provider,
301
+ });
226
302
  const schema = z.object({
227
303
  tags: z.array(z.string()).min(1).max(10),
228
304
  status: z.union([z.literal('active'), z.literal('inactive')]),
@@ -271,12 +347,31 @@ describe('Provider Compatibility Classes', () => {
271
347
  });
272
348
 
273
349
  const providers = [
274
- new AnthropicSchemaCompatLayer(mockModels.anthropic),
275
- new OpenAISchemaCompatLayer(mockModels.openai),
276
- new OpenAIReasoningSchemaCompatLayer(mockModels.openaiReasoning),
277
- new GoogleSchemaCompatLayer(mockModels.google),
278
- new DeepSeekSchemaCompatLayer(mockModels.deepseek),
279
- new MetaSchemaCompatLayer(mockModels.meta),
350
+ new AnthropicSchemaCompatLayer({
351
+ modelId: mockModels.anthropic.modelId,
352
+ supportsStructuredOutputs: mockModels.anthropic.supportsStructuredOutputs ?? false,
353
+ provider: mockModels.anthropic.provider,
354
+ }),
355
+ new OpenAISchemaCompatLayer({
356
+ modelId: mockModels.openai.modelId,
357
+ supportsStructuredOutputs: mockModels.openai.supportsStructuredOutputs ?? false,
358
+ provider: mockModels.openai.provider,
359
+ }),
360
+ new OpenAIReasoningSchemaCompatLayer({
361
+ modelId: mockModels.openaiReasoning.modelId,
362
+ supportsStructuredOutputs: mockModels.openaiReasoning.supportsStructuredOutputs ?? false,
363
+ provider: mockModels.openaiReasoning.provider,
364
+ }),
365
+ new GoogleSchemaCompatLayer({
366
+ modelId: mockModels.google.modelId,
367
+ supportsStructuredOutputs: mockModels.google.supportsStructuredOutputs ?? false,
368
+ provider: mockModels.google.provider,
369
+ }),
370
+ new MetaSchemaCompatLayer({
371
+ modelId: mockModels.meta.modelId,
372
+ supportsStructuredOutputs: mockModels.meta.supportsStructuredOutputs ?? false,
373
+ provider: mockModels.meta.provider,
374
+ }),
280
375
  ];
281
376
 
282
377
  providers.forEach(provider => {