@openclaw/ai 2026.8.1-beta.3 → 2026.8.2
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-B8KwTtlb.mjs → anthropic-DTGZ4VMY.mjs} +25 -23
- package/dist/{anthropic-compaction-replay-4Zx7-aCr.mjs → anthropic-compaction-replay-CKCDrZJv.mjs} +34 -35
- package/dist/{anthropic-payload-policy-CSx7MoKH.d.mts → anthropic-payload-policy-BTzmNC14.d.mts} +3 -2
- package/dist/{api-registry-DFxSnFqG.d.mts → api-registry-BbUHEKER.d.mts} +2 -1
- package/dist/{azure-openai-responses-Aa4mVcYG.mjs → azure-openai-responses-BmqaDXM1.mjs} +6 -5
- 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-D91-ohD_.d.mts → event-stream-DcvAPSBe.d.mts} +1 -2
- package/dist/event-stream-pXT3FI6X.d.mts +1 -0
- package/dist/event-stream.d.mts +2 -1
- package/dist/{google-yDRnipeq.mjs → google-CqjVZMwo.mjs} +8 -6
- package/dist/{google-shared-M1dl64nz.mjs → google-shared-Bnqni5j3.mjs} +34 -25
- package/dist/{google-vertex-DCJHM_a5.mjs → google-vertex-D_WREEJi.mjs} +8 -6
- package/dist/{host-DF_yxOsA.mjs → host-BIaiBURL.mjs} +4 -8
- package/dist/{host-DJq_FGcz.d.mts → host-DKFUAmCn.d.mts} +3 -3
- package/dist/index-CefaUGSS.d.mts +111 -0
- package/dist/index.d.mts +9 -10
- package/dist/index.mjs +4 -5
- package/dist/internal/anthropic.d.mts +51 -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 -9
- package/dist/internal/retry-after.d.mts +2 -3
- package/dist/internal/runtime.d.mts +51 -40
- package/dist/internal/runtime.mjs +9 -9
- package/dist/internal/shared.d.mts +25 -25
- package/dist/internal/shared.mjs +3 -3
- package/dist/{json-parse-CDnesDM_.mjs → json-parse-BuAJEbdW.mjs} +18 -2
- package/dist/{mistral-D6hRXI7A.mjs → mistral-C24jVd6f.mjs} +34 -32
- package/dist/{openai-chatgpt-responses-C339VIfT.mjs → openai-chatgpt-responses-Czum0oXz.mjs} +38 -20
- package/dist/openai-completions-BfFwac6f.mjs +404 -0
- package/dist/{openai-completions-compat-B0KTleXp.d.mts → openai-completions-compat-SZhi0Lf4.d.mts} +3 -3
- package/dist/openai-completions-stream-DGOvau4J.mjs +1625 -0
- package/dist/{openai-responses-BXEcr-Tp.mjs → openai-responses-BAryVEJM.mjs} +11 -7
- package/dist/openai-responses-compaction-window-DO7yV7az.mjs +686 -0
- package/dist/{openai-responses-contracts-CD75pQ0m.d.mts → openai-responses-contracts-BthRXano.d.mts} +14 -5
- package/dist/{openai-responses-payload-policy-CNGFT9zr.d.mts → openai-responses-payload-policy-rLRPsSmB.d.mts} +1 -0
- package/dist/{openai-responses-prompt-observer-internal-BG-1AsBI.mjs → openai-responses-prompt-observer-internal--R0kZLOZ.mjs} +3 -2
- package/dist/{openai-responses-shared-Cvt5MgEm.mjs → openai-responses-shared-CxgdGBvm.mjs} +332 -1484
- package/dist/openai-stop-reason-Drnn_6Qj.mjs +28 -0
- package/dist/openai-tool-schema-CSMh5e9O.mjs +1697 -0
- package/dist/{provider-error-C8mBRJDl.mjs → provider-error-BI8kIViN.mjs} +14 -88
- package/dist/{provider-options-Cc5ZQ6Aa.d.mts → provider-options-DJd_9euj.d.mts} +13 -4
- package/dist/{provider-transcript-transform--gtf0ZbR.mjs → provider-transcript-transform-WvJmFUAf.mjs} +1 -1
- 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-5ygO2rZc.mjs → reasoning-tag-text-partitioner-BsUX44Tq.mjs} +30 -20
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{sanitize-unicode-BP7HLLZn.mjs → sanitize-unicode-S6binQG-.mjs} +2 -9
- package/dist/simple-options-rdOPzr7Z.mjs +155 -0
- package/dist/{anthropic-BQXQudmA.mjs → src-C8U7lkoa.mjs} +75 -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-FEr-jOoZ.mjs → tool-schema-json-projection-BtZiml7r.mjs} +22 -32
- package/dist/transport-stream-shared-BbG7PThh.d.mts +138 -0
- package/dist/{simple-options-oqsXB6U3.mjs → transport-stream-shared-D8nQWCUo.mjs} +10 -118
- package/dist/{transport-utils-Bv0FHkd0.mjs → transport-utils-CtuS1Upe.mjs} +10 -10
- package/dist/transports.d.mts +130 -189
- package/dist/transports.mjs +227 -1249
- package/dist/{types-DTAP49HF.d.mts → types-Beemor2E.d.mts} +58 -24
- package/dist/types-COdhCX0U.d.mts +1 -0
- package/dist/types.d.mts +6 -5
- package/dist/types.mjs +3 -4
- package/dist/utf16-slice-qz3nsy87.mjs +84 -0
- package/dist/{validation-DYNNrU9u.d.mts → validation-CC6s7Smp.d.mts} +1 -2
- package/dist/{validation-B61OhAio.mjs → validation-CJZtym2g.mjs} +7 -5
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +6 -6
- package/dist/anthropic-BECQCNdF.d.mts +0 -98
- package/dist/event-stream-BVVgDSdq.d.mts +0 -1
- package/dist/headers-B_e4-1J0.mjs +0 -9
- package/dist/index-BVVgDSdq.d.mts +0 -1
- package/dist/openai-completions-D88orSJ9.mjs +0 -632
- package/dist/openai-responses-contracts-DvILoqBk.mjs +0 -247
- package/dist/openai-responses-payload-policy-BDo8CQsx.mjs +0 -208
- package/dist/openai-stop-reason-9OONKyAm.mjs +0 -579
- package/dist/openai-tool-projection-CHGLt03m.mjs +0 -426
- package/dist/provider-options-AvldWZt8.mjs +0 -21
- 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,20 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as clampThinkingLevel, r as calculateCost } from "./sanitize-unicode-
|
|
3
|
-
import { a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload, n as getAiTransportHost } from "./host-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { n as parseStreamingJson } from "./json-parse-CDnesDM_.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-S6binQG-.mjs";
|
|
3
|
+
import { a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload, n as getAiTransportHost } from "./host-BIaiBURL.mjs";
|
|
4
|
+
import { n as projectProviderError } from "./provider-error-BI8kIViN.mjs";
|
|
5
|
+
import { r as asFiniteNumber } from "./base64-CEFBpSkN.mjs";
|
|
6
|
+
import { d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName } from "./simple-options-rdOPzr7Z.mjs";
|
|
7
|
+
import { c as transformTransportMessages } from "./tool-schema-json-projection-BtZiml7r.mjs";
|
|
8
|
+
import { d as redactSensitiveText, u as redactIdentifier } from "./transport-utils-CtuS1Upe.mjs";
|
|
9
|
+
import { _ as transportAbortError, g as sanitizeTransportPayloadText, h as sanitizeNonEmptyTransportPayloadText, m as parseTerminalToolCallArguments, y as withProviderResponseHook } from "./transport-stream-shared-D8nQWCUo.mjs";
|
|
10
|
+
import { r as parseStreamingJson, t as createToolArgumentPreviewSchedule } from "./json-parse-BuAJEbdW.mjs";
|
|
12
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 { a as
|
|
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-CSMh5e9O.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-WvJmFUAf.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-DO7yV7az.mjs";
|
|
18
17
|
import { createHash, randomUUID } from "node:crypto";
|
|
19
18
|
//#region packages/ai/src/transports/model-transport-debug.ts
|
|
20
19
|
/**
|
|
@@ -57,1270 +56,6 @@ function emitModelTransportDebug(log, message) {
|
|
|
57
56
|
log.debug(message);
|
|
58
57
|
}
|
|
59
58
|
//#endregion
|
|
60
|
-
//#region packages/ai/src/providers/clean-for-gemini.ts
|
|
61
|
-
const GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
62
|
-
"patternProperties",
|
|
63
|
-
"additionalProperties",
|
|
64
|
-
"$schema",
|
|
65
|
-
"$id",
|
|
66
|
-
"$ref",
|
|
67
|
-
"$defs",
|
|
68
|
-
"definitions",
|
|
69
|
-
"examples",
|
|
70
|
-
"minLength",
|
|
71
|
-
"maxLength",
|
|
72
|
-
"minimum",
|
|
73
|
-
"maximum",
|
|
74
|
-
"multipleOf",
|
|
75
|
-
"pattern",
|
|
76
|
-
"format",
|
|
77
|
-
"minItems",
|
|
78
|
-
"maxItems",
|
|
79
|
-
"uniqueItems",
|
|
80
|
-
"minProperties",
|
|
81
|
-
"maxProperties",
|
|
82
|
-
"not"
|
|
83
|
-
]);
|
|
84
|
-
const SCHEMA_META_KEYS = [
|
|
85
|
-
"description",
|
|
86
|
-
"title",
|
|
87
|
-
"default"
|
|
88
|
-
];
|
|
89
|
-
function copySchemaMeta$1(from, to) {
|
|
90
|
-
for (const key of SCHEMA_META_KEYS) if (key in from && from[key] !== void 0) to[key] = from[key];
|
|
91
|
-
}
|
|
92
|
-
function stringifyGeminiEnumValue(value) {
|
|
93
|
-
if (typeof value === "string") return value;
|
|
94
|
-
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
95
|
-
if (typeof value === "boolean") return String(value);
|
|
96
|
-
}
|
|
97
|
-
function cleanGeminiEnumValues(value) {
|
|
98
|
-
if (!Array.isArray(value)) return;
|
|
99
|
-
const values = value.flatMap((entry) => {
|
|
100
|
-
const stringified = stringifyGeminiEnumValue(entry);
|
|
101
|
-
return stringified === void 0 ? [] : [stringified];
|
|
102
|
-
});
|
|
103
|
-
const unique = [...new Set(values)];
|
|
104
|
-
return unique.length > 0 ? unique : void 0;
|
|
105
|
-
}
|
|
106
|
-
function tryFlattenLiteralAnyOf(variants) {
|
|
107
|
-
if (variants.length === 0) return null;
|
|
108
|
-
const allValues = [];
|
|
109
|
-
let commonType = null;
|
|
110
|
-
for (const variant of variants) {
|
|
111
|
-
if (!variant || typeof variant !== "object") return null;
|
|
112
|
-
const v = variant;
|
|
113
|
-
let literalValue;
|
|
114
|
-
if ("const" in v) literalValue = v.const;
|
|
115
|
-
else if (Array.isArray(v.enum) && v.enum.length === 1) literalValue = v.enum[0];
|
|
116
|
-
else return null;
|
|
117
|
-
const variantType = typeof v.type === "string" ? v.type : null;
|
|
118
|
-
if (!variantType) return null;
|
|
119
|
-
if (commonType === null) commonType = variantType;
|
|
120
|
-
else if (commonType !== variantType) return null;
|
|
121
|
-
allValues.push(literalValue);
|
|
122
|
-
}
|
|
123
|
-
if (commonType && allValues.length > 0) return {
|
|
124
|
-
type: commonType,
|
|
125
|
-
enum: allValues
|
|
126
|
-
};
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
function isNullSchema(variant) {
|
|
130
|
-
if (!variant || typeof variant !== "object" || Array.isArray(variant)) return false;
|
|
131
|
-
const record = variant;
|
|
132
|
-
if ("const" in record && record.const === null) return true;
|
|
133
|
-
if (Array.isArray(record.enum) && record.enum.length === 1) return record.enum[0] === null;
|
|
134
|
-
const typeValue = record.type;
|
|
135
|
-
if (typeValue === "null") return true;
|
|
136
|
-
if (Array.isArray(typeValue) && typeValue.length === 1 && typeValue[0] === "null") return true;
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
function stripNullVariants(variants) {
|
|
140
|
-
if (variants.length === 0) return {
|
|
141
|
-
variants,
|
|
142
|
-
stripped: false
|
|
143
|
-
};
|
|
144
|
-
const nonNull = variants.filter((variant) => !isNullSchema(variant));
|
|
145
|
-
return {
|
|
146
|
-
variants: nonNull,
|
|
147
|
-
stripped: nonNull.length !== variants.length
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function extendSchemaDefs$1(defs, schema) {
|
|
151
|
-
const defsEntry = schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs) ? schema.$defs : void 0;
|
|
152
|
-
const legacyDefsEntry = schema.definitions && typeof schema.definitions === "object" && !Array.isArray(schema.definitions) ? schema.definitions : void 0;
|
|
153
|
-
if (!defsEntry && !legacyDefsEntry) return defs;
|
|
154
|
-
const next = defs ? new Map(defs) : /* @__PURE__ */ new Map();
|
|
155
|
-
if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.set(key, value);
|
|
156
|
-
if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.set(key, value);
|
|
157
|
-
return next;
|
|
158
|
-
}
|
|
159
|
-
function decodeJsonPointerSegment$1(segment) {
|
|
160
|
-
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
161
|
-
}
|
|
162
|
-
function tryResolveLocalRef$1(ref, defs) {
|
|
163
|
-
if (!defs) return;
|
|
164
|
-
const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
165
|
-
if (!match) return;
|
|
166
|
-
const name = decodeJsonPointerSegment$1(match[1] ?? "");
|
|
167
|
-
if (!name) return;
|
|
168
|
-
return defs.get(name);
|
|
169
|
-
}
|
|
170
|
-
function simplifyUnionVariants(params) {
|
|
171
|
-
const { obj, variants } = params;
|
|
172
|
-
const { variants: nonNullVariants, stripped } = stripNullVariants(variants);
|
|
173
|
-
const flattened = tryFlattenLiteralAnyOf(nonNullVariants);
|
|
174
|
-
if (flattened) {
|
|
175
|
-
const result = {
|
|
176
|
-
type: flattened.type,
|
|
177
|
-
enum: flattened.enum
|
|
178
|
-
};
|
|
179
|
-
copySchemaMeta$1(obj, result);
|
|
180
|
-
return {
|
|
181
|
-
kind: "simplified",
|
|
182
|
-
value: result
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
if (stripped && nonNullVariants.length === 1) {
|
|
186
|
-
const lone = nonNullVariants[0];
|
|
187
|
-
if (lone && typeof lone === "object" && !Array.isArray(lone)) {
|
|
188
|
-
const result = { ...lone };
|
|
189
|
-
copySchemaMeta$1(obj, result);
|
|
190
|
-
return {
|
|
191
|
-
kind: "simplified",
|
|
192
|
-
value: result
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
return {
|
|
196
|
-
kind: "simplified",
|
|
197
|
-
value: lone
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
kind: "variants",
|
|
202
|
-
value: stripped ? nonNullVariants : variants
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
function sanitizeRequiredFields(schema) {
|
|
206
|
-
if (!Array.isArray(schema.required)) return schema;
|
|
207
|
-
if (!schema.properties || typeof schema.properties !== "object" || Array.isArray(schema.properties)) {
|
|
208
|
-
if (schema.type === "object") delete schema.required;
|
|
209
|
-
return schema;
|
|
210
|
-
}
|
|
211
|
-
const properties = schema.properties;
|
|
212
|
-
const required = schema.required.filter((key) => typeof key === "string" && Object.hasOwn(properties, key));
|
|
213
|
-
if (required.length > 0) schema.required = required;
|
|
214
|
-
else delete schema.required;
|
|
215
|
-
return schema;
|
|
216
|
-
}
|
|
217
|
-
function cleanSchemaForGeminiWithDefs(schema, defs, refStack) {
|
|
218
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
219
|
-
if (Array.isArray(schema)) return schema.map((item) => cleanSchemaForGeminiWithDefs(item, defs, refStack));
|
|
220
|
-
const obj = schema;
|
|
221
|
-
const nextDefs = extendSchemaDefs$1(defs, obj);
|
|
222
|
-
const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
|
|
223
|
-
if (refValue) {
|
|
224
|
-
if (refStack?.has(refValue)) return {};
|
|
225
|
-
const resolved = tryResolveLocalRef$1(refValue, nextDefs);
|
|
226
|
-
if (resolved) {
|
|
227
|
-
const nextRefStack = refStack ? new Set(refStack) : /* @__PURE__ */ new Set();
|
|
228
|
-
nextRefStack.add(refValue);
|
|
229
|
-
const cleaned = cleanSchemaForGeminiWithDefs(resolved, nextDefs, nextRefStack);
|
|
230
|
-
if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) return cleaned;
|
|
231
|
-
const result = { ...cleaned };
|
|
232
|
-
copySchemaMeta$1(obj, result);
|
|
233
|
-
return result;
|
|
234
|
-
}
|
|
235
|
-
const result = {};
|
|
236
|
-
copySchemaMeta$1(obj, result);
|
|
237
|
-
return result;
|
|
238
|
-
}
|
|
239
|
-
const hasAnyOf = "anyOf" in obj && Array.isArray(obj.anyOf);
|
|
240
|
-
const hasOneOf = "oneOf" in obj && Array.isArray(obj.oneOf);
|
|
241
|
-
let cleanedAnyOf = hasAnyOf ? obj.anyOf.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack)) : void 0;
|
|
242
|
-
let cleanedOneOf = hasOneOf ? obj.oneOf.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack)) : void 0;
|
|
243
|
-
if (hasAnyOf) {
|
|
244
|
-
const simplified = simplifyUnionVariants({
|
|
245
|
-
obj,
|
|
246
|
-
variants: cleanedAnyOf ?? []
|
|
247
|
-
});
|
|
248
|
-
if (simplified.kind === "simplified") return simplified.value;
|
|
249
|
-
cleanedAnyOf = simplified.value;
|
|
250
|
-
}
|
|
251
|
-
if (hasOneOf) {
|
|
252
|
-
const simplified = simplifyUnionVariants({
|
|
253
|
-
obj,
|
|
254
|
-
variants: cleanedOneOf ?? []
|
|
255
|
-
});
|
|
256
|
-
if (simplified.kind === "simplified") return simplified.value;
|
|
257
|
-
cleanedOneOf = simplified.value;
|
|
258
|
-
}
|
|
259
|
-
const cleaned = {};
|
|
260
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
261
|
-
if (GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS.has(key)) continue;
|
|
262
|
-
if (key === "const") {
|
|
263
|
-
const enumValues = cleanGeminiEnumValues([value]);
|
|
264
|
-
if (enumValues) cleaned.enum = enumValues;
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
if (key === "enum") {
|
|
268
|
-
const enumValues = cleanGeminiEnumValues(value);
|
|
269
|
-
if (enumValues) cleaned.enum = enumValues;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
if (key === "required" && Array.isArray(value) && value.length === 0) continue;
|
|
273
|
-
if (key === "type" && (hasAnyOf || hasOneOf)) continue;
|
|
274
|
-
if (key === "type" && Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
275
|
-
const types = value.filter((entry) => entry !== "null");
|
|
276
|
-
cleaned.type = types.length === 1 ? types[0] : types;
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
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)]));
|
|
280
|
-
else cleaned[key] = {};
|
|
281
|
-
else if (key === "items" && value) if (Array.isArray(value)) cleaned[key] = value.map((entry) => cleanSchemaForGeminiWithDefs(entry, nextDefs, refStack));
|
|
282
|
-
else if (typeof value === "object") cleaned[key] = cleanSchemaForGeminiWithDefs(value, nextDefs, refStack);
|
|
283
|
-
else cleaned[key] = value;
|
|
284
|
-
else if (key === "anyOf" && Array.isArray(value)) cleaned[key] = cleanedAnyOf ?? value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
|
|
285
|
-
else if (key === "oneOf" && Array.isArray(value)) cleaned[key] = cleanedOneOf ?? value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
|
|
286
|
-
else if (key === "allOf" && Array.isArray(value)) cleaned[key] = value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
|
|
287
|
-
else cleaned[key] = value;
|
|
288
|
-
}
|
|
289
|
-
if (cleaned.anyOf && Array.isArray(cleaned.anyOf)) {
|
|
290
|
-
const flattened = flattenUnionFallback(cleaned, cleaned.anyOf);
|
|
291
|
-
if (flattened) return sanitizeRequiredFields(flattened);
|
|
292
|
-
}
|
|
293
|
-
if (cleaned.oneOf && Array.isArray(cleaned.oneOf)) {
|
|
294
|
-
const flattened = flattenUnionFallback(cleaned, cleaned.oneOf);
|
|
295
|
-
if (flattened) return sanitizeRequiredFields(flattened);
|
|
296
|
-
}
|
|
297
|
-
return sanitizeRequiredFields(cleaned);
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Last-resort flattening for anyOf/oneOf arrays that could not be simplified
|
|
301
|
-
* by `simplifyUnionVariants`. Picks a representative type so the schema is
|
|
302
|
-
* accepted by Google's restricted JSON Schema validation.
|
|
303
|
-
*/
|
|
304
|
-
function flattenUnionFallback(obj, variants) {
|
|
305
|
-
const objects = variants.filter((v) => Boolean(v) && typeof v === "object");
|
|
306
|
-
if (objects.length === 0) return;
|
|
307
|
-
const types = new Set(objects.map((v) => v.type).filter(Boolean));
|
|
308
|
-
if (objects.length === 1) {
|
|
309
|
-
const merged = { ...objects[0] };
|
|
310
|
-
copySchemaMeta$1(obj, merged);
|
|
311
|
-
return merged;
|
|
312
|
-
}
|
|
313
|
-
if (types.size === 1) {
|
|
314
|
-
const merged = { type: Array.from(types)[0] };
|
|
315
|
-
copySchemaMeta$1(obj, merged);
|
|
316
|
-
return merged;
|
|
317
|
-
}
|
|
318
|
-
const first = objects[0];
|
|
319
|
-
if (first?.type) {
|
|
320
|
-
const merged = { type: first.type };
|
|
321
|
-
copySchemaMeta$1(obj, merged);
|
|
322
|
-
return merged;
|
|
323
|
-
}
|
|
324
|
-
const merged = {};
|
|
325
|
-
copySchemaMeta$1(obj, merged);
|
|
326
|
-
return merged;
|
|
327
|
-
}
|
|
328
|
-
function cleanSchemaForGemini(schema) {
|
|
329
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
330
|
-
if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);
|
|
331
|
-
return cleanSchemaForGeminiWithDefs(schema, extendSchemaDefs$1(void 0, schema), void 0);
|
|
332
|
-
}
|
|
333
|
-
//#endregion
|
|
334
|
-
//#region packages/ai/src/providers/clean-for-llamacpp-gbnf.ts
|
|
335
|
-
/** llama.cpp rejects grammar repetitions whose expanded rule count reaches 2000. */
|
|
336
|
-
const LLAMACPP_GBNF_MAX_REPETITION_THRESHOLD = 2e3;
|
|
337
|
-
const SCHEMA_MAP_KEYS$2 = /* @__PURE__ */ new Set([
|
|
338
|
-
"$defs",
|
|
339
|
-
"definitions",
|
|
340
|
-
"dependentSchemas",
|
|
341
|
-
"patternProperties",
|
|
342
|
-
"properties"
|
|
343
|
-
]);
|
|
344
|
-
const SCHEMA_CHILD_KEYS = /* @__PURE__ */ new Set([
|
|
345
|
-
"additionalItems",
|
|
346
|
-
"additionalProperties",
|
|
347
|
-
"allOf",
|
|
348
|
-
"anyOf",
|
|
349
|
-
"contains",
|
|
350
|
-
"else",
|
|
351
|
-
"if",
|
|
352
|
-
"items",
|
|
353
|
-
"not",
|
|
354
|
-
"oneOf",
|
|
355
|
-
"prefixItems",
|
|
356
|
-
"propertyNames",
|
|
357
|
-
"then",
|
|
358
|
-
"unevaluatedItems",
|
|
359
|
-
"unevaluatedProperties"
|
|
360
|
-
]);
|
|
361
|
-
function cleanSchemaNode(node) {
|
|
362
|
-
if (Array.isArray(node)) {
|
|
363
|
-
let changed = false;
|
|
364
|
-
const entries = node.map((entry) => {
|
|
365
|
-
const next = cleanSchemaNode(entry);
|
|
366
|
-
changed ||= next !== entry;
|
|
367
|
-
return next;
|
|
368
|
-
});
|
|
369
|
-
return changed ? entries : node;
|
|
370
|
-
}
|
|
371
|
-
if (!isRecord(node)) return node;
|
|
372
|
-
let changed = false;
|
|
373
|
-
const cleaned = {};
|
|
374
|
-
for (const [key, value] of Object.entries(node)) {
|
|
375
|
-
if (key === "pattern") {
|
|
376
|
-
changed = true;
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
if (key === "maxLength" && typeof value === "number" && value >= 2e3) {
|
|
380
|
-
changed = true;
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
let next = value;
|
|
384
|
-
if (SCHEMA_MAP_KEYS$2.has(key) && isRecord(value)) {
|
|
385
|
-
let mapChanged = false;
|
|
386
|
-
next = Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => {
|
|
387
|
-
const cleanedChild = cleanSchemaNode(childValue);
|
|
388
|
-
mapChanged ||= cleanedChild !== childValue;
|
|
389
|
-
return [childKey, cleanedChild];
|
|
390
|
-
}));
|
|
391
|
-
if (!mapChanged) next = value;
|
|
392
|
-
} else if (SCHEMA_CHILD_KEYS.has(key)) next = cleanSchemaNode(value);
|
|
393
|
-
cleaned[key] = next;
|
|
394
|
-
changed ||= next !== value;
|
|
395
|
-
}
|
|
396
|
-
return changed ? cleaned : node;
|
|
397
|
-
}
|
|
398
|
-
function collectSchemaViolations(node, path, violations) {
|
|
399
|
-
if (Array.isArray(node)) {
|
|
400
|
-
node.forEach((entry, index) => collectSchemaViolations(entry, `${path}[${index}]`, violations));
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
if (!isRecord(node)) return;
|
|
404
|
-
if ("pattern" in node) violations.push(`${path}.pattern`);
|
|
405
|
-
if (typeof node.maxLength === "number" && node.maxLength >= 2e3) violations.push(`${path}.maxLength`);
|
|
406
|
-
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);
|
|
407
|
-
else if (SCHEMA_CHILD_KEYS.has(key)) collectSchemaViolations(value, `${path}.${key}`, violations);
|
|
408
|
-
}
|
|
409
|
-
/** Removes JSON Schema constraints that llama.cpp cannot compile into GBNF. */
|
|
410
|
-
function cleanSchemaForLlamacppGbnf(schema) {
|
|
411
|
-
return cleanSchemaNode(schema);
|
|
412
|
-
}
|
|
413
|
-
/** Reports schema paths that llama.cpp cannot compile into GBNF. */
|
|
414
|
-
function findLlamacppGbnfSchemaViolations(schema, path) {
|
|
415
|
-
const violations = [];
|
|
416
|
-
collectSchemaViolations(schema, path, violations);
|
|
417
|
-
return violations;
|
|
418
|
-
}
|
|
419
|
-
//#endregion
|
|
420
|
-
//#region packages/ai/src/providers/schema-keyword-strip.ts
|
|
421
|
-
const SCHEMA_MAP_KEYS$1 = /* @__PURE__ */ new Set([
|
|
422
|
-
"$defs",
|
|
423
|
-
"definitions",
|
|
424
|
-
"dependentSchemas",
|
|
425
|
-
"dependencies",
|
|
426
|
-
"patternProperties",
|
|
427
|
-
"properties"
|
|
428
|
-
]);
|
|
429
|
-
/** Containers whose value is a single nested schema. */
|
|
430
|
-
const SCHEMA_OBJECT_KEYS$1 = /* @__PURE__ */ new Set([
|
|
431
|
-
"additionalItems",
|
|
432
|
-
"additionalProperties",
|
|
433
|
-
"contains",
|
|
434
|
-
"contentSchema",
|
|
435
|
-
"else",
|
|
436
|
-
"if",
|
|
437
|
-
"items",
|
|
438
|
-
"not",
|
|
439
|
-
"propertyNames",
|
|
440
|
-
"then",
|
|
441
|
-
"unevaluatedItems",
|
|
442
|
-
"unevaluatedProperties"
|
|
443
|
-
]);
|
|
444
|
-
/** Containers whose value is a list of nested schemas. */
|
|
445
|
-
const SCHEMA_ARRAY_KEYS$1 = /* @__PURE__ */ new Set([
|
|
446
|
-
"allOf",
|
|
447
|
-
"anyOf",
|
|
448
|
-
"items",
|
|
449
|
-
"oneOf",
|
|
450
|
-
"prefixItems"
|
|
451
|
-
]);
|
|
452
|
-
/** Recursively remove schema keywords unsupported by a target provider/tool surface. */
|
|
453
|
-
function stripUnsupportedSchemaKeywords(schema, unsupportedKeywords) {
|
|
454
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
455
|
-
if (Array.isArray(schema)) return schema.map((entry) => stripUnsupportedSchemaKeywords(entry, unsupportedKeywords));
|
|
456
|
-
const obj = schema;
|
|
457
|
-
const cleaned = {};
|
|
458
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
459
|
-
if (unsupportedKeywords.has(key)) continue;
|
|
460
|
-
if (SCHEMA_MAP_KEYS$1.has(key) && value && typeof value === "object" && !Array.isArray(value)) {
|
|
461
|
-
cleaned[key] = Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => [childKey, stripUnsupportedSchemaKeywords(childValue, unsupportedKeywords)]));
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
if (SCHEMA_ARRAY_KEYS$1.has(key) && Array.isArray(value)) {
|
|
465
|
-
cleaned[key] = value.map((entry) => stripUnsupportedSchemaKeywords(entry, unsupportedKeywords));
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
if (SCHEMA_OBJECT_KEYS$1.has(key) && value && typeof value === "object") {
|
|
469
|
-
cleaned[key] = stripUnsupportedSchemaKeywords(value, unsupportedKeywords);
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
cleaned[key] = value;
|
|
473
|
-
}
|
|
474
|
-
return cleaned;
|
|
475
|
-
}
|
|
476
|
-
//#endregion
|
|
477
|
-
//#region packages/ai/src/providers/agent-tools-parameter-schema.ts
|
|
478
|
-
/**
|
|
479
|
-
* Normalizes model-facing tool parameter schemas across provider quirks.
|
|
480
|
-
* Handles local JSON Schema refs, OpenAPI nullable syntax, top-level unions,
|
|
481
|
-
* and provider-specific unsupported keyword stripping.
|
|
482
|
-
*/
|
|
483
|
-
/** Extracts the compat record whether callers pass a model (`{ compat }`) or the compat itself. */
|
|
484
|
-
function extractToolSchemaModelCompat(modelOrCompat) {
|
|
485
|
-
if (!modelOrCompat || typeof modelOrCompat !== "object") return;
|
|
486
|
-
if ("compat" in modelOrCompat) {
|
|
487
|
-
const compat = modelOrCompat.compat;
|
|
488
|
-
return compat && typeof compat === "object" ? compat : void 0;
|
|
489
|
-
}
|
|
490
|
-
return modelOrCompat;
|
|
491
|
-
}
|
|
492
|
-
/** JSON Schema keywords this model/provider rejects in tool schemas. */
|
|
493
|
-
function resolveUnsupportedToolSchemaKeywords(modelOrCompat) {
|
|
494
|
-
const keywords = extractToolSchemaModelCompat(modelOrCompat)?.unsupportedToolSchemaKeywords ?? [];
|
|
495
|
-
return new Set(normalizeStringEntries(keywords.filter((keyword) => typeof keyword === "string")));
|
|
496
|
-
}
|
|
497
|
-
/** Whether empty `items: {}` on array schemas must be omitted for this model/provider. */
|
|
498
|
-
function shouldOmitEmptyArrayItems(modelOrCompat) {
|
|
499
|
-
return extractToolSchemaModelCompat(modelOrCompat)?.omitEmptyArrayItems === true;
|
|
500
|
-
}
|
|
501
|
-
const MAX_TOOL_PARAMETER_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
|
|
502
|
-
const toolParameterSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
503
|
-
function resolveToolParameterSchemaCacheKey(options) {
|
|
504
|
-
const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
|
|
505
|
-
const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
|
|
506
|
-
const toolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
|
|
507
|
-
const unsupportedKeywords = Array.from(resolveUnsupportedToolSchemaKeywords(options?.modelCompat)).toSorted();
|
|
508
|
-
const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
|
|
509
|
-
return JSON.stringify([
|
|
510
|
-
normalizedProvider,
|
|
511
|
-
normalizedModelId,
|
|
512
|
-
toolSchemaProfile,
|
|
513
|
-
unsupportedKeywords,
|
|
514
|
-
omitEmptyArrayItems
|
|
515
|
-
]);
|
|
516
|
-
}
|
|
517
|
-
function getCachedToolParameterSchema(schema, key) {
|
|
518
|
-
return toolParameterSchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
|
|
519
|
-
}
|
|
520
|
-
function rememberCachedToolParameterSchema(schema, key, value) {
|
|
521
|
-
const entries = toolParameterSchemaCache.get(schema) ?? [];
|
|
522
|
-
toolParameterSchemaCache.set(schema, [{
|
|
523
|
-
key,
|
|
524
|
-
value
|
|
525
|
-
}, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_TOOL_PARAMETER_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
|
|
526
|
-
return value;
|
|
527
|
-
}
|
|
528
|
-
function isGeminiModelId(modelId) {
|
|
529
|
-
return /(?:^|[/:])gemini(?:$|[-/:.])/.test(modelId);
|
|
530
|
-
}
|
|
531
|
-
function extractEnumValues(schema) {
|
|
532
|
-
if (!schema || typeof schema !== "object") return;
|
|
533
|
-
const record = schema;
|
|
534
|
-
if (Array.isArray(record.enum)) return record.enum;
|
|
535
|
-
if ("const" in record) return [record.const];
|
|
536
|
-
const variants = Array.isArray(record.anyOf) ? record.anyOf : Array.isArray(record.oneOf) ? record.oneOf : null;
|
|
537
|
-
if (variants) {
|
|
538
|
-
const values = variants.flatMap((variant) => {
|
|
539
|
-
return extractEnumValues(variant) ?? [];
|
|
540
|
-
});
|
|
541
|
-
return values.length > 0 ? values : void 0;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
function mergePropertySchemas(existing, incoming) {
|
|
545
|
-
if (!existing) return incoming;
|
|
546
|
-
if (!incoming) return existing;
|
|
547
|
-
const existingEnum = extractEnumValues(existing);
|
|
548
|
-
const incomingEnum = extractEnumValues(incoming);
|
|
549
|
-
if (existingEnum || incomingEnum) {
|
|
550
|
-
const values = uniqueValues([...existingEnum ?? [], ...incomingEnum ?? []]);
|
|
551
|
-
const merged = {};
|
|
552
|
-
for (const source of [existing, incoming]) {
|
|
553
|
-
if (!source || typeof source !== "object") continue;
|
|
554
|
-
const record = source;
|
|
555
|
-
for (const key of [
|
|
556
|
-
"title",
|
|
557
|
-
"description",
|
|
558
|
-
"default"
|
|
559
|
-
]) if (!(key in merged) && key in record) merged[key] = record[key];
|
|
560
|
-
}
|
|
561
|
-
const types = new Set(values.map((value) => typeof value));
|
|
562
|
-
if (types.size === 1) merged.type = Array.from(types)[0];
|
|
563
|
-
merged.enum = values;
|
|
564
|
-
return merged;
|
|
565
|
-
}
|
|
566
|
-
return existing;
|
|
567
|
-
}
|
|
568
|
-
function setOwnSchemaProperty(target, key, value) {
|
|
569
|
-
Object.defineProperty(target, key, {
|
|
570
|
-
value,
|
|
571
|
-
enumerable: true,
|
|
572
|
-
configurable: true,
|
|
573
|
-
writable: true
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
function hasTopLevelArrayKeyword(schemaRecord, key) {
|
|
577
|
-
return Array.isArray(schemaRecord[key]);
|
|
578
|
-
}
|
|
579
|
-
function getFlattenableVariantKey(schemaRecord) {
|
|
580
|
-
if (hasTopLevelArrayKeyword(schemaRecord, "anyOf")) return "anyOf";
|
|
581
|
-
if (hasTopLevelArrayKeyword(schemaRecord, "oneOf")) return "oneOf";
|
|
582
|
-
return null;
|
|
583
|
-
}
|
|
584
|
-
function getTopLevelConditionalKey(schemaRecord) {
|
|
585
|
-
return getFlattenableVariantKey(schemaRecord) ?? (hasTopLevelArrayKeyword(schemaRecord, "allOf") ? "allOf" : null);
|
|
586
|
-
}
|
|
587
|
-
function hasTopLevelObjectSchema(schemaRecord, conditionalKey) {
|
|
588
|
-
return schemaRecord.type === "object" && isRecord(schemaRecord.properties) && conditionalKey === null;
|
|
589
|
-
}
|
|
590
|
-
function isObjectLikeSchemaMissingType(schemaRecord, conditionalKey) {
|
|
591
|
-
return !("type" in schemaRecord) && (isRecord(schemaRecord.properties) || Array.isArray(schemaRecord.required)) && conditionalKey === null;
|
|
592
|
-
}
|
|
593
|
-
function isTypedObjectSchemaMissingValidProperties(schemaRecord, conditionalKey) {
|
|
594
|
-
return schemaRecord.type === "object" && !isRecord(schemaRecord.properties) && conditionalKey === null;
|
|
595
|
-
}
|
|
596
|
-
function isTrulyEmptySchema(schemaRecord) {
|
|
597
|
-
return Object.keys(schemaRecord).length === 0;
|
|
598
|
-
}
|
|
599
|
-
function normalizeArraySchemasMissingItems(schema) {
|
|
600
|
-
if (!isRecord(schema)) return schema;
|
|
601
|
-
let changed = false;
|
|
602
|
-
const nextSchema = { ...schema };
|
|
603
|
-
if (nextSchema.type === "array" && nextSchema.items === void 0) {
|
|
604
|
-
nextSchema.items = {};
|
|
605
|
-
changed = true;
|
|
606
|
-
}
|
|
607
|
-
const normalizeSchemaValue = (key) => {
|
|
608
|
-
if (!(key in nextSchema)) return;
|
|
609
|
-
const value = nextSchema[key];
|
|
610
|
-
if (Array.isArray(value)) {
|
|
611
|
-
const normalized = value.map(normalizeArraySchemasMissingItems);
|
|
612
|
-
if (normalized.some((entry, index) => entry !== value[index])) {
|
|
613
|
-
nextSchema[key] = normalized;
|
|
614
|
-
changed = true;
|
|
615
|
-
}
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
const normalized = normalizeArraySchemasMissingItems(value);
|
|
619
|
-
if (normalized !== value) {
|
|
620
|
-
nextSchema[key] = normalized;
|
|
621
|
-
changed = true;
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
for (const key of [
|
|
625
|
-
"items",
|
|
626
|
-
"contains",
|
|
627
|
-
"additionalProperties",
|
|
628
|
-
"propertyNames",
|
|
629
|
-
"not",
|
|
630
|
-
"if",
|
|
631
|
-
"then",
|
|
632
|
-
"else"
|
|
633
|
-
]) normalizeSchemaValue(key);
|
|
634
|
-
for (const key of [
|
|
635
|
-
"anyOf",
|
|
636
|
-
"oneOf",
|
|
637
|
-
"allOf",
|
|
638
|
-
"prefixItems"
|
|
639
|
-
]) normalizeSchemaValue(key);
|
|
640
|
-
for (const key of [
|
|
641
|
-
"properties",
|
|
642
|
-
"patternProperties",
|
|
643
|
-
"dependentSchemas",
|
|
644
|
-
"$defs",
|
|
645
|
-
"definitions"
|
|
646
|
-
]) {
|
|
647
|
-
const value = nextSchema[key];
|
|
648
|
-
if (!isRecord(value)) continue;
|
|
649
|
-
let entriesChanged = false;
|
|
650
|
-
const normalizedEntries = Object.entries(value).map(([entryKey, entryValue]) => {
|
|
651
|
-
const normalizedEntryValue = normalizeArraySchemasMissingItems(entryValue);
|
|
652
|
-
if (normalizedEntryValue !== entryValue) entriesChanged = true;
|
|
653
|
-
return [entryKey, normalizedEntryValue];
|
|
654
|
-
});
|
|
655
|
-
if (entriesChanged) {
|
|
656
|
-
nextSchema[key] = Object.fromEntries(normalizedEntries);
|
|
657
|
-
changed = true;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
return changed ? nextSchema : schema;
|
|
661
|
-
}
|
|
662
|
-
function schemaAllowsArrayType(schema) {
|
|
663
|
-
const type = schema.type;
|
|
664
|
-
return type === "array" || Array.isArray(type) && type.includes("array");
|
|
665
|
-
}
|
|
666
|
-
const ARRAY_ITEMS_SCHEMA_OBJECT_KEYS = /* @__PURE__ */ new Set([
|
|
667
|
-
"additionalProperties",
|
|
668
|
-
"contains",
|
|
669
|
-
"else",
|
|
670
|
-
"if",
|
|
671
|
-
"items",
|
|
672
|
-
"not",
|
|
673
|
-
"propertyNames",
|
|
674
|
-
"then"
|
|
675
|
-
]);
|
|
676
|
-
const ARRAY_ITEMS_SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set([
|
|
677
|
-
"allOf",
|
|
678
|
-
"anyOf",
|
|
679
|
-
"oneOf",
|
|
680
|
-
"prefixItems"
|
|
681
|
-
]);
|
|
682
|
-
const ARRAY_ITEMS_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
683
|
-
"$defs",
|
|
684
|
-
"definitions",
|
|
685
|
-
"dependentSchemas",
|
|
686
|
-
"patternProperties",
|
|
687
|
-
"properties"
|
|
688
|
-
]);
|
|
689
|
-
function stripEmptyArrayItemsFromArraySchemas(schema) {
|
|
690
|
-
if (Array.isArray(schema)) {
|
|
691
|
-
let changed = false;
|
|
692
|
-
const entries = schema.map((entry) => {
|
|
693
|
-
const next = stripEmptyArrayItemsFromArraySchemas(entry);
|
|
694
|
-
changed ||= next !== entry;
|
|
695
|
-
return next;
|
|
696
|
-
});
|
|
697
|
-
return changed ? entries : schema;
|
|
698
|
-
}
|
|
699
|
-
if (!isRecord(schema)) return schema;
|
|
700
|
-
let changed = false;
|
|
701
|
-
const entries = Object.entries(schema).flatMap(([key, value]) => {
|
|
702
|
-
if (key === "items" && schemaAllowsArrayType(schema) && isRecord(value) && isTrulyEmptySchema(value)) {
|
|
703
|
-
changed = true;
|
|
704
|
-
return [];
|
|
705
|
-
}
|
|
706
|
-
if (ARRAY_ITEMS_SCHEMA_OBJECT_KEYS.has(key)) {
|
|
707
|
-
const next = stripEmptyArrayItemsFromArraySchemas(value);
|
|
708
|
-
changed ||= next !== value;
|
|
709
|
-
return [[key, next]];
|
|
710
|
-
}
|
|
711
|
-
if (ARRAY_ITEMS_SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
712
|
-
const next = stripEmptyArrayItemsFromArraySchemas(value);
|
|
713
|
-
changed ||= next !== value;
|
|
714
|
-
return [[key, next]];
|
|
715
|
-
}
|
|
716
|
-
if (ARRAY_ITEMS_SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
717
|
-
let mapChanged = false;
|
|
718
|
-
const next = Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => {
|
|
719
|
-
const entryNext = stripEmptyArrayItemsFromArraySchemas(entryValue);
|
|
720
|
-
mapChanged ||= entryNext !== entryValue;
|
|
721
|
-
return [entryKey, entryNext];
|
|
722
|
-
}));
|
|
723
|
-
changed ||= mapChanged;
|
|
724
|
-
return [[key, mapChanged ? next : value]];
|
|
725
|
-
}
|
|
726
|
-
return [[key, value]];
|
|
727
|
-
});
|
|
728
|
-
return changed ? Object.fromEntries(entries) : schema;
|
|
729
|
-
}
|
|
730
|
-
function copySchemaMeta(from, to) {
|
|
731
|
-
for (const key of [
|
|
732
|
-
"title",
|
|
733
|
-
"description",
|
|
734
|
-
"default"
|
|
735
|
-
]) if (key in from && from[key] !== void 0) to[key] = from[key];
|
|
736
|
-
}
|
|
737
|
-
function extendSchemaDefs(defs, schema) {
|
|
738
|
-
const defsEntry = schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs) ? schema.$defs : void 0;
|
|
739
|
-
const legacyDefsEntry = schema.definitions && typeof schema.definitions === "object" && !Array.isArray(schema.definitions) ? schema.definitions : void 0;
|
|
740
|
-
if (!defsEntry && !legacyDefsEntry) return defs;
|
|
741
|
-
const next = defs ? {
|
|
742
|
-
$defs: new Map(defs.$defs),
|
|
743
|
-
definitions: new Map(defs.definitions)
|
|
744
|
-
} : {
|
|
745
|
-
$defs: /* @__PURE__ */ new Map(),
|
|
746
|
-
definitions: /* @__PURE__ */ new Map()
|
|
747
|
-
};
|
|
748
|
-
if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.$defs.set(key, value);
|
|
749
|
-
if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.definitions.set(key, value);
|
|
750
|
-
return next;
|
|
751
|
-
}
|
|
752
|
-
function decodeJsonPointerSegment(segment) {
|
|
753
|
-
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
754
|
-
}
|
|
755
|
-
function resolveJsonPointerPath(value, segments) {
|
|
756
|
-
let current = value;
|
|
757
|
-
for (const segment of segments) {
|
|
758
|
-
if (!current || typeof current !== "object") return;
|
|
759
|
-
const key = decodeJsonPointerSegment(segment);
|
|
760
|
-
if (Array.isArray(current)) {
|
|
761
|
-
const index = /^(?:0|[1-9]\d*)$/.test(key) ? Number(key) : -1;
|
|
762
|
-
if (index < 0 || index >= current.length) return;
|
|
763
|
-
current = current[index];
|
|
764
|
-
continue;
|
|
765
|
-
}
|
|
766
|
-
const record = current;
|
|
767
|
-
if (!Object.hasOwn(record, key)) return;
|
|
768
|
-
current = record[key];
|
|
769
|
-
}
|
|
770
|
-
return current;
|
|
771
|
-
}
|
|
772
|
-
function resolveLocalJsonPointer(rootDocument, ref) {
|
|
773
|
-
if (!ref.startsWith("#/")) return;
|
|
774
|
-
return resolveJsonPointerPath(rootDocument, ref.slice(2).split("/"));
|
|
775
|
-
}
|
|
776
|
-
const SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
777
|
-
"$defs",
|
|
778
|
-
"definitions",
|
|
779
|
-
"dependentSchemas",
|
|
780
|
-
"patternProperties",
|
|
781
|
-
"properties"
|
|
782
|
-
]);
|
|
783
|
-
const SCHEMA_OBJECT_KEYS = /* @__PURE__ */ new Set([
|
|
784
|
-
"additionalProperties",
|
|
785
|
-
"contains",
|
|
786
|
-
"else",
|
|
787
|
-
"if",
|
|
788
|
-
"items",
|
|
789
|
-
"not",
|
|
790
|
-
"propertyNames",
|
|
791
|
-
"then"
|
|
792
|
-
]);
|
|
793
|
-
const SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set([
|
|
794
|
-
"allOf",
|
|
795
|
-
"anyOf",
|
|
796
|
-
"items",
|
|
797
|
-
"oneOf",
|
|
798
|
-
"prefixItems"
|
|
799
|
-
]);
|
|
800
|
-
const SCHEMA_LITERAL_KEYS = /* @__PURE__ */ new Set([
|
|
801
|
-
"const",
|
|
802
|
-
"default",
|
|
803
|
-
"enum",
|
|
804
|
-
"examples"
|
|
805
|
-
]);
|
|
806
|
-
function tryResolveLocalRef(ref, defs, rootDocument) {
|
|
807
|
-
const match = ref.match(/^#\/(\$defs|definitions)\/([^/]+)(?:\/(.*))?$/);
|
|
808
|
-
if (match && defs) {
|
|
809
|
-
const namespace = match[1] === "$defs" ? defs.$defs : defs.definitions;
|
|
810
|
-
const name = decodeJsonPointerSegment(match[2] ?? "");
|
|
811
|
-
const resolved = name ? namespace.get(name) : void 0;
|
|
812
|
-
if (resolved !== void 0) return resolveJsonPointerPath(resolved, match[3] ? match[3].split("/") : []);
|
|
813
|
-
}
|
|
814
|
-
return resolveLocalJsonPointer(rootDocument, ref);
|
|
815
|
-
}
|
|
816
|
-
function inlineLocalSchemaRefsWithDefs(schema, defs, refStack, state, rootDocument) {
|
|
817
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
818
|
-
if (Array.isArray(schema)) return schema.map((entry) => inlineLocalSchemaRefsWithDefs(entry, defs, refStack, state, rootDocument));
|
|
819
|
-
const obj = schema;
|
|
820
|
-
const nextDefs = extendSchemaDefs(defs, obj);
|
|
821
|
-
const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
|
|
822
|
-
if (refValue) {
|
|
823
|
-
if (refStack?.has(refValue)) return {};
|
|
824
|
-
const resolved = tryResolveLocalRef(refValue, nextDefs, rootDocument);
|
|
825
|
-
if (resolved === void 0) {
|
|
826
|
-
if (refValue.startsWith("#/")) state.unresolvedLocalRefs = true;
|
|
827
|
-
return { ...obj };
|
|
828
|
-
}
|
|
829
|
-
const nextRefStack = refStack ? new Set(refStack) : /* @__PURE__ */ new Set();
|
|
830
|
-
nextRefStack.add(refValue);
|
|
831
|
-
const inlined = inlineLocalSchemaRefsWithDefs(resolved, nextDefs, nextRefStack, state, rootDocument);
|
|
832
|
-
if (!inlined || typeof inlined !== "object" || Array.isArray(inlined)) return inlined;
|
|
833
|
-
const result = { ...inlined };
|
|
834
|
-
copySchemaMeta(obj, result);
|
|
835
|
-
if (obj.nullable === true) result.nullable = true;
|
|
836
|
-
return result;
|
|
837
|
-
}
|
|
838
|
-
const result = {};
|
|
839
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
840
|
-
if (key === "$defs" || key === "definitions" || key === "components") continue;
|
|
841
|
-
if (SCHEMA_LITERAL_KEYS.has(key)) {
|
|
842
|
-
setOwnSchemaProperty(result, key, value);
|
|
843
|
-
continue;
|
|
844
|
-
}
|
|
845
|
-
if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
846
|
-
setOwnSchemaProperty(result, key, Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, inlineLocalSchemaRefsWithDefs(entryValue, nextDefs, refStack, state, rootDocument)])));
|
|
847
|
-
continue;
|
|
848
|
-
}
|
|
849
|
-
if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) {
|
|
850
|
-
setOwnSchemaProperty(result, key, inlineLocalSchemaRefsWithDefs(value, nextDefs, refStack, state, rootDocument));
|
|
851
|
-
continue;
|
|
852
|
-
}
|
|
853
|
-
if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
854
|
-
setOwnSchemaProperty(result, key, value.map((entry) => inlineLocalSchemaRefsWithDefs(entry, nextDefs, refStack, state, rootDocument)));
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
setOwnSchemaProperty(result, key, value);
|
|
858
|
-
}
|
|
859
|
-
if (state.unresolvedLocalRefs) {
|
|
860
|
-
if ("$defs" in obj) result.$defs = obj.$defs;
|
|
861
|
-
if ("definitions" in obj) result.definitions = obj.definitions;
|
|
862
|
-
if ("components" in obj) result.components = obj.components;
|
|
863
|
-
}
|
|
864
|
-
return result;
|
|
865
|
-
}
|
|
866
|
-
/** Inline local $ref pointers so providers receive self-contained tool schemas. */
|
|
867
|
-
function inlineLocalToolSchemaRefs(schema) {
|
|
868
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
869
|
-
return inlineLocalSchemaRefsWithDefs(schema, extendSchemaDefs(void 0, schema), void 0, { unresolvedLocalRefs: false }, schema);
|
|
870
|
-
}
|
|
871
|
-
const OPENAPI_SCHEMA_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
872
|
-
"discriminator",
|
|
873
|
-
"externalDocs",
|
|
874
|
-
"readOnly",
|
|
875
|
-
"writeOnly",
|
|
876
|
-
"xml",
|
|
877
|
-
"example"
|
|
878
|
-
]);
|
|
879
|
-
function appendNullSchemaType(type) {
|
|
880
|
-
if (type === "null") return type;
|
|
881
|
-
if (typeof type === "string") return [type, "null"];
|
|
882
|
-
if (Array.isArray(type)) return type.includes("null") ? type : [...type, "null"];
|
|
883
|
-
return type;
|
|
884
|
-
}
|
|
885
|
-
function isNullSchemaLike(schema) {
|
|
886
|
-
if (!isRecord(schema)) return false;
|
|
887
|
-
if (schema.type === "null") return true;
|
|
888
|
-
if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
|
|
889
|
-
if ("const" in schema && schema.const === null) return true;
|
|
890
|
-
return Array.isArray(schema.enum) && schema.enum.includes(null);
|
|
891
|
-
}
|
|
892
|
-
function hasOpenApiComposition(schema) {
|
|
893
|
-
return [
|
|
894
|
-
"allOf",
|
|
895
|
-
"anyOf",
|
|
896
|
-
"oneOf"
|
|
897
|
-
].some((key) => Array.isArray(schema[key]));
|
|
898
|
-
}
|
|
899
|
-
function schemaCompositionAlreadyAllowsNull(schema) {
|
|
900
|
-
return Array.isArray(schema.anyOf) && schema.anyOf.some(isNullSchemaLike) || Array.isArray(schema.oneOf) && schema.oneOf.some(isNullSchemaLike);
|
|
901
|
-
}
|
|
902
|
-
function wrapNullableComposedSchema(schema) {
|
|
903
|
-
if (schemaCompositionAlreadyAllowsNull(schema)) return schema;
|
|
904
|
-
const wrapped = { anyOf: [schema, { type: "null" }] };
|
|
905
|
-
copySchemaMeta(schema, wrapped);
|
|
906
|
-
return wrapped;
|
|
907
|
-
}
|
|
908
|
-
function normalizeOpenApiSchemaKeywords(schema) {
|
|
909
|
-
if (Array.isArray(schema)) {
|
|
910
|
-
let changed = false;
|
|
911
|
-
const normalized = schema.map((entry) => {
|
|
912
|
-
const next = normalizeOpenApiSchemaKeywords(entry);
|
|
913
|
-
changed ||= next !== entry;
|
|
914
|
-
return next;
|
|
915
|
-
});
|
|
916
|
-
return changed ? normalized : schema;
|
|
917
|
-
}
|
|
918
|
-
if (!isRecord(schema)) return schema;
|
|
919
|
-
let changed = false;
|
|
920
|
-
const nullable = schema.nullable === true;
|
|
921
|
-
const normalized = {};
|
|
922
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
923
|
-
if (key === "nullable" || OPENAPI_SCHEMA_ANNOTATION_KEYS.has(key)) {
|
|
924
|
-
changed = true;
|
|
925
|
-
continue;
|
|
926
|
-
}
|
|
927
|
-
if (SCHEMA_LITERAL_KEYS.has(key)) {
|
|
928
|
-
normalized[key] = value;
|
|
929
|
-
continue;
|
|
930
|
-
}
|
|
931
|
-
if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
|
|
932
|
-
let mapChanged = false;
|
|
933
|
-
const next = Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => {
|
|
934
|
-
const nextEntry = normalizeOpenApiSchemaKeywords(entryValue);
|
|
935
|
-
mapChanged ||= nextEntry !== entryValue;
|
|
936
|
-
return [entryKey, nextEntry];
|
|
937
|
-
}));
|
|
938
|
-
normalized[key] = mapChanged ? next : value;
|
|
939
|
-
changed ||= mapChanged;
|
|
940
|
-
continue;
|
|
941
|
-
}
|
|
942
|
-
if (key === "components") {
|
|
943
|
-
normalized[key] = value;
|
|
944
|
-
continue;
|
|
945
|
-
}
|
|
946
|
-
if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) {
|
|
947
|
-
const next = normalizeOpenApiSchemaKeywords(value);
|
|
948
|
-
normalized[key] = next;
|
|
949
|
-
changed ||= next !== value;
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
|
|
953
|
-
const next = value.map(normalizeOpenApiSchemaKeywords);
|
|
954
|
-
normalized[key] = next;
|
|
955
|
-
changed ||= next.some((entry, index) => entry !== value[index]);
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
|
-
normalized[key] = value;
|
|
959
|
-
}
|
|
960
|
-
if (nullable) {
|
|
961
|
-
if (hasOpenApiComposition(normalized)) return wrapNullableComposedSchema(normalized);
|
|
962
|
-
if ("type" in normalized) {
|
|
963
|
-
const nextType = appendNullSchemaType(normalized.type);
|
|
964
|
-
if (nextType !== normalized.type) normalized.type = nextType;
|
|
965
|
-
}
|
|
966
|
-
if (Array.isArray(normalized.enum) && !normalized.enum.includes(null)) normalized.enum = [...normalized.enum, null];
|
|
967
|
-
}
|
|
968
|
-
return changed || nullable ? normalized : schema;
|
|
969
|
-
}
|
|
970
|
-
function normalizeToolParameterSchemaUncached(schema, options) {
|
|
971
|
-
const inlinedSchema = normalizeOpenApiSchemaKeywords(inlineLocalToolSchemaRefs(schema));
|
|
972
|
-
const schemaRecord = inlinedSchema && typeof inlinedSchema === "object" ? inlinedSchema : void 0;
|
|
973
|
-
if (!schemaRecord) return inlinedSchema;
|
|
974
|
-
const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
|
|
975
|
-
const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
|
|
976
|
-
const normalizedToolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
|
|
977
|
-
const isGeminiProvider = normalizedProvider.includes("google") || normalizedProvider.includes("gemini") || isGeminiModelId(normalizedModelId) || normalizedToolSchemaProfile === "gemini";
|
|
978
|
-
const isAnthropicProvider = normalizedProvider.includes("anthropic");
|
|
979
|
-
const unsupportedToolSchemaKeywords = resolveUnsupportedToolSchemaKeywords(options?.modelCompat);
|
|
980
|
-
const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
|
|
981
|
-
const isLlamacppGbnfProfile = normalizedToolSchemaProfile === "llamacpp";
|
|
982
|
-
function applyProviderCleaning(s) {
|
|
983
|
-
const normalizedSchema = normalizeArraySchemasMissingItems(s);
|
|
984
|
-
let arrayItemsCompatibleSchema = omitEmptyArrayItems ? stripEmptyArrayItemsFromArraySchemas(normalizedSchema) : normalizedSchema;
|
|
985
|
-
if (isLlamacppGbnfProfile) arrayItemsCompatibleSchema = cleanSchemaForLlamacppGbnf(arrayItemsCompatibleSchema);
|
|
986
|
-
if (isGeminiProvider && !isAnthropicProvider) {
|
|
987
|
-
const geminiCompatibleSchema = cleanSchemaForGemini(arrayItemsCompatibleSchema);
|
|
988
|
-
return unsupportedToolSchemaKeywords.size > 0 ? stripUnsupportedSchemaKeywords(geminiCompatibleSchema, unsupportedToolSchemaKeywords) : geminiCompatibleSchema;
|
|
989
|
-
}
|
|
990
|
-
if (unsupportedToolSchemaKeywords.size > 0) return stripUnsupportedSchemaKeywords(arrayItemsCompatibleSchema, unsupportedToolSchemaKeywords);
|
|
991
|
-
return arrayItemsCompatibleSchema;
|
|
992
|
-
}
|
|
993
|
-
const conditionalKey = getTopLevelConditionalKey(schemaRecord);
|
|
994
|
-
const flattenableVariantKey = getFlattenableVariantKey(schemaRecord);
|
|
995
|
-
if (hasTopLevelObjectSchema(schemaRecord, conditionalKey)) return applyProviderCleaning(schemaRecord);
|
|
996
|
-
if (isObjectLikeSchemaMissingType(schemaRecord, conditionalKey)) return applyProviderCleaning({
|
|
997
|
-
...schemaRecord,
|
|
998
|
-
type: "object",
|
|
999
|
-
properties: isRecord(schemaRecord.properties) ? schemaRecord.properties : {}
|
|
1000
|
-
});
|
|
1001
|
-
if (isTypedObjectSchemaMissingValidProperties(schemaRecord, conditionalKey)) return applyProviderCleaning({
|
|
1002
|
-
...schemaRecord,
|
|
1003
|
-
properties: {}
|
|
1004
|
-
});
|
|
1005
|
-
if (!flattenableVariantKey) {
|
|
1006
|
-
if (isTrulyEmptySchema(schemaRecord)) return applyProviderCleaning({
|
|
1007
|
-
type: "object",
|
|
1008
|
-
properties: {}
|
|
1009
|
-
});
|
|
1010
|
-
if (conditionalKey === "allOf") return applyProviderCleaning(inlinedSchema);
|
|
1011
|
-
return applyProviderCleaning(inlinedSchema);
|
|
1012
|
-
}
|
|
1013
|
-
const variants = schemaRecord[flattenableVariantKey];
|
|
1014
|
-
const mergedProperties = {};
|
|
1015
|
-
const requiredCounts = /* @__PURE__ */ new Map();
|
|
1016
|
-
let objectVariants = 0;
|
|
1017
|
-
for (const entry of variants) {
|
|
1018
|
-
if (!entry || typeof entry !== "object") continue;
|
|
1019
|
-
const props = entry.properties;
|
|
1020
|
-
if (!props || typeof props !== "object") continue;
|
|
1021
|
-
objectVariants += 1;
|
|
1022
|
-
for (const [key, value] of Object.entries(props)) {
|
|
1023
|
-
if (!(key in mergedProperties)) {
|
|
1024
|
-
mergedProperties[key] = value;
|
|
1025
|
-
continue;
|
|
1026
|
-
}
|
|
1027
|
-
mergedProperties[key] = mergePropertySchemas(mergedProperties[key], value);
|
|
1028
|
-
}
|
|
1029
|
-
const required = Array.isArray(entry.required) ? entry.required : [];
|
|
1030
|
-
for (const key of required) {
|
|
1031
|
-
if (typeof key !== "string") continue;
|
|
1032
|
-
requiredCounts.set(key, (requiredCounts.get(key) ?? 0) + 1);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
const baseRequired = Array.isArray(schemaRecord.required) ? schemaRecord.required.filter((key) => typeof key === "string") : void 0;
|
|
1036
|
-
const mergedRequired = baseRequired && baseRequired.length > 0 ? baseRequired : objectVariants > 0 ? Array.from(requiredCounts.entries()).filter(([, count]) => count === objectVariants).map(([key]) => key) : void 0;
|
|
1037
|
-
const nextSchema = { ...schemaRecord };
|
|
1038
|
-
return applyProviderCleaning({
|
|
1039
|
-
type: "object",
|
|
1040
|
-
...typeof nextSchema.title === "string" ? { title: nextSchema.title } : {},
|
|
1041
|
-
...typeof nextSchema.description === "string" ? { description: nextSchema.description } : {},
|
|
1042
|
-
properties: Object.keys(mergedProperties).length > 0 ? mergedProperties : schemaRecord.properties ?? {},
|
|
1043
|
-
...mergedRequired && mergedRequired.length > 0 ? { required: mergedRequired } : {},
|
|
1044
|
-
additionalProperties: "additionalProperties" in schemaRecord ? schemaRecord.additionalProperties : true
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
/** Return a provider-compatible JSON schema for a model-facing tool. */
|
|
1048
|
-
function normalizeToolParameterSchema(schema, options) {
|
|
1049
|
-
if (!schema || typeof schema !== "object") return normalizeToolParameterSchemaUncached(schema, options);
|
|
1050
|
-
const cacheKey = resolveToolParameterSchemaCacheKey(options);
|
|
1051
|
-
const cached = getCachedToolParameterSchema(schema, cacheKey);
|
|
1052
|
-
if (cached) return cached;
|
|
1053
|
-
return rememberCachedToolParameterSchema(schema, cacheKey, normalizeToolParameterSchemaUncached(schema, options));
|
|
1054
|
-
}
|
|
1055
|
-
//#endregion
|
|
1056
|
-
//#region packages/ai/src/providers/openai-tool-schema-compat.ts
|
|
1057
|
-
const OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
1058
|
-
"$defs",
|
|
1059
|
-
"definitions",
|
|
1060
|
-
"dependentSchemas",
|
|
1061
|
-
"dependencies",
|
|
1062
|
-
"patternProperties",
|
|
1063
|
-
"properties"
|
|
1064
|
-
]);
|
|
1065
|
-
const OPENAI_NULLABLE_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
1066
|
-
"default",
|
|
1067
|
-
"description",
|
|
1068
|
-
"examples",
|
|
1069
|
-
"format",
|
|
1070
|
-
"title"
|
|
1071
|
-
]);
|
|
1072
|
-
const OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS = /* @__PURE__ */ new Set([
|
|
1073
|
-
"additionalItems",
|
|
1074
|
-
"additionalProperties",
|
|
1075
|
-
"allOf",
|
|
1076
|
-
"anyOf",
|
|
1077
|
-
"contains",
|
|
1078
|
-
"contentSchema",
|
|
1079
|
-
"else",
|
|
1080
|
-
"if",
|
|
1081
|
-
"items",
|
|
1082
|
-
"not",
|
|
1083
|
-
"oneOf",
|
|
1084
|
-
"prefixItems",
|
|
1085
|
-
"propertyNames",
|
|
1086
|
-
"then",
|
|
1087
|
-
"unevaluatedItems",
|
|
1088
|
-
"unevaluatedProperties"
|
|
1089
|
-
]);
|
|
1090
|
-
function normalizeOpenAIStrictCompatSchemaMap(schema) {
|
|
1091
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
|
|
1092
|
-
let changed = false;
|
|
1093
|
-
const normalized = {};
|
|
1094
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
1095
|
-
const next = normalizeOpenAIStrictCompatSchemaRecursive(value, { promoteEmptyObject: false });
|
|
1096
|
-
normalized[key] = next;
|
|
1097
|
-
changed ||= next !== value;
|
|
1098
|
-
}
|
|
1099
|
-
return changed ? normalized : schema;
|
|
1100
|
-
}
|
|
1101
|
-
function normalizeOpenAIStrictCompatSchemaRecursive(schema, options) {
|
|
1102
|
-
if (Array.isArray(schema)) {
|
|
1103
|
-
let changed = false;
|
|
1104
|
-
const normalized = schema.map((entry) => {
|
|
1105
|
-
const next = normalizeOpenAIStrictCompatSchemaRecursive(entry, { promoteEmptyObject: false });
|
|
1106
|
-
changed ||= next !== entry;
|
|
1107
|
-
return next;
|
|
1108
|
-
});
|
|
1109
|
-
return changed ? normalized : schema;
|
|
1110
|
-
}
|
|
1111
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1112
|
-
const record = schema;
|
|
1113
|
-
let changed = false;
|
|
1114
|
-
let hadNullType = false;
|
|
1115
|
-
const normalized = {};
|
|
1116
|
-
for (const [key, value] of Object.entries(record)) {
|
|
1117
|
-
if (value === null && OPENAI_NULLABLE_ANNOTATION_KEYS.has(key)) {
|
|
1118
|
-
changed = true;
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1121
|
-
if (value === null && key === "type") {
|
|
1122
|
-
hadNullType = true;
|
|
1123
|
-
changed = true;
|
|
1124
|
-
continue;
|
|
1125
|
-
}
|
|
1126
|
-
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;
|
|
1127
|
-
normalized[key] = next;
|
|
1128
|
-
changed ||= next !== value;
|
|
1129
|
-
}
|
|
1130
|
-
if (Object.keys(normalized).length === 0) {
|
|
1131
|
-
if (!options.promoteEmptyObject) return schema;
|
|
1132
|
-
return {
|
|
1133
|
-
type: "object",
|
|
1134
|
-
properties: {},
|
|
1135
|
-
required: [],
|
|
1136
|
-
additionalProperties: false
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
const hasObjectShapeHints = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) || Array.isArray(normalized.required);
|
|
1140
|
-
const hasArrayShapeHints = "items" in normalized;
|
|
1141
|
-
if (!("type" in normalized) && hasObjectShapeHints !== hasArrayShapeHints) {
|
|
1142
|
-
normalized.type = hasObjectShapeHints ? "object" : "array";
|
|
1143
|
-
changed = true;
|
|
1144
|
-
} else if (hadNullType && !("type" in normalized)) normalized.type = null;
|
|
1145
|
-
if (normalized.type === "object" && !("properties" in normalized)) {
|
|
1146
|
-
normalized.properties = {};
|
|
1147
|
-
changed = true;
|
|
1148
|
-
}
|
|
1149
|
-
const hasEmptyProperties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) && Object.keys(normalized.properties).length === 0;
|
|
1150
|
-
if (normalized.type === "object" && !Array.isArray(normalized.required) && hasEmptyProperties) {
|
|
1151
|
-
normalized.required = [];
|
|
1152
|
-
changed = true;
|
|
1153
|
-
}
|
|
1154
|
-
if (normalized.type === "object" && hasEmptyProperties && !("additionalProperties" in normalized)) {
|
|
1155
|
-
normalized.additionalProperties = false;
|
|
1156
|
-
changed = true;
|
|
1157
|
-
}
|
|
1158
|
-
return changed ? normalized : schema;
|
|
1159
|
-
}
|
|
1160
|
-
/** Repairs recoverable OpenAI tool-schema shapes before canonical normalization. */
|
|
1161
|
-
function normalizeOpenAIStrictCompatSchema(schema) {
|
|
1162
|
-
return normalizeOpenAIStrictCompatSchemaRecursive(schema, { promoteEmptyObject: true });
|
|
1163
|
-
}
|
|
1164
|
-
/** Finds schema paths that violate OpenAI strict tool-schema requirements. */
|
|
1165
|
-
function findOpenAIStrictSchemaViolations(schema, path, options) {
|
|
1166
|
-
if (Array.isArray(schema)) {
|
|
1167
|
-
if (options?.requireObjectRoot) return [`${path}.type`];
|
|
1168
|
-
return schema.flatMap((item, index) => findOpenAIStrictSchemaViolations(item, `${path}[${index}]`));
|
|
1169
|
-
}
|
|
1170
|
-
if (!schema || typeof schema !== "object") return options?.requireObjectRoot ? [`${path}.type`] : [];
|
|
1171
|
-
const record = schema;
|
|
1172
|
-
const violations = [];
|
|
1173
|
-
for (const key of [
|
|
1174
|
-
"anyOf",
|
|
1175
|
-
"oneOf",
|
|
1176
|
-
"allOf"
|
|
1177
|
-
]) if (key in record) violations.push(`${path}.${key}`);
|
|
1178
|
-
if (Array.isArray(record.type)) violations.push(`${path}.type`);
|
|
1179
|
-
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : void 0;
|
|
1180
|
-
if (record.type === "object") {
|
|
1181
|
-
if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
|
|
1182
|
-
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
1183
|
-
if (!required) violations.push(`${path}.required`);
|
|
1184
|
-
else if (properties) {
|
|
1185
|
-
const requiredSet = new Set(required);
|
|
1186
|
-
for (const key of Object.keys(properties)) if (!requiredSet.has(key)) violations.push(`${path}.required.${key}`);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
for (const key of OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS) {
|
|
1190
|
-
const schemaMap = record[key];
|
|
1191
|
-
if (!schemaMap || typeof schemaMap !== "object" || Array.isArray(schemaMap)) continue;
|
|
1192
|
-
for (const [entryKey, value] of Object.entries(schemaMap)) violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}.${entryKey}`));
|
|
1193
|
-
}
|
|
1194
|
-
for (const key of OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS) {
|
|
1195
|
-
const value = record[key];
|
|
1196
|
-
if (value && typeof value === "object") violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}`));
|
|
1197
|
-
}
|
|
1198
|
-
return violations;
|
|
1199
|
-
}
|
|
1200
|
-
//#endregion
|
|
1201
|
-
//#region packages/ai/src/providers/openai-tool-schema.ts
|
|
1202
|
-
/**
|
|
1203
|
-
* OpenAI strict JSON-schema normalization for tool inventories and request payloads.
|
|
1204
|
-
*
|
|
1205
|
-
* Caches normalized object inputs by provider compatibility so repeated inventory builds preserve identity.
|
|
1206
|
-
*/
|
|
1207
|
-
const MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
|
|
1208
|
-
const strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
|
|
1209
|
-
function resolveToolSchemaModelCompat(compat) {
|
|
1210
|
-
if (!compat) return;
|
|
1211
|
-
const unsupportedToolSchemaKeywords = Array.isArray(compat.unsupportedToolSchemaKeywords) ? compat.unsupportedToolSchemaKeywords.filter((keyword) => typeof keyword === "string") : [];
|
|
1212
|
-
if (unsupportedToolSchemaKeywords.length === 0 && compat.omitEmptyArrayItems !== true) return;
|
|
1213
|
-
return {
|
|
1214
|
-
...unsupportedToolSchemaKeywords.length > 0 ? { unsupportedToolSchemaKeywords } : {},
|
|
1215
|
-
...compat.omitEmptyArrayItems === true ? { omitEmptyArrayItems: true } : {}
|
|
1216
|
-
};
|
|
1217
|
-
}
|
|
1218
|
-
function resolveStrictOpenAISchemaCacheKey(modelCompat) {
|
|
1219
|
-
const compat = resolveToolSchemaModelCompat(modelCompat);
|
|
1220
|
-
return JSON.stringify([[...compat?.unsupportedToolSchemaKeywords ?? []].toSorted(), shouldOmitEmptyArrayItems(compat)]);
|
|
1221
|
-
}
|
|
1222
|
-
function readCachedStrictOpenAISchema(schema, key) {
|
|
1223
|
-
return strictOpenAISchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
|
|
1224
|
-
}
|
|
1225
|
-
function rememberStrictOpenAISchema(schema, key, value) {
|
|
1226
|
-
const entries = strictOpenAISchemaCache.get(schema) ?? [];
|
|
1227
|
-
strictOpenAISchemaCache.set(schema, [{
|
|
1228
|
-
key,
|
|
1229
|
-
value
|
|
1230
|
-
}, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
|
|
1231
|
-
return value;
|
|
1232
|
-
}
|
|
1233
|
-
/** Normalizes a tool parameter schema into the OpenAI strict JSON-schema subset. */
|
|
1234
|
-
function normalizeStrictOpenAIJsonSchema(schema, modelCompat) {
|
|
1235
|
-
const schemaInput = schema ?? {};
|
|
1236
|
-
if (!schemaInput || typeof schemaInput !== "object") return normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0);
|
|
1237
|
-
const cacheKey = resolveStrictOpenAISchemaCacheKey(modelCompat);
|
|
1238
|
-
const cached = readCachedStrictOpenAISchema(schemaInput, cacheKey);
|
|
1239
|
-
if (cached !== void 0) return cached;
|
|
1240
|
-
return rememberStrictOpenAISchema(schemaInput, cacheKey, normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0));
|
|
1241
|
-
}
|
|
1242
|
-
function normalizeStrictOpenAIJsonSchemaRecursive(schema, depth) {
|
|
1243
|
-
if (Array.isArray(schema)) {
|
|
1244
|
-
let changed = false;
|
|
1245
|
-
const normalized = schema.map((entry) => {
|
|
1246
|
-
const next = normalizeStrictOpenAIJsonSchemaRecursive(entry, depth);
|
|
1247
|
-
changed ||= next !== entry;
|
|
1248
|
-
return next;
|
|
1249
|
-
});
|
|
1250
|
-
return changed ? normalized : schema;
|
|
1251
|
-
}
|
|
1252
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1253
|
-
const record = schema;
|
|
1254
|
-
let changed = false;
|
|
1255
|
-
const normalized = {};
|
|
1256
|
-
for (const [key, value] of Object.entries(record)) {
|
|
1257
|
-
const next = normalizeStrictOpenAIJsonSchemaRecursive(value, key === "properties" ? depth : depth + 1);
|
|
1258
|
-
normalized[key] = next;
|
|
1259
|
-
changed ||= next !== value;
|
|
1260
|
-
}
|
|
1261
|
-
if (normalized.type === "object") {
|
|
1262
|
-
const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : void 0;
|
|
1263
|
-
if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
|
|
1264
|
-
normalized.required = [];
|
|
1265
|
-
changed = true;
|
|
1266
|
-
}
|
|
1267
|
-
if (depth === 0 && !("additionalProperties" in normalized)) {
|
|
1268
|
-
normalized.additionalProperties = false;
|
|
1269
|
-
changed = true;
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
return changed ? normalized : schema;
|
|
1273
|
-
}
|
|
1274
|
-
/** Normalizes tool parameters using strict OpenAI rules only when strict mode is active. */
|
|
1275
|
-
function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
|
|
1276
|
-
const toolSchemaCompat = resolveToolSchemaModelCompat(modelCompat);
|
|
1277
|
-
if (!strict) return normalizeToolParameterSchema(schema ?? {}, { modelCompat: toolSchemaCompat });
|
|
1278
|
-
return normalizeStrictOpenAIJsonSchema(schema, toolSchemaCompat);
|
|
1279
|
-
}
|
|
1280
|
-
/** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
|
|
1281
|
-
function isStrictOpenAIJsonSchemaCompatible(schema) {
|
|
1282
|
-
return isStrictOpenAIJsonSchemaCompatibleRecursive(normalizeStrictOpenAIJsonSchema(schema));
|
|
1283
|
-
}
|
|
1284
|
-
/** Returns strict-schema diagnostics for an already materialized OpenAI tool projection. */
|
|
1285
|
-
function findOpenAIStrictToolProjectionDiagnostics(projection) {
|
|
1286
|
-
return [...projection.diagnostics.map((diagnostic) => ({
|
|
1287
|
-
toolIndex: diagnostic.toolIndex,
|
|
1288
|
-
...diagnostic.toolName ? { toolName: diagnostic.toolName } : {},
|
|
1289
|
-
violations: [...diagnostic.violations]
|
|
1290
|
-
})), ...projection.tools.flatMap((tool) => {
|
|
1291
|
-
const violations = findOpenAIStrictSchemaViolations(normalizeStrictOpenAIJsonSchema(tool.parameters), `${tool.name}.parameters`);
|
|
1292
|
-
return violations.length > 0 ? [{
|
|
1293
|
-
toolIndex: tool.toolIndex,
|
|
1294
|
-
toolName: tool.name,
|
|
1295
|
-
violations
|
|
1296
|
-
}] : [];
|
|
1297
|
-
})];
|
|
1298
|
-
}
|
|
1299
|
-
function isStrictOpenAIJsonSchemaCompatibleRecursive(schema) {
|
|
1300
|
-
if (Array.isArray(schema)) return schema.every((entry) => isStrictOpenAIJsonSchemaCompatibleRecursive(entry));
|
|
1301
|
-
if (!schema || typeof schema !== "object") return true;
|
|
1302
|
-
const record = schema;
|
|
1303
|
-
if ("anyOf" in record || "oneOf" in record || "allOf" in record) return false;
|
|
1304
|
-
if (Array.isArray(record.type)) return false;
|
|
1305
|
-
if (record.type === "object" && record.additionalProperties !== false) return false;
|
|
1306
|
-
if (record.type === "object") {
|
|
1307
|
-
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
|
|
1308
|
-
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
1309
|
-
if (!required) return false;
|
|
1310
|
-
const requiredSet = new Set(required);
|
|
1311
|
-
if (Object.keys(properties).some((key) => !requiredSet.has(key))) return false;
|
|
1312
|
-
}
|
|
1313
|
-
return Object.entries(record).every(([key, entry]) => {
|
|
1314
|
-
if (key === "properties" && entry && typeof entry === "object" && !Array.isArray(entry)) return Object.values(entry).every((value) => isStrictOpenAIJsonSchemaCompatibleRecursive(value));
|
|
1315
|
-
return isStrictOpenAIJsonSchemaCompatibleRecursive(entry);
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
/** Resolves strict mode for the projected tools that will be emitted in the request payload. */
|
|
1319
|
-
function resolveOpenAIProjectedToolsStrictToolFlag(projection, strict) {
|
|
1320
|
-
if (strict !== true) return strict === false ? false : void 0;
|
|
1321
|
-
return projection.tools.every((tool) => isStrictOpenAIJsonSchemaCompatible(tool.parameters));
|
|
1322
|
-
}
|
|
1323
|
-
//#endregion
|
|
1324
59
|
//#region packages/ai/src/transports/openai-responses-replay.ts
|
|
1325
60
|
/** Resolves the assistant message id that can be replayed to OpenAI Responses. */
|
|
1326
61
|
function resolveReplayableResponsesMessageId(params) {
|
|
@@ -1362,7 +97,7 @@ function readOpenAIResponsesCompactionReplayState(value) {
|
|
|
1362
97
|
function openAIResponsesReplayContextMatches(state, context) {
|
|
1363
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;
|
|
1364
99
|
}
|
|
1365
|
-
function captureOpenAIResponsesCompaction(output, item, boundary, model, captureMetadata) {
|
|
100
|
+
function captureOpenAIResponsesCompaction(output, item, boundary, model, captureMetadata, compactedOutput) {
|
|
1366
101
|
const metadata = captureMetadata ?? buildOpenAIResponsesReasoningReplayMetadata(model);
|
|
1367
102
|
if (!item.encrypted_content) return;
|
|
1368
103
|
if (!metadata?.baseUrlHash) {
|
|
@@ -1371,19 +106,28 @@ function captureOpenAIResponsesCompaction(output, item, boundary, model, capture
|
|
|
1371
106
|
}
|
|
1372
107
|
const currentReplay = readOpenAIResponsesCompactionReplayState(output.providerReplay);
|
|
1373
108
|
if (typeof boundary === "number" && currentReplay?.type === "openai-responses-compaction" && (currentReplay.replayIndex ?? -1) > boundary) return;
|
|
1374
|
-
|
|
109
|
+
if (compactedOutput && !isOpenAIResponsesCompactionOutput(compactedOutput, model)) throw new Error("Responses compact endpoint checkpoint is invalid");
|
|
110
|
+
const replay = {
|
|
1375
111
|
v: 1,
|
|
1376
|
-
|
|
112
|
+
...boundary === "retained-users" ? { type: OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE } : {
|
|
113
|
+
type: OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE,
|
|
114
|
+
replayIndex: boundary
|
|
115
|
+
},
|
|
1377
116
|
...item.id ? { id: item.id } : {},
|
|
1378
117
|
data: item.encrypted_content,
|
|
1379
|
-
...typeof boundary === "number" ? { replayIndex: boundary } : {},
|
|
1380
118
|
provider: metadata.provider,
|
|
1381
119
|
api: metadata.api,
|
|
1382
120
|
model: metadata.model,
|
|
1383
121
|
baseUrlHash: metadata.baseUrlHash,
|
|
1384
122
|
...metadata.sessionHash ? { sessionHash: metadata.sessionHash } : {},
|
|
1385
|
-
...metadata.authProfileHash ? { authProfileHash: metadata.authProfileHash } : {}
|
|
123
|
+
...metadata.authProfileHash ? { authProfileHash: metadata.authProfileHash } : {},
|
|
124
|
+
...compactedOutput ? { compactedWindow: {
|
|
125
|
+
state: "ready",
|
|
126
|
+
output: JSON.stringify(compactedOutput)
|
|
127
|
+
} } : {}
|
|
1386
128
|
};
|
|
129
|
+
if (compactedOutput && !readOpenAIResponsesCompactionWindow(replay, model)) throw new Error("Responses compact endpoint checkpoint is invalid or exceeds 16 MiB");
|
|
130
|
+
output.providerReplay = replay;
|
|
1387
131
|
}
|
|
1388
132
|
function suppressOpenAIResponsesCompaction(output, model, options, rejectedCheckpoint) {
|
|
1389
133
|
const context = buildOpenAIResponsesReplayContext(model, options);
|
|
@@ -1432,6 +176,21 @@ function resolveNewestOpenAIResponsesCompactionReplay(messages, model, options)
|
|
|
1432
176
|
continue;
|
|
1433
177
|
}
|
|
1434
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
|
+
}
|
|
1435
194
|
return {
|
|
1436
195
|
owner: message,
|
|
1437
196
|
item: {
|
|
@@ -1439,13 +198,17 @@ function resolveNewestOpenAIResponsesCompactionReplay(messages, model, options)
|
|
|
1439
198
|
...isSafeResponsesReplayItemId(replay.id) ? { id: replay.id } : {},
|
|
1440
199
|
encrypted_content: replay.data
|
|
1441
200
|
},
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
replayIndex: replay.replayIndex ?? 0
|
|
1445
|
-
}
|
|
201
|
+
mode: "compacted-prefix",
|
|
202
|
+
replayIndex: replay.replayIndex ?? 0
|
|
1446
203
|
};
|
|
1447
204
|
}
|
|
1448
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
|
+
};
|
|
1449
212
|
function buildOpenAIResponsesCompactionReplayPlan(messages, model, options) {
|
|
1450
213
|
if (options?.mode === "full-history") return {
|
|
1451
214
|
messages,
|
|
@@ -1456,22 +219,14 @@ function buildOpenAIResponsesCompactionReplayPlan(messages, model, options) {
|
|
|
1456
219
|
messages,
|
|
1457
220
|
preserveUnframedToolResults: false
|
|
1458
221
|
};
|
|
222
|
+
if (compaction.mode === "refresh-required") throw new CompactionReplayRefreshRequiredError();
|
|
1459
223
|
const ownerIndex = messages.indexOf(compaction.owner);
|
|
1460
|
-
const collectRetainedUserMessages = (prefix) => {
|
|
1461
|
-
const previous = resolveNewestOpenAIResponsesCompactionReplay(prefix, model, options);
|
|
1462
|
-
if (!previous) return prefix.filter((message) => message.role === "user");
|
|
1463
|
-
const previousOwnerIndex = prefix.indexOf(previous.owner);
|
|
1464
|
-
const laterUsers = prefix.slice(previousOwnerIndex + 1).filter((message) => message.role === "user");
|
|
1465
|
-
return previous.mode === "retained-users" ? [...collectRetainedUserMessages(prefix.slice(0, previousOwnerIndex)), ...laterUsers] : laterUsers;
|
|
1466
|
-
};
|
|
1467
|
-
const retainedMessages = compaction.mode === "retained-users" ? collectRetainedUserMessages(messages.slice(0, ownerIndex)) : void 0;
|
|
1468
224
|
return {
|
|
1469
225
|
messages: [{
|
|
1470
226
|
...compaction.owner,
|
|
1471
|
-
content: compaction.
|
|
227
|
+
content: compaction.owner.content.slice(compaction.replayIndex)
|
|
1472
228
|
}, ...messages.slice(ownerIndex + 1)],
|
|
1473
|
-
...
|
|
1474
|
-
compaction: compaction.item,
|
|
229
|
+
...compaction.mode === "complete-window" ? { compactedWindow: compaction.output } : { compaction: compaction.item },
|
|
1475
230
|
preserveUnframedToolResults: true
|
|
1476
231
|
};
|
|
1477
232
|
}
|
|
@@ -1663,39 +418,47 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
1663
418
|
preserveUnframedToolResults: replayPlan.preserveUnframedToolResults
|
|
1664
419
|
});
|
|
1665
420
|
const transformedMessages = transformMessages(replayPlan.messages);
|
|
1666
|
-
|
|
1667
|
-
if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && (providerStyle ? model.compat?.supportsDeveloperRole !== false : options?.supportsDeveloperRole !== false) ? "developer" : "system", [{
|
|
421
|
+
if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && model.compat?.supportsDeveloperRole !== false ? "developer" : "system", [{
|
|
1668
422
|
type: "input_text",
|
|
1669
423
|
text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
|
|
1670
424
|
}]));
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
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
|
+
}
|
|
1676
438
|
let msgIndex = 0;
|
|
1677
439
|
for (const msg of replayMessages) {
|
|
1678
440
|
if (!("role" in msg)) {
|
|
1679
441
|
messages.push(msg);
|
|
1680
442
|
continue;
|
|
1681
443
|
}
|
|
1682
|
-
if (msg.role === "user")
|
|
1683
|
-
|
|
1684
|
-
text: sanitizeTransportPayloadText(msg.content)
|
|
1685
|
-
}]));
|
|
1686
|
-
else {
|
|
1687
|
-
const content = msg.content.map((item) => item.type === "text" ? {
|
|
444
|
+
if (msg.role === "user") {
|
|
445
|
+
if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
|
|
1688
446
|
type: "input_text",
|
|
1689
|
-
text: sanitizeTransportPayloadText(
|
|
1690
|
-
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
447
|
+
text: sanitizeTransportPayloadText(msg.content)
|
|
448
|
+
}]));
|
|
449
|
+
else {
|
|
450
|
+
const content = msg.content.map((item) => item.type === "text" ? {
|
|
451
|
+
type: "input_text",
|
|
452
|
+
text: sanitizeTransportPayloadText(item.text)
|
|
453
|
+
} : {
|
|
454
|
+
type: "input_image",
|
|
455
|
+
detail: "auto",
|
|
456
|
+
image_url: `data:${item.mimeType};base64,${item.data}`
|
|
457
|
+
}).filter((item) => providerStyle || model.input.includes("image") || item.type !== "input_image");
|
|
458
|
+
if (content.length > 0) messages.push(buildResponsesInputMessage("user", content));
|
|
459
|
+
else if (providerStyle) continue;
|
|
460
|
+
}
|
|
461
|
+
} else if (msg.role === "assistant") {
|
|
1699
462
|
const output = [];
|
|
1700
463
|
let textFallbackOrdinal = 0;
|
|
1701
464
|
let previousReplayItemWasReasoning = false;
|
|
@@ -1799,8 +562,8 @@ function isInvalidEncryptedContentError(error) {
|
|
|
1799
562
|
if (!error || typeof error !== "object") return false;
|
|
1800
563
|
const record = error;
|
|
1801
564
|
if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
|
|
1802
|
-
const message = typeof record.message === "string" ? record.message : "";
|
|
1803
|
-
return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || record.status === 400 && message
|
|
565
|
+
const message = typeof record.message === "string" ? record.message.toLowerCase() : "";
|
|
566
|
+
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")));
|
|
1804
567
|
}
|
|
1805
568
|
function isOrphanedFunctionCallOutputError(error) {
|
|
1806
569
|
if (!error || typeof error !== "object") return false;
|
|
@@ -1880,7 +643,45 @@ async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
|
1880
643
|
payloadVariant: attempt.kind
|
|
1881
644
|
});
|
|
1882
645
|
try {
|
|
1883
|
-
|
|
646
|
+
const result = await sendAttempt(attempt);
|
|
647
|
+
return {
|
|
648
|
+
...result,
|
|
649
|
+
stream: { async *[Symbol.asyncIterator]() {
|
|
650
|
+
let rejectedEvent;
|
|
651
|
+
try {
|
|
652
|
+
for await (const event of params.wrapStream?.(result) ?? result.stream) {
|
|
653
|
+
if (isRecord(event)) {
|
|
654
|
+
const failure = event.type === "response.failed" && isRecord(event.response) ? event.response.error : event.type === "error" ? event.error ?? event : void 0;
|
|
655
|
+
if (isRecord(failure) && params.canRetryStream?.() === true && isInvalidEncryptedContentError(failure)) {
|
|
656
|
+
rejectedEvent = event;
|
|
657
|
+
const message = typeof failure.message === "string" ? failure.message : "";
|
|
658
|
+
throw Object.assign(new Error(message), {
|
|
659
|
+
code: failure.code,
|
|
660
|
+
status: failure.status
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
yield event;
|
|
665
|
+
}
|
|
666
|
+
} catch (error) {
|
|
667
|
+
const nextAttempt = params.canRetryStream?.() === true && !params.requestOptions?.signal?.aborted ? await resolveNextResponsesEncryptedContentAttempt(result.attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest }) : void 0;
|
|
668
|
+
if (!nextAttempt) {
|
|
669
|
+
if (rejectedEvent !== void 0) {
|
|
670
|
+
yield rejectedEvent;
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
throw error;
|
|
674
|
+
}
|
|
675
|
+
log.warn(`[responses] retrying streamed encrypted content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
676
|
+
yield* (await createResponsesStreamWithEncryptedContentRetry({
|
|
677
|
+
...params,
|
|
678
|
+
request: nextAttempt.request,
|
|
679
|
+
initialAttemptKind: nextAttempt.kind,
|
|
680
|
+
initialRejectedCompaction: nextAttempt.rejectedCompaction
|
|
681
|
+
})).stream;
|
|
682
|
+
}
|
|
683
|
+
} }
|
|
684
|
+
};
|
|
1884
685
|
} catch (error) {
|
|
1885
686
|
let nextAttempt = await resolveNextResponsesEncryptedContentAttempt(attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest });
|
|
1886
687
|
if (!nextAttempt && attempt.request.previous_response_id && error && typeof error === "object" && typeof error.status === "number" && error.code === "previous_response_not_found") {
|
|
@@ -1952,14 +753,14 @@ function createResponsesToolCallTracker() {
|
|
|
1952
753
|
state.callId ??= identity.callId;
|
|
1953
754
|
return state;
|
|
1954
755
|
};
|
|
1955
|
-
const resolveCompatible = (candidates, identity) => {
|
|
756
|
+
const resolveCompatible = (candidates, identity, allowUnmatchedIdentity) => {
|
|
1956
757
|
const uniqueCandidates = [...new Set(candidates)];
|
|
1957
|
-
if (!identity.itemId && !identity.callId) return uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
|
|
758
|
+
if (!identity.itemId && !identity.callId) return allowUnmatchedIdentity && uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
|
|
1958
759
|
const compatible = uniqueCandidates.filter((state) => !identitiesConflict(state, identity));
|
|
1959
760
|
const matches = compatible.filter((state) => sharesIdentity(state, identity));
|
|
1960
761
|
const matched = matches.length === 1 ? matches.at(0) : void 0;
|
|
1961
762
|
if (matched) return adoptIdentity(matched, identity);
|
|
1962
|
-
const soleCompatible = uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
|
|
763
|
+
const soleCompatible = allowUnmatchedIdentity && uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
|
|
1963
764
|
return soleCompatible ? adoptIdentity(soleCompatible, identity) : void 0;
|
|
1964
765
|
};
|
|
1965
766
|
return {
|
|
@@ -1970,9 +771,10 @@ function createResponsesToolCallTracker() {
|
|
|
1970
771
|
return;
|
|
1971
772
|
}
|
|
1972
773
|
if (indexedCalls.has(outputIndex)) throw new Error(`Responses stream reused active tool-call output index ${outputIndex}`);
|
|
774
|
+
state.outputIndex = outputIndex;
|
|
1973
775
|
indexedCalls.set(outputIndex, state);
|
|
1974
776
|
},
|
|
1975
|
-
resolve(event, identity = readEventIdentity(event)) {
|
|
777
|
+
resolve(event, identity = readEventIdentity(event), allowUnmatchedIdentity = true) {
|
|
1976
778
|
const outputIndex = readOutputIndex(event);
|
|
1977
779
|
if (outputIndex !== void 0) {
|
|
1978
780
|
const indexed = indexedCalls.get(outputIndex);
|
|
@@ -1980,14 +782,15 @@ function createResponsesToolCallTracker() {
|
|
|
1980
782
|
if (indexed.callId && identity.callId && indexed.callId !== identity.callId) return;
|
|
1981
783
|
return adoptIdentity(indexed, identity);
|
|
1982
784
|
}
|
|
1983
|
-
const unindexed = resolveCompatible(unindexedCalls, identity);
|
|
785
|
+
const unindexed = resolveCompatible(unindexedCalls, identity, allowUnmatchedIdentity);
|
|
1984
786
|
if (unindexed) {
|
|
1985
787
|
unindexedCalls.delete(unindexed);
|
|
788
|
+
unindexed.outputIndex = outputIndex;
|
|
1986
789
|
indexedCalls.set(outputIndex, unindexed);
|
|
1987
790
|
}
|
|
1988
791
|
return unindexed;
|
|
1989
792
|
}
|
|
1990
|
-
return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity);
|
|
793
|
+
return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity, allowUnmatchedIdentity);
|
|
1991
794
|
},
|
|
1992
795
|
forget(toolCall) {
|
|
1993
796
|
for (const [outputIndex, tracked] of indexedCalls) if (tracked === toolCall) indexedCalls.delete(outputIndex);
|
|
@@ -1998,6 +801,10 @@ function createResponsesToolCallTracker() {
|
|
|
1998
801
|
},
|
|
1999
802
|
hasActive() {
|
|
2000
803
|
return indexedCalls.size > 0 || unindexedCalls.size > 0;
|
|
804
|
+
},
|
|
805
|
+
hasExactlyActive(expected) {
|
|
806
|
+
const active = /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls]);
|
|
807
|
+
return active.size === expected.length && expected.every((state) => active.has(state));
|
|
2001
808
|
}
|
|
2002
809
|
};
|
|
2003
810
|
}
|
|
@@ -2123,9 +930,10 @@ const RESPONSE_FAILED_FAILURE_FIELD_KEYS = [
|
|
|
2123
930
|
function readResponseFailedString(record, key) {
|
|
2124
931
|
return stringifyUnknown(record?.[key]);
|
|
2125
932
|
}
|
|
2126
|
-
function buildResponsesFailedEventSummary(message, responseId, observation) {
|
|
933
|
+
function buildResponsesFailedEventSummary(message, responseId, code, observation) {
|
|
2127
934
|
const summary = { message };
|
|
2128
935
|
if (responseId) summary.responseId = responseId;
|
|
936
|
+
if (code) summary.code = code;
|
|
2129
937
|
if (observation) summary.observation = observation;
|
|
2130
938
|
return summary;
|
|
2131
939
|
}
|
|
@@ -2244,11 +1052,11 @@ function normalizeResponsesFailedEvent(event, model) {
|
|
|
2244
1052
|
if (error) {
|
|
2245
1053
|
const code = readResponseFailedString(error, "code").trim();
|
|
2246
1054
|
const message = readResponseFailedString(error, "message").trim();
|
|
2247
|
-
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId);
|
|
1055
|
+
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId, code || void 0);
|
|
2248
1056
|
}
|
|
2249
1057
|
const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
|
|
2250
1058
|
if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
|
|
2251
|
-
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
1059
|
+
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, void 0, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
2252
1060
|
}
|
|
2253
1061
|
var ResponsesStreamFailure = class extends Error {
|
|
2254
1062
|
constructor(failure, response) {
|
|
@@ -2256,6 +1064,7 @@ var ResponsesStreamFailure = class extends Error {
|
|
|
2256
1064
|
this.name = "ResponsesStreamFailure";
|
|
2257
1065
|
this.responseId = failure.responseId;
|
|
2258
1066
|
this.response = response;
|
|
1067
|
+
this.code = failure.code;
|
|
2259
1068
|
this.observation = failure.observation;
|
|
2260
1069
|
}
|
|
2261
1070
|
};
|
|
@@ -2346,20 +1155,39 @@ async function* observeResponsesStream(stream, model, requestStartedAt) {
|
|
|
2346
1155
|
}
|
|
2347
1156
|
//#endregion
|
|
2348
1157
|
//#region packages/ai/src/transports/openai-responses-stream-slots-internal.ts
|
|
2349
|
-
function
|
|
2350
|
-
const
|
|
1158
|
+
function createResponsesOutputTracker() {
|
|
1159
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
2351
1160
|
const identity = (item) => {
|
|
2352
1161
|
if ((item.type === "reasoning" || item.type === "message") && item.id) return `${item.type}:${item.id}`;
|
|
2353
|
-
|
|
1162
|
+
const callId = item.call_id ?? item.id;
|
|
1163
|
+
return item.type === "function_call" && callId ? `function_call:${callId}` : void 0;
|
|
1164
|
+
};
|
|
1165
|
+
const get = (item, outputIndex) => {
|
|
1166
|
+
const key = identity(item);
|
|
1167
|
+
const output = (outputIndex === void 0 ? void 0 : outputs.get(outputIndex)) ?? (key === void 0 ? void 0 : outputs.get(key));
|
|
1168
|
+
if (!output || outputIndex !== void 0 && output.outputIndex !== void 0 && output.outputIndex !== outputIndex) return;
|
|
1169
|
+
if (output.type !== item.type || output.callId && item.call_id && output.callId !== item.call_id) throw new Error("Responses stream changed output item identity");
|
|
1170
|
+
return output;
|
|
2354
1171
|
};
|
|
2355
1172
|
return {
|
|
2356
|
-
get
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
1173
|
+
get,
|
|
1174
|
+
set(item, contentIndex, outputIndex, completed = false) {
|
|
1175
|
+
const output = get(item, outputIndex) ?? {
|
|
1176
|
+
type: item.type,
|
|
1177
|
+
contentIndex,
|
|
1178
|
+
completed
|
|
1179
|
+
};
|
|
1180
|
+
Object.assign(output, {
|
|
1181
|
+
contentIndex,
|
|
1182
|
+
completed
|
|
1183
|
+
});
|
|
1184
|
+
if (item.call_id) output.callId = item.call_id;
|
|
1185
|
+
if (outputIndex !== void 0) {
|
|
1186
|
+
output.outputIndex = outputIndex;
|
|
1187
|
+
outputs.set(outputIndex, output);
|
|
1188
|
+
}
|
|
2361
1189
|
const key = identity(item);
|
|
2362
|
-
if (key !== void 0)
|
|
1190
|
+
if (key !== void 0) outputs.set(key, output);
|
|
2363
1191
|
}
|
|
2364
1192
|
};
|
|
2365
1193
|
}
|
|
@@ -2419,8 +1247,11 @@ function createResponsesOutputSlotTracker() {
|
|
|
2419
1247
|
* package and managed transports from drifting on token buckets, service-tier pricing, or future
|
|
2420
1248
|
* terminal-event semantics.
|
|
2421
1249
|
*/
|
|
1250
|
+
function readReportedCount(value) {
|
|
1251
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1252
|
+
}
|
|
2422
1253
|
function readCount(value) {
|
|
2423
|
-
return
|
|
1254
|
+
return readReportedCount(value) ?? 0;
|
|
2424
1255
|
}
|
|
2425
1256
|
/**
|
|
2426
1257
|
* Split a terminal usage payload into the priced buckets.
|
|
@@ -2438,12 +1269,21 @@ function mapResponsesTerminalUsage(usage) {
|
|
|
2438
1269
|
const input = Math.max(0, readCount(usage.input_tokens) - cacheRead - cacheWrite);
|
|
2439
1270
|
const output = readCount(usage.output_tokens);
|
|
2440
1271
|
const bucketTotal = input + output + cacheRead + cacheWrite;
|
|
1272
|
+
const totalTokens = Math.max(bucketTotal, readCount(usage.total_tokens));
|
|
1273
|
+
const reportedInput = readReportedCount(usage.input_tokens);
|
|
1274
|
+
const reportedOutput = readReportedCount(usage.output_tokens);
|
|
1275
|
+
const reportedTotal = readReportedCount(usage.total_tokens);
|
|
2441
1276
|
return {
|
|
2442
1277
|
input,
|
|
2443
1278
|
output,
|
|
2444
1279
|
cacheRead,
|
|
2445
1280
|
cacheWrite,
|
|
2446
|
-
|
|
1281
|
+
contextUsage: reportedInput !== void 0 && (reportedOutput !== void 0 || reportedTotal !== void 0 && reportedTotal >= reportedInput) && cacheRead + cacheWrite <= reportedInput ? {
|
|
1282
|
+
state: "available",
|
|
1283
|
+
promptTokens: reportedInput,
|
|
1284
|
+
totalTokens: Math.max(totalTokens, reportedInput + (reportedOutput ?? 0))
|
|
1285
|
+
} : { state: "unavailable" },
|
|
1286
|
+
totalTokens
|
|
2447
1287
|
};
|
|
2448
1288
|
}
|
|
2449
1289
|
/** Reasoning tokens are reported by the agent path only; the package path does not track them. */
|
|
@@ -2509,51 +1349,52 @@ function createResponsesTerminalController(params) {
|
|
|
2509
1349
|
const { output, stream, model, options } = params;
|
|
2510
1350
|
const blocks = output.content;
|
|
2511
1351
|
const backfillReasoning = (items) => {
|
|
2512
|
-
for (const item of items) {
|
|
1352
|
+
for (const [outputIndex, item] of items.entries()) {
|
|
2513
1353
|
if (item.type !== "reasoning" || !item.encrypted_content) continue;
|
|
2514
|
-
const
|
|
2515
|
-
|
|
1354
|
+
const tracked = params.outputs.get(item, outputIndex);
|
|
1355
|
+
const block = tracked && blocks[tracked.contentIndex];
|
|
1356
|
+
if (block?.type !== "thinking" || !block.thinkingSignature) continue;
|
|
2516
1357
|
const stored = JSON.parse(block.thinkingSignature);
|
|
2517
1358
|
if (!stored.encrypted_content) block.thinkingSignature = JSON.stringify({
|
|
2518
1359
|
...stored,
|
|
2519
1360
|
encrypted_content: item.encrypted_content
|
|
2520
1361
|
});
|
|
2521
|
-
if (options?.reasoningReplayMetadata) block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY]
|
|
1362
|
+
if (options?.reasoningReplayMetadata) Object.assign(block, { [OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY]: options.reasoningReplayMetadata });
|
|
2522
1363
|
}
|
|
2523
1364
|
};
|
|
2524
|
-
const appendText = (item) => {
|
|
1365
|
+
const appendText = (item, contentIndex) => {
|
|
2525
1366
|
const text = (Array.isArray(item.content) ? item.content : []).map((part) => {
|
|
2526
1367
|
const content = part;
|
|
2527
1368
|
return content.type === "output_text" || content.type === "text" ? content.text ?? "" : content.refusal ?? "";
|
|
2528
1369
|
}).join("");
|
|
2529
|
-
const
|
|
1370
|
+
const block = contentIndex === void 0 ? void 0 : blocks[contentIndex];
|
|
1371
|
+
const started = block?.type === "text" ? block : void 0;
|
|
2530
1372
|
if (!text && !started) return;
|
|
2531
1373
|
const phase = item.phase ?? void 0;
|
|
2532
|
-
if (started) {
|
|
2533
|
-
const previousText = started.
|
|
2534
|
-
started.
|
|
2535
|
-
started.
|
|
1374
|
+
if (started && contentIndex !== void 0) {
|
|
1375
|
+
const previousText = started.text;
|
|
1376
|
+
started.text = text;
|
|
1377
|
+
started.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2536
1378
|
params.setLastTextBlock({
|
|
2537
|
-
block: started
|
|
2538
|
-
index:
|
|
1379
|
+
block: started,
|
|
1380
|
+
index: contentIndex,
|
|
2539
1381
|
phase
|
|
2540
1382
|
});
|
|
2541
|
-
params.startedTextBlocksByItemId.delete(item.id);
|
|
2542
1383
|
if (text.startsWith(previousText)) {
|
|
2543
1384
|
const delta = text.slice(previousText.length);
|
|
2544
1385
|
if (delta) stream.push({
|
|
2545
1386
|
type: "text_delta",
|
|
2546
|
-
contentIndex
|
|
1387
|
+
contentIndex,
|
|
2547
1388
|
delta
|
|
2548
1389
|
});
|
|
2549
1390
|
}
|
|
2550
1391
|
stream.push({
|
|
2551
1392
|
type: "text_end",
|
|
2552
|
-
contentIndex
|
|
1393
|
+
contentIndex,
|
|
2553
1394
|
content: text,
|
|
2554
1395
|
partial: output
|
|
2555
1396
|
});
|
|
2556
|
-
return
|
|
1397
|
+
return contentIndex;
|
|
2557
1398
|
}
|
|
2558
1399
|
const previous = params.getLastTextBlock();
|
|
2559
1400
|
const collapse = resolveResponsesMessageSnapshotCollapse({
|
|
@@ -2575,15 +1416,15 @@ function createResponsesTerminalController(params) {
|
|
|
2575
1416
|
});
|
|
2576
1417
|
return previous.index;
|
|
2577
1418
|
}
|
|
2578
|
-
const
|
|
1419
|
+
const newBlock = {
|
|
2579
1420
|
type: "text",
|
|
2580
1421
|
text,
|
|
2581
1422
|
textSignature: encodeTextSignatureV1(item.id, phase)
|
|
2582
1423
|
};
|
|
2583
|
-
blocks.push(
|
|
1424
|
+
blocks.push(newBlock);
|
|
2584
1425
|
const index = blocks.length - 1;
|
|
2585
1426
|
params.setLastTextBlock({
|
|
2586
|
-
block,
|
|
1427
|
+
block: newBlock,
|
|
2587
1428
|
index,
|
|
2588
1429
|
phase
|
|
2589
1430
|
});
|
|
@@ -2600,65 +1441,71 @@ function createResponsesTerminalController(params) {
|
|
|
2600
1441
|
});
|
|
2601
1442
|
return index;
|
|
2602
1443
|
};
|
|
2603
|
-
const
|
|
2604
|
-
const
|
|
2605
|
-
|
|
1444
|
+
const emitToolCallCompletion = (item, outputIndex, started, validated) => {
|
|
1445
|
+
const completed = {
|
|
1446
|
+
id: resolveResponsesToolCallId(item, started?.block.id),
|
|
1447
|
+
...validated
|
|
1448
|
+
};
|
|
1449
|
+
const toolCall = started ? Object.assign(started.block, completed) : {
|
|
2606
1450
|
type: "toolCall",
|
|
2607
|
-
|
|
2608
|
-
name: validated.name,
|
|
2609
|
-
arguments: validated.arguments
|
|
1451
|
+
...completed
|
|
2610
1452
|
};
|
|
2611
|
-
|
|
2612
|
-
const contentIndex = blocks.length
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
1453
|
+
delete toolCall.partialJson;
|
|
1454
|
+
const contentIndex = started?.contentIndex ?? blocks.length;
|
|
1455
|
+
if (!started) {
|
|
1456
|
+
blocks.push(toolCall);
|
|
1457
|
+
stream.push({
|
|
1458
|
+
type: "toolcall_start",
|
|
1459
|
+
contentIndex,
|
|
1460
|
+
partial: output
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
params.outputs.set(item, contentIndex, outputIndex, true);
|
|
2618
1464
|
stream.push({
|
|
2619
1465
|
type: "toolcall_end",
|
|
2620
1466
|
contentIndex,
|
|
2621
1467
|
toolCall,
|
|
2622
1468
|
partial: output
|
|
2623
1469
|
});
|
|
2624
|
-
return contentIndex;
|
|
2625
1470
|
};
|
|
2626
|
-
const recoverTerminalOutput = (items,
|
|
1471
|
+
const recoverTerminalOutput = (items, completeToolCall) => {
|
|
2627
1472
|
let hasCompletedLaterOutput = false;
|
|
2628
|
-
for (const item of items.toReversed()) {
|
|
1473
|
+
for (const [outputIndex, item] of [...items.entries()].toReversed()) {
|
|
1474
|
+
const tracked = params.outputs.get(item, outputIndex);
|
|
2629
1475
|
if (item.type === "reasoning") {
|
|
2630
|
-
hasCompletedLaterOutput ||=
|
|
1476
|
+
hasCompletedLaterOutput ||= tracked !== void 0;
|
|
2631
1477
|
continue;
|
|
2632
1478
|
}
|
|
2633
1479
|
if (item.type !== "message" && item.type !== "function_call") continue;
|
|
2634
|
-
if (
|
|
1480
|
+
if (tracked) {
|
|
2635
1481
|
hasCompletedLaterOutput = true;
|
|
2636
1482
|
continue;
|
|
2637
1483
|
}
|
|
2638
|
-
if (item.type === "function_call" && !
|
|
1484
|
+
if (item.type === "function_call" && !completeToolCall) continue;
|
|
2639
1485
|
if (hasCompletedLaterOutput) throw new Error("Responses stream omitted an output item before completed output");
|
|
2640
|
-
if (item.type === "function_call") resolveCompletedResponsesToolCall(item);
|
|
2641
1486
|
}
|
|
2642
1487
|
for (const [terminalIndex, item] of items.entries()) if (item.type === "message") {
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
1488
|
+
const tracked = params.outputs.get(item, terminalIndex);
|
|
1489
|
+
if (tracked?.completed) continue;
|
|
1490
|
+
const appendedIndex = appendText(item, tracked?.contentIndex);
|
|
1491
|
+
if (appendedIndex !== void 0) params.outputs.set(item, appendedIndex, terminalIndex, true);
|
|
2646
1492
|
} else {
|
|
2647
1493
|
params.setLastTextBlock(null);
|
|
2648
1494
|
const alreadyCapturedCompaction = item.type === "compaction" && output.providerReplay?.type === "openai-responses-compaction" && output.providerReplay.id === item.id && output.providerReplay.data === item.encrypted_content;
|
|
2649
1495
|
if (item.type === "compaction" && !alreadyCapturedCompaction) {
|
|
2650
1496
|
let replayIndex = blocks.length;
|
|
2651
|
-
for (const laterItem of items.
|
|
2652
|
-
|
|
1497
|
+
for (const [laterIndex, laterItem] of items.entries()) {
|
|
1498
|
+
if (laterIndex <= terminalIndex) continue;
|
|
1499
|
+
const laterContentIndex = params.outputs.get(laterItem, laterIndex)?.contentIndex;
|
|
2653
1500
|
if (laterContentIndex !== void 0) {
|
|
2654
1501
|
replayIndex = laterContentIndex;
|
|
2655
1502
|
break;
|
|
2656
1503
|
}
|
|
2657
1504
|
}
|
|
2658
1505
|
captureOpenAIResponsesCompaction(output, item, replayIndex, model, options?.reasoningReplayMetadata);
|
|
2659
|
-
} else if (
|
|
2660
|
-
if (params.
|
|
2661
|
-
|
|
1506
|
+
} else if (completeToolCall && item.type === "function_call") {
|
|
1507
|
+
if (params.outputs.get(item, terminalIndex)?.completed) continue;
|
|
1508
|
+
completeToolCall(terminalIndex);
|
|
2662
1509
|
}
|
|
2663
1510
|
}
|
|
2664
1511
|
};
|
|
@@ -2685,7 +1532,6 @@ function createResponsesTerminalController(params) {
|
|
|
2685
1532
|
}
|
|
2686
1533
|
};
|
|
2687
1534
|
const finalizeResponse = (response, terminalEventType) => {
|
|
2688
|
-
params.markFinalized();
|
|
2689
1535
|
backfillReasoning(response.output ?? []);
|
|
2690
1536
|
finalizeTerminalFacts(response);
|
|
2691
1537
|
const terminal = resolveResponsesTerminalStopReason({
|
|
@@ -2700,7 +1546,8 @@ function createResponsesTerminalController(params) {
|
|
|
2700
1546
|
return {
|
|
2701
1547
|
finalizeResponse,
|
|
2702
1548
|
finalizeFailedResponse: finalizeTerminalFacts,
|
|
2703
|
-
recoverTerminalOutput
|
|
1549
|
+
recoverTerminalOutput,
|
|
1550
|
+
emitToolCallCompletion
|
|
2704
1551
|
};
|
|
2705
1552
|
}
|
|
2706
1553
|
//#endregion
|
|
@@ -2708,9 +1555,7 @@ function createResponsesTerminalController(params) {
|
|
|
2708
1555
|
async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
2709
1556
|
const streamingToolCalls = createResponsesToolCallTracker();
|
|
2710
1557
|
const outputSlots = createResponsesOutputSlotTracker();
|
|
2711
|
-
const
|
|
2712
|
-
const outputItemContentIndexes = createResponsesOutputContentIndex();
|
|
2713
|
-
const startedTextBlocksByItemId = /* @__PURE__ */ new Map();
|
|
1558
|
+
const outputs = createResponsesOutputTracker();
|
|
2714
1559
|
let terminalResponse;
|
|
2715
1560
|
let lastTextBlock = null;
|
|
2716
1561
|
const blocks = output.content;
|
|
@@ -2725,11 +1570,11 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2725
1570
|
type: "thinking",
|
|
2726
1571
|
item,
|
|
2727
1572
|
block,
|
|
2728
|
-
contentIndex: blocks.length
|
|
1573
|
+
contentIndex: blocks.length,
|
|
1574
|
+
outputIndex: readResponsesOutputIndex(event)
|
|
2729
1575
|
};
|
|
2730
1576
|
blocks.push(block);
|
|
2731
|
-
|
|
2732
|
-
outputItemContentIndexes.set(item, slot.contentIndex);
|
|
1577
|
+
outputs.set(item, slot.contentIndex, slot.outputIndex);
|
|
2733
1578
|
outputSlots.register(event, slot);
|
|
2734
1579
|
stream.push({
|
|
2735
1580
|
type: "thinking_start",
|
|
@@ -2751,17 +1596,13 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2751
1596
|
item: messageItem,
|
|
2752
1597
|
block,
|
|
2753
1598
|
contentIndex: block ? blocks.length : void 0,
|
|
1599
|
+
outputIndex: readResponsesOutputIndex(event),
|
|
2754
1600
|
pendingText: collapseCandidate ? "" : null,
|
|
2755
1601
|
collapseCandidate
|
|
2756
1602
|
};
|
|
2757
1603
|
if (block) {
|
|
2758
1604
|
blocks.push(block);
|
|
2759
|
-
|
|
2760
|
-
startedTextBlocksByItemId.set(messageItem.id, {
|
|
2761
|
-
block,
|
|
2762
|
-
index: slot.contentIndex ?? blocks.length - 1,
|
|
2763
|
-
phase: messageItem.phase ?? void 0
|
|
2764
|
-
});
|
|
1605
|
+
outputs.set(messageItem, slot.contentIndex ?? blocks.length - 1, slot.outputIndex);
|
|
2765
1606
|
}
|
|
2766
1607
|
outputSlots.register(event, slot);
|
|
2767
1608
|
if (slot.contentIndex !== void 0) stream.push({
|
|
@@ -2777,9 +1618,6 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2777
1618
|
if (item.type === "message") return outputSlots.resolve(event, "text");
|
|
2778
1619
|
return readResponsesOutputIndex(event) === void 0 ? void 0 : outputSlots.get(event);
|
|
2779
1620
|
};
|
|
2780
|
-
const getOrCreateOutputSlot = (event, item) => {
|
|
2781
|
-
return resolveOutputItemSlot(event, item) ?? createOutputSlot(event, item);
|
|
2782
|
-
};
|
|
2783
1621
|
const materializeDeferredTextSlot = (slot) => {
|
|
2784
1622
|
if (slot.block || slot.pendingText === null) return;
|
|
2785
1623
|
const text = slot.pendingText;
|
|
@@ -2790,12 +1628,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2790
1628
|
};
|
|
2791
1629
|
blocks.push(slot.block);
|
|
2792
1630
|
slot.contentIndex = blocks.length - 1;
|
|
2793
|
-
|
|
2794
|
-
startedTextBlocksByItemId.set(slot.item.id, {
|
|
2795
|
-
block: slot.block,
|
|
2796
|
-
index: slot.contentIndex,
|
|
2797
|
-
phase: slot.item.phase ?? void 0
|
|
2798
|
-
});
|
|
1631
|
+
outputs.set(slot.item, slot.contentIndex, slot.outputIndex);
|
|
2799
1632
|
stream.push({
|
|
2800
1633
|
type: "text_start",
|
|
2801
1634
|
contentIndex: slot.contentIndex,
|
|
@@ -2813,20 +1646,58 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2813
1646
|
const materializeDeferredTextSlots = (except) => {
|
|
2814
1647
|
for (const slot of outputSlots.values()) if (slot !== except && slot.type === "text") materializeDeferredTextSlot(slot);
|
|
2815
1648
|
};
|
|
2816
|
-
const
|
|
1649
|
+
const terminal = createResponsesTerminalController({
|
|
2817
1650
|
output,
|
|
2818
1651
|
stream,
|
|
2819
1652
|
model,
|
|
2820
1653
|
options,
|
|
2821
|
-
|
|
2822
|
-
startedTextBlocksByItemId,
|
|
2823
|
-
outputItemContentIndexes,
|
|
1654
|
+
outputs,
|
|
2824
1655
|
getLastTextBlock: () => lastTextBlock,
|
|
2825
1656
|
setLastTextBlock: (block) => {
|
|
2826
1657
|
lastTextBlock = block;
|
|
2827
|
-
}
|
|
2828
|
-
markFinalized: () => void 0
|
|
1658
|
+
}
|
|
2829
1659
|
});
|
|
1660
|
+
const finalizeToolCall = (item, outputIndex, streamingToolCall, validated) => {
|
|
1661
|
+
const identity = {
|
|
1662
|
+
type: item.type,
|
|
1663
|
+
id: item.id || streamingToolCall?.itemId,
|
|
1664
|
+
call_id: item.call_id || streamingToolCall?.callId
|
|
1665
|
+
};
|
|
1666
|
+
const finalOutputIndex = outputIndex ?? streamingToolCall?.outputIndex;
|
|
1667
|
+
if (finalOutputIndex === void 0 && !identity.id && !identity.call_id) {
|
|
1668
|
+
if (!streamingToolCall) throw new Error("Responses stream completed tool call without an output identity");
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
if (streamingToolCall) {
|
|
1672
|
+
streamingToolCalls.forget(streamingToolCall);
|
|
1673
|
+
for (const slot of outputSlots.values()) if (slot.type === "toolCall" && slot.toolCall === streamingToolCall) outputSlots.forget(slot);
|
|
1674
|
+
}
|
|
1675
|
+
terminal.emitToolCallCompletion(identity, finalOutputIndex, streamingToolCall, validated);
|
|
1676
|
+
};
|
|
1677
|
+
const prepareTerminalToolCalls = (items) => {
|
|
1678
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
1679
|
+
const recovered = [];
|
|
1680
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
1681
|
+
const allowUnmatchedIdentity = items.filter((item, index) => item.type === "function_call" && !outputs.get(item, index)?.completed).length === 1;
|
|
1682
|
+
for (const [outputIndex, item] of items.entries()) {
|
|
1683
|
+
const tracked = outputs.get(item, outputIndex);
|
|
1684
|
+
if (item.type !== "function_call") continue;
|
|
1685
|
+
if (item.call_id && callIds.has(item.call_id)) throw new Error("Responses stream repeated a terminal tool-call identity");
|
|
1686
|
+
if (item.call_id) callIds.add(item.call_id);
|
|
1687
|
+
if (tracked?.completed) continue;
|
|
1688
|
+
const state = streamingToolCalls.resolve({ output_index: outputIndex }, readResponsesToolCallItemIdentity(item), allowUnmatchedIdentity);
|
|
1689
|
+
if (tracked && !state) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1690
|
+
const validated = resolveCompletedResponsesToolCall(item, { name: state?.block.name });
|
|
1691
|
+
if (state) recovered.push(state);
|
|
1692
|
+
prepared.set(outputIndex, () => finalizeToolCall(item, outputIndex, state, validated));
|
|
1693
|
+
}
|
|
1694
|
+
if (!streamingToolCalls.hasExactlyActive(recovered)) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1695
|
+
return (outputIndex) => {
|
|
1696
|
+
const complete = prepared.get(outputIndex);
|
|
1697
|
+
if (!complete) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1698
|
+
complete();
|
|
1699
|
+
};
|
|
1700
|
+
};
|
|
2830
1701
|
const guardedStream = adaptResponsesStream(withFirstStreamEventTimeout(openaiStream, {
|
|
2831
1702
|
provider: model.provider,
|
|
2832
1703
|
api: model.api,
|
|
@@ -2860,6 +1731,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2860
1731
|
block: toolCallBlock,
|
|
2861
1732
|
contentIndex,
|
|
2862
1733
|
argumentStreamReliable: true,
|
|
1734
|
+
previewSchedule: createToolArgumentPreviewSchedule(),
|
|
2863
1735
|
...readResponsesToolCallItemIdentity(item)
|
|
2864
1736
|
};
|
|
2865
1737
|
streamingToolCalls.register(event, toolCallState);
|
|
@@ -2868,7 +1740,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2868
1740
|
toolCall: toolCallState
|
|
2869
1741
|
});
|
|
2870
1742
|
output.content.push(toolCallBlock);
|
|
2871
|
-
|
|
1743
|
+
outputs.set(item, contentIndex, readResponsesOutputIndex(event));
|
|
2872
1744
|
stream.push({
|
|
2873
1745
|
type: "toolcall_start",
|
|
2874
1746
|
contentIndex,
|
|
@@ -2994,7 +1866,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2994
1866
|
const toolCall = streamingToolCalls.resolve(event);
|
|
2995
1867
|
if (toolCall) {
|
|
2996
1868
|
toolCall.block.partialJson += event.delta;
|
|
2997
|
-
toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
1869
|
+
if (toolCall.previewSchedule(toolCall.block.partialJson.length)) toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
2998
1870
|
stream.push({
|
|
2999
1871
|
type: "toolcall_delta",
|
|
3000
1872
|
contentIndex: toolCall.contentIndex,
|
|
@@ -3027,13 +1899,14 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3027
1899
|
if (item.type !== "message") lastTextBlock = null;
|
|
3028
1900
|
const existingOutputSlot = resolveOutputItemSlot(event, item);
|
|
3029
1901
|
materializeDeferredTextSlots(existingOutputSlot);
|
|
3030
|
-
const outputSlot = existingOutputSlot ??
|
|
1902
|
+
const outputSlot = existingOutputSlot ?? createOutputSlot(event, item);
|
|
3031
1903
|
compactionTracker.completed(item, blocks.length);
|
|
3032
1904
|
if (item.type === "reasoning" && outputSlot?.type === "thinking") {
|
|
3033
1905
|
const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
|
|
3034
1906
|
const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
|
|
3035
1907
|
outputSlot.block.thinking = summaryText || contentText || outputSlot.block.thinking;
|
|
3036
1908
|
outputSlot.block.thinkingSignature = JSON.stringify(item);
|
|
1909
|
+
outputs.set(item, outputSlot.contentIndex, readResponsesOutputIndex(event) ?? outputSlot.outputIndex, true);
|
|
3037
1910
|
if (item.encrypted_content && options?.reasoningReplayMetadata) outputSlot.block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = options.reasoningReplayMetadata;
|
|
3038
1911
|
stream.push({
|
|
3039
1912
|
type: "thinking_end",
|
|
@@ -3065,7 +1938,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3065
1938
|
partial: output
|
|
3066
1939
|
});
|
|
3067
1940
|
lastTextBlock = outputSlot.collapseCandidate;
|
|
3068
|
-
|
|
1941
|
+
outputs.set(item, outputSlot.collapseCandidate.index, readResponsesOutputIndex(event) ?? outputSlot.outputIndex, true);
|
|
3069
1942
|
} else {
|
|
3070
1943
|
if (!outputSlot.block) {
|
|
3071
1944
|
outputSlot.block = {
|
|
@@ -3090,7 +1963,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3090
1963
|
index: contentIndex,
|
|
3091
1964
|
phase
|
|
3092
1965
|
};
|
|
3093
|
-
|
|
1966
|
+
outputs.set(item, contentIndex, readResponsesOutputIndex(event) ?? outputSlot.outputIndex, true);
|
|
3094
1967
|
stream.push({
|
|
3095
1968
|
type: "text_end",
|
|
3096
1969
|
contentIndex,
|
|
@@ -3099,66 +1972,33 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
3099
1972
|
});
|
|
3100
1973
|
}
|
|
3101
1974
|
outputSlots.forget(outputSlot);
|
|
3102
|
-
startedTextBlocksByItemId.delete(item.id);
|
|
3103
1975
|
} else if (item.type === "function_call") {
|
|
1976
|
+
if (outputs.get(item, readResponsesOutputIndex(event))?.completed) continue;
|
|
3104
1977
|
const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
|
|
3105
1978
|
if (!streamingToolCall && streamingToolCalls.hasActive()) continue;
|
|
3106
|
-
const streamedArguments = streamingToolCall?.block.partialJson ?? "";
|
|
3107
1979
|
const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
|
|
3108
1980
|
if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) continue;
|
|
3109
|
-
const finalArguments = completedArguments !== void 0 && (completedArguments.length > 0 || !streamedArguments) ? completedArguments : streamedArguments;
|
|
3110
1981
|
const validated = resolveCompletedResponsesToolCall(item, {
|
|
3111
1982
|
name: streamingToolCall?.block.name,
|
|
3112
|
-
arguments:
|
|
1983
|
+
arguments: completedArguments || streamingToolCall?.block.partialJson || ""
|
|
3113
1984
|
});
|
|
3114
|
-
|
|
3115
|
-
let contentIndex;
|
|
3116
|
-
if (streamingToolCall) {
|
|
3117
|
-
const block = streamingToolCall.block;
|
|
3118
|
-
block.id = resolveResponsesToolCallId(item, block.id);
|
|
3119
|
-
block.name = validated.name;
|
|
3120
|
-
block.arguments = validated.arguments;
|
|
3121
|
-
delete block.partialJson;
|
|
3122
|
-
toolCall = block;
|
|
3123
|
-
contentIndex = streamingToolCall.contentIndex;
|
|
3124
|
-
} else {
|
|
3125
|
-
toolCall = {
|
|
3126
|
-
type: "toolCall",
|
|
3127
|
-
id: resolveResponsesToolCallId(item),
|
|
3128
|
-
name: validated.name,
|
|
3129
|
-
arguments: validated.arguments
|
|
3130
|
-
};
|
|
3131
|
-
blocks.push(toolCall);
|
|
3132
|
-
contentIndex = blocks.length - 1;
|
|
3133
|
-
stream.push({
|
|
3134
|
-
type: "toolcall_start",
|
|
3135
|
-
contentIndex,
|
|
3136
|
-
partial: output
|
|
3137
|
-
});
|
|
3138
|
-
}
|
|
3139
|
-
if (streamingToolCall) {
|
|
3140
|
-
streamingToolCalls.forget(streamingToolCall);
|
|
3141
|
-
for (const slot of outputSlots.values()) if (slot.type === "toolCall" && slot.toolCall === streamingToolCall) outputSlots.forget(slot);
|
|
3142
|
-
}
|
|
3143
|
-
stream.push({
|
|
3144
|
-
type: "toolcall_end",
|
|
3145
|
-
contentIndex,
|
|
3146
|
-
toolCall,
|
|
3147
|
-
partial: output
|
|
3148
|
-
});
|
|
3149
|
-
outputItemContentIndexes.set(item, contentIndex);
|
|
1985
|
+
finalizeToolCall(item, readResponsesOutputIndex(event), streamingToolCall, validated);
|
|
3150
1986
|
}
|
|
3151
1987
|
} else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
3152
|
-
if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
3153
|
-
finalizeResponse(event.response, event.type);
|
|
3154
|
-
if (event.type === "response.completed" || output.stopReason === "length")
|
|
1988
|
+
if (event.type === "response.incomplete" && streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
1989
|
+
terminal.finalizeResponse(event.response, event.type);
|
|
1990
|
+
if (event.type === "response.completed" || output.stopReason === "length") {
|
|
1991
|
+
const items = event.response.output ?? [];
|
|
1992
|
+
const completeToolCall = event.type === "response.completed" ? prepareTerminalToolCalls(items) : void 0;
|
|
1993
|
+
terminal.recoverTerminalOutput(items, completeToolCall);
|
|
1994
|
+
}
|
|
3155
1995
|
terminalResponse = event.type === "response.completed" ? event.response : null;
|
|
3156
1996
|
if (output.stopReason === "stop" && output.content.some((block) => block.type === "toolCall")) output.stopReason = "toolUse";
|
|
3157
1997
|
break;
|
|
3158
1998
|
} else if (event.type === "error") throw new Error(event.message ? `Error Code ${event.code}: ${event.message}` : "Unknown error");
|
|
3159
1999
|
else if (event.type === "response.failed") {
|
|
3160
2000
|
const failure = normalizeResponsesFailedEvent(isRecord(event) ? event : {}, model);
|
|
3161
|
-
finalizeFailedResponse(event.response, failure.responseId);
|
|
2001
|
+
terminal.finalizeFailedResponse(event.response, failure.responseId);
|
|
3162
2002
|
throw new ResponsesStreamFailure(failure, event.response);
|
|
3163
2003
|
}
|
|
3164
2004
|
}
|
|
@@ -3312,26 +2152,34 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
3312
2152
|
return request;
|
|
3313
2153
|
};
|
|
3314
2154
|
const requestParams = await buildRequest("checkpoint");
|
|
3315
|
-
|
|
3316
|
-
|
|
2155
|
+
const firstEvent = createFirstStreamEventAbortController(options?.signal);
|
|
2156
|
+
firstEventAbort = firstEvent;
|
|
2157
|
+
let started = false;
|
|
2158
|
+
const { stream: hookedOpenAIStream } = await createResponsesStreamWithEncryptedContentRetry({
|
|
3317
2159
|
client,
|
|
3318
2160
|
request: requestParams,
|
|
3319
2161
|
requestOptions: {
|
|
3320
2162
|
...buildResponsesRequestOptions(options),
|
|
3321
|
-
signal:
|
|
2163
|
+
signal: firstEvent.signal
|
|
3322
2164
|
},
|
|
3323
2165
|
model,
|
|
3324
2166
|
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
3325
|
-
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, options, checkpoint)
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
2167
|
+
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, options, checkpoint),
|
|
2168
|
+
canRetryStream: () => output.content.length === 0,
|
|
2169
|
+
wrapStream: ({ stream: openaiStream, response }) => withProviderResponseHook({
|
|
2170
|
+
stream: openaiStream,
|
|
2171
|
+
signal: firstEvent.signal,
|
|
2172
|
+
abort: firstEvent.abort,
|
|
2173
|
+
hook: createOpenAIProviderAcceptanceHook(options, response, model),
|
|
2174
|
+
onReady: () => {
|
|
2175
|
+
if (!started) {
|
|
2176
|
+
started = true;
|
|
2177
|
+
stream.push({
|
|
2178
|
+
type: "start",
|
|
2179
|
+
partial: output
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
3335
2183
|
})
|
|
3336
2184
|
});
|
|
3337
2185
|
const firstEventTimeoutMs = getFirstStreamEventTimeoutMs(options);
|
|
@@ -3372,4 +2220,4 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
3372
2220
|
}
|
|
3373
2221
|
}
|
|
3374
2222
|
//#endregion
|
|
3375
|
-
export {
|
|
2223
|
+
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 };
|