@openclaw/ai 2026.8.1-beta.2 → 2026.8.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/dist/{anthropic-B6dLpq5L.mjs → anthropic-Djj7L1rx.mjs} +49 -39
- package/dist/{anthropic-compaction-replay-8lJNKXOE.mjs → anthropic-compaction-replay-CY4tkD4V.mjs} +42 -44
- package/dist/{anthropic-payload-policy-CiEuQS72.d.mts → anthropic-payload-policy-i_5_inWe.d.mts} +4 -2
- package/dist/{api-registry-k3zTz0cV.d.mts → api-registry-C9-wZd_W.d.mts} +2 -1
- package/dist/{azure-openai-responses-mxIOtUnn.mjs → azure-openai-responses-Bj3czHNa.mjs} +4 -4
- package/dist/{number-coercion-H9qHik3g.mjs → base64-CEFBpSkN.mjs} +73 -1
- package/dist/diagnostics.d.mts +2 -3
- package/dist/diagnostics.mjs +1 -1
- package/dist/{env-api-keys-DrgeBuva.mjs → env-api-keys-bktO00EJ.mjs} +2 -15
- package/dist/event-stream-C988TSLe.d.mts +1 -0
- package/dist/{event-stream-BP6AWT8j.d.mts → event-stream-UhnxHuqt.d.mts} +1 -2
- package/dist/event-stream.d.mts +2 -1
- package/dist/{google-C7h2QzDX.mjs → google-0Ca6TYbw.mjs} +4 -4
- package/dist/{google-shared-B0Qr9OR7.mjs → google-shared-DVfL9COU.mjs} +22 -8
- package/dist/{google-vertex-Bhc3TjDl.mjs → google-vertex-5dAMxprN.mjs} +4 -4
- package/dist/{host-DTqNc7ad.mjs → host-D51fmkH6.mjs} +40 -33
- package/dist/{host-vWgMMhiJ.d.mts → host-DoPJJ1KD.d.mts} +7 -7
- package/dist/index-N3zr6__t.d.mts +105 -0
- package/dist/index.d.mts +9 -10
- package/dist/index.mjs +2 -3
- package/dist/internal/anthropic.d.mts +57 -49
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/openai-responses-payload-policy.d.mts +3 -3
- package/dist/internal/openai-responses-payload-policy.mjs +2 -3
- package/dist/internal/openai.d.mts +65 -63
- package/dist/internal/openai.mjs +10 -8
- package/dist/internal/retry-after.d.mts +2 -3
- package/dist/internal/runtime.d.mts +53 -40
- package/dist/internal/runtime.mjs +10 -9
- package/dist/internal/shared.d.mts +37 -23
- package/dist/internal/shared.mjs +4 -4
- package/dist/{json-parse-CDnesDM_.mjs → json-parse-BuAJEbdW.mjs} +18 -2
- package/dist/{headers-DdOQtGuU.mjs → llm-request-activity-BjtkplhG.mjs} +1 -9
- package/dist/{mistral-CEWoQI_g.mjs → mistral-BCnrI-kV.mjs} +64 -53
- package/dist/{openai-chatgpt-responses-CrPmqERt.mjs → openai-chatgpt-responses-CGBGWSqr.mjs} +45 -20
- package/dist/{openai-completions-BPnt4Sml.mjs → openai-completions-BaTuwID7.mjs} +63 -258
- package/dist/{openai-completions-compat-Dt3dcawL.d.mts → openai-completions-compat-DB7FFq7k.d.mts} +3 -3
- package/dist/openai-completions-stream-Bpns6uNc.mjs +1622 -0
- package/dist/{openai-responses-DhIKtOup.mjs → openai-responses-1GBcQujk.mjs} +8 -6
- package/dist/openai-responses-compaction-window-DwydGZI8.mjs +686 -0
- package/dist/{openai-responses-contracts-XpZJxrRG.d.mts → openai-responses-contracts-DgaR3t5T.d.mts} +14 -5
- package/dist/{openai-responses-payload-policy-BSs371VM.d.mts → openai-responses-payload-policy-rLRPsSmB.d.mts} +2 -0
- package/dist/{openai-responses-prompt-observer-internal-DgNTYnRY.mjs → openai-responses-prompt-observer-internal-_nhGTn2b.mjs} +3 -3
- package/dist/{openai-responses-shared-DXIt3iY5.mjs → openai-responses-shared-zPmKnhF5.mjs} +345 -1553
- package/dist/openai-stop-reason-Drnn_6Qj.mjs +28 -0
- package/dist/openai-tool-schema-Dgl4H2MU.mjs +1695 -0
- package/dist/{provider-error-BUwEnjXq.mjs → provider-error-jZP5NMTZ.mjs} +3 -82
- package/dist/{provider-options-B96RdNpH.d.mts → provider-options-HvA_slFa.d.mts} +23 -4
- package/dist/provider-transcript-transform-C01bQ5a3.mjs +32 -0
- package/dist/provider-types.d.mts +18 -17
- package/dist/providers.d.mts +5 -6
- package/dist/providers.mjs +9 -9
- package/dist/{reasoning-tag-text-partitioner-rnPwX2pg.mjs → reasoning-tag-text-partitioner-CwzKb0bA.mjs} +4 -1
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{sanitize-unicode-BYqrYtC_.mjs → sanitize-unicode-DCkltN94.mjs} +1 -2
- package/dist/{simple-options-D58D5Kvw.mjs → simple-options-DKx86yX9.mjs} +42 -4
- package/dist/{anthropic-JsNA5KCu.mjs → src-Bu3FiFAG.mjs} +1 -1
- package/dist/{stream-first-event-timeout-MK28puvq.mjs → stream-first-event-timeout-DcNjoFQE.mjs} +1 -1
- package/dist/{streaming-byte-guard-BrbkbwUu.mjs → streaming-byte-guard-CC-HMn_u.mjs} +5 -6
- package/dist/string-normalization--fwJ4S2q.mjs +16 -0
- package/dist/{tool-schema-json-projection-q5d7QX5c.mjs → tool-schema-json-projection-qvTEs5Am.mjs} +17 -8
- package/dist/transport-stream-shared-CT5ycdzn.mjs +342 -0
- package/dist/transport-stream-shared-NjwWNaRY.d.mts +138 -0
- package/dist/{transport-utils-DJqkxbhC.mjs → transport-utils-BHx9z8Cl.mjs} +9 -9
- package/dist/transports.d.mts +140 -169
- package/dist/transports.mjs +269 -1262
- package/dist/types-DtGu2d3C.d.mts +1 -0
- package/dist/{types-BHNrPS1l.d.mts → types-h38Azne1.d.mts} +53 -17
- package/dist/types.d.mts +6 -5
- package/dist/types.mjs +1 -2
- package/dist/utf16-slice-qz3nsy87.mjs +84 -0
- package/dist/{validation-DT9SrFn3.d.mts → validation-C68lP2uy.d.mts} +1 -2
- package/dist/validation.d.mts +1 -1
- package/package.json +6 -6
- package/dist/anthropic-SrGtwsJu.d.mts +0 -100
- package/dist/index-BVVgDSdq.d.mts +0 -1
- package/dist/openai-responses-contracts-CyfIkQi5.mjs +0 -253
- package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +0 -206
- package/dist/openai-stop-reason-BkFkqqK0.mjs +0 -563
- package/dist/openai-tool-projection-CY04OcvQ.mjs +0 -338
- package/dist/provider-transcript-transform-ePx-Bbfr.mjs +0 -155
- package/dist/record-coerce-DdXsgUd_.mjs +0 -23
- package/dist/src-D2H6yKkH.mjs +0 -2
- package/dist/stream-first-event-timeout-DvDeSucC.d.mts +0 -29
- package/dist/string-coerce-fsri9iCu.mjs +0 -34
- package/dist/types-BVVgDSdq.d.mts +0 -1
- package/dist/utf16-slice-CvGodqok.mjs +0 -29
|
@@ -1,113 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as clampThinkingLevel, r as calculateCost } from "./sanitize-unicode-
|
|
3
|
-
import { a as describeToolResultMediaPlaceholder,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { n as notifyLlmRequestActivity } from "./headers-DdOQtGuU.mjs";
|
|
1
|
+
import { c as normalizeLowercaseStringOrEmpty, d as normalizeOptionalString, o as isRecord, t as truncateUtf16Safe } from "./utf16-slice-qz3nsy87.mjs";
|
|
2
|
+
import { i as clampThinkingLevel, r as calculateCost } from "./sanitize-unicode-DCkltN94.mjs";
|
|
3
|
+
import { a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload, n as getAiTransportHost } from "./host-D51fmkH6.mjs";
|
|
4
|
+
import { n as projectProviderError } from "./provider-error-jZP5NMTZ.mjs";
|
|
5
|
+
import { r as asFiniteNumber } from "./base64-CEFBpSkN.mjs";
|
|
6
|
+
import { d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName } from "./simple-options-DKx86yX9.mjs";
|
|
7
|
+
import { c as transformTransportMessages } from "./tool-schema-json-projection-qvTEs5Am.mjs";
|
|
8
|
+
import { d as redactSensitiveText, u as redactIdentifier } from "./transport-utils-BHx9z8Cl.mjs";
|
|
9
|
+
import { _ as transportAbortError, g as sanitizeTransportPayloadText, h as sanitizeNonEmptyTransportPayloadText, m as parseTerminalToolCallArguments, y as withProviderResponseHook } from "./transport-stream-shared-CT5ycdzn.mjs";
|
|
10
|
+
import { r as parseStreamingJson, t as createToolArgumentPreviewSchedule } from "./json-parse-BuAJEbdW.mjs";
|
|
11
|
+
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
13
12
|
import { t as shortHash } from "./hash-CHgqbJmD.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
13
|
+
import { C as createOpenAIProviderAcceptanceHook, E as log, S as createModelStreamCooperativeScheduler, a as resolveOpenAIProjectedToolsStrictToolFlag, r as normalizeOpenAIStrictToolParameters, t as findOpenAIStrictToolProjectionDiagnostics, v as projectOpenAITools } from "./openai-tool-schema-Dgl4H2MU.mjs";
|
|
14
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-DcNjoFQE.mjs";
|
|
15
|
+
import { t as transformProviderMessages } from "./provider-transcript-transform-C01bQ5a3.mjs";
|
|
16
|
+
import { A as supportsOpenAITemperature, D as resolveOpenAIReasoningEffortForModel, b as RESPONSE_FAILED_NO_DETAILS_MESSAGE, g as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, h as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY, k as supportsOpenAIReasoningEffort, m as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, n as isOpenAIResponsesCompactionOutput, p as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, r as readOpenAIResponsesCompactionWindow } from "./openai-responses-compaction-window-DwydGZI8.mjs";
|
|
18
17
|
import { createHash, randomUUID } from "node:crypto";
|
|
19
|
-
//#region packages/ai/src/transports/json-unsafe-integers.ts
|
|
20
|
-
/**
|
|
21
|
-
* JSON parsing helpers that preserve integer literals larger than
|
|
22
|
-
* Number.MAX_SAFE_INTEGER as strings before JSON.parse can round them.
|
|
23
|
-
*/
|
|
24
|
-
const MAX_SAFE_INTEGER_ABS_STR = String(Number.MAX_SAFE_INTEGER);
|
|
25
|
-
function isAsciiDigit(ch) {
|
|
26
|
-
return ch !== void 0 && ch >= "0" && ch <= "9";
|
|
27
|
-
}
|
|
28
|
-
function parseJsonNumberToken(input, start) {
|
|
29
|
-
let idx = start;
|
|
30
|
-
if (input[idx] === "-") idx += 1;
|
|
31
|
-
if (idx >= input.length) return null;
|
|
32
|
-
if (input[idx] === "0") idx += 1;
|
|
33
|
-
else if (isAsciiDigit(input[idx]) && input[idx] !== "0") while (isAsciiDigit(input[idx])) idx += 1;
|
|
34
|
-
else return null;
|
|
35
|
-
let isInteger = true;
|
|
36
|
-
if (input[idx] === ".") {
|
|
37
|
-
isInteger = false;
|
|
38
|
-
idx += 1;
|
|
39
|
-
if (!isAsciiDigit(input[idx])) return null;
|
|
40
|
-
while (isAsciiDigit(input[idx])) idx += 1;
|
|
41
|
-
}
|
|
42
|
-
if (input[idx] === "e" || input[idx] === "E") {
|
|
43
|
-
isInteger = false;
|
|
44
|
-
idx += 1;
|
|
45
|
-
if (input[idx] === "+" || input[idx] === "-") idx += 1;
|
|
46
|
-
if (!isAsciiDigit(input[idx])) return null;
|
|
47
|
-
while (isAsciiDigit(input[idx])) idx += 1;
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
token: input.slice(start, idx),
|
|
51
|
-
end: idx,
|
|
52
|
-
isInteger
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
function isUnsafeIntegerLiteral(token) {
|
|
56
|
-
const digits = token[0] === "-" ? token.slice(1) : token;
|
|
57
|
-
if (digits.length < MAX_SAFE_INTEGER_ABS_STR.length) return false;
|
|
58
|
-
if (digits.length > MAX_SAFE_INTEGER_ABS_STR.length) return true;
|
|
59
|
-
return digits > MAX_SAFE_INTEGER_ABS_STR;
|
|
60
|
-
}
|
|
61
|
-
/** Quotes integer literals above Number.MAX_SAFE_INTEGER before JSON.parse. */
|
|
62
|
-
function quoteUnsafeIntegerLiterals(input) {
|
|
63
|
-
let out = "";
|
|
64
|
-
let inString = false;
|
|
65
|
-
let escaped = false;
|
|
66
|
-
let idx = 0;
|
|
67
|
-
while (idx < input.length) {
|
|
68
|
-
const ch = input[idx] ?? "";
|
|
69
|
-
if (inString) {
|
|
70
|
-
out += ch;
|
|
71
|
-
if (escaped) escaped = false;
|
|
72
|
-
else if (ch === "\\") escaped = true;
|
|
73
|
-
else if (ch === "\"") inString = false;
|
|
74
|
-
idx += 1;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (ch === "\"") {
|
|
78
|
-
inString = true;
|
|
79
|
-
out += ch;
|
|
80
|
-
idx += 1;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (ch === "-" || isAsciiDigit(ch)) {
|
|
84
|
-
const parsed = parseJsonNumberToken(input, idx);
|
|
85
|
-
if (parsed) {
|
|
86
|
-
if (parsed.isInteger && isUnsafeIntegerLiteral(parsed.token)) out += `"${parsed.token}"`;
|
|
87
|
-
else out += parsed.token;
|
|
88
|
-
idx = parsed.end;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
out += ch;
|
|
93
|
-
idx += 1;
|
|
94
|
-
}
|
|
95
|
-
return out;
|
|
96
|
-
}
|
|
97
|
-
/** Parses JSON while preserving unsafe integer literals as strings. */
|
|
98
|
-
function parseJsonPreservingUnsafeIntegers(input) {
|
|
99
|
-
return JSON.parse(quoteUnsafeIntegerLiterals(input));
|
|
100
|
-
}
|
|
101
|
-
/** Parses or accepts an object while preserving unsafe integer literals in string input. */
|
|
102
|
-
function parseJsonObjectPreservingUnsafeIntegers(value) {
|
|
103
|
-
if (typeof value === "string") try {
|
|
104
|
-
return asNullableRecord(parseJsonPreservingUnsafeIntegers(value));
|
|
105
|
-
} catch {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
return asNullableRecord(value);
|
|
109
|
-
}
|
|
110
|
-
//#endregion
|
|
111
18
|
//#region packages/ai/src/transports/model-transport-debug.ts
|
|
112
19
|
/**
|
|
113
20
|
* Environment-driven debug controls for model transport logging.
|
|
@@ -149,1267 +56,6 @@ function emitModelTransportDebug(log, message) {
|
|
|
149
56
|
log.debug(message);
|
|
150
57
|
}
|
|
151
58
|
//#endregion
|
|
152
|
-
//#region packages/ai/src/providers/clean-for-gemini.ts
|
|
153
|
-
const GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
154
|
-
"patternProperties",
|
|
155
|
-
"additionalProperties",
|
|
156
|
-
"$schema",
|
|
157
|
-
"$id",
|
|
158
|
-
"$ref",
|
|
159
|
-
"$defs",
|
|
160
|
-
"definitions",
|
|
161
|
-
"examples",
|
|
162
|
-
"minLength",
|
|
163
|
-
"maxLength",
|
|
164
|
-
"minimum",
|
|
165
|
-
"maximum",
|
|
166
|
-
"multipleOf",
|
|
167
|
-
"pattern",
|
|
168
|
-
"format",
|
|
169
|
-
"minItems",
|
|
170
|
-
"maxItems",
|
|
171
|
-
"uniqueItems",
|
|
172
|
-
"minProperties",
|
|
173
|
-
"maxProperties",
|
|
174
|
-
"not"
|
|
175
|
-
]);
|
|
176
|
-
const SCHEMA_META_KEYS = [
|
|
177
|
-
"description",
|
|
178
|
-
"title",
|
|
179
|
-
"default"
|
|
180
|
-
];
|
|
181
|
-
function copySchemaMeta$1(from, to) {
|
|
182
|
-
for (const key of SCHEMA_META_KEYS) if (key in from && from[key] !== void 0) to[key] = from[key];
|
|
183
|
-
}
|
|
184
|
-
function stringifyGeminiEnumValue(value) {
|
|
185
|
-
if (typeof value === "string") return value;
|
|
186
|
-
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
187
|
-
if (typeof value === "boolean") return String(value);
|
|
188
|
-
}
|
|
189
|
-
function cleanGeminiEnumValues(value) {
|
|
190
|
-
if (!Array.isArray(value)) return;
|
|
191
|
-
const values = value.flatMap((entry) => {
|
|
192
|
-
const stringified = stringifyGeminiEnumValue(entry);
|
|
193
|
-
return stringified === void 0 ? [] : [stringified];
|
|
194
|
-
});
|
|
195
|
-
const unique = [...new Set(values)];
|
|
196
|
-
return unique.length > 0 ? unique : void 0;
|
|
197
|
-
}
|
|
198
|
-
function tryFlattenLiteralAnyOf(variants) {
|
|
199
|
-
if (variants.length === 0) return null;
|
|
200
|
-
const allValues = [];
|
|
201
|
-
let commonType = null;
|
|
202
|
-
for (const variant of variants) {
|
|
203
|
-
if (!variant || typeof variant !== "object") return null;
|
|
204
|
-
const v = variant;
|
|
205
|
-
let literalValue;
|
|
206
|
-
if ("const" in v) literalValue = v.const;
|
|
207
|
-
else if (Array.isArray(v.enum) && v.enum.length === 1) literalValue = v.enum[0];
|
|
208
|
-
else return null;
|
|
209
|
-
const variantType = typeof v.type === "string" ? v.type : null;
|
|
210
|
-
if (!variantType) return null;
|
|
211
|
-
if (commonType === null) commonType = variantType;
|
|
212
|
-
else if (commonType !== variantType) return null;
|
|
213
|
-
allValues.push(literalValue);
|
|
214
|
-
}
|
|
215
|
-
if (commonType && allValues.length > 0) return {
|
|
216
|
-
type: commonType,
|
|
217
|
-
enum: allValues
|
|
218
|
-
};
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
function isNullSchema(variant) {
|
|
222
|
-
if (!variant || typeof variant !== "object" || Array.isArray(variant)) return false;
|
|
223
|
-
const record = variant;
|
|
224
|
-
if ("const" in record && record.const === null) return true;
|
|
225
|
-
if (Array.isArray(record.enum) && record.enum.length === 1) return record.enum[0] === null;
|
|
226
|
-
const typeValue = record.type;
|
|
227
|
-
if (typeValue === "null") return true;
|
|
228
|
-
if (Array.isArray(typeValue) && typeValue.length === 1 && typeValue[0] === "null") return true;
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
function stripNullVariants(variants) {
|
|
232
|
-
if (variants.length === 0) return {
|
|
233
|
-
variants,
|
|
234
|
-
stripped: false
|
|
235
|
-
};
|
|
236
|
-
const nonNull = variants.filter((variant) => !isNullSchema(variant));
|
|
237
|
-
return {
|
|
238
|
-
variants: nonNull,
|
|
239
|
-
stripped: nonNull.length !== variants.length
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
function extendSchemaDefs$1(defs, schema) {
|
|
243
|
-
const defsEntry = schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs) ? schema.$defs : void 0;
|
|
244
|
-
const legacyDefsEntry = schema.definitions && typeof schema.definitions === "object" && !Array.isArray(schema.definitions) ? schema.definitions : void 0;
|
|
245
|
-
if (!defsEntry && !legacyDefsEntry) return defs;
|
|
246
|
-
const next = defs ? new Map(defs) : /* @__PURE__ */ new Map();
|
|
247
|
-
if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.set(key, value);
|
|
248
|
-
if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.set(key, value);
|
|
249
|
-
return next;
|
|
250
|
-
}
|
|
251
|
-
function decodeJsonPointerSegment$1(segment) {
|
|
252
|
-
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
253
|
-
}
|
|
254
|
-
function tryResolveLocalRef$1(ref, defs) {
|
|
255
|
-
if (!defs) return;
|
|
256
|
-
const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
257
|
-
if (!match) return;
|
|
258
|
-
const name = decodeJsonPointerSegment$1(match[1] ?? "");
|
|
259
|
-
if (!name) return;
|
|
260
|
-
return defs.get(name);
|
|
261
|
-
}
|
|
262
|
-
function simplifyUnionVariants(params) {
|
|
263
|
-
const { obj, variants } = params;
|
|
264
|
-
const { variants: nonNullVariants, stripped } = stripNullVariants(variants);
|
|
265
|
-
const flattened = tryFlattenLiteralAnyOf(nonNullVariants);
|
|
266
|
-
if (flattened) {
|
|
267
|
-
const result = {
|
|
268
|
-
type: flattened.type,
|
|
269
|
-
enum: flattened.enum
|
|
270
|
-
};
|
|
271
|
-
copySchemaMeta$1(obj, result);
|
|
272
|
-
return {
|
|
273
|
-
variants: nonNullVariants,
|
|
274
|
-
simplified: result
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
if (stripped && nonNullVariants.length === 1) {
|
|
278
|
-
const lone = nonNullVariants[0];
|
|
279
|
-
if (lone && typeof lone === "object" && !Array.isArray(lone)) {
|
|
280
|
-
const result = { ...lone };
|
|
281
|
-
copySchemaMeta$1(obj, result);
|
|
282
|
-
return {
|
|
283
|
-
variants: nonNullVariants,
|
|
284
|
-
simplified: result
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
return {
|
|
288
|
-
variants: nonNullVariants,
|
|
289
|
-
simplified: lone
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
return { variants: stripped ? nonNullVariants : variants };
|
|
293
|
-
}
|
|
294
|
-
function sanitizeRequiredFields(schema) {
|
|
295
|
-
if (!Array.isArray(schema.required)) return schema;
|
|
296
|
-
if (!schema.properties || typeof schema.properties !== "object" || Array.isArray(schema.properties)) {
|
|
297
|
-
if (schema.type === "object") delete schema.required;
|
|
298
|
-
return schema;
|
|
299
|
-
}
|
|
300
|
-
const properties = schema.properties;
|
|
301
|
-
const required = schema.required.filter((key) => typeof key === "string" && Object.hasOwn(properties, key));
|
|
302
|
-
if (required.length > 0) schema.required = required;
|
|
303
|
-
else delete schema.required;
|
|
304
|
-
return schema;
|
|
305
|
-
}
|
|
306
|
-
function cleanSchemaForGeminiWithDefs(schema, defs, refStack) {
|
|
307
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
308
|
-
if (Array.isArray(schema)) return schema.map((item) => cleanSchemaForGeminiWithDefs(item, defs, refStack));
|
|
309
|
-
const obj = schema;
|
|
310
|
-
const nextDefs = extendSchemaDefs$1(defs, obj);
|
|
311
|
-
const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
|
|
312
|
-
if (refValue) {
|
|
313
|
-
if (refStack?.has(refValue)) return {};
|
|
314
|
-
const resolved = tryResolveLocalRef$1(refValue, nextDefs);
|
|
315
|
-
if (resolved) {
|
|
316
|
-
const nextRefStack = refStack ? new Set(refStack) : /* @__PURE__ */ new Set();
|
|
317
|
-
nextRefStack.add(refValue);
|
|
318
|
-
const cleaned = cleanSchemaForGeminiWithDefs(resolved, nextDefs, nextRefStack);
|
|
319
|
-
if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) return cleaned;
|
|
320
|
-
const result = { ...cleaned };
|
|
321
|
-
copySchemaMeta$1(obj, result);
|
|
322
|
-
return result;
|
|
323
|
-
}
|
|
324
|
-
const result = {};
|
|
325
|
-
copySchemaMeta$1(obj, result);
|
|
326
|
-
return result;
|
|
327
|
-
}
|
|
328
|
-
const hasAnyOf = "anyOf" in obj && Array.isArray(obj.anyOf);
|
|
329
|
-
const hasOneOf = "oneOf" in obj && Array.isArray(obj.oneOf);
|
|
330
|
-
let cleanedAnyOf = hasAnyOf ? obj.anyOf.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack)) : void 0;
|
|
331
|
-
let cleanedOneOf = hasOneOf ? obj.oneOf.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack)) : void 0;
|
|
332
|
-
if (hasAnyOf) {
|
|
333
|
-
const simplified = simplifyUnionVariants({
|
|
334
|
-
obj,
|
|
335
|
-
variants: cleanedAnyOf ?? []
|
|
336
|
-
});
|
|
337
|
-
cleanedAnyOf = simplified.variants;
|
|
338
|
-
if ("simplified" in simplified) return simplified.simplified;
|
|
339
|
-
}
|
|
340
|
-
if (hasOneOf) {
|
|
341
|
-
const simplified = simplifyUnionVariants({
|
|
342
|
-
obj,
|
|
343
|
-
variants: cleanedOneOf ?? []
|
|
344
|
-
});
|
|
345
|
-
cleanedOneOf = simplified.variants;
|
|
346
|
-
if ("simplified" in simplified) return simplified.simplified;
|
|
347
|
-
}
|
|
348
|
-
const cleaned = {};
|
|
349
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
350
|
-
if (GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS.has(key)) continue;
|
|
351
|
-
if (key === "const") {
|
|
352
|
-
const enumValues = cleanGeminiEnumValues([value]);
|
|
353
|
-
if (enumValues) cleaned.enum = enumValues;
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
if (key === "enum") {
|
|
357
|
-
const enumValues = cleanGeminiEnumValues(value);
|
|
358
|
-
if (enumValues) cleaned.enum = enumValues;
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
if (key === "required" && Array.isArray(value) && value.length === 0) continue;
|
|
362
|
-
if (key === "type" && (hasAnyOf || hasOneOf)) continue;
|
|
363
|
-
if (key === "type" && Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
364
|
-
const types = value.filter((entry) => entry !== "null");
|
|
365
|
-
cleaned.type = types.length === 1 ? types[0] : types;
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
if (key === "properties") if (value && typeof value === "object" && !Array.isArray(value)) cleaned[key] = Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cleanSchemaForGeminiWithDefs(v, nextDefs, refStack)]));
|
|
369
|
-
else cleaned[key] = {};
|
|
370
|
-
else if (key === "items" && value) if (Array.isArray(value)) cleaned[key] = value.map((entry) => cleanSchemaForGeminiWithDefs(entry, nextDefs, refStack));
|
|
371
|
-
else if (typeof value === "object") cleaned[key] = cleanSchemaForGeminiWithDefs(value, nextDefs, refStack);
|
|
372
|
-
else cleaned[key] = value;
|
|
373
|
-
else if (key === "anyOf" && Array.isArray(value)) cleaned[key] = cleanedAnyOf ?? value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
|
|
374
|
-
else if (key === "oneOf" && Array.isArray(value)) cleaned[key] = cleanedOneOf ?? value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
|
|
375
|
-
else if (key === "allOf" && Array.isArray(value)) cleaned[key] = value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
|
|
376
|
-
else cleaned[key] = value;
|
|
377
|
-
}
|
|
378
|
-
if (cleaned.anyOf && Array.isArray(cleaned.anyOf)) {
|
|
379
|
-
const flattened = flattenUnionFallback(cleaned, cleaned.anyOf);
|
|
380
|
-
if (flattened) return sanitizeRequiredFields(flattened);
|
|
381
|
-
}
|
|
382
|
-
if (cleaned.oneOf && Array.isArray(cleaned.oneOf)) {
|
|
383
|
-
const flattened = flattenUnionFallback(cleaned, cleaned.oneOf);
|
|
384
|
-
if (flattened) return sanitizeRequiredFields(flattened);
|
|
385
|
-
}
|
|
386
|
-
return sanitizeRequiredFields(cleaned);
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Last-resort flattening for anyOf/oneOf arrays that could not be simplified
|
|
390
|
-
* by `simplifyUnionVariants`. Picks a representative type so the schema is
|
|
391
|
-
* accepted by Google's restricted JSON Schema validation.
|
|
392
|
-
*/
|
|
393
|
-
function flattenUnionFallback(obj, variants) {
|
|
394
|
-
const objects = variants.filter((v) => Boolean(v) && typeof v === "object");
|
|
395
|
-
if (objects.length === 0) return;
|
|
396
|
-
const types = new Set(objects.map((v) => v.type).filter(Boolean));
|
|
397
|
-
if (objects.length === 1) {
|
|
398
|
-
const merged = { ...objects[0] };
|
|
399
|
-
copySchemaMeta$1(obj, merged);
|
|
400
|
-
return merged;
|
|
401
|
-
}
|
|
402
|
-
if (types.size === 1) {
|
|
403
|
-
const merged = { type: Array.from(types)[0] };
|
|
404
|
-
copySchemaMeta$1(obj, merged);
|
|
405
|
-
return merged;
|
|
406
|
-
}
|
|
407
|
-
const first = objects[0];
|
|
408
|
-
if (first?.type) {
|
|
409
|
-
const merged = { type: first.type };
|
|
410
|
-
copySchemaMeta$1(obj, merged);
|
|
411
|
-
return merged;
|
|
412
|
-
}
|
|
413
|
-
const merged = {};
|
|
414
|
-
copySchemaMeta$1(obj, merged);
|
|
415
|
-
return merged;
|
|
416
|
-
}
|
|
417
|
-
function cleanSchemaForGemini(schema) {
|
|
418
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
419
|
-
if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);
|
|
420
|
-
return cleanSchemaForGeminiWithDefs(schema, extendSchemaDefs$1(void 0, schema), void 0);
|
|
421
|
-
}
|
|
422
|
-
//#endregion
|
|
423
|
-
//#region packages/ai/src/providers/clean-for-llamacpp-gbnf.ts
|
|
424
|
-
/** llama.cpp rejects grammar repetitions whose expanded rule count reaches 2000. */
|
|
425
|
-
const LLAMACPP_GBNF_MAX_REPETITION_THRESHOLD = 2e3;
|
|
426
|
-
const SCHEMA_MAP_KEYS$2 = /* @__PURE__ */ new Set([
|
|
427
|
-
"$defs",
|
|
428
|
-
"definitions",
|
|
429
|
-
"dependentSchemas",
|
|
430
|
-
"patternProperties",
|
|
431
|
-
"properties"
|
|
432
|
-
]);
|
|
433
|
-
const SCHEMA_CHILD_KEYS = /* @__PURE__ */ new Set([
|
|
434
|
-
"additionalItems",
|
|
435
|
-
"additionalProperties",
|
|
436
|
-
"allOf",
|
|
437
|
-
"anyOf",
|
|
438
|
-
"contains",
|
|
439
|
-
"else",
|
|
440
|
-
"if",
|
|
441
|
-
"items",
|
|
442
|
-
"not",
|
|
443
|
-
"oneOf",
|
|
444
|
-
"prefixItems",
|
|
445
|
-
"propertyNames",
|
|
446
|
-
"then",
|
|
447
|
-
"unevaluatedItems",
|
|
448
|
-
"unevaluatedProperties"
|
|
449
|
-
]);
|
|
450
|
-
function cleanSchemaNode(node) {
|
|
451
|
-
if (Array.isArray(node)) {
|
|
452
|
-
let changed = false;
|
|
453
|
-
const entries = node.map((entry) => {
|
|
454
|
-
const next = cleanSchemaNode(entry);
|
|
455
|
-
changed ||= next !== entry;
|
|
456
|
-
return next;
|
|
457
|
-
});
|
|
458
|
-
return changed ? entries : node;
|
|
459
|
-
}
|
|
460
|
-
if (!isRecord(node)) return node;
|
|
461
|
-
let changed = false;
|
|
462
|
-
const cleaned = {};
|
|
463
|
-
for (const [key, value] of Object.entries(node)) {
|
|
464
|
-
if (key === "pattern") {
|
|
465
|
-
changed = true;
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
if (key === "maxLength" && typeof value === "number" && value >= 2e3) {
|
|
469
|
-
changed = true;
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
let next = value;
|
|
473
|
-
if (SCHEMA_MAP_KEYS$2.has(key) && isRecord(value)) {
|
|
474
|
-
let mapChanged = false;
|
|
475
|
-
next = Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => {
|
|
476
|
-
const cleanedChild = cleanSchemaNode(childValue);
|
|
477
|
-
mapChanged ||= cleanedChild !== childValue;
|
|
478
|
-
return [childKey, cleanedChild];
|
|
479
|
-
}));
|
|
480
|
-
if (!mapChanged) next = value;
|
|
481
|
-
} else if (SCHEMA_CHILD_KEYS.has(key)) next = cleanSchemaNode(value);
|
|
482
|
-
cleaned[key] = next;
|
|
483
|
-
changed ||= next !== value;
|
|
484
|
-
}
|
|
485
|
-
return changed ? cleaned : node;
|
|
486
|
-
}
|
|
487
|
-
function collectSchemaViolations(node, path, violations) {
|
|
488
|
-
if (Array.isArray(node)) {
|
|
489
|
-
node.forEach((entry, index) => collectSchemaViolations(entry, `${path}[${index}]`, violations));
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
if (!isRecord(node)) return;
|
|
493
|
-
if ("pattern" in node) violations.push(`${path}.pattern`);
|
|
494
|
-
if (typeof node.maxLength === "number" && node.maxLength >= 2e3) violations.push(`${path}.maxLength`);
|
|
495
|
-
for (const [key, value] of Object.entries(node)) if (SCHEMA_MAP_KEYS$2.has(key) && isRecord(value)) for (const [childKey, childValue] of Object.entries(value)) collectSchemaViolations(childValue, `${path}.${key}.${childKey}`, violations);
|
|
496
|
-
else if (SCHEMA_CHILD_KEYS.has(key)) collectSchemaViolations(value, `${path}.${key}`, violations);
|
|
497
|
-
}
|
|
498
|
-
/** Removes JSON Schema constraints that llama.cpp cannot compile into GBNF. */
|
|
499
|
-
function cleanSchemaForLlamacppGbnf(schema) {
|
|
500
|
-
return cleanSchemaNode(schema);
|
|
501
|
-
}
|
|
502
|
-
/** Reports schema paths that llama.cpp cannot compile into GBNF. */
|
|
503
|
-
function findLlamacppGbnfSchemaViolations(schema, path) {
|
|
504
|
-
const violations = [];
|
|
505
|
-
collectSchemaViolations(schema, path, violations);
|
|
506
|
-
return violations;
|
|
507
|
-
}
|
|
508
|
-
//#endregion
|
|
509
|
-
//#region packages/ai/src/providers/schema-keyword-strip.ts
|
|
510
|
-
const SCHEMA_MAP_KEYS$1 = /* @__PURE__ */ new Set([
|
|
511
|
-
"$defs",
|
|
512
|
-
"definitions",
|
|
513
|
-
"dependentSchemas",
|
|
514
|
-
"dependencies",
|
|
515
|
-
"patternProperties",
|
|
516
|
-
"properties"
|
|
517
|
-
]);
|
|
518
|
-
/** Containers whose value is a single nested schema. */
|
|
519
|
-
const SCHEMA_OBJECT_KEYS$1 = /* @__PURE__ */ new Set([
|
|
520
|
-
"additionalItems",
|
|
521
|
-
"additionalProperties",
|
|
522
|
-
"contains",
|
|
523
|
-
"contentSchema",
|
|
524
|
-
"else",
|
|
525
|
-
"if",
|
|
526
|
-
"items",
|
|
527
|
-
"not",
|
|
528
|
-
"propertyNames",
|
|
529
|
-
"then",
|
|
530
|
-
"unevaluatedItems",
|
|
531
|
-
"unevaluatedProperties"
|
|
532
|
-
]);
|
|
533
|
-
/** Containers whose value is a list of nested schemas. */
|
|
534
|
-
const SCHEMA_ARRAY_KEYS$1 = /* @__PURE__ */ new Set([
|
|
535
|
-
"allOf",
|
|
536
|
-
"anyOf",
|
|
537
|
-
"items",
|
|
538
|
-
"oneOf",
|
|
539
|
-
"prefixItems"
|
|
540
|
-
]);
|
|
541
|
-
/** Recursively remove schema keywords unsupported by a target provider/tool surface. */
|
|
542
|
-
function stripUnsupportedSchemaKeywords(schema, unsupportedKeywords) {
|
|
543
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
544
|
-
if (Array.isArray(schema)) return schema.map((entry) => stripUnsupportedSchemaKeywords(entry, unsupportedKeywords));
|
|
545
|
-
const obj = schema;
|
|
546
|
-
const cleaned = {};
|
|
547
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
548
|
-
if (unsupportedKeywords.has(key)) continue;
|
|
549
|
-
if (SCHEMA_MAP_KEYS$1.has(key) && value && typeof value === "object" && !Array.isArray(value)) {
|
|
550
|
-
cleaned[key] = Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => [childKey, stripUnsupportedSchemaKeywords(childValue, unsupportedKeywords)]));
|
|
551
|
-
continue;
|
|
552
|
-
}
|
|
553
|
-
if (SCHEMA_ARRAY_KEYS$1.has(key) && Array.isArray(value)) {
|
|
554
|
-
cleaned[key] = value.map((entry) => stripUnsupportedSchemaKeywords(entry, unsupportedKeywords));
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
if (SCHEMA_OBJECT_KEYS$1.has(key) && value && typeof value === "object") {
|
|
558
|
-
cleaned[key] = stripUnsupportedSchemaKeywords(value, unsupportedKeywords);
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
cleaned[key] = value;
|
|
562
|
-
}
|
|
563
|
-
return cleaned;
|
|
564
|
-
}
|
|
565
|
-
//#endregion
|
|
566
|
-
//#region packages/ai/src/providers/agent-tools-parameter-schema.ts
|
|
567
|
-
/**
|
|
568
|
-
* Normalizes model-facing tool parameter schemas across provider quirks.
|
|
569
|
-
* Handles local JSON Schema refs, OpenAPI nullable syntax, top-level unions,
|
|
570
|
-
* and provider-specific unsupported keyword stripping.
|
|
571
|
-
*/
|
|
572
|
-
/** Extracts the compat record whether callers pass a model (`{ compat }`) or the compat itself. */
|
|
573
|
-
function extractToolSchemaModelCompat(modelOrCompat) {
|
|
574
|
-
if (!modelOrCompat || typeof modelOrCompat !== "object") return;
|
|
575
|
-
if ("compat" in modelOrCompat) {
|
|
576
|
-
const compat = modelOrCompat.compat;
|
|
577
|
-
return compat && typeof compat === "object" ? compat : void 0;
|
|
578
|
-
}
|
|
579
|
-
return modelOrCompat;
|
|
580
|
-
}
|
|
581
|
-
/** JSON Schema keywords this model/provider rejects in tool schemas. */
|
|
582
|
-
function resolveUnsupportedToolSchemaKeywords(modelOrCompat) {
|
|
583
|
-
const keywords = extractToolSchemaModelCompat(modelOrCompat)?.unsupportedToolSchemaKeywords ?? [];
|
|
584
|
-
return new Set(normalizeStringEntries(keywords.filter((keyword) => typeof keyword === "string")));
|
|
585
|
-
}
|
|
586
|
-
/** Whether empty `items: {}` on array schemas must be omitted for this model/provider. */
|
|
587
|
-
function shouldOmitEmptyArrayItems(modelOrCompat) {
|
|
588
|
-
return extractToolSchemaModelCompat(modelOrCompat)?.omitEmptyArrayItems === true;
|
|
589
|
-
}
|
|
590
|
-
const MAX_TOOL_PARAMETER_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
|
|
591
|
-
const toolParameterSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
592
|
-
function resolveToolParameterSchemaCacheKey(options) {
|
|
593
|
-
const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
|
|
594
|
-
const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
|
|
595
|
-
const toolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
|
|
596
|
-
const unsupportedKeywords = Array.from(resolveUnsupportedToolSchemaKeywords(options?.modelCompat)).toSorted();
|
|
597
|
-
const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
|
|
598
|
-
return JSON.stringify([
|
|
599
|
-
normalizedProvider,
|
|
600
|
-
normalizedModelId,
|
|
601
|
-
toolSchemaProfile,
|
|
602
|
-
unsupportedKeywords,
|
|
603
|
-
omitEmptyArrayItems
|
|
604
|
-
]);
|
|
605
|
-
}
|
|
606
|
-
function getCachedToolParameterSchema(schema, key) {
|
|
607
|
-
return toolParameterSchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
|
|
608
|
-
}
|
|
609
|
-
function rememberCachedToolParameterSchema(schema, key, value) {
|
|
610
|
-
const entries = toolParameterSchemaCache.get(schema) ?? [];
|
|
611
|
-
toolParameterSchemaCache.set(schema, [{
|
|
612
|
-
key,
|
|
613
|
-
value
|
|
614
|
-
}, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_TOOL_PARAMETER_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
|
|
615
|
-
return value;
|
|
616
|
-
}
|
|
617
|
-
function isGeminiModelId(modelId) {
|
|
618
|
-
return /(?:^|[/:])gemini(?:$|[-/:.])/.test(modelId);
|
|
619
|
-
}
|
|
620
|
-
function extractEnumValues(schema) {
|
|
621
|
-
if (!schema || typeof schema !== "object") return;
|
|
622
|
-
const record = schema;
|
|
623
|
-
if (Array.isArray(record.enum)) return record.enum;
|
|
624
|
-
if ("const" in record) return [record.const];
|
|
625
|
-
const variants = Array.isArray(record.anyOf) ? record.anyOf : Array.isArray(record.oneOf) ? record.oneOf : null;
|
|
626
|
-
if (variants) {
|
|
627
|
-
const values = variants.flatMap((variant) => {
|
|
628
|
-
return extractEnumValues(variant) ?? [];
|
|
629
|
-
});
|
|
630
|
-
return values.length > 0 ? values : void 0;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
function mergePropertySchemas(existing, incoming) {
|
|
634
|
-
if (!existing) return incoming;
|
|
635
|
-
if (!incoming) return existing;
|
|
636
|
-
const existingEnum = extractEnumValues(existing);
|
|
637
|
-
const incomingEnum = extractEnumValues(incoming);
|
|
638
|
-
if (existingEnum || incomingEnum) {
|
|
639
|
-
const values = uniqueValues([...existingEnum ?? [], ...incomingEnum ?? []]);
|
|
640
|
-
const merged = {};
|
|
641
|
-
for (const source of [existing, incoming]) {
|
|
642
|
-
if (!source || typeof source !== "object") continue;
|
|
643
|
-
const record = source;
|
|
644
|
-
for (const key of [
|
|
645
|
-
"title",
|
|
646
|
-
"description",
|
|
647
|
-
"default"
|
|
648
|
-
]) if (!(key in merged) && key in record) merged[key] = record[key];
|
|
649
|
-
}
|
|
650
|
-
const types = new Set(values.map((value) => typeof value));
|
|
651
|
-
if (types.size === 1) merged.type = Array.from(types)[0];
|
|
652
|
-
merged.enum = values;
|
|
653
|
-
return merged;
|
|
654
|
-
}
|
|
655
|
-
return existing;
|
|
656
|
-
}
|
|
657
|
-
function setOwnSchemaProperty(target, key, value) {
|
|
658
|
-
Object.defineProperty(target, key, {
|
|
659
|
-
value,
|
|
660
|
-
enumerable: true,
|
|
661
|
-
configurable: true,
|
|
662
|
-
writable: true
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
function hasTopLevelArrayKeyword(schemaRecord, key) {
|
|
666
|
-
return Array.isArray(schemaRecord[key]);
|
|
667
|
-
}
|
|
668
|
-
function getFlattenableVariantKey(schemaRecord) {
|
|
669
|
-
if (hasTopLevelArrayKeyword(schemaRecord, "anyOf")) return "anyOf";
|
|
670
|
-
if (hasTopLevelArrayKeyword(schemaRecord, "oneOf")) return "oneOf";
|
|
671
|
-
return null;
|
|
672
|
-
}
|
|
673
|
-
function getTopLevelConditionalKey(schemaRecord) {
|
|
674
|
-
return getFlattenableVariantKey(schemaRecord) ?? (hasTopLevelArrayKeyword(schemaRecord, "allOf") ? "allOf" : null);
|
|
675
|
-
}
|
|
676
|
-
function hasTopLevelObjectSchema(schemaRecord, conditionalKey) {
|
|
677
|
-
return schemaRecord.type === "object" && isRecord(schemaRecord.properties) && conditionalKey === null;
|
|
678
|
-
}
|
|
679
|
-
function isObjectLikeSchemaMissingType(schemaRecord, conditionalKey) {
|
|
680
|
-
return !("type" in schemaRecord) && (isRecord(schemaRecord.properties) || Array.isArray(schemaRecord.required)) && conditionalKey === null;
|
|
681
|
-
}
|
|
682
|
-
function isTypedObjectSchemaMissingValidProperties(schemaRecord, conditionalKey) {
|
|
683
|
-
return schemaRecord.type === "object" && !isRecord(schemaRecord.properties) && conditionalKey === null;
|
|
684
|
-
}
|
|
685
|
-
function isTrulyEmptySchema(schemaRecord) {
|
|
686
|
-
return Object.keys(schemaRecord).length === 0;
|
|
687
|
-
}
|
|
688
|
-
function normalizeArraySchemasMissingItems(schema) {
|
|
689
|
-
if (!isRecord(schema)) return schema;
|
|
690
|
-
let changed = false;
|
|
691
|
-
const nextSchema = { ...schema };
|
|
692
|
-
if (nextSchema.type === "array" && nextSchema.items === void 0) {
|
|
693
|
-
nextSchema.items = {};
|
|
694
|
-
changed = true;
|
|
695
|
-
}
|
|
696
|
-
const normalizeSchemaValue = (key) => {
|
|
697
|
-
if (!(key in nextSchema)) return;
|
|
698
|
-
const value = nextSchema[key];
|
|
699
|
-
if (Array.isArray(value)) {
|
|
700
|
-
const normalized = value.map(normalizeArraySchemasMissingItems);
|
|
701
|
-
if (normalized.some((entry, index) => entry !== value[index])) {
|
|
702
|
-
nextSchema[key] = normalized;
|
|
703
|
-
changed = true;
|
|
704
|
-
}
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
const normalized = normalizeArraySchemasMissingItems(value);
|
|
708
|
-
if (normalized !== value) {
|
|
709
|
-
nextSchema[key] = normalized;
|
|
710
|
-
changed = true;
|
|
711
|
-
}
|
|
712
|
-
};
|
|
713
|
-
for (const key of [
|
|
714
|
-
"items",
|
|
715
|
-
"contains",
|
|
716
|
-
"additionalProperties",
|
|
717
|
-
"propertyNames",
|
|
718
|
-
"not",
|
|
719
|
-
"if",
|
|
720
|
-
"then",
|
|
721
|
-
"else"
|
|
722
|
-
]) normalizeSchemaValue(key);
|
|
723
|
-
for (const key of [
|
|
724
|
-
"anyOf",
|
|
725
|
-
"oneOf",
|
|
726
|
-
"allOf",
|
|
727
|
-
"prefixItems"
|
|
728
|
-
]) normalizeSchemaValue(key);
|
|
729
|
-
for (const key of [
|
|
730
|
-
"properties",
|
|
731
|
-
"patternProperties",
|
|
732
|
-
"dependentSchemas",
|
|
733
|
-
"$defs",
|
|
734
|
-
"definitions"
|
|
735
|
-
]) {
|
|
736
|
-
const value = nextSchema[key];
|
|
737
|
-
if (!isRecord(value)) continue;
|
|
738
|
-
let entriesChanged = false;
|
|
739
|
-
const normalizedEntries = Object.entries(value).map(([entryKey, entryValue]) => {
|
|
740
|
-
const normalizedEntryValue = normalizeArraySchemasMissingItems(entryValue);
|
|
741
|
-
if (normalizedEntryValue !== entryValue) entriesChanged = true;
|
|
742
|
-
return [entryKey, normalizedEntryValue];
|
|
743
|
-
});
|
|
744
|
-
if (entriesChanged) {
|
|
745
|
-
nextSchema[key] = Object.fromEntries(normalizedEntries);
|
|
746
|
-
changed = true;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
return changed ? nextSchema : schema;
|
|
750
|
-
}
|
|
751
|
-
function schemaAllowsArrayType(schema) {
|
|
752
|
-
const type = schema.type;
|
|
753
|
-
return type === "array" || Array.isArray(type) && type.includes("array");
|
|
754
|
-
}
|
|
755
|
-
const ARRAY_ITEMS_SCHEMA_OBJECT_KEYS = /* @__PURE__ */ new Set([
|
|
756
|
-
"additionalProperties",
|
|
757
|
-
"contains",
|
|
758
|
-
"else",
|
|
759
|
-
"if",
|
|
760
|
-
"items",
|
|
761
|
-
"not",
|
|
762
|
-
"propertyNames",
|
|
763
|
-
"then"
|
|
764
|
-
]);
|
|
765
|
-
const ARRAY_ITEMS_SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set([
|
|
766
|
-
"allOf",
|
|
767
|
-
"anyOf",
|
|
768
|
-
"oneOf",
|
|
769
|
-
"prefixItems"
|
|
770
|
-
]);
|
|
771
|
-
const ARRAY_ITEMS_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
772
|
-
"$defs",
|
|
773
|
-
"definitions",
|
|
774
|
-
"dependentSchemas",
|
|
775
|
-
"patternProperties",
|
|
776
|
-
"properties"
|
|
777
|
-
]);
|
|
778
|
-
function stripEmptyArrayItemsFromArraySchemas(schema) {
|
|
779
|
-
if (Array.isArray(schema)) {
|
|
780
|
-
let changed = false;
|
|
781
|
-
const entries = schema.map((entry) => {
|
|
782
|
-
const next = stripEmptyArrayItemsFromArraySchemas(entry);
|
|
783
|
-
changed ||= next !== entry;
|
|
784
|
-
return next;
|
|
785
|
-
});
|
|
786
|
-
return changed ? entries : schema;
|
|
787
|
-
}
|
|
788
|
-
if (!isRecord(schema)) return schema;
|
|
789
|
-
let changed = false;
|
|
790
|
-
const entries = Object.entries(schema).flatMap(([key, value]) => {
|
|
791
|
-
if (key === "items" && schemaAllowsArrayType(schema) && isRecord(value) && isTrulyEmptySchema(value)) {
|
|
792
|
-
changed = true;
|
|
793
|
-
return [];
|
|
794
|
-
}
|
|
795
|
-
if (ARRAY_ITEMS_SCHEMA_OBJECT_KEYS.has(key)) {
|
|
796
|
-
const next = stripEmptyArrayItemsFromArraySchemas(value);
|
|
797
|
-
changed ||= next !== value;
|
|
798
|
-
return [[key, next]];
|
|
799
|
-
}
|
|
800
|
-
if (ARRAY_ITEMS_SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
801
|
-
const next = stripEmptyArrayItemsFromArraySchemas(value);
|
|
802
|
-
changed ||= next !== value;
|
|
803
|
-
return [[key, next]];
|
|
804
|
-
}
|
|
805
|
-
if (ARRAY_ITEMS_SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
806
|
-
let mapChanged = false;
|
|
807
|
-
const next = Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => {
|
|
808
|
-
const entryNext = stripEmptyArrayItemsFromArraySchemas(entryValue);
|
|
809
|
-
mapChanged ||= entryNext !== entryValue;
|
|
810
|
-
return [entryKey, entryNext];
|
|
811
|
-
}));
|
|
812
|
-
changed ||= mapChanged;
|
|
813
|
-
return [[key, mapChanged ? next : value]];
|
|
814
|
-
}
|
|
815
|
-
return [[key, value]];
|
|
816
|
-
});
|
|
817
|
-
return changed ? Object.fromEntries(entries) : schema;
|
|
818
|
-
}
|
|
819
|
-
function copySchemaMeta(from, to) {
|
|
820
|
-
for (const key of [
|
|
821
|
-
"title",
|
|
822
|
-
"description",
|
|
823
|
-
"default"
|
|
824
|
-
]) if (key in from && from[key] !== void 0) to[key] = from[key];
|
|
825
|
-
}
|
|
826
|
-
function extendSchemaDefs(defs, schema) {
|
|
827
|
-
const defsEntry = schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs) ? schema.$defs : void 0;
|
|
828
|
-
const legacyDefsEntry = schema.definitions && typeof schema.definitions === "object" && !Array.isArray(schema.definitions) ? schema.definitions : void 0;
|
|
829
|
-
if (!defsEntry && !legacyDefsEntry) return defs;
|
|
830
|
-
const next = defs ? {
|
|
831
|
-
$defs: new Map(defs.$defs),
|
|
832
|
-
definitions: new Map(defs.definitions)
|
|
833
|
-
} : {
|
|
834
|
-
$defs: /* @__PURE__ */ new Map(),
|
|
835
|
-
definitions: /* @__PURE__ */ new Map()
|
|
836
|
-
};
|
|
837
|
-
if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.$defs.set(key, value);
|
|
838
|
-
if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.definitions.set(key, value);
|
|
839
|
-
return next;
|
|
840
|
-
}
|
|
841
|
-
function decodeJsonPointerSegment(segment) {
|
|
842
|
-
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
843
|
-
}
|
|
844
|
-
function resolveJsonPointerPath(value, segments) {
|
|
845
|
-
let current = value;
|
|
846
|
-
for (const segment of segments) {
|
|
847
|
-
if (!current || typeof current !== "object") return;
|
|
848
|
-
const key = decodeJsonPointerSegment(segment);
|
|
849
|
-
if (Array.isArray(current)) {
|
|
850
|
-
const index = /^(?:0|[1-9]\d*)$/.test(key) ? Number(key) : -1;
|
|
851
|
-
if (index < 0 || index >= current.length) return;
|
|
852
|
-
current = current[index];
|
|
853
|
-
continue;
|
|
854
|
-
}
|
|
855
|
-
const record = current;
|
|
856
|
-
if (!Object.hasOwn(record, key)) return;
|
|
857
|
-
current = record[key];
|
|
858
|
-
}
|
|
859
|
-
return current;
|
|
860
|
-
}
|
|
861
|
-
function resolveLocalJsonPointer(rootDocument, ref) {
|
|
862
|
-
if (!ref.startsWith("#/")) return;
|
|
863
|
-
return resolveJsonPointerPath(rootDocument, ref.slice(2).split("/"));
|
|
864
|
-
}
|
|
865
|
-
const SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
866
|
-
"$defs",
|
|
867
|
-
"definitions",
|
|
868
|
-
"dependentSchemas",
|
|
869
|
-
"patternProperties",
|
|
870
|
-
"properties"
|
|
871
|
-
]);
|
|
872
|
-
const SCHEMA_OBJECT_KEYS = /* @__PURE__ */ new Set([
|
|
873
|
-
"additionalProperties",
|
|
874
|
-
"contains",
|
|
875
|
-
"else",
|
|
876
|
-
"if",
|
|
877
|
-
"items",
|
|
878
|
-
"not",
|
|
879
|
-
"propertyNames",
|
|
880
|
-
"then"
|
|
881
|
-
]);
|
|
882
|
-
const SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set([
|
|
883
|
-
"allOf",
|
|
884
|
-
"anyOf",
|
|
885
|
-
"items",
|
|
886
|
-
"oneOf",
|
|
887
|
-
"prefixItems"
|
|
888
|
-
]);
|
|
889
|
-
const SCHEMA_LITERAL_KEYS = /* @__PURE__ */ new Set([
|
|
890
|
-
"const",
|
|
891
|
-
"default",
|
|
892
|
-
"enum",
|
|
893
|
-
"examples"
|
|
894
|
-
]);
|
|
895
|
-
function tryResolveLocalRef(ref, defs, rootDocument) {
|
|
896
|
-
const match = ref.match(/^#\/(\$defs|definitions)\/([^/]+)(?:\/(.*))?$/);
|
|
897
|
-
if (match && defs) {
|
|
898
|
-
const namespace = match[1] === "$defs" ? defs.$defs : defs.definitions;
|
|
899
|
-
const name = decodeJsonPointerSegment(match[2] ?? "");
|
|
900
|
-
const resolved = name ? namespace.get(name) : void 0;
|
|
901
|
-
if (resolved !== void 0) return resolveJsonPointerPath(resolved, match[3] ? match[3].split("/") : []);
|
|
902
|
-
}
|
|
903
|
-
return resolveLocalJsonPointer(rootDocument, ref);
|
|
904
|
-
}
|
|
905
|
-
function inlineLocalSchemaRefsWithDefs(schema, defs, refStack, state, rootDocument) {
|
|
906
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
907
|
-
if (Array.isArray(schema)) return schema.map((entry) => inlineLocalSchemaRefsWithDefs(entry, defs, refStack, state, rootDocument));
|
|
908
|
-
const obj = schema;
|
|
909
|
-
const nextDefs = extendSchemaDefs(defs, obj);
|
|
910
|
-
const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
|
|
911
|
-
if (refValue) {
|
|
912
|
-
if (refStack?.has(refValue)) return {};
|
|
913
|
-
const resolved = tryResolveLocalRef(refValue, nextDefs, rootDocument);
|
|
914
|
-
if (resolved === void 0) {
|
|
915
|
-
if (refValue.startsWith("#/")) state.unresolvedLocalRefs = true;
|
|
916
|
-
return { ...obj };
|
|
917
|
-
}
|
|
918
|
-
const nextRefStack = refStack ? new Set(refStack) : /* @__PURE__ */ new Set();
|
|
919
|
-
nextRefStack.add(refValue);
|
|
920
|
-
const inlined = inlineLocalSchemaRefsWithDefs(resolved, nextDefs, nextRefStack, state, rootDocument);
|
|
921
|
-
if (!inlined || typeof inlined !== "object" || Array.isArray(inlined)) return inlined;
|
|
922
|
-
const result = { ...inlined };
|
|
923
|
-
copySchemaMeta(obj, result);
|
|
924
|
-
if (obj.nullable === true) result.nullable = true;
|
|
925
|
-
return result;
|
|
926
|
-
}
|
|
927
|
-
const result = {};
|
|
928
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
929
|
-
if (key === "$defs" || key === "definitions" || key === "components") continue;
|
|
930
|
-
if (SCHEMA_LITERAL_KEYS.has(key)) {
|
|
931
|
-
setOwnSchemaProperty(result, key, value);
|
|
932
|
-
continue;
|
|
933
|
-
}
|
|
934
|
-
if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
935
|
-
setOwnSchemaProperty(result, key, Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, inlineLocalSchemaRefsWithDefs(entryValue, nextDefs, refStack, state, rootDocument)])));
|
|
936
|
-
continue;
|
|
937
|
-
}
|
|
938
|
-
if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) {
|
|
939
|
-
setOwnSchemaProperty(result, key, inlineLocalSchemaRefsWithDefs(value, nextDefs, refStack, state, rootDocument));
|
|
940
|
-
continue;
|
|
941
|
-
}
|
|
942
|
-
if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
943
|
-
setOwnSchemaProperty(result, key, value.map((entry) => inlineLocalSchemaRefsWithDefs(entry, nextDefs, refStack, state, rootDocument)));
|
|
944
|
-
continue;
|
|
945
|
-
}
|
|
946
|
-
setOwnSchemaProperty(result, key, value);
|
|
947
|
-
}
|
|
948
|
-
if (state.unresolvedLocalRefs) {
|
|
949
|
-
if ("$defs" in obj) result.$defs = obj.$defs;
|
|
950
|
-
if ("definitions" in obj) result.definitions = obj.definitions;
|
|
951
|
-
if ("components" in obj) result.components = obj.components;
|
|
952
|
-
}
|
|
953
|
-
return result;
|
|
954
|
-
}
|
|
955
|
-
/** Inline local $ref pointers so providers receive self-contained tool schemas. */
|
|
956
|
-
function inlineLocalToolSchemaRefs(schema) {
|
|
957
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
958
|
-
return inlineLocalSchemaRefsWithDefs(schema, extendSchemaDefs(void 0, schema), void 0, { unresolvedLocalRefs: false }, schema);
|
|
959
|
-
}
|
|
960
|
-
const OPENAPI_SCHEMA_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
961
|
-
"discriminator",
|
|
962
|
-
"externalDocs",
|
|
963
|
-
"readOnly",
|
|
964
|
-
"writeOnly",
|
|
965
|
-
"xml",
|
|
966
|
-
"example"
|
|
967
|
-
]);
|
|
968
|
-
function appendNullSchemaType(type) {
|
|
969
|
-
if (type === "null") return type;
|
|
970
|
-
if (typeof type === "string") return [type, "null"];
|
|
971
|
-
if (Array.isArray(type)) return type.includes("null") ? type : [...type, "null"];
|
|
972
|
-
return type;
|
|
973
|
-
}
|
|
974
|
-
function isNullSchemaLike(schema) {
|
|
975
|
-
if (!isRecord(schema)) return false;
|
|
976
|
-
if (schema.type === "null") return true;
|
|
977
|
-
if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
|
|
978
|
-
if ("const" in schema && schema.const === null) return true;
|
|
979
|
-
return Array.isArray(schema.enum) && schema.enum.includes(null);
|
|
980
|
-
}
|
|
981
|
-
function hasOpenApiComposition(schema) {
|
|
982
|
-
return [
|
|
983
|
-
"allOf",
|
|
984
|
-
"anyOf",
|
|
985
|
-
"oneOf"
|
|
986
|
-
].some((key) => Array.isArray(schema[key]));
|
|
987
|
-
}
|
|
988
|
-
function schemaCompositionAlreadyAllowsNull(schema) {
|
|
989
|
-
return Array.isArray(schema.anyOf) && schema.anyOf.some(isNullSchemaLike) || Array.isArray(schema.oneOf) && schema.oneOf.some(isNullSchemaLike);
|
|
990
|
-
}
|
|
991
|
-
function wrapNullableComposedSchema(schema) {
|
|
992
|
-
if (schemaCompositionAlreadyAllowsNull(schema)) return schema;
|
|
993
|
-
const wrapped = { anyOf: [schema, { type: "null" }] };
|
|
994
|
-
copySchemaMeta(schema, wrapped);
|
|
995
|
-
return wrapped;
|
|
996
|
-
}
|
|
997
|
-
function normalizeOpenApiSchemaKeywords(schema) {
|
|
998
|
-
if (Array.isArray(schema)) {
|
|
999
|
-
let changed = false;
|
|
1000
|
-
const normalized = schema.map((entry) => {
|
|
1001
|
-
const next = normalizeOpenApiSchemaKeywords(entry);
|
|
1002
|
-
changed ||= next !== entry;
|
|
1003
|
-
return next;
|
|
1004
|
-
});
|
|
1005
|
-
return changed ? normalized : schema;
|
|
1006
|
-
}
|
|
1007
|
-
if (!isRecord(schema)) return schema;
|
|
1008
|
-
let changed = false;
|
|
1009
|
-
const nullable = schema.nullable === true;
|
|
1010
|
-
const normalized = {};
|
|
1011
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
1012
|
-
if (key === "nullable" || OPENAPI_SCHEMA_ANNOTATION_KEYS.has(key)) {
|
|
1013
|
-
changed = true;
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
if (SCHEMA_LITERAL_KEYS.has(key)) {
|
|
1017
|
-
normalized[key] = value;
|
|
1018
|
-
continue;
|
|
1019
|
-
}
|
|
1020
|
-
if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
1021
|
-
let mapChanged = false;
|
|
1022
|
-
const next = Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => {
|
|
1023
|
-
const nextEntry = normalizeOpenApiSchemaKeywords(entryValue);
|
|
1024
|
-
mapChanged ||= nextEntry !== entryValue;
|
|
1025
|
-
return [entryKey, nextEntry];
|
|
1026
|
-
}));
|
|
1027
|
-
normalized[key] = mapChanged ? next : value;
|
|
1028
|
-
changed ||= mapChanged;
|
|
1029
|
-
continue;
|
|
1030
|
-
}
|
|
1031
|
-
if (key === "components") {
|
|
1032
|
-
normalized[key] = value;
|
|
1033
|
-
continue;
|
|
1034
|
-
}
|
|
1035
|
-
if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) {
|
|
1036
|
-
const next = normalizeOpenApiSchemaKeywords(value);
|
|
1037
|
-
normalized[key] = next;
|
|
1038
|
-
changed ||= next !== value;
|
|
1039
|
-
continue;
|
|
1040
|
-
}
|
|
1041
|
-
if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
1042
|
-
const next = value.map(normalizeOpenApiSchemaKeywords);
|
|
1043
|
-
normalized[key] = next;
|
|
1044
|
-
changed ||= next.some((entry, index) => entry !== value[index]);
|
|
1045
|
-
continue;
|
|
1046
|
-
}
|
|
1047
|
-
normalized[key] = value;
|
|
1048
|
-
}
|
|
1049
|
-
if (nullable) {
|
|
1050
|
-
if (hasOpenApiComposition(normalized)) return wrapNullableComposedSchema(normalized);
|
|
1051
|
-
if ("type" in normalized) {
|
|
1052
|
-
const nextType = appendNullSchemaType(normalized.type);
|
|
1053
|
-
if (nextType !== normalized.type) normalized.type = nextType;
|
|
1054
|
-
}
|
|
1055
|
-
if (Array.isArray(normalized.enum) && !normalized.enum.includes(null)) normalized.enum = [...normalized.enum, null];
|
|
1056
|
-
}
|
|
1057
|
-
return changed || nullable ? normalized : schema;
|
|
1058
|
-
}
|
|
1059
|
-
function normalizeToolParameterSchemaUncached(schema, options) {
|
|
1060
|
-
const inlinedSchema = normalizeOpenApiSchemaKeywords(inlineLocalToolSchemaRefs(schema));
|
|
1061
|
-
const schemaRecord = inlinedSchema && typeof inlinedSchema === "object" ? inlinedSchema : void 0;
|
|
1062
|
-
if (!schemaRecord) return inlinedSchema;
|
|
1063
|
-
const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
|
|
1064
|
-
const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
|
|
1065
|
-
const normalizedToolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
|
|
1066
|
-
const isGeminiProvider = normalizedProvider.includes("google") || normalizedProvider.includes("gemini") || isGeminiModelId(normalizedModelId) || normalizedToolSchemaProfile === "gemini";
|
|
1067
|
-
const isAnthropicProvider = normalizedProvider.includes("anthropic");
|
|
1068
|
-
const unsupportedToolSchemaKeywords = resolveUnsupportedToolSchemaKeywords(options?.modelCompat);
|
|
1069
|
-
const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
|
|
1070
|
-
const isLlamacppGbnfProfile = normalizedToolSchemaProfile === "llamacpp";
|
|
1071
|
-
function applyProviderCleaning(s) {
|
|
1072
|
-
const normalizedSchema = normalizeArraySchemasMissingItems(s);
|
|
1073
|
-
let arrayItemsCompatibleSchema = omitEmptyArrayItems ? stripEmptyArrayItemsFromArraySchemas(normalizedSchema) : normalizedSchema;
|
|
1074
|
-
if (isLlamacppGbnfProfile) arrayItemsCompatibleSchema = cleanSchemaForLlamacppGbnf(arrayItemsCompatibleSchema);
|
|
1075
|
-
if (isGeminiProvider && !isAnthropicProvider) {
|
|
1076
|
-
const geminiCompatibleSchema = cleanSchemaForGemini(arrayItemsCompatibleSchema);
|
|
1077
|
-
return unsupportedToolSchemaKeywords.size > 0 ? stripUnsupportedSchemaKeywords(geminiCompatibleSchema, unsupportedToolSchemaKeywords) : geminiCompatibleSchema;
|
|
1078
|
-
}
|
|
1079
|
-
if (unsupportedToolSchemaKeywords.size > 0) return stripUnsupportedSchemaKeywords(arrayItemsCompatibleSchema, unsupportedToolSchemaKeywords);
|
|
1080
|
-
return arrayItemsCompatibleSchema;
|
|
1081
|
-
}
|
|
1082
|
-
const conditionalKey = getTopLevelConditionalKey(schemaRecord);
|
|
1083
|
-
const flattenableVariantKey = getFlattenableVariantKey(schemaRecord);
|
|
1084
|
-
if (hasTopLevelObjectSchema(schemaRecord, conditionalKey)) return applyProviderCleaning(schemaRecord);
|
|
1085
|
-
if (isObjectLikeSchemaMissingType(schemaRecord, conditionalKey)) return applyProviderCleaning({
|
|
1086
|
-
...schemaRecord,
|
|
1087
|
-
type: "object",
|
|
1088
|
-
properties: isRecord(schemaRecord.properties) ? schemaRecord.properties : {}
|
|
1089
|
-
});
|
|
1090
|
-
if (isTypedObjectSchemaMissingValidProperties(schemaRecord, conditionalKey)) return applyProviderCleaning({
|
|
1091
|
-
...schemaRecord,
|
|
1092
|
-
properties: {}
|
|
1093
|
-
});
|
|
1094
|
-
if (!flattenableVariantKey) {
|
|
1095
|
-
if (isTrulyEmptySchema(schemaRecord)) return applyProviderCleaning({
|
|
1096
|
-
type: "object",
|
|
1097
|
-
properties: {}
|
|
1098
|
-
});
|
|
1099
|
-
if (conditionalKey === "allOf") return applyProviderCleaning(inlinedSchema);
|
|
1100
|
-
return applyProviderCleaning(inlinedSchema);
|
|
1101
|
-
}
|
|
1102
|
-
const variants = schemaRecord[flattenableVariantKey];
|
|
1103
|
-
const mergedProperties = {};
|
|
1104
|
-
const requiredCounts = /* @__PURE__ */ new Map();
|
|
1105
|
-
let objectVariants = 0;
|
|
1106
|
-
for (const entry of variants) {
|
|
1107
|
-
if (!entry || typeof entry !== "object") continue;
|
|
1108
|
-
const props = entry.properties;
|
|
1109
|
-
if (!props || typeof props !== "object") continue;
|
|
1110
|
-
objectVariants += 1;
|
|
1111
|
-
for (const [key, value] of Object.entries(props)) {
|
|
1112
|
-
if (!(key in mergedProperties)) {
|
|
1113
|
-
mergedProperties[key] = value;
|
|
1114
|
-
continue;
|
|
1115
|
-
}
|
|
1116
|
-
mergedProperties[key] = mergePropertySchemas(mergedProperties[key], value);
|
|
1117
|
-
}
|
|
1118
|
-
const required = Array.isArray(entry.required) ? entry.required : [];
|
|
1119
|
-
for (const key of required) {
|
|
1120
|
-
if (typeof key !== "string") continue;
|
|
1121
|
-
requiredCounts.set(key, (requiredCounts.get(key) ?? 0) + 1);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
const baseRequired = Array.isArray(schemaRecord.required) ? schemaRecord.required.filter((key) => typeof key === "string") : void 0;
|
|
1125
|
-
const mergedRequired = baseRequired && baseRequired.length > 0 ? baseRequired : objectVariants > 0 ? Array.from(requiredCounts.entries()).filter(([, count]) => count === objectVariants).map(([key]) => key) : void 0;
|
|
1126
|
-
const nextSchema = { ...schemaRecord };
|
|
1127
|
-
return applyProviderCleaning({
|
|
1128
|
-
type: "object",
|
|
1129
|
-
...typeof nextSchema.title === "string" ? { title: nextSchema.title } : {},
|
|
1130
|
-
...typeof nextSchema.description === "string" ? { description: nextSchema.description } : {},
|
|
1131
|
-
properties: Object.keys(mergedProperties).length > 0 ? mergedProperties : schemaRecord.properties ?? {},
|
|
1132
|
-
...mergedRequired && mergedRequired.length > 0 ? { required: mergedRequired } : {},
|
|
1133
|
-
additionalProperties: "additionalProperties" in schemaRecord ? schemaRecord.additionalProperties : true
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
/** Return a provider-compatible JSON schema for a model-facing tool. */
|
|
1137
|
-
function normalizeToolParameterSchema(schema, options) {
|
|
1138
|
-
if (!schema || typeof schema !== "object") return normalizeToolParameterSchemaUncached(schema, options);
|
|
1139
|
-
const cacheKey = resolveToolParameterSchemaCacheKey(options);
|
|
1140
|
-
const cached = getCachedToolParameterSchema(schema, cacheKey);
|
|
1141
|
-
if (cached) return cached;
|
|
1142
|
-
return rememberCachedToolParameterSchema(schema, cacheKey, normalizeToolParameterSchemaUncached(schema, options));
|
|
1143
|
-
}
|
|
1144
|
-
//#endregion
|
|
1145
|
-
//#region packages/ai/src/providers/openai-tool-schema-compat.ts
|
|
1146
|
-
const OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
1147
|
-
"$defs",
|
|
1148
|
-
"definitions",
|
|
1149
|
-
"dependentSchemas",
|
|
1150
|
-
"dependencies",
|
|
1151
|
-
"patternProperties",
|
|
1152
|
-
"properties"
|
|
1153
|
-
]);
|
|
1154
|
-
const OPENAI_NULLABLE_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
1155
|
-
"default",
|
|
1156
|
-
"description",
|
|
1157
|
-
"examples",
|
|
1158
|
-
"format",
|
|
1159
|
-
"title"
|
|
1160
|
-
]);
|
|
1161
|
-
const OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS = /* @__PURE__ */ new Set([
|
|
1162
|
-
"additionalItems",
|
|
1163
|
-
"additionalProperties",
|
|
1164
|
-
"allOf",
|
|
1165
|
-
"anyOf",
|
|
1166
|
-
"contains",
|
|
1167
|
-
"contentSchema",
|
|
1168
|
-
"else",
|
|
1169
|
-
"if",
|
|
1170
|
-
"items",
|
|
1171
|
-
"not",
|
|
1172
|
-
"oneOf",
|
|
1173
|
-
"prefixItems",
|
|
1174
|
-
"propertyNames",
|
|
1175
|
-
"then",
|
|
1176
|
-
"unevaluatedItems",
|
|
1177
|
-
"unevaluatedProperties"
|
|
1178
|
-
]);
|
|
1179
|
-
function normalizeOpenAIStrictCompatSchemaMap(schema) {
|
|
1180
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
|
|
1181
|
-
let changed = false;
|
|
1182
|
-
const normalized = {};
|
|
1183
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
1184
|
-
const next = normalizeOpenAIStrictCompatSchemaRecursive(value, { promoteEmptyObject: false });
|
|
1185
|
-
normalized[key] = next;
|
|
1186
|
-
changed ||= next !== value;
|
|
1187
|
-
}
|
|
1188
|
-
return changed ? normalized : schema;
|
|
1189
|
-
}
|
|
1190
|
-
function normalizeOpenAIStrictCompatSchemaRecursive(schema, options) {
|
|
1191
|
-
if (Array.isArray(schema)) {
|
|
1192
|
-
let changed = false;
|
|
1193
|
-
const normalized = schema.map((entry) => {
|
|
1194
|
-
const next = normalizeOpenAIStrictCompatSchemaRecursive(entry, { promoteEmptyObject: false });
|
|
1195
|
-
changed ||= next !== entry;
|
|
1196
|
-
return next;
|
|
1197
|
-
});
|
|
1198
|
-
return changed ? normalized : schema;
|
|
1199
|
-
}
|
|
1200
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1201
|
-
const record = schema;
|
|
1202
|
-
let changed = false;
|
|
1203
|
-
let hadNullType = false;
|
|
1204
|
-
const normalized = {};
|
|
1205
|
-
for (const [key, value] of Object.entries(record)) {
|
|
1206
|
-
if (value === null && OPENAI_NULLABLE_ANNOTATION_KEYS.has(key)) {
|
|
1207
|
-
changed = true;
|
|
1208
|
-
continue;
|
|
1209
|
-
}
|
|
1210
|
-
if (value === null && key === "type") {
|
|
1211
|
-
hadNullType = true;
|
|
1212
|
-
changed = true;
|
|
1213
|
-
continue;
|
|
1214
|
-
}
|
|
1215
|
-
const next = OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS.has(key) ? normalizeOpenAIStrictCompatSchemaMap(value) : OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS.has(key) ? normalizeOpenAIStrictCompatSchemaRecursive(value, { promoteEmptyObject: false }) : value;
|
|
1216
|
-
normalized[key] = next;
|
|
1217
|
-
changed ||= next !== value;
|
|
1218
|
-
}
|
|
1219
|
-
if (Object.keys(normalized).length === 0) {
|
|
1220
|
-
if (!options.promoteEmptyObject) return schema;
|
|
1221
|
-
return {
|
|
1222
|
-
type: "object",
|
|
1223
|
-
properties: {},
|
|
1224
|
-
required: [],
|
|
1225
|
-
additionalProperties: false
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
const hasObjectShapeHints = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) || Array.isArray(normalized.required);
|
|
1229
|
-
const hasArrayShapeHints = "items" in normalized;
|
|
1230
|
-
if (!("type" in normalized) && hasObjectShapeHints !== hasArrayShapeHints) {
|
|
1231
|
-
normalized.type = hasObjectShapeHints ? "object" : "array";
|
|
1232
|
-
changed = true;
|
|
1233
|
-
} else if (hadNullType && !("type" in normalized)) normalized.type = null;
|
|
1234
|
-
if (normalized.type === "object" && !("properties" in normalized)) {
|
|
1235
|
-
normalized.properties = {};
|
|
1236
|
-
changed = true;
|
|
1237
|
-
}
|
|
1238
|
-
const hasEmptyProperties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) && Object.keys(normalized.properties).length === 0;
|
|
1239
|
-
if (normalized.type === "object" && !Array.isArray(normalized.required) && hasEmptyProperties) {
|
|
1240
|
-
normalized.required = [];
|
|
1241
|
-
changed = true;
|
|
1242
|
-
}
|
|
1243
|
-
if (normalized.type === "object" && hasEmptyProperties && !("additionalProperties" in normalized)) {
|
|
1244
|
-
normalized.additionalProperties = false;
|
|
1245
|
-
changed = true;
|
|
1246
|
-
}
|
|
1247
|
-
return changed ? normalized : schema;
|
|
1248
|
-
}
|
|
1249
|
-
/** Repairs recoverable OpenAI tool-schema shapes before canonical normalization. */
|
|
1250
|
-
function normalizeOpenAIStrictCompatSchema(schema) {
|
|
1251
|
-
return normalizeOpenAIStrictCompatSchemaRecursive(schema, { promoteEmptyObject: true });
|
|
1252
|
-
}
|
|
1253
|
-
/** Finds schema paths that violate OpenAI strict tool-schema requirements. */
|
|
1254
|
-
function findOpenAIStrictSchemaViolations(schema, path, options) {
|
|
1255
|
-
if (Array.isArray(schema)) {
|
|
1256
|
-
if (options?.requireObjectRoot) return [`${path}.type`];
|
|
1257
|
-
return schema.flatMap((item, index) => findOpenAIStrictSchemaViolations(item, `${path}[${index}]`));
|
|
1258
|
-
}
|
|
1259
|
-
if (!schema || typeof schema !== "object") return options?.requireObjectRoot ? [`${path}.type`] : [];
|
|
1260
|
-
const record = schema;
|
|
1261
|
-
const violations = [];
|
|
1262
|
-
for (const key of [
|
|
1263
|
-
"anyOf",
|
|
1264
|
-
"oneOf",
|
|
1265
|
-
"allOf"
|
|
1266
|
-
]) if (key in record) violations.push(`${path}.${key}`);
|
|
1267
|
-
if (Array.isArray(record.type)) violations.push(`${path}.type`);
|
|
1268
|
-
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : void 0;
|
|
1269
|
-
if (record.type === "object") {
|
|
1270
|
-
if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
|
|
1271
|
-
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
1272
|
-
if (!required) violations.push(`${path}.required`);
|
|
1273
|
-
else if (properties) {
|
|
1274
|
-
const requiredSet = new Set(required);
|
|
1275
|
-
for (const key of Object.keys(properties)) if (!requiredSet.has(key)) violations.push(`${path}.required.${key}`);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
for (const key of OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS) {
|
|
1279
|
-
const schemaMap = record[key];
|
|
1280
|
-
if (!schemaMap || typeof schemaMap !== "object" || Array.isArray(schemaMap)) continue;
|
|
1281
|
-
for (const [entryKey, value] of Object.entries(schemaMap)) violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}.${entryKey}`));
|
|
1282
|
-
}
|
|
1283
|
-
for (const key of OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS) {
|
|
1284
|
-
const value = record[key];
|
|
1285
|
-
if (value && typeof value === "object") violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}`));
|
|
1286
|
-
}
|
|
1287
|
-
return violations;
|
|
1288
|
-
}
|
|
1289
|
-
//#endregion
|
|
1290
|
-
//#region packages/ai/src/providers/openai-tool-schema.ts
|
|
1291
|
-
/**
|
|
1292
|
-
* OpenAI strict JSON-schema normalization for tool inventories and request payloads.
|
|
1293
|
-
*
|
|
1294
|
-
* Caches normalized object inputs by provider compatibility so repeated inventory builds preserve identity.
|
|
1295
|
-
*/
|
|
1296
|
-
const MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
|
|
1297
|
-
const strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
|
|
1298
|
-
function resolveToolSchemaModelCompat(compat) {
|
|
1299
|
-
if (!compat) return;
|
|
1300
|
-
const unsupportedToolSchemaKeywords = Array.isArray(compat.unsupportedToolSchemaKeywords) ? compat.unsupportedToolSchemaKeywords.filter((keyword) => typeof keyword === "string") : [];
|
|
1301
|
-
if (unsupportedToolSchemaKeywords.length === 0 && compat.omitEmptyArrayItems !== true) return;
|
|
1302
|
-
return {
|
|
1303
|
-
...unsupportedToolSchemaKeywords.length > 0 ? { unsupportedToolSchemaKeywords } : {},
|
|
1304
|
-
...compat.omitEmptyArrayItems === true ? { omitEmptyArrayItems: true } : {}
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
function resolveStrictOpenAISchemaCacheKey(modelCompat) {
|
|
1308
|
-
const compat = resolveToolSchemaModelCompat(modelCompat);
|
|
1309
|
-
return JSON.stringify([[...compat?.unsupportedToolSchemaKeywords ?? []].toSorted(), shouldOmitEmptyArrayItems(compat)]);
|
|
1310
|
-
}
|
|
1311
|
-
function readCachedStrictOpenAISchema(schema, key) {
|
|
1312
|
-
return strictOpenAISchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
|
|
1313
|
-
}
|
|
1314
|
-
function rememberStrictOpenAISchema(schema, key, value) {
|
|
1315
|
-
const entries = strictOpenAISchemaCache.get(schema) ?? [];
|
|
1316
|
-
strictOpenAISchemaCache.set(schema, [{
|
|
1317
|
-
key,
|
|
1318
|
-
value
|
|
1319
|
-
}, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
|
|
1320
|
-
return value;
|
|
1321
|
-
}
|
|
1322
|
-
/** Normalizes a tool parameter schema into the OpenAI strict JSON-schema subset. */
|
|
1323
|
-
function normalizeStrictOpenAIJsonSchema(schema, modelCompat) {
|
|
1324
|
-
const schemaInput = schema ?? {};
|
|
1325
|
-
if (!schemaInput || typeof schemaInput !== "object") return normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0);
|
|
1326
|
-
const cacheKey = resolveStrictOpenAISchemaCacheKey(modelCompat);
|
|
1327
|
-
const cached = readCachedStrictOpenAISchema(schemaInput, cacheKey);
|
|
1328
|
-
if (cached !== void 0) return cached;
|
|
1329
|
-
return rememberStrictOpenAISchema(schemaInput, cacheKey, normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0));
|
|
1330
|
-
}
|
|
1331
|
-
function normalizeStrictOpenAIJsonSchemaRecursive(schema, depth) {
|
|
1332
|
-
if (Array.isArray(schema)) {
|
|
1333
|
-
let changed = false;
|
|
1334
|
-
const normalized = schema.map((entry) => {
|
|
1335
|
-
const next = normalizeStrictOpenAIJsonSchemaRecursive(entry, depth);
|
|
1336
|
-
changed ||= next !== entry;
|
|
1337
|
-
return next;
|
|
1338
|
-
});
|
|
1339
|
-
return changed ? normalized : schema;
|
|
1340
|
-
}
|
|
1341
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1342
|
-
const record = schema;
|
|
1343
|
-
let changed = false;
|
|
1344
|
-
const normalized = {};
|
|
1345
|
-
for (const [key, value] of Object.entries(record)) {
|
|
1346
|
-
const next = normalizeStrictOpenAIJsonSchemaRecursive(value, key === "properties" ? depth : depth + 1);
|
|
1347
|
-
normalized[key] = next;
|
|
1348
|
-
changed ||= next !== value;
|
|
1349
|
-
}
|
|
1350
|
-
if (normalized.type === "object") {
|
|
1351
|
-
const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : void 0;
|
|
1352
|
-
if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
|
|
1353
|
-
normalized.required = [];
|
|
1354
|
-
changed = true;
|
|
1355
|
-
}
|
|
1356
|
-
if (depth === 0 && !("additionalProperties" in normalized)) {
|
|
1357
|
-
normalized.additionalProperties = false;
|
|
1358
|
-
changed = true;
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
return changed ? normalized : schema;
|
|
1362
|
-
}
|
|
1363
|
-
/** Normalizes tool parameters using strict OpenAI rules only when strict mode is active. */
|
|
1364
|
-
function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
|
|
1365
|
-
const toolSchemaCompat = resolveToolSchemaModelCompat(modelCompat);
|
|
1366
|
-
if (!strict) return normalizeToolParameterSchema(schema ?? {}, { modelCompat: toolSchemaCompat });
|
|
1367
|
-
return normalizeStrictOpenAIJsonSchema(schema, toolSchemaCompat);
|
|
1368
|
-
}
|
|
1369
|
-
/** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
|
|
1370
|
-
function isStrictOpenAIJsonSchemaCompatible(schema) {
|
|
1371
|
-
return isStrictOpenAIJsonSchemaCompatibleRecursive(normalizeStrictOpenAIJsonSchema(schema));
|
|
1372
|
-
}
|
|
1373
|
-
/** Returns strict-schema diagnostics for an already materialized OpenAI tool projection. */
|
|
1374
|
-
function findOpenAIStrictToolProjectionDiagnostics(projection) {
|
|
1375
|
-
return [...projection.diagnostics.map((diagnostic) => ({
|
|
1376
|
-
toolIndex: diagnostic.toolIndex,
|
|
1377
|
-
...diagnostic.toolName ? { toolName: diagnostic.toolName } : {},
|
|
1378
|
-
violations: [...diagnostic.violations]
|
|
1379
|
-
})), ...projection.tools.flatMap((tool) => {
|
|
1380
|
-
const violations = findOpenAIStrictSchemaViolations(normalizeStrictOpenAIJsonSchema(tool.parameters), `${tool.name}.parameters`);
|
|
1381
|
-
return violations.length > 0 ? [{
|
|
1382
|
-
toolIndex: tool.toolIndex,
|
|
1383
|
-
toolName: tool.name,
|
|
1384
|
-
violations
|
|
1385
|
-
}] : [];
|
|
1386
|
-
})];
|
|
1387
|
-
}
|
|
1388
|
-
function isStrictOpenAIJsonSchemaCompatibleRecursive(schema) {
|
|
1389
|
-
if (Array.isArray(schema)) return schema.every((entry) => isStrictOpenAIJsonSchemaCompatibleRecursive(entry));
|
|
1390
|
-
if (!schema || typeof schema !== "object") return true;
|
|
1391
|
-
const record = schema;
|
|
1392
|
-
if ("anyOf" in record || "oneOf" in record || "allOf" in record) return false;
|
|
1393
|
-
if (Array.isArray(record.type)) return false;
|
|
1394
|
-
if (record.type === "object" && record.additionalProperties !== false) return false;
|
|
1395
|
-
if (record.type === "object") {
|
|
1396
|
-
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
|
|
1397
|
-
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
1398
|
-
if (!required) return false;
|
|
1399
|
-
const requiredSet = new Set(required);
|
|
1400
|
-
if (Object.keys(properties).some((key) => !requiredSet.has(key))) return false;
|
|
1401
|
-
}
|
|
1402
|
-
return Object.entries(record).every(([key, entry]) => {
|
|
1403
|
-
if (key === "properties" && entry && typeof entry === "object" && !Array.isArray(entry)) return Object.values(entry).every((value) => isStrictOpenAIJsonSchemaCompatibleRecursive(value));
|
|
1404
|
-
return isStrictOpenAIJsonSchemaCompatibleRecursive(entry);
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
/** Resolves strict mode for the projected tools that will be emitted in the request payload. */
|
|
1408
|
-
function resolveOpenAIProjectedToolsStrictToolFlag(projection, strict) {
|
|
1409
|
-
if (strict !== true) return strict === false ? false : void 0;
|
|
1410
|
-
return projection.tools.every((tool) => isStrictOpenAIJsonSchemaCompatible(tool.parameters));
|
|
1411
|
-
}
|
|
1412
|
-
//#endregion
|
|
1413
59
|
//#region packages/ai/src/transports/openai-responses-replay.ts
|
|
1414
60
|
/** Resolves the assistant message id that can be replayed to OpenAI Responses. */
|
|
1415
61
|
function resolveReplayableResponsesMessageId(params) {
|
|
@@ -1439,16 +85,19 @@ function isOpenAIResponsesReplayContext(value) {
|
|
|
1439
85
|
if (!isRecord(value)) return false;
|
|
1440
86
|
return typeof value.provider === "string" && typeof value.api === "string" && typeof value.model === "string" && (value.baseUrlHash === void 0 || typeof value.baseUrlHash === "string") && (value.sessionHash === void 0 || typeof value.sessionHash === "string") && (value.authProfileHash === void 0 || typeof value.authProfileHash === "string");
|
|
1441
87
|
}
|
|
88
|
+
function isOpenAIResponsesCompactionState(state) {
|
|
89
|
+
if (typeof state.baseUrlHash !== "string" || state.v !== 1) return false;
|
|
90
|
+
if (state.type === OPENAI_RESPONSES_COMPACTION_SUPPRESSION_TYPE) return state.data === OPENAI_RESPONSES_COMPACTION_SUPPRESSION_DATA;
|
|
91
|
+
if (state.type === "openai-responses-retained-compaction") return typeof state.data === "string" && state.data.length > 0 && (state.id === void 0 || typeof state.id === "string") && state.replayIndex === void 0;
|
|
92
|
+
return state.type === "openai-responses-compaction" && typeof state.data === "string" && state.data.length > 0 && (state.id === void 0 || typeof state.id === "string") && (state.replayIndex === void 0 || typeof state.replayIndex === "number" && Number.isSafeInteger(state.replayIndex) && state.replayIndex >= 0);
|
|
93
|
+
}
|
|
1442
94
|
function readOpenAIResponsesCompactionReplayState(value) {
|
|
1443
|
-
|
|
1444
|
-
const state = value;
|
|
1445
|
-
if (state.type === OPENAI_RESPONSES_COMPACTION_SUPPRESSION_TYPE) return state.data === OPENAI_RESPONSES_COMPACTION_SUPPRESSION_DATA ? state : void 0;
|
|
1446
|
-
return state.type === "openai-responses-compaction" && typeof state.data === "string" && state.data.length > 0 && (state.id === void 0 || typeof state.id === "string") && (state.replayIndex === void 0 || Number.isSafeInteger(state.replayIndex) && state.replayIndex >= 0) ? state : void 0;
|
|
95
|
+
return isRecord(value) && isOpenAIResponsesReplayContext(value) && isOpenAIResponsesCompactionState(value) ? value : void 0;
|
|
1447
96
|
}
|
|
1448
97
|
function openAIResponsesReplayContextMatches(state, context) {
|
|
1449
98
|
return state.provider === context.provider && state.api === context.api && state.model === context.model && state.baseUrlHash === context.baseUrlHash && state.sessionHash === context.sessionHash && state.authProfileHash === context.authProfileHash;
|
|
1450
99
|
}
|
|
1451
|
-
function captureOpenAIResponsesCompaction(output, item,
|
|
100
|
+
function captureOpenAIResponsesCompaction(output, item, boundary, model, captureMetadata, compactedOutput) {
|
|
1452
101
|
const metadata = captureMetadata ?? buildOpenAIResponsesReasoningReplayMetadata(model);
|
|
1453
102
|
if (!item.encrypted_content) return;
|
|
1454
103
|
if (!metadata?.baseUrlHash) {
|
|
@@ -1456,20 +105,29 @@ function captureOpenAIResponsesCompaction(output, item, replayIndex, model, capt
|
|
|
1456
105
|
return;
|
|
1457
106
|
}
|
|
1458
107
|
const currentReplay = readOpenAIResponsesCompactionReplayState(output.providerReplay);
|
|
1459
|
-
if (currentReplay?.type === "openai-responses-compaction" && (currentReplay.replayIndex ?? -1) >
|
|
1460
|
-
|
|
108
|
+
if (typeof boundary === "number" && currentReplay?.type === "openai-responses-compaction" && (currentReplay.replayIndex ?? -1) > boundary) return;
|
|
109
|
+
if (compactedOutput && !isOpenAIResponsesCompactionOutput(compactedOutput, model)) throw new Error("Responses compact endpoint checkpoint is invalid");
|
|
110
|
+
const replay = {
|
|
1461
111
|
v: 1,
|
|
1462
|
-
type:
|
|
112
|
+
...boundary === "retained-users" ? { type: OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE } : {
|
|
113
|
+
type: OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE,
|
|
114
|
+
replayIndex: boundary
|
|
115
|
+
},
|
|
1463
116
|
...item.id ? { id: item.id } : {},
|
|
1464
117
|
data: item.encrypted_content,
|
|
1465
|
-
...replayIndex === void 0 ? {} : { replayIndex },
|
|
1466
118
|
provider: metadata.provider,
|
|
1467
119
|
api: metadata.api,
|
|
1468
120
|
model: metadata.model,
|
|
1469
121
|
baseUrlHash: metadata.baseUrlHash,
|
|
1470
122
|
...metadata.sessionHash ? { sessionHash: metadata.sessionHash } : {},
|
|
1471
|
-
...metadata.authProfileHash ? { authProfileHash: metadata.authProfileHash } : {}
|
|
123
|
+
...metadata.authProfileHash ? { authProfileHash: metadata.authProfileHash } : {},
|
|
124
|
+
...compactedOutput ? { compactedWindow: {
|
|
125
|
+
state: "ready",
|
|
126
|
+
output: JSON.stringify(compactedOutput)
|
|
127
|
+
} } : {}
|
|
1472
128
|
};
|
|
129
|
+
if (compactedOutput && !readOpenAIResponsesCompactionWindow(replay, model)) throw new Error("Responses compact endpoint checkpoint is invalid or exceeds 16 MiB");
|
|
130
|
+
output.providerReplay = replay;
|
|
1473
131
|
}
|
|
1474
132
|
function suppressOpenAIResponsesCompaction(output, model, options, rejectedCheckpoint) {
|
|
1475
133
|
const context = buildOpenAIResponsesReplayContext(model, options);
|
|
@@ -1513,11 +171,26 @@ function resolveNewestOpenAIResponsesCompactionReplay(messages, model, options)
|
|
|
1513
171
|
if (openAIResponsesReplayContextMatches(replay, context)) return;
|
|
1514
172
|
continue;
|
|
1515
173
|
}
|
|
1516
|
-
if (replay?.type !== "openai-responses-compaction") {
|
|
1517
|
-
if (message.providerReplay?.type === "openai-responses-compaction") return;
|
|
174
|
+
if (replay?.type !== "openai-responses-compaction" && replay?.type !== "openai-responses-retained-compaction") {
|
|
175
|
+
if (message.providerReplay?.type === "openai-responses-compaction" || message.providerReplay?.type === "openai-responses-retained-compaction") return;
|
|
1518
176
|
continue;
|
|
1519
177
|
}
|
|
1520
178
|
if (!openAIResponsesReplayContextMatches(replay, context)) return;
|
|
179
|
+
if (replay.compactedWindow !== void 0 || replay.type === "openai-responses-retained-compaction") {
|
|
180
|
+
const output = readOpenAIResponsesCompactionWindow(replay, model);
|
|
181
|
+
const item = output?.at(-1);
|
|
182
|
+
if (!output || item?.type !== "compaction") return {
|
|
183
|
+
owner: message,
|
|
184
|
+
mode: "refresh-required"
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
owner: message,
|
|
188
|
+
mode: "complete-window",
|
|
189
|
+
output,
|
|
190
|
+
item,
|
|
191
|
+
replayIndex: replay.type === "openai-responses-retained-compaction" ? message.content.length : replay.replayIndex ?? 0
|
|
192
|
+
};
|
|
193
|
+
}
|
|
1521
194
|
return {
|
|
1522
195
|
owner: message,
|
|
1523
196
|
item: {
|
|
@@ -1525,10 +198,17 @@ function resolveNewestOpenAIResponsesCompactionReplay(messages, model, options)
|
|
|
1525
198
|
...isSafeResponsesReplayItemId(replay.id) ? { id: replay.id } : {},
|
|
1526
199
|
encrypted_content: replay.data
|
|
1527
200
|
},
|
|
201
|
+
mode: "compacted-prefix",
|
|
1528
202
|
replayIndex: replay.replayIndex ?? 0
|
|
1529
203
|
};
|
|
1530
204
|
}
|
|
1531
205
|
}
|
|
206
|
+
var CompactionReplayRefreshRequiredError = class extends Error {
|
|
207
|
+
constructor() {
|
|
208
|
+
super("Provider compaction checkpoint needs rebuilding. Run /compact to rebuild from saved conversation history.");
|
|
209
|
+
this.name = "CompactionReplayRefreshRequiredError";
|
|
210
|
+
}
|
|
211
|
+
};
|
|
1532
212
|
function buildOpenAIResponsesCompactionReplayPlan(messages, model, options) {
|
|
1533
213
|
if (options?.mode === "full-history") return {
|
|
1534
214
|
messages,
|
|
@@ -1539,13 +219,14 @@ function buildOpenAIResponsesCompactionReplayPlan(messages, model, options) {
|
|
|
1539
219
|
messages,
|
|
1540
220
|
preserveUnframedToolResults: false
|
|
1541
221
|
};
|
|
222
|
+
if (compaction.mode === "refresh-required") throw new CompactionReplayRefreshRequiredError();
|
|
1542
223
|
const ownerIndex = messages.indexOf(compaction.owner);
|
|
1543
224
|
return {
|
|
1544
225
|
messages: [{
|
|
1545
226
|
...compaction.owner,
|
|
1546
227
|
content: compaction.owner.content.slice(compaction.replayIndex)
|
|
1547
228
|
}, ...messages.slice(ownerIndex + 1)],
|
|
1548
|
-
compaction: compaction.item,
|
|
229
|
+
...compaction.mode === "complete-window" ? { compactedWindow: compaction.output } : { compaction: compaction.item },
|
|
1549
230
|
preserveUnframedToolResults: true
|
|
1550
231
|
};
|
|
1551
232
|
}
|
|
@@ -1732,17 +413,34 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
1732
413
|
authProfileId: options?.authProfileId,
|
|
1733
414
|
mode: options?.replayMode
|
|
1734
415
|
});
|
|
1735
|
-
const
|
|
416
|
+
const transformMessages = (source) => providerStyle ? transformProviderMessages(source, model, normalizeToolCallId) : transformTransportMessages(source, model, normalizeToolCallId, {
|
|
1736
417
|
normalizeSameModelToolCallIds: shouldNormalizeSameModelToolCallIds,
|
|
1737
418
|
preserveUnframedToolResults: replayPlan.preserveUnframedToolResults
|
|
1738
419
|
});
|
|
1739
|
-
|
|
420
|
+
const transformedMessages = transformMessages(replayPlan.messages);
|
|
421
|
+
if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && model.compat?.supportsDeveloperRole !== false ? "developer" : "system", [{
|
|
1740
422
|
type: "input_text",
|
|
1741
423
|
text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
|
|
1742
424
|
}]));
|
|
1743
|
-
if (replayPlan.
|
|
425
|
+
if (replayPlan.compactedWindow) messages.push(...replayPlan.compactedWindow);
|
|
426
|
+
let replayMessages = replayPlan.compaction ? [replayPlan.compaction, ...transformedMessages] : transformedMessages;
|
|
427
|
+
const isCarrier = (message) => "role" in message && message.role === "user" && message.runtimeContextCarrier === true;
|
|
428
|
+
const carriers = replayMessages.filter(isCarrier);
|
|
429
|
+
if (carriers.length > 0) {
|
|
430
|
+
const stableMessages = replayMessages.filter((message) => !isCarrier(message));
|
|
431
|
+
const insertionIndex = stableMessages.findLastIndex((message) => "role" in message ? message.role === "user" : message.type === "compaction") + 1;
|
|
432
|
+
if (insertionIndex > 0 || replayPlan.compactedWindow) replayMessages = [
|
|
433
|
+
...stableMessages.slice(0, insertionIndex),
|
|
434
|
+
...carriers,
|
|
435
|
+
...stableMessages.slice(insertionIndex)
|
|
436
|
+
];
|
|
437
|
+
}
|
|
1744
438
|
let msgIndex = 0;
|
|
1745
|
-
for (const msg of
|
|
439
|
+
for (const msg of replayMessages) {
|
|
440
|
+
if (!("role" in msg)) {
|
|
441
|
+
messages.push(msg);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
1746
444
|
if (msg.role === "user") if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
|
|
1747
445
|
type: "input_text",
|
|
1748
446
|
text: sanitizeTransportPayloadText(msg.content)
|
|
@@ -1766,7 +464,14 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
1766
464
|
const isDifferentModel = msg.model !== model.id && msg.provider === model.provider && msg.api === model.api;
|
|
1767
465
|
for (const block of msg.content) if (block.type === "thinking") {
|
|
1768
466
|
if (shouldReplayReasoningItems && block.thinkingSignature && (providerStyle || block.thinkingSignature.startsWith("{"))) {
|
|
1769
|
-
|
|
467
|
+
let reasoningItem;
|
|
468
|
+
try {
|
|
469
|
+
reasoningItem = JSON.parse(block.thinkingSignature);
|
|
470
|
+
} catch {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (!isRecord(reasoningItem) || reasoningItem.type !== "reasoning") continue;
|
|
474
|
+
const replayableReasoningItem = prepareOpenAIResponsesReasoningItemForReplay(reasoningItem, replayContext, readOpenAIResponsesReasoningReplayBlockMetadata(isRecord(block) ? block : {}), providerStyle ? { preserveUnattributedEncryptedContent: true } : void 0);
|
|
1770
475
|
if (!shouldReplayResponsesItemIds) delete replayableReasoningItem.id;
|
|
1771
476
|
if (shouldReplayResponsesItemIds && !providerStyle && model.provider === "github-copilot" && !isSafeResponsesReplayItemId(replayableReasoningItem.id)) continue;
|
|
1772
477
|
output.push(replayableReasoningItem);
|
|
@@ -1849,12 +554,15 @@ function convertProviderResponsesMessages(model, context, allowedToolCallProvide
|
|
|
1849
554
|
}
|
|
1850
555
|
//#endregion
|
|
1851
556
|
//#region packages/ai/src/transports/openai-responses-replay-internal.ts
|
|
557
|
+
function isAsyncIterable(value) {
|
|
558
|
+
return (typeof value === "object" && value !== null || typeof value === "function") && Symbol.asyncIterator in value;
|
|
559
|
+
}
|
|
1852
560
|
function isInvalidEncryptedContentError(error) {
|
|
1853
561
|
if (!error || typeof error !== "object") return false;
|
|
1854
562
|
const record = error;
|
|
1855
563
|
if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
|
|
1856
|
-
const message = typeof record.message === "string" ? record.message : "";
|
|
1857
|
-
return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || record.status === 400 && message
|
|
564
|
+
const message = typeof record.message === "string" ? record.message.toLowerCase() : "";
|
|
565
|
+
return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || (record.status === 400 || record.status == null && /(?:^|[\s(])400(?:[\s):]|$)/.test(message)) && (message.includes("could not decrypt the provided encrypted_content") || message.includes("encrypted content") && (message.includes("could not be verified") || message.includes("could not be decrypted or parsed")));
|
|
1858
566
|
}
|
|
1859
567
|
function isOrphanedFunctionCallOutputError(error) {
|
|
1860
568
|
if (!error || typeof error !== "object") return false;
|
|
@@ -1916,6 +624,7 @@ async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
|
1916
624
|
commitResponsesEncryptedContentAttempt(attempt, (checkpoint) => {
|
|
1917
625
|
if (checkpoint) params.onCompactionRejected?.(checkpoint);
|
|
1918
626
|
});
|
|
627
|
+
if (!isAsyncIterable(data)) throw new Error("OpenAI Responses streaming request returned a non-stream response");
|
|
1919
628
|
return {
|
|
1920
629
|
stream: data,
|
|
1921
630
|
response,
|
|
@@ -1933,7 +642,45 @@ async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
|
1933
642
|
payloadVariant: attempt.kind
|
|
1934
643
|
});
|
|
1935
644
|
try {
|
|
1936
|
-
|
|
645
|
+
const result = await sendAttempt(attempt);
|
|
646
|
+
return {
|
|
647
|
+
...result,
|
|
648
|
+
stream: { async *[Symbol.asyncIterator]() {
|
|
649
|
+
let rejectedEvent;
|
|
650
|
+
try {
|
|
651
|
+
for await (const event of params.wrapStream?.(result) ?? result.stream) {
|
|
652
|
+
if (isRecord(event)) {
|
|
653
|
+
const failure = event.type === "response.failed" && isRecord(event.response) ? event.response.error : event.type === "error" ? event.error ?? event : void 0;
|
|
654
|
+
if (isRecord(failure) && params.canRetryStream?.() === true && isInvalidEncryptedContentError(failure)) {
|
|
655
|
+
rejectedEvent = event;
|
|
656
|
+
const message = typeof failure.message === "string" ? failure.message : "";
|
|
657
|
+
throw Object.assign(new Error(message), {
|
|
658
|
+
code: failure.code,
|
|
659
|
+
status: failure.status
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
yield event;
|
|
664
|
+
}
|
|
665
|
+
} catch (error) {
|
|
666
|
+
const nextAttempt = params.canRetryStream?.() === true && !params.requestOptions?.signal?.aborted ? await resolveNextResponsesEncryptedContentAttempt(result.attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest }) : void 0;
|
|
667
|
+
if (!nextAttempt) {
|
|
668
|
+
if (rejectedEvent !== void 0) {
|
|
669
|
+
yield rejectedEvent;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
throw error;
|
|
673
|
+
}
|
|
674
|
+
log.warn(`[responses] retrying streamed encrypted content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
675
|
+
yield* (await createResponsesStreamWithEncryptedContentRetry({
|
|
676
|
+
...params,
|
|
677
|
+
request: nextAttempt.request,
|
|
678
|
+
initialAttemptKind: nextAttempt.kind,
|
|
679
|
+
initialRejectedCompaction: nextAttempt.rejectedCompaction
|
|
680
|
+
})).stream;
|
|
681
|
+
}
|
|
682
|
+
} }
|
|
683
|
+
};
|
|
1937
684
|
} catch (error) {
|
|
1938
685
|
let nextAttempt = await resolveNextResponsesEncryptedContentAttempt(attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest });
|
|
1939
686
|
if (!nextAttempt && attempt.request.previous_response_id && error && typeof error === "object" && typeof error.status === "number" && error.code === "previous_response_not_found") {
|
|
@@ -2005,14 +752,14 @@ function createResponsesToolCallTracker() {
|
|
|
2005
752
|
state.callId ??= identity.callId;
|
|
2006
753
|
return state;
|
|
2007
754
|
};
|
|
2008
|
-
const resolveCompatible = (candidates, identity) => {
|
|
755
|
+
const resolveCompatible = (candidates, identity, allowUnmatchedIdentity) => {
|
|
2009
756
|
const uniqueCandidates = [...new Set(candidates)];
|
|
2010
|
-
if (!identity.itemId && !identity.callId) return uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
|
|
757
|
+
if (!identity.itemId && !identity.callId) return allowUnmatchedIdentity && uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
|
|
2011
758
|
const compatible = uniqueCandidates.filter((state) => !identitiesConflict(state, identity));
|
|
2012
759
|
const matches = compatible.filter((state) => sharesIdentity(state, identity));
|
|
2013
760
|
const matched = matches.length === 1 ? matches.at(0) : void 0;
|
|
2014
761
|
if (matched) return adoptIdentity(matched, identity);
|
|
2015
|
-
const soleCompatible = uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
|
|
762
|
+
const soleCompatible = allowUnmatchedIdentity && uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
|
|
2016
763
|
return soleCompatible ? adoptIdentity(soleCompatible, identity) : void 0;
|
|
2017
764
|
};
|
|
2018
765
|
return {
|
|
@@ -2023,9 +770,10 @@ function createResponsesToolCallTracker() {
|
|
|
2023
770
|
return;
|
|
2024
771
|
}
|
|
2025
772
|
if (indexedCalls.has(outputIndex)) throw new Error(`Responses stream reused active tool-call output index ${outputIndex}`);
|
|
773
|
+
state.outputIndex = outputIndex;
|
|
2026
774
|
indexedCalls.set(outputIndex, state);
|
|
2027
775
|
},
|
|
2028
|
-
resolve(event, identity = readEventIdentity(event)) {
|
|
776
|
+
resolve(event, identity = readEventIdentity(event), allowUnmatchedIdentity = true) {
|
|
2029
777
|
const outputIndex = readOutputIndex(event);
|
|
2030
778
|
if (outputIndex !== void 0) {
|
|
2031
779
|
const indexed = indexedCalls.get(outputIndex);
|
|
@@ -2033,14 +781,15 @@ function createResponsesToolCallTracker() {
|
|
|
2033
781
|
if (indexed.callId && identity.callId && indexed.callId !== identity.callId) return;
|
|
2034
782
|
return adoptIdentity(indexed, identity);
|
|
2035
783
|
}
|
|
2036
|
-
const unindexed = resolveCompatible(unindexedCalls, identity);
|
|
784
|
+
const unindexed = resolveCompatible(unindexedCalls, identity, allowUnmatchedIdentity);
|
|
2037
785
|
if (unindexed) {
|
|
2038
786
|
unindexedCalls.delete(unindexed);
|
|
787
|
+
unindexed.outputIndex = outputIndex;
|
|
2039
788
|
indexedCalls.set(outputIndex, unindexed);
|
|
2040
789
|
}
|
|
2041
790
|
return unindexed;
|
|
2042
791
|
}
|
|
2043
|
-
return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity);
|
|
792
|
+
return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity, allowUnmatchedIdentity);
|
|
2044
793
|
},
|
|
2045
794
|
forget(toolCall) {
|
|
2046
795
|
for (const [outputIndex, tracked] of indexedCalls) if (tracked === toolCall) indexedCalls.delete(outputIndex);
|
|
@@ -2051,6 +800,10 @@ function createResponsesToolCallTracker() {
|
|
|
2051
800
|
},
|
|
2052
801
|
hasActive() {
|
|
2053
802
|
return indexedCalls.size > 0 || unindexedCalls.size > 0;
|
|
803
|
+
},
|
|
804
|
+
hasExactlyActive(expected) {
|
|
805
|
+
const active = /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls]);
|
|
806
|
+
return active.size === expected.length && expected.every((state) => active.has(state));
|
|
2054
807
|
}
|
|
2055
808
|
};
|
|
2056
809
|
}
|
|
@@ -2176,9 +929,10 @@ const RESPONSE_FAILED_FAILURE_FIELD_KEYS = [
|
|
|
2176
929
|
function readResponseFailedString(record, key) {
|
|
2177
930
|
return stringifyUnknown(record?.[key]);
|
|
2178
931
|
}
|
|
2179
|
-
function buildResponsesFailedEventSummary(message, responseId, observation) {
|
|
932
|
+
function buildResponsesFailedEventSummary(message, responseId, code, observation) {
|
|
2180
933
|
const summary = { message };
|
|
2181
934
|
if (responseId) summary.responseId = responseId;
|
|
935
|
+
if (code) summary.code = code;
|
|
2182
936
|
if (observation) summary.observation = observation;
|
|
2183
937
|
return summary;
|
|
2184
938
|
}
|
|
@@ -2297,11 +1051,11 @@ function normalizeResponsesFailedEvent(event, model) {
|
|
|
2297
1051
|
if (error) {
|
|
2298
1052
|
const code = readResponseFailedString(error, "code").trim();
|
|
2299
1053
|
const message = readResponseFailedString(error, "message").trim();
|
|
2300
|
-
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId);
|
|
1054
|
+
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId, code || void 0);
|
|
2301
1055
|
}
|
|
2302
1056
|
const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
|
|
2303
1057
|
if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
|
|
2304
|
-
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
1058
|
+
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, void 0, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
2305
1059
|
}
|
|
2306
1060
|
var ResponsesStreamFailure = class extends Error {
|
|
2307
1061
|
constructor(failure, response) {
|
|
@@ -2309,6 +1063,7 @@ var ResponsesStreamFailure = class extends Error {
|
|
|
2309
1063
|
this.name = "ResponsesStreamFailure";
|
|
2310
1064
|
this.responseId = failure.responseId;
|
|
2311
1065
|
this.response = response;
|
|
1066
|
+
this.code = failure.code;
|
|
2312
1067
|
this.observation = failure.observation;
|
|
2313
1068
|
}
|
|
2314
1069
|
};
|
|
@@ -2399,20 +1154,39 @@ async function* observeResponsesStream(stream, model, requestStartedAt) {
|
|
|
2399
1154
|
}
|
|
2400
1155
|
//#endregion
|
|
2401
1156
|
//#region packages/ai/src/transports/openai-responses-stream-slots-internal.ts
|
|
2402
|
-
function
|
|
2403
|
-
const
|
|
1157
|
+
function createResponsesOutputTracker() {
|
|
1158
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
2404
1159
|
const identity = (item) => {
|
|
2405
1160
|
if ((item.type === "reasoning" || item.type === "message") && item.id) return `${item.type}:${item.id}`;
|
|
2406
|
-
|
|
1161
|
+
const callId = item.call_id ?? item.id;
|
|
1162
|
+
return item.type === "function_call" && callId ? `function_call:${callId}` : void 0;
|
|
1163
|
+
};
|
|
1164
|
+
const get = (item, outputIndex) => {
|
|
1165
|
+
const key = identity(item);
|
|
1166
|
+
const output = (outputIndex === void 0 ? void 0 : outputs.get(outputIndex)) ?? (key === void 0 ? void 0 : outputs.get(key));
|
|
1167
|
+
if (!output || outputIndex !== void 0 && output.outputIndex !== void 0 && output.outputIndex !== outputIndex) return;
|
|
1168
|
+
if (output.type !== item.type || output.callId && item.call_id && output.callId !== item.call_id) throw new Error("Responses stream changed output item identity");
|
|
1169
|
+
return output;
|
|
2407
1170
|
};
|
|
2408
1171
|
return {
|
|
2409
|
-
get
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
1172
|
+
get,
|
|
1173
|
+
set(item, contentIndex, outputIndex, completed = false) {
|
|
1174
|
+
const output = get(item, outputIndex) ?? {
|
|
1175
|
+
type: item.type,
|
|
1176
|
+
contentIndex,
|
|
1177
|
+
completed
|
|
1178
|
+
};
|
|
1179
|
+
Object.assign(output, {
|
|
1180
|
+
contentIndex,
|
|
1181
|
+
completed
|
|
1182
|
+
});
|
|
1183
|
+
if (item.call_id) output.callId = item.call_id;
|
|
1184
|
+
if (outputIndex !== void 0) {
|
|
1185
|
+
output.outputIndex = outputIndex;
|
|
1186
|
+
outputs.set(outputIndex, output);
|
|
1187
|
+
}
|
|
2414
1188
|
const key = identity(item);
|
|
2415
|
-
if (key !== void 0)
|
|
1189
|
+
if (key !== void 0) outputs.set(key, output);
|
|
2416
1190
|
}
|
|
2417
1191
|
};
|
|
2418
1192
|
}
|
|
@@ -2472,8 +1246,11 @@ function createResponsesOutputSlotTracker() {
|
|
|
2472
1246
|
* package and managed transports from drifting on token buckets, service-tier pricing, or future
|
|
2473
1247
|
* terminal-event semantics.
|
|
2474
1248
|
*/
|
|
1249
|
+
function readReportedCount(value) {
|
|
1250
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1251
|
+
}
|
|
2475
1252
|
function readCount(value) {
|
|
2476
|
-
return
|
|
1253
|
+
return readReportedCount(value) ?? 0;
|
|
2477
1254
|
}
|
|
2478
1255
|
/**
|
|
2479
1256
|
* Split a terminal usage payload into the priced buckets.
|
|
@@ -2491,12 +1268,21 @@ function mapResponsesTerminalUsage(usage) {
|
|
|
2491
1268
|
const input = Math.max(0, readCount(usage.input_tokens) - cacheRead - cacheWrite);
|
|
2492
1269
|
const output = readCount(usage.output_tokens);
|
|
2493
1270
|
const bucketTotal = input + output + cacheRead + cacheWrite;
|
|
1271
|
+
const totalTokens = Math.max(bucketTotal, readCount(usage.total_tokens));
|
|
1272
|
+
const reportedInput = readReportedCount(usage.input_tokens);
|
|
1273
|
+
const reportedOutput = readReportedCount(usage.output_tokens);
|
|
1274
|
+
const reportedTotal = readReportedCount(usage.total_tokens);
|
|
2494
1275
|
return {
|
|
2495
1276
|
input,
|
|
2496
1277
|
output,
|
|
2497
1278
|
cacheRead,
|
|
2498
1279
|
cacheWrite,
|
|
2499
|
-
|
|
1280
|
+
contextUsage: reportedInput !== void 0 && (reportedOutput !== void 0 || reportedTotal !== void 0 && reportedTotal >= reportedInput) && cacheRead + cacheWrite <= reportedInput ? {
|
|
1281
|
+
state: "available",
|
|
1282
|
+
promptTokens: reportedInput,
|
|
1283
|
+
totalTokens: Math.max(totalTokens, reportedInput + (reportedOutput ?? 0))
|
|
1284
|
+
} : { state: "unavailable" },
|
|
1285
|
+
totalTokens
|
|
2500
1286
|
};
|
|
2501
1287
|
}
|
|
2502
1288
|
/** Reasoning tokens are reported by the agent path only; the package path does not track them. */
|
|
@@ -2553,62 +1339,61 @@ function resolveCompletedResponsesToolCall(item, streamed) {
|
|
|
2553
1339
|
if (streamedName && completedName && streamedName !== completedName) throw new Error(`Responses stream changed tool-call function name from ${streamedName} to ${completedName}`);
|
|
2554
1340
|
const name = completedName ?? streamedName;
|
|
2555
1341
|
if (!name) throw new Error("Responses stream completed tool call without a function name");
|
|
2556
|
-
const argumentsValue = parseJsonObjectPreservingUnsafeIntegers(streamed?.arguments ?? item.arguments);
|
|
2557
|
-
if (!argumentsValue) throw new Error("Responses stream completed tool call with invalid JSON arguments");
|
|
2558
1342
|
return {
|
|
2559
1343
|
name,
|
|
2560
|
-
arguments:
|
|
1344
|
+
arguments: parseTerminalToolCallArguments(streamed?.arguments ?? item.arguments, "Responses stream completed tool call with invalid JSON arguments")
|
|
2561
1345
|
};
|
|
2562
1346
|
}
|
|
2563
1347
|
function createResponsesTerminalController(params) {
|
|
2564
1348
|
const { output, stream, model, options } = params;
|
|
2565
1349
|
const blocks = output.content;
|
|
2566
1350
|
const backfillReasoning = (items) => {
|
|
2567
|
-
for (const item of items) {
|
|
1351
|
+
for (const [outputIndex, item] of items.entries()) {
|
|
2568
1352
|
if (item.type !== "reasoning" || !item.encrypted_content) continue;
|
|
2569
|
-
const
|
|
2570
|
-
|
|
1353
|
+
const tracked = params.outputs.get(item, outputIndex);
|
|
1354
|
+
const block = tracked && blocks[tracked.contentIndex];
|
|
1355
|
+
if (block?.type !== "thinking" || !block.thinkingSignature) continue;
|
|
2571
1356
|
const stored = JSON.parse(block.thinkingSignature);
|
|
2572
1357
|
if (!stored.encrypted_content) block.thinkingSignature = JSON.stringify({
|
|
2573
1358
|
...stored,
|
|
2574
1359
|
encrypted_content: item.encrypted_content
|
|
2575
1360
|
});
|
|
2576
|
-
if (options?.reasoningReplayMetadata) block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY]
|
|
1361
|
+
if (options?.reasoningReplayMetadata) Object.assign(block, { [OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY]: options.reasoningReplayMetadata });
|
|
2577
1362
|
}
|
|
2578
1363
|
};
|
|
2579
|
-
const appendText = (item) => {
|
|
1364
|
+
const appendText = (item, contentIndex) => {
|
|
2580
1365
|
const text = (Array.isArray(item.content) ? item.content : []).map((part) => {
|
|
2581
1366
|
const content = part;
|
|
2582
1367
|
return content.type === "output_text" || content.type === "text" ? content.text ?? "" : content.refusal ?? "";
|
|
2583
1368
|
}).join("");
|
|
2584
|
-
const
|
|
1369
|
+
const block = contentIndex === void 0 ? void 0 : blocks[contentIndex];
|
|
1370
|
+
const started = block?.type === "text" ? block : void 0;
|
|
2585
1371
|
if (!text && !started) return;
|
|
2586
1372
|
const phase = item.phase ?? void 0;
|
|
2587
|
-
if (started) {
|
|
2588
|
-
const previousText = started.
|
|
2589
|
-
started.
|
|
2590
|
-
started.
|
|
1373
|
+
if (started && contentIndex !== void 0) {
|
|
1374
|
+
const previousText = started.text;
|
|
1375
|
+
started.text = text;
|
|
1376
|
+
started.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2591
1377
|
params.setLastTextBlock({
|
|
2592
|
-
block: started
|
|
2593
|
-
index:
|
|
1378
|
+
block: started,
|
|
1379
|
+
index: contentIndex,
|
|
2594
1380
|
phase
|
|
2595
1381
|
});
|
|
2596
|
-
params.startedTextBlocksByItemId.delete(item.id);
|
|
2597
1382
|
if (text.startsWith(previousText)) {
|
|
2598
1383
|
const delta = text.slice(previousText.length);
|
|
2599
1384
|
if (delta) stream.push({
|
|
2600
1385
|
type: "text_delta",
|
|
2601
|
-
contentIndex
|
|
1386
|
+
contentIndex,
|
|
2602
1387
|
delta
|
|
2603
1388
|
});
|
|
2604
1389
|
}
|
|
2605
1390
|
stream.push({
|
|
2606
1391
|
type: "text_end",
|
|
2607
|
-
contentIndex
|
|
1392
|
+
contentIndex,
|
|
2608
1393
|
content: text,
|
|
2609
1394
|
partial: output
|
|
2610
1395
|
});
|
|
2611
|
-
return
|
|
1396
|
+
return contentIndex;
|
|
2612
1397
|
}
|
|
2613
1398
|
const previous = params.getLastTextBlock();
|
|
2614
1399
|
const collapse = resolveResponsesMessageSnapshotCollapse({
|
|
@@ -2630,15 +1415,15 @@ function createResponsesTerminalController(params) {
|
|
|
2630
1415
|
});
|
|
2631
1416
|
return previous.index;
|
|
2632
1417
|
}
|
|
2633
|
-
const
|
|
1418
|
+
const newBlock = {
|
|
2634
1419
|
type: "text",
|
|
2635
1420
|
text,
|
|
2636
1421
|
textSignature: encodeTextSignatureV1(item.id, phase)
|
|
2637
1422
|
};
|
|
2638
|
-
blocks.push(
|
|
1423
|
+
blocks.push(newBlock);
|
|
2639
1424
|
const index = blocks.length - 1;
|
|
2640
1425
|
params.setLastTextBlock({
|
|
2641
|
-
block,
|
|
1426
|
+
block: newBlock,
|
|
2642
1427
|
index,
|
|
2643
1428
|
phase
|
|
2644
1429
|
});
|
|
@@ -2655,65 +1440,71 @@ function createResponsesTerminalController(params) {
|
|
|
2655
1440
|
});
|
|
2656
1441
|
return index;
|
|
2657
1442
|
};
|
|
2658
|
-
const
|
|
2659
|
-
const
|
|
2660
|
-
|
|
1443
|
+
const emitToolCallCompletion = (item, outputIndex, started, validated) => {
|
|
1444
|
+
const completed = {
|
|
1445
|
+
id: resolveResponsesToolCallId(item, started?.block.id),
|
|
1446
|
+
...validated
|
|
1447
|
+
};
|
|
1448
|
+
const toolCall = started ? Object.assign(started.block, completed) : {
|
|
2661
1449
|
type: "toolCall",
|
|
2662
|
-
|
|
2663
|
-
name: validated.name,
|
|
2664
|
-
arguments: validated.arguments
|
|
1450
|
+
...completed
|
|
2665
1451
|
};
|
|
2666
|
-
|
|
2667
|
-
const contentIndex = blocks.length
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
1452
|
+
delete toolCall.partialJson;
|
|
1453
|
+
const contentIndex = started?.contentIndex ?? blocks.length;
|
|
1454
|
+
if (!started) {
|
|
1455
|
+
blocks.push(toolCall);
|
|
1456
|
+
stream.push({
|
|
1457
|
+
type: "toolcall_start",
|
|
1458
|
+
contentIndex,
|
|
1459
|
+
partial: output
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
params.outputs.set(item, contentIndex, outputIndex, true);
|
|
2673
1463
|
stream.push({
|
|
2674
1464
|
type: "toolcall_end",
|
|
2675
1465
|
contentIndex,
|
|
2676
1466
|
toolCall,
|
|
2677
1467
|
partial: output
|
|
2678
1468
|
});
|
|
2679
|
-
return contentIndex;
|
|
2680
1469
|
};
|
|
2681
|
-
const recoverTerminalOutput = (items,
|
|
1470
|
+
const recoverTerminalOutput = (items, completeToolCall) => {
|
|
2682
1471
|
let hasCompletedLaterOutput = false;
|
|
2683
|
-
for (const item of items.toReversed()) {
|
|
1472
|
+
for (const [outputIndex, item] of [...items.entries()].toReversed()) {
|
|
1473
|
+
const tracked = params.outputs.get(item, outputIndex);
|
|
2684
1474
|
if (item.type === "reasoning") {
|
|
2685
|
-
hasCompletedLaterOutput ||=
|
|
1475
|
+
hasCompletedLaterOutput ||= tracked !== void 0;
|
|
2686
1476
|
continue;
|
|
2687
1477
|
}
|
|
2688
1478
|
if (item.type !== "message" && item.type !== "function_call") continue;
|
|
2689
|
-
if (
|
|
1479
|
+
if (tracked) {
|
|
2690
1480
|
hasCompletedLaterOutput = true;
|
|
2691
1481
|
continue;
|
|
2692
1482
|
}
|
|
2693
|
-
if (item.type === "function_call" && !
|
|
1483
|
+
if (item.type === "function_call" && !completeToolCall) continue;
|
|
2694
1484
|
if (hasCompletedLaterOutput) throw new Error("Responses stream omitted an output item before completed output");
|
|
2695
|
-
if (item.type === "function_call") resolveCompletedResponsesToolCall(item);
|
|
2696
1485
|
}
|
|
2697
1486
|
for (const [terminalIndex, item] of items.entries()) if (item.type === "message") {
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
1487
|
+
const tracked = params.outputs.get(item, terminalIndex);
|
|
1488
|
+
if (tracked?.completed) continue;
|
|
1489
|
+
const appendedIndex = appendText(item, tracked?.contentIndex);
|
|
1490
|
+
if (appendedIndex !== void 0) params.outputs.set(item, appendedIndex, terminalIndex, true);
|
|
2701
1491
|
} else {
|
|
2702
1492
|
params.setLastTextBlock(null);
|
|
2703
1493
|
const alreadyCapturedCompaction = item.type === "compaction" && output.providerReplay?.type === "openai-responses-compaction" && output.providerReplay.id === item.id && output.providerReplay.data === item.encrypted_content;
|
|
2704
1494
|
if (item.type === "compaction" && !alreadyCapturedCompaction) {
|
|
2705
1495
|
let replayIndex = blocks.length;
|
|
2706
|
-
for (const laterItem of items.
|
|
2707
|
-
|
|
1496
|
+
for (const [laterIndex, laterItem] of items.entries()) {
|
|
1497
|
+
if (laterIndex <= terminalIndex) continue;
|
|
1498
|
+
const laterContentIndex = params.outputs.get(laterItem, laterIndex)?.contentIndex;
|
|
2708
1499
|
if (laterContentIndex !== void 0) {
|
|
2709
1500
|
replayIndex = laterContentIndex;
|
|
2710
1501
|
break;
|
|
2711
1502
|
}
|
|
2712
1503
|
}
|
|
2713
1504
|
captureOpenAIResponsesCompaction(output, item, replayIndex, model, options?.reasoningReplayMetadata);
|
|
2714
|
-
} else if (
|
|
2715
|
-
if (params.
|
|
2716
|
-
|
|
1505
|
+
} else if (completeToolCall && item.type === "function_call") {
|
|
1506
|
+
if (params.outputs.get(item, terminalIndex)?.completed) continue;
|
|
1507
|
+
completeToolCall(terminalIndex);
|
|
2717
1508
|
}
|
|
2718
1509
|
}
|
|
2719
1510
|
};
|
|
@@ -2740,7 +1531,6 @@ function createResponsesTerminalController(params) {
|
|
|
2740
1531
|
}
|
|
2741
1532
|
};
|
|
2742
1533
|
const finalizeResponse = (response, terminalEventType) => {
|
|
2743
|
-
params.markFinalized();
|
|
2744
1534
|
backfillReasoning(response.output ?? []);
|
|
2745
1535
|
finalizeTerminalFacts(response);
|
|
2746
1536
|
const terminal = resolveResponsesTerminalStopReason({
|
|
@@ -2755,7 +1545,8 @@ function createResponsesTerminalController(params) {
|
|
|
2755
1545
|
return {
|
|
2756
1546
|
finalizeResponse,
|
|
2757
1547
|
finalizeFailedResponse: finalizeTerminalFacts,
|
|
2758
|
-
recoverTerminalOutput
|
|
1548
|
+
recoverTerminalOutput,
|
|
1549
|
+
emitToolCallCompletion
|
|
2759
1550
|
};
|
|
2760
1551
|
}
|
|
2761
1552
|
//#endregion
|
|
@@ -2763,9 +1554,7 @@ function createResponsesTerminalController(params) {
|
|
|
2763
1554
|
async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
2764
1555
|
const streamingToolCalls = createResponsesToolCallTracker();
|
|
2765
1556
|
const outputSlots = createResponsesOutputSlotTracker();
|
|
2766
|
-
const
|
|
2767
|
-
const outputItemContentIndexes = createResponsesOutputContentIndex();
|
|
2768
|
-
const startedTextBlocksByItemId = /* @__PURE__ */ new Map();
|
|
1557
|
+
const outputs = createResponsesOutputTracker();
|
|
2769
1558
|
let terminalResponse;
|
|
2770
1559
|
let lastTextBlock = null;
|
|
2771
1560
|
const blocks = output.content;
|
|
@@ -2780,11 +1569,11 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2780
1569
|
type: "thinking",
|
|
2781
1570
|
item,
|
|
2782
1571
|
block,
|
|
2783
|
-
contentIndex: blocks.length
|
|
1572
|
+
contentIndex: blocks.length,
|
|
1573
|
+
outputIndex: readResponsesOutputIndex(event)
|
|
2784
1574
|
};
|
|
2785
1575
|
blocks.push(block);
|
|
2786
|
-
|
|
2787
|
-
outputItemContentIndexes.set(item, slot.contentIndex);
|
|
1576
|
+
outputs.set(item, slot.contentIndex, slot.outputIndex);
|
|
2788
1577
|
outputSlots.register(event, slot);
|
|
2789
1578
|
stream.push({
|
|
2790
1579
|
type: "thinking_start",
|
|
@@ -2806,17 +1595,13 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2806
1595
|
item: messageItem,
|
|
2807
1596
|
block,
|
|
2808
1597
|
contentIndex: block ? blocks.length : void 0,
|
|
1598
|
+
outputIndex: readResponsesOutputIndex(event),
|
|
2809
1599
|
pendingText: collapseCandidate ? "" : null,
|
|
2810
1600
|
collapseCandidate
|
|
2811
1601
|
};
|
|
2812
1602
|
if (block) {
|
|
2813
1603
|
blocks.push(block);
|
|
2814
|
-
|
|
2815
|
-
startedTextBlocksByItemId.set(messageItem.id, {
|
|
2816
|
-
block,
|
|
2817
|
-
index: slot.contentIndex ?? blocks.length - 1,
|
|
2818
|
-
phase: messageItem.phase ?? void 0
|
|
2819
|
-
});
|
|
1604
|
+
outputs.set(messageItem, slot.contentIndex ?? blocks.length - 1, slot.outputIndex);
|
|
2820
1605
|
}
|
|
2821
1606
|
outputSlots.register(event, slot);
|
|
2822
1607
|
if (slot.contentIndex !== void 0) stream.push({
|
|
@@ -2832,9 +1617,6 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2832
1617
|
if (item.type === "message") return outputSlots.resolve(event, "text");
|
|
2833
1618
|
return readResponsesOutputIndex(event) === void 0 ? void 0 : outputSlots.get(event);
|
|
2834
1619
|
};
|
|
2835
|
-
const getOrCreateOutputSlot = (event, item) => {
|
|
2836
|
-
return resolveOutputItemSlot(event, item) ?? createOutputSlot(event, item);
|
|
2837
|
-
};
|
|
2838
1620
|
const materializeDeferredTextSlot = (slot) => {
|
|
2839
1621
|
if (slot.block || slot.pendingText === null) return;
|
|
2840
1622
|
const text = slot.pendingText;
|
|
@@ -2845,12 +1627,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2845
1627
|
};
|
|
2846
1628
|
blocks.push(slot.block);
|
|
2847
1629
|
slot.contentIndex = blocks.length - 1;
|
|
2848
|
-
|
|
2849
|
-
startedTextBlocksByItemId.set(slot.item.id, {
|
|
2850
|
-
block: slot.block,
|
|
2851
|
-
index: slot.contentIndex,
|
|
2852
|
-
phase: slot.item.phase ?? void 0
|
|
2853
|
-
});
|
|
1630
|
+
outputs.set(slot.item, slot.contentIndex, slot.outputIndex);
|
|
2854
1631
|
stream.push({
|
|
2855
1632
|
type: "text_start",
|
|
2856
1633
|
contentIndex: slot.contentIndex,
|
|
@@ -2868,20 +1645,58 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2868
1645
|
const materializeDeferredTextSlots = (except) => {
|
|
2869
1646
|
for (const slot of outputSlots.values()) if (slot !== except && slot.type === "text") materializeDeferredTextSlot(slot);
|
|
2870
1647
|
};
|
|
2871
|
-
const
|
|
1648
|
+
const terminal = createResponsesTerminalController({
|
|
2872
1649
|
output,
|
|
2873
1650
|
stream,
|
|
2874
1651
|
model,
|
|
2875
1652
|
options,
|
|
2876
|
-
|
|
2877
|
-
startedTextBlocksByItemId,
|
|
2878
|
-
outputItemContentIndexes,
|
|
1653
|
+
outputs,
|
|
2879
1654
|
getLastTextBlock: () => lastTextBlock,
|
|
2880
1655
|
setLastTextBlock: (block) => {
|
|
2881
1656
|
lastTextBlock = block;
|
|
2882
|
-
}
|
|
2883
|
-
markFinalized: () => void 0
|
|
1657
|
+
}
|
|
2884
1658
|
});
|
|
1659
|
+
const finalizeToolCall = (item, outputIndex, streamingToolCall, validated) => {
|
|
1660
|
+
const identity = {
|
|
1661
|
+
type: item.type,
|
|
1662
|
+
id: item.id || streamingToolCall?.itemId,
|
|
1663
|
+
call_id: item.call_id || streamingToolCall?.callId
|
|
1664
|
+
};
|
|
1665
|
+
const finalOutputIndex = outputIndex ?? streamingToolCall?.outputIndex;
|
|
1666
|
+
if (finalOutputIndex === void 0 && !identity.id && !identity.call_id) {
|
|
1667
|
+
if (!streamingToolCall) throw new Error("Responses stream completed tool call without an output identity");
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
if (streamingToolCall) {
|
|
1671
|
+
streamingToolCalls.forget(streamingToolCall);
|
|
1672
|
+
for (const slot of outputSlots.values()) if (slot.type === "toolCall" && slot.toolCall === streamingToolCall) outputSlots.forget(slot);
|
|
1673
|
+
}
|
|
1674
|
+
terminal.emitToolCallCompletion(identity, finalOutputIndex, streamingToolCall, validated);
|
|
1675
|
+
};
|
|
1676
|
+
const prepareTerminalToolCalls = (items) => {
|
|
1677
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
1678
|
+
const recovered = [];
|
|
1679
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
1680
|
+
const allowUnmatchedIdentity = items.filter((item, index) => item.type === "function_call" && !outputs.get(item, index)?.completed).length === 1;
|
|
1681
|
+
for (const [outputIndex, item] of items.entries()) {
|
|
1682
|
+
const tracked = outputs.get(item, outputIndex);
|
|
1683
|
+
if (item.type !== "function_call") continue;
|
|
1684
|
+
if (item.call_id && callIds.has(item.call_id)) throw new Error("Responses stream repeated a terminal tool-call identity");
|
|
1685
|
+
if (item.call_id) callIds.add(item.call_id);
|
|
1686
|
+
if (tracked?.completed) continue;
|
|
1687
|
+
const state = streamingToolCalls.resolve({ output_index: outputIndex }, readResponsesToolCallItemIdentity(item), allowUnmatchedIdentity);
|
|
1688
|
+
if (tracked && !state) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1689
|
+
const validated = resolveCompletedResponsesToolCall(item, { name: state?.block.name });
|
|
1690
|
+
if (state) recovered.push(state);
|
|
1691
|
+
prepared.set(outputIndex, () => finalizeToolCall(item, outputIndex, state, validated));
|
|
1692
|
+
}
|
|
1693
|
+
if (!streamingToolCalls.hasExactlyActive(recovered)) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1694
|
+
return (outputIndex) => {
|
|
1695
|
+
const complete = prepared.get(outputIndex);
|
|
1696
|
+
if (!complete) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1697
|
+
complete();
|
|
1698
|
+
};
|
|
1699
|
+
};
|
|
2885
1700
|
const guardedStream = adaptResponsesStream(withFirstStreamEventTimeout(openaiStream, {
|
|
2886
1701
|
provider: model.provider,
|
|
2887
1702
|
api: model.api,
|
|
@@ -2915,6 +1730,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2915
1730
|
block: toolCallBlock,
|
|
2916
1731
|
contentIndex,
|
|
2917
1732
|
argumentStreamReliable: true,
|
|
1733
|
+
previewSchedule: createToolArgumentPreviewSchedule(),
|
|
2918
1734
|
...readResponsesToolCallItemIdentity(item)
|
|
2919
1735
|
};
|
|
2920
1736
|
streamingToolCalls.register(event, toolCallState);
|
|
@@ -2923,7 +1739,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2923
1739
|
toolCall: toolCallState
|
|
2924
1740
|
});
|
|
2925
1741
|
output.content.push(toolCallBlock);
|
|
2926
|
-
|
|
1742
|
+
outputs.set(item, contentIndex, readResponsesOutputIndex(event));
|
|
2927
1743
|
stream.push({
|
|
2928
1744
|
type: "toolcall_start",
|
|
2929
1745
|
contentIndex,
|
|
@@ -3049,7 +1865,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3049
1865
|
const toolCall = streamingToolCalls.resolve(event);
|
|
3050
1866
|
if (toolCall) {
|
|
3051
1867
|
toolCall.block.partialJson += event.delta;
|
|
3052
|
-
toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
1868
|
+
if (toolCall.previewSchedule(toolCall.block.partialJson.length)) toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
3053
1869
|
stream.push({
|
|
3054
1870
|
type: "toolcall_delta",
|
|
3055
1871
|
contentIndex: toolCall.contentIndex,
|
|
@@ -3082,13 +1898,14 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3082
1898
|
if (item.type !== "message") lastTextBlock = null;
|
|
3083
1899
|
const existingOutputSlot = resolveOutputItemSlot(event, item);
|
|
3084
1900
|
materializeDeferredTextSlots(existingOutputSlot);
|
|
3085
|
-
const outputSlot = existingOutputSlot ??
|
|
1901
|
+
const outputSlot = existingOutputSlot ?? createOutputSlot(event, item);
|
|
3086
1902
|
compactionTracker.completed(item, blocks.length);
|
|
3087
1903
|
if (item.type === "reasoning" && outputSlot?.type === "thinking") {
|
|
3088
1904
|
const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
|
|
3089
1905
|
const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
|
|
3090
1906
|
outputSlot.block.thinking = summaryText || contentText || outputSlot.block.thinking;
|
|
3091
1907
|
outputSlot.block.thinkingSignature = JSON.stringify(item);
|
|
1908
|
+
outputs.set(item, outputSlot.contentIndex, readResponsesOutputIndex(event) ?? outputSlot.outputIndex, true);
|
|
3092
1909
|
if (item.encrypted_content && options?.reasoningReplayMetadata) outputSlot.block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = options.reasoningReplayMetadata;
|
|
3093
1910
|
stream.push({
|
|
3094
1911
|
type: "thinking_end",
|
|
@@ -3120,7 +1937,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3120
1937
|
partial: output
|
|
3121
1938
|
});
|
|
3122
1939
|
lastTextBlock = outputSlot.collapseCandidate;
|
|
3123
|
-
|
|
1940
|
+
outputs.set(item, outputSlot.collapseCandidate.index, readResponsesOutputIndex(event) ?? outputSlot.outputIndex, true);
|
|
3124
1941
|
} else {
|
|
3125
1942
|
if (!outputSlot.block) {
|
|
3126
1943
|
outputSlot.block = {
|
|
@@ -3145,7 +1962,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3145
1962
|
index: contentIndex,
|
|
3146
1963
|
phase
|
|
3147
1964
|
};
|
|
3148
|
-
|
|
1965
|
+
outputs.set(item, contentIndex, readResponsesOutputIndex(event) ?? outputSlot.outputIndex, true);
|
|
3149
1966
|
stream.push({
|
|
3150
1967
|
type: "text_end",
|
|
3151
1968
|
contentIndex,
|
|
@@ -3154,66 +1971,33 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3154
1971
|
});
|
|
3155
1972
|
}
|
|
3156
1973
|
outputSlots.forget(outputSlot);
|
|
3157
|
-
startedTextBlocksByItemId.delete(item.id);
|
|
3158
1974
|
} else if (item.type === "function_call") {
|
|
1975
|
+
if (outputs.get(item, readResponsesOutputIndex(event))?.completed) continue;
|
|
3159
1976
|
const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
|
|
3160
1977
|
if (!streamingToolCall && streamingToolCalls.hasActive()) continue;
|
|
3161
|
-
const streamedArguments = streamingToolCall?.block.partialJson ?? "";
|
|
3162
1978
|
const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
|
|
3163
1979
|
if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) continue;
|
|
3164
|
-
const finalArguments = completedArguments !== void 0 && (completedArguments.length > 0 || !streamedArguments) ? completedArguments : streamedArguments;
|
|
3165
1980
|
const validated = resolveCompletedResponsesToolCall(item, {
|
|
3166
1981
|
name: streamingToolCall?.block.name,
|
|
3167
|
-
arguments:
|
|
1982
|
+
arguments: completedArguments || streamingToolCall?.block.partialJson || ""
|
|
3168
1983
|
});
|
|
3169
|
-
|
|
3170
|
-
let contentIndex;
|
|
3171
|
-
if (streamingToolCall) {
|
|
3172
|
-
const block = streamingToolCall.block;
|
|
3173
|
-
block.id = resolveResponsesToolCallId(item, block.id);
|
|
3174
|
-
block.name = validated.name;
|
|
3175
|
-
block.arguments = validated.arguments;
|
|
3176
|
-
delete block.partialJson;
|
|
3177
|
-
toolCall = block;
|
|
3178
|
-
contentIndex = streamingToolCall.contentIndex;
|
|
3179
|
-
} else {
|
|
3180
|
-
toolCall = {
|
|
3181
|
-
type: "toolCall",
|
|
3182
|
-
id: resolveResponsesToolCallId(item),
|
|
3183
|
-
name: validated.name,
|
|
3184
|
-
arguments: validated.arguments
|
|
3185
|
-
};
|
|
3186
|
-
blocks.push(toolCall);
|
|
3187
|
-
contentIndex = blocks.length - 1;
|
|
3188
|
-
stream.push({
|
|
3189
|
-
type: "toolcall_start",
|
|
3190
|
-
contentIndex,
|
|
3191
|
-
partial: output
|
|
3192
|
-
});
|
|
3193
|
-
}
|
|
3194
|
-
if (streamingToolCall) {
|
|
3195
|
-
streamingToolCalls.forget(streamingToolCall);
|
|
3196
|
-
for (const slot of outputSlots.values()) if (slot.type === "toolCall" && slot.toolCall === streamingToolCall) outputSlots.forget(slot);
|
|
3197
|
-
}
|
|
3198
|
-
stream.push({
|
|
3199
|
-
type: "toolcall_end",
|
|
3200
|
-
contentIndex,
|
|
3201
|
-
toolCall,
|
|
3202
|
-
partial: output
|
|
3203
|
-
});
|
|
3204
|
-
outputItemContentIndexes.set(item, contentIndex);
|
|
1984
|
+
finalizeToolCall(item, readResponsesOutputIndex(event), streamingToolCall, validated);
|
|
3205
1985
|
}
|
|
3206
1986
|
} else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
3207
|
-
if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
3208
|
-
finalizeResponse(event.response, event.type);
|
|
3209
|
-
if (event.type === "response.completed" || output.stopReason === "length")
|
|
1987
|
+
if (event.type === "response.incomplete" && streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1988
|
+
terminal.finalizeResponse(event.response, event.type);
|
|
1989
|
+
if (event.type === "response.completed" || output.stopReason === "length") {
|
|
1990
|
+
const items = event.response.output ?? [];
|
|
1991
|
+
const completeToolCall = event.type === "response.completed" ? prepareTerminalToolCalls(items) : void 0;
|
|
1992
|
+
terminal.recoverTerminalOutput(items, completeToolCall);
|
|
1993
|
+
}
|
|
3210
1994
|
terminalResponse = event.type === "response.completed" ? event.response : null;
|
|
3211
1995
|
if (output.stopReason === "stop" && output.content.some((block) => block.type === "toolCall")) output.stopReason = "toolUse";
|
|
3212
1996
|
break;
|
|
3213
1997
|
} else if (event.type === "error") throw new Error(event.message ? `Error Code ${event.code}: ${event.message}` : "Unknown error");
|
|
3214
1998
|
else if (event.type === "response.failed") {
|
|
3215
|
-
const failure = normalizeResponsesFailedEvent(event, model);
|
|
3216
|
-
finalizeFailedResponse(event.response, failure.responseId);
|
|
1999
|
+
const failure = normalizeResponsesFailedEvent(isRecord(event) ? event : {}, model);
|
|
2000
|
+
terminal.finalizeFailedResponse(event.response, failure.responseId);
|
|
3217
2001
|
throw new ResponsesStreamFailure(failure, event.response);
|
|
3218
2002
|
}
|
|
3219
2003
|
}
|
|
@@ -3367,26 +2151,34 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
3367
2151
|
return request;
|
|
3368
2152
|
};
|
|
3369
2153
|
const requestParams = await buildRequest("checkpoint");
|
|
3370
|
-
|
|
3371
|
-
|
|
2154
|
+
const firstEvent = createFirstStreamEventAbortController(options?.signal);
|
|
2155
|
+
firstEventAbort = firstEvent;
|
|
2156
|
+
let started = false;
|
|
2157
|
+
const { stream: hookedOpenAIStream } = await createResponsesStreamWithEncryptedContentRetry({
|
|
3372
2158
|
client,
|
|
3373
2159
|
request: requestParams,
|
|
3374
2160
|
requestOptions: {
|
|
3375
2161
|
...buildResponsesRequestOptions(options),
|
|
3376
|
-
signal:
|
|
2162
|
+
signal: firstEvent.signal
|
|
3377
2163
|
},
|
|
3378
2164
|
model,
|
|
3379
2165
|
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
3380
|
-
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, options, checkpoint)
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
2166
|
+
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, options, checkpoint),
|
|
2167
|
+
canRetryStream: () => output.content.length === 0,
|
|
2168
|
+
wrapStream: ({ stream: openaiStream, response }) => withProviderResponseHook({
|
|
2169
|
+
stream: openaiStream,
|
|
2170
|
+
signal: firstEvent.signal,
|
|
2171
|
+
abort: firstEvent.abort,
|
|
2172
|
+
hook: createOpenAIProviderAcceptanceHook(options, response, model),
|
|
2173
|
+
onReady: () => {
|
|
2174
|
+
if (!started) {
|
|
2175
|
+
started = true;
|
|
2176
|
+
stream.push({
|
|
2177
|
+
type: "start",
|
|
2178
|
+
partial: output
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
3390
2182
|
})
|
|
3391
2183
|
});
|
|
3392
2184
|
const firstEventTimeoutMs = getFirstStreamEventTimeoutMs(options);
|
|
@@ -3427,4 +2219,4 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
3427
2219
|
}
|
|
3428
2220
|
}
|
|
3429
2221
|
//#endregion
|
|
3430
|
-
export {
|
|
2222
|
+
export { isAzureResponsesTextDeltaEvent as A, buildResponsesInputMessage as B, summarizeResponsesTools as C, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as D, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as E, commitResponsesEncryptedContentAttempt as F, captureOpenAIResponsesCompaction as G, createOpenAIResponsesAssistantOutput as H, createResponsesStreamWithEncryptedContentRetry as I, resolveReplayableResponsesMessageId as J, resolveNewestOpenAIResponsesCompactionReplay as K, isInvalidEncryptedContentError as L, isResponsesTextContentPartType as M, isResponsesTextDeltaEventType as N, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as O, resolveResponsesMessageSnapshotCollapse as P, resolveAzureOpenAIApiVersion as R, summarizeResponsesPayload as S, readResponsesToolCallItemIdentity as T, CompactionReplayRefreshRequiredError as U, convertResponsesMessages$1 as V, buildOpenAIResponsesReasoningReplayMetadata as W, resolveModelPayloadDebugMode as X, emitModelTransportDebug as Y, resolveModelSseDebugMode as Z, safeDebugValue as _, resolveResponsesReasoningEffort as a, summarizeOpenAITransportError as b, processResponsesStream as c, resolveResponsesTerminalStopReason as d, observeResponsesStream as f, normalizeResponsesFailedEvent as g, logResponsesFailedNoDetails as h, createResponsesAssistantOutput as i, isAzureResponsesTextDeltaEventType as j, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as k, mapResponsesTerminalUsage as l, buildResponsesFailedNoDetailsObservation as m, applyResponsesServiceTierPricing as n, runResponsesStreamLifecycle as o, ResponsesStreamFailure as p, suppressOpenAIResponsesCompaction as q, convertResponsesMessages as r, convertResponsesToolPayload as s, applyCommonResponsesParams as t, readResponsesReasoningTokens as u, stringifyRedactedEvent as v, createResponsesToolCallTracker as w, summarizeResponsesFailedNoDetailsObservation as x, stringifyRedactedPayload as y, resolveNextResponsesEncryptedContentAttempt as z };
|