@aeye/core 0.5.4 → 0.7.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 +790 -724
- package/aeye-core-prompts.md +179 -178
- package/aeye-core-schema.md +43 -16
- package/aeye-core-tools.md +179 -177
- package/aeye-core-types.md +1 -0
- 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 +134 -15
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +220 -164
- 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/check-bag.d.ts +28 -0
- package/dist/schema/check-bag.d.ts.map +1 -0
- package/dist/schema/check-bag.js +129 -0
- package/dist/schema/check-bag.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 +692 -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 +38 -4
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +52 -8
- 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 +2 -2
- package/dist/schema.d.ts +0 -840
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -2478
- package/dist/schema.js.map +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when the descriptor's "any" encoding is a self-referencing definition,
|
|
3
|
+
* i.e. it has to be hoisted into `$defs/Any` and referenced by `$ref`. The
|
|
4
|
+
* non-recursive encodings (`flat`, `unconstrained`) are inlined at each use
|
|
5
|
+
* site instead, so no `$defs` entry is ever created for them.
|
|
6
|
+
*/
|
|
7
|
+
export function usesSharedAnyDefinition(descriptor) {
|
|
8
|
+
return descriptor.anyEncoding === 'recursive-strict'
|
|
9
|
+
|| descriptor.anyEncoding === 'recursive-open';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds the body of the "any value" schema — for most dialects an `anyOf`
|
|
13
|
+
* covering every JSON value (string, number, boolean, null, array, object).
|
|
14
|
+
*
|
|
15
|
+
* Encoding modes:
|
|
16
|
+
* - `recursive-strict` (OpenAI strict): self-referencing `$defs/Any` with
|
|
17
|
+
* array-of-pairs records (no open-object support in strict mode).
|
|
18
|
+
* - `recursive-open` (Lenient): self-referencing `$defs/Any` with
|
|
19
|
+
* `additionalProperties: <self>` records.
|
|
20
|
+
* - `flat` (xAI strict): non-recursive — array branch has no `items`
|
|
21
|
+
* constraint, object branch is `additionalProperties: true`. Equivalent
|
|
22
|
+
* to TypeScript `any`; safe under descriptors that reject cyclic `$defs`.
|
|
23
|
+
* - `unconstrained` (Google): the empty schema `{}`. A schema with no
|
|
24
|
+
* assertion keywords validates every instance, so this says "any JSON
|
|
25
|
+
* value" using zero keywords — the only option for a dialect that forbids
|
|
26
|
+
* `anyOf` AND named `$defs`/`$ref`. The caller re-attaches `description`
|
|
27
|
+
* from the source schema's metadata, so the model still gets the prose.
|
|
28
|
+
* - `none` (Anthropic strict): no spelling exists. `expressible` keeps such a
|
|
29
|
+
* schema off a strict request, so this is only reached by a direct
|
|
30
|
+
* `toJSONSchema` call, which gets the empty schema.
|
|
31
|
+
*/
|
|
32
|
+
export function buildAnyValueSchema(descriptor) {
|
|
33
|
+
if (descriptor.anyEncoding === 'unconstrained' || descriptor.anyEncoding === 'none') {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
const isFlat = descriptor.anyEncoding === 'flat';
|
|
37
|
+
const selfRef = isFlat ? {} : { $ref: '#/$defs/Any' };
|
|
38
|
+
const arrayBranch = isFlat
|
|
39
|
+
? { type: 'array' }
|
|
40
|
+
: { type: 'array', items: selfRef };
|
|
41
|
+
const branches = [
|
|
42
|
+
{ type: 'string' },
|
|
43
|
+
{ type: 'number' },
|
|
44
|
+
{ type: 'boolean' },
|
|
45
|
+
{ type: 'null' },
|
|
46
|
+
arrayBranch,
|
|
47
|
+
];
|
|
48
|
+
if (descriptor.anyEncoding === 'recursive-strict') {
|
|
49
|
+
branches.push({
|
|
50
|
+
type: 'array',
|
|
51
|
+
items: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
key: { type: 'string' },
|
|
55
|
+
value: selfRef,
|
|
56
|
+
},
|
|
57
|
+
required: ['key', 'value'],
|
|
58
|
+
additionalProperties: false,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (descriptor.anyEncoding === 'recursive-open') {
|
|
63
|
+
branches.push({
|
|
64
|
+
type: 'object',
|
|
65
|
+
additionalProperties: selfRef,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// flat
|
|
70
|
+
branches.push({
|
|
71
|
+
type: 'object',
|
|
72
|
+
additionalProperties: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return { anyOf: branches };
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=any-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"any-value.js","sourceRoot":"","sources":["../../src/schema/any-value.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAA4B;IAClE,OAAO,UAAU,CAAC,WAAW,KAAK,kBAAkB;WAC/C,UAAU,CAAC,WAAW,KAAK,gBAAgB,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAA4B;IAC9D,IAAI,UAAU,CAAC,WAAW,KAAK,eAAe,IAAI,UAAU,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;QACpF,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,KAAK,MAAM,CAAC;IACjD,MAAM,OAAO,GAAe,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAClE,MAAM,WAAW,GAAe,MAAM;QACpC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;QACnB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAiB;QAC7B,EAAE,IAAI,EAAE,QAAQ,EAAE;QAClB,EAAE,IAAI,EAAE,QAAQ,EAAE;QAClB,EAAE,IAAI,EAAE,SAAS,EAAE;QACnB,EAAE,IAAI,EAAE,MAAM,EAAE;QAChB,WAAW;KACZ,CAAC;IACF,IAAI,UAAU,CAAC,WAAW,KAAK,kBAAkB,EAAE,CAAC;QAClD,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,OAAO;YACb,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACvB,KAAK,EAAE,OAAO;iBACf;gBACD,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;gBAC1B,oBAAoB,EAAE,KAAK;aAC5B;SACF,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,UAAU,CAAC,WAAW,KAAK,gBAAgB,EAAE,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ;YACd,oBAAoB,EAAE,OAAO;SAC9B,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO;QACP,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,QAAQ;YACd,oBAAoB,EAAE,IAAI;SAC3B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import type { FormatDescriptor } from './descriptor';
|
|
3
|
+
/**
|
|
4
|
+
* Per-request strict-mode allocator.
|
|
5
|
+
*
|
|
6
|
+
* Constructed once per outgoing request with the chosen model's tool and
|
|
7
|
+
* structured-output descriptors (the same one when only one is given; they
|
|
8
|
+
* differ when the model has strict tools but not strict output, or the
|
|
9
|
+
* reverse). The slot limits come from the stricter of the two. Tracks remaining slots (strict tools, optional parameters,
|
|
10
|
+
* union types) against the descriptor's documented per-request limits and
|
|
11
|
+
* decides per-tool / per-output whether to emit strict or fall back to the
|
|
12
|
+
* SAME FAMILY's non-strict descriptor (`degraded` — never the family-blind
|
|
13
|
+
* `LENIENT`, which would undo dialect rules that outlive strict mode).
|
|
14
|
+
*
|
|
15
|
+
* Selection guarantees that any tool with `strict: true` (hard requirement)
|
|
16
|
+
* has a model that supports the family — `allocate` therefore always grants
|
|
17
|
+
* strict for `requested === true` (subject only to feasibility, not budget;
|
|
18
|
+
* if `true` items overflow the budget the API call will fail, which is the
|
|
19
|
+
* correct behavior for hard requirements).
|
|
20
|
+
*
|
|
21
|
+
* Numeric `strict: <priority>` items are allocated greedily after `true`
|
|
22
|
+
* items have consumed their share of the budget. Caller is expected to
|
|
23
|
+
* pre-sort by descending priority before iterating.
|
|
24
|
+
*/
|
|
25
|
+
export declare class SchemaBudget {
|
|
26
|
+
private readonly tool;
|
|
27
|
+
private readonly output;
|
|
28
|
+
/**
|
|
29
|
+
* What an item that does NOT get strict is emitted through: the descriptor's
|
|
30
|
+
* own family, non-strict.
|
|
31
|
+
*
|
|
32
|
+
* It is deliberately NOT the family-blind `LENIENT`. A dialect restriction
|
|
33
|
+
* can belong to the DIALECT rather than to strict mode — `GOOGLE_NON_STRICT`
|
|
34
|
+
* exists for exactly one such rule, the `unconstrained` encoding of
|
|
35
|
+
* `z.any()`, because Gemini builds a decoding grammar whenever a tool call is
|
|
36
|
+
* forced, with no per-tool strict flag involved. Degrading to `LENIENT` put
|
|
37
|
+
* the self-referencing `$defs/Any` that encoding was created to avoid back on
|
|
38
|
+
* the Google wire — and it did so for every gin/query-style recursive `anyOf`
|
|
39
|
+
* schema, i.e. the schemas that ALWAYS degrade, since `GOOGLE_STRICT` cannot
|
|
40
|
+
* express `anyOf` at all (measured: the product's real `api_set` tool schema
|
|
41
|
+
* allocates to `lenient` under `GOOGLE_STRICT`, and its emitted JSON carries
|
|
42
|
+
* nine `#/$defs/Any` references).
|
|
43
|
+
*
|
|
44
|
+
* For the other families this is behaviour-neutral by construction:
|
|
45
|
+
* `OPENAI_NON_STRICT` and `ANTHROPIC_NON_STRICT` are `LENIENT` with a family
|
|
46
|
+
* tag, and a family with no registered non-strict variant resolves to
|
|
47
|
+
* `LENIENT` (`getDescriptor`). Only the pinned `descriptor` id on the tool
|
|
48
|
+
* changes, which round-trips through `getDescriptorById`.
|
|
49
|
+
*/
|
|
50
|
+
private readonly degradedTool;
|
|
51
|
+
private readonly degradedOutput;
|
|
52
|
+
private remainingTools;
|
|
53
|
+
private remainingOptionalParams;
|
|
54
|
+
private remainingUnionTypes;
|
|
55
|
+
constructor(tool: FormatDescriptor, output?: FormatDescriptor);
|
|
56
|
+
/**
|
|
57
|
+
* Decide the effective descriptor for a tool's parameter schema given the
|
|
58
|
+
* dev's strict request, the schema's features, and the remaining budget.
|
|
59
|
+
*
|
|
60
|
+
* Returns the family's NON-STRICT descriptor (`LENIENT` for a family that
|
|
61
|
+
* registers none) when:
|
|
62
|
+
* - `requested === false`
|
|
63
|
+
* - the schema uses a feature the descriptor can't represent
|
|
64
|
+
* - the schema exceeds the dialect's per-schema size limits
|
|
65
|
+
* (`checkSchemaSizeLimits`)
|
|
66
|
+
* - the descriptor has slot limits and the budget is exhausted (only
|
|
67
|
+
* applies to `requested` being a positive number; `true` always wins
|
|
68
|
+
* subject to feasibility and size)
|
|
69
|
+
*
|
|
70
|
+
* Returns the strict descriptor (and decrements the budget) otherwise.
|
|
71
|
+
*/
|
|
72
|
+
allocateTool(schema: z.ZodType | z.core.$ZodType, requested: boolean | number | undefined): FormatDescriptor;
|
|
73
|
+
/**
|
|
74
|
+
* Same as `allocateTool` but for the request's structured-output schema.
|
|
75
|
+
* The output schema doesn't consume a tool slot but it does count toward
|
|
76
|
+
* the optional-parameter and union-type budgets — descriptors document
|
|
77
|
+
* those limits as "across all strict schemas in one request".
|
|
78
|
+
*/
|
|
79
|
+
allocateOutput(schema: z.ZodType | z.core.$ZodType, requested: boolean | number | undefined): FormatDescriptor;
|
|
80
|
+
/** Snapshot of remaining budget. Useful for telemetry / debug logs. */
|
|
81
|
+
remaining(): {
|
|
82
|
+
strictTools: number;
|
|
83
|
+
optionalParams: number;
|
|
84
|
+
unionTypes: number;
|
|
85
|
+
};
|
|
86
|
+
private allocate;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Compare two descriptors and return the one with the tighter per-request
|
|
90
|
+
* budget. Used by providers to pick a single descriptor for a SchemaBudget
|
|
91
|
+
* shared between `convertTools` and `convertResponseFormat` — Anthropic's
|
|
92
|
+
* documented limits apply across the whole request, so the strictest
|
|
93
|
+
* descriptor wins.
|
|
94
|
+
*
|
|
95
|
+
* `schemaSizeLimits` deliberately does NOT participate. Those bounds are
|
|
96
|
+
* per-schema, so they are evaluated against whichever descriptor this returns
|
|
97
|
+
* rather than shared between the two — and every provider here passes two
|
|
98
|
+
* descriptors resolved from the SAME family, so there is nothing to break the
|
|
99
|
+
* tie on. Ranking six size bounds against three slot budgets would be an
|
|
100
|
+
* invented ordering, not a documented one.
|
|
101
|
+
*/
|
|
102
|
+
export declare function strictestOf(a: FormatDescriptor, b: FormatDescriptor): FormatDescriptor;
|
|
103
|
+
/**
|
|
104
|
+
* Convert a `boolean | number | undefined` strict request into a numeric
|
|
105
|
+
* priority for sorting. Used by providers to allocate strict slots in
|
|
106
|
+
* descending priority order.
|
|
107
|
+
*
|
|
108
|
+
* - `true` → `+Infinity` (hard requirement; always first in line)
|
|
109
|
+
* - `false` → `-Infinity` (always lenient; never wants strict)
|
|
110
|
+
* - `number > 0` → the number itself
|
|
111
|
+
* - `number <= 0` or `undefined` → `0` (no preference)
|
|
112
|
+
*/
|
|
113
|
+
export declare function strictPriority(requested: boolean | number | undefined): number;
|
|
114
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/schema/budget.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmB;IAClD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,mBAAmB,CAAS;gBAExB,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAE,gBAAuB;IAanE;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CACV,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EACnC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GACtC,gBAAgB;IAInB;;;;;OAKG;IACH,cAAc,CACZ,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EACnC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GACtC,gBAAgB;IAInB,uEAAuE;IACvE,SAAS,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAQhF,OAAO,CAAC,QAAQ;CAkEjB;AAYD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAWtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAK9E"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { getDescriptor } from './registry';
|
|
2
|
+
import { fitsStrictAlone, sendableUnder } from './expressible';
|
|
3
|
+
import { analyzeSchema } from './analyze';
|
|
4
|
+
/**
|
|
5
|
+
* Per-request strict-mode allocator.
|
|
6
|
+
*
|
|
7
|
+
* Constructed once per outgoing request with the chosen model's tool and
|
|
8
|
+
* structured-output descriptors (the same one when only one is given; they
|
|
9
|
+
* differ when the model has strict tools but not strict output, or the
|
|
10
|
+
* reverse). The slot limits come from the stricter of the two. Tracks remaining slots (strict tools, optional parameters,
|
|
11
|
+
* union types) against the descriptor's documented per-request limits and
|
|
12
|
+
* decides per-tool / per-output whether to emit strict or fall back to the
|
|
13
|
+
* SAME FAMILY's non-strict descriptor (`degraded` — never the family-blind
|
|
14
|
+
* `LENIENT`, which would undo dialect rules that outlive strict mode).
|
|
15
|
+
*
|
|
16
|
+
* Selection guarantees that any tool with `strict: true` (hard requirement)
|
|
17
|
+
* has a model that supports the family — `allocate` therefore always grants
|
|
18
|
+
* strict for `requested === true` (subject only to feasibility, not budget;
|
|
19
|
+
* if `true` items overflow the budget the API call will fail, which is the
|
|
20
|
+
* correct behavior for hard requirements).
|
|
21
|
+
*
|
|
22
|
+
* Numeric `strict: <priority>` items are allocated greedily after `true`
|
|
23
|
+
* items have consumed their share of the budget. Caller is expected to
|
|
24
|
+
* pre-sort by descending priority before iterating.
|
|
25
|
+
*/
|
|
26
|
+
export class SchemaBudget {
|
|
27
|
+
tool;
|
|
28
|
+
output;
|
|
29
|
+
/**
|
|
30
|
+
* What an item that does NOT get strict is emitted through: the descriptor's
|
|
31
|
+
* own family, non-strict.
|
|
32
|
+
*
|
|
33
|
+
* It is deliberately NOT the family-blind `LENIENT`. A dialect restriction
|
|
34
|
+
* can belong to the DIALECT rather than to strict mode — `GOOGLE_NON_STRICT`
|
|
35
|
+
* exists for exactly one such rule, the `unconstrained` encoding of
|
|
36
|
+
* `z.any()`, because Gemini builds a decoding grammar whenever a tool call is
|
|
37
|
+
* forced, with no per-tool strict flag involved. Degrading to `LENIENT` put
|
|
38
|
+
* the self-referencing `$defs/Any` that encoding was created to avoid back on
|
|
39
|
+
* the Google wire — and it did so for every gin/query-style recursive `anyOf`
|
|
40
|
+
* schema, i.e. the schemas that ALWAYS degrade, since `GOOGLE_STRICT` cannot
|
|
41
|
+
* express `anyOf` at all (measured: the product's real `api_set` tool schema
|
|
42
|
+
* allocates to `lenient` under `GOOGLE_STRICT`, and its emitted JSON carries
|
|
43
|
+
* nine `#/$defs/Any` references).
|
|
44
|
+
*
|
|
45
|
+
* For the other families this is behaviour-neutral by construction:
|
|
46
|
+
* `OPENAI_NON_STRICT` and `ANTHROPIC_NON_STRICT` are `LENIENT` with a family
|
|
47
|
+
* tag, and a family with no registered non-strict variant resolves to
|
|
48
|
+
* `LENIENT` (`getDescriptor`). Only the pinned `descriptor` id on the tool
|
|
49
|
+
* changes, which round-trips through `getDescriptorById`.
|
|
50
|
+
*/
|
|
51
|
+
degradedTool;
|
|
52
|
+
degradedOutput;
|
|
53
|
+
remainingTools;
|
|
54
|
+
remainingOptionalParams;
|
|
55
|
+
remainingUnionTypes;
|
|
56
|
+
constructor(tool, output = tool) {
|
|
57
|
+
this.tool = tool;
|
|
58
|
+
this.output = output;
|
|
59
|
+
this.degradedTool = degrade(tool);
|
|
60
|
+
// A family with no non-strict structured output keeps the strict dialect
|
|
61
|
+
// (`canExpress` already kept an unfitting schema off the wire)
|
|
62
|
+
this.degradedOutput = output.structuredOutputAlwaysStrict ? output : degrade(output);
|
|
63
|
+
const descriptor = strictestOf(tool, output);
|
|
64
|
+
this.remainingTools = descriptor.maxStrictTools ?? Infinity;
|
|
65
|
+
this.remainingOptionalParams = descriptor.maxStrictOptionalParams ?? Infinity;
|
|
66
|
+
this.remainingUnionTypes = descriptor.maxStrictUnionTypes ?? Infinity;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Decide the effective descriptor for a tool's parameter schema given the
|
|
70
|
+
* dev's strict request, the schema's features, and the remaining budget.
|
|
71
|
+
*
|
|
72
|
+
* Returns the family's NON-STRICT descriptor (`LENIENT` for a family that
|
|
73
|
+
* registers none) when:
|
|
74
|
+
* - `requested === false`
|
|
75
|
+
* - the schema uses a feature the descriptor can't represent
|
|
76
|
+
* - the schema exceeds the dialect's per-schema size limits
|
|
77
|
+
* (`checkSchemaSizeLimits`)
|
|
78
|
+
* - the descriptor has slot limits and the budget is exhausted (only
|
|
79
|
+
* applies to `requested` being a positive number; `true` always wins
|
|
80
|
+
* subject to feasibility and size)
|
|
81
|
+
*
|
|
82
|
+
* Returns the strict descriptor (and decrements the budget) otherwise.
|
|
83
|
+
*/
|
|
84
|
+
allocateTool(schema, requested) {
|
|
85
|
+
return this.allocate(schema, requested, /* isTool */ true);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Same as `allocateTool` but for the request's structured-output schema.
|
|
89
|
+
* The output schema doesn't consume a tool slot but it does count toward
|
|
90
|
+
* the optional-parameter and union-type budgets — descriptors document
|
|
91
|
+
* those limits as "across all strict schemas in one request".
|
|
92
|
+
*/
|
|
93
|
+
allocateOutput(schema, requested) {
|
|
94
|
+
return this.allocate(schema, requested, /* isTool */ false);
|
|
95
|
+
}
|
|
96
|
+
/** Snapshot of remaining budget. Useful for telemetry / debug logs. */
|
|
97
|
+
remaining() {
|
|
98
|
+
return {
|
|
99
|
+
strictTools: this.remainingTools,
|
|
100
|
+
optionalParams: this.remainingOptionalParams,
|
|
101
|
+
unionTypes: this.remainingUnionTypes,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
allocate(schema, requested, isTool) {
|
|
105
|
+
const descriptor = isTool ? this.tool : this.output;
|
|
106
|
+
const degraded = isTool ? this.degradedTool : this.degradedOutput;
|
|
107
|
+
// strict: false → always non-strict, no budget movement.
|
|
108
|
+
if (requested === false)
|
|
109
|
+
return degraded;
|
|
110
|
+
// Descriptor itself is lenient → nothing to allocate.
|
|
111
|
+
if (!descriptor.strict)
|
|
112
|
+
return degraded;
|
|
113
|
+
const isHardRequired = requested === true;
|
|
114
|
+
const isPreferredNumeric = typeof requested === 'number' && requested > 0;
|
|
115
|
+
if (!isHardRequired && !isPreferredNumeric) {
|
|
116
|
+
// requested is undefined or 0 — treat as no preference; default to
|
|
117
|
+
// non-strict so callers explicitly opt into strict. Decided BEFORE any
|
|
118
|
+
// walk of `schema`: analysing a schema whose verdict is already
|
|
119
|
+
// non-strict is pure waste on the hot path of every tool of every request.
|
|
120
|
+
return degraded;
|
|
121
|
+
}
|
|
122
|
+
// FEASIBILITY, before budget — the second clause of this method's contract,
|
|
123
|
+
// and the one that was documented but never implemented. Emitting a strict
|
|
124
|
+
// schema carrying a combinator the descriptor forbids is a guaranteed
|
|
125
|
+
// provider 400, not a degradation: `GOOGLE_STRICT` forbids `anyOf`, and the
|
|
126
|
+
// recursive union a gin/query codegen layer produces is nothing but.
|
|
127
|
+
// Degrading to the family's non-strict descriptor is the documented answer
|
|
128
|
+
// for a TOOL (unlike a structured OUTPUT, a tool schema has no prompt-text
|
|
129
|
+
// delivery to fall back to — `applySchemaDeliveryFallback` covers
|
|
130
|
+
// `responseFormat` only), and it is also what keeps the API-level
|
|
131
|
+
// `strict: true` flag off the wire, since providers set it from
|
|
132
|
+
// `descriptor.strict`.
|
|
133
|
+
//
|
|
134
|
+
// `sendableUnder`, not `canExpress`: a cycle is always sendable (the
|
|
135
|
+
// emitter's bounded placeholder), only looser — see that function's note on
|
|
136
|
+
// why the two questions are different.
|
|
137
|
+
if (!sendableUnder(schema, descriptor))
|
|
138
|
+
return degraded;
|
|
139
|
+
// SIZE, still before budget and still regardless of priority: the per-SCHEMA
|
|
140
|
+
// bounds (see `checkSchemaSizeLimits`), and the per-request budgets when
|
|
141
|
+
// this schema alone exceeds them, cannot be met by anything else in the
|
|
142
|
+
// request, and a hard `strict: true` cannot force a schema the API will
|
|
143
|
+
// refuse to compile.
|
|
144
|
+
if (!fitsStrictAlone(schema, descriptor))
|
|
145
|
+
return degraded;
|
|
146
|
+
const features = analyzeSchema(schema);
|
|
147
|
+
// Soft-priority items must fit the budget. Hard-required items skip
|
|
148
|
+
// budget checks because selection already promised the model can take
|
|
149
|
+
// them — if there are too many, that's a request-construction bug we
|
|
150
|
+
// surface via API error rather than degrading silently.
|
|
151
|
+
if (isPreferredNumeric) {
|
|
152
|
+
if (isTool && this.remainingTools <= 0)
|
|
153
|
+
return degraded;
|
|
154
|
+
if (this.remainingOptionalParams - features.optionalParameterCount < 0)
|
|
155
|
+
return degraded;
|
|
156
|
+
if (this.remainingUnionTypes - features.unionTypeCount < 0)
|
|
157
|
+
return degraded;
|
|
158
|
+
}
|
|
159
|
+
// Allocation succeeded — decrement.
|
|
160
|
+
if (isTool)
|
|
161
|
+
this.remainingTools -= 1;
|
|
162
|
+
this.remainingOptionalParams -= features.optionalParameterCount;
|
|
163
|
+
this.remainingUnionTypes -= features.unionTypeCount;
|
|
164
|
+
return descriptor;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* What an item that does not get strict is emitted through (see
|
|
169
|
+
* `SchemaBudget.degradedTool`). An already-non-strict descriptor degrades to
|
|
170
|
+
* itself: there is nothing looser in its family, and `LENIENT`'s own family
|
|
171
|
+
* has no other variant.
|
|
172
|
+
*/
|
|
173
|
+
function degrade(descriptor) {
|
|
174
|
+
return descriptor.strict ? getDescriptor(descriptor.family, false) : descriptor;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Compare two descriptors and return the one with the tighter per-request
|
|
178
|
+
* budget. Used by providers to pick a single descriptor for a SchemaBudget
|
|
179
|
+
* shared between `convertTools` and `convertResponseFormat` — Anthropic's
|
|
180
|
+
* documented limits apply across the whole request, so the strictest
|
|
181
|
+
* descriptor wins.
|
|
182
|
+
*
|
|
183
|
+
* `schemaSizeLimits` deliberately does NOT participate. Those bounds are
|
|
184
|
+
* per-schema, so they are evaluated against whichever descriptor this returns
|
|
185
|
+
* rather than shared between the two — and every provider here passes two
|
|
186
|
+
* descriptors resolved from the SAME family, so there is nothing to break the
|
|
187
|
+
* tie on. Ranking six size bounds against three slot budgets would be an
|
|
188
|
+
* invented ordering, not a documented one.
|
|
189
|
+
*/
|
|
190
|
+
export function strictestOf(a, b) {
|
|
191
|
+
// LENIENT is the loosest — pick the strict side if either is strict.
|
|
192
|
+
if (a.strict && !b.strict)
|
|
193
|
+
return a;
|
|
194
|
+
if (b.strict && !a.strict)
|
|
195
|
+
return b;
|
|
196
|
+
if (!a.strict && !b.strict)
|
|
197
|
+
return a;
|
|
198
|
+
// Both strict — pick the one with the smallest documented limits.
|
|
199
|
+
const score = (d) => (d.maxStrictTools ?? Infinity) +
|
|
200
|
+
(d.maxStrictOptionalParams ?? Infinity) +
|
|
201
|
+
(d.maxStrictUnionTypes ?? Infinity);
|
|
202
|
+
return score(a) <= score(b) ? a : b;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Convert a `boolean | number | undefined` strict request into a numeric
|
|
206
|
+
* priority for sorting. Used by providers to allocate strict slots in
|
|
207
|
+
* descending priority order.
|
|
208
|
+
*
|
|
209
|
+
* - `true` → `+Infinity` (hard requirement; always first in line)
|
|
210
|
+
* - `false` → `-Infinity` (always lenient; never wants strict)
|
|
211
|
+
* - `number > 0` → the number itself
|
|
212
|
+
* - `number <= 0` or `undefined` → `0` (no preference)
|
|
213
|
+
*/
|
|
214
|
+
export function strictPriority(requested) {
|
|
215
|
+
if (requested === true)
|
|
216
|
+
return Infinity;
|
|
217
|
+
if (requested === false)
|
|
218
|
+
return -Infinity;
|
|
219
|
+
if (typeof requested === 'number')
|
|
220
|
+
return requested > 0 ? requested : 0;
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/schema/budget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The constraints a schema's checks add up to — the fields the JSON Schema
|
|
4
|
+
* converter reads for strings, numbers, arrays and tuples.
|
|
5
|
+
*
|
|
6
|
+
* zod 4.1 kept these on `schema._zod.bag`, filled by each check as it was
|
|
7
|
+
* attached. From zod 4.6 the bag stays empty for first-party checks and zod's
|
|
8
|
+
* own converter folds `def.checks` instead (`aggregateChecks`, which zod does
|
|
9
|
+
* not export). {@link checkBag} is that fold, so the converter reads the same
|
|
10
|
+
* facts on either version.
|
|
11
|
+
*/
|
|
12
|
+
export interface CheckBag {
|
|
13
|
+
minimum?: number;
|
|
14
|
+
maximum?: number;
|
|
15
|
+
exclusiveMinimum?: number;
|
|
16
|
+
exclusiveMaximum?: number;
|
|
17
|
+
multipleOf?: number;
|
|
18
|
+
format?: string;
|
|
19
|
+
patterns?: Set<RegExp>;
|
|
20
|
+
contentEncoding?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A schema's constraints, folded from its checks. A format schema (`z.email()`,
|
|
24
|
+
* `z.int()`) is its own first check, as zod treats it; anything zod still puts
|
|
25
|
+
* on `_zod.bag` (third-party `onattach` hooks) is merged in last.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkBag(schema: z.core.$ZodType): CheckBag;
|
|
28
|
+
//# sourceMappingURL=check-bag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-bag.d.ts","sourceRoot":"","sources":["../../src/schema/check-bag.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAsGD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAkB1D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
/** The value a numeric check carries, as a plain number (bigint bounds are converted). */
|
|
3
|
+
function numeric(value) {
|
|
4
|
+
return typeof value === 'number' ? value : Number(value);
|
|
5
|
+
}
|
|
6
|
+
function narrowMin(bag, key, value) {
|
|
7
|
+
const current = bag[key];
|
|
8
|
+
if (current === undefined || value > current)
|
|
9
|
+
bag[key] = value;
|
|
10
|
+
}
|
|
11
|
+
function narrowMax(bag, key, value) {
|
|
12
|
+
const current = bag[key];
|
|
13
|
+
if (current === undefined || value < current)
|
|
14
|
+
bag[key] = value;
|
|
15
|
+
}
|
|
16
|
+
function addPattern(bag, pattern) {
|
|
17
|
+
bag.patterns ??= new Set();
|
|
18
|
+
bag.patterns.add(pattern);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Every first-party check kind. Typed against zod's own union, so a kind zod
|
|
22
|
+
* adds or renames fails to compile here rather than being skipped silently.
|
|
23
|
+
*/
|
|
24
|
+
const FIRST_PARTY = {
|
|
25
|
+
less_than: true, greater_than: true, multiple_of: true, number_format: true, bigint_format: true,
|
|
26
|
+
max_size: true, min_size: true, size_equals: true, max_length: true, min_length: true,
|
|
27
|
+
length_equals: true, string_format: true, property: true, mime_type: true, overwrite: true,
|
|
28
|
+
};
|
|
29
|
+
function isFirstPartyKind(kind) {
|
|
30
|
+
return typeof kind === 'string' && Object.hasOwn(FIRST_PARTY, kind);
|
|
31
|
+
}
|
|
32
|
+
/** A check zod's first-party union describes, so its `def` narrows by `check`. */
|
|
33
|
+
function isFirstParty(check) {
|
|
34
|
+
return isFirstPartyKind(check._zod.def.check);
|
|
35
|
+
}
|
|
36
|
+
/** A format schema (`z.email()`, `z.int()`) — a schema that is also its own first check. */
|
|
37
|
+
function isFormatSchema(schema) {
|
|
38
|
+
return schema._zod.traits.has('$ZodCheck') && isFirstPartyKind(Reflect.get(schema._zod.def, 'check'));
|
|
39
|
+
}
|
|
40
|
+
/** Folds one check into the bag. Kinds with no JSON Schema meaning are ignored. */
|
|
41
|
+
function applyCheck(bag, check) {
|
|
42
|
+
const def = check._zod.def;
|
|
43
|
+
switch (def.check) {
|
|
44
|
+
case 'greater_than':
|
|
45
|
+
narrowMin(bag, def.inclusive ? 'minimum' : 'exclusiveMinimum', numeric(def.value));
|
|
46
|
+
return;
|
|
47
|
+
case 'less_than':
|
|
48
|
+
narrowMax(bag, def.inclusive ? 'maximum' : 'exclusiveMaximum', numeric(def.value));
|
|
49
|
+
return;
|
|
50
|
+
case 'multiple_of':
|
|
51
|
+
bag.multipleOf ??= numeric(def.value);
|
|
52
|
+
return;
|
|
53
|
+
case 'number_format': {
|
|
54
|
+
bag.format = def.format;
|
|
55
|
+
const [min, max] = z.core.util.NUMBER_FORMAT_RANGES[def.format];
|
|
56
|
+
narrowMin(bag, 'minimum', min);
|
|
57
|
+
narrowMax(bag, 'maximum', max);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
case 'bigint_format': {
|
|
61
|
+
bag.format = def.format;
|
|
62
|
+
const [min, max] = z.core.util.BIGINT_FORMAT_RANGES[def.format];
|
|
63
|
+
narrowMin(bag, 'minimum', Number(min));
|
|
64
|
+
narrowMax(bag, 'maximum', Number(max));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
case 'min_length':
|
|
68
|
+
case 'min_size':
|
|
69
|
+
narrowMin(bag, 'minimum', def.minimum);
|
|
70
|
+
return;
|
|
71
|
+
case 'max_length':
|
|
72
|
+
case 'max_size':
|
|
73
|
+
narrowMax(bag, 'maximum', def.maximum);
|
|
74
|
+
return;
|
|
75
|
+
case 'length_equals':
|
|
76
|
+
narrowMin(bag, 'minimum', def.length);
|
|
77
|
+
narrowMax(bag, 'maximum', def.length);
|
|
78
|
+
return;
|
|
79
|
+
case 'size_equals':
|
|
80
|
+
narrowMin(bag, 'minimum', def.size);
|
|
81
|
+
narrowMax(bag, 'maximum', def.size);
|
|
82
|
+
return;
|
|
83
|
+
case 'string_format':
|
|
84
|
+
bag.format = def.format;
|
|
85
|
+
if (def.pattern)
|
|
86
|
+
addPattern(bag, def.pattern);
|
|
87
|
+
if (def.format === 'base64' || def.format === 'base64url')
|
|
88
|
+
bag.contentEncoding = def.format;
|
|
89
|
+
return;
|
|
90
|
+
default:
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A schema's constraints, folded from its checks. A format schema (`z.email()`,
|
|
96
|
+
* `z.int()`) is its own first check, as zod treats it; anything zod still puts
|
|
97
|
+
* on `_zod.bag` (third-party `onattach` hooks) is merged in last.
|
|
98
|
+
*/
|
|
99
|
+
export function checkBag(schema) {
|
|
100
|
+
const bag = {};
|
|
101
|
+
const internals = schema._zod;
|
|
102
|
+
if (isFormatSchema(schema))
|
|
103
|
+
applyCheck(bag, schema);
|
|
104
|
+
for (const check of internals.def.checks ?? [])
|
|
105
|
+
if (isFirstParty(check))
|
|
106
|
+
applyCheck(bag, check);
|
|
107
|
+
const legacy = internals.bag;
|
|
108
|
+
if (typeof legacy.minimum === 'number')
|
|
109
|
+
narrowMin(bag, 'minimum', legacy.minimum);
|
|
110
|
+
if (typeof legacy.exclusiveMinimum === 'number')
|
|
111
|
+
narrowMin(bag, 'exclusiveMinimum', legacy.exclusiveMinimum);
|
|
112
|
+
if (typeof legacy.maximum === 'number')
|
|
113
|
+
narrowMax(bag, 'maximum', legacy.maximum);
|
|
114
|
+
if (typeof legacy.exclusiveMaximum === 'number')
|
|
115
|
+
narrowMax(bag, 'exclusiveMaximum', legacy.exclusiveMaximum);
|
|
116
|
+
if (typeof legacy.multipleOf === 'number')
|
|
117
|
+
bag.multipleOf ??= legacy.multipleOf;
|
|
118
|
+
if (typeof legacy.format === 'string')
|
|
119
|
+
bag.format ??= legacy.format;
|
|
120
|
+
if (typeof legacy.contentEncoding === 'string')
|
|
121
|
+
bag.contentEncoding ??= legacy.contentEncoding;
|
|
122
|
+
if (legacy.patterns instanceof Set) {
|
|
123
|
+
for (const pattern of legacy.patterns)
|
|
124
|
+
if (pattern instanceof RegExp)
|
|
125
|
+
addPattern(bag, pattern);
|
|
126
|
+
}
|
|
127
|
+
return bag;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=check-bag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-bag.js","sourceRoot":"","sources":["../../src/schema/check-bag.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FormatDescriptor } from './descriptor';
|
|
2
|
+
/**
|
|
3
|
+
* Report the ways a `FormatDescriptor` contradicts itself — settings that ask
|
|
4
|
+
* the emitter to produce a keyword the same descriptor forbids, or a limit
|
|
5
|
+
* that would quietly strip every constraint it governs. Returns one
|
|
6
|
+
* human-readable line per problem; an empty array means the descriptor is
|
|
7
|
+
* internally consistent.
|
|
8
|
+
*
|
|
9
|
+
* This exists because a contradiction here is invisible until a provider
|
|
10
|
+
* answers HTTP 400: `GOOGLE_STRICT` shipped with `allowAnyOf: false` +
|
|
11
|
+
* `allowDefsRef: false` AND `anyEncoding: 'recursive-open'` (which needs
|
|
12
|
+
* both), so every `z.any()` in a Gemini tool schema emitted exactly the
|
|
13
|
+
* `anyOf` + `$defs/Any` shape the descriptor declared unrepresentable.
|
|
14
|
+
* Nothing checked, so nothing caught it.
|
|
15
|
+
*
|
|
16
|
+
* Called by `registerDescriptor` (which warns) and asserted over every
|
|
17
|
+
* built-in by the test suite.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* checkDescriptorConsistency(GOOGLE_STRICT); // → []
|
|
22
|
+
* checkDescriptorConsistency({ ...GOOGLE_STRICT, anyEncoding: 'flat' });
|
|
23
|
+
* // → ["anyEncoding 'flat' emits `anyOf`, but allowAnyOf is false"]
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkDescriptorConsistency(descriptor: FormatDescriptor): string[];
|
|
27
|
+
//# sourceMappingURL=consistency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consistency.d.ts","sourceRoot":"","sources":["../../src/schema/consistency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,cAAc,CAAC;AAsBvE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,EAAE,CA6BjF"}
|