@openclaw/ai 2026.10.1 → 2026.10.5-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/{anthropic-BDfXKgF1.mjs → anthropic-BAHKgWam.mjs} +30 -59
- package/dist/{anthropic-payload-policy-BZUAchqb.d.mts → anthropic-payload-policy-DGblkHL5.d.mts} +19 -16
- package/dist/{anthropic-stream-reducer-DHt9uFWX.mjs → anthropic-stream-reducer-g6w1YVii.mjs} +195 -283
- package/dist/{api-registry-DcEP_o4h.d.mts → api-registry-BhXXYdRA.d.mts} +2 -2
- package/dist/assistant-message-events-CHMJgbhs.mjs +18 -0
- package/dist/{assistant-output-mJ9cL4Qg.mjs → assistant-output-CUEeSotf.mjs} +2 -2
- package/dist/{assistant-text-phase-DAv1BdTm.mjs → assistant-text-phase-BrV8KdFO.mjs} +7 -6
- package/dist/{azure-openai-responses-gn9hMf8x.mjs → azure-openai-responses-C7qBuR23.mjs} +8 -9
- package/dist/{credential-redaction-BKv49aiv.d.mts → credential-redaction-BHxruEH6.d.mts} +3 -1
- package/dist/{diagnostics-Caj-4RBA.mjs → diagnostics-tQasYct1.mjs} +10 -3
- package/dist/diagnostics.d.mts +7 -7
- package/dist/diagnostics.mjs +3 -3
- package/dist/{env-api-keys-CyX4VKGO.mjs → env-api-keys-CrxwdylR.mjs} +24 -35
- package/dist/event-stream-6UNs-AhN.d.mts +1 -0
- package/dist/{event-stream-DrRM_90c.d.mts → event-stream-BcqWLo_u.d.mts} +1 -1
- package/dist/{event-stream-C5AbCAaZ.mjs → event-stream-X2gmsUWv.mjs} +5 -8
- package/dist/event-stream.d.mts +2 -2
- package/dist/event-stream.mjs +1 -1
- package/dist/google-Boz2QcpK.mjs +20 -0
- package/dist/{google-interactions-DXu4IYGb.mjs → google-interactions-tjAb1mc5.mjs} +52 -84
- package/dist/{google-messages-Bmheetdg.mjs → google-messages-C-RZzloH.mjs} +25 -35
- package/dist/google-provider-stream-CMImyxbX.mjs +58 -0
- package/dist/{google-shared-DE_ddfsC.mjs → google-shared-OyAtMTZv.mjs} +6 -15
- package/dist/google-vertex-hCxTJea7.mjs +40 -0
- package/dist/{host-BjIYEjwT.d.mts → host-CTDz7LXD.d.mts} +6 -3
- package/dist/{host-JpNulOOs.mjs → host-JWXZWa-c.mjs} +3 -2
- package/dist/{host-policy-CC5H0iRZ.mjs → host-policy-q4Azym9F.mjs} +1 -1
- package/dist/{index-DJrNQnKw.d.mts → index-BYDvopUB.d.mts} +8 -5
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +7 -6
- package/dist/internal/anthropic.d.mts +5 -5
- package/dist/internal/anthropic.mjs +4 -4
- package/dist/internal/openai-completions-compat.d.mts +1 -1
- package/dist/internal/openai-completions-compat.mjs +1 -1
- package/dist/internal/openai-responses-payload-policy.d.mts +8 -1
- package/dist/internal/openai-responses-payload-policy.mjs +3 -3
- package/dist/internal/openai.d.mts +9 -9
- package/dist/internal/openai.mjs +9 -11
- package/dist/internal/runtime.d.mts +31 -13
- package/dist/internal/runtime.mjs +10 -13
- package/dist/internal/shared.d.mts +5 -5
- package/dist/internal/shared.mjs +6 -7
- package/dist/internal/tool-schema.mjs +2 -2
- package/dist/{llm-request-activity-D8f_oxHj.mjs → llm-request-activity-CfMuh5cC.mjs} +2 -2
- package/dist/{mistral-1Bnbneg1.mjs → mistral-DUveP4hF.mjs} +71 -146
- package/dist/{model-transport-url-C5f59L3Y.d.mts → model-transport-url-BrsjjdjR.d.mts} +5 -1
- package/dist/{number-coercion-V8-BhME4.mjs → number-coercion-C3hztWoY.mjs} +5 -4
- package/dist/{openai-chatgpt-responses-CB1JwNFF.mjs → openai-chatgpt-responses-OCwckGhY.mjs} +164 -234
- package/dist/{openai-completions-BPadHHKe.mjs → openai-completions-DOltBUe9.mjs} +7 -6
- package/dist/{openai-completions-compat-CJ0ktmGy.d.mts → openai-completions-compat-i-nLZxFy.d.mts} +10 -11
- package/dist/{openai-completions-compat-D7hWYB0G.mjs → openai-completions-compat-pg1q4uNG.mjs} +10 -6
- package/dist/{openai-completions-transport-DITBEALX.mjs → openai-completions-transport-OMrn7gnZ.mjs} +1429 -294
- package/dist/{openai-prompt-cache-Dj8qeOHT.mjs → openai-prompt-cache-Cr2xKVrS.mjs} +1 -1
- package/dist/{openai-prompt-cache-C-Q1Ksfs.d.mts → openai-prompt-cache-DIPMrMFy.d.mts} +2 -2
- package/dist/{openai-responses-1_TvcWI3.mjs → openai-responses-BSBJUA19.mjs} +9 -10
- package/dist/{openai-responses-compaction-replay-BB2SWdsx.d.mts → openai-responses-compaction-replay-B2FE4Nw8.d.mts} +4 -4
- package/dist/{openai-responses-contracts-sAvdqNjS.d.mts → openai-responses-contracts-DKqMJ1Ct.d.mts} +9 -4
- package/dist/{openai-responses-contracts-BKu8O7l4.mjs → openai-responses-contracts-DlKMMTxK.mjs} +26 -6
- package/dist/{openai-responses-compaction-window-m878aVpU.mjs → openai-responses-endpoint-CinGh4VZ.mjs} +14 -4
- package/dist/{openai-responses-prompt-observer-internal-CbH84xIS.mjs → openai-responses-request-lifecycle-D2IFiI0-.mjs} +39 -4
- package/dist/{openai-responses-shared-TCJRAoMW.mjs → openai-responses-shared-DVGhq5xF.mjs} +1036 -327
- package/dist/{openai-responses-terminal-usage-BVvyXZ8u.d.mts → openai-responses-terminal-usage-BygjiAdi.d.mts} +2 -2
- package/dist/{openai-tool-schema-DYNePiF8.mjs → openai-tool-schema-DDPXsvJP.mjs} +236 -399
- package/dist/{positive-integer-CwLi8LUL.mjs → positive-integer-CqWjqSi9.mjs} +1 -1
- package/dist/{provider-error-DQBE1bUQ.mjs → provider-error-CF9GWCs2.mjs} +5 -3
- package/dist/{provider-options-Dk4PeQV3.d.mts → provider-options-CVrXZBtA.d.mts} +3 -2
- package/dist/{provider-transport-turn-state-8c_Nj2NS.mjs → provider-transport-turn-state-LuCaTsSO.mjs} +2 -2
- package/dist/{provider-types-De8PRdR0.d.mts → provider-types-CHtRx0dI.d.mts} +3 -3
- package/dist/provider-types.d.mts +6 -6
- package/dist/providers.d.mts +2 -2
- package/dist/providers.mjs +12 -12
- package/dist/{reasoning-tag-text-partitioner-Ju9VpUGl.mjs → reasoning-tag-text-partitioner-0-Rsezog.mjs} +13 -12
- package/dist/{required-api-key-D4kn3252.mjs → required-api-key-CFJvcFy_.mjs} +1 -1
- package/dist/{simple-options-wW3117Fu.mjs → simple-options-BARFAzk9.mjs} +9 -11
- package/dist/{src-DWJq14pn.mjs → src-C_wfIUmV.mjs} +19 -4
- package/dist/{stream-CElngO8h.mjs → stream-DwrETW0_.mjs} +12 -21
- package/dist/{stream-first-event-timeout-D9ZmL_s9.mjs → stream-first-event-timeout-Dc42hKv6.mjs} +8 -21
- package/dist/{streaming-byte-guard-CC-HMn_u.mjs → streaming-byte-guard-DBXr1XYn.mjs} +16 -1
- package/dist/{string-normalization-PN10hf6d.mjs → string-normalization-I3ux50s9.mjs} +1 -1
- package/dist/{text-signature-rOn31nxc.mjs → text-signature-B_MB6U_9.mjs} +32 -5
- package/dist/{tool-schema-json-projection-Ct8SeBeJ.mjs → tool-schema-json-projection-B8sG1I9v.mjs} +29 -23
- package/dist/{transport-stream-shared-CAxkgOSj.d.mts → transport-stream-shared-BmpzDtdQ.d.mts} +4 -4
- package/dist/{transport-stream-shared-C5TAc3U_.mjs → transport-stream-shared-DH08b7Il.mjs} +8 -16
- package/dist/transports.d.mts +33 -22
- package/dist/transports.mjs +73 -1234
- package/dist/{types-DvzmWW8J.d.mts → types-Bd2T86VJ.d.mts} +5 -23
- package/dist/types-Kdfw_04V.d.mts +1 -0
- package/dist/types.d.mts +6 -6
- package/dist/types.mjs +5 -4
- package/dist/{validation-B3lQt_VE.d.mts → validation-DdGB1yy3.d.mts} +1 -1
- package/dist/{validation-DbqlIfDn.mjs → validation-opC2pjk9.mjs} +2 -3
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +4 -9
- package/dist/env-api-keys-BR0MUYYD.d.mts +0 -17
- package/dist/event-stream-DqDQWqiy.d.mts +0 -1
- package/dist/google-CyMz2jF_.mjs +0 -54
- package/dist/google-vertex-BEW0OQIA.mjs +0 -88
- package/dist/internal/env-api-keys.d.mts +0 -2
- package/dist/internal/env-api-keys.mjs +0 -2
- package/dist/openai-responses-request-lifecycle-Fsag-IGZ.mjs +0 -36
- package/dist/openai-transport-params-ildrm49s.mjs +0 -735
- package/dist/openai-web-search-tools-Cfu0uLyN.mjs +0 -14
- package/dist/provider-replay-context-ywPKKLjL.mjs +0 -25
- package/dist/types-Dlz-8qcy.d.mts +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { o as isRecord } from "./record-coerce-CNDovvft.mjs";
|
|
2
2
|
import { t as parseLocalSchemaRefPointer } from "./json-schema-Bp07Qui3.mjs";
|
|
3
3
|
import { n as normalizeLowercaseStringOrEmpty } from "./string-coerce-CtgOVOSv.mjs";
|
|
4
|
-
import { i as uniqueValues, t as normalizeStringEntries } from "./string-normalization-
|
|
5
|
-
import { a as
|
|
4
|
+
import { i as uniqueValues, t as normalizeStringEntries } from "./string-normalization-I3ux50s9.mjs";
|
|
5
|
+
import { a as SCHEMA_NESTED_KEYS, c as walkSchemaArray, i as SCHEMA_MAP_KEYS$1, l as walkSchemaValue, o as SCHEMA_OBJECT_KEYS$1, r as SCHEMA_ARRAY_KEYS$1, s as evaluateSchemaWalk } from "./tool-schema-json-projection-B8sG1I9v.mjs";
|
|
6
6
|
//#region packages/ai/src/providers/clean-for-gemini.ts
|
|
7
7
|
const GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
8
8
|
"~optional",
|
|
@@ -35,18 +35,11 @@ const SCHEMA_META_KEYS = [
|
|
|
35
35
|
];
|
|
36
36
|
function copySchemaMeta$1(from, to) {
|
|
37
37
|
for (const key of SCHEMA_META_KEYS) if (key in from && from[key] !== void 0) to[key] = from[key];
|
|
38
|
-
|
|
39
|
-
function stringifyGeminiEnumValue(value) {
|
|
40
|
-
if (typeof value === "string") return value;
|
|
41
|
-
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
42
|
-
if (typeof value === "boolean") return String(value);
|
|
38
|
+
return to;
|
|
43
39
|
}
|
|
44
40
|
function cleanGeminiEnumValues(value) {
|
|
45
41
|
if (!Array.isArray(value)) return;
|
|
46
|
-
const values = value.
|
|
47
|
-
const stringified = stringifyGeminiEnumValue(entry);
|
|
48
|
-
return stringified === void 0 ? [] : [stringified];
|
|
49
|
-
});
|
|
42
|
+
const values = value.filter((entry) => typeof entry === "string" || typeof entry === "boolean" || typeof entry === "number" && Number.isFinite(entry)).map(String);
|
|
50
43
|
const unique = [...new Set(values)];
|
|
51
44
|
return unique.length > 0 ? unique : void 0;
|
|
52
45
|
}
|
|
@@ -62,9 +55,8 @@ function tryFlattenLiteralAnyOf(variants) {
|
|
|
62
55
|
else if (Array.isArray(v.enum) && v.enum.length === 1) literalValue = v.enum[0];
|
|
63
56
|
else return null;
|
|
64
57
|
const variantType = typeof v.type === "string" ? v.type : null;
|
|
65
|
-
if (!variantType) return null;
|
|
66
|
-
|
|
67
|
-
else if (commonType !== variantType) return null;
|
|
58
|
+
if (!variantType || commonType !== null && commonType !== variantType) return null;
|
|
59
|
+
commonType = variantType;
|
|
68
60
|
allValues.push(literalValue);
|
|
69
61
|
}
|
|
70
62
|
if (commonType && allValues.length > 0) return {
|
|
@@ -75,21 +67,17 @@ function tryFlattenLiteralAnyOf(variants) {
|
|
|
75
67
|
}
|
|
76
68
|
function isNullSchema(variant) {
|
|
77
69
|
if (!isRecord(variant)) return false;
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (typeValue === "null") return true;
|
|
83
|
-
if (Array.isArray(typeValue) && typeValue.length === 1 && typeValue[0] === "null") return true;
|
|
84
|
-
return false;
|
|
70
|
+
if ("const" in variant && variant.const === null) return true;
|
|
71
|
+
if (Array.isArray(variant.enum) && variant.enum.length === 1) return variant.enum[0] === null;
|
|
72
|
+
const typeValue = variant.type;
|
|
73
|
+
return typeValue === "null" || Array.isArray(typeValue) && typeValue.length === 1 && typeValue[0] === "null";
|
|
85
74
|
}
|
|
86
75
|
function extendSchemaDefs$1(defs, schema) {
|
|
87
76
|
const defsEntry = isRecord(schema.$defs) ? schema.$defs : void 0;
|
|
88
77
|
const legacyDefsEntry = isRecord(schema.definitions) ? schema.definitions : void 0;
|
|
89
78
|
if (!defsEntry && !legacyDefsEntry) return defs;
|
|
90
|
-
const next =
|
|
91
|
-
|
|
92
|
-
if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.set(key, value);
|
|
79
|
+
const next = new Map(defs);
|
|
80
|
+
for (const entry of [defsEntry, legacyDefsEntry]) for (const [key, value] of Object.entries(entry ?? {})) next.set(key, value);
|
|
93
81
|
return next;
|
|
94
82
|
}
|
|
95
83
|
function tryResolveLocalRef$1(ref, defs) {
|
|
@@ -104,30 +92,15 @@ function simplifyUnionVariants(params) {
|
|
|
104
92
|
const nonNullVariants = variants.filter((variant) => !isNullSchema(variant));
|
|
105
93
|
const stripped = nonNullVariants.length !== variants.length;
|
|
106
94
|
const flattened = tryFlattenLiteralAnyOf(nonNullVariants);
|
|
107
|
-
if (flattened) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
};
|
|
112
|
-
copySchemaMeta$1(obj, result);
|
|
113
|
-
return {
|
|
114
|
-
kind: "simplified",
|
|
115
|
-
value: result
|
|
116
|
-
};
|
|
117
|
-
}
|
|
95
|
+
if (flattened) return {
|
|
96
|
+
kind: "simplified",
|
|
97
|
+
value: copySchemaMeta$1(obj, flattened)
|
|
98
|
+
};
|
|
118
99
|
if (stripped && nonNullVariants.length === 1) {
|
|
119
100
|
const lone = nonNullVariants[0];
|
|
120
|
-
if (lone && typeof lone === "object" && !Array.isArray(lone)) {
|
|
121
|
-
const result = { ...lone };
|
|
122
|
-
copySchemaMeta$1(obj, result);
|
|
123
|
-
return {
|
|
124
|
-
kind: "simplified",
|
|
125
|
-
value: result
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
101
|
return {
|
|
129
102
|
kind: "simplified",
|
|
130
|
-
value: lone
|
|
103
|
+
value: isRecord(lone) ? copySchemaMeta$1(obj, { ...lone }) : lone
|
|
131
104
|
};
|
|
132
105
|
}
|
|
133
106
|
return {
|
|
@@ -147,69 +120,42 @@ function sanitizeRequiredFields(schema) {
|
|
|
147
120
|
else delete schema.required;
|
|
148
121
|
return schema;
|
|
149
122
|
}
|
|
150
|
-
function* cleanSchemaArray(schemas, defs, refStack, ancestors, result) {
|
|
151
|
-
result.length = schemas.length;
|
|
152
|
-
for (let index = 0; index < result.length; index += 1) if (index in schemas) result[index] = yield cleanSchemaForGeminiWithDefs(schemas[index], defs, refStack, ancestors);
|
|
153
|
-
return result;
|
|
154
|
-
}
|
|
155
123
|
function* cleanSchemaForGeminiWithDefs(schema, defs, refStack, ancestors) {
|
|
156
|
-
|
|
157
|
-
if (ancestors.has(schema)) throw new TypeError("Tool schema contains a circular reference.");
|
|
158
|
-
ancestors.add(schema);
|
|
159
|
-
try {
|
|
160
|
-
if (Array.isArray(schema)) {
|
|
161
|
-
const result = [];
|
|
162
|
-
yield cleanSchemaArray(schema, defs, refStack, ancestors, result);
|
|
163
|
-
return result;
|
|
164
|
-
}
|
|
165
|
-
const obj = schema;
|
|
124
|
+
return yield walkSchemaValue(schema, ancestors, function* (obj) {
|
|
166
125
|
const nextDefs = extendSchemaDefs$1(defs, obj);
|
|
126
|
+
const visit = (value) => cleanSchemaForGeminiWithDefs(value, nextDefs, refStack, ancestors);
|
|
167
127
|
const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
|
|
168
128
|
if (refValue) {
|
|
169
129
|
if (refStack?.has(refValue)) return {};
|
|
170
130
|
const resolved = tryResolveLocalRef$1(refValue, nextDefs);
|
|
171
131
|
if (resolved) {
|
|
172
|
-
const nextRefStack =
|
|
132
|
+
const nextRefStack = new Set(refStack);
|
|
173
133
|
nextRefStack.add(refValue);
|
|
174
134
|
const cleaned = yield cleanSchemaForGeminiWithDefs(resolved, nextDefs, nextRefStack, /* @__PURE__ */ new Set());
|
|
175
135
|
if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) return cleaned;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
136
|
+
return copySchemaMeta$1(obj, { ...cleaned });
|
|
137
|
+
}
|
|
138
|
+
return copySchemaMeta$1(obj, {});
|
|
139
|
+
}
|
|
140
|
+
const unions = {};
|
|
141
|
+
for (const key of ["anyOf", "oneOf"]) {
|
|
142
|
+
const variants = obj[key];
|
|
143
|
+
if (Array.isArray(variants)) {
|
|
144
|
+
const cleaned = [];
|
|
145
|
+
yield walkSchemaArray(variants, visit, cleaned);
|
|
146
|
+
unions[key] = cleaned;
|
|
179
147
|
}
|
|
180
|
-
const result = {};
|
|
181
|
-
copySchemaMeta$1(obj, result);
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
const hasAnyOf = "anyOf" in obj && Array.isArray(obj.anyOf);
|
|
185
|
-
const hasOneOf = "oneOf" in obj && Array.isArray(obj.oneOf);
|
|
186
|
-
let cleanedAnyOf;
|
|
187
|
-
if (hasAnyOf) {
|
|
188
|
-
const variants = obj.anyOf;
|
|
189
|
-
cleanedAnyOf = [];
|
|
190
|
-
yield cleanSchemaArray(variants, nextDefs, refStack, ancestors, cleanedAnyOf);
|
|
191
|
-
}
|
|
192
|
-
let cleanedOneOf;
|
|
193
|
-
if (hasOneOf) {
|
|
194
|
-
const variants = obj.oneOf;
|
|
195
|
-
cleanedOneOf = [];
|
|
196
|
-
yield cleanSchemaArray(variants, nextDefs, refStack, ancestors, cleanedOneOf);
|
|
197
|
-
}
|
|
198
|
-
if (hasAnyOf) {
|
|
199
|
-
const simplified = simplifyUnionVariants({
|
|
200
|
-
obj,
|
|
201
|
-
variants: cleanedAnyOf ?? []
|
|
202
|
-
});
|
|
203
|
-
if (simplified.kind === "simplified") return simplified.value;
|
|
204
|
-
cleanedAnyOf = simplified.value;
|
|
205
148
|
}
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
149
|
+
for (const key of ["anyOf", "oneOf"]) {
|
|
150
|
+
const variants = unions[key];
|
|
151
|
+
if (variants) {
|
|
152
|
+
const simplified = simplifyUnionVariants({
|
|
153
|
+
obj,
|
|
154
|
+
variants
|
|
155
|
+
});
|
|
156
|
+
if (simplified.kind === "simplified") return simplified.value;
|
|
157
|
+
unions[key] = simplified.value;
|
|
158
|
+
}
|
|
213
159
|
}
|
|
214
160
|
const cleaned = {};
|
|
215
161
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -220,7 +166,7 @@ function* cleanSchemaForGeminiWithDefs(schema, defs, refStack, ancestors) {
|
|
|
220
166
|
continue;
|
|
221
167
|
}
|
|
222
168
|
if (key === "required" && Array.isArray(value) && value.length === 0) continue;
|
|
223
|
-
if (key === "type" && (
|
|
169
|
+
if (key === "type" && (unions.anyOf || unions.oneOf)) continue;
|
|
224
170
|
if (key === "type" && Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
225
171
|
const types = value.filter((entry) => entry !== "null");
|
|
226
172
|
cleaned.type = types.length === 1 ? types[0] : types;
|
|
@@ -229,32 +175,20 @@ function* cleanSchemaForGeminiWithDefs(schema, defs, refStack, ancestors) {
|
|
|
229
175
|
if (key === "properties") {
|
|
230
176
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
231
177
|
const entries = Object.entries(value);
|
|
232
|
-
for (const entry of entries) entry[1] = yield
|
|
178
|
+
for (const entry of entries) entry[1] = yield visit(entry[1]);
|
|
233
179
|
cleaned[key] = Object.fromEntries(entries);
|
|
234
180
|
} else cleaned[key] = {};
|
|
235
|
-
} else if (key === "items" && value)
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
cleaned[key] = result;
|
|
240
|
-
} else if (typeof value === "object") cleaned[key] = yield cleanSchemaForGeminiWithDefs(value, nextDefs, refStack, ancestors);
|
|
241
|
-
else cleaned[key] = value;
|
|
242
|
-
} else if (key === "anyOf" && Array.isArray(value)) cleaned[key] = cleanedAnyOf;
|
|
243
|
-
else if (key === "oneOf" && Array.isArray(value)) cleaned[key] = cleanedOneOf;
|
|
244
|
-
else if (key === "allOf" && Array.isArray(value)) {
|
|
245
|
-
const result = [];
|
|
246
|
-
yield cleanSchemaArray(value, nextDefs, refStack, ancestors, result);
|
|
247
|
-
cleaned[key] = result;
|
|
248
|
-
} else cleaned[key] = value;
|
|
181
|
+
} else if ((key === "items" || key === "allOf") && Array.isArray(value)) cleaned[key] = yield walkSchemaArray(value, visit);
|
|
182
|
+
else if (key === "items" && value && typeof value === "object") cleaned[key] = yield visit(value);
|
|
183
|
+
else if ((key === "anyOf" || key === "oneOf") && Array.isArray(value)) cleaned[key] = unions[key];
|
|
184
|
+
else cleaned[key] = value;
|
|
249
185
|
}
|
|
250
186
|
for (const variants of [cleaned.anyOf, cleaned.oneOf]) if (Array.isArray(variants)) {
|
|
251
187
|
const flattened = flattenUnionFallback(cleaned, variants);
|
|
252
188
|
if (flattened) return sanitizeRequiredFields(flattened);
|
|
253
189
|
}
|
|
254
190
|
return sanitizeRequiredFields(cleaned);
|
|
255
|
-
}
|
|
256
|
-
ancestors.delete(schema);
|
|
257
|
-
}
|
|
191
|
+
});
|
|
258
192
|
}
|
|
259
193
|
/**
|
|
260
194
|
* Last-resort flattening for anyOf/oneOf arrays that could not be simplified
|
|
@@ -267,9 +201,7 @@ function flattenUnionFallback(obj, variants) {
|
|
|
267
201
|
const types = new Set(objects.map((v) => v.type).filter(Boolean));
|
|
268
202
|
const first = objects[0];
|
|
269
203
|
const type = types.size === 1 ? Array.from(types)[0] : first?.type;
|
|
270
|
-
|
|
271
|
-
copySchemaMeta$1(obj, merged);
|
|
272
|
-
return merged;
|
|
204
|
+
return copySchemaMeta$1(obj, objects.length === 1 ? { ...first } : type ? { type } : {});
|
|
273
205
|
}
|
|
274
206
|
function cleanSchemaForGemini(schema) {
|
|
275
207
|
return evaluateSchemaWalk(cleanSchemaForGeminiWithDefs(schema, void 0, void 0, /* @__PURE__ */ new Set()));
|
|
@@ -295,12 +227,9 @@ function extendSchemaDefs(defs, schema) {
|
|
|
295
227
|
const defsEntry = isRecord(schema.$defs) ? schema.$defs : void 0;
|
|
296
228
|
const legacyDefsEntry = isRecord(schema.definitions) ? schema.definitions : void 0;
|
|
297
229
|
if (!defsEntry && !legacyDefsEntry) return defs;
|
|
298
|
-
const next =
|
|
299
|
-
$defs: new Map(defs
|
|
300
|
-
definitions: new Map(defs
|
|
301
|
-
} : {
|
|
302
|
-
$defs: /* @__PURE__ */ new Map(),
|
|
303
|
-
definitions: /* @__PURE__ */ new Map()
|
|
230
|
+
const next = {
|
|
231
|
+
$defs: new Map(defs?.$defs),
|
|
232
|
+
definitions: new Map(defs?.definitions)
|
|
304
233
|
};
|
|
305
234
|
if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.$defs.set(key, value);
|
|
306
235
|
if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.definitions.set(key, value);
|
|
@@ -321,10 +250,6 @@ function resolveJsonPointerPath(value, tokens) {
|
|
|
321
250
|
}
|
|
322
251
|
return current;
|
|
323
252
|
}
|
|
324
|
-
function resolveLocalJsonPointer(rootDocument, ref) {
|
|
325
|
-
const tokens = parseLocalSchemaRefPointer(ref);
|
|
326
|
-
return tokens ? resolveJsonPointerPath(rootDocument, tokens) : void 0;
|
|
327
|
-
}
|
|
328
253
|
const SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
329
254
|
"$defs",
|
|
330
255
|
"definitions",
|
|
@@ -378,7 +303,7 @@ function inlineLocalSchemaRefsWithDefs(schema, defs, refStack, state, rootDocume
|
|
|
378
303
|
if (refValue.startsWith("#/") || parseLocalSchemaRefPointer(refValue)) state.unresolvedLocalRefs = true;
|
|
379
304
|
return { ...obj };
|
|
380
305
|
}
|
|
381
|
-
const nextRefStack =
|
|
306
|
+
const nextRefStack = new Set(refStack);
|
|
382
307
|
nextRefStack.add(refValue);
|
|
383
308
|
const inlined = inlineLocalSchemaRefsWithDefs(resolved, nextDefs, nextRefStack, state, rootDocument);
|
|
384
309
|
if (!isRecord(inlined)) return inlined;
|
|
@@ -400,9 +325,11 @@ function inlineLocalSchemaRefsWithDefs(schema, defs, refStack, state, rootDocume
|
|
|
400
325
|
setOwnSchemaProperty(result, key, next);
|
|
401
326
|
}
|
|
402
327
|
if (state.unresolvedLocalRefs) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
328
|
+
for (const key of [
|
|
329
|
+
"$defs",
|
|
330
|
+
"definitions",
|
|
331
|
+
"components"
|
|
332
|
+
]) if (key in obj) result[key] = obj[key];
|
|
406
333
|
}
|
|
407
334
|
return result;
|
|
408
335
|
}
|
|
@@ -425,7 +352,7 @@ function canPreserveRootSchemaRefs(schema) {
|
|
|
425
352
|
if (!isRecord(node)) return true;
|
|
426
353
|
if (ancestors.has(node) || "$id" in node || "id" in node || "components" in node || node !== schema && ("$defs" in node || "definitions" in node)) return false;
|
|
427
354
|
if ("$ref" in node) {
|
|
428
|
-
if (typeof node.$ref !== "string" || !/^#\/(\$defs|definitions)\/[^/]+$/.test(node.$ref) || node.nullable === true ||
|
|
355
|
+
if (typeof node.$ref !== "string" || !/^#\/(\$defs|definitions)\/[^/]+$/.test(node.$ref) || node.nullable === true || tryResolveLocalRef(node.$ref, void 0, schema) === void 0) return false;
|
|
429
356
|
hasRefs ||= !inDefinitions;
|
|
430
357
|
}
|
|
431
358
|
ancestors.add(node);
|
|
@@ -521,34 +448,22 @@ function findLlamacppGbnfSchemaViolations(schema, path) {
|
|
|
521
448
|
}
|
|
522
449
|
//#endregion
|
|
523
450
|
//#region packages/ai/src/providers/schema-keyword-strip.ts
|
|
524
|
-
function* stripSchemaArray(schemas, unsupportedKeywords, ancestors) {
|
|
525
|
-
const result = [];
|
|
526
|
-
result.length = schemas.length;
|
|
527
|
-
for (let index = 0; index < result.length; index += 1) if (index in schemas) result[index] = yield stripSchemaKeywords(schemas[index], unsupportedKeywords, ancestors);
|
|
528
|
-
return result;
|
|
529
|
-
}
|
|
530
451
|
function* stripSchemaKeywords(schema, unsupportedKeywords, ancestors) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
ancestors.add(schema);
|
|
534
|
-
try {
|
|
535
|
-
if (Array.isArray(schema)) return yield stripSchemaArray(schema, unsupportedKeywords, ancestors);
|
|
536
|
-
const obj = schema;
|
|
452
|
+
const visit = (value) => stripSchemaKeywords(value, unsupportedKeywords, ancestors);
|
|
453
|
+
return yield walkSchemaValue(schema, ancestors, function* (obj) {
|
|
537
454
|
const cleaned = {};
|
|
538
455
|
for (const [key, value] of Object.entries(obj)) {
|
|
539
456
|
if (unsupportedKeywords.has(key)) continue;
|
|
540
457
|
if (SCHEMA_MAP_KEYS$1.has(key) && value && typeof value === "object" && !Array.isArray(value)) {
|
|
541
458
|
const entries = Object.entries(value);
|
|
542
|
-
for (const entry of entries) entry[1] = yield
|
|
459
|
+
for (const entry of entries) entry[1] = yield visit(entry[1]);
|
|
543
460
|
cleaned[key] = Object.fromEntries(entries);
|
|
544
|
-
} else if (SCHEMA_ARRAY_KEYS$1.has(key) && Array.isArray(value)) cleaned[key] = yield
|
|
545
|
-
else if (SCHEMA_OBJECT_KEYS$1.has(key) && value && typeof value === "object") cleaned[key] = yield
|
|
461
|
+
} else if (SCHEMA_ARRAY_KEYS$1.has(key) && Array.isArray(value)) cleaned[key] = yield walkSchemaArray(value, visit);
|
|
462
|
+
else if (SCHEMA_OBJECT_KEYS$1.has(key) && value && typeof value === "object") cleaned[key] = yield visit(value);
|
|
546
463
|
else cleaned[key] = value;
|
|
547
464
|
}
|
|
548
465
|
return cleaned;
|
|
549
|
-
}
|
|
550
|
-
ancestors.delete(schema);
|
|
551
|
-
}
|
|
466
|
+
});
|
|
552
467
|
}
|
|
553
468
|
/** Remove schema keywords unsupported by a target provider/tool surface. */
|
|
554
469
|
function stripUnsupportedSchemaKeywords(schema, unsupportedKeywords) {
|
|
@@ -607,6 +522,136 @@ function createToolSchemaNormalizationCache(maxEntries) {
|
|
|
607
522
|
};
|
|
608
523
|
}
|
|
609
524
|
//#endregion
|
|
525
|
+
//#region packages/ai/src/providers/tool-schema-normalization.ts
|
|
526
|
+
const OPENAPI_SCHEMA_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
527
|
+
"discriminator",
|
|
528
|
+
"externalDocs",
|
|
529
|
+
"readOnly",
|
|
530
|
+
"writeOnly",
|
|
531
|
+
"xml",
|
|
532
|
+
"example"
|
|
533
|
+
]);
|
|
534
|
+
function isNullSchemaLike(schema) {
|
|
535
|
+
if (!isRecord(schema)) return false;
|
|
536
|
+
if (schema.type === "null") return true;
|
|
537
|
+
if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
|
|
538
|
+
if ("const" in schema && schema.const === null) return true;
|
|
539
|
+
return Array.isArray(schema.enum) && schema.enum.includes(null);
|
|
540
|
+
}
|
|
541
|
+
const OPENAI_NULLABLE_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
542
|
+
"default",
|
|
543
|
+
"description",
|
|
544
|
+
"examples",
|
|
545
|
+
"format",
|
|
546
|
+
"title"
|
|
547
|
+
]);
|
|
548
|
+
function normalizeToolSchema(schema, profile, depth = 0) {
|
|
549
|
+
if (Array.isArray(schema)) {
|
|
550
|
+
if (profile === "compat-map" || profile === "openapi-map") return schema;
|
|
551
|
+
let changed = false;
|
|
552
|
+
const normalized = schema.map((entry) => {
|
|
553
|
+
const next = normalizeToolSchema(entry, profile, profile === "strict" ? depth : 1);
|
|
554
|
+
changed ||= next !== entry;
|
|
555
|
+
return next;
|
|
556
|
+
});
|
|
557
|
+
return changed ? normalized : schema;
|
|
558
|
+
}
|
|
559
|
+
if (!isRecord(schema)) return schema;
|
|
560
|
+
const record = schema;
|
|
561
|
+
let changed = false;
|
|
562
|
+
let hadNullType = false;
|
|
563
|
+
const nullable = profile === "openapi" && record.nullable === true;
|
|
564
|
+
const entries = [];
|
|
565
|
+
for (const [key, value] of Object.entries(record)) {
|
|
566
|
+
if (profile === "compat" && value === null && (OPENAI_NULLABLE_ANNOTATION_KEYS.has(key) || key === "type")) {
|
|
567
|
+
hadNullType ||= key === "type";
|
|
568
|
+
changed = true;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (profile === "openapi") {
|
|
572
|
+
if (key === "nullable" || OPENAPI_SCHEMA_ANNOTATION_KEYS.has(key)) {
|
|
573
|
+
changed = true;
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (SCHEMA_LITERAL_KEYS.has(key) || key === "components") {
|
|
577
|
+
entries.push([key, value]);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
let next = value;
|
|
581
|
+
if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) next = normalizeToolSchema(value, "openapi-map", 1);
|
|
582
|
+
else if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) next = normalizeToolSchema(value, profile, 1);
|
|
583
|
+
else if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
584
|
+
const nextEntries = value.map((entry) => normalizeToolSchema(entry, profile, 1));
|
|
585
|
+
changed ||= nextEntries.some((entry, index) => entry !== value[index]);
|
|
586
|
+
entries.push([key, nextEntries]);
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
changed ||= next !== value;
|
|
590
|
+
entries.push([key, next]);
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
let next = value;
|
|
594
|
+
if (profile === "strict") next = normalizeToolSchema(value, profile, key === "properties" ? depth : depth + 1);
|
|
595
|
+
else if (profile === "compat-map" || profile === "openapi-map") next = normalizeToolSchema(value, profile === "compat-map" ? "compat" : "openapi", 1);
|
|
596
|
+
else if (SCHEMA_MAP_KEYS$1.has(key)) next = normalizeToolSchema(value, "compat-map", 1);
|
|
597
|
+
else if (SCHEMA_NESTED_KEYS.has(key)) next = normalizeToolSchema(value, "compat", 1);
|
|
598
|
+
changed ||= next !== value;
|
|
599
|
+
entries.push([key, next]);
|
|
600
|
+
}
|
|
601
|
+
const normalized = Object.fromEntries(entries);
|
|
602
|
+
if (profile === "compat-map" || profile === "openapi-map") return changed ? normalized : schema;
|
|
603
|
+
if (profile === "openapi") {
|
|
604
|
+
if (nullable) {
|
|
605
|
+
if ([
|
|
606
|
+
normalized.allOf,
|
|
607
|
+
normalized.anyOf,
|
|
608
|
+
normalized.oneOf
|
|
609
|
+
].some(Array.isArray)) {
|
|
610
|
+
if (Array.isArray(normalized.anyOf) && normalized.anyOf.some(isNullSchemaLike) || Array.isArray(normalized.oneOf) && normalized.oneOf.some(isNullSchemaLike)) return normalized;
|
|
611
|
+
const wrapped = { anyOf: [normalized, { type: "null" }] };
|
|
612
|
+
copySchemaMeta(normalized, wrapped);
|
|
613
|
+
return wrapped;
|
|
614
|
+
}
|
|
615
|
+
const type = normalized.type;
|
|
616
|
+
if (typeof type === "string" && type !== "null") normalized.type = [type, "null"];
|
|
617
|
+
else if (Array.isArray(type) && !type.includes("null")) normalized.type = [...type, "null"];
|
|
618
|
+
if (Array.isArray(normalized.enum) && !normalized.enum.includes(null)) normalized.enum = [...normalized.enum, null];
|
|
619
|
+
}
|
|
620
|
+
return changed || nullable ? normalized : schema;
|
|
621
|
+
}
|
|
622
|
+
if (profile === "compat") {
|
|
623
|
+
if (Object.keys(normalized).length === 0) {
|
|
624
|
+
if (depth !== 0) return schema;
|
|
625
|
+
return {
|
|
626
|
+
type: "object",
|
|
627
|
+
properties: {},
|
|
628
|
+
required: [],
|
|
629
|
+
additionalProperties: false
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
const hasObjectShapeHints = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) || Array.isArray(normalized.required);
|
|
633
|
+
const hasArrayShapeHints = "items" in normalized;
|
|
634
|
+
if (!("type" in normalized) && hasObjectShapeHints !== hasArrayShapeHints) {
|
|
635
|
+
normalized.type = hasObjectShapeHints ? "object" : "array";
|
|
636
|
+
changed = true;
|
|
637
|
+
} else if (hadNullType && !("type" in normalized)) normalized.type = null;
|
|
638
|
+
if (normalized.type === "object" && !("properties" in normalized)) {
|
|
639
|
+
normalized.properties = {};
|
|
640
|
+
changed = true;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const hasEmptyProperties = isRecord(normalized.properties) && Object.keys(normalized.properties).length === 0;
|
|
644
|
+
if (normalized.type === "object" && !Array.isArray(normalized.required) && hasEmptyProperties) {
|
|
645
|
+
normalized.required = [];
|
|
646
|
+
changed = true;
|
|
647
|
+
}
|
|
648
|
+
if (normalized.type === "object" && (profile === "strict" ? depth === 0 : hasEmptyProperties) && !("additionalProperties" in normalized)) {
|
|
649
|
+
normalized.additionalProperties = false;
|
|
650
|
+
changed = true;
|
|
651
|
+
}
|
|
652
|
+
return changed ? normalized : schema;
|
|
653
|
+
}
|
|
654
|
+
//#endregion
|
|
610
655
|
//#region packages/ai/src/providers/agent-tools-parameter-schema.ts
|
|
611
656
|
/**
|
|
612
657
|
* Normalizes model-facing tool parameter schemas across provider quirks.
|
|
@@ -632,20 +677,6 @@ function shouldOmitEmptyArrayItems(modelOrCompat) {
|
|
|
632
677
|
return extractToolSchemaModelCompat(modelOrCompat)?.omitEmptyArrayItems === true;
|
|
633
678
|
}
|
|
634
679
|
const toolParameterSchemaCache = createToolSchemaNormalizationCache(8);
|
|
635
|
-
function resolveToolParameterSchemaCacheKey(options) {
|
|
636
|
-
const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
|
|
637
|
-
const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
|
|
638
|
-
const toolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
|
|
639
|
-
const unsupportedKeywords = Array.from(resolveUnsupportedToolSchemaKeywords(options?.modelCompat)).toSorted();
|
|
640
|
-
const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
|
|
641
|
-
return JSON.stringify([
|
|
642
|
-
normalizedProvider,
|
|
643
|
-
normalizedModelId,
|
|
644
|
-
toolSchemaProfile,
|
|
645
|
-
unsupportedKeywords,
|
|
646
|
-
omitEmptyArrayItems
|
|
647
|
-
]);
|
|
648
|
-
}
|
|
649
680
|
function isGeminiModelId(modelId) {
|
|
650
681
|
return /(?:^|[/:])gemini(?:$|[-/:.])/.test(modelId);
|
|
651
682
|
}
|
|
@@ -750,121 +781,42 @@ function normalizeArraySchemaItems(schema, mode) {
|
|
|
750
781
|
}
|
|
751
782
|
return changed ? normalized : schema;
|
|
752
783
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
"externalDocs",
|
|
756
|
-
"readOnly",
|
|
757
|
-
"writeOnly",
|
|
758
|
-
"xml",
|
|
759
|
-
"example"
|
|
760
|
-
]);
|
|
761
|
-
function appendNullSchemaType(type) {
|
|
762
|
-
if (type === "null") return type;
|
|
763
|
-
if (typeof type === "string") return [type, "null"];
|
|
764
|
-
if (Array.isArray(type)) return type.includes("null") ? type : [...type, "null"];
|
|
765
|
-
return type;
|
|
766
|
-
}
|
|
767
|
-
function isNullSchemaLike(schema) {
|
|
768
|
-
if (!isRecord(schema)) return false;
|
|
769
|
-
if (schema.type === "null") return true;
|
|
770
|
-
if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
|
|
771
|
-
if ("const" in schema && schema.const === null) return true;
|
|
772
|
-
return Array.isArray(schema.enum) && schema.enum.includes(null);
|
|
773
|
-
}
|
|
774
|
-
function hasOpenApiComposition(schema) {
|
|
775
|
-
return [
|
|
776
|
-
"allOf",
|
|
777
|
-
"anyOf",
|
|
778
|
-
"oneOf"
|
|
779
|
-
].some((key) => Array.isArray(schema[key]));
|
|
780
|
-
}
|
|
781
|
-
function schemaCompositionAlreadyAllowsNull(schema) {
|
|
782
|
-
return Array.isArray(schema.anyOf) && schema.anyOf.some(isNullSchemaLike) || Array.isArray(schema.oneOf) && schema.oneOf.some(isNullSchemaLike);
|
|
783
|
-
}
|
|
784
|
-
function wrapNullableComposedSchema(schema) {
|
|
785
|
-
if (schemaCompositionAlreadyAllowsNull(schema)) return schema;
|
|
786
|
-
const wrapped = { anyOf: [schema, { type: "null" }] };
|
|
787
|
-
copySchemaMeta(schema, wrapped);
|
|
788
|
-
return wrapped;
|
|
789
|
-
}
|
|
790
|
-
function normalizeOpenApiSchemaKeywords(schema) {
|
|
791
|
-
if (Array.isArray(schema)) {
|
|
792
|
-
let changed = false;
|
|
793
|
-
const normalized = schema.map((entry) => {
|
|
794
|
-
const next = normalizeOpenApiSchemaKeywords(entry);
|
|
795
|
-
changed ||= next !== entry;
|
|
796
|
-
return next;
|
|
797
|
-
});
|
|
798
|
-
return changed ? normalized : schema;
|
|
799
|
-
}
|
|
800
|
-
if (!isRecord(schema)) return schema;
|
|
801
|
-
let changed = false;
|
|
802
|
-
const nullable = schema.nullable === true;
|
|
803
|
-
const entries = Object.entries(schema);
|
|
804
|
-
let normalized;
|
|
805
|
-
for (const [key, value] of entries) {
|
|
806
|
-
if (key === "nullable" || OPENAPI_SCHEMA_ANNOTATION_KEYS.has(key)) {
|
|
807
|
-
normalized ??= Object.fromEntries(entries);
|
|
808
|
-
delete normalized[key];
|
|
809
|
-
changed = true;
|
|
810
|
-
continue;
|
|
811
|
-
}
|
|
812
|
-
if (SCHEMA_LITERAL_KEYS.has(key) || key === "components") continue;
|
|
813
|
-
let next = value;
|
|
814
|
-
if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
815
|
-
let mapChanged = false;
|
|
816
|
-
const mapEntries = Object.entries(value);
|
|
817
|
-
for (const entry of mapEntries) {
|
|
818
|
-
const nextEntry = normalizeOpenApiSchemaKeywords(entry[1]);
|
|
819
|
-
mapChanged ||= nextEntry !== entry[1];
|
|
820
|
-
entry[1] = nextEntry;
|
|
821
|
-
}
|
|
822
|
-
next = mapChanged ? Object.fromEntries(mapEntries) : value;
|
|
823
|
-
} else if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) next = normalizeOpenApiSchemaKeywords(value);
|
|
824
|
-
else if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
825
|
-
const nextEntries = value.map(normalizeOpenApiSchemaKeywords);
|
|
826
|
-
(normalized ??= Object.fromEntries(entries))[key] = nextEntries;
|
|
827
|
-
changed ||= nextEntries.some((entry, index) => entry !== value[index]);
|
|
828
|
-
continue;
|
|
829
|
-
}
|
|
830
|
-
if (next !== value) {
|
|
831
|
-
(normalized ??= Object.fromEntries(entries))[key] = next;
|
|
832
|
-
changed = true;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
if (nullable) {
|
|
836
|
-
normalized ??= Object.fromEntries(entries);
|
|
837
|
-
if (hasOpenApiComposition(normalized)) return wrapNullableComposedSchema(normalized);
|
|
838
|
-
if ("type" in normalized) {
|
|
839
|
-
const nextType = appendNullSchemaType(normalized.type);
|
|
840
|
-
if (nextType !== normalized.type) normalized.type = nextType;
|
|
841
|
-
}
|
|
842
|
-
if (Array.isArray(normalized.enum) && !normalized.enum.includes(null)) normalized.enum = [...normalized.enum, null];
|
|
843
|
-
}
|
|
844
|
-
return changed || nullable ? normalized ?? schema : schema;
|
|
845
|
-
}
|
|
846
|
-
function normalizeToolParameterSchemaUncached(schema, options) {
|
|
784
|
+
/** Return a provider-compatible JSON schema for a model-facing tool. */
|
|
785
|
+
function normalizeToolParameterSchema(schema, options) {
|
|
847
786
|
const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
|
|
848
787
|
const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
|
|
849
788
|
const normalizedToolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
|
|
850
|
-
const isGeminiProvider = normalizedProvider.includes("google") || normalizedProvider.includes("gemini") || isGeminiModelId(normalizedModelId) || normalizedToolSchemaProfile === "gemini";
|
|
851
|
-
const isAnthropicProvider = normalizedProvider.includes("anthropic");
|
|
852
789
|
const unsupportedToolSchemaKeywords = resolveUnsupportedToolSchemaKeywords(options?.modelCompat);
|
|
853
790
|
const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
|
|
791
|
+
const source = schema && typeof schema === "object" ? schema : void 0;
|
|
792
|
+
const cacheKey = source ? JSON.stringify([
|
|
793
|
+
normalizedProvider,
|
|
794
|
+
normalizedModelId,
|
|
795
|
+
normalizedToolSchemaProfile,
|
|
796
|
+
[...unsupportedToolSchemaKeywords].toSorted(),
|
|
797
|
+
omitEmptyArrayItems
|
|
798
|
+
]) : "";
|
|
799
|
+
if (source) {
|
|
800
|
+
const cached = toolParameterSchemaCache.get(source, cacheKey);
|
|
801
|
+
if (cached) return cached;
|
|
802
|
+
}
|
|
803
|
+
const rememberResult = (normalized) => source ? toolParameterSchemaCache.remember(source, cacheKey, normalized) : normalized;
|
|
804
|
+
const isGeminiProvider = normalizedProvider.includes("google") || normalizedProvider.includes("gemini") || isGeminiModelId(normalizedModelId) || normalizedToolSchemaProfile === "gemini";
|
|
805
|
+
const isAnthropicProvider = normalizedProvider.includes("anthropic");
|
|
854
806
|
const isLlamacppGbnfProfile = normalizedToolSchemaProfile === "llamacpp";
|
|
855
|
-
const inlinedSchema =
|
|
807
|
+
const inlinedSchema = normalizeToolSchema(normalizedProvider === "openai" && !isGeminiProvider && !isLlamacppGbnfProfile && ![
|
|
856
808
|
"$ref",
|
|
857
809
|
"$defs",
|
|
858
810
|
"definitions"
|
|
859
|
-
].some((key) => unsupportedToolSchemaKeywords.has(key)) && canPreserveRootSchemaRefs(schema) ? schema : inlineLocalToolSchemaRefs(schema));
|
|
811
|
+
].some((key) => unsupportedToolSchemaKeywords.has(key)) && canPreserveRootSchemaRefs(schema) ? schema : inlineLocalToolSchemaRefs(schema), "openapi");
|
|
860
812
|
const schemaRecord = inlinedSchema && typeof inlinedSchema === "object" ? inlinedSchema : void 0;
|
|
861
|
-
if (!schemaRecord) return inlinedSchema;
|
|
813
|
+
if (!schemaRecord) return rememberResult(inlinedSchema);
|
|
862
814
|
function applyProviderCleaning(s) {
|
|
863
815
|
let arrayItemsCompatibleSchema = normalizeArraySchemaItems(s, omitEmptyArrayItems ? "normalize" : "add");
|
|
864
816
|
if (isLlamacppGbnfProfile) arrayItemsCompatibleSchema = cleanSchemaForLlamacppGbnf(arrayItemsCompatibleSchema);
|
|
865
817
|
if (isGeminiProvider && !isAnthropicProvider) arrayItemsCompatibleSchema = cleanSchemaForGemini(arrayItemsCompatibleSchema);
|
|
866
818
|
if (unsupportedToolSchemaKeywords.size > 0) arrayItemsCompatibleSchema = stripUnsupportedSchemaKeywords(arrayItemsCompatibleSchema, unsupportedToolSchemaKeywords);
|
|
867
|
-
return arrayItemsCompatibleSchema;
|
|
819
|
+
return rememberResult(arrayItemsCompatibleSchema);
|
|
868
820
|
}
|
|
869
821
|
const flattenableVariantKey = Array.isArray(schemaRecord.anyOf) ? "anyOf" : Array.isArray(schemaRecord.oneOf) ? "oneOf" : void 0;
|
|
870
822
|
if (!flattenableVariantKey && !Array.isArray(schemaRecord.allOf)) {
|
|
@@ -922,92 +874,26 @@ function normalizeToolParameterSchemaUncached(schema, options) {
|
|
|
922
874
|
additionalProperties: "additionalProperties" in schemaRecord ? schemaRecord.additionalProperties : true
|
|
923
875
|
});
|
|
924
876
|
}
|
|
925
|
-
/** Return a provider-compatible JSON schema for a model-facing tool. */
|
|
926
|
-
function normalizeToolParameterSchema(schema, options) {
|
|
927
|
-
if (!schema || typeof schema !== "object") return normalizeToolParameterSchemaUncached(schema, options);
|
|
928
|
-
const cacheKey = resolveToolParameterSchemaCacheKey(options);
|
|
929
|
-
const cached = toolParameterSchemaCache.get(schema, cacheKey);
|
|
930
|
-
if (cached) return cached;
|
|
931
|
-
return toolParameterSchemaCache.remember(schema, cacheKey, normalizeToolParameterSchemaUncached(schema, options));
|
|
932
|
-
}
|
|
933
877
|
//#endregion
|
|
934
878
|
//#region packages/ai/src/providers/openai-tool-schema-compat.ts
|
|
935
|
-
const OPENAI_NULLABLE_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
936
|
-
"default",
|
|
937
|
-
"description",
|
|
938
|
-
"examples",
|
|
939
|
-
"format",
|
|
940
|
-
"title"
|
|
941
|
-
]);
|
|
942
|
-
const OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS = new Set([...SCHEMA_OBJECT_KEYS$1, ...SCHEMA_ARRAY_KEYS$1].toSorted());
|
|
943
|
-
function normalizeOpenAIStrictCompatSchemaMap(schema) {
|
|
944
|
-
if (!isRecord(schema)) return schema;
|
|
945
|
-
let changed = false;
|
|
946
|
-
const normalized = Object.fromEntries(Object.entries(schema).map(([key, value]) => {
|
|
947
|
-
const next = normalizeOpenAIStrictCompatSchemaRecursive(value);
|
|
948
|
-
changed ||= next !== value;
|
|
949
|
-
return [key, next];
|
|
950
|
-
}));
|
|
951
|
-
return changed ? normalized : schema;
|
|
952
|
-
}
|
|
953
|
-
function normalizeOpenAIStrictCompatSchemaRecursive(schema, promoteEmptyObject = false) {
|
|
954
|
-
if (Array.isArray(schema)) {
|
|
955
|
-
let changed = false;
|
|
956
|
-
const normalized = schema.map((entry) => {
|
|
957
|
-
const next = normalizeOpenAIStrictCompatSchemaRecursive(entry);
|
|
958
|
-
changed ||= next !== entry;
|
|
959
|
-
return next;
|
|
960
|
-
});
|
|
961
|
-
return changed ? normalized : schema;
|
|
962
|
-
}
|
|
963
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
964
|
-
const record = schema;
|
|
965
|
-
let changed = false;
|
|
966
|
-
let hadNullType = false;
|
|
967
|
-
const entries = Object.entries(record).flatMap(([key, value]) => {
|
|
968
|
-
if (value === null && (OPENAI_NULLABLE_ANNOTATION_KEYS.has(key) || key === "type")) {
|
|
969
|
-
hadNullType ||= key === "type";
|
|
970
|
-
changed = true;
|
|
971
|
-
return [];
|
|
972
|
-
}
|
|
973
|
-
const next = SCHEMA_MAP_KEYS$1.has(key) ? normalizeOpenAIStrictCompatSchemaMap(value) : OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS.has(key) ? normalizeOpenAIStrictCompatSchemaRecursive(value) : value;
|
|
974
|
-
changed ||= next !== value;
|
|
975
|
-
return [[key, next]];
|
|
976
|
-
});
|
|
977
|
-
const normalized = Object.fromEntries(entries);
|
|
978
|
-
if (Object.keys(normalized).length === 0) {
|
|
979
|
-
if (!promoteEmptyObject) return schema;
|
|
980
|
-
return {
|
|
981
|
-
type: "object",
|
|
982
|
-
properties: {},
|
|
983
|
-
required: [],
|
|
984
|
-
additionalProperties: false
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
const hasObjectShapeHints = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) || Array.isArray(normalized.required);
|
|
988
|
-
const hasArrayShapeHints = "items" in normalized;
|
|
989
|
-
if (!("type" in normalized) && hasObjectShapeHints !== hasArrayShapeHints) {
|
|
990
|
-
normalized.type = hasObjectShapeHints ? "object" : "array";
|
|
991
|
-
changed = true;
|
|
992
|
-
} else if (hadNullType && !("type" in normalized)) normalized.type = null;
|
|
993
|
-
if (normalized.type === "object" && !("properties" in normalized)) {
|
|
994
|
-
normalized.properties = {};
|
|
995
|
-
changed = true;
|
|
996
|
-
}
|
|
997
|
-
const hasEmptyProperties = isRecord(normalized.properties) && Object.keys(normalized.properties).length === 0;
|
|
998
|
-
if (normalized.type === "object" && !Array.isArray(normalized.required) && hasEmptyProperties) {
|
|
999
|
-
normalized.required = [];
|
|
1000
|
-
changed = true;
|
|
1001
|
-
}
|
|
1002
|
-
if (normalized.type === "object" && hasEmptyProperties && !("additionalProperties" in normalized)) {
|
|
1003
|
-
normalized.additionalProperties = false;
|
|
1004
|
-
changed = true;
|
|
1005
|
-
}
|
|
1006
|
-
return changed ? normalized : schema;
|
|
1007
|
-
}
|
|
1008
879
|
/** Repairs recoverable OpenAI tool-schema shapes before canonical normalization. */
|
|
1009
880
|
function normalizeOpenAIStrictCompatSchema(schema) {
|
|
1010
|
-
return
|
|
881
|
+
return normalizeToolSchema(schema, "compat");
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Returns whether a regex pattern contains a lookahead or lookbehind group.
|
|
885
|
+
* Escaped parentheses, character classes, and named groups are not lookarounds.
|
|
886
|
+
*/
|
|
887
|
+
function hasRegexLookaround(pattern) {
|
|
888
|
+
let inCharacterClass = false;
|
|
889
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
890
|
+
const char = pattern[index];
|
|
891
|
+
if (char === "\\") index += 1;
|
|
892
|
+
else if (inCharacterClass) inCharacterClass = char !== "]";
|
|
893
|
+
else if (char === "[") inCharacterClass = true;
|
|
894
|
+
else if (char === "(" && /^\?<?[=!]/.test(pattern.slice(index + 1, index + 4))) return true;
|
|
895
|
+
}
|
|
896
|
+
return false;
|
|
1011
897
|
}
|
|
1012
898
|
/** Finds schema paths that violate OpenAI strict tool-schema requirements. */
|
|
1013
899
|
function findOpenAIStrictSchemaViolations(schema, path, options) {
|
|
@@ -1024,6 +910,7 @@ function findOpenAIStrictSchemaViolations(schema, path, options) {
|
|
|
1024
910
|
"allOf"
|
|
1025
911
|
]) if (key in record) violations.push(`${path}.${key}`);
|
|
1026
912
|
if (Array.isArray(record.type)) violations.push(`${path}.type`);
|
|
913
|
+
if (typeof record.pattern === "string" && hasRegexLookaround(record.pattern)) violations.push(`${path}.pattern`);
|
|
1027
914
|
const properties = isRecord(record.properties) ? record.properties : void 0;
|
|
1028
915
|
if (record.type === "object") {
|
|
1029
916
|
if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
|
|
@@ -1039,7 +926,7 @@ function findOpenAIStrictSchemaViolations(schema, path, options) {
|
|
|
1039
926
|
if (!isRecord(schemaMap)) continue;
|
|
1040
927
|
for (const [entryKey, value] of Object.entries(schemaMap)) violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}.${entryKey}`));
|
|
1041
928
|
}
|
|
1042
|
-
for (const key of
|
|
929
|
+
for (const key of SCHEMA_NESTED_KEYS) {
|
|
1043
930
|
const value = record[key];
|
|
1044
931
|
if (value && typeof value === "object") violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}`));
|
|
1045
932
|
}
|
|
@@ -1070,40 +957,9 @@ function normalizeStrictOpenAIJsonSchema(schema, modelCompat) {
|
|
|
1070
957
|
const cached = strictOpenAISchemaCache.get(schemaInput, cacheKey);
|
|
1071
958
|
if (cached !== void 0) return cached;
|
|
1072
959
|
}
|
|
1073
|
-
const normalized =
|
|
960
|
+
const normalized = normalizeToolSchema(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), "strict");
|
|
1074
961
|
return cacheable ? strictOpenAISchemaCache.remember(schemaInput, cacheKey, normalized) : normalized;
|
|
1075
962
|
}
|
|
1076
|
-
function normalizeStrictOpenAIJsonSchemaRecursive(schema, depth) {
|
|
1077
|
-
if (Array.isArray(schema)) {
|
|
1078
|
-
let changed = false;
|
|
1079
|
-
const normalized = schema.map((entry) => {
|
|
1080
|
-
const next = normalizeStrictOpenAIJsonSchemaRecursive(entry, depth);
|
|
1081
|
-
changed ||= next !== entry;
|
|
1082
|
-
return next;
|
|
1083
|
-
});
|
|
1084
|
-
return changed ? normalized : schema;
|
|
1085
|
-
}
|
|
1086
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1087
|
-
const record = schema;
|
|
1088
|
-
let changed = false;
|
|
1089
|
-
const normalized = Object.fromEntries(Object.entries(record).map(([key, value]) => {
|
|
1090
|
-
const next = normalizeStrictOpenAIJsonSchemaRecursive(value, key === "properties" ? depth : depth + 1);
|
|
1091
|
-
changed ||= next !== value;
|
|
1092
|
-
return [key, next];
|
|
1093
|
-
}));
|
|
1094
|
-
if (normalized.type === "object") {
|
|
1095
|
-
const properties = isRecord(normalized.properties) ? normalized.properties : void 0;
|
|
1096
|
-
if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
|
|
1097
|
-
normalized.required = [];
|
|
1098
|
-
changed = true;
|
|
1099
|
-
}
|
|
1100
|
-
if (depth === 0 && !("additionalProperties" in normalized)) {
|
|
1101
|
-
normalized.additionalProperties = false;
|
|
1102
|
-
changed = true;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
return changed ? normalized : schema;
|
|
1106
|
-
}
|
|
1107
963
|
/** Normalizes tool parameters using strict OpenAI rules only when strict mode is active. */
|
|
1108
964
|
function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
|
|
1109
965
|
const toolSchemaCompat = resolveToolSchemaModelCompat(modelCompat);
|
|
@@ -1112,7 +968,7 @@ function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
|
|
|
1112
968
|
}
|
|
1113
969
|
/** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
|
|
1114
970
|
function isStrictOpenAIJsonSchemaCompatible(schema) {
|
|
1115
|
-
return
|
|
971
|
+
return findOpenAIStrictSchemaViolations(normalizeStrictOpenAIJsonSchema(schema), "parameters").length === 0;
|
|
1116
972
|
}
|
|
1117
973
|
/** Returns strict-schema diagnostics for an already materialized OpenAI tool projection. */
|
|
1118
974
|
function findOpenAIStrictToolProjectionDiagnostics(projection) {
|
|
@@ -1129,25 +985,6 @@ function findOpenAIStrictToolProjectionDiagnostics(projection) {
|
|
|
1129
985
|
}] : [];
|
|
1130
986
|
})];
|
|
1131
987
|
}
|
|
1132
|
-
function isStrictOpenAIJsonSchemaCompatibleRecursive(schema) {
|
|
1133
|
-
if (Array.isArray(schema)) return schema.every((entry) => isStrictOpenAIJsonSchemaCompatibleRecursive(entry));
|
|
1134
|
-
if (!schema || typeof schema !== "object") return true;
|
|
1135
|
-
const record = schema;
|
|
1136
|
-
if ("anyOf" in record || "oneOf" in record || "allOf" in record) return false;
|
|
1137
|
-
if (Array.isArray(record.type)) return false;
|
|
1138
|
-
if (record.type === "object" && record.additionalProperties !== false) return false;
|
|
1139
|
-
if (record.type === "object") {
|
|
1140
|
-
const properties = isRecord(record.properties) ? record.properties : {};
|
|
1141
|
-
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
1142
|
-
if (!required) return false;
|
|
1143
|
-
const requiredSet = new Set(required);
|
|
1144
|
-
if (Object.keys(properties).some((key) => !requiredSet.has(key))) return false;
|
|
1145
|
-
}
|
|
1146
|
-
return Object.entries(record).every(([key, entry]) => {
|
|
1147
|
-
if (key === "properties" && isRecord(entry)) return Object.values(entry).every(isStrictOpenAIJsonSchemaCompatibleRecursive);
|
|
1148
|
-
return isStrictOpenAIJsonSchemaCompatibleRecursive(entry);
|
|
1149
|
-
});
|
|
1150
|
-
}
|
|
1151
988
|
/** Resolves strict mode for the projected tools that will be emitted in the request payload. */
|
|
1152
989
|
function resolveOpenAIProjectedToolsStrictToolFlag(projection, strict) {
|
|
1153
990
|
if (strict !== true) return strict === false ? false : void 0;
|