@aeye/core 0.5.3 → 0.6.0

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 (117) hide show
  1. package/README.md +51 -4
  2. package/aeye-core-schema.md +11 -7
  3. package/dist/common.d.ts +30 -5
  4. package/dist/common.d.ts.map +1 -1
  5. package/dist/common.js +134 -129
  6. package/dist/common.js.map +1 -1
  7. package/dist/delivery.d.ts +39 -0
  8. package/dist/delivery.d.ts.map +1 -0
  9. package/dist/delivery.js +127 -0
  10. package/dist/delivery.js.map +1 -0
  11. package/dist/errors.d.ts +60 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +84 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/prompt.d.ts +101 -9
  20. package/dist/prompt.d.ts.map +1 -1
  21. package/dist/prompt.js +163 -141
  22. package/dist/prompt.js.map +1 -1
  23. package/dist/schema/analyze.d.ts +94 -0
  24. package/dist/schema/analyze.d.ts.map +1 -0
  25. package/dist/schema/analyze.js +221 -0
  26. package/dist/schema/analyze.js.map +1 -0
  27. package/dist/schema/any-value.d.ts +32 -0
  28. package/dist/schema/any-value.d.ts.map +1 -0
  29. package/dist/schema/any-value.js +77 -0
  30. package/dist/schema/any-value.js.map +1 -0
  31. package/dist/schema/budget.d.ts +114 -0
  32. package/dist/schema/budget.d.ts.map +1 -0
  33. package/dist/schema/budget.js +223 -0
  34. package/dist/schema/budget.js.map +1 -0
  35. package/dist/schema/consistency.d.ts +27 -0
  36. package/dist/schema/consistency.d.ts.map +1 -0
  37. package/dist/schema/consistency.js +110 -0
  38. package/dist/schema/consistency.js.map +1 -0
  39. package/dist/schema/cycle-breaker.d.ts +14 -0
  40. package/dist/schema/cycle-breaker.d.ts.map +1 -0
  41. package/dist/schema/cycle-breaker.js +157 -0
  42. package/dist/schema/cycle-breaker.js.map +1 -0
  43. package/dist/schema/decode.d.ts +31 -0
  44. package/dist/schema/decode.d.ts.map +1 -0
  45. package/dist/schema/decode.js +207 -0
  46. package/dist/schema/decode.js.map +1 -0
  47. package/dist/schema/descriptor.d.ts +300 -0
  48. package/dist/schema/descriptor.d.ts.map +1 -0
  49. package/dist/schema/descriptor.js +15 -0
  50. package/dist/schema/descriptor.js.map +1 -0
  51. package/dist/schema/dialects/anthropic.d.ts +34 -0
  52. package/dist/schema/dialects/anthropic.d.ts.map +1 -0
  53. package/dist/schema/dialects/anthropic.js +77 -0
  54. package/dist/schema/dialects/anthropic.js.map +1 -0
  55. package/dist/schema/dialects/google.d.ts +47 -0
  56. package/dist/schema/dialects/google.d.ts.map +1 -0
  57. package/dist/schema/dialects/google.js +91 -0
  58. package/dist/schema/dialects/google.js.map +1 -0
  59. package/dist/schema/dialects/lenient.d.ts +4 -0
  60. package/dist/schema/dialects/lenient.d.ts.map +1 -0
  61. package/dist/schema/dialects/lenient.js +26 -0
  62. package/dist/schema/dialects/lenient.js.map +1 -0
  63. package/dist/schema/dialects/openai.d.ts +40 -0
  64. package/dist/schema/dialects/openai.d.ts.map +1 -0
  65. package/dist/schema/dialects/openai.js +81 -0
  66. package/dist/schema/dialects/openai.js.map +1 -0
  67. package/dist/schema/dialects/xai.d.ts +23 -0
  68. package/dist/schema/dialects/xai.d.ts.map +1 -0
  69. package/dist/schema/dialects/xai.js +53 -0
  70. package/dist/schema/dialects/xai.js.map +1 -0
  71. package/dist/schema/enum-values.d.ts +44 -0
  72. package/dist/schema/enum-values.d.ts.map +1 -0
  73. package/dist/schema/enum-values.js +56 -0
  74. package/dist/schema/enum-values.js.map +1 -0
  75. package/dist/schema/expressible.d.ts +59 -0
  76. package/dist/schema/expressible.d.ts.map +1 -0
  77. package/dist/schema/expressible.js +161 -0
  78. package/dist/schema/expressible.js.map +1 -0
  79. package/dist/schema/index.d.ts +20 -0
  80. package/dist/schema/index.d.ts.map +1 -0
  81. package/dist/schema/index.js +20 -0
  82. package/dist/schema/index.js.map +1 -0
  83. package/dist/schema/json-schema.d.ts +64 -0
  84. package/dist/schema/json-schema.d.ts.map +1 -0
  85. package/dist/schema/json-schema.js +2 -0
  86. package/dist/schema/json-schema.js.map +1 -0
  87. package/dist/schema/registry.d.ts +73 -0
  88. package/dist/schema/registry.d.ts.map +1 -0
  89. package/dist/schema/registry.js +135 -0
  90. package/dist/schema/registry.js.map +1 -0
  91. package/dist/schema/strictify.d.ts +19 -0
  92. package/dist/schema/strictify.d.ts.map +1 -0
  93. package/dist/schema/strictify.js +237 -0
  94. package/dist/schema/strictify.js.map +1 -0
  95. package/dist/schema/to-json-schema.d.ts +29 -0
  96. package/dist/schema/to-json-schema.d.ts.map +1 -0
  97. package/dist/schema/to-json-schema.js +691 -0
  98. package/dist/schema/to-json-schema.js.map +1 -0
  99. package/dist/schema/walk-key.d.ts +24 -0
  100. package/dist/schema/walk-key.d.ts.map +1 -0
  101. package/dist/schema/walk-key.js +33 -0
  102. package/dist/schema/walk-key.js.map +1 -0
  103. package/dist/to.js +1 -1
  104. package/dist/to.js.map +1 -1
  105. package/dist/tool.d.ts +32 -3
  106. package/dist/tool.d.ts.map +1 -1
  107. package/dist/tool.js +41 -0
  108. package/dist/tool.js.map +1 -1
  109. package/dist/types.d.ts +265 -8
  110. package/dist/types.d.ts.map +1 -1
  111. package/dist/types.js +4 -1
  112. package/dist/types.js.map +1 -1
  113. package/package.json +72 -72
  114. package/dist/schema.d.ts +0 -837
  115. package/dist/schema.d.ts.map +0 -1
  116. package/dist/schema.js +0 -2472
  117. package/dist/schema.js.map +0 -1
@@ -0,0 +1,77 @@
1
+ /**
2
+ * True when the descriptor's "any" encoding is a self-referencing definition,
3
+ * i.e. it has to be hoisted into `$defs/Any` and referenced by `$ref`. The
4
+ * non-recursive encodings (`flat`, `unconstrained`) are inlined at each use
5
+ * site instead, so no `$defs` entry is ever created for them.
6
+ */
7
+ export function usesSharedAnyDefinition(descriptor) {
8
+ return descriptor.anyEncoding === 'recursive-strict'
9
+ || descriptor.anyEncoding === 'recursive-open';
10
+ }
11
+ /**
12
+ * Builds the body of the "any value" schema — for most dialects an `anyOf`
13
+ * covering every JSON value (string, number, boolean, null, array, object).
14
+ *
15
+ * Encoding modes:
16
+ * - `recursive-strict` (OpenAI strict): self-referencing `$defs/Any` with
17
+ * array-of-pairs records (no open-object support in strict mode).
18
+ * - `recursive-open` (Lenient): self-referencing `$defs/Any` with
19
+ * `additionalProperties: <self>` records.
20
+ * - `flat` (xAI strict): non-recursive — array branch has no `items`
21
+ * constraint, object branch is `additionalProperties: true`. Equivalent
22
+ * to TypeScript `any`; safe under descriptors that reject cyclic `$defs`.
23
+ * - `unconstrained` (Google): the empty schema `{}`. A schema with no
24
+ * assertion keywords validates every instance, so this says "any JSON
25
+ * value" using zero keywords — the only option for a dialect that forbids
26
+ * `anyOf` AND named `$defs`/`$ref`. The caller re-attaches `description`
27
+ * from the source schema's metadata, so the model still gets the prose.
28
+ * - `none` (Anthropic strict): no spelling exists. `expressible` keeps such a
29
+ * schema off a strict request, so this is only reached by a direct
30
+ * `toJSONSchema` call, which gets the empty schema.
31
+ */
32
+ export function buildAnyValueSchema(descriptor) {
33
+ if (descriptor.anyEncoding === 'unconstrained' || descriptor.anyEncoding === 'none') {
34
+ return {};
35
+ }
36
+ const isFlat = descriptor.anyEncoding === 'flat';
37
+ const selfRef = isFlat ? {} : { $ref: '#/$defs/Any' };
38
+ const arrayBranch = isFlat
39
+ ? { type: 'array' }
40
+ : { type: 'array', items: selfRef };
41
+ const branches = [
42
+ { type: 'string' },
43
+ { type: 'number' },
44
+ { type: 'boolean' },
45
+ { type: 'null' },
46
+ arrayBranch,
47
+ ];
48
+ if (descriptor.anyEncoding === 'recursive-strict') {
49
+ branches.push({
50
+ type: 'array',
51
+ items: {
52
+ type: 'object',
53
+ properties: {
54
+ key: { type: 'string' },
55
+ value: selfRef,
56
+ },
57
+ required: ['key', 'value'],
58
+ additionalProperties: false,
59
+ },
60
+ });
61
+ }
62
+ else if (descriptor.anyEncoding === 'recursive-open') {
63
+ branches.push({
64
+ type: 'object',
65
+ additionalProperties: selfRef,
66
+ });
67
+ }
68
+ else {
69
+ // flat
70
+ branches.push({
71
+ type: 'object',
72
+ additionalProperties: true,
73
+ });
74
+ }
75
+ return { anyOf: branches };
76
+ }
77
+ //# sourceMappingURL=any-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"any-value.js","sourceRoot":"","sources":["../../src/schema/any-value.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAA4B;IAClE,OAAO,UAAU,CAAC,WAAW,KAAK,kBAAkB;WAC/C,UAAU,CAAC,WAAW,KAAK,gBAAgB,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAA4B;IAC9D,IAAI,UAAU,CAAC,WAAW,KAAK,eAAe,IAAI,UAAU,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;QACpF,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,KAAK,MAAM,CAAC;IACjD,MAAM,OAAO,GAAe,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAClE,MAAM,WAAW,GAAe,MAAM;QACpC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;QACnB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAiB;QAC7B,EAAE,IAAI,EAAE,QAAQ,EAAE;QAClB,EAAE,IAAI,EAAE,QAAQ,EAAE;QAClB,EAAE,IAAI,EAAE,SAAS,EAAE;QACnB,EAAE,IAAI,EAAE,MAAM,EAAE;QAChB,WAAW;KACZ,CAAC;IACF,IAAI,UAAU,CAAC,WAAW,KAAK,kBAAkB,EAAE,CAAC;QAClD,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,OAAO;YACb,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACvB,KAAK,EAAE,OAAO;iBACf;gBACD,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;gBAC1B,oBAAoB,EAAE,KAAK;aAC5B;SACF,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,UAAU,CAAC,WAAW,KAAK,gBAAgB,EAAE,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ;YACd,oBAAoB,EAAE,OAAO;SAC9B,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO;QACP,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ;YACd,oBAAoB,EAAE,IAAI;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,114 @@
1
+ import z from 'zod';
2
+ import type { FormatDescriptor } from './descriptor';
3
+ /**
4
+ * Per-request strict-mode allocator.
5
+ *
6
+ * Constructed once per outgoing request with the chosen model's tool and
7
+ * structured-output descriptors (the same one when only one is given; they
8
+ * differ when the model has strict tools but not strict output, or the
9
+ * reverse). The slot limits come from the stricter of the two. Tracks remaining slots (strict tools, optional parameters,
10
+ * union types) against the descriptor's documented per-request limits and
11
+ * decides per-tool / per-output whether to emit strict or fall back to the
12
+ * SAME FAMILY's non-strict descriptor (`degraded` — never the family-blind
13
+ * `LENIENT`, which would undo dialect rules that outlive strict mode).
14
+ *
15
+ * Selection guarantees that any tool with `strict: true` (hard requirement)
16
+ * has a model that supports the family — `allocate` therefore always grants
17
+ * strict for `requested === true` (subject only to feasibility, not budget;
18
+ * if `true` items overflow the budget the API call will fail, which is the
19
+ * correct behavior for hard requirements).
20
+ *
21
+ * Numeric `strict: <priority>` items are allocated greedily after `true`
22
+ * items have consumed their share of the budget. Caller is expected to
23
+ * pre-sort by descending priority before iterating.
24
+ */
25
+ export declare class SchemaBudget {
26
+ private readonly tool;
27
+ private readonly output;
28
+ /**
29
+ * What an item that does NOT get strict is emitted through: the descriptor's
30
+ * own family, non-strict.
31
+ *
32
+ * It is deliberately NOT the family-blind `LENIENT`. A dialect restriction
33
+ * can belong to the DIALECT rather than to strict mode — `GOOGLE_NON_STRICT`
34
+ * exists for exactly one such rule, the `unconstrained` encoding of
35
+ * `z.any()`, because Gemini builds a decoding grammar whenever a tool call is
36
+ * forced, with no per-tool strict flag involved. Degrading to `LENIENT` put
37
+ * the self-referencing `$defs/Any` that encoding was created to avoid back on
38
+ * the Google wire — and it did so for every gin/query-style recursive `anyOf`
39
+ * schema, i.e. the schemas that ALWAYS degrade, since `GOOGLE_STRICT` cannot
40
+ * express `anyOf` at all (measured: the product's real `api_set` tool schema
41
+ * allocates to `lenient` under `GOOGLE_STRICT`, and its emitted JSON carries
42
+ * nine `#/$defs/Any` references).
43
+ *
44
+ * For the other families this is behaviour-neutral by construction:
45
+ * `OPENAI_NON_STRICT` and `ANTHROPIC_NON_STRICT` are `LENIENT` with a family
46
+ * tag, and a family with no registered non-strict variant resolves to
47
+ * `LENIENT` (`getDescriptor`). Only the pinned `descriptor` id on the tool
48
+ * changes, which round-trips through `getDescriptorById`.
49
+ */
50
+ private readonly degradedTool;
51
+ private readonly degradedOutput;
52
+ private remainingTools;
53
+ private remainingOptionalParams;
54
+ private remainingUnionTypes;
55
+ constructor(tool: FormatDescriptor, output?: FormatDescriptor);
56
+ /**
57
+ * Decide the effective descriptor for a tool's parameter schema given the
58
+ * dev's strict request, the schema's features, and the remaining budget.
59
+ *
60
+ * Returns the family's NON-STRICT descriptor (`LENIENT` for a family that
61
+ * registers none) when:
62
+ * - `requested === false`
63
+ * - the schema uses a feature the descriptor can't represent
64
+ * - the schema exceeds the dialect's per-schema size limits
65
+ * (`checkSchemaSizeLimits`)
66
+ * - the descriptor has slot limits and the budget is exhausted (only
67
+ * applies to `requested` being a positive number; `true` always wins
68
+ * subject to feasibility and size)
69
+ *
70
+ * Returns the strict descriptor (and decrements the budget) otherwise.
71
+ */
72
+ allocateTool(schema: z.ZodType | z.core.$ZodType, requested: boolean | number | undefined): FormatDescriptor;
73
+ /**
74
+ * Same as `allocateTool` but for the request's structured-output schema.
75
+ * The output schema doesn't consume a tool slot but it does count toward
76
+ * the optional-parameter and union-type budgets — descriptors document
77
+ * those limits as "across all strict schemas in one request".
78
+ */
79
+ allocateOutput(schema: z.ZodType | z.core.$ZodType, requested: boolean | number | undefined): FormatDescriptor;
80
+ /** Snapshot of remaining budget. Useful for telemetry / debug logs. */
81
+ remaining(): {
82
+ strictTools: number;
83
+ optionalParams: number;
84
+ unionTypes: number;
85
+ };
86
+ private allocate;
87
+ }
88
+ /**
89
+ * Compare two descriptors and return the one with the tighter per-request
90
+ * budget. Used by providers to pick a single descriptor for a SchemaBudget
91
+ * shared between `convertTools` and `convertResponseFormat` — Anthropic's
92
+ * documented limits apply across the whole request, so the strictest
93
+ * descriptor wins.
94
+ *
95
+ * `schemaSizeLimits` deliberately does NOT participate. Those bounds are
96
+ * per-schema, so they are evaluated against whichever descriptor this returns
97
+ * rather than shared between the two — and every provider here passes two
98
+ * descriptors resolved from the SAME family, so there is nothing to break the
99
+ * tie on. Ranking six size bounds against three slot budgets would be an
100
+ * invented ordering, not a documented one.
101
+ */
102
+ export declare function strictestOf(a: FormatDescriptor, b: FormatDescriptor): FormatDescriptor;
103
+ /**
104
+ * Convert a `boolean | number | undefined` strict request into a numeric
105
+ * priority for sorting. Used by providers to allocate strict slots in
106
+ * descending priority order.
107
+ *
108
+ * - `true` → `+Infinity` (hard requirement; always first in line)
109
+ * - `false` → `-Infinity` (always lenient; never wants strict)
110
+ * - `number > 0` → the number itself
111
+ * - `number <= 0` or `undefined` → `0` (no preference)
112
+ */
113
+ export declare function strictPriority(requested: boolean | number | undefined): number;
114
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/schema/budget.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmB;IAClD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,mBAAmB,CAAS;gBAExB,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAE,gBAAuB;IAanE;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CACV,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EACnC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GACtC,gBAAgB;IAInB;;;;;OAKG;IACH,cAAc,CACZ,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EACnC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GACtC,gBAAgB;IAInB,uEAAuE;IACvE,SAAS,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAQhF,OAAO,CAAC,QAAQ;CAkEjB;AAYD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAWtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAK9E"}
@@ -0,0 +1,223 @@
1
+ import { getDescriptor } from './registry';
2
+ import { fitsStrictAlone, sendableUnder } from './expressible';
3
+ import { analyzeSchema } from './analyze';
4
+ /**
5
+ * Per-request strict-mode allocator.
6
+ *
7
+ * Constructed once per outgoing request with the chosen model's tool and
8
+ * structured-output descriptors (the same one when only one is given; they
9
+ * differ when the model has strict tools but not strict output, or the
10
+ * reverse). The slot limits come from the stricter of the two. Tracks remaining slots (strict tools, optional parameters,
11
+ * union types) against the descriptor's documented per-request limits and
12
+ * decides per-tool / per-output whether to emit strict or fall back to the
13
+ * SAME FAMILY's non-strict descriptor (`degraded` — never the family-blind
14
+ * `LENIENT`, which would undo dialect rules that outlive strict mode).
15
+ *
16
+ * Selection guarantees that any tool with `strict: true` (hard requirement)
17
+ * has a model that supports the family — `allocate` therefore always grants
18
+ * strict for `requested === true` (subject only to feasibility, not budget;
19
+ * if `true` items overflow the budget the API call will fail, which is the
20
+ * correct behavior for hard requirements).
21
+ *
22
+ * Numeric `strict: <priority>` items are allocated greedily after `true`
23
+ * items have consumed their share of the budget. Caller is expected to
24
+ * pre-sort by descending priority before iterating.
25
+ */
26
+ export class SchemaBudget {
27
+ tool;
28
+ output;
29
+ /**
30
+ * What an item that does NOT get strict is emitted through: the descriptor's
31
+ * own family, non-strict.
32
+ *
33
+ * It is deliberately NOT the family-blind `LENIENT`. A dialect restriction
34
+ * can belong to the DIALECT rather than to strict mode — `GOOGLE_NON_STRICT`
35
+ * exists for exactly one such rule, the `unconstrained` encoding of
36
+ * `z.any()`, because Gemini builds a decoding grammar whenever a tool call is
37
+ * forced, with no per-tool strict flag involved. Degrading to `LENIENT` put
38
+ * the self-referencing `$defs/Any` that encoding was created to avoid back on
39
+ * the Google wire — and it did so for every gin/query-style recursive `anyOf`
40
+ * schema, i.e. the schemas that ALWAYS degrade, since `GOOGLE_STRICT` cannot
41
+ * express `anyOf` at all (measured: the product's real `api_set` tool schema
42
+ * allocates to `lenient` under `GOOGLE_STRICT`, and its emitted JSON carries
43
+ * nine `#/$defs/Any` references).
44
+ *
45
+ * For the other families this is behaviour-neutral by construction:
46
+ * `OPENAI_NON_STRICT` and `ANTHROPIC_NON_STRICT` are `LENIENT` with a family
47
+ * tag, and a family with no registered non-strict variant resolves to
48
+ * `LENIENT` (`getDescriptor`). Only the pinned `descriptor` id on the tool
49
+ * changes, which round-trips through `getDescriptorById`.
50
+ */
51
+ degradedTool;
52
+ degradedOutput;
53
+ remainingTools;
54
+ remainingOptionalParams;
55
+ remainingUnionTypes;
56
+ constructor(tool, output = tool) {
57
+ this.tool = tool;
58
+ this.output = output;
59
+ this.degradedTool = degrade(tool);
60
+ // A family with no non-strict structured output keeps the strict dialect
61
+ // (`canExpress` already kept an unfitting schema off the wire)
62
+ this.degradedOutput = output.structuredOutputAlwaysStrict ? output : degrade(output);
63
+ const descriptor = strictestOf(tool, output);
64
+ this.remainingTools = descriptor.maxStrictTools ?? Infinity;
65
+ this.remainingOptionalParams = descriptor.maxStrictOptionalParams ?? Infinity;
66
+ this.remainingUnionTypes = descriptor.maxStrictUnionTypes ?? Infinity;
67
+ }
68
+ /**
69
+ * Decide the effective descriptor for a tool's parameter schema given the
70
+ * dev's strict request, the schema's features, and the remaining budget.
71
+ *
72
+ * Returns the family's NON-STRICT descriptor (`LENIENT` for a family that
73
+ * registers none) when:
74
+ * - `requested === false`
75
+ * - the schema uses a feature the descriptor can't represent
76
+ * - the schema exceeds the dialect's per-schema size limits
77
+ * (`checkSchemaSizeLimits`)
78
+ * - the descriptor has slot limits and the budget is exhausted (only
79
+ * applies to `requested` being a positive number; `true` always wins
80
+ * subject to feasibility and size)
81
+ *
82
+ * Returns the strict descriptor (and decrements the budget) otherwise.
83
+ */
84
+ allocateTool(schema, requested) {
85
+ return this.allocate(schema, requested, /* isTool */ true);
86
+ }
87
+ /**
88
+ * Same as `allocateTool` but for the request's structured-output schema.
89
+ * The output schema doesn't consume a tool slot but it does count toward
90
+ * the optional-parameter and union-type budgets — descriptors document
91
+ * those limits as "across all strict schemas in one request".
92
+ */
93
+ allocateOutput(schema, requested) {
94
+ return this.allocate(schema, requested, /* isTool */ false);
95
+ }
96
+ /** Snapshot of remaining budget. Useful for telemetry / debug logs. */
97
+ remaining() {
98
+ return {
99
+ strictTools: this.remainingTools,
100
+ optionalParams: this.remainingOptionalParams,
101
+ unionTypes: this.remainingUnionTypes,
102
+ };
103
+ }
104
+ allocate(schema, requested, isTool) {
105
+ const descriptor = isTool ? this.tool : this.output;
106
+ const degraded = isTool ? this.degradedTool : this.degradedOutput;
107
+ // strict: false → always non-strict, no budget movement.
108
+ if (requested === false)
109
+ return degraded;
110
+ // Descriptor itself is lenient → nothing to allocate.
111
+ if (!descriptor.strict)
112
+ return degraded;
113
+ const isHardRequired = requested === true;
114
+ const isPreferredNumeric = typeof requested === 'number' && requested > 0;
115
+ if (!isHardRequired && !isPreferredNumeric) {
116
+ // requested is undefined or 0 — treat as no preference; default to
117
+ // non-strict so callers explicitly opt into strict. Decided BEFORE any
118
+ // walk of `schema`: analysing a schema whose verdict is already
119
+ // non-strict is pure waste on the hot path of every tool of every request.
120
+ return degraded;
121
+ }
122
+ // FEASIBILITY, before budget — the second clause of this method's contract,
123
+ // and the one that was documented but never implemented. Emitting a strict
124
+ // schema carrying a combinator the descriptor forbids is a guaranteed
125
+ // provider 400, not a degradation: `GOOGLE_STRICT` forbids `anyOf`, and the
126
+ // recursive union a gin/query codegen layer produces is nothing but.
127
+ // Degrading to the family's non-strict descriptor is the documented answer
128
+ // for a TOOL (unlike a structured OUTPUT, a tool schema has no prompt-text
129
+ // delivery to fall back to — `applySchemaDeliveryFallback` covers
130
+ // `responseFormat` only), and it is also what keeps the API-level
131
+ // `strict: true` flag off the wire, since providers set it from
132
+ // `descriptor.strict`.
133
+ //
134
+ // `sendableUnder`, not `canExpress`: a cycle is always sendable (the
135
+ // emitter's bounded placeholder), only looser — see that function's note on
136
+ // why the two questions are different.
137
+ if (!sendableUnder(schema, descriptor))
138
+ return degraded;
139
+ // SIZE, still before budget and still regardless of priority: the per-SCHEMA
140
+ // bounds (see `checkSchemaSizeLimits`), and the per-request budgets when
141
+ // this schema alone exceeds them, cannot be met by anything else in the
142
+ // request, and a hard `strict: true` cannot force a schema the API will
143
+ // refuse to compile.
144
+ if (!fitsStrictAlone(schema, descriptor))
145
+ return degraded;
146
+ const features = analyzeSchema(schema);
147
+ // Soft-priority items must fit the budget. Hard-required items skip
148
+ // budget checks because selection already promised the model can take
149
+ // them — if there are too many, that's a request-construction bug we
150
+ // surface via API error rather than degrading silently.
151
+ if (isPreferredNumeric) {
152
+ if (isTool && this.remainingTools <= 0)
153
+ return degraded;
154
+ if (this.remainingOptionalParams - features.optionalParameterCount < 0)
155
+ return degraded;
156
+ if (this.remainingUnionTypes - features.unionTypeCount < 0)
157
+ return degraded;
158
+ }
159
+ // Allocation succeeded — decrement.
160
+ if (isTool)
161
+ this.remainingTools -= 1;
162
+ this.remainingOptionalParams -= features.optionalParameterCount;
163
+ this.remainingUnionTypes -= features.unionTypeCount;
164
+ return descriptor;
165
+ }
166
+ }
167
+ /**
168
+ * What an item that does not get strict is emitted through (see
169
+ * `SchemaBudget.degradedTool`). An already-non-strict descriptor degrades to
170
+ * itself: there is nothing looser in its family, and `LENIENT`'s own family
171
+ * has no other variant.
172
+ */
173
+ function degrade(descriptor) {
174
+ return descriptor.strict ? getDescriptor(descriptor.family, false) : descriptor;
175
+ }
176
+ /**
177
+ * Compare two descriptors and return the one with the tighter per-request
178
+ * budget. Used by providers to pick a single descriptor for a SchemaBudget
179
+ * shared between `convertTools` and `convertResponseFormat` — Anthropic's
180
+ * documented limits apply across the whole request, so the strictest
181
+ * descriptor wins.
182
+ *
183
+ * `schemaSizeLimits` deliberately does NOT participate. Those bounds are
184
+ * per-schema, so they are evaluated against whichever descriptor this returns
185
+ * rather than shared between the two — and every provider here passes two
186
+ * descriptors resolved from the SAME family, so there is nothing to break the
187
+ * tie on. Ranking six size bounds against three slot budgets would be an
188
+ * invented ordering, not a documented one.
189
+ */
190
+ export function strictestOf(a, b) {
191
+ // LENIENT is the loosest — pick the strict side if either is strict.
192
+ if (a.strict && !b.strict)
193
+ return a;
194
+ if (b.strict && !a.strict)
195
+ return b;
196
+ if (!a.strict && !b.strict)
197
+ return a;
198
+ // Both strict — pick the one with the smallest documented limits.
199
+ const score = (d) => (d.maxStrictTools ?? Infinity) +
200
+ (d.maxStrictOptionalParams ?? Infinity) +
201
+ (d.maxStrictUnionTypes ?? Infinity);
202
+ return score(a) <= score(b) ? a : b;
203
+ }
204
+ /**
205
+ * Convert a `boolean | number | undefined` strict request into a numeric
206
+ * priority for sorting. Used by providers to allocate strict slots in
207
+ * descending priority order.
208
+ *
209
+ * - `true` → `+Infinity` (hard requirement; always first in line)
210
+ * - `false` → `-Infinity` (always lenient; never wants strict)
211
+ * - `number > 0` → the number itself
212
+ * - `number <= 0` or `undefined` → `0` (no preference)
213
+ */
214
+ export function strictPriority(requested) {
215
+ if (requested === true)
216
+ return Infinity;
217
+ if (requested === false)
218
+ return -Infinity;
219
+ if (typeof requested === 'number')
220
+ return requested > 0 ? requested : 0;
221
+ return 0;
222
+ }
223
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/schema/budget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,YAAY;IACN,IAAI,CAAmB;IACvB,MAAM,CAAmB;IAC1C;;;;;;;;;;;;;;;;;;;;;OAqBG;IACc,YAAY,CAAmB;IAC/B,cAAc,CAAmB;IAC1C,cAAc,CAAS;IACvB,uBAAuB,CAAS;IAChC,mBAAmB,CAAS;IAEpC,YAAY,IAAsB,EAAE,SAA2B,IAAI;QACjE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAClC,yEAAyE;QACzE,+DAA+D;QAC/D,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACrF,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,cAAc,IAAI,QAAQ,CAAC;QAC5D,IAAI,CAAC,uBAAuB,GAAG,UAAU,CAAC,uBAAuB,IAAI,QAAQ,CAAC;QAC9E,IAAI,CAAC,mBAAmB,GAAG,UAAU,CAAC,mBAAmB,IAAI,QAAQ,CAAC;IACxE,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CACV,MAAmC,EACnC,SAAuC;QAEvC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;OAKG;IACH,cAAc,CACZ,MAAmC,EACnC,SAAuC;QAEvC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC;IAED,uEAAuE;IACvE,SAAS;QACP,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,cAAc;YAChC,cAAc,EAAE,IAAI,CAAC,uBAAuB;YAC5C,UAAU,EAAE,IAAI,CAAC,mBAAmB;SACrC,CAAC;IACJ,CAAC;IAEO,QAAQ,CACd,MAAmC,EACnC,SAAuC,EACvC,MAAe;QAEf,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QACpD,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;QAClE,yDAAyD;QACzD,IAAI,SAAS,KAAK,KAAK;YAAE,OAAO,QAAQ,CAAC;QACzC,sDAAsD;QACtD,IAAI,CAAC,UAAU,CAAC,MAAM;YAAE,OAAO,QAAQ,CAAC;QAExC,MAAM,cAAc,GAAG,SAAS,KAAK,IAAI,CAAC;QAC1C,MAAM,kBAAkB,GAAG,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,GAAG,CAAC,CAAC;QAE1E,IAAI,CAAC,cAAc,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC3C,mEAAmE;YACnE,uEAAuE;YACvE,gEAAgE;YAChE,2EAA2E;YAC3E,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,4EAA4E;QAC5E,2EAA2E;QAC3E,sEAAsE;QACtE,4EAA4E;QAC5E,qEAAqE;QACrE,2EAA2E;QAC3E,2EAA2E;QAC3E,kEAAkE;QAClE,kEAAkE;QAClE,gEAAgE;QAChE,uBAAuB;QACvB,EAAE;QACF,qEAAqE;QACrE,4EAA4E;QAC5E,uCAAuC;QACvC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC;YAAE,OAAO,QAAQ,CAAC;QAExD,6EAA6E;QAC7E,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,qBAAqB;QACrB,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC;YAAE,OAAO,QAAQ,CAAC;QAE1D,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAEvC,oEAAoE;QACpE,sEAAsE;QACtE,qEAAqE;QACrE,wDAAwD;QACxD,IAAI,kBAAkB,EAAE,CAAC;YACvB,IAAI,MAAM,IAAI,IAAI,CAAC,cAAc,IAAI,CAAC;gBAAE,OAAO,QAAQ,CAAC;YACxD,IAAI,IAAI,CAAC,uBAAuB,GAAG,QAAQ,CAAC,sBAAsB,GAAG,CAAC;gBAAE,OAAO,QAAQ,CAAC;YACxF,IAAI,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAC,cAAc,GAAG,CAAC;gBAAE,OAAO,QAAQ,CAAC;QAC9E,CAAC;QAED,oCAAoC;QACpC,IAAI,MAAM;YAAE,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,uBAAuB,IAAI,QAAQ,CAAC,sBAAsB,CAAC;QAChE,IAAI,CAAC,mBAAmB,IAAI,QAAQ,CAAC,cAAc,CAAC;QAEpD,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AAED;;;;;GAKG;AACH,SAAS,OAAO,CAAC,UAA4B;IAC3C,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AAClF,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,CAAmB,EAAE,CAAmB;IAClE,qEAAqE;IACrE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACrC,kEAAkE;IAClE,MAAM,KAAK,GAAG,CAAC,CAAmB,EAAU,EAAE,CAC5C,CAAC,CAAC,CAAC,cAAc,IAAI,QAAQ,CAAC;QAC9B,CAAC,CAAC,CAAC,uBAAuB,IAAI,QAAQ,CAAC;QACvC,CAAC,CAAC,CAAC,mBAAmB,IAAI,QAAQ,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,SAAuC;IACpE,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,CAAC,QAAQ,CAAC;IAC1C,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { FormatDescriptor } from './descriptor';
2
+ /**
3
+ * Report the ways a `FormatDescriptor` contradicts itself — settings that ask
4
+ * the emitter to produce a keyword the same descriptor forbids, or a limit
5
+ * that would quietly strip every constraint it governs. Returns one
6
+ * human-readable line per problem; an empty array means the descriptor is
7
+ * internally consistent.
8
+ *
9
+ * This exists because a contradiction here is invisible until a provider
10
+ * answers HTTP 400: `GOOGLE_STRICT` shipped with `allowAnyOf: false` +
11
+ * `allowDefsRef: false` AND `anyEncoding: 'recursive-open'` (which needs
12
+ * both), so every `z.any()` in a Gemini tool schema emitted exactly the
13
+ * `anyOf` + `$defs/Any` shape the descriptor declared unrepresentable.
14
+ * Nothing checked, so nothing caught it.
15
+ *
16
+ * Called by `registerDescriptor` (which warns) and asserted over every
17
+ * built-in by the test suite.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * checkDescriptorConsistency(GOOGLE_STRICT); // → []
22
+ * checkDescriptorConsistency({ ...GOOGLE_STRICT, anyEncoding: 'flat' });
23
+ * // → ["anyEncoding 'flat' emits `anyOf`, but allowAnyOf is false"]
24
+ * ```
25
+ */
26
+ export declare function checkDescriptorConsistency(descriptor: FormatDescriptor): string[];
27
+ //# sourceMappingURL=consistency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consistency.d.ts","sourceRoot":"","sources":["../../src/schema/consistency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,cAAc,CAAC;AAsBvE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,EAAE,CA6BjF"}
@@ -0,0 +1,110 @@
1
+ // ============================================================================
2
+ // Descriptor self-consistency
3
+ // ============================================================================
4
+ /**
5
+ * JSON-Schema keywords each `anyEncoding` has to emit. Single source of truth
6
+ * for both `buildAnyValueSchema` (which produces the shape) and
7
+ * `checkDescriptorConsistency` (which checks the descriptor is allowed to).
8
+ */
9
+ const ANY_ENCODING_REQUIREMENTS = Object.freeze({
10
+ 'recursive-strict': { anyOf: true, defsRef: true },
11
+ 'recursive-open': { anyOf: true, defsRef: true },
12
+ 'flat': { anyOf: true, defsRef: false },
13
+ 'unconstrained': { anyOf: false, defsRef: false },
14
+ 'none': { anyOf: false, defsRef: false },
15
+ });
16
+ /**
17
+ * Report the ways a `FormatDescriptor` contradicts itself — settings that ask
18
+ * the emitter to produce a keyword the same descriptor forbids, or a limit
19
+ * that would quietly strip every constraint it governs. Returns one
20
+ * human-readable line per problem; an empty array means the descriptor is
21
+ * internally consistent.
22
+ *
23
+ * This exists because a contradiction here is invisible until a provider
24
+ * answers HTTP 400: `GOOGLE_STRICT` shipped with `allowAnyOf: false` +
25
+ * `allowDefsRef: false` AND `anyEncoding: 'recursive-open'` (which needs
26
+ * both), so every `z.any()` in a Gemini tool schema emitted exactly the
27
+ * `anyOf` + `$defs/Any` shape the descriptor declared unrepresentable.
28
+ * Nothing checked, so nothing caught it.
29
+ *
30
+ * Called by `registerDescriptor` (which warns) and asserted over every
31
+ * built-in by the test suite.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * checkDescriptorConsistency(GOOGLE_STRICT); // → []
36
+ * checkDescriptorConsistency({ ...GOOGLE_STRICT, anyEncoding: 'flat' });
37
+ * // → ["anyEncoding 'flat' emits `anyOf`, but allowAnyOf is false"]
38
+ * ```
39
+ */
40
+ export function checkDescriptorConsistency(descriptor) {
41
+ const problems = [];
42
+ const needs = ANY_ENCODING_REQUIREMENTS[descriptor.anyEncoding];
43
+ if (needs === undefined) {
44
+ problems.push(`anyEncoding '${descriptor.anyEncoding}' is not a known encoding`);
45
+ return problems;
46
+ }
47
+ if (needs.anyOf && !descriptor.allowAnyOf) {
48
+ problems.push(`anyEncoding '${descriptor.anyEncoding}' emits \`anyOf\`, but allowAnyOf is false`);
49
+ }
50
+ if (needs.defsRef && !descriptor.allowDefsRef) {
51
+ problems.push(`anyEncoding '${descriptor.anyEncoding}' emits a self-referencing \`$defs/Any\`, but allowDefsRef is false`);
52
+ }
53
+ if (needs.defsRef && !descriptor.supportsRecursion) {
54
+ problems.push(`anyEncoding '${descriptor.anyEncoding}' emits a self-referencing \`$defs/Any\`, but supportsRecursion is false`);
55
+ }
56
+ // `maxEnumValues` is an independent scalar — there is no other field it can
57
+ // contradict — but a cap below 1 widens EVERY enum the dialect ever emits
58
+ // (including a two-member one), and a fractional cap reads as a threshold
59
+ // nobody intended. Both are silent: the schema stays valid, it just stops
60
+ // constraining anything, which is the "invisible until you read the wire"
61
+ // class this function exists to catch. Only reachable through
62
+ // `registerDescriptor`; the built-ins are asserted clean by the test suite.
63
+ const cap = descriptor.maxEnumValues;
64
+ if (cap !== undefined && (!Number.isInteger(cap) || cap < 1)) {
65
+ problems.push(`maxEnumValues must be a positive integer, got ${cap}`);
66
+ }
67
+ problems.push(...schemaSizeLimitProblems(descriptor));
68
+ return problems;
69
+ }
70
+ /**
71
+ * The ways a `schemaSizeLimits` group contradicts itself or the descriptor
72
+ * carrying it. Split out only to keep `checkDescriptorConsistency` readable;
73
+ * it is part of the same contract.
74
+ *
75
+ * The failures below are all SILENT: a non-positive bound degrades every
76
+ * schema to lenient (strict mode simply stops happening, with no error
77
+ * anywhere), a threshold above the total makes the large-enum rule
78
+ * unreachable, and limits on a non-strict descriptor describe a rule the API
79
+ * does not apply — each one is a rule that reads as enforced and is not.
80
+ */
81
+ function schemaSizeLimitProblems(descriptor) {
82
+ const limits = descriptor.schemaSizeLimits;
83
+ if (limits === undefined)
84
+ return [];
85
+ const problems = [];
86
+ if (!descriptor.strict) {
87
+ problems.push('schemaSizeLimits is declared on a non-strict descriptor, but the size rules bound Structured Outputs only');
88
+ }
89
+ // Keyed off the interface, so a bound added to `SchemaSizeLimits` is checked
90
+ // here without an edit — `satisfies` makes a missed key a compile error.
91
+ const bounds = {
92
+ maxObjectProperties: limits.maxObjectProperties,
93
+ maxNestingDepth: limits.maxNestingDepth,
94
+ maxTotalStringChars: limits.maxTotalStringChars,
95
+ maxTotalEnumValues: limits.maxTotalEnumValues,
96
+ largeEnumValueCount: limits.largeEnumValueCount,
97
+ maxLargeEnumStringChars: limits.maxLargeEnumStringChars,
98
+ };
99
+ for (const [name, value] of Object.entries(bounds)) {
100
+ if (!Number.isInteger(value) || value < 1) {
101
+ problems.push(`schemaSizeLimits.${name} must be a positive integer, got ${value}`);
102
+ }
103
+ }
104
+ if (limits.largeEnumValueCount >= limits.maxTotalEnumValues) {
105
+ problems.push(`schemaSizeLimits.largeEnumValueCount (${limits.largeEnumValueCount}) is not below maxTotalEnumValues ` +
106
+ `(${limits.maxTotalEnumValues}), so the large-enum character rule can never apply`);
107
+ }
108
+ return problems;
109
+ }
110
+ //# sourceMappingURL=consistency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consistency.js","sourceRoot":"","sources":["../../src/schema/consistency.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,8BAA8B;AAC9B,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,yBAAyB,GAG1B,MAAM,CAAC,MAAM,CAAC;IACjB,kBAAkB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;IAClD,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;IAChD,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;IACvC,eAAe,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE;IACjD,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE;CACzC,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAA4B;IACrE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,yBAAyB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAChE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,gBAAgB,UAAU,CAAC,WAAW,2BAA2B,CAAC,CAAC;QACjF,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,gBAAgB,UAAU,CAAC,WAAW,4CAA4C,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC,gBAAgB,UAAU,CAAC,WAAW,qEAAqE,CAAC,CAAC;IAC7H,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC;QACnD,QAAQ,CAAC,IAAI,CAAC,gBAAgB,UAAU,CAAC,WAAW,0EAA0E,CAAC,CAAC;IAClI,CAAC;IACD,4EAA4E;IAC5E,0EAA0E;IAC1E,0EAA0E;IAC1E,0EAA0E;IAC1E,0EAA0E;IAC1E,8DAA8D;IAC9D,4EAA4E;IAC5E,MAAM,GAAG,GAAG,UAAU,CAAC,aAAa,CAAC;IACrC,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7D,QAAQ,CAAC,IAAI,CAAC,iDAAiD,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,uBAAuB,CAAC,UAA4B;IAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,gBAAgB,CAAC;IAC3C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,2GAA2G,CAAC,CAAC;IAC7H,CAAC;IACD,6EAA6E;IAC7E,yEAAyE;IACzE,MAAM,MAAM,GAAG;QACb,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;QAC/C,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;KACP,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC,oBAAoB,IAAI,oCAAoC,KAAK,EAAE,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,mBAAmB,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC5D,QAAQ,CAAC,IAAI,CACX,yCAAyC,MAAM,CAAC,mBAAmB,oCAAoC;YACrG,IAAI,MAAM,CAAC,kBAAkB,qDAAqD,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import z from 'zod';
2
+ import type { FormatDescriptor } from './descriptor';
3
+ import type { JSONSchema } from './json-schema';
4
+ /**
5
+ * Build the inline placeholder used in place of a cyclic `$ref` for
6
+ * descriptors with `supportsRecursion: false`. Inspects the recursive zod
7
+ * `schema` and emits a JSON-Schema node whose top-level shape matches it
8
+ * (number → `{type: 'number'}`, array → `{type: 'array', items: ...}`, union
9
+ * → `{anyOf: [...]}`, etc.) so the LLM still knows what JSON value to send
10
+ * at the cycle position. One level of structural fidelity, then bottoms out
11
+ * at the descriptor's flat "any" encoding.
12
+ */
13
+ export declare function buildCycleBreakerSchema(schema: z.ZodType | z.core.$ZodType, refId: string, descriptor: FormatDescriptor): JSONSchema;
14
+ //# sourceMappingURL=cycle-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cycle-breaker.d.ts","sourceRoot":"","sources":["../../src/schema/cycle-breaker.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EACnC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,gBAAgB,GAC3B,UAAU,CAUZ"}