@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.
- package/README.md +51 -4
- package/aeye-core-schema.md +11 -7
- package/dist/common.d.ts +30 -5
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +134 -129
- package/dist/common.js.map +1 -1
- package/dist/delivery.d.ts +39 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +127 -0
- package/dist/delivery.js.map +1 -0
- package/dist/errors.d.ts +60 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +84 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prompt.d.ts +101 -9
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +163 -141
- package/dist/prompt.js.map +1 -1
- package/dist/schema/analyze.d.ts +94 -0
- package/dist/schema/analyze.d.ts.map +1 -0
- package/dist/schema/analyze.js +221 -0
- package/dist/schema/analyze.js.map +1 -0
- package/dist/schema/any-value.d.ts +32 -0
- package/dist/schema/any-value.d.ts.map +1 -0
- package/dist/schema/any-value.js +77 -0
- package/dist/schema/any-value.js.map +1 -0
- package/dist/schema/budget.d.ts +114 -0
- package/dist/schema/budget.d.ts.map +1 -0
- package/dist/schema/budget.js +223 -0
- package/dist/schema/budget.js.map +1 -0
- package/dist/schema/consistency.d.ts +27 -0
- package/dist/schema/consistency.d.ts.map +1 -0
- package/dist/schema/consistency.js +110 -0
- package/dist/schema/consistency.js.map +1 -0
- package/dist/schema/cycle-breaker.d.ts +14 -0
- package/dist/schema/cycle-breaker.d.ts.map +1 -0
- package/dist/schema/cycle-breaker.js +157 -0
- package/dist/schema/cycle-breaker.js.map +1 -0
- package/dist/schema/decode.d.ts +31 -0
- package/dist/schema/decode.d.ts.map +1 -0
- package/dist/schema/decode.js +207 -0
- package/dist/schema/decode.js.map +1 -0
- package/dist/schema/descriptor.d.ts +300 -0
- package/dist/schema/descriptor.d.ts.map +1 -0
- package/dist/schema/descriptor.js +15 -0
- package/dist/schema/descriptor.js.map +1 -0
- package/dist/schema/dialects/anthropic.d.ts +34 -0
- package/dist/schema/dialects/anthropic.d.ts.map +1 -0
- package/dist/schema/dialects/anthropic.js +77 -0
- package/dist/schema/dialects/anthropic.js.map +1 -0
- package/dist/schema/dialects/google.d.ts +47 -0
- package/dist/schema/dialects/google.d.ts.map +1 -0
- package/dist/schema/dialects/google.js +91 -0
- package/dist/schema/dialects/google.js.map +1 -0
- package/dist/schema/dialects/lenient.d.ts +4 -0
- package/dist/schema/dialects/lenient.d.ts.map +1 -0
- package/dist/schema/dialects/lenient.js +26 -0
- package/dist/schema/dialects/lenient.js.map +1 -0
- package/dist/schema/dialects/openai.d.ts +40 -0
- package/dist/schema/dialects/openai.d.ts.map +1 -0
- package/dist/schema/dialects/openai.js +81 -0
- package/dist/schema/dialects/openai.js.map +1 -0
- package/dist/schema/dialects/xai.d.ts +23 -0
- package/dist/schema/dialects/xai.d.ts.map +1 -0
- package/dist/schema/dialects/xai.js +53 -0
- package/dist/schema/dialects/xai.js.map +1 -0
- package/dist/schema/enum-values.d.ts +44 -0
- package/dist/schema/enum-values.d.ts.map +1 -0
- package/dist/schema/enum-values.js +56 -0
- package/dist/schema/enum-values.js.map +1 -0
- package/dist/schema/expressible.d.ts +59 -0
- package/dist/schema/expressible.d.ts.map +1 -0
- package/dist/schema/expressible.js +161 -0
- package/dist/schema/expressible.js.map +1 -0
- package/dist/schema/index.d.ts +20 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +20 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/json-schema.d.ts +64 -0
- package/dist/schema/json-schema.d.ts.map +1 -0
- package/dist/schema/json-schema.js +2 -0
- package/dist/schema/json-schema.js.map +1 -0
- package/dist/schema/registry.d.ts +73 -0
- package/dist/schema/registry.d.ts.map +1 -0
- package/dist/schema/registry.js +135 -0
- package/dist/schema/registry.js.map +1 -0
- package/dist/schema/strictify.d.ts +19 -0
- package/dist/schema/strictify.d.ts.map +1 -0
- package/dist/schema/strictify.js +237 -0
- package/dist/schema/strictify.js.map +1 -0
- package/dist/schema/to-json-schema.d.ts +29 -0
- package/dist/schema/to-json-schema.d.ts.map +1 -0
- package/dist/schema/to-json-schema.js +691 -0
- package/dist/schema/to-json-schema.js.map +1 -0
- package/dist/schema/walk-key.d.ts +24 -0
- package/dist/schema/walk-key.d.ts.map +1 -0
- package/dist/schema/walk-key.js +33 -0
- package/dist/schema/walk-key.js.map +1 -0
- package/dist/to.js +1 -1
- package/dist/to.js.map +1 -1
- package/dist/tool.d.ts +32 -3
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +41 -0
- package/dist/tool.js.map +1 -1
- package/dist/types.d.ts +265 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -1
- package/dist/types.js.map +1 -1
- package/package.json +72 -72
- package/dist/schema.d.ts +0 -837
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -2472
- package/dist/schema.js.map +0 -1
package/dist/schema.d.ts
DELETED
|
@@ -1,837 +0,0 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Provider family for a `FormatDescriptor`. The three known wire dialects
|
|
4
|
-
* (`'openai'`, `'anthropic'`, `'google'`) are autocomplete hints; any string
|
|
5
|
-
* is accepted so users can register custom descriptors via
|
|
6
|
-
* `registerDescriptor` and reference them by family name in
|
|
7
|
-
* `ModelInfo.strictFormat`. `'lenient'` is the no-rewrite catch-all and is
|
|
8
|
-
* not registerable as a custom family.
|
|
9
|
-
*
|
|
10
|
-
* Use this type wherever you need to talk about a descriptor family —
|
|
11
|
-
* registry functions, model declarations, etc.
|
|
12
|
-
*/
|
|
13
|
-
export type DescriptorFamily = 'openai' | 'anthropic' | 'google' | (string & {});
|
|
14
|
-
/**
|
|
15
|
-
* Documented ceilings on how BIG one strict schema may be, as opposed to which
|
|
16
|
-
* keywords it may use. Every limit here is a SUM taken over a single schema —
|
|
17
|
-
* one tool's `parameters`, or one structured-output schema — not a per-request
|
|
18
|
-
* allowance shared between them (that is `maxStrictTools` and friends, which
|
|
19
|
-
* the `SchemaBudget` spends across the whole request).
|
|
20
|
-
*
|
|
21
|
-
* A schema that exceeds one of these is a guaranteed provider rejection under
|
|
22
|
-
* strict mode, so `SchemaBudget.allocate` treats it like any other
|
|
23
|
-
* infeasibility and degrades that ONE item to the family's non-strict
|
|
24
|
-
* descriptor, where no size rule applies. The rest of the request is
|
|
25
|
-
* unaffected.
|
|
26
|
-
*
|
|
27
|
-
* Declared as one object rather than six optional fields on purpose: a
|
|
28
|
-
* provider documents these together, and the object being present is what says
|
|
29
|
-
* "this dialect publishes size limits". Adding a rule here is then a compile
|
|
30
|
-
* error at every descriptor that declares the group, which is the point
|
|
31
|
-
* (a missed limit is invisible until a 400).
|
|
32
|
-
*/
|
|
33
|
-
export interface SchemaSizeLimits {
|
|
34
|
-
/** Maximum object properties in one schema, summed over every nesting level. */
|
|
35
|
-
readonly maxObjectProperties: number;
|
|
36
|
-
/** Maximum levels of nested containers below the schema root. */
|
|
37
|
-
readonly maxNestingDepth: number;
|
|
38
|
-
/**
|
|
39
|
-
* Maximum total characters over the schema's property names, `enum` values
|
|
40
|
-
* and `const` values together.
|
|
41
|
-
*/
|
|
42
|
-
readonly maxTotalStringChars: number;
|
|
43
|
-
/** Maximum `enum` values summed across every enum property in one schema. */
|
|
44
|
-
readonly maxTotalEnumValues: number;
|
|
45
|
-
/**
|
|
46
|
-
* The single-enum value count above which `maxLargeEnumStringChars` starts
|
|
47
|
-
* to apply. A smaller enum is bounded only by the two totals above.
|
|
48
|
-
*/
|
|
49
|
-
readonly largeEnumValueCount: number;
|
|
50
|
-
/**
|
|
51
|
-
* Maximum total characters of ONE enum's string values, applied only to an
|
|
52
|
-
* enum carrying more than `largeEnumValueCount` values.
|
|
53
|
-
*/
|
|
54
|
-
readonly maxLargeEnumStringChars: number;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Format descriptor — describes the JSON Schema dialect of a target.
|
|
58
|
-
*
|
|
59
|
-
* Each provider/strict combination is represented by one descriptor. The
|
|
60
|
-
* descriptor controls both how `strictify` rewrites a Zod schema (preprocesses
|
|
61
|
-
* for record-as-array, tuple-as-object, etc.) and how `toJSONSchema` emits the
|
|
62
|
-
* matching JSON Schema. Every `if (strict)` decision in the conversion
|
|
63
|
-
* pipeline dispatches off a descriptor field, so adding a new dialect is a
|
|
64
|
-
* matter of declaring a new `FormatDescriptor` and registering it.
|
|
65
|
-
*/
|
|
66
|
-
export interface FormatDescriptor {
|
|
67
|
-
/** Stable id used as the cache key inside `strictify`. */
|
|
68
|
-
readonly id: string;
|
|
69
|
-
/**
|
|
70
|
-
* Provider family for this descriptor. The three built-in wire dialects
|
|
71
|
-
* are `'openai'` / `'anthropic'` / `'google'`; `'lenient'` is the
|
|
72
|
-
* no-rewrite catch-all. Custom descriptors registered via
|
|
73
|
-
* `registerDescriptor` may use any string here — the family is the
|
|
74
|
-
* lookup key used by `getDescriptor(family, strict)`.
|
|
75
|
-
*/
|
|
76
|
-
readonly family: DescriptorFamily | 'lenient';
|
|
77
|
-
/** True if this descriptor represents the strict variant for its family. */
|
|
78
|
-
readonly strict: boolean;
|
|
79
|
-
/** When true, every object field appears in `required[]` (OpenAI strict). */
|
|
80
|
-
readonly objectAllFieldsRequired: boolean;
|
|
81
|
-
/** When true, every object emits `additionalProperties: false`. */
|
|
82
|
-
readonly objectClosedByDefault: boolean;
|
|
83
|
-
/** How to encode `z.record(K, V)`. */
|
|
84
|
-
readonly recordEncoding: 'array-of-pairs' | 'open-record';
|
|
85
|
-
/**
|
|
86
|
-
* How to encode `z.tuple([...])`.
|
|
87
|
-
* - `object-numeric-keys`: OpenAI-strict workaround (`{ "0": A, "1": B, ... }`)
|
|
88
|
-
* - `prefix-items`: standard JSON Schema `prefixItems` (Anthropic / Google)
|
|
89
|
-
* - `items-union`: collapse to homogeneous `items: { anyOf: [...] }` (last-resort)
|
|
90
|
-
*/
|
|
91
|
-
readonly tupleEncoding: 'object-numeric-keys' | 'prefix-items' | 'items-union';
|
|
92
|
-
readonly allowAllOf: boolean;
|
|
93
|
-
readonly allowAnyOf: boolean;
|
|
94
|
-
readonly allowOneOf: boolean;
|
|
95
|
-
/** Whether `{ $ref: '#' }` self-reference is permitted. */
|
|
96
|
-
readonly allowRootRef: boolean;
|
|
97
|
-
readonly allowDefsRef: boolean;
|
|
98
|
-
/** Reserved: emit Google-style `propertyOrdering` hints. */
|
|
99
|
-
readonly emitPropertyOrdering: boolean;
|
|
100
|
-
/** Whitelist of `format:` values to emit, or `'all'` to pass through. */
|
|
101
|
-
readonly supportedStringFormats: ReadonlySet<string> | 'all';
|
|
102
|
-
readonly allowPattern: boolean;
|
|
103
|
-
readonly allowMultiplePatterns: boolean;
|
|
104
|
-
readonly allowMinMaxLength: boolean;
|
|
105
|
-
readonly allowMinMaxItems: boolean;
|
|
106
|
-
readonly allowMinimumMaximum: boolean;
|
|
107
|
-
/** When true, `optional` becomes `T|null` (because every prop must be required). */
|
|
108
|
-
readonly optionalAsNullable: boolean;
|
|
109
|
-
/**
|
|
110
|
-
* How `z.any()` / `z.unknown()` is encoded.
|
|
111
|
-
* - `recursive-strict`: self-referencing $defs/Any with array-of-pairs records
|
|
112
|
-
* (OpenAI strict has no open-object support). REQUIRES `allowAnyOf` and
|
|
113
|
-
* `allowDefsRef`.
|
|
114
|
-
* - `recursive-open`: $defs/Any with `additionalProperties: <self>` records.
|
|
115
|
-
* REQUIRES `allowAnyOf` and `allowDefsRef`.
|
|
116
|
-
* - `flat`: a non-recursive `anyOf` over the JSON value types
|
|
117
|
-
* (`string`/`number`/`boolean`/`null`/`array`/open-`object`). Used by
|
|
118
|
-
* descriptors that reject recursive `$defs/Any` definitions (Anthropic
|
|
119
|
-
* strict). Equivalent to TypeScript `any` — accepts every JSON value
|
|
120
|
-
* without imposing any structural constraints. REQUIRES `allowAnyOf`
|
|
121
|
-
* (no `$defs` needed).
|
|
122
|
-
* - `unconstrained`: the empty schema `{}` (plus any `description`). Per
|
|
123
|
-
* JSON Schema, a schema with no assertion keywords validates EVERY
|
|
124
|
-
* instance — which is exactly what "any JSON value" means — and it
|
|
125
|
-
* introduces no keyword a restrictive dialect could reject. REQUIRES
|
|
126
|
-
* NOTHING, so it's the only encoding available to a dialect that forbids
|
|
127
|
-
* both combinators and named `$defs`/`$ref` (Google — see `GOOGLE_STRICT`).
|
|
128
|
-
*
|
|
129
|
-
* The `REQUIRES` notes above are enforced by `checkDescriptorConsistency`,
|
|
130
|
-
* which `registerDescriptor` runs so a self-contradictory descriptor is
|
|
131
|
-
* caught at declaration time rather than as a provider HTTP 400.
|
|
132
|
-
*/
|
|
133
|
-
readonly anyEncoding: 'recursive-strict' | 'recursive-open' | 'flat' | 'unconstrained';
|
|
134
|
-
/** Maximum number of tools that can carry `strict: true` in one request. */
|
|
135
|
-
readonly maxStrictTools?: number;
|
|
136
|
-
/** Maximum total optional parameters across all strict schemas in one request. */
|
|
137
|
-
readonly maxStrictOptionalParams?: number;
|
|
138
|
-
/** Maximum total union-type parameters across all strict schemas in one request. */
|
|
139
|
-
readonly maxStrictUnionTypes?: number;
|
|
140
|
-
/**
|
|
141
|
-
* Documented ceilings on the SIZE of one strict schema (properties, nesting,
|
|
142
|
-
* total string bytes, enum values), or `undefined` when the dialect
|
|
143
|
-
* publishes none. See {@link SchemaSizeLimits} for what each bound counts and
|
|
144
|
-
* why it is per-schema rather than per-request.
|
|
145
|
-
*
|
|
146
|
-
* Only a STRICT descriptor declares these: they are Structured-Outputs rules
|
|
147
|
-
* — the API compiles the schema into a decoder and rejects one it cannot —
|
|
148
|
-
* and a non-strict schema is a best-effort hint the API does not compile. So
|
|
149
|
-
* degrading the offending item to the family's non-strict descriptor is both
|
|
150
|
-
* the fix and the reason the limits stop applying to it
|
|
151
|
-
* (`SchemaBudget.allocate`, `checkSchemaSizeLimits`).
|
|
152
|
-
*
|
|
153
|
-
* The measurement is taken from the ZOD schema, not from the emitted JSON,
|
|
154
|
-
* so it is deliberately CONSERVATIVE where an encoding is descriptor-
|
|
155
|
-
* dependent (a record counts as its widest encoding, a tuple as its
|
|
156
|
-
* numeric-key one). Over-counting costs strictness on a schema that would
|
|
157
|
-
* have fit; under-counting costs an HTTP 400, so the round goes up.
|
|
158
|
-
*/
|
|
159
|
-
readonly schemaSizeLimits?: SchemaSizeLimits;
|
|
160
|
-
/**
|
|
161
|
-
* Maximum number of values ONE emitted `enum` may carry, or `undefined`
|
|
162
|
-
* (the default) for no cap.
|
|
163
|
-
*
|
|
164
|
-
* This is deliberately NOT one of the per-request budget fields above. Those
|
|
165
|
-
* are strict-mode allowances the SchemaBudget spends across a whole request,
|
|
166
|
-
* degrading an item that no longer fits to the family's non-strict
|
|
167
|
-
* descriptor; this one is a property of the dialect itself — it fires whatever the strictness — so it is enforced
|
|
168
|
-
* structurally at the point an `enum` is emitted (`convertSchema`), where
|
|
169
|
-
* the cardinality is actually known.
|
|
170
|
-
*
|
|
171
|
-
* Over the cap the emitter **widens** rather than truncates: it drops the
|
|
172
|
-
* `enum` keyword entirely, emits the node's plain scalar `type`
|
|
173
|
-
* (`{type: 'string'}` for a string enum), and puts EVERY value into the
|
|
174
|
-
* node's `description`. Nothing becomes unreachable — the model still has
|
|
175
|
-
* the complete list and can name any member — and the cardinality the
|
|
176
|
-
* dialect could not compile is simply no longer on the wire.
|
|
177
|
-
*
|
|
178
|
-
* The trade this makes is enforcement: a widened node no longer rejects a
|
|
179
|
-
* value structurally, so whatever validates the model's output afterwards
|
|
180
|
-
* has to. `Tool.parse` does, against the ORIGINAL zod schema — the enum is
|
|
181
|
-
* still there, only the emitted JSON Schema widened — so a hallucinated
|
|
182
|
-
* member comes back as a normal, re-promptable validation error.
|
|
183
|
-
*
|
|
184
|
-
* **NO BUILT-IN DESCRIPTOR DECLARES A CAP, and that is a measured result, not
|
|
185
|
-
* an omission.** This field exists because a large `enum` was believed to make
|
|
186
|
-
* Gemini answer `400 INVALID_ARGUMENT`; measured against
|
|
187
|
-
* `google/gemini-3-flash-preview` through OpenRouter (2026-08-23), it does
|
|
188
|
-
* not. Every one of these returned HTTP 200 with the expected tool call, under
|
|
189
|
-
* `tool_choice` both `required` (which is what makes Gemini compile the tool
|
|
190
|
-
* schemas into a decoding grammar) and `auto`:
|
|
191
|
-
*
|
|
192
|
-
* - single-parameter `enum` at 50 / 64 / 72 / 80 / 96 / 128 / 256 / 512 /
|
|
193
|
-
* 1024 / **2048** values (52 KB of tool schema);
|
|
194
|
-
* - 96 values with 80-character members (byte size, not member count);
|
|
195
|
-
* - 20 tools each carrying a 96-value `enum` in one request (51 KB);
|
|
196
|
-
* - a 96-value `enum` alongside `strict: true`, alongside `minItems` on an
|
|
197
|
-
* array-of-enum, alongside `propertyOrdering`, alongside a sibling `anyOf`;
|
|
198
|
-
* - a 2048-value `enum` inside a `response_format` JSON schema.
|
|
199
|
-
*
|
|
200
|
-
* A second pass answered the obvious objection — that an isolated probe is
|
|
201
|
-
* not what a real session sends. Same date, same model, upstream PINNED to
|
|
202
|
-
* Google AI Studio with fallbacks off (the upstream that served the
|
|
203
|
-
* production failures), an eleven-tool roster emitted through this library
|
|
204
|
-
* for a Google model and modelled on a real kind-authoring agent
|
|
205
|
-
* (`fn_load`/`fn_search`/`type_load`/`api_signature`/`api_set`/…, real
|
|
206
|
-
* descriptions, a real system prompt): a 96-value enum on one of those tools
|
|
207
|
-
* returned 200 with the expected tool call under `tool_choice` `required`
|
|
208
|
-
* and `auto` alike, as did the same roster at 8 values and a tool carrying a
|
|
209
|
-
* bare typeless `{}` property.
|
|
210
|
-
*
|
|
211
|
-
* **Caveat that keeps this from being proof**: the CONTROL — the recursive
|
|
212
|
-
* `$defs/Any` shape this file recorded as "100% reproducible" HTTP 400 —
|
|
213
|
-
* also returned 200, pinned and unpinned, forced and automatic, both
|
|
214
|
-
* hand-built and as the product's real 32 KB gin `api_set` schema. So that
|
|
215
|
-
* failure class is simply not visible from this route today (upstream fixed
|
|
216
|
-
* it, or sanitizes), and a negative here is evidence, not a verdict. What it
|
|
217
|
-
* does establish is that nothing reachable from a request — isolated or
|
|
218
|
-
* realistic — reproduces an enum-size limit, so declaring a cap would trade
|
|
219
|
-
* real structural enforcement for an unverified one.
|
|
220
|
-
*
|
|
221
|
-
* One trap for whoever measures this next: with `max_tokens` at 64, the
|
|
222
|
-
* $defs/Any tool came back `finish_reason: 'length'` with no tool call and
|
|
223
|
-
* empty content, which reads exactly like a grammar that produced nothing.
|
|
224
|
-
* It is not — this model spends reasoning tokens first, and at 400 the same
|
|
225
|
-
* request emitted a correct call in 28 output tokens. Give a forced-tool
|
|
226
|
-
* probe room before calling an empty completion a failure.
|
|
227
|
-
*
|
|
228
|
-
* Reliability was compared too, since a cap's whole cost is losing the
|
|
229
|
-
* `enum`'s can't-hallucinate guarantee: same tool and paraphrased prompt,
|
|
230
|
-
* 10 trials each, `enum` vs plain string with the full list in the
|
|
231
|
-
* description, over confusable near-duplicate names. At 96 values and again
|
|
232
|
-
* at 512, both encodings picked the correct member 10/10 with zero off-list
|
|
233
|
-
* answers. No measured reliability gap — but also no measured reason to give
|
|
234
|
-
* up the guarantee.
|
|
235
|
-
*
|
|
236
|
-
* So: set this only where a provider is MEASURED to reject a large enum, and
|
|
237
|
-
* set it as high as the measurement allows. An `enum` that fits is the more
|
|
238
|
-
* correct encoding, because it is the one the model cannot answer outside of.
|
|
239
|
-
*/
|
|
240
|
-
readonly maxEnumValues?: number;
|
|
241
|
-
/**
|
|
242
|
-
* Whether the descriptor can express recursive `$ref` schemas (`$ref: '#'`
|
|
243
|
-
* to root, or any `$ref: '#/$defs/X'` whose target transitively references
|
|
244
|
-
* itself). OpenAI / Google strict / Lenient: true. Anthropic strict: false
|
|
245
|
-
* — Anthropic's tool `input_schema` validator explicitly rejects circular
|
|
246
|
-
* `$defs` graphs.
|
|
247
|
-
*
|
|
248
|
-
* When `false`, the JSON-Schema emitter detects cycles in the source
|
|
249
|
-
* schema and replaces the offending back-edge with the descriptor's flat
|
|
250
|
-
* "any" shape (see `anyEncoding`) carrying a description that names the
|
|
251
|
-
* `$defs` entry it would otherwise have referenced. Non-cyclic shared
|
|
252
|
-
* `$ref` reuse is unaffected — those still emit as a `$ref` to a `$defs`
|
|
253
|
-
* entry.
|
|
254
|
-
*/
|
|
255
|
-
readonly supportsRecursion: boolean;
|
|
256
|
-
/**
|
|
257
|
-
* Instruction appended after the schema text when a Prompt's/Tool's schema
|
|
258
|
-
* can't be expressed as this descriptor's structured output and is instead
|
|
259
|
-
* delivered to the model as PROMPT TEXT (see `canExpress` and the ai-layer
|
|
260
|
-
* `applySchemaDeliveryFallback`). Steers the model to emit a single raw JSON
|
|
261
|
-
* object rather than echoing the schema or wrapping it in prose/fences.
|
|
262
|
-
*
|
|
263
|
-
* Optional — descriptors that omit it fall back to
|
|
264
|
-
* `DEFAULT_JSON_FALLBACK_INSTRUCTION` via `getJsonFallbackInstruction`.
|
|
265
|
-
*/
|
|
266
|
-
readonly jsonFallbackInstruction?: string;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Default instruction used when a `FormatDescriptor` omits
|
|
270
|
-
* `jsonFallbackInstruction`. Appended after the schema text when a schema is
|
|
271
|
-
* delivered as prompt text instead of as structured output.
|
|
272
|
-
*/
|
|
273
|
-
export declare const DEFAULT_JSON_FALLBACK_INSTRUCTION = "Return ONLY a single raw JSON object conforming to the schema above \u2014 no markdown code fences, no prose, and do NOT echo the schema itself.";
|
|
274
|
-
/**
|
|
275
|
-
* Resolve the prompt-text fallback instruction for a descriptor, falling back
|
|
276
|
-
* to `DEFAULT_JSON_FALLBACK_INSTRUCTION` when the descriptor doesn't declare
|
|
277
|
-
* one of its own.
|
|
278
|
-
*/
|
|
279
|
-
export declare function getJsonFallbackInstruction(descriptor: FormatDescriptor): string;
|
|
280
|
-
/** Lenient descriptor — no rewrites, no closure, formats pass through. Default for unsupported models. */
|
|
281
|
-
export declare const LENIENT: FormatDescriptor;
|
|
282
|
-
/**
|
|
283
|
-
* OpenAI strict — current behavior verbatim. Records→pairs,
|
|
284
|
-
* tuples→numeric-keys, optional→nullable.
|
|
285
|
-
*
|
|
286
|
-
* No documented per-request slot limit (max strict tools / optional params /
|
|
287
|
-
* union types) — the SchemaBudget treats those caps as `undefined` for OpenAI.
|
|
288
|
-
*
|
|
289
|
-
* OpenAI DOES document per-SCHEMA size limits for Structured Outputs, and they
|
|
290
|
-
* are declared below (`schemaSizeLimits`). Verified against
|
|
291
|
-
* developers.openai.com/api/docs/guides/structured-outputs on 2026-08-23,
|
|
292
|
-
* section "Supported schemas": *"A schema may have up to 5000 object
|
|
293
|
-
* properties total, with up to 10 levels of nesting"*, *"total string length of
|
|
294
|
-
* all property names, definition names, enum values, and const values cannot
|
|
295
|
-
* exceed 120,000 characters"*, *"A schema may have up to 1000 enum values
|
|
296
|
-
* across all enum properties"*, and *"For a single enum property with string
|
|
297
|
-
* values, the total string length of all enum values cannot exceed 15,000
|
|
298
|
-
* characters when there are more than 250 enum values"*. Every one of those
|
|
299
|
-
* numbers was raised in July 2025 (100→5000 properties, 15,000→120,000
|
|
300
|
-
* characters, 500→1000 enum values, 7,500→15,000 for the >250-value enum) —
|
|
301
|
-
* the pre-raise figures are still widely quoted secondhand, so they are named
|
|
302
|
-
* here to keep a future reader from "correcting" these downward.
|
|
303
|
-
*
|
|
304
|
-
* Scope is Structured Outputs, i.e. a tool carrying `strict: true` or a
|
|
305
|
-
* `json_schema` response format — the function-calling guide defines strict
|
|
306
|
-
* mode as "leveraging our structured outputs feature", and non-strict function
|
|
307
|
-
* calling is best-effort with no compiled decoder. That is exactly why
|
|
308
|
-
* degrading the over-size item to `OPENAI_NON_STRICT` is a complete answer.
|
|
309
|
-
*/
|
|
310
|
-
export declare const OPENAI_STRICT: FormatDescriptor;
|
|
311
|
-
/** OpenAI non-strict — alias of LENIENT but tagged with the openai family. */
|
|
312
|
-
export declare const OPENAI_NON_STRICT: FormatDescriptor;
|
|
313
|
-
/**
|
|
314
|
-
* Anthropic strict — Claude 4.5+ only (Opus 4.7/4.6/4.5, Sonnet 4.6/4.5,
|
|
315
|
-
* Haiku 4.5). Per Anthropic's structured-outputs docs:
|
|
316
|
-
*
|
|
317
|
-
* - `additionalProperties` may **only** be `false` (no schema-valued open
|
|
318
|
-
* records), so `z.record(...)` falls back to the OpenAI-style
|
|
319
|
-
* array-of-pairs workaround.
|
|
320
|
-
* - Non-cyclic `$ref` / `$defs` reuse is supported and used freely — only
|
|
321
|
-
* *circular* `$defs` graphs are rejected by the API
|
|
322
|
-
* (`Circular reference detected in schema definitions: …`). The emitter
|
|
323
|
-
* sets `supportsRecursion: false` so the cycle-breaker replaces the
|
|
324
|
-
* offending back-edge with the flat "any" shape (see `anyEncoding`) and
|
|
325
|
-
* leaves shared non-cyclic `$ref`s alone. Root self-reference (`$ref: '#'`)
|
|
326
|
-
* is always cyclic by definition, so `allowRootRef` stays `false`.
|
|
327
|
-
* - Numerical (`minimum`/`maximum`/`multipleOf`) and string-length
|
|
328
|
-
* (`minLength`/`maxLength`) constraints are not supported and are dropped.
|
|
329
|
-
* - `prefixItems` and other positional tuple keywords are not in the
|
|
330
|
-
* supported list, so tuples collapse to a homogeneous `items: anyOf`.
|
|
331
|
-
* - Supported formats include `uri` (unlike OpenAI strict). We pass all
|
|
332
|
-
* formats through and let Anthropic ignore unknowns.
|
|
333
|
-
*/
|
|
334
|
-
export declare const ANTHROPIC_STRICT: FormatDescriptor;
|
|
335
|
-
export declare const ANTHROPIC_NON_STRICT: FormatDescriptor;
|
|
336
|
-
/**
|
|
337
|
-
* Google Gemini strict — per `ai.google.dev/gemini-api/docs/structured-output`:
|
|
338
|
-
*
|
|
339
|
-
* - Supported keywords: types, `properties`, `required`, `additionalProperties`,
|
|
340
|
-
* `enum`, `format` (date-time/date/time only), `minimum`, `maximum`, `items`,
|
|
341
|
-
* `prefixItems`, `minItems`, `maxItems`, `title`, `description`,
|
|
342
|
-
* `propertyOrdering`. Recursion via `$ref: "#"` (root self-ref).
|
|
343
|
-
* - Combinators (`allOf`, `anyOf`, `oneOf`) are NOT in the supported list;
|
|
344
|
-
* we conservatively flag them off so unions degrade to a representable form
|
|
345
|
-
* rather than emit something Gemini ignores.
|
|
346
|
-
* - String constraints (`minLength`, `maxLength`, `pattern` formats beyond the
|
|
347
|
-
* three documented) are not supported.
|
|
348
|
-
* - `propertyOrdering` is REQUIRED for Gemini 2.0 strict; we always emit it
|
|
349
|
-
* on object schemas under this descriptor.
|
|
350
|
-
* - `z.any()` / `z.unknown()` uses the `unconstrained` encoding. Every other
|
|
351
|
-
* "any" encoding needs `anyOf` and/or a named `$defs/Any` — both of which
|
|
352
|
-
* this descriptor forbids two lines below — so the recursive `$defs/Any`
|
|
353
|
-
* this descriptor used to declare was a schema it says it cannot emit.
|
|
354
|
-
* Gemini compiles the tool schemas into a decoding grammar whenever the
|
|
355
|
-
* caller forces a tool call (`toolChoice: 'required'` → Google's function
|
|
356
|
-
* calling mode `ANY`), and a self-referencing `$defs/Any` failed that
|
|
357
|
-
* compile with `400 INVALID_ARGUMENT` — the same request succeeded under
|
|
358
|
-
* `toolChoice: 'auto'`, where no grammar is built. Reported against
|
|
359
|
-
* `google/gemini-3-flash-preview` via OpenRouter as 100% reproducible.
|
|
360
|
-
*
|
|
361
|
-
* **That 400 no longer reproduces (re-measured 2026-08-23)** and the
|
|
362
|
-
* encoding stays anyway. Same model, same route, upstream pinned to Google
|
|
363
|
-
* AI Studio with fallbacks off, `tool_choice` forced to one named tool: the
|
|
364
|
-
* hand-built recursive `$defs/Any` tool, and the product's real 32 KB gin
|
|
365
|
-
* `api_set` schema carrying nine `#/$defs/Any` references, both answered
|
|
366
|
-
* HTTP 200 with a valid tool call. So the upstream either fixed or now
|
|
367
|
-
* sanitizes it. The descriptor still declares what this dialect's own
|
|
368
|
-
* documented keyword list supports; depending on a provider's current
|
|
369
|
-
* leniency for a shape we say it cannot express is how the first version of
|
|
370
|
-
* this descriptor shipped a guaranteed 400 in the first place.
|
|
371
|
-
*/
|
|
372
|
-
export declare const GOOGLE_STRICT: FormatDescriptor;
|
|
373
|
-
/**
|
|
374
|
-
* Google Gemini non-strict — LENIENT's permissive rules EXCEPT for the "any"
|
|
375
|
-
* encoding.
|
|
376
|
-
*
|
|
377
|
-
* Gemini rejects a self-referencing `$defs/Any` when it has to build a
|
|
378
|
-
* decoding grammar, and it builds one whenever a tool call is forced —
|
|
379
|
-
* independent of any per-tool `strict` flag, which Google's function-calling
|
|
380
|
-
* API doesn't even have. So the `unconstrained` encoding is a property of the
|
|
381
|
-
* DIALECT, not of strict mode, and belongs on both Google descriptors.
|
|
382
|
-
*
|
|
383
|
-
* Everything else stays LENIENT deliberately: the restrictions on
|
|
384
|
-
* `GOOGLE_STRICT` come from the documented structured-output keyword list,
|
|
385
|
-
* which is a grammar-mode concern, and tightening them here would push
|
|
386
|
-
* ordinary Gemini schemas onto the prompt-text delivery fallback for no
|
|
387
|
-
* measured reason.
|
|
388
|
-
*/
|
|
389
|
-
export declare const GOOGLE_NON_STRICT: FormatDescriptor;
|
|
390
|
-
/**
|
|
391
|
-
* Report the ways a `FormatDescriptor` contradicts itself — settings that ask
|
|
392
|
-
* the emitter to produce a keyword the same descriptor forbids, or a limit
|
|
393
|
-
* that would quietly strip every constraint it governs. Returns one
|
|
394
|
-
* human-readable line per problem; an empty array means the descriptor is
|
|
395
|
-
* internally consistent.
|
|
396
|
-
*
|
|
397
|
-
* This exists because a contradiction here is invisible until a provider
|
|
398
|
-
* answers HTTP 400: `GOOGLE_STRICT` shipped with `allowAnyOf: false` +
|
|
399
|
-
* `allowDefsRef: false` AND `anyEncoding: 'recursive-open'` (which needs
|
|
400
|
-
* both), so every `z.any()` in a Gemini tool schema emitted exactly the
|
|
401
|
-
* `anyOf` + `$defs/Any` shape the descriptor declared unrepresentable.
|
|
402
|
-
* Nothing checked, so nothing caught it.
|
|
403
|
-
*
|
|
404
|
-
* Called by `registerDescriptor` (which warns) and asserted over every
|
|
405
|
-
* built-in by the test suite.
|
|
406
|
-
*
|
|
407
|
-
* @example
|
|
408
|
-
* ```ts
|
|
409
|
-
* checkDescriptorConsistency(GOOGLE_STRICT); // → []
|
|
410
|
-
* checkDescriptorConsistency({ ...GOOGLE_STRICT, anyEncoding: 'flat' });
|
|
411
|
-
* // → ["anyEncoding 'flat' emits `anyOf`, but allowAnyOf is false"]
|
|
412
|
-
* ```
|
|
413
|
-
*/
|
|
414
|
-
export declare function checkDescriptorConsistency(descriptor: FormatDescriptor): string[];
|
|
415
|
-
/**
|
|
416
|
-
* Register a custom `FormatDescriptor` so it can be looked up by `id` or
|
|
417
|
-
* by `(family, strict)`. Useful for adding support for a new provider
|
|
418
|
-
* dialect or for registering a tweaked variant of an existing family.
|
|
419
|
-
*
|
|
420
|
-
* The descriptor's `id` and `family` become the lookup keys. If a
|
|
421
|
-
* descriptor with the same id is already registered, the new one
|
|
422
|
-
* replaces it. The family slot tracks one strict and one lenient
|
|
423
|
-
* variant; registering a third variant overwrites the matching slot.
|
|
424
|
-
*
|
|
425
|
-
* Built-in descriptors (`OPENAI_STRICT`, `LENIENT`, etc.) are seeded at
|
|
426
|
-
* module load — you don't need to register them manually. They CAN be
|
|
427
|
-
* overridden by registering a same-id descriptor afterwards, but doing
|
|
428
|
-
* so is generally a sign that you should pick a different id.
|
|
429
|
-
*
|
|
430
|
-
* @example
|
|
431
|
-
* ```ts
|
|
432
|
-
* import { registerDescriptor, OPENAI_STRICT } from '@aeye/core';
|
|
433
|
-
*
|
|
434
|
-
* // A tighter OpenAI-flavored descriptor: same wire shape but with
|
|
435
|
-
* // pattern support disabled (some downstream tools choke on regex).
|
|
436
|
-
* registerDescriptor({
|
|
437
|
-
* ...OPENAI_STRICT,
|
|
438
|
-
* id: 'openai-no-regex',
|
|
439
|
-
* family: 'openai-no-regex',
|
|
440
|
-
* allowPattern: false,
|
|
441
|
-
* allowMultiplePatterns: false,
|
|
442
|
-
* });
|
|
443
|
-
*
|
|
444
|
-
* // Now resolvable:
|
|
445
|
-
* getDescriptor('openai-no-regex', true); // → the registered descriptor
|
|
446
|
-
* getDescriptorById('openai-no-regex'); // → same
|
|
447
|
-
* ```
|
|
448
|
-
*
|
|
449
|
-
* A descriptor that contradicts itself (see `checkDescriptorConsistency`) is
|
|
450
|
-
* still registered — the caller may know something we don't — but each
|
|
451
|
-
* problem is warned once at registration, because the alternative is finding
|
|
452
|
-
* out from a provider's HTTP 400.
|
|
453
|
-
*/
|
|
454
|
-
export declare function registerDescriptor(descriptor: FormatDescriptor): void;
|
|
455
|
-
/**
|
|
456
|
-
* Resolve a descriptor by family + strictness.
|
|
457
|
-
*
|
|
458
|
-
* Built-in families (`'openai'`, `'anthropic'`, `'google'`) always
|
|
459
|
-
* resolve. Custom families registered via `registerDescriptor` resolve
|
|
460
|
-
* once they're registered. Unknown families fall back to `LENIENT`
|
|
461
|
-
* (safe default — strict mode silently degrades for unrecognized models).
|
|
462
|
-
*
|
|
463
|
-
* @param family - provider family (built-in or registered)
|
|
464
|
-
* @param strict - whether the strict variant is wanted
|
|
465
|
-
*/
|
|
466
|
-
export declare function getDescriptor(family: DescriptorFamily, strict: boolean): FormatDescriptor;
|
|
467
|
-
/**
|
|
468
|
-
* Look up a descriptor by id. Returns LENIENT for unknown ids (safe default).
|
|
469
|
-
* Custom descriptors registered via `registerDescriptor` are reachable here
|
|
470
|
-
* by their `id`.
|
|
471
|
-
*/
|
|
472
|
-
export declare function getDescriptorById(id: string | undefined): FormatDescriptor;
|
|
473
|
-
/**
|
|
474
|
-
* True if `family` has at least one registered descriptor (strict or
|
|
475
|
-
* lenient). Used by `@aeye/ai`'s `resolveStrictFormat` fallback chain to
|
|
476
|
-
* decide whether a model id prefix or provider name is a recognized
|
|
477
|
-
* dialect.
|
|
478
|
-
*/
|
|
479
|
-
export declare function hasDescriptorFamily(family: string | undefined): boolean;
|
|
480
|
-
/**
|
|
481
|
-
* Resolve descriptor from various input shapes (boolean, options object, or descriptor).
|
|
482
|
-
* Used by `toJSONSchema` to support its overloaded signature.
|
|
483
|
-
*/
|
|
484
|
-
export declare function resolveDescriptor(input: FormatDescriptor | ToJSONSchemaOptions | boolean | undefined): FormatDescriptor;
|
|
485
|
-
/**
|
|
486
|
-
* Decide whether `schema` can be expressed as structured output under
|
|
487
|
-
* `descriptor`'s JSON-Schema dialect. Returns `false` when the schema uses a
|
|
488
|
-
* construct the descriptor forbids, so the caller can DROP the wire schema and
|
|
489
|
-
* deliver it as prompt text instead (see the ai-layer
|
|
490
|
-
* `applySchemaDeliveryFallback`).
|
|
491
|
-
*
|
|
492
|
-
* Walks the Zod schema in the same style as `strictify`/`convert`, honoring:
|
|
493
|
-
* - **unions** (`z.union` / `z.discriminatedUnion` → `anyOf`) when
|
|
494
|
-
* `!descriptor.allowAnyOf` (e.g. Gemini strict rejects `anyOf`);
|
|
495
|
-
* - **intersections** (`z.intersection` → `allOf`) when `!descriptor.allowAllOf`;
|
|
496
|
-
* - **recursion** (a self-referential `z.lazy` cycle → `$ref`): a cycle back to
|
|
497
|
-
* the ROOT is fine when `descriptor.allowRootRef && descriptor.supportsRecursion`
|
|
498
|
-
* (Gemini supports `$ref: '#'`); a non-root cycle needs
|
|
499
|
-
* `descriptor.allowDefsRef && descriptor.supportsRecursion`.
|
|
500
|
-
*
|
|
501
|
-
* `ZodNullable` / `ZodOptional` are transparent wrappers here (nullability is
|
|
502
|
-
* NOT treated as a union), so only genuine `z.union(...)` trips `allowAnyOf`.
|
|
503
|
-
* Non-combinator, non-recursive schemas (plain objects, arrays, primitives)
|
|
504
|
-
* are always expressible. The structure mirrors the descriptor flags so more
|
|
505
|
-
* feasibility checks are easy to add.
|
|
506
|
-
*/
|
|
507
|
-
export declare function canExpress(schema: z.ZodType | z.core.$ZodType, descriptor: FormatDescriptor): boolean;
|
|
508
|
-
/**
|
|
509
|
-
* Recursively transforms a Zod schema to a target dialect.
|
|
510
|
-
*
|
|
511
|
-
* For LENIENT, returns the input schema unchanged (no rewrites). For strict
|
|
512
|
-
* descriptors, installs preprocesses that accept the dialect's wire shape
|
|
513
|
-
* (e.g. array-of-pairs records, numeric-key tuple objects) and normalize them
|
|
514
|
-
* back to the natural Zod shape before validation.
|
|
515
|
-
*
|
|
516
|
-
* Repeated calls with the same `(schema, descriptor)` return the cached
|
|
517
|
-
* transformed schema. Different descriptors share the same outer entry but
|
|
518
|
-
* map to different inner values — bounded by the small descriptor count.
|
|
519
|
-
*
|
|
520
|
-
* @param schema - input Zod schema
|
|
521
|
-
* @param descriptor - target dialect descriptor (defaults to OPENAI_STRICT)
|
|
522
|
-
*/
|
|
523
|
-
export declare function strictify<S extends z.ZodType>(schema: S, descriptor?: FormatDescriptor): S;
|
|
524
|
-
/**
|
|
525
|
-
* Produce a NON-FAILING variant of `schema` that keeps every transform
|
|
526
|
-
* (`preprocess`, `codec`, `pipe`, `transform`) but drops validation. Parsing
|
|
527
|
-
* a wire value through the result runs the decode preprocesses installed by
|
|
528
|
-
* `strictify` (array-of-pairs → record, numeric-key object → tuple, null →
|
|
529
|
-
* undefined) and yields the conceptual value, without ever rejecting.
|
|
530
|
-
*
|
|
531
|
-
* Recursion (`z.lazy` self-reference) is handled with a per-call memo map: an
|
|
532
|
-
* in-flight node is recorded as a thunk and re-encounters resolve to
|
|
533
|
-
* `z.lazy(thunk)`, so a self-referential schema relaxes in finite time and
|
|
534
|
-
* never infinite-loops.
|
|
535
|
-
*/
|
|
536
|
-
export declare function relaxValidation(schema: z.ZodType): z.ZodType;
|
|
537
|
-
/**
|
|
538
|
-
* DECODE a model's wire `value` back to the conceptual shape a custom parser
|
|
539
|
-
* expects — symmetric with how `strictify` ENCODEs the request for the wire.
|
|
540
|
-
*
|
|
541
|
-
* Runs the descriptor's decode transforms (via `relaxValidation(strictify(...))`)
|
|
542
|
-
* without imposing validation, so array-of-pairs records, numeric-key tuples,
|
|
543
|
-
* and null-for-optional fields are normalized before a provider-agnostic
|
|
544
|
-
* custom parser sees them. Best-effort: NEVER throws — if the relaxed decoder
|
|
545
|
-
* can't parse the value it is returned unchanged.
|
|
546
|
-
*
|
|
547
|
-
* @param schema - the PRE-strictify conceptual schema (strictify runs internally)
|
|
548
|
-
* @param value - the model's raw wire value (already `JSON.parse`d)
|
|
549
|
-
* @param descriptor - the wire dialect the request was encoded with
|
|
550
|
-
*/
|
|
551
|
-
export declare function decodeWire(schema: z.ZodType, value: unknown, descriptor: FormatDescriptor): unknown;
|
|
552
|
-
/**
|
|
553
|
-
* Format specification for JSON Schema generation
|
|
554
|
-
*/
|
|
555
|
-
/**
|
|
556
|
-
* Family name for `ToJSONSchemaOptions.format`. Alias of `DescriptorFamily`
|
|
557
|
-
* — the three built-in dialects (`'openai'`, `'anthropic'`, `'google'`)
|
|
558
|
-
* plus any string registered via `registerDescriptor`.
|
|
559
|
-
*
|
|
560
|
-
* @deprecated Prefer `DescriptorFamily`. Both refer to the same widened type.
|
|
561
|
-
*/
|
|
562
|
-
export type JSONSchemaFormat = DescriptorFamily;
|
|
563
|
-
/**
|
|
564
|
-
* Options for toJSONSchema
|
|
565
|
-
*/
|
|
566
|
-
export interface ToJSONSchemaOptions {
|
|
567
|
-
/**
|
|
568
|
-
* Whether to use strict mode (all fields required, additionalProperties: false)
|
|
569
|
-
*/
|
|
570
|
-
strict: boolean;
|
|
571
|
-
/**
|
|
572
|
-
* The target format for the JSON Schema. Resolves to a named FormatDescriptor.
|
|
573
|
-
* Built-in (`'openai'` / `'anthropic'` / `'google'`) or any string
|
|
574
|
-
* registered via `registerDescriptor`.
|
|
575
|
-
*/
|
|
576
|
-
format?: DescriptorFamily;
|
|
577
|
-
}
|
|
578
|
-
export type JSONSchemaType = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'null' | 'integer';
|
|
579
|
-
/**
|
|
580
|
-
* JSON Schema type definition
|
|
581
|
-
*/
|
|
582
|
-
export interface JSONSchema {
|
|
583
|
-
type?: JSONSchemaType | JSONSchemaType[];
|
|
584
|
-
properties?: Record<string, JSONSchema>;
|
|
585
|
-
items?: JSONSchema;
|
|
586
|
-
prefixItems?: JSONSchema[];
|
|
587
|
-
required?: string[];
|
|
588
|
-
additionalProperties?: boolean | JSONSchema;
|
|
589
|
-
propertyNames?: JSONSchema;
|
|
590
|
-
enum?: any[];
|
|
591
|
-
anyOf?: JSONSchema[];
|
|
592
|
-
allOf?: JSONSchema[];
|
|
593
|
-
additionalItems?: boolean | JSONSchema;
|
|
594
|
-
not?: JSONSchema;
|
|
595
|
-
$ref?: string;
|
|
596
|
-
description?: string;
|
|
597
|
-
default?: any;
|
|
598
|
-
pattern?: string;
|
|
599
|
-
format?: string;
|
|
600
|
-
minimum?: number;
|
|
601
|
-
maximum?: number;
|
|
602
|
-
exclusiveMinimum?: number;
|
|
603
|
-
exclusiveMaximum?: number;
|
|
604
|
-
multipleOf?: number;
|
|
605
|
-
minItems?: number;
|
|
606
|
-
maxItems?: number;
|
|
607
|
-
const?: any;
|
|
608
|
-
title?: string;
|
|
609
|
-
$defs?: Record<string, JSONSchema>;
|
|
610
|
-
/** Gemini-specific: deterministic property emission order for strict mode. */
|
|
611
|
-
propertyOrdering?: string[];
|
|
612
|
-
[metadata: string]: unknown;
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Converts a Zod schema to JSON Schema with support for different provider dialects.
|
|
616
|
-
*
|
|
617
|
-
* Accepts either a boolean (legacy: true = OPENAI_STRICT, false = LENIENT),
|
|
618
|
-
* an options object `{ strict, format? }`, or a `FormatDescriptor` directly.
|
|
619
|
-
*
|
|
620
|
-
* @param schema - The Zod schema to convert
|
|
621
|
-
* @param options - Configuration options for the conversion
|
|
622
|
-
* @returns JSON Schema object compatible with the resolved dialect
|
|
623
|
-
*
|
|
624
|
-
* @example
|
|
625
|
-
* ```typescript
|
|
626
|
-
* const schema = z.object({
|
|
627
|
-
* name: z.string(),
|
|
628
|
-
* age: z.number().optional(),
|
|
629
|
-
* });
|
|
630
|
-
*
|
|
631
|
-
* // For OpenAI strict mode
|
|
632
|
-
* const jsonSchema = toJSONSchema(schema, { strict: true, format: 'openai' });
|
|
633
|
-
*
|
|
634
|
-
* // For Anthropic strict mode
|
|
635
|
-
* const anthropic = toJSONSchema(schema, ANTHROPIC_STRICT);
|
|
636
|
-
* ```
|
|
637
|
-
*/
|
|
638
|
-
export declare function toJSONSchema(schema: z.ZodType, options: ToJSONSchemaOptions | boolean | FormatDescriptor): JSONSchema;
|
|
639
|
-
/**
|
|
640
|
-
* One emitted `enum` node's contribution to a dialect's size limits. A dialect
|
|
641
|
-
* bounds the TOTAL number of enum values in a schema and, separately, the
|
|
642
|
-
* character length of a SINGLE large enum — so the per-enum breakdown has to
|
|
643
|
-
* survive the walk; a pair of totals cannot answer the second question
|
|
644
|
-
* (two enums, one long-valued and one many-valued, would answer it wrongly).
|
|
645
|
-
*/
|
|
646
|
-
export interface EnumFeature {
|
|
647
|
-
/** How many members the enum emits. */
|
|
648
|
-
readonly valueCount: number;
|
|
649
|
-
/**
|
|
650
|
-
* Total character length of its STRING members. Non-string members
|
|
651
|
-
* contribute nothing: the character rules are written for string values.
|
|
652
|
-
*/
|
|
653
|
-
readonly stringValueChars: number;
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Structural feature counts for a Zod schema, used by the SchemaBudget to
|
|
657
|
-
* decide whether an item fits a descriptor's per-request budget, whether it
|
|
658
|
-
* fits the dialect's per-schema size limits, and whether the descriptor can
|
|
659
|
-
* express it at all.
|
|
660
|
-
*/
|
|
661
|
-
export interface SchemaFeatures {
|
|
662
|
-
/** True if the schema (or any subschema) uses `z.lazy` / self-recursion. */
|
|
663
|
-
hasRecursion: boolean;
|
|
664
|
-
/** Count of `z.optional()` leaves anywhere in the schema. */
|
|
665
|
-
optionalParameterCount: number;
|
|
666
|
-
/**
|
|
667
|
-
* Count of union-shaped nodes — `z.union`, `z.discriminatedUnion`, plus
|
|
668
|
-
* `z.nullable` (which on the wire under OpenAI strict becomes a type
|
|
669
|
-
* array `[T, "null"]`, also counts toward Anthropic's union-type budget).
|
|
670
|
-
*/
|
|
671
|
-
unionTypeCount: number;
|
|
672
|
-
/** Count of `z.record` nodes. */
|
|
673
|
-
recordCount: number;
|
|
674
|
-
/** Count of `z.tuple` nodes. */
|
|
675
|
-
tupleCount: number;
|
|
676
|
-
/** One entry per emitted `enum` node, in walk order. */
|
|
677
|
-
enums: readonly EnumFeature[];
|
|
678
|
-
/**
|
|
679
|
-
* Object properties the schema emits, summed over every nesting level. A
|
|
680
|
-
* record counts as the 2 properties its widest encoding emits
|
|
681
|
-
* (`{key, value}` pairs) and a tuple as one per item (numeric-key
|
|
682
|
-
* encoding) — the count is descriptor-independent, so it takes the largest
|
|
683
|
-
* encoding any dialect could choose (see `schemaSizeLimits`).
|
|
684
|
-
*/
|
|
685
|
-
objectPropertyCount: number;
|
|
686
|
-
/** Deepest container nesting below the root (an object of scalars is 1). */
|
|
687
|
-
maxNestingDepth: number;
|
|
688
|
-
/**
|
|
689
|
-
* Total characters over property names plus `enum` and `const` string
|
|
690
|
-
* values — the "total string size" a dialect bounds. `$defs` entry names are
|
|
691
|
-
* NOT included: they are generated at emit time, are few, and are short
|
|
692
|
-
* relative to a 120,000-character budget.
|
|
693
|
-
*/
|
|
694
|
-
stringSizeChars: number;
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Walk a Zod schema once and count its structural features. Result is
|
|
698
|
-
* cached per schema in a WeakMap — second-and-subsequent calls are O(1) and
|
|
699
|
-
* the entry is GC'd with the schema (same OOM-safe pattern as `strictify`).
|
|
700
|
-
*
|
|
701
|
-
* The walk is EAGER (it has to descend to count), so its cycle guard keys on
|
|
702
|
-
* `walkKey`, not on object identity: a `z.lazy` that rebuilds its inner schema
|
|
703
|
-
* per call would otherwise never re-enter the same object and the walk would
|
|
704
|
-
* never terminate. That is not hypothetical — it is a measured, reproducible
|
|
705
|
-
* heap-death, and this function is on the hot path of EVERY tool of EVERY
|
|
706
|
-
* request to a strict-family model (`SchemaBudget.allocate`).
|
|
707
|
-
*/
|
|
708
|
-
export declare function analyzeSchema(schema: z.ZodType | z.core.$ZodType): SchemaFeatures;
|
|
709
|
-
/**
|
|
710
|
-
* Report the ways ONE schema exceeds the descriptor's documented per-schema
|
|
711
|
-
* size limits (`FormatDescriptor.schemaSizeLimits`). Empty means it fits, or
|
|
712
|
-
* that the dialect publishes no limits.
|
|
713
|
-
*
|
|
714
|
-
* This is a FEASIBILITY question, not a budget one: the numbers bound a single
|
|
715
|
-
* schema, so no other tool in the request can change the answer, and
|
|
716
|
-
* `SchemaBudget.allocate` therefore treats a hit exactly like an
|
|
717
|
-
* unrepresentable keyword — that item degrades to the family's non-strict
|
|
718
|
-
* descriptor and the rest of the request is untouched.
|
|
719
|
-
*
|
|
720
|
-
* Exported because "why did my tool silently stop being strict?" needs an
|
|
721
|
-
* answer a caller can print; each line names the bound and the measurement.
|
|
722
|
-
*
|
|
723
|
-
* @example
|
|
724
|
-
* ```ts
|
|
725
|
-
* checkSchemaSizeLimits(z.object({ pick: z.enum(tenThousandNames) }), OPENAI_STRICT);
|
|
726
|
-
* // → ['enum values 10000 exceed maxTotalEnumValues 1000']
|
|
727
|
-
* ```
|
|
728
|
-
*/
|
|
729
|
-
export declare function checkSchemaSizeLimits(schema: z.ZodType | z.core.$ZodType, descriptor: FormatDescriptor): readonly string[];
|
|
730
|
-
/**
|
|
731
|
-
* Per-request strict-mode allocator.
|
|
732
|
-
*
|
|
733
|
-
* Constructed once per outgoing request with the chosen model's strictest
|
|
734
|
-
* descriptor. Tracks remaining slots (strict tools, optional parameters,
|
|
735
|
-
* union types) against the descriptor's documented per-request limits and
|
|
736
|
-
* decides per-tool / per-output whether to emit strict or fall back to the
|
|
737
|
-
* SAME FAMILY's non-strict descriptor (`degraded` — never the family-blind
|
|
738
|
-
* `LENIENT`, which would undo dialect rules that outlive strict mode).
|
|
739
|
-
*
|
|
740
|
-
* Selection guarantees that any tool with `strict: true` (hard requirement)
|
|
741
|
-
* has a model that supports the family — `allocate` therefore always grants
|
|
742
|
-
* strict for `requested === true` (subject only to feasibility, not budget;
|
|
743
|
-
* if `true` items overflow the budget the API call will fail, which is the
|
|
744
|
-
* correct behavior for hard requirements).
|
|
745
|
-
*
|
|
746
|
-
* Numeric `strict: <priority>` items are allocated greedily after `true`
|
|
747
|
-
* items have consumed their share of the budget. Caller is expected to
|
|
748
|
-
* pre-sort by descending priority before iterating.
|
|
749
|
-
*/
|
|
750
|
-
export declare class SchemaBudget {
|
|
751
|
-
private readonly descriptor;
|
|
752
|
-
/**
|
|
753
|
-
* What an item that does NOT get strict is emitted through: the descriptor's
|
|
754
|
-
* own family, non-strict.
|
|
755
|
-
*
|
|
756
|
-
* It is deliberately NOT the family-blind `LENIENT`. A dialect restriction
|
|
757
|
-
* can belong to the DIALECT rather than to strict mode — `GOOGLE_NON_STRICT`
|
|
758
|
-
* exists for exactly one such rule, the `unconstrained` encoding of
|
|
759
|
-
* `z.any()`, because Gemini builds a decoding grammar whenever a tool call is
|
|
760
|
-
* forced, with no per-tool strict flag involved. Degrading to `LENIENT` put
|
|
761
|
-
* the self-referencing `$defs/Any` that encoding was created to avoid back on
|
|
762
|
-
* the Google wire — and it did so for every gin/query-style recursive `anyOf`
|
|
763
|
-
* schema, i.e. the schemas that ALWAYS degrade, since `GOOGLE_STRICT` cannot
|
|
764
|
-
* express `anyOf` at all (measured: the product's real `api_set` tool schema
|
|
765
|
-
* allocates to `lenient` under `GOOGLE_STRICT`, and its emitted JSON carries
|
|
766
|
-
* nine `#/$defs/Any` references).
|
|
767
|
-
*
|
|
768
|
-
* For the other families this is behaviour-neutral by construction:
|
|
769
|
-
* `OPENAI_NON_STRICT` and `ANTHROPIC_NON_STRICT` are `LENIENT` with a family
|
|
770
|
-
* tag, and a family with no registered non-strict variant resolves to
|
|
771
|
-
* `LENIENT` (`getDescriptor`). Only the pinned `descriptor` id on the tool
|
|
772
|
-
* changes, which round-trips through `getDescriptorById`.
|
|
773
|
-
*/
|
|
774
|
-
private readonly degraded;
|
|
775
|
-
private remainingTools;
|
|
776
|
-
private remainingOptionalParams;
|
|
777
|
-
private remainingUnionTypes;
|
|
778
|
-
constructor(descriptor: FormatDescriptor);
|
|
779
|
-
/**
|
|
780
|
-
* Decide the effective descriptor for a tool's parameter schema given the
|
|
781
|
-
* dev's strict request, the schema's features, and the remaining budget.
|
|
782
|
-
*
|
|
783
|
-
* Returns the family's NON-STRICT descriptor (`LENIENT` for a family that
|
|
784
|
-
* registers none) when:
|
|
785
|
-
* - `requested === false`
|
|
786
|
-
* - the schema uses a feature the descriptor can't represent
|
|
787
|
-
* - the schema exceeds the dialect's per-schema size limits
|
|
788
|
-
* (`checkSchemaSizeLimits`)
|
|
789
|
-
* - the descriptor has slot limits and the budget is exhausted (only
|
|
790
|
-
* applies to `requested` being a positive number; `true` always wins
|
|
791
|
-
* subject to feasibility and size)
|
|
792
|
-
*
|
|
793
|
-
* Returns the strict descriptor (and decrements the budget) otherwise.
|
|
794
|
-
*/
|
|
795
|
-
allocateTool(schema: z.ZodType | z.core.$ZodType, requested: boolean | number | undefined): FormatDescriptor;
|
|
796
|
-
/**
|
|
797
|
-
* Same as `allocateTool` but for the request's structured-output schema.
|
|
798
|
-
* The output schema doesn't consume a tool slot but it does count toward
|
|
799
|
-
* the optional-parameter and union-type budgets — descriptors document
|
|
800
|
-
* those limits as "across all strict schemas in one request".
|
|
801
|
-
*/
|
|
802
|
-
allocateOutput(schema: z.ZodType | z.core.$ZodType, requested: boolean | number | undefined): FormatDescriptor;
|
|
803
|
-
/** Snapshot of remaining budget. Useful for telemetry / debug logs. */
|
|
804
|
-
remaining(): {
|
|
805
|
-
strictTools: number;
|
|
806
|
-
optionalParams: number;
|
|
807
|
-
unionTypes: number;
|
|
808
|
-
};
|
|
809
|
-
private allocate;
|
|
810
|
-
}
|
|
811
|
-
/**
|
|
812
|
-
* Compare two descriptors and return the one with the tighter per-request
|
|
813
|
-
* budget. Used by providers to pick a single descriptor for a SchemaBudget
|
|
814
|
-
* shared between `convertTools` and `convertResponseFormat` — Anthropic's
|
|
815
|
-
* documented limits apply across the whole request, so the strictest
|
|
816
|
-
* descriptor wins.
|
|
817
|
-
*
|
|
818
|
-
* `schemaSizeLimits` deliberately does NOT participate. Those bounds are
|
|
819
|
-
* per-schema, so they are evaluated against whichever descriptor this returns
|
|
820
|
-
* rather than shared between the two — and every provider here passes two
|
|
821
|
-
* descriptors resolved from the SAME family, so there is nothing to break the
|
|
822
|
-
* tie on. Ranking six size bounds against three slot budgets would be an
|
|
823
|
-
* invented ordering, not a documented one.
|
|
824
|
-
*/
|
|
825
|
-
export declare function strictestOf(a: FormatDescriptor, b: FormatDescriptor): FormatDescriptor;
|
|
826
|
-
/**
|
|
827
|
-
* Convert a `boolean | number | undefined` strict request into a numeric
|
|
828
|
-
* priority for sorting. Used by providers to allocate strict slots in
|
|
829
|
-
* descending priority order.
|
|
830
|
-
*
|
|
831
|
-
* - `true` → `+Infinity` (hard requirement; always first in line)
|
|
832
|
-
* - `false` → `-Infinity` (always lenient; never wants strict)
|
|
833
|
-
* - `number > 0` → the number itself
|
|
834
|
-
* - `number <= 0` or `undefined` → `0` (no preference)
|
|
835
|
-
*/
|
|
836
|
-
export declare function strictPriority(requested: boolean | number | undefined): number;
|
|
837
|
-
//# sourceMappingURL=schema.d.ts.map
|