@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
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider family for a `FormatDescriptor`. The three known wire dialects
|
|
3
|
+
* (`'openai'`, `'anthropic'`, `'google'`) are autocomplete hints; any string
|
|
4
|
+
* is accepted so users can register custom descriptors via
|
|
5
|
+
* `registerDescriptor` and reference them by family name in
|
|
6
|
+
* `ModelInfo.strictFormat`. `'lenient'` is the no-rewrite catch-all and is
|
|
7
|
+
* not registerable as a custom family.
|
|
8
|
+
*
|
|
9
|
+
* Use this type wherever you need to talk about a descriptor family —
|
|
10
|
+
* registry functions, model declarations, etc.
|
|
11
|
+
*/
|
|
12
|
+
export type DescriptorFamily = 'openai' | 'anthropic' | 'google' | 'xai' | (string & {});
|
|
13
|
+
/**
|
|
14
|
+
* Documented ceilings on how BIG one strict schema may be, as opposed to which
|
|
15
|
+
* keywords it may use. Every limit here is a SUM taken over a single schema —
|
|
16
|
+
* one tool's `parameters`, or one structured-output schema — not a per-request
|
|
17
|
+
* allowance shared between them (that is `maxStrictTools` and friends, which
|
|
18
|
+
* the `SchemaBudget` spends across the whole request).
|
|
19
|
+
*
|
|
20
|
+
* A schema that exceeds one of these is a guaranteed provider rejection under
|
|
21
|
+
* strict mode, so `SchemaBudget.allocate` treats it like any other
|
|
22
|
+
* infeasibility and degrades that ONE item to the family's non-strict
|
|
23
|
+
* descriptor, where no size rule applies. The rest of the request is
|
|
24
|
+
* unaffected.
|
|
25
|
+
*
|
|
26
|
+
* Declared as one object rather than six optional fields on purpose: a
|
|
27
|
+
* provider documents these together, and the object being present is what says
|
|
28
|
+
* "this dialect publishes size limits". Adding a rule here is then a compile
|
|
29
|
+
* error at every descriptor that declares the group, which is the point
|
|
30
|
+
* (a missed limit is invisible until a 400).
|
|
31
|
+
*/
|
|
32
|
+
export interface SchemaSizeLimits {
|
|
33
|
+
/** Maximum object properties in one schema, summed over every nesting level. */
|
|
34
|
+
readonly maxObjectProperties: number;
|
|
35
|
+
/** Maximum levels of nested containers below the schema root. */
|
|
36
|
+
readonly maxNestingDepth: number;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum total characters over the schema's property names, `enum` values
|
|
39
|
+
* and `const` values together.
|
|
40
|
+
*/
|
|
41
|
+
readonly maxTotalStringChars: number;
|
|
42
|
+
/** Maximum `enum` values summed across every enum property in one schema. */
|
|
43
|
+
readonly maxTotalEnumValues: number;
|
|
44
|
+
/**
|
|
45
|
+
* The single-enum value count above which `maxLargeEnumStringChars` starts
|
|
46
|
+
* to apply. A smaller enum is bounded only by the two totals above.
|
|
47
|
+
*/
|
|
48
|
+
readonly largeEnumValueCount: number;
|
|
49
|
+
/**
|
|
50
|
+
* Maximum total characters of ONE enum's string values, applied only to an
|
|
51
|
+
* enum carrying more than `largeEnumValueCount` values.
|
|
52
|
+
*/
|
|
53
|
+
readonly maxLargeEnumStringChars: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Format descriptor — describes the JSON Schema dialect of a target.
|
|
57
|
+
*
|
|
58
|
+
* Each provider/strict combination is represented by one descriptor. The
|
|
59
|
+
* descriptor controls both how `strictify` rewrites a Zod schema (preprocesses
|
|
60
|
+
* for record-as-array, tuple-as-object, etc.) and how `toJSONSchema` emits the
|
|
61
|
+
* matching JSON Schema. Every `if (strict)` decision in the conversion
|
|
62
|
+
* pipeline dispatches off a descriptor field, so adding a new dialect is a
|
|
63
|
+
* matter of declaring a new `FormatDescriptor` and registering it.
|
|
64
|
+
*/
|
|
65
|
+
export interface FormatDescriptor {
|
|
66
|
+
/** Stable id used as the cache key inside `strictify`. */
|
|
67
|
+
readonly id: string;
|
|
68
|
+
/**
|
|
69
|
+
* Provider family for this descriptor. The three built-in wire dialects
|
|
70
|
+
* are `'openai'` / `'anthropic'` / `'google'`; `'lenient'` is the
|
|
71
|
+
* no-rewrite catch-all. Custom descriptors registered via
|
|
72
|
+
* `registerDescriptor` may use any string here — the family is the
|
|
73
|
+
* lookup key used by `getDescriptor(family, strict)`.
|
|
74
|
+
*/
|
|
75
|
+
readonly family: DescriptorFamily | 'lenient';
|
|
76
|
+
/** True if this descriptor represents the strict variant for its family. */
|
|
77
|
+
readonly strict: boolean;
|
|
78
|
+
/** When true, every object field appears in `required[]` (OpenAI strict). */
|
|
79
|
+
readonly objectAllFieldsRequired: boolean;
|
|
80
|
+
/** When true, every object emits `additionalProperties: false`. */
|
|
81
|
+
readonly objectClosedByDefault: boolean;
|
|
82
|
+
/** How to encode `z.record(K, V)`. */
|
|
83
|
+
readonly recordEncoding: 'array-of-pairs' | 'open-record';
|
|
84
|
+
/**
|
|
85
|
+
* How to encode `z.tuple([...])`.
|
|
86
|
+
* - `object-numeric-keys`: OpenAI-strict workaround (`{ "0": A, "1": B, ... }`)
|
|
87
|
+
* - `prefix-items`: standard JSON Schema `prefixItems` (Anthropic / Google)
|
|
88
|
+
* - `items-union`: collapse to homogeneous `items: { anyOf: [...] }` (last-resort)
|
|
89
|
+
*/
|
|
90
|
+
readonly tupleEncoding: 'object-numeric-keys' | 'prefix-items' | 'items-union';
|
|
91
|
+
readonly allowAllOf: boolean;
|
|
92
|
+
readonly allowAnyOf: boolean;
|
|
93
|
+
readonly allowOneOf: boolean;
|
|
94
|
+
/** Whether `{ $ref: '#' }` self-reference is permitted. */
|
|
95
|
+
readonly allowRootRef: boolean;
|
|
96
|
+
readonly allowDefsRef: boolean;
|
|
97
|
+
/** Reserved: emit Google-style `propertyOrdering` hints. */
|
|
98
|
+
readonly emitPropertyOrdering: boolean;
|
|
99
|
+
/** Whitelist of `format:` values to emit, or `'all'` for every format JSON Schema defines. */
|
|
100
|
+
readonly supportedStringFormats: ReadonlySet<string> | 'all';
|
|
101
|
+
readonly allowPattern: boolean;
|
|
102
|
+
readonly allowMultiplePatterns: boolean;
|
|
103
|
+
readonly allowMinMaxLength: boolean;
|
|
104
|
+
readonly allowMinMaxItems: boolean;
|
|
105
|
+
readonly allowMinimumMaximum: boolean;
|
|
106
|
+
/** When true, `optional` becomes `T|null` (because every prop must be required). */
|
|
107
|
+
readonly optionalAsNullable: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* How `z.any()` / `z.unknown()` is encoded.
|
|
110
|
+
* - `recursive-strict`: self-referencing $defs/Any with array-of-pairs records
|
|
111
|
+
* (OpenAI strict has no open-object support). REQUIRES `allowAnyOf` and
|
|
112
|
+
* `allowDefsRef`.
|
|
113
|
+
* - `recursive-open`: $defs/Any with `additionalProperties: <self>` records.
|
|
114
|
+
* REQUIRES `allowAnyOf` and `allowDefsRef`.
|
|
115
|
+
* - `flat`: a non-recursive `anyOf` over the JSON value types
|
|
116
|
+
* (`string`/`number`/`boolean`/`null`/`array`/open-`object`). Used by
|
|
117
|
+
* descriptors that reject recursive `$defs/Any` definitions (xAI strict).
|
|
118
|
+
* Equivalent to TypeScript `any` — accepts every JSON value without
|
|
119
|
+
* imposing any structural constraints. REQUIRES `allowAnyOf` (no `$defs`
|
|
120
|
+
* needed).
|
|
121
|
+
* - `unconstrained`: the empty schema `{}` (plus any `description`). Per
|
|
122
|
+
* JSON Schema, a schema with no assertion keywords validates EVERY
|
|
123
|
+
* instance — which is exactly what "any JSON value" means — and it
|
|
124
|
+
* introduces no keyword a restrictive dialect could reject. REQUIRES
|
|
125
|
+
* NOTHING, so it's the only encoding available to a dialect that forbids
|
|
126
|
+
* both combinators and named `$defs`/`$ref` (Google — see `GOOGLE_STRICT`).
|
|
127
|
+
* - `none`: the dialect has no spelling for "any JSON value" (Anthropic
|
|
128
|
+
* strict rejects `{}`, a typeless schema, an open object and a recursive
|
|
129
|
+
* `$defs/Any`). A schema containing one — or a recursion cycle, whose
|
|
130
|
+
* placeholder bottoms out at "any" — is not sendable, so the budget
|
|
131
|
+
* degrades the tool and `canExpress` sends an output as prompt text.
|
|
132
|
+
*
|
|
133
|
+
* The `REQUIRES` notes above are enforced by `checkDescriptorConsistency`,
|
|
134
|
+
* which `registerDescriptor` runs so a self-contradictory descriptor is
|
|
135
|
+
* caught at declaration time rather than as a provider HTTP 400.
|
|
136
|
+
*/
|
|
137
|
+
readonly anyEncoding: 'recursive-strict' | 'recursive-open' | 'flat' | 'unconstrained' | 'none';
|
|
138
|
+
/** Maximum number of tools that can carry `strict: true` in one request. */
|
|
139
|
+
readonly maxStrictTools?: number;
|
|
140
|
+
/** Maximum total optional parameters across all strict schemas in one request. */
|
|
141
|
+
readonly maxStrictOptionalParams?: number;
|
|
142
|
+
/** Maximum total union-type parameters across all strict schemas in one request. */
|
|
143
|
+
readonly maxStrictUnionTypes?: number;
|
|
144
|
+
/**
|
|
145
|
+
* Documented ceilings on the SIZE of one strict schema (properties, nesting,
|
|
146
|
+
* total string bytes, enum values), or `undefined` when the dialect
|
|
147
|
+
* publishes none. See {@link SchemaSizeLimits} for what each bound counts and
|
|
148
|
+
* why it is per-schema rather than per-request.
|
|
149
|
+
*
|
|
150
|
+
* Only a STRICT descriptor declares these: they are Structured-Outputs rules
|
|
151
|
+
* — the API compiles the schema into a decoder and rejects one it cannot —
|
|
152
|
+
* and a non-strict schema is a best-effort hint the API does not compile. So
|
|
153
|
+
* degrading the offending item to the family's non-strict descriptor is both
|
|
154
|
+
* the fix and the reason the limits stop applying to it
|
|
155
|
+
* (`SchemaBudget.allocate`, `checkSchemaSizeLimits`).
|
|
156
|
+
*
|
|
157
|
+
* The measurement is taken from the ZOD schema, not from the emitted JSON,
|
|
158
|
+
* so it is deliberately CONSERVATIVE where an encoding is descriptor-
|
|
159
|
+
* dependent (a record counts as its widest encoding, a tuple as its
|
|
160
|
+
* numeric-key one). Over-counting costs strictness on a schema that would
|
|
161
|
+
* have fit; under-counting costs an HTTP 400, so the round goes up.
|
|
162
|
+
*/
|
|
163
|
+
readonly schemaSizeLimits?: SchemaSizeLimits;
|
|
164
|
+
/**
|
|
165
|
+
* Maximum number of values ONE emitted `enum` may carry, or `undefined`
|
|
166
|
+
* (the default) for no cap.
|
|
167
|
+
*
|
|
168
|
+
* This is deliberately NOT one of the per-request budget fields above. Those
|
|
169
|
+
* are strict-mode allowances the SchemaBudget spends across a whole request,
|
|
170
|
+
* degrading an item that no longer fits to the family's non-strict
|
|
171
|
+
* descriptor; this one is a property of the dialect itself — it fires whatever the strictness — so it is enforced
|
|
172
|
+
* structurally at the point an `enum` is emitted (`convertSchema`), where
|
|
173
|
+
* the cardinality is actually known.
|
|
174
|
+
*
|
|
175
|
+
* Over the cap the emitter **widens** rather than truncates: it drops the
|
|
176
|
+
* `enum` keyword entirely, emits the node's plain scalar `type`
|
|
177
|
+
* (`{type: 'string'}` for a string enum), and puts EVERY value into the
|
|
178
|
+
* node's `description`. Nothing becomes unreachable — the model still has
|
|
179
|
+
* the complete list and can name any member — and the cardinality the
|
|
180
|
+
* dialect could not compile is simply no longer on the wire.
|
|
181
|
+
*
|
|
182
|
+
* The trade this makes is enforcement: a widened node no longer rejects a
|
|
183
|
+
* value structurally, so whatever validates the model's output afterwards
|
|
184
|
+
* has to. `Tool.parse` does, against the ORIGINAL zod schema — the enum is
|
|
185
|
+
* still there, only the emitted JSON Schema widened — so a hallucinated
|
|
186
|
+
* member comes back as a normal, re-promptable validation error.
|
|
187
|
+
*
|
|
188
|
+
* **`GOOGLE_STRICT`/`GOOGLE_NON_STRICT` declare 40. Every other built-in
|
|
189
|
+
* declares no cap.** Two earlier rounds of live measurement against
|
|
190
|
+
* `google/gemini-3-flash-preview` through OpenRouter (2026-08-23) — single
|
|
191
|
+
* enums up to 2048 SYNTHETIC values, 20 tools each with a 96-value enum, and
|
|
192
|
+
* an eleven-tool roster modelled on a real kind-authoring agent — all
|
|
193
|
+
* returned HTTP 200 and concluded there was no reproducible limit. **That
|
|
194
|
+
* conclusion was wrong, because none of those enums used the CONTENT of a
|
|
195
|
+
* real session.** A third round replayed the exact wire bytes of a real
|
|
196
|
+
* product request — `fn_load`'s `names` array, enum-constrained to the
|
|
197
|
+
* session's actual 98 known component/fn names (`default_renderer`,
|
|
198
|
+
* `QueryWidget`, `ConfirmPrompt`, `list_widget_types`, …) — and it 400'd,
|
|
199
|
+
* reproducibly, twice, byte-for-byte identical to what the provider was
|
|
200
|
+
* actually sent. Bisecting that same real value list: 90 values passes, 93
|
|
201
|
+
* fails. Bisecting what changes between the passing synthetic probes and the
|
|
202
|
+
* failing real one: 98 SHORT synthetic values (`n0`..`n97`, 284 chars) pass;
|
|
203
|
+
* 206 short values matching the REAL list's total character count (714
|
|
204
|
+
* chars) also pass; only the REAL, longer, dictionary-like/identifier-shaped
|
|
205
|
+
* values fail, and only past ~92 of them. So the limit tracks neither raw
|
|
206
|
+
* member count nor total character/byte budget — most likely actual
|
|
207
|
+
* tokenized grammar-state count, which short repetitive synthetic strings
|
|
208
|
+
* compress far below what real identifier text does at the same count or
|
|
209
|
+
* character budget. This is why the first two rounds' synthetic and
|
|
210
|
+
* lightly-realistic probes never tripped it.
|
|
211
|
+
*
|
|
212
|
+
* **The widen-to-string fallback this field triggers was verified against
|
|
213
|
+
* the exact failing real schema**: dropping `enum` and putting the same 98
|
|
214
|
+
* values into the `description` of a plain string field returned HTTP 200
|
|
215
|
+
* with the expected tool call — confirming the fallback mechanism actually
|
|
216
|
+
* fixes the failure it exists for, not just a synthetic stand-in.
|
|
217
|
+
*
|
|
218
|
+
* **40 is not a measured edge — it is a conservative floor with roughly 2x
|
|
219
|
+
* margin under the real one (90 passes / 93 fails) measured on this one real
|
|
220
|
+
* value set.** Given the failure tracks content, not just count, a different
|
|
221
|
+
* session's actual name list could plausibly fail at a different count; 40
|
|
222
|
+
* was also the cap this exact product ran under in production for months
|
|
223
|
+
* before a since-reverted attempt to remove it, which is corroborating
|
|
224
|
+
* evidence it holds in practice, not just in this one measurement.
|
|
225
|
+
*
|
|
226
|
+
* The reliability comparison from the earlier rounds still stands and is
|
|
227
|
+
* why a cap is preferable to leaving every enum uncapped "to be safe" at a
|
|
228
|
+
* much lower number: `enum` vs plain-string-with-full-list, 10 trials each
|
|
229
|
+
* over confusable near-duplicate names at 96 and 512 values, scored 10/10
|
|
230
|
+
* on-target with zero off-list answers for BOTH encodings. So there is no
|
|
231
|
+
* reliability cost to raising this cap later if a higher safe value is
|
|
232
|
+
* measured — only downside risk to leaving it too high.
|
|
233
|
+
*
|
|
234
|
+
* One trap for whoever measures this next: with `max_tokens` at 64, a
|
|
235
|
+
* forced-tool probe can come back `finish_reason: 'length'` with no tool
|
|
236
|
+
* call and empty content, which reads exactly like a grammar that produced
|
|
237
|
+
* nothing. It usually is not — this model spends reasoning tokens first.
|
|
238
|
+
* Give a forced-tool probe room before calling an empty completion a
|
|
239
|
+
* failure.
|
|
240
|
+
*
|
|
241
|
+
* So: set this only where a provider is MEASURED to reject a large enum
|
|
242
|
+
* using the SESSION'S OWN real content, not synthetic stand-ins, and set it
|
|
243
|
+
* conservatively below the measured edge until more value sets are tested.
|
|
244
|
+
* An `enum` that fits is the more correct encoding, because it is the one
|
|
245
|
+
* the model cannot answer outside of.
|
|
246
|
+
*/
|
|
247
|
+
readonly maxEnumValues?: number;
|
|
248
|
+
/**
|
|
249
|
+
* Whether the descriptor can express recursive `$ref` schemas (`$ref: '#'`
|
|
250
|
+
* to root, or any `$ref: '#/$defs/X'` whose target transitively references
|
|
251
|
+
* itself). OpenAI / Google strict / Lenient: true. Anthropic strict: false
|
|
252
|
+
* — Anthropic's tool `input_schema` validator explicitly rejects circular
|
|
253
|
+
* `$defs` graphs.
|
|
254
|
+
*
|
|
255
|
+
* When `false`, the JSON-Schema emitter detects cycles in the source
|
|
256
|
+
* schema and replaces the offending back-edge with the descriptor's flat
|
|
257
|
+
* "any" shape (see `anyEncoding`) carrying a description that names the
|
|
258
|
+
* `$defs` entry it would otherwise have referenced. Non-cyclic shared
|
|
259
|
+
* `$ref` reuse is unaffected — those still emit as a `$ref` to a `$defs`
|
|
260
|
+
* entry.
|
|
261
|
+
*/
|
|
262
|
+
readonly supportsRecursion: boolean;
|
|
263
|
+
/**
|
|
264
|
+
* True when the family's structured output is always compiled to a grammar,
|
|
265
|
+
* so there is no non-strict `json_schema` to degrade an output to. Anthropic:
|
|
266
|
+
* `output_config.format` is strict by definition, and OpenRouter adds the
|
|
267
|
+
* structured-outputs flag to every `json_schema` bound for Claude — a
|
|
268
|
+
* "non-strict" output with 30 optional fields still earns "Schemas contains
|
|
269
|
+
* too many optional parameters (30) … (limit: 24)" (measured 2026-11-04).
|
|
270
|
+
*
|
|
271
|
+
* A schema over the strict limits then cannot go out as structured output at
|
|
272
|
+
* all: `canExpress` answers `false`, so it is delivered as prompt text, and
|
|
273
|
+
* `SchemaBudget` never degrades an output to the non-strict variant.
|
|
274
|
+
*/
|
|
275
|
+
readonly structuredOutputAlwaysStrict?: boolean;
|
|
276
|
+
/**
|
|
277
|
+
* Instruction appended after the schema text when a Prompt's/Tool's schema
|
|
278
|
+
* can't be expressed as this descriptor's structured output and is instead
|
|
279
|
+
* delivered to the model as PROMPT TEXT (see `canExpress` and the ai-layer
|
|
280
|
+
* `applySchemaDeliveryFallback`). Steers the model to emit a single raw JSON
|
|
281
|
+
* object rather than echoing the schema or wrapping it in prose/fences.
|
|
282
|
+
*
|
|
283
|
+
* Optional — descriptors that omit it fall back to
|
|
284
|
+
* `DEFAULT_JSON_FALLBACK_INSTRUCTION` via `getJsonFallbackInstruction`.
|
|
285
|
+
*/
|
|
286
|
+
readonly jsonFallbackInstruction?: string;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Default instruction used when a `FormatDescriptor` omits
|
|
290
|
+
* `jsonFallbackInstruction`. Appended after the schema text when a schema is
|
|
291
|
+
* delivered as prompt text instead of as structured output.
|
|
292
|
+
*/
|
|
293
|
+
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.";
|
|
294
|
+
/**
|
|
295
|
+
* Resolve the prompt-text fallback instruction for a descriptor, falling back
|
|
296
|
+
* to `DEFAULT_JSON_FALLBACK_INSTRUCTION` when the descriptor doesn't declare
|
|
297
|
+
* one of its own.
|
|
298
|
+
*/
|
|
299
|
+
export declare function getJsonFallbackInstruction(descriptor: FormatDescriptor): string;
|
|
300
|
+
//# sourceMappingURL=descriptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../../src/schema/descriptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default instruction used when a `FormatDescriptor` omits
|
|
3
|
+
* `jsonFallbackInstruction`. Appended after the schema text when a schema is
|
|
4
|
+
* delivered as prompt text instead of as structured output.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_JSON_FALLBACK_INSTRUCTION = 'Return ONLY a single raw JSON object conforming to the schema above — no markdown code fences, no prose, and do NOT echo the schema itself.';
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the prompt-text fallback instruction for a descriptor, falling back
|
|
9
|
+
* to `DEFAULT_JSON_FALLBACK_INSTRUCTION` when the descriptor doesn't declare
|
|
10
|
+
* one of its own.
|
|
11
|
+
*/
|
|
12
|
+
export function getJsonFallbackInstruction(descriptor) {
|
|
13
|
+
return descriptor.jsonFallbackInstruction ?? DEFAULT_JSON_FALLBACK_INSTRUCTION;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=descriptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.js","sourceRoot":"","sources":["../../src/schema/descriptor.ts"],"names":[],"mappings":"AAmUA;;;;GAIG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAC5C,6IAA6I,CAAC;AAEhJ;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAA4B;IACrE,OAAO,UAAU,CAAC,uBAAuB,IAAI,iCAAiC,CAAC;AACjF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FormatDescriptor } from '../descriptor';
|
|
2
|
+
/**
|
|
3
|
+
* Anthropic strict — Claude 4.5 and later (Opus 4.5–4.8, Sonnet 4.5/4.6,
|
|
4
|
+
* Haiku 4.5, and the 5.x Opus, Sonnet, Haiku, Fable, and Mythos models). Strict
|
|
5
|
+
* tools and `output_config.format` are GA with no beta header (re-read
|
|
6
|
+
* 2026-10-30). Per Anthropic's structured-outputs docs:
|
|
7
|
+
*
|
|
8
|
+
* - `additionalProperties` may **only** be `false` (no schema-valued open
|
|
9
|
+
* records), so `z.record(...)` falls back to the OpenAI-style
|
|
10
|
+
* array-of-pairs workaround.
|
|
11
|
+
* - Non-cyclic `$ref` / `$defs` reuse is supported and used freely — only
|
|
12
|
+
* *circular* `$defs` graphs are rejected by the API
|
|
13
|
+
* (`Circular reference detected in schema definitions: …`). The emitter
|
|
14
|
+
* sets `supportsRecursion: false`; shared non-cyclic `$ref`s are emitted
|
|
15
|
+
* normally, and a cycle makes the schema unsendable (see `anyEncoding`).
|
|
16
|
+
* Root self-reference (`$ref: '#'`) is always cyclic by definition, so
|
|
17
|
+
* `allowRootRef` stays `false`.
|
|
18
|
+
* - Numerical (`minimum`/`maximum`/`multipleOf`) and string-length
|
|
19
|
+
* (`minLength`/`maxLength`) constraints are not supported and are dropped,
|
|
20
|
+
* as are array sizes (only `minItems` of 0 or 1 is accepted).
|
|
21
|
+
* - `allOf` is supported except around a `$ref`.
|
|
22
|
+
* - `pattern` may not use backreferences, lookaround, `\b` or large `{n,m}`
|
|
23
|
+
* ranges; a schema that compiles too big is rejected with "Schema is too
|
|
24
|
+
* complex for compilation".
|
|
25
|
+
* - `prefixItems` and other positional tuple keywords are not in the
|
|
26
|
+
* supported list, so tuples collapse to a homogeneous `items: anyOf`.
|
|
27
|
+
* - Formats are a closed list (re-read 2026-11-04): OpenAI strict's nine plus
|
|
28
|
+
* `uri`. Anything else is dropped rather than risk a 400.
|
|
29
|
+
*
|
|
30
|
+
* See docs/plans/schema-dialects-2026.md for sources.
|
|
31
|
+
*/
|
|
32
|
+
export declare const ANTHROPIC_STRICT: FormatDescriptor;
|
|
33
|
+
export declare const ANTHROPIC_NON_STRICT: FormatDescriptor;
|
|
34
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/schema/dialects/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,gBAAgB,EAAE,gBAoC7B,CAAC;AAEH,eAAO,MAAM,oBAAoB,EAAE,gBAIjC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { LENIENT } from './lenient';
|
|
2
|
+
const ANTHROPIC_STRICT_FORMATS = new Set([
|
|
3
|
+
'date-time', 'time', 'date', 'duration', 'email', 'hostname', 'uri', 'ipv4', 'ipv6', 'uuid',
|
|
4
|
+
]);
|
|
5
|
+
/**
|
|
6
|
+
* Anthropic strict — Claude 4.5 and later (Opus 4.5–4.8, Sonnet 4.5/4.6,
|
|
7
|
+
* Haiku 4.5, and the 5.x Opus, Sonnet, Haiku, Fable, and Mythos models). Strict
|
|
8
|
+
* tools and `output_config.format` are GA with no beta header (re-read
|
|
9
|
+
* 2026-10-30). Per Anthropic's structured-outputs docs:
|
|
10
|
+
*
|
|
11
|
+
* - `additionalProperties` may **only** be `false` (no schema-valued open
|
|
12
|
+
* records), so `z.record(...)` falls back to the OpenAI-style
|
|
13
|
+
* array-of-pairs workaround.
|
|
14
|
+
* - Non-cyclic `$ref` / `$defs` reuse is supported and used freely — only
|
|
15
|
+
* *circular* `$defs` graphs are rejected by the API
|
|
16
|
+
* (`Circular reference detected in schema definitions: …`). The emitter
|
|
17
|
+
* sets `supportsRecursion: false`; shared non-cyclic `$ref`s are emitted
|
|
18
|
+
* normally, and a cycle makes the schema unsendable (see `anyEncoding`).
|
|
19
|
+
* Root self-reference (`$ref: '#'`) is always cyclic by definition, so
|
|
20
|
+
* `allowRootRef` stays `false`.
|
|
21
|
+
* - Numerical (`minimum`/`maximum`/`multipleOf`) and string-length
|
|
22
|
+
* (`minLength`/`maxLength`) constraints are not supported and are dropped,
|
|
23
|
+
* as are array sizes (only `minItems` of 0 or 1 is accepted).
|
|
24
|
+
* - `allOf` is supported except around a `$ref`.
|
|
25
|
+
* - `pattern` may not use backreferences, lookaround, `\b` or large `{n,m}`
|
|
26
|
+
* ranges; a schema that compiles too big is rejected with "Schema is too
|
|
27
|
+
* complex for compilation".
|
|
28
|
+
* - `prefixItems` and other positional tuple keywords are not in the
|
|
29
|
+
* supported list, so tuples collapse to a homogeneous `items: anyOf`.
|
|
30
|
+
* - Formats are a closed list (re-read 2026-11-04): OpenAI strict's nine plus
|
|
31
|
+
* `uri`. Anything else is dropped rather than risk a 400.
|
|
32
|
+
*
|
|
33
|
+
* See docs/plans/schema-dialects-2026.md for sources.
|
|
34
|
+
*/
|
|
35
|
+
export const ANTHROPIC_STRICT = Object.freeze({
|
|
36
|
+
id: 'anthropic-strict',
|
|
37
|
+
family: 'anthropic',
|
|
38
|
+
strict: true,
|
|
39
|
+
objectAllFieldsRequired: false,
|
|
40
|
+
objectClosedByDefault: true,
|
|
41
|
+
recordEncoding: 'array-of-pairs',
|
|
42
|
+
tupleEncoding: 'items-union',
|
|
43
|
+
allowAllOf: true,
|
|
44
|
+
allowAnyOf: true,
|
|
45
|
+
allowOneOf: false,
|
|
46
|
+
allowRootRef: false,
|
|
47
|
+
allowDefsRef: true,
|
|
48
|
+
emitPropertyOrdering: false,
|
|
49
|
+
supportedStringFormats: ANTHROPIC_STRICT_FORMATS,
|
|
50
|
+
allowPattern: true,
|
|
51
|
+
allowMultiplePatterns: false,
|
|
52
|
+
allowMinMaxLength: false,
|
|
53
|
+
allowMinMaxItems: false,
|
|
54
|
+
allowMinimumMaximum: false,
|
|
55
|
+
optionalAsNullable: false,
|
|
56
|
+
// No spelling for "any JSON value": `{}` ("Empty schema … is not
|
|
57
|
+
// supported"), a typeless schema, `additionalProperties: true` and a
|
|
58
|
+
// recursive `$defs/Any` are all rejected (measured 2026-11-04).
|
|
59
|
+
anyEncoding: 'none',
|
|
60
|
+
// Anthropic-documented per-request limits (apply across ALL strict tool
|
|
61
|
+
// schemas + JSON output schemas in one request).
|
|
62
|
+
// Source: https://platform.claude.com/docs/en/build-with-claude/structured-outputs
|
|
63
|
+
maxStrictTools: 20,
|
|
64
|
+
maxStrictOptionalParams: 24,
|
|
65
|
+
maxStrictUnionTypes: 16,
|
|
66
|
+
// Recursive `$ref` graphs are rejected by Anthropic's tool input_schema
|
|
67
|
+
// validator, and the cycle-breaker's placeholder needs "any", so a cyclic
|
|
68
|
+
// schema is not sent strict.
|
|
69
|
+
supportsRecursion: false,
|
|
70
|
+
structuredOutputAlwaysStrict: true,
|
|
71
|
+
});
|
|
72
|
+
export const ANTHROPIC_NON_STRICT = Object.freeze({
|
|
73
|
+
...LENIENT,
|
|
74
|
+
id: 'anthropic-non-strict',
|
|
75
|
+
family: 'anthropic',
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/schema/dialects/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC;IACvC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAC5F,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqB,MAAM,CAAC,MAAM,CAAC;IAC9D,EAAE,EAAE,kBAAkB;IACtB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,IAAI;IACZ,uBAAuB,EAAE,KAAK;IAC9B,qBAAqB,EAAE,IAAI;IAC3B,cAAc,EAAE,gBAAgB;IAChC,aAAa,EAAE,aAAa;IAC5B,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,KAAK;IACnB,YAAY,EAAE,IAAI;IAClB,oBAAoB,EAAE,KAAK;IAC3B,sBAAsB,EAAE,wBAAwB;IAChD,YAAY,EAAE,IAAI;IAClB,qBAAqB,EAAE,KAAK;IAC5B,iBAAiB,EAAE,KAAK;IACxB,gBAAgB,EAAE,KAAK;IACvB,mBAAmB,EAAE,KAAK;IAC1B,kBAAkB,EAAE,KAAK;IACzB,iEAAiE;IACjE,qEAAqE;IACrE,gEAAgE;IAChE,WAAW,EAAE,MAAM;IACnB,wEAAwE;IACxE,iDAAiD;IACjD,mFAAmF;IACnF,cAAc,EAAE,EAAE;IAClB,uBAAuB,EAAE,EAAE;IAC3B,mBAAmB,EAAE,EAAE;IACvB,wEAAwE;IACxE,0EAA0E;IAC1E,6BAA6B;IAC7B,iBAAiB,EAAE,KAAK;IACxB,4BAA4B,EAAE,IAAI;CACnC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAqB,MAAM,CAAC,MAAM,CAAC;IAClE,GAAG,OAAO;IACV,EAAE,EAAE,sBAAsB;IAC1B,MAAM,EAAE,WAAW;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { FormatDescriptor } from '../descriptor';
|
|
2
|
+
/**
|
|
3
|
+
* Google Gemini strict — per `ai.google.dev/gemini-api/docs/structured-output`
|
|
4
|
+
* (re-read 2026-10-30, after Google's November 2025 JSON Schema expansion):
|
|
5
|
+
*
|
|
6
|
+
* - Supported keywords: types (including `["T", "null"]` arrays),
|
|
7
|
+
* `properties`, `required`, `additionalProperties`, `enum`, `format`
|
|
8
|
+
* (date-time/date/time), `minimum`, `maximum`, `items`, `prefixItems`,
|
|
9
|
+
* `minItems`, `maxItems`, `anyOf` (`oneOf` is read as `anyOf`), `$defs`,
|
|
10
|
+
* `$ref`, `title`, `description`, `propertyOrdering`.
|
|
11
|
+
* - `allOf` is not supported and `pattern` is not listed; both stay off.
|
|
12
|
+
* Live on `google/gemini-3-flash-preview` via OpenRouter (2026-10-30) an
|
|
13
|
+
* `allOf` tool was accepted but not enforced and a `pattern` tool timed out,
|
|
14
|
+
* while `anyOf`, shared `$defs`, `prefixItems`, nullable type arrays, closed
|
|
15
|
+
* objects, and recursion through array items or optional properties all
|
|
16
|
+
* answered with valid tool calls.
|
|
17
|
+
* - `minLength`/`maxLength` are stripped by the API, so they are not emitted.
|
|
18
|
+
* - Key order is preserved from the schema on Gemini 2.5+, so
|
|
19
|
+
* `propertyOrdering` is only needed by older models; it is still emitted.
|
|
20
|
+
* - `z.any()` / `z.unknown()` uses the `unconstrained` encoding. Gemini
|
|
21
|
+
* rejects ref loops through REQUIRED values: a self-referencing `$defs/Any`
|
|
22
|
+
* that recurses through `additionalProperties` fails with "ref loops are
|
|
23
|
+
* only supported if they include optional or nullable property values, or
|
|
24
|
+
* a potentially-zero-length array items" (reproduced 2026-10-30 after an
|
|
25
|
+
* interval in August 2026 when it was accepted), so the empty schema stays.
|
|
26
|
+
* Ordinary recursive models recurse through optional fields or arrays,
|
|
27
|
+
* which Gemini accepts.
|
|
28
|
+
*/
|
|
29
|
+
export declare const GOOGLE_STRICT: FormatDescriptor;
|
|
30
|
+
/**
|
|
31
|
+
* Google Gemini non-strict — LENIENT's permissive rules EXCEPT for the "any"
|
|
32
|
+
* encoding.
|
|
33
|
+
*
|
|
34
|
+
* Gemini rejects a self-referencing `$defs/Any` when it has to build a
|
|
35
|
+
* decoding grammar, and it builds one whenever a tool call is forced —
|
|
36
|
+
* independent of any per-tool `strict` flag, which Google's function-calling
|
|
37
|
+
* API doesn't even have. So the `unconstrained` encoding is a property of the
|
|
38
|
+
* DIALECT, not of strict mode, and belongs on both Google descriptors.
|
|
39
|
+
*
|
|
40
|
+
* Everything else stays LENIENT deliberately: the restrictions on
|
|
41
|
+
* `GOOGLE_STRICT` come from the documented structured-output keyword list,
|
|
42
|
+
* which is a grammar-mode concern, and tightening them here would push
|
|
43
|
+
* ordinary Gemini schemas onto the prompt-text delivery fallback for no
|
|
44
|
+
* measured reason.
|
|
45
|
+
*/
|
|
46
|
+
export declare const GOOGLE_NON_STRICT: FormatDescriptor;
|
|
47
|
+
//# sourceMappingURL=google.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../../src/schema/dialects/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,aAAa,EAAE,gBAmC1B,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,gBAW9B,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { LENIENT } from './lenient';
|
|
2
|
+
/**
|
|
3
|
+
* Google Gemini strict — per `ai.google.dev/gemini-api/docs/structured-output`
|
|
4
|
+
* (re-read 2026-10-30, after Google's November 2025 JSON Schema expansion):
|
|
5
|
+
*
|
|
6
|
+
* - Supported keywords: types (including `["T", "null"]` arrays),
|
|
7
|
+
* `properties`, `required`, `additionalProperties`, `enum`, `format`
|
|
8
|
+
* (date-time/date/time), `minimum`, `maximum`, `items`, `prefixItems`,
|
|
9
|
+
* `minItems`, `maxItems`, `anyOf` (`oneOf` is read as `anyOf`), `$defs`,
|
|
10
|
+
* `$ref`, `title`, `description`, `propertyOrdering`.
|
|
11
|
+
* - `allOf` is not supported and `pattern` is not listed; both stay off.
|
|
12
|
+
* Live on `google/gemini-3-flash-preview` via OpenRouter (2026-10-30) an
|
|
13
|
+
* `allOf` tool was accepted but not enforced and a `pattern` tool timed out,
|
|
14
|
+
* while `anyOf`, shared `$defs`, `prefixItems`, nullable type arrays, closed
|
|
15
|
+
* objects, and recursion through array items or optional properties all
|
|
16
|
+
* answered with valid tool calls.
|
|
17
|
+
* - `minLength`/`maxLength` are stripped by the API, so they are not emitted.
|
|
18
|
+
* - Key order is preserved from the schema on Gemini 2.5+, so
|
|
19
|
+
* `propertyOrdering` is only needed by older models; it is still emitted.
|
|
20
|
+
* - `z.any()` / `z.unknown()` uses the `unconstrained` encoding. Gemini
|
|
21
|
+
* rejects ref loops through REQUIRED values: a self-referencing `$defs/Any`
|
|
22
|
+
* that recurses through `additionalProperties` fails with "ref loops are
|
|
23
|
+
* only supported if they include optional or nullable property values, or
|
|
24
|
+
* a potentially-zero-length array items" (reproduced 2026-10-30 after an
|
|
25
|
+
* interval in August 2026 when it was accepted), so the empty schema stays.
|
|
26
|
+
* Ordinary recursive models recurse through optional fields or arrays,
|
|
27
|
+
* which Gemini accepts.
|
|
28
|
+
*/
|
|
29
|
+
export const GOOGLE_STRICT = Object.freeze({
|
|
30
|
+
id: 'google-strict',
|
|
31
|
+
family: 'google',
|
|
32
|
+
strict: true,
|
|
33
|
+
objectAllFieldsRequired: false,
|
|
34
|
+
objectClosedByDefault: false,
|
|
35
|
+
recordEncoding: 'open-record',
|
|
36
|
+
tupleEncoding: 'prefix-items',
|
|
37
|
+
allowAllOf: false,
|
|
38
|
+
allowAnyOf: true,
|
|
39
|
+
allowOneOf: false,
|
|
40
|
+
allowRootRef: true,
|
|
41
|
+
allowDefsRef: true,
|
|
42
|
+
emitPropertyOrdering: true,
|
|
43
|
+
supportedStringFormats: new Set(['date-time', 'date', 'time']),
|
|
44
|
+
allowPattern: false,
|
|
45
|
+
allowMultiplePatterns: false,
|
|
46
|
+
allowMinMaxLength: false,
|
|
47
|
+
allowMinMaxItems: true,
|
|
48
|
+
allowMinimumMaximum: true,
|
|
49
|
+
optionalAsNullable: false,
|
|
50
|
+
// Not a recursive $defs/Any: Gemini rejects ref loops through required
|
|
51
|
+
// values — see the descriptor doc above.
|
|
52
|
+
anyEncoding: 'unconstrained',
|
|
53
|
+
// No documented per-request slot limits. `maxEnumValues: 40` IS a measured
|
|
54
|
+
// response to a real `400 INVALID_ARGUMENT` on this exact product's live
|
|
55
|
+
// fn-catalog enum — see `FormatDescriptor.maxEnumValues` for the numbers,
|
|
56
|
+
// the bisection, and why 40 is conservative rather than the measured edge.
|
|
57
|
+
maxEnumValues: 40,
|
|
58
|
+
supportsRecursion: true,
|
|
59
|
+
// When a schema can't be expressed here (e.g. it needs `allOf`) it's
|
|
60
|
+
// delivered as prompt text instead; this hint keeps the reply a single raw
|
|
61
|
+
// JSON object.
|
|
62
|
+
jsonFallbackInstruction: 'Return ONLY a single raw JSON object that conforms to the schema above. Do NOT wrap it in markdown code fences, do NOT add any prose before or after it, and do NOT echo the schema itself — emit only the JSON instance.',
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Google Gemini non-strict — LENIENT's permissive rules EXCEPT for the "any"
|
|
66
|
+
* encoding.
|
|
67
|
+
*
|
|
68
|
+
* Gemini rejects a self-referencing `$defs/Any` when it has to build a
|
|
69
|
+
* decoding grammar, and it builds one whenever a tool call is forced —
|
|
70
|
+
* independent of any per-tool `strict` flag, which Google's function-calling
|
|
71
|
+
* API doesn't even have. So the `unconstrained` encoding is a property of the
|
|
72
|
+
* DIALECT, not of strict mode, and belongs on both Google descriptors.
|
|
73
|
+
*
|
|
74
|
+
* Everything else stays LENIENT deliberately: the restrictions on
|
|
75
|
+
* `GOOGLE_STRICT` come from the documented structured-output keyword list,
|
|
76
|
+
* which is a grammar-mode concern, and tightening them here would push
|
|
77
|
+
* ordinary Gemini schemas onto the prompt-text delivery fallback for no
|
|
78
|
+
* measured reason.
|
|
79
|
+
*/
|
|
80
|
+
export const GOOGLE_NON_STRICT = Object.freeze({
|
|
81
|
+
...LENIENT,
|
|
82
|
+
id: 'google-non-strict',
|
|
83
|
+
family: 'google',
|
|
84
|
+
anyEncoding: 'unconstrained',
|
|
85
|
+
// Same measured enum-cardinality limit as GOOGLE_STRICT — it fires whatever
|
|
86
|
+
// the strictness (see FormatDescriptor.maxEnumValues), so a tool degraded
|
|
87
|
+
// to this descriptor still needs it.
|
|
88
|
+
maxEnumValues: 40,
|
|
89
|
+
jsonFallbackInstruction: 'Return ONLY a single raw JSON object that conforms to the schema above. Do NOT wrap it in markdown code fences, do NOT add any prose before or after it, and do NOT echo the schema itself — emit only the JSON instance.',
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=google.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google.js","sourceRoot":"","sources":["../../../src/schema/dialects/google.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,aAAa,GAAqB,MAAM,CAAC,MAAM,CAAC;IAC3D,EAAE,EAAE,eAAe;IACnB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,IAAI;IACZ,uBAAuB,EAAE,KAAK;IAC9B,qBAAqB,EAAE,KAAK;IAC5B,cAAc,EAAE,aAAa;IAC7B,aAAa,EAAE,cAAc;IAC7B,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,IAAI;IAClB,oBAAoB,EAAE,IAAI;IAC1B,sBAAsB,EAAE,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9D,YAAY,EAAE,KAAK;IACnB,qBAAqB,EAAE,KAAK;IAC5B,iBAAiB,EAAE,KAAK;IACxB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;IACzB,kBAAkB,EAAE,KAAK;IACzB,uEAAuE;IACvE,yCAAyC;IACzC,WAAW,EAAE,eAAe;IAC5B,2EAA2E;IAC3E,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,aAAa,EAAE,EAAE;IACjB,iBAAiB,EAAE,IAAI;IACvB,qEAAqE;IACrE,2EAA2E;IAC3E,eAAe;IACf,uBAAuB,EACrB,2NAA2N;CAC9N,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAqB,MAAM,CAAC,MAAM,CAAC;IAC/D,GAAG,OAAO;IACV,EAAE,EAAE,mBAAmB;IACvB,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,eAAe;IAC5B,4EAA4E;IAC5E,0EAA0E;IAC1E,qCAAqC;IACrC,aAAa,EAAE,EAAE;IACjB,uBAAuB,EACrB,2NAA2N;CAC9N,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenient.d.ts","sourceRoot":"","sources":["../../../src/schema/dialects/lenient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,0GAA0G;AAC1G,eAAO,MAAM,OAAO,EAAE,gBAuBpB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Lenient descriptor — no rewrites, no closure, formats pass through. Default for unsupported models. */
|
|
2
|
+
export const LENIENT = Object.freeze({
|
|
3
|
+
id: 'lenient',
|
|
4
|
+
family: 'lenient',
|
|
5
|
+
strict: false,
|
|
6
|
+
objectAllFieldsRequired: false,
|
|
7
|
+
objectClosedByDefault: false,
|
|
8
|
+
recordEncoding: 'open-record',
|
|
9
|
+
tupleEncoding: 'prefix-items',
|
|
10
|
+
allowAllOf: true,
|
|
11
|
+
allowAnyOf: true,
|
|
12
|
+
allowOneOf: true,
|
|
13
|
+
allowRootRef: true,
|
|
14
|
+
allowDefsRef: true,
|
|
15
|
+
emitPropertyOrdering: false,
|
|
16
|
+
supportedStringFormats: 'all',
|
|
17
|
+
allowPattern: true,
|
|
18
|
+
allowMultiplePatterns: true,
|
|
19
|
+
allowMinMaxLength: true,
|
|
20
|
+
allowMinMaxItems: true,
|
|
21
|
+
allowMinimumMaximum: true,
|
|
22
|
+
optionalAsNullable: false,
|
|
23
|
+
anyEncoding: 'recursive-open',
|
|
24
|
+
supportsRecursion: true,
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=lenient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenient.js","sourceRoot":"","sources":["../../../src/schema/dialects/lenient.ts"],"names":[],"mappings":"AAEA,0GAA0G;AAC1G,MAAM,CAAC,MAAM,OAAO,GAAqB,MAAM,CAAC,MAAM,CAAC;IACrD,EAAE,EAAE,SAAS;IACb,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,KAAK;IACb,uBAAuB,EAAE,KAAK;IAC9B,qBAAqB,EAAE,KAAK;IAC5B,cAAc,EAAE,aAAa;IAC7B,aAAa,EAAE,cAAc;IAC7B,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,IAAI;IAChB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,IAAI;IAClB,oBAAoB,EAAE,KAAK;IAC3B,sBAAsB,EAAE,KAAK;IAC7B,YAAY,EAAE,IAAI;IAClB,qBAAqB,EAAE,IAAI;IAC3B,iBAAiB,EAAE,IAAI;IACvB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;IACzB,kBAAkB,EAAE,KAAK;IACzB,WAAW,EAAE,gBAAgB;IAC7B,iBAAiB,EAAE,IAAI;CACxB,CAAC,CAAC"}
|