@openclaw/ai 2026.7.2-beta.7 → 2026.7.33

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.
Files changed (78) hide show
  1. package/README.md +5 -6
  2. package/dist/{anthropic-CH4UUnZr.mjs → anthropic-BML72tLz.mjs} +468 -101
  3. package/dist/{api-registry-DlMgPR39.d.mts → api-registry-BXYnCOIR.d.mts} +2 -1
  4. package/dist/{azure-openai-responses-client-compat-C7K7QfUE.mjs → azure-openai-responses-client-compat-a_O_GVQV.mjs} +2 -23
  5. package/dist/{azure-openai-responses-CImcwB83.mjs → azure-openai-responses-kEN8II8I.mjs} +13 -11
  6. package/dist/{env-api-keys-DrgeBuva.mjs → env-api-keys-CtMlqaQ4.mjs} +4 -5
  7. package/dist/{event-stream-YjaPW20U.d.mts → event-stream-0nZeBKl2.d.mts} +1 -1
  8. package/dist/{event-stream-D8n2uFee.mjs → event-stream-ReMmOTzX.mjs} +6 -14
  9. package/dist/event-stream.d.mts +1 -1
  10. package/dist/event-stream.mjs +1 -1
  11. package/dist/{github-copilot-headers-NCJtz9i0.mjs → github-copilot-headers-BsH5cqGj.mjs} +12 -1
  12. package/dist/{google-CtSg0iTS.mjs → google-DTM4O3e9.mjs} +5 -5
  13. package/dist/{google-shared-DNBz5rcD.mjs → google-shared-BAP0BQVi.mjs} +22 -34
  14. package/dist/{google-vertex-31f1uS9L.mjs → google-vertex-D58MZHCQ.mjs} +19 -6
  15. package/dist/host-4t713IeR.mjs +37 -0
  16. package/dist/{anthropic-SrGtwsJu.d.mts → index-BoTnz8cv.d.mts} +1 -27
  17. package/dist/index.d.mts +50 -7
  18. package/dist/index.mjs +5 -5
  19. package/dist/internal/anthropic.d.mts +62 -22
  20. package/dist/internal/anthropic.mjs +4 -5
  21. package/dist/internal/openai.d.mts +82 -98
  22. package/dist/internal/openai.mjs +6 -8
  23. package/dist/internal/runtime.d.mts +34 -13
  24. package/dist/internal/runtime.mjs +14 -26
  25. package/dist/internal/shared.d.mts +9 -26
  26. package/dist/internal/shared.mjs +2 -6
  27. package/dist/{json-parse-BvXNt1-7.mjs → json-parse-DzNSIQBq.mjs} +6 -4
  28. package/dist/{llm-request-activity-BjtkplhG.mjs → llm-request-activity-CehVkZP-.mjs} +19 -1
  29. package/dist/{mistral-CWmpvWYh.mjs → mistral-CePVNdws.mjs} +39 -141
  30. package/dist/model-utils-DgmOla96.mjs +69 -0
  31. package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-BHqbcipc.mjs} +154 -101
  32. package/dist/{openai-completions-DsOxhOD1.mjs → openai-completions-9qYFVS7H.mjs} +295 -81
  33. package/dist/{openai-responses-BT7A3sLu.mjs → openai-responses-D6l9URyV.mjs} +8 -10
  34. package/dist/openai-responses-shared-L3PWZvkm.mjs +1949 -0
  35. package/dist/{openai-tool-projection-OhX64DoP.mjs → openai-tool-projection-BPlyXt8H.mjs} +12 -32
  36. package/dist/providers.d.mts +2 -3
  37. package/dist/providers.mjs +10 -11
  38. package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
  39. package/dist/{src-QkygScBs.mjs → src-CZ503MYJ.mjs} +5 -24
  40. package/dist/{stream-first-event-timeout-BBys9hSb.mjs → stream-first-event-timeout-RjWszj8c.mjs} +22 -2
  41. package/dist/transform-messages-BhGF_fF4.mjs +507 -0
  42. package/dist/{types-bzp5k29J.d.mts → types-DRgdPqaZ.d.mts} +0 -19
  43. package/dist/types.d.mts +5 -5
  44. package/dist/types.mjs +4 -4
  45. package/dist/{validation-B-j7cOYp.d.mts → validation-BDMWOr8d.d.mts} +1 -1
  46. package/dist/{validation-DAa_yFOM.mjs → validation-FrchoOlv.mjs} +7 -2
  47. package/dist/validation.d.mts +1 -1
  48. package/dist/validation.mjs +1 -1
  49. package/npm-shrinkwrap.json +645 -0
  50. package/package.json +11 -36
  51. package/dist/anthropic-usage-DWU-x8MI.mjs +0 -459
  52. package/dist/cache-retention-0x979a5V.mjs +0 -12
  53. package/dist/deferred-event-buffer-DAvyP7qA.mjs +0 -19
  54. package/dist/error-coercion-DgxlWC0n.mjs +0 -15
  55. package/dist/host-B9GUmcra.d.mts +0 -173
  56. package/dist/host-Dog2WQiR.mjs +0 -369
  57. package/dist/index-BVVgDSdq.d.mts +0 -1
  58. package/dist/internal/retry-after.d.mts +0 -5
  59. package/dist/internal/retry-after.mjs +0 -101
  60. package/dist/number-coercion-DvG7SNMg.mjs +0 -129
  61. package/dist/openai-completions-compat-DBWjXoMZ.d.mts +0 -43
  62. package/dist/openai-prompt-cache-2uo_1OR1.d.mts +0 -7
  63. package/dist/openai-prompt-cache-mZTCdRPo.mjs +0 -12
  64. package/dist/openai-reasoning-compat-YgeLncHw.mjs +0 -396
  65. package/dist/openai-responses-shared-pXl6Wd8S.mjs +0 -392
  66. package/dist/openai-responses-stream-internal-Cw5txaGW.mjs +0 -2964
  67. package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +0 -13
  68. package/dist/provider-error-CAEvRjry.mjs +0 -47
  69. package/dist/provider-options-D8bB3z9b.d.mts +0 -144
  70. package/dist/reasoning-tag-text-partitioner-CGDyLWUR.mjs +0 -12209
  71. package/dist/simple-options-9lhRrN73.mjs +0 -50
  72. package/dist/stream-first-event-timeout-DvDeSucC.d.mts +0 -29
  73. package/dist/tls-certificate-errors-DXSpluKI.mjs +0 -93
  74. package/dist/tool-result-text-CTpIRbYd.mjs +0 -225
  75. package/dist/transform-messages-C8mBqZxF.mjs +0 -2
  76. package/dist/transport-stream-shared-D81p90xq.mjs +0 -297
  77. package/dist/transports.d.mts +0 -573
  78. package/dist/transports.mjs +0 -4110
@@ -1,2964 +0,0 @@
1
- import { g as isRecord, m as normalizeOptionalString, n as getAiTransportHost, p as normalizeLowercaseStringOrEmpty } from "./host-Dog2WQiR.mjs";
2
- import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
3
- import { a as isImageWithMediaPayload, d as stripSystemPromptCacheBoundary, o as truncateUtf16Safe, r as extractToolResultText, t as describeToolResultMediaPlaceholder } from "./tool-result-text-CTpIRbYd.mjs";
4
- import { u as transformTransportMessages } from "./openai-tool-projection-OhX64DoP.mjs";
5
- import { c as calculateCost } from "./number-coercion-DvG7SNMg.mjs";
6
- import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
7
- import { b as redactIdentifier, d as transportAbortError, l as sanitizeNonEmptyTransportPayloadText, u as sanitizeTransportPayloadText, x as redactSensitiveText } from "./transport-stream-shared-D81p90xq.mjs";
8
- import { a as withFirstStreamEventTimeout } from "./stream-first-event-timeout-BBys9hSb.mjs";
9
- import { t as shortHash } from "./hash-CHgqbJmD.mjs";
10
- import { randomUUID } from "node:crypto";
11
- //#region packages/ai/src/transports/model-transport-debug.ts
12
- function normalizeEnv(value) {
13
- return typeof value === "string" ? value.trim().toLowerCase() : "";
14
- }
15
- function isTruthyEnv(value) {
16
- const normalized = normalizeEnv(value);
17
- return normalized.length > 0 && normalized !== "0" && normalized !== "false" && normalized !== "off" && normalized !== "no";
18
- }
19
- /** Resolves model payload debug verbosity from `OPENCLAW_DEBUG_MODEL_PAYLOAD`. */
20
- function resolveModelPayloadDebugMode(env = process.env) {
21
- const normalized = normalizeEnv(env.OPENCLAW_DEBUG_MODEL_PAYLOAD);
22
- if (normalized === "tools" || normalized === "full-redacted") return normalized;
23
- if (normalized === "summary") return "summary";
24
- return "off";
25
- }
26
- /** Resolves SSE stream debug verbosity from `OPENCLAW_DEBUG_SSE`. */
27
- function resolveModelSseDebugMode(env = process.env) {
28
- const normalized = normalizeEnv(env.OPENCLAW_DEBUG_SSE);
29
- if (normalized === "peek") return "peek";
30
- if (normalized === "events" || isTruthyEnv(normalized)) return "events";
31
- return "off";
32
- }
33
- /** Returns whether any model transport debug channel is enabled. */
34
- function isModelTransportDebugEnabled(env = process.env) {
35
- return isTruthyEnv(env.OPENCLAW_DEBUG_MODEL_TRANSPORT) || resolveModelPayloadDebugMode(env) !== "off" || resolveModelSseDebugMode(env) !== "off" || isTruthyEnv(env.OPENCLAW_DEBUG_CODE_MODE);
36
- }
37
- function isModelFetchMetadataMessage(message) {
38
- return message.startsWith("[model-fetch]");
39
- }
40
- /** Emits model-fetch metadata at info level by default; other diagnostics require debug env. */
41
- function emitModelTransportDebug(log, message) {
42
- if (isModelFetchMetadataMessage(message) || isModelTransportDebugEnabled()) {
43
- log.info(message);
44
- return;
45
- }
46
- log.debug(message);
47
- }
48
- //#endregion
49
- //#region packages/normalization-core/src/string-normalization.ts
50
- /** Coerces entries to strings, trims them, and drops empty results. */
51
- function normalizeStringEntries(list) {
52
- return (list ?? []).map((entry) => normalizeOptionalString(String(entry)) ?? "").filter(Boolean);
53
- }
54
- /** Returns first-seen unique values while preserving insertion order. */
55
- function uniqueValues(values) {
56
- return [...new Set(values)];
57
- }
58
- /** Returns first-seen unique strings while preserving insertion order. */
59
- function uniqueStrings(values) {
60
- return uniqueValues(values);
61
- }
62
- //#endregion
63
- //#region packages/ai/src/providers/openai-reasoning-effort.ts
64
- /**
65
- * OpenAI-compatible reasoning-effort normalization. Different GPT families
66
- * expose different accepted effort enums, so callers map requested values here
67
- * before constructing provider payloads.
68
- */
69
- const GPT_5_REASONING_EFFORTS = [
70
- "minimal",
71
- "low",
72
- "medium",
73
- "high"
74
- ];
75
- const GPT_51_REASONING_EFFORTS = [
76
- "none",
77
- "low",
78
- "medium",
79
- "high"
80
- ];
81
- const GPT_52_REASONING_EFFORTS = [
82
- "none",
83
- "low",
84
- "medium",
85
- "high",
86
- "xhigh"
87
- ];
88
- const GPT_56_REASONING_EFFORTS = [
89
- "none",
90
- "low",
91
- "medium",
92
- "high",
93
- "xhigh",
94
- "max"
95
- ];
96
- const GPT_CODEX_REASONING_EFFORTS = [
97
- "low",
98
- "medium",
99
- "high",
100
- "xhigh"
101
- ];
102
- const GPT_PRO_REASONING_EFFORTS = [
103
- "medium",
104
- "high",
105
- "xhigh"
106
- ];
107
- const GPT_5_PRO_REASONING_EFFORTS = ["high"];
108
- const GPT_51_CODEX_MAX_REASONING_EFFORTS = [
109
- "none",
110
- "medium",
111
- "high",
112
- "xhigh"
113
- ];
114
- const GPT_51_CODEX_MINI_REASONING_EFFORTS = ["medium"];
115
- const GENERIC_REASONING_EFFORTS = [
116
- "low",
117
- "medium",
118
- "high"
119
- ];
120
- const CANONICAL_REASONING_EFFORTS = /* @__PURE__ */ new Set([
121
- "none",
122
- "minimal",
123
- "low",
124
- "medium",
125
- "high",
126
- "xhigh",
127
- "max",
128
- "off"
129
- ]);
130
- function normalizeModelId(id) {
131
- return normalizeLowercaseStringOrEmpty(id ?? "").replace(/-\d{4}-\d{2}-\d{2}$/u, "");
132
- }
133
- /** Return whether a model is the GPT-5.4 mini family. */
134
- function isOpenAIGpt54MiniModel(model) {
135
- const id = normalizeModelId(typeof model.id === "string" ? model.id : void 0);
136
- return /^gpt-5\.4-mini(?:-|$)/u.test(id);
137
- }
138
- /** Return whether a model is the GPT-5.5 family. */
139
- function isOpenAIGpt55Model(model) {
140
- const id = normalizeModelId(typeof model.id === "string" ? model.id : void 0);
141
- const name = normalizeModelId(typeof model.name === "string" ? model.name : void 0);
142
- return /^gpt-5\.5(?:-|$)/u.test(id) || /^gpt-5\.5(?:\s|\(|-|$)/u.test(name);
143
- }
144
- /** Return whether a model is the GPT-5.6 family. */
145
- function isOpenAIGpt56Model(model) {
146
- const id = normalizeModelId(typeof model.id === "string" ? model.id : void 0);
147
- const name = normalizeModelId(typeof model.name === "string" ? model.name : void 0);
148
- return /^gpt-5\.6(?:-|$)/u.test(id) || /^gpt-5\.6(?:\s|\(|-|$)/u.test(name);
149
- }
150
- /** Normalize user-facing reasoning effort names to API effort names. */
151
- function normalizeOpenAIReasoningEffort(effort) {
152
- const trimmed = effort.trim();
153
- const folded = trimmed.toLowerCase();
154
- return CANONICAL_REASONING_EFFORTS.has(folded) ? folded : trimmed;
155
- }
156
- function readCompatReasoningEfforts(compat) {
157
- if (!compat || typeof compat !== "object") return;
158
- if (compat.supportsReasoningEffort === false) return [];
159
- const raw = compat.supportedReasoningEfforts;
160
- if (!Array.isArray(raw)) return;
161
- const supported = uniqueStrings(normalizeStringEntries(raw.filter((value) => typeof value === "string")));
162
- return supported.length > 0 ? supported : void 0;
163
- }
164
- function isDisabledReasoningEffort(effort) {
165
- return effort === "none" || effort === "off";
166
- }
167
- /** Resolve the reasoning efforts accepted by a specific OpenAI-compatible model. */
168
- function resolveOpenAISupportedReasoningEfforts(model) {
169
- const compatEfforts = readCompatReasoningEfforts(model.compat);
170
- if (compatEfforts) return compatEfforts;
171
- const id = normalizeModelId(typeof model.id === "string" ? model.id : void 0);
172
- if (/^gpt-5\.6(?:-|$)/u.test(id)) return GPT_56_REASONING_EFFORTS;
173
- if (id === "gpt-5.1-codex-mini") return GPT_51_CODEX_MINI_REASONING_EFFORTS;
174
- if (id === "gpt-5.1-codex-max") return GPT_51_CODEX_MAX_REASONING_EFFORTS;
175
- if (/^gpt-5(?:\.\d+)?-codex(?:-|$)/u.test(id)) return GPT_CODEX_REASONING_EFFORTS;
176
- if (id === "gpt-5-pro") return GPT_5_PRO_REASONING_EFFORTS;
177
- if (/^gpt-5\.[2-9](?:\.\d+)?-pro(?:-|$)/u.test(id)) return GPT_PRO_REASONING_EFFORTS;
178
- if (/^gpt-5\.[2-9](?:\.\d+)?(?:-|$)/u.test(id)) return GPT_52_REASONING_EFFORTS;
179
- if (/^gpt-5\.1(?:-|$)/u.test(id)) return GPT_51_REASONING_EFFORTS;
180
- if (/^gpt-5(?:-|$)/u.test(id)) return GPT_5_REASONING_EFFORTS;
181
- return GENERIC_REASONING_EFFORTS;
182
- }
183
- /**
184
- * Return whether a model accepts the temperature parameter. The GPT-5.6
185
- * family rejects it with a 400; catalog compat can override per model.
186
- */
187
- function supportsOpenAITemperature(model) {
188
- const compat = model.compat;
189
- if (compat && typeof compat === "object") {
190
- const declared = compat.supportsTemperature;
191
- if (typeof declared === "boolean") return declared;
192
- }
193
- const id = normalizeModelId(typeof model.id === "string" ? model.id : void 0);
194
- return !/^gpt-5\.6(?:-|$)/u.test(id);
195
- }
196
- /** Return whether a model accepts a requested reasoning effort. */
197
- function supportsOpenAIReasoningEffort(model, effort) {
198
- return resolveOpenAISupportedReasoningEfforts(model).includes(normalizeOpenAIReasoningEffort(effort));
199
- }
200
- /** Resolve a requested reasoning effort to the closest value supported by the model. */
201
- function resolveOpenAIReasoningEffortForModel(params) {
202
- const requested = normalizeOpenAIReasoningEffort(params.effort);
203
- const mapped = params.fallbackMap?.[requested] ?? (params.fallbackMap && CANONICAL_REASONING_EFFORTS.has(requested) ? Object.entries(params.fallbackMap).find(([effort]) => normalizeOpenAIReasoningEffort(effort) === requested)?.[1] : void 0);
204
- const normalized = mapped === void 0 ? requested : mapped.trim();
205
- const supported = resolveOpenAISupportedReasoningEfforts(params.model);
206
- if (supported.includes(normalized)) return normalized;
207
- if (requested === "off" && supported.includes("none")) return "none";
208
- if (isDisabledReasoningEffort(requested) || isDisabledReasoningEffort(normalized)) return;
209
- if (requested === "minimal" && supported.includes("low")) return "low";
210
- if ((requested === "minimal" || requested === "low") && supported.includes("medium")) return "medium";
211
- if (requested === "xhigh" && supported.includes("high")) return "high";
212
- if (requested === "max" && supported.includes("xhigh")) return "xhigh";
213
- return supported.find((effort) => !isDisabledReasoningEffort(normalizeOpenAIReasoningEffort(effort)));
214
- }
215
- //#endregion
216
- //#region packages/ai/src/providers/clean-for-gemini.ts
217
- const GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
218
- "patternProperties",
219
- "additionalProperties",
220
- "$schema",
221
- "$id",
222
- "$ref",
223
- "$defs",
224
- "definitions",
225
- "examples",
226
- "minLength",
227
- "maxLength",
228
- "minimum",
229
- "maximum",
230
- "multipleOf",
231
- "pattern",
232
- "format",
233
- "minItems",
234
- "maxItems",
235
- "uniqueItems",
236
- "minProperties",
237
- "maxProperties",
238
- "not"
239
- ]);
240
- const SCHEMA_META_KEYS = [
241
- "description",
242
- "title",
243
- "default"
244
- ];
245
- function copySchemaMeta$1(from, to) {
246
- for (const key of SCHEMA_META_KEYS) if (key in from && from[key] !== void 0) to[key] = from[key];
247
- }
248
- function stringifyGeminiEnumValue(value) {
249
- if (typeof value === "string") return value;
250
- if (typeof value === "number" && Number.isFinite(value)) return String(value);
251
- if (typeof value === "boolean") return String(value);
252
- }
253
- function cleanGeminiEnumValues(value) {
254
- if (!Array.isArray(value)) return;
255
- const values = value.flatMap((entry) => {
256
- const stringified = stringifyGeminiEnumValue(entry);
257
- return stringified === void 0 ? [] : [stringified];
258
- });
259
- const unique = [...new Set(values)];
260
- return unique.length > 0 ? unique : void 0;
261
- }
262
- function tryFlattenLiteralAnyOf(variants) {
263
- if (variants.length === 0) return null;
264
- const allValues = [];
265
- let commonType = null;
266
- for (const variant of variants) {
267
- if (!variant || typeof variant !== "object") return null;
268
- const v = variant;
269
- let literalValue;
270
- if ("const" in v) literalValue = v.const;
271
- else if (Array.isArray(v.enum) && v.enum.length === 1) literalValue = v.enum[0];
272
- else return null;
273
- const variantType = typeof v.type === "string" ? v.type : null;
274
- if (!variantType) return null;
275
- if (commonType === null) commonType = variantType;
276
- else if (commonType !== variantType) return null;
277
- allValues.push(literalValue);
278
- }
279
- if (commonType && allValues.length > 0) return {
280
- type: commonType,
281
- enum: allValues
282
- };
283
- return null;
284
- }
285
- function isNullSchema(variant) {
286
- if (!variant || typeof variant !== "object" || Array.isArray(variant)) return false;
287
- const record = variant;
288
- if ("const" in record && record.const === null) return true;
289
- if (Array.isArray(record.enum) && record.enum.length === 1) return record.enum[0] === null;
290
- const typeValue = record.type;
291
- if (typeValue === "null") return true;
292
- if (Array.isArray(typeValue) && typeValue.length === 1 && typeValue[0] === "null") return true;
293
- return false;
294
- }
295
- function stripNullVariants(variants) {
296
- if (variants.length === 0) return {
297
- variants,
298
- stripped: false
299
- };
300
- const nonNull = variants.filter((variant) => !isNullSchema(variant));
301
- return {
302
- variants: nonNull,
303
- stripped: nonNull.length !== variants.length
304
- };
305
- }
306
- function extendSchemaDefs$1(defs, schema) {
307
- const defsEntry = schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs) ? schema.$defs : void 0;
308
- const legacyDefsEntry = schema.definitions && typeof schema.definitions === "object" && !Array.isArray(schema.definitions) ? schema.definitions : void 0;
309
- if (!defsEntry && !legacyDefsEntry) return defs;
310
- const next = defs ? new Map(defs) : /* @__PURE__ */ new Map();
311
- if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.set(key, value);
312
- if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.set(key, value);
313
- return next;
314
- }
315
- function decodeJsonPointerSegment$1(segment) {
316
- return segment.replaceAll("~1", "/").replaceAll("~0", "~");
317
- }
318
- function tryResolveLocalRef$1(ref, defs) {
319
- if (!defs) return;
320
- const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
321
- if (!match) return;
322
- const name = decodeJsonPointerSegment$1(match[1] ?? "");
323
- if (!name) return;
324
- return defs.get(name);
325
- }
326
- function simplifyUnionVariants(params) {
327
- const { obj, variants } = params;
328
- const { variants: nonNullVariants, stripped } = stripNullVariants(variants);
329
- const flattened = tryFlattenLiteralAnyOf(nonNullVariants);
330
- if (flattened) {
331
- const result = {
332
- type: flattened.type,
333
- enum: flattened.enum
334
- };
335
- copySchemaMeta$1(obj, result);
336
- return {
337
- variants: nonNullVariants,
338
- simplified: result
339
- };
340
- }
341
- if (stripped && nonNullVariants.length === 1) {
342
- const lone = nonNullVariants[0];
343
- if (lone && typeof lone === "object" && !Array.isArray(lone)) {
344
- const result = { ...lone };
345
- copySchemaMeta$1(obj, result);
346
- return {
347
- variants: nonNullVariants,
348
- simplified: result
349
- };
350
- }
351
- return {
352
- variants: nonNullVariants,
353
- simplified: lone
354
- };
355
- }
356
- return { variants: stripped ? nonNullVariants : variants };
357
- }
358
- function sanitizeRequiredFields(schema) {
359
- if (!Array.isArray(schema.required)) return schema;
360
- if (!schema.properties || typeof schema.properties !== "object" || Array.isArray(schema.properties)) {
361
- if (schema.type === "object") delete schema.required;
362
- return schema;
363
- }
364
- const properties = schema.properties;
365
- const required = schema.required.filter((key) => typeof key === "string" && Object.hasOwn(properties, key));
366
- if (required.length > 0) schema.required = required;
367
- else delete schema.required;
368
- return schema;
369
- }
370
- function cleanSchemaForGeminiWithDefs(schema, defs, refStack) {
371
- if (!schema || typeof schema !== "object") return schema;
372
- if (Array.isArray(schema)) return schema.map((item) => cleanSchemaForGeminiWithDefs(item, defs, refStack));
373
- const obj = schema;
374
- const nextDefs = extendSchemaDefs$1(defs, obj);
375
- const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
376
- if (refValue) {
377
- if (refStack?.has(refValue)) return {};
378
- const resolved = tryResolveLocalRef$1(refValue, nextDefs);
379
- if (resolved) {
380
- const nextRefStack = refStack ? new Set(refStack) : /* @__PURE__ */ new Set();
381
- nextRefStack.add(refValue);
382
- const cleaned = cleanSchemaForGeminiWithDefs(resolved, nextDefs, nextRefStack);
383
- if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) return cleaned;
384
- const result = { ...cleaned };
385
- copySchemaMeta$1(obj, result);
386
- return result;
387
- }
388
- const result = {};
389
- copySchemaMeta$1(obj, result);
390
- return result;
391
- }
392
- const hasAnyOf = "anyOf" in obj && Array.isArray(obj.anyOf);
393
- const hasOneOf = "oneOf" in obj && Array.isArray(obj.oneOf);
394
- let cleanedAnyOf = hasAnyOf ? obj.anyOf.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack)) : void 0;
395
- let cleanedOneOf = hasOneOf ? obj.oneOf.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack)) : void 0;
396
- if (hasAnyOf) {
397
- const simplified = simplifyUnionVariants({
398
- obj,
399
- variants: cleanedAnyOf ?? []
400
- });
401
- cleanedAnyOf = simplified.variants;
402
- if ("simplified" in simplified) return simplified.simplified;
403
- }
404
- if (hasOneOf) {
405
- const simplified = simplifyUnionVariants({
406
- obj,
407
- variants: cleanedOneOf ?? []
408
- });
409
- cleanedOneOf = simplified.variants;
410
- if ("simplified" in simplified) return simplified.simplified;
411
- }
412
- const cleaned = {};
413
- for (const [key, value] of Object.entries(obj)) {
414
- if (GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS.has(key)) continue;
415
- if (key === "const") {
416
- const enumValues = cleanGeminiEnumValues([value]);
417
- if (enumValues) cleaned.enum = enumValues;
418
- continue;
419
- }
420
- if (key === "enum") {
421
- const enumValues = cleanGeminiEnumValues(value);
422
- if (enumValues) cleaned.enum = enumValues;
423
- continue;
424
- }
425
- if (key === "required" && Array.isArray(value) && value.length === 0) continue;
426
- if (key === "type" && (hasAnyOf || hasOneOf)) continue;
427
- if (key === "type" && Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
428
- const types = value.filter((entry) => entry !== "null");
429
- cleaned.type = types.length === 1 ? types[0] : types;
430
- continue;
431
- }
432
- 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)]));
433
- else cleaned[key] = {};
434
- else if (key === "items" && value) if (Array.isArray(value)) cleaned[key] = value.map((entry) => cleanSchemaForGeminiWithDefs(entry, nextDefs, refStack));
435
- else if (typeof value === "object") cleaned[key] = cleanSchemaForGeminiWithDefs(value, nextDefs, refStack);
436
- else cleaned[key] = value;
437
- else if (key === "anyOf" && Array.isArray(value)) cleaned[key] = cleanedAnyOf ?? value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
438
- else if (key === "oneOf" && Array.isArray(value)) cleaned[key] = cleanedOneOf ?? value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
439
- else if (key === "allOf" && Array.isArray(value)) cleaned[key] = value.map((variant) => cleanSchemaForGeminiWithDefs(variant, nextDefs, refStack));
440
- else cleaned[key] = value;
441
- }
442
- if (cleaned.anyOf && Array.isArray(cleaned.anyOf)) {
443
- const flattened = flattenUnionFallback(cleaned, cleaned.anyOf);
444
- if (flattened) return sanitizeRequiredFields(flattened);
445
- }
446
- if (cleaned.oneOf && Array.isArray(cleaned.oneOf)) {
447
- const flattened = flattenUnionFallback(cleaned, cleaned.oneOf);
448
- if (flattened) return sanitizeRequiredFields(flattened);
449
- }
450
- return sanitizeRequiredFields(cleaned);
451
- }
452
- /**
453
- * Last-resort flattening for anyOf/oneOf arrays that could not be simplified
454
- * by `simplifyUnionVariants`. Picks a representative type so the schema is
455
- * accepted by Google's restricted JSON Schema validation.
456
- */
457
- function flattenUnionFallback(obj, variants) {
458
- const objects = variants.filter((v) => Boolean(v) && typeof v === "object");
459
- if (objects.length === 0) return;
460
- const types = new Set(objects.map((v) => v.type).filter(Boolean));
461
- if (objects.length === 1) {
462
- const merged = { ...objects[0] };
463
- copySchemaMeta$1(obj, merged);
464
- return merged;
465
- }
466
- if (types.size === 1) {
467
- const merged = { type: Array.from(types)[0] };
468
- copySchemaMeta$1(obj, merged);
469
- return merged;
470
- }
471
- const first = objects[0];
472
- if (first?.type) {
473
- const merged = { type: first.type };
474
- copySchemaMeta$1(obj, merged);
475
- return merged;
476
- }
477
- const merged = {};
478
- copySchemaMeta$1(obj, merged);
479
- return merged;
480
- }
481
- function cleanSchemaForGemini(schema) {
482
- if (!schema || typeof schema !== "object") return schema;
483
- if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);
484
- return cleanSchemaForGeminiWithDefs(schema, extendSchemaDefs$1(void 0, schema), void 0);
485
- }
486
- //#endregion
487
- //#region packages/ai/src/providers/clean-for-llamacpp-gbnf.ts
488
- /** llama.cpp rejects grammar repetitions whose expanded rule count reaches 2000. */
489
- const LLAMACPP_GBNF_MAX_REPETITION_THRESHOLD = 2e3;
490
- const SCHEMA_MAP_KEYS$2 = /* @__PURE__ */ new Set([
491
- "$defs",
492
- "definitions",
493
- "dependentSchemas",
494
- "patternProperties",
495
- "properties"
496
- ]);
497
- const SCHEMA_CHILD_KEYS = /* @__PURE__ */ new Set([
498
- "additionalItems",
499
- "additionalProperties",
500
- "allOf",
501
- "anyOf",
502
- "contains",
503
- "else",
504
- "if",
505
- "items",
506
- "not",
507
- "oneOf",
508
- "prefixItems",
509
- "propertyNames",
510
- "then",
511
- "unevaluatedItems",
512
- "unevaluatedProperties"
513
- ]);
514
- function isSchemaRecord(value) {
515
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
516
- }
517
- function cleanSchemaNode(node) {
518
- if (Array.isArray(node)) {
519
- let changed = false;
520
- const entries = node.map((entry) => {
521
- const next = cleanSchemaNode(entry);
522
- changed ||= next !== entry;
523
- return next;
524
- });
525
- return changed ? entries : node;
526
- }
527
- if (!isSchemaRecord(node)) return node;
528
- let changed = false;
529
- const cleaned = {};
530
- for (const [key, value] of Object.entries(node)) {
531
- if (key === "pattern") {
532
- changed = true;
533
- continue;
534
- }
535
- if (key === "maxLength" && typeof value === "number" && value >= 2e3) {
536
- changed = true;
537
- continue;
538
- }
539
- let next = value;
540
- if (SCHEMA_MAP_KEYS$2.has(key) && isSchemaRecord(value)) {
541
- let mapChanged = false;
542
- next = Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => {
543
- const cleanedChild = cleanSchemaNode(childValue);
544
- mapChanged ||= cleanedChild !== childValue;
545
- return [childKey, cleanedChild];
546
- }));
547
- if (!mapChanged) next = value;
548
- } else if (SCHEMA_CHILD_KEYS.has(key)) next = cleanSchemaNode(value);
549
- cleaned[key] = next;
550
- changed ||= next !== value;
551
- }
552
- return changed ? cleaned : node;
553
- }
554
- function collectSchemaViolations(node, path, violations) {
555
- if (Array.isArray(node)) {
556
- node.forEach((entry, index) => collectSchemaViolations(entry, `${path}[${index}]`, violations));
557
- return;
558
- }
559
- if (!isSchemaRecord(node)) return;
560
- if ("pattern" in node) violations.push(`${path}.pattern`);
561
- if (typeof node.maxLength === "number" && node.maxLength >= 2e3) violations.push(`${path}.maxLength`);
562
- for (const [key, value] of Object.entries(node)) if (SCHEMA_MAP_KEYS$2.has(key) && isSchemaRecord(value)) for (const [childKey, childValue] of Object.entries(value)) collectSchemaViolations(childValue, `${path}.${key}.${childKey}`, violations);
563
- else if (SCHEMA_CHILD_KEYS.has(key)) collectSchemaViolations(value, `${path}.${key}`, violations);
564
- }
565
- /** Removes JSON Schema constraints that llama.cpp cannot compile into GBNF. */
566
- function cleanSchemaForLlamacppGbnf(schema) {
567
- return cleanSchemaNode(schema);
568
- }
569
- /** Reports schema paths that llama.cpp cannot compile into GBNF. */
570
- function findLlamacppGbnfSchemaViolations(schema, path) {
571
- const violations = [];
572
- collectSchemaViolations(schema, path, violations);
573
- return violations;
574
- }
575
- //#endregion
576
- //#region packages/ai/src/providers/schema-keyword-strip.ts
577
- const SCHEMA_MAP_KEYS$1 = /* @__PURE__ */ new Set([
578
- "$defs",
579
- "definitions",
580
- "dependentSchemas",
581
- "dependencies",
582
- "patternProperties",
583
- "properties"
584
- ]);
585
- /** Containers whose value is a single nested schema. */
586
- const SCHEMA_OBJECT_KEYS$1 = /* @__PURE__ */ new Set([
587
- "additionalItems",
588
- "additionalProperties",
589
- "contains",
590
- "contentSchema",
591
- "else",
592
- "if",
593
- "items",
594
- "not",
595
- "propertyNames",
596
- "then",
597
- "unevaluatedItems",
598
- "unevaluatedProperties"
599
- ]);
600
- /** Containers whose value is a list of nested schemas. */
601
- const SCHEMA_ARRAY_KEYS$1 = /* @__PURE__ */ new Set([
602
- "allOf",
603
- "anyOf",
604
- "items",
605
- "oneOf",
606
- "prefixItems"
607
- ]);
608
- /** Recursively remove schema keywords unsupported by a target provider/tool surface. */
609
- function stripUnsupportedSchemaKeywords(schema, unsupportedKeywords) {
610
- if (!schema || typeof schema !== "object") return schema;
611
- if (Array.isArray(schema)) return schema.map((entry) => stripUnsupportedSchemaKeywords(entry, unsupportedKeywords));
612
- const obj = schema;
613
- const cleaned = {};
614
- for (const [key, value] of Object.entries(obj)) {
615
- if (unsupportedKeywords.has(key)) continue;
616
- if (SCHEMA_MAP_KEYS$1.has(key) && value && typeof value === "object" && !Array.isArray(value)) {
617
- cleaned[key] = Object.fromEntries(Object.entries(value).map(([childKey, childValue]) => [childKey, stripUnsupportedSchemaKeywords(childValue, unsupportedKeywords)]));
618
- continue;
619
- }
620
- if (SCHEMA_ARRAY_KEYS$1.has(key) && Array.isArray(value)) {
621
- cleaned[key] = value.map((entry) => stripUnsupportedSchemaKeywords(entry, unsupportedKeywords));
622
- continue;
623
- }
624
- if (SCHEMA_OBJECT_KEYS$1.has(key) && value && typeof value === "object") {
625
- cleaned[key] = stripUnsupportedSchemaKeywords(value, unsupportedKeywords);
626
- continue;
627
- }
628
- cleaned[key] = value;
629
- }
630
- return cleaned;
631
- }
632
- //#endregion
633
- //#region packages/ai/src/providers/agent-tools-parameter-schema.ts
634
- /**
635
- * Normalizes model-facing tool parameter schemas across provider quirks.
636
- * Handles local JSON Schema refs, OpenAPI nullable syntax, top-level unions,
637
- * and provider-specific unsupported keyword stripping.
638
- */
639
- /** Extracts the compat record whether callers pass a model (`{ compat }`) or the compat itself. */
640
- function extractToolSchemaModelCompat(modelOrCompat) {
641
- if (!modelOrCompat || typeof modelOrCompat !== "object") return;
642
- if ("compat" in modelOrCompat) {
643
- const compat = modelOrCompat.compat;
644
- return compat && typeof compat === "object" ? compat : void 0;
645
- }
646
- return modelOrCompat;
647
- }
648
- /** JSON Schema keywords this model/provider rejects in tool schemas. */
649
- function resolveUnsupportedToolSchemaKeywords(modelOrCompat) {
650
- const keywords = extractToolSchemaModelCompat(modelOrCompat)?.unsupportedToolSchemaKeywords ?? [];
651
- return new Set(normalizeStringEntries(keywords.filter((keyword) => typeof keyword === "string")));
652
- }
653
- /** Whether empty `items: {}` on array schemas must be omitted for this model/provider. */
654
- function shouldOmitEmptyArrayItems(modelOrCompat) {
655
- return extractToolSchemaModelCompat(modelOrCompat)?.omitEmptyArrayItems === true;
656
- }
657
- const MAX_TOOL_PARAMETER_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
658
- const toolParameterSchemaCache = /* @__PURE__ */ new WeakMap();
659
- function resolveToolParameterSchemaCacheKey(options) {
660
- const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
661
- const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
662
- const toolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
663
- const unsupportedKeywords = Array.from(resolveUnsupportedToolSchemaKeywords(options?.modelCompat)).toSorted();
664
- const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
665
- return JSON.stringify([
666
- normalizedProvider,
667
- normalizedModelId,
668
- toolSchemaProfile,
669
- unsupportedKeywords,
670
- omitEmptyArrayItems
671
- ]);
672
- }
673
- function getCachedToolParameterSchema(schema, key) {
674
- return toolParameterSchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
675
- }
676
- function rememberCachedToolParameterSchema(schema, key, value) {
677
- const entries = toolParameterSchemaCache.get(schema) ?? [];
678
- toolParameterSchemaCache.set(schema, [{
679
- key,
680
- value
681
- }, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_TOOL_PARAMETER_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
682
- return value;
683
- }
684
- function isGeminiModelId(modelId) {
685
- return /(?:^|[/:])gemini(?:$|[-/:.])/.test(modelId);
686
- }
687
- function extractEnumValues(schema) {
688
- if (!schema || typeof schema !== "object") return;
689
- const record = schema;
690
- if (Array.isArray(record.enum)) return record.enum;
691
- if ("const" in record) return [record.const];
692
- const variants = Array.isArray(record.anyOf) ? record.anyOf : Array.isArray(record.oneOf) ? record.oneOf : null;
693
- if (variants) {
694
- const values = variants.flatMap((variant) => {
695
- return extractEnumValues(variant) ?? [];
696
- });
697
- return values.length > 0 ? values : void 0;
698
- }
699
- }
700
- function mergePropertySchemas(existing, incoming) {
701
- if (!existing) return incoming;
702
- if (!incoming) return existing;
703
- const existingEnum = extractEnumValues(existing);
704
- const incomingEnum = extractEnumValues(incoming);
705
- if (existingEnum || incomingEnum) {
706
- const values = uniqueValues([...existingEnum ?? [], ...incomingEnum ?? []]);
707
- const merged = {};
708
- for (const source of [existing, incoming]) {
709
- if (!source || typeof source !== "object") continue;
710
- const record = source;
711
- for (const key of [
712
- "title",
713
- "description",
714
- "default"
715
- ]) if (!(key in merged) && key in record) merged[key] = record[key];
716
- }
717
- const types = new Set(values.map((value) => typeof value));
718
- if (types.size === 1) merged.type = Array.from(types)[0];
719
- merged.enum = values;
720
- return merged;
721
- }
722
- return existing;
723
- }
724
- function setOwnSchemaProperty(target, key, value) {
725
- Object.defineProperty(target, key, {
726
- value,
727
- enumerable: true,
728
- configurable: true,
729
- writable: true
730
- });
731
- }
732
- function hasTopLevelArrayKeyword(schemaRecord, key) {
733
- return Array.isArray(schemaRecord[key]);
734
- }
735
- function getFlattenableVariantKey(schemaRecord) {
736
- if (hasTopLevelArrayKeyword(schemaRecord, "anyOf")) return "anyOf";
737
- if (hasTopLevelArrayKeyword(schemaRecord, "oneOf")) return "oneOf";
738
- return null;
739
- }
740
- function getTopLevelConditionalKey(schemaRecord) {
741
- return getFlattenableVariantKey(schemaRecord) ?? (hasTopLevelArrayKeyword(schemaRecord, "allOf") ? "allOf" : null);
742
- }
743
- function hasTopLevelObjectSchema(schemaRecord, conditionalKey) {
744
- return schemaRecord.type === "object" && isRecord(schemaRecord.properties) && conditionalKey === null;
745
- }
746
- function isObjectLikeSchemaMissingType(schemaRecord, conditionalKey) {
747
- return !("type" in schemaRecord) && (isRecord(schemaRecord.properties) || Array.isArray(schemaRecord.required)) && conditionalKey === null;
748
- }
749
- function isTypedObjectSchemaMissingValidProperties(schemaRecord, conditionalKey) {
750
- return schemaRecord.type === "object" && !isRecord(schemaRecord.properties) && conditionalKey === null;
751
- }
752
- function isTrulyEmptySchema(schemaRecord) {
753
- return Object.keys(schemaRecord).length === 0;
754
- }
755
- function normalizeArraySchemasMissingItems(schema) {
756
- if (!isRecord(schema)) return schema;
757
- let changed = false;
758
- const nextSchema = { ...schema };
759
- if (nextSchema.type === "array" && nextSchema.items === void 0) {
760
- nextSchema.items = {};
761
- changed = true;
762
- }
763
- const normalizeSchemaValue = (key) => {
764
- if (!(key in nextSchema)) return;
765
- const value = nextSchema[key];
766
- if (Array.isArray(value)) {
767
- const normalized = value.map(normalizeArraySchemasMissingItems);
768
- if (normalized.some((entry, index) => entry !== value[index])) {
769
- nextSchema[key] = normalized;
770
- changed = true;
771
- }
772
- return;
773
- }
774
- const normalized = normalizeArraySchemasMissingItems(value);
775
- if (normalized !== value) {
776
- nextSchema[key] = normalized;
777
- changed = true;
778
- }
779
- };
780
- for (const key of [
781
- "items",
782
- "contains",
783
- "additionalProperties",
784
- "propertyNames",
785
- "not",
786
- "if",
787
- "then",
788
- "else"
789
- ]) normalizeSchemaValue(key);
790
- for (const key of [
791
- "anyOf",
792
- "oneOf",
793
- "allOf",
794
- "prefixItems"
795
- ]) normalizeSchemaValue(key);
796
- for (const key of [
797
- "properties",
798
- "patternProperties",
799
- "dependentSchemas",
800
- "$defs",
801
- "definitions"
802
- ]) {
803
- const value = nextSchema[key];
804
- if (!isRecord(value)) continue;
805
- let entriesChanged = false;
806
- const normalizedEntries = Object.entries(value).map(([entryKey, entryValue]) => {
807
- const normalizedEntryValue = normalizeArraySchemasMissingItems(entryValue);
808
- if (normalizedEntryValue !== entryValue) entriesChanged = true;
809
- return [entryKey, normalizedEntryValue];
810
- });
811
- if (entriesChanged) {
812
- nextSchema[key] = Object.fromEntries(normalizedEntries);
813
- changed = true;
814
- }
815
- }
816
- return changed ? nextSchema : schema;
817
- }
818
- function schemaAllowsArrayType(schema) {
819
- const type = schema.type;
820
- return type === "array" || Array.isArray(type) && type.includes("array");
821
- }
822
- const ARRAY_ITEMS_SCHEMA_OBJECT_KEYS = /* @__PURE__ */ new Set([
823
- "additionalProperties",
824
- "contains",
825
- "else",
826
- "if",
827
- "items",
828
- "not",
829
- "propertyNames",
830
- "then"
831
- ]);
832
- const ARRAY_ITEMS_SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set([
833
- "allOf",
834
- "anyOf",
835
- "oneOf",
836
- "prefixItems"
837
- ]);
838
- const ARRAY_ITEMS_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
839
- "$defs",
840
- "definitions",
841
- "dependentSchemas",
842
- "patternProperties",
843
- "properties"
844
- ]);
845
- function stripEmptyArrayItemsFromArraySchemas(schema) {
846
- if (Array.isArray(schema)) {
847
- let changed = false;
848
- const entries = schema.map((entry) => {
849
- const next = stripEmptyArrayItemsFromArraySchemas(entry);
850
- changed ||= next !== entry;
851
- return next;
852
- });
853
- return changed ? entries : schema;
854
- }
855
- if (!isRecord(schema)) return schema;
856
- let changed = false;
857
- const entries = Object.entries(schema).flatMap(([key, value]) => {
858
- if (key === "items" && schemaAllowsArrayType(schema) && isRecord(value) && isTrulyEmptySchema(value)) {
859
- changed = true;
860
- return [];
861
- }
862
- if (ARRAY_ITEMS_SCHEMA_OBJECT_KEYS.has(key)) {
863
- const next = stripEmptyArrayItemsFromArraySchemas(value);
864
- changed ||= next !== value;
865
- return [[key, next]];
866
- }
867
- if (ARRAY_ITEMS_SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
868
- const next = stripEmptyArrayItemsFromArraySchemas(value);
869
- changed ||= next !== value;
870
- return [[key, next]];
871
- }
872
- if (ARRAY_ITEMS_SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
873
- let mapChanged = false;
874
- const next = Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => {
875
- const entryNext = stripEmptyArrayItemsFromArraySchemas(entryValue);
876
- mapChanged ||= entryNext !== entryValue;
877
- return [entryKey, entryNext];
878
- }));
879
- changed ||= mapChanged;
880
- return [[key, mapChanged ? next : value]];
881
- }
882
- return [[key, value]];
883
- });
884
- return changed ? Object.fromEntries(entries) : schema;
885
- }
886
- function copySchemaMeta(from, to) {
887
- for (const key of [
888
- "title",
889
- "description",
890
- "default"
891
- ]) if (key in from && from[key] !== void 0) to[key] = from[key];
892
- }
893
- function extendSchemaDefs(defs, schema) {
894
- const defsEntry = schema.$defs && typeof schema.$defs === "object" && !Array.isArray(schema.$defs) ? schema.$defs : void 0;
895
- const legacyDefsEntry = schema.definitions && typeof schema.definitions === "object" && !Array.isArray(schema.definitions) ? schema.definitions : void 0;
896
- if (!defsEntry && !legacyDefsEntry) return defs;
897
- const next = defs ? {
898
- $defs: new Map(defs.$defs),
899
- definitions: new Map(defs.definitions)
900
- } : {
901
- $defs: /* @__PURE__ */ new Map(),
902
- definitions: /* @__PURE__ */ new Map()
903
- };
904
- if (defsEntry) for (const [key, value] of Object.entries(defsEntry)) next.$defs.set(key, value);
905
- if (legacyDefsEntry) for (const [key, value] of Object.entries(legacyDefsEntry)) next.definitions.set(key, value);
906
- return next;
907
- }
908
- function decodeJsonPointerSegment(segment) {
909
- return segment.replaceAll("~1", "/").replaceAll("~0", "~");
910
- }
911
- function resolveJsonPointerPath(value, segments) {
912
- let current = value;
913
- for (const segment of segments) {
914
- if (!current || typeof current !== "object") return;
915
- const key = decodeJsonPointerSegment(segment);
916
- if (Array.isArray(current)) {
917
- const index = /^(?:0|[1-9]\d*)$/.test(key) ? Number(key) : -1;
918
- if (index < 0 || index >= current.length) return;
919
- current = current[index];
920
- continue;
921
- }
922
- const record = current;
923
- if (!Object.hasOwn(record, key)) return;
924
- current = record[key];
925
- }
926
- return current;
927
- }
928
- function resolveLocalJsonPointer(rootDocument, ref) {
929
- if (!ref.startsWith("#/")) return;
930
- return resolveJsonPointerPath(rootDocument, ref.slice(2).split("/"));
931
- }
932
- const SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
933
- "$defs",
934
- "definitions",
935
- "dependentSchemas",
936
- "patternProperties",
937
- "properties"
938
- ]);
939
- const SCHEMA_OBJECT_KEYS = /* @__PURE__ */ new Set([
940
- "additionalProperties",
941
- "contains",
942
- "else",
943
- "if",
944
- "items",
945
- "not",
946
- "propertyNames",
947
- "then"
948
- ]);
949
- const SCHEMA_ARRAY_KEYS = /* @__PURE__ */ new Set([
950
- "allOf",
951
- "anyOf",
952
- "items",
953
- "oneOf",
954
- "prefixItems"
955
- ]);
956
- const SCHEMA_LITERAL_KEYS = /* @__PURE__ */ new Set([
957
- "const",
958
- "default",
959
- "enum",
960
- "examples"
961
- ]);
962
- function tryResolveLocalRef(ref, defs, rootDocument) {
963
- const match = ref.match(/^#\/(\$defs|definitions)\/([^/]+)(?:\/(.*))?$/);
964
- if (match && defs) {
965
- const namespace = match[1] === "$defs" ? defs.$defs : defs.definitions;
966
- const name = decodeJsonPointerSegment(match[2] ?? "");
967
- const resolved = name ? namespace.get(name) : void 0;
968
- if (resolved !== void 0) return resolveJsonPointerPath(resolved, match[3] ? match[3].split("/") : []);
969
- }
970
- return resolveLocalJsonPointer(rootDocument, ref);
971
- }
972
- function inlineLocalSchemaRefsWithDefs(schema, defs, refStack, state, rootDocument) {
973
- if (!schema || typeof schema !== "object") return schema;
974
- if (Array.isArray(schema)) return schema.map((entry) => inlineLocalSchemaRefsWithDefs(entry, defs, refStack, state, rootDocument));
975
- const obj = schema;
976
- const nextDefs = extendSchemaDefs(defs, obj);
977
- const refValue = typeof obj.$ref === "string" ? obj.$ref : void 0;
978
- if (refValue) {
979
- if (refStack?.has(refValue)) return {};
980
- const resolved = tryResolveLocalRef(refValue, nextDefs, rootDocument);
981
- if (resolved === void 0) {
982
- if (refValue.startsWith("#/")) state.unresolvedLocalRefs = true;
983
- return { ...obj };
984
- }
985
- const nextRefStack = refStack ? new Set(refStack) : /* @__PURE__ */ new Set();
986
- nextRefStack.add(refValue);
987
- const inlined = inlineLocalSchemaRefsWithDefs(resolved, nextDefs, nextRefStack, state, rootDocument);
988
- if (!inlined || typeof inlined !== "object" || Array.isArray(inlined)) return inlined;
989
- const result = { ...inlined };
990
- copySchemaMeta(obj, result);
991
- if (obj.nullable === true) result.nullable = true;
992
- return result;
993
- }
994
- const result = {};
995
- for (const [key, value] of Object.entries(obj)) {
996
- if (key === "$defs" || key === "definitions" || key === "components") continue;
997
- if (SCHEMA_LITERAL_KEYS.has(key)) {
998
- setOwnSchemaProperty(result, key, value);
999
- continue;
1000
- }
1001
- if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
1002
- setOwnSchemaProperty(result, key, Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, inlineLocalSchemaRefsWithDefs(entryValue, nextDefs, refStack, state, rootDocument)])));
1003
- continue;
1004
- }
1005
- if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) {
1006
- setOwnSchemaProperty(result, key, inlineLocalSchemaRefsWithDefs(value, nextDefs, refStack, state, rootDocument));
1007
- continue;
1008
- }
1009
- if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
1010
- setOwnSchemaProperty(result, key, value.map((entry) => inlineLocalSchemaRefsWithDefs(entry, nextDefs, refStack, state, rootDocument)));
1011
- continue;
1012
- }
1013
- setOwnSchemaProperty(result, key, value);
1014
- }
1015
- if (state.unresolvedLocalRefs) {
1016
- if ("$defs" in obj) result.$defs = obj.$defs;
1017
- if ("definitions" in obj) result.definitions = obj.definitions;
1018
- if ("components" in obj) result.components = obj.components;
1019
- }
1020
- return result;
1021
- }
1022
- /** Inline local $ref pointers so providers receive self-contained tool schemas. */
1023
- function inlineLocalToolSchemaRefs(schema) {
1024
- if (!schema || typeof schema !== "object") return schema;
1025
- return inlineLocalSchemaRefsWithDefs(schema, extendSchemaDefs(void 0, schema), void 0, { unresolvedLocalRefs: false }, schema);
1026
- }
1027
- const OPENAPI_SCHEMA_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
1028
- "discriminator",
1029
- "externalDocs",
1030
- "readOnly",
1031
- "writeOnly",
1032
- "xml",
1033
- "example"
1034
- ]);
1035
- function appendNullSchemaType(type) {
1036
- if (type === "null") return type;
1037
- if (typeof type === "string") return [type, "null"];
1038
- if (Array.isArray(type)) return type.includes("null") ? type : [...type, "null"];
1039
- return type;
1040
- }
1041
- function isNullSchemaLike(schema) {
1042
- if (!isRecord(schema)) return false;
1043
- if (schema.type === "null") return true;
1044
- if (Array.isArray(schema.type) && schema.type.includes("null")) return true;
1045
- if ("const" in schema && schema.const === null) return true;
1046
- return Array.isArray(schema.enum) && schema.enum.includes(null);
1047
- }
1048
- function hasOpenApiComposition(schema) {
1049
- return [
1050
- "allOf",
1051
- "anyOf",
1052
- "oneOf"
1053
- ].some((key) => Array.isArray(schema[key]));
1054
- }
1055
- function schemaCompositionAlreadyAllowsNull(schema) {
1056
- return Array.isArray(schema.anyOf) && schema.anyOf.some(isNullSchemaLike) || Array.isArray(schema.oneOf) && schema.oneOf.some(isNullSchemaLike);
1057
- }
1058
- function wrapNullableComposedSchema(schema) {
1059
- if (schemaCompositionAlreadyAllowsNull(schema)) return schema;
1060
- const wrapped = { anyOf: [schema, { type: "null" }] };
1061
- copySchemaMeta(schema, wrapped);
1062
- return wrapped;
1063
- }
1064
- function normalizeOpenApiSchemaKeywords(schema) {
1065
- if (Array.isArray(schema)) {
1066
- let changed = false;
1067
- const normalized = schema.map((entry) => {
1068
- const next = normalizeOpenApiSchemaKeywords(entry);
1069
- changed ||= next !== entry;
1070
- return next;
1071
- });
1072
- return changed ? normalized : schema;
1073
- }
1074
- if (!isRecord(schema)) return schema;
1075
- let changed = false;
1076
- const nullable = schema.nullable === true;
1077
- const normalized = {};
1078
- for (const [key, value] of Object.entries(schema)) {
1079
- if (key === "nullable" || OPENAPI_SCHEMA_ANNOTATION_KEYS.has(key)) {
1080
- changed = true;
1081
- continue;
1082
- }
1083
- if (SCHEMA_LITERAL_KEYS.has(key)) {
1084
- normalized[key] = value;
1085
- continue;
1086
- }
1087
- if (SCHEMA_MAP_KEYS.has(key) && isRecord(value)) {
1088
- let mapChanged = false;
1089
- const next = Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => {
1090
- const nextEntry = normalizeOpenApiSchemaKeywords(entryValue);
1091
- mapChanged ||= nextEntry !== entryValue;
1092
- return [entryKey, nextEntry];
1093
- }));
1094
- normalized[key] = mapChanged ? next : value;
1095
- changed ||= mapChanged;
1096
- continue;
1097
- }
1098
- if (key === "components") {
1099
- normalized[key] = value;
1100
- continue;
1101
- }
1102
- if (SCHEMA_OBJECT_KEYS.has(key) && isRecord(value)) {
1103
- const next = normalizeOpenApiSchemaKeywords(value);
1104
- normalized[key] = next;
1105
- changed ||= next !== value;
1106
- continue;
1107
- }
1108
- if (SCHEMA_ARRAY_KEYS.has(key) && Array.isArray(value)) {
1109
- const next = value.map(normalizeOpenApiSchemaKeywords);
1110
- normalized[key] = next;
1111
- changed ||= next.some((entry, index) => entry !== value[index]);
1112
- continue;
1113
- }
1114
- normalized[key] = value;
1115
- }
1116
- if (nullable) {
1117
- if (hasOpenApiComposition(normalized)) return wrapNullableComposedSchema(normalized);
1118
- if ("type" in normalized) {
1119
- const nextType = appendNullSchemaType(normalized.type);
1120
- if (nextType !== normalized.type) normalized.type = nextType;
1121
- }
1122
- if (Array.isArray(normalized.enum) && !normalized.enum.includes(null)) normalized.enum = [...normalized.enum, null];
1123
- }
1124
- return changed || nullable ? normalized : schema;
1125
- }
1126
- function normalizeToolParameterSchemaUncached(schema, options) {
1127
- const inlinedSchema = normalizeOpenApiSchemaKeywords(inlineLocalToolSchemaRefs(schema));
1128
- const schemaRecord = inlinedSchema && typeof inlinedSchema === "object" ? inlinedSchema : void 0;
1129
- if (!schemaRecord) return inlinedSchema;
1130
- const normalizedProvider = normalizeLowercaseStringOrEmpty(options?.modelProvider);
1131
- const normalizedModelId = normalizeLowercaseStringOrEmpty(options?.modelId);
1132
- const normalizedToolSchemaProfile = normalizeLowercaseStringOrEmpty(options?.modelCompat?.toolSchemaProfile);
1133
- const isGeminiProvider = normalizedProvider.includes("google") || normalizedProvider.includes("gemini") || isGeminiModelId(normalizedModelId) || normalizedToolSchemaProfile === "gemini";
1134
- const isAnthropicProvider = normalizedProvider.includes("anthropic");
1135
- const unsupportedToolSchemaKeywords = resolveUnsupportedToolSchemaKeywords(options?.modelCompat);
1136
- const omitEmptyArrayItems = shouldOmitEmptyArrayItems(options?.modelCompat);
1137
- const isLlamacppGbnfProfile = normalizedToolSchemaProfile === "llamacpp";
1138
- function applyProviderCleaning(s) {
1139
- const normalizedSchema = normalizeArraySchemasMissingItems(s);
1140
- let arrayItemsCompatibleSchema = omitEmptyArrayItems ? stripEmptyArrayItemsFromArraySchemas(normalizedSchema) : normalizedSchema;
1141
- if (isLlamacppGbnfProfile) arrayItemsCompatibleSchema = cleanSchemaForLlamacppGbnf(arrayItemsCompatibleSchema);
1142
- if (isGeminiProvider && !isAnthropicProvider) {
1143
- const geminiCompatibleSchema = cleanSchemaForGemini(arrayItemsCompatibleSchema);
1144
- return unsupportedToolSchemaKeywords.size > 0 ? stripUnsupportedSchemaKeywords(geminiCompatibleSchema, unsupportedToolSchemaKeywords) : geminiCompatibleSchema;
1145
- }
1146
- if (unsupportedToolSchemaKeywords.size > 0) return stripUnsupportedSchemaKeywords(arrayItemsCompatibleSchema, unsupportedToolSchemaKeywords);
1147
- return arrayItemsCompatibleSchema;
1148
- }
1149
- const conditionalKey = getTopLevelConditionalKey(schemaRecord);
1150
- const flattenableVariantKey = getFlattenableVariantKey(schemaRecord);
1151
- if (hasTopLevelObjectSchema(schemaRecord, conditionalKey)) return applyProviderCleaning(schemaRecord);
1152
- if (isObjectLikeSchemaMissingType(schemaRecord, conditionalKey)) return applyProviderCleaning({
1153
- ...schemaRecord,
1154
- type: "object",
1155
- properties: isRecord(schemaRecord.properties) ? schemaRecord.properties : {}
1156
- });
1157
- if (isTypedObjectSchemaMissingValidProperties(schemaRecord, conditionalKey)) return applyProviderCleaning({
1158
- ...schemaRecord,
1159
- properties: {}
1160
- });
1161
- if (!flattenableVariantKey) {
1162
- if (isTrulyEmptySchema(schemaRecord)) return applyProviderCleaning({
1163
- type: "object",
1164
- properties: {}
1165
- });
1166
- if (conditionalKey === "allOf") return applyProviderCleaning(inlinedSchema);
1167
- return applyProviderCleaning(inlinedSchema);
1168
- }
1169
- const variants = schemaRecord[flattenableVariantKey];
1170
- const mergedProperties = {};
1171
- const requiredCounts = /* @__PURE__ */ new Map();
1172
- let objectVariants = 0;
1173
- for (const entry of variants) {
1174
- if (!entry || typeof entry !== "object") continue;
1175
- const props = entry.properties;
1176
- if (!props || typeof props !== "object") continue;
1177
- objectVariants += 1;
1178
- for (const [key, value] of Object.entries(props)) {
1179
- if (!(key in mergedProperties)) {
1180
- mergedProperties[key] = value;
1181
- continue;
1182
- }
1183
- mergedProperties[key] = mergePropertySchemas(mergedProperties[key], value);
1184
- }
1185
- const required = Array.isArray(entry.required) ? entry.required : [];
1186
- for (const key of required) {
1187
- if (typeof key !== "string") continue;
1188
- requiredCounts.set(key, (requiredCounts.get(key) ?? 0) + 1);
1189
- }
1190
- }
1191
- const baseRequired = Array.isArray(schemaRecord.required) ? schemaRecord.required.filter((key) => typeof key === "string") : void 0;
1192
- const mergedRequired = baseRequired && baseRequired.length > 0 ? baseRequired : objectVariants > 0 ? Array.from(requiredCounts.entries()).filter(([, count]) => count === objectVariants).map(([key]) => key) : void 0;
1193
- const nextSchema = { ...schemaRecord };
1194
- return applyProviderCleaning({
1195
- type: "object",
1196
- ...typeof nextSchema.title === "string" ? { title: nextSchema.title } : {},
1197
- ...typeof nextSchema.description === "string" ? { description: nextSchema.description } : {},
1198
- properties: Object.keys(mergedProperties).length > 0 ? mergedProperties : schemaRecord.properties ?? {},
1199
- ...mergedRequired && mergedRequired.length > 0 ? { required: mergedRequired } : {},
1200
- additionalProperties: "additionalProperties" in schemaRecord ? schemaRecord.additionalProperties : true
1201
- });
1202
- }
1203
- /** Return a provider-compatible JSON schema for a model-facing tool. */
1204
- function normalizeToolParameterSchema(schema, options) {
1205
- if (!schema || typeof schema !== "object") return normalizeToolParameterSchemaUncached(schema, options);
1206
- const cacheKey = resolveToolParameterSchemaCacheKey(options);
1207
- const cached = getCachedToolParameterSchema(schema, cacheKey);
1208
- if (cached) return cached;
1209
- return rememberCachedToolParameterSchema(schema, cacheKey, normalizeToolParameterSchemaUncached(schema, options));
1210
- }
1211
- //#endregion
1212
- //#region packages/ai/src/providers/openai-tool-schema-compat.ts
1213
- const OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
1214
- "$defs",
1215
- "definitions",
1216
- "dependentSchemas",
1217
- "dependencies",
1218
- "patternProperties",
1219
- "properties"
1220
- ]);
1221
- const OPENAI_NULLABLE_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
1222
- "default",
1223
- "description",
1224
- "examples",
1225
- "format",
1226
- "title"
1227
- ]);
1228
- const OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS = /* @__PURE__ */ new Set([
1229
- "additionalItems",
1230
- "additionalProperties",
1231
- "allOf",
1232
- "anyOf",
1233
- "contains",
1234
- "contentSchema",
1235
- "else",
1236
- "if",
1237
- "items",
1238
- "not",
1239
- "oneOf",
1240
- "prefixItems",
1241
- "propertyNames",
1242
- "then",
1243
- "unevaluatedItems",
1244
- "unevaluatedProperties"
1245
- ]);
1246
- function normalizeOpenAIStrictCompatSchemaMap(schema) {
1247
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
1248
- let changed = false;
1249
- const normalized = {};
1250
- for (const [key, value] of Object.entries(schema)) {
1251
- const next = normalizeOpenAIStrictCompatSchemaRecursive(value, { promoteEmptyObject: false });
1252
- normalized[key] = next;
1253
- changed ||= next !== value;
1254
- }
1255
- return changed ? normalized : schema;
1256
- }
1257
- function normalizeOpenAIStrictCompatSchemaRecursive(schema, options) {
1258
- if (Array.isArray(schema)) {
1259
- let changed = false;
1260
- const normalized = schema.map((entry) => {
1261
- const next = normalizeOpenAIStrictCompatSchemaRecursive(entry, { promoteEmptyObject: false });
1262
- changed ||= next !== entry;
1263
- return next;
1264
- });
1265
- return changed ? normalized : schema;
1266
- }
1267
- if (!schema || typeof schema !== "object") return schema;
1268
- const record = schema;
1269
- let changed = false;
1270
- let hadNullType = false;
1271
- const normalized = {};
1272
- for (const [key, value] of Object.entries(record)) {
1273
- if (value === null && OPENAI_NULLABLE_ANNOTATION_KEYS.has(key)) {
1274
- changed = true;
1275
- continue;
1276
- }
1277
- if (value === null && key === "type") {
1278
- hadNullType = true;
1279
- changed = true;
1280
- continue;
1281
- }
1282
- 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;
1283
- normalized[key] = next;
1284
- changed ||= next !== value;
1285
- }
1286
- if (Object.keys(normalized).length === 0) {
1287
- if (!options.promoteEmptyObject) return schema;
1288
- return {
1289
- type: "object",
1290
- properties: {},
1291
- required: [],
1292
- additionalProperties: false
1293
- };
1294
- }
1295
- const hasObjectShapeHints = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) || Array.isArray(normalized.required);
1296
- const hasArrayShapeHints = "items" in normalized;
1297
- if (!("type" in normalized) && hasObjectShapeHints !== hasArrayShapeHints) {
1298
- normalized.type = hasObjectShapeHints ? "object" : "array";
1299
- changed = true;
1300
- } else if (hadNullType && !("type" in normalized)) normalized.type = null;
1301
- if (normalized.type === "object" && !("properties" in normalized)) {
1302
- normalized.properties = {};
1303
- changed = true;
1304
- }
1305
- const hasEmptyProperties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) && Object.keys(normalized.properties).length === 0;
1306
- if (normalized.type === "object" && !Array.isArray(normalized.required) && hasEmptyProperties) {
1307
- normalized.required = [];
1308
- changed = true;
1309
- }
1310
- if (normalized.type === "object" && hasEmptyProperties && !("additionalProperties" in normalized)) {
1311
- normalized.additionalProperties = false;
1312
- changed = true;
1313
- }
1314
- return changed ? normalized : schema;
1315
- }
1316
- /** Repairs recoverable OpenAI tool-schema shapes before canonical normalization. */
1317
- function normalizeOpenAIStrictCompatSchema(schema) {
1318
- return normalizeOpenAIStrictCompatSchemaRecursive(schema, { promoteEmptyObject: true });
1319
- }
1320
- /** Finds schema paths that violate OpenAI strict tool-schema requirements. */
1321
- function findOpenAIStrictSchemaViolations(schema, path, options) {
1322
- if (Array.isArray(schema)) {
1323
- if (options?.requireObjectRoot) return [`${path}.type`];
1324
- return schema.flatMap((item, index) => findOpenAIStrictSchemaViolations(item, `${path}[${index}]`));
1325
- }
1326
- if (!schema || typeof schema !== "object") return options?.requireObjectRoot ? [`${path}.type`] : [];
1327
- const record = schema;
1328
- const violations = [];
1329
- for (const key of [
1330
- "anyOf",
1331
- "oneOf",
1332
- "allOf"
1333
- ]) if (key in record) violations.push(`${path}.${key}`);
1334
- if (Array.isArray(record.type)) violations.push(`${path}.type`);
1335
- const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : void 0;
1336
- if (record.type === "object") {
1337
- if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
1338
- const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
1339
- if (!required) violations.push(`${path}.required`);
1340
- else if (properties) {
1341
- const requiredSet = new Set(required);
1342
- for (const key of Object.keys(properties)) if (!requiredSet.has(key)) violations.push(`${path}.required.${key}`);
1343
- }
1344
- }
1345
- for (const key of OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS) {
1346
- const schemaMap = record[key];
1347
- if (!schemaMap || typeof schemaMap !== "object" || Array.isArray(schemaMap)) continue;
1348
- for (const [entryKey, value] of Object.entries(schemaMap)) violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}.${entryKey}`));
1349
- }
1350
- for (const key of OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS) {
1351
- const value = record[key];
1352
- if (value && typeof value === "object") violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}`));
1353
- }
1354
- return violations;
1355
- }
1356
- //#endregion
1357
- //#region packages/ai/src/providers/openai-tool-schema.ts
1358
- /**
1359
- * OpenAI strict JSON-schema normalization for tool inventories and request payloads.
1360
- *
1361
- * Caches normalized object inputs by provider compatibility so repeated inventory builds preserve identity.
1362
- */
1363
- const MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
1364
- let strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
1365
- function resolveToolSchemaModelCompat(compat) {
1366
- if (!compat) return;
1367
- const unsupportedToolSchemaKeywords = Array.isArray(compat.unsupportedToolSchemaKeywords) ? compat.unsupportedToolSchemaKeywords.filter((keyword) => typeof keyword === "string") : [];
1368
- if (unsupportedToolSchemaKeywords.length === 0 && compat.omitEmptyArrayItems !== true) return;
1369
- return {
1370
- ...unsupportedToolSchemaKeywords.length > 0 ? { unsupportedToolSchemaKeywords } : {},
1371
- ...compat.omitEmptyArrayItems === true ? { omitEmptyArrayItems: true } : {}
1372
- };
1373
- }
1374
- function resolveStrictOpenAISchemaCacheKey(modelCompat) {
1375
- const compat = resolveToolSchemaModelCompat(modelCompat);
1376
- return JSON.stringify([[...compat?.unsupportedToolSchemaKeywords ?? []].toSorted(), shouldOmitEmptyArrayItems(compat)]);
1377
- }
1378
- function readCachedStrictOpenAISchema(schema, key) {
1379
- return strictOpenAISchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
1380
- }
1381
- function rememberStrictOpenAISchema(schema, key, value) {
1382
- const entries = strictOpenAISchemaCache.get(schema) ?? [];
1383
- strictOpenAISchemaCache.set(schema, [{
1384
- key,
1385
- value
1386
- }, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
1387
- return value;
1388
- }
1389
- function clearOpenAIToolSchemaCacheForTest() {
1390
- strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
1391
- }
1392
- /** Normalizes a tool parameter schema into the OpenAI strict JSON-schema subset. */
1393
- function normalizeStrictOpenAIJsonSchema(schema, modelCompat) {
1394
- const schemaInput = schema ?? {};
1395
- if (!schemaInput || typeof schemaInput !== "object") return normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0);
1396
- const cacheKey = resolveStrictOpenAISchemaCacheKey(modelCompat);
1397
- const cached = readCachedStrictOpenAISchema(schemaInput, cacheKey);
1398
- if (cached !== void 0) return cached;
1399
- return rememberStrictOpenAISchema(schemaInput, cacheKey, normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0));
1400
- }
1401
- function normalizeStrictOpenAIJsonSchemaRecursive(schema, depth) {
1402
- if (Array.isArray(schema)) {
1403
- let changed = false;
1404
- const normalized = schema.map((entry) => {
1405
- const next = normalizeStrictOpenAIJsonSchemaRecursive(entry, depth);
1406
- changed ||= next !== entry;
1407
- return next;
1408
- });
1409
- return changed ? normalized : schema;
1410
- }
1411
- if (!schema || typeof schema !== "object") return schema;
1412
- const record = schema;
1413
- let changed = false;
1414
- const normalized = {};
1415
- for (const [key, value] of Object.entries(record)) {
1416
- const next = normalizeStrictOpenAIJsonSchemaRecursive(value, key === "properties" ? depth : depth + 1);
1417
- normalized[key] = next;
1418
- changed ||= next !== value;
1419
- }
1420
- if (normalized.type === "object") {
1421
- const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : void 0;
1422
- if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
1423
- normalized.required = [];
1424
- changed = true;
1425
- }
1426
- if (depth === 0 && !("additionalProperties" in normalized)) {
1427
- normalized.additionalProperties = false;
1428
- changed = true;
1429
- }
1430
- }
1431
- return changed ? normalized : schema;
1432
- }
1433
- /** Normalizes tool parameters using strict OpenAI rules only when strict mode is active. */
1434
- function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
1435
- const toolSchemaCompat = resolveToolSchemaModelCompat(modelCompat);
1436
- if (!strict) return normalizeToolParameterSchema(schema ?? {}, { modelCompat: toolSchemaCompat });
1437
- return normalizeStrictOpenAIJsonSchema(schema, toolSchemaCompat);
1438
- }
1439
- /** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
1440
- function isStrictOpenAIJsonSchemaCompatible(schema) {
1441
- return isStrictOpenAIJsonSchemaCompatibleRecursive(normalizeStrictOpenAIJsonSchema(schema));
1442
- }
1443
- /** Returns strict-schema diagnostics for an already materialized OpenAI tool projection. */
1444
- function findOpenAIStrictToolProjectionDiagnostics(projection) {
1445
- return [...projection.diagnostics.map((diagnostic) => ({
1446
- toolIndex: diagnostic.toolIndex,
1447
- ...diagnostic.toolName ? { toolName: diagnostic.toolName } : {},
1448
- violations: [...diagnostic.violations]
1449
- })), ...projection.tools.flatMap((tool) => {
1450
- const violations = findOpenAIStrictSchemaViolations(normalizeStrictOpenAIJsonSchema(tool.parameters), `${tool.name}.parameters`);
1451
- return violations.length > 0 ? [{
1452
- toolIndex: tool.toolIndex,
1453
- toolName: tool.name,
1454
- violations
1455
- }] : [];
1456
- })];
1457
- }
1458
- function isStrictOpenAIJsonSchemaCompatibleRecursive(schema) {
1459
- if (Array.isArray(schema)) return schema.every((entry) => isStrictOpenAIJsonSchemaCompatibleRecursive(entry));
1460
- if (!schema || typeof schema !== "object") return true;
1461
- const record = schema;
1462
- if ("anyOf" in record || "oneOf" in record || "allOf" in record) return false;
1463
- if (Array.isArray(record.type)) return false;
1464
- if (record.type === "object" && record.additionalProperties !== false) return false;
1465
- if (record.type === "object") {
1466
- const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
1467
- const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
1468
- if (!required) return false;
1469
- const requiredSet = new Set(required);
1470
- if (Object.keys(properties).some((key) => !requiredSet.has(key))) return false;
1471
- }
1472
- return Object.entries(record).every(([key, entry]) => {
1473
- if (key === "properties" && entry && typeof entry === "object" && !Array.isArray(entry)) return Object.values(entry).every((value) => isStrictOpenAIJsonSchemaCompatibleRecursive(value));
1474
- return isStrictOpenAIJsonSchemaCompatibleRecursive(entry);
1475
- });
1476
- }
1477
- /** Resolves strict mode for the projected tools that will be emitted in the request payload. */
1478
- function resolveOpenAIProjectedToolsStrictToolFlag(projection, strict) {
1479
- if (strict !== true) return strict === false ? false : void 0;
1480
- return projection.tools.every((tool) => isStrictOpenAIJsonSchemaCompatible(tool.parameters));
1481
- }
1482
- //#endregion
1483
- //#region packages/ai/src/transports/openai-transport-shared.ts
1484
- /** Shared options, usage shape, cache identity, ordering, and stream scheduling for OpenAI APIs. */
1485
- const MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS = 12;
1486
- const MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS = 64;
1487
- const GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP = "skip_thought_signature_validator";
1488
- const log = {
1489
- debug(message, data) {
1490
- getAiTransportHost().logDebug("openai-transport", () => ({
1491
- message,
1492
- data
1493
- }));
1494
- },
1495
- info(message, data) {
1496
- getAiTransportHost().logInfo("openai-transport", message, data);
1497
- },
1498
- warn(message, data) {
1499
- getAiTransportHost().logWarn("openai-transport", message, data);
1500
- }
1501
- };
1502
- function throwIfModelStreamAborted(signal) {
1503
- if (signal?.aborted) throw transportAbortError(signal);
1504
- }
1505
- function createModelStreamCooperativeScheduler(signal) {
1506
- let lastYieldedAt = Date.now();
1507
- let eventsSinceYield = 0;
1508
- return { async afterEvent() {
1509
- throwIfModelStreamAborted(signal);
1510
- eventsSinceYield += 1;
1511
- const now = Date.now();
1512
- if (eventsSinceYield < MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS && now - lastYieldedAt < MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS) return;
1513
- eventsSinceYield = 0;
1514
- lastYieldedAt = now;
1515
- await new Promise((resolve) => {
1516
- setTimeout(resolve, 0);
1517
- });
1518
- throwIfModelStreamAborted(signal);
1519
- } };
1520
- }
1521
- function resolvePromptCacheKey(options, cacheRetention) {
1522
- if (cacheRetention === "none") return;
1523
- return clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId);
1524
- }
1525
- //#endregion
1526
- //#region packages/ai/src/transports/openai-responses-replay.ts
1527
- /** Resolves the assistant message id that can be replayed to OpenAI Responses. */
1528
- function resolveReplayableResponsesMessageId(params) {
1529
- if (!params.replayResponsesItemIds) return;
1530
- if (!params.textSignatureId) return params.fallbackOrdinal === 0 ? params.fallbackId : `${params.fallbackId}_${params.fallbackOrdinal}`;
1531
- return params.previousReplayItemWasReasoning ? params.textSignatureId : void 0;
1532
- }
1533
- const OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS = "Follow the user request.";
1534
- const AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS = 3e4;
1535
- const RESPONSE_FAILED_NO_DETAILS_MESSAGE = "Unknown error (no error details in response)";
1536
- const OPENAI_RESPONSES_REASONING_REPLAY_META_KEY = "__openclaw_replay";
1537
- const OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY = "openclawReasoningReplay";
1538
- //#endregion
1539
- //#region packages/ai/src/transports/openai-responses-debug.ts
1540
- function stringifyUnknown(value, fallback = "") {
1541
- if (typeof value === "string") return value;
1542
- if (typeof value === "number" || typeof value === "boolean") return String(value);
1543
- return fallback;
1544
- }
1545
- function getServiceTierCostMultiplier(serviceTier) {
1546
- switch (serviceTier) {
1547
- case "flex": return .5;
1548
- case "priority": return 2;
1549
- default: return 1;
1550
- }
1551
- }
1552
- function applyServiceTierPricing(usage, serviceTier) {
1553
- const multiplier = getServiceTierCostMultiplier(serviceTier);
1554
- if (multiplier === 1) return;
1555
- usage.cost.input *= multiplier;
1556
- usage.cost.output *= multiplier;
1557
- usage.cost.cacheRead *= multiplier;
1558
- usage.cost.cacheWrite *= multiplier;
1559
- usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
1560
- }
1561
- function safeDebugValue(value) {
1562
- if (typeof value === "string") return value;
1563
- if (typeof value === "number" || typeof value === "boolean") return String(value);
1564
- if (value === null) return "null";
1565
- if (value === void 0) return "undefined";
1566
- return Array.isArray(value) ? "array" : typeof value;
1567
- }
1568
- function responseInputTextChars(input) {
1569
- if (typeof input === "string") return input.length;
1570
- if (Array.isArray(input)) return input.reduce((total, item) => total + responseInputTextChars(item), 0);
1571
- if (!input || typeof input !== "object") return 0;
1572
- const record = input;
1573
- let total = 0;
1574
- if (typeof record.text === "string") total += record.text.length;
1575
- if (typeof record.content === "string") total += record.content.length;
1576
- else if (Array.isArray(record.content)) total += responseInputTextChars(record.content);
1577
- return total;
1578
- }
1579
- function responseInputRoles(input) {
1580
- if (!Array.isArray(input)) return "";
1581
- const roles = /* @__PURE__ */ new Set();
1582
- for (const item of input) if (item && typeof item === "object") {
1583
- const role = item.role;
1584
- if (typeof role === "string" && role.trim()) roles.add(role.trim());
1585
- }
1586
- return [...roles].toSorted().join(",");
1587
- }
1588
- function readToolPayloadField(record, field) {
1589
- try {
1590
- return record[field];
1591
- } catch {
1592
- return;
1593
- }
1594
- }
1595
- function readResponsesToolDisplayName(tool) {
1596
- if (!tool || typeof tool !== "object") return "";
1597
- const record = tool;
1598
- const name = readToolPayloadField(record, "name");
1599
- if (typeof name === "string") return name;
1600
- const fn = readToolPayloadField(record, "function");
1601
- if (fn && typeof fn === "object") {
1602
- const fnName = readToolPayloadField(fn, "name");
1603
- if (typeof fnName === "string") return fnName;
1604
- }
1605
- const type = readToolPayloadField(record, "type");
1606
- return typeof type === "string" && type !== "function" ? type : "";
1607
- }
1608
- function summarizeResponsesTools(tools) {
1609
- if (!Array.isArray(tools)) return "count=0";
1610
- const names = tools.map(readResponsesToolDisplayName).filter(Boolean);
1611
- const mode = resolveModelPayloadDebugMode();
1612
- const maxNames = mode === "tools" || mode === "full-redacted" ? names.length : 12;
1613
- const label = maxNames >= names.length ? "names" : "sample";
1614
- const shown = names.slice(0, maxNames).join(",");
1615
- return `count=${tools.length}${shown ? ` ${label}=${shown}` : ""}`;
1616
- }
1617
- function stringifyRedactedPayload(value) {
1618
- try {
1619
- const encoded = JSON.stringify(value);
1620
- if (!encoded) return "<empty>";
1621
- const redacted = redactSensitiveText(encoded, { mode: "tools" });
1622
- return redacted.length > 8e3 ? `${truncateUtf16Safe(redacted, 8e3)}…<truncated>` : redacted;
1623
- } catch {
1624
- return "<unserializable>";
1625
- }
1626
- }
1627
- function stringifyRedactedEvent(value) {
1628
- const redacted = stringifyRedactedPayload(value);
1629
- return redacted.length > 2e3 ? `${truncateUtf16Safe(redacted, 2e3)}…<truncated>` : redacted;
1630
- }
1631
- const RESPONSE_FAILED_FAILURE_FIELD_KEYS = [
1632
- "error",
1633
- "incomplete_details",
1634
- "status_details",
1635
- "failure_reason",
1636
- "last_error",
1637
- "provider_error",
1638
- "error_details"
1639
- ];
1640
- function readResponseFailedString(record, key) {
1641
- return stringifyUnknown(record?.[key]);
1642
- }
1643
- function buildResponsesFailedEventSummary(message, responseId, observation) {
1644
- const summary = { message };
1645
- if (responseId) summary.responseId = responseId;
1646
- if (observation) summary.observation = observation;
1647
- return summary;
1648
- }
1649
- function isResponseFailedIdentifierKey(key) {
1650
- const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
1651
- return normalized === "requestid" || normalized === "xrequestid" || normalized === "providerrequestid" || normalized === "providerresponseid" || normalized === "litellmrequestid" || normalized.includes("request") && normalized.endsWith("id") || normalized.includes("provider") && normalized.endsWith("id");
1652
- }
1653
- function collectResponseFailedIdentifierHashes(value, opts = {}) {
1654
- const path = opts.path ?? "";
1655
- const depth = opts.depth ?? 0;
1656
- const identifierKey = opts.identifierKey ?? "";
1657
- const out = opts.out ?? [];
1658
- const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
1659
- if (out.length >= 12 || depth > 4 || !value || typeof value !== "object") return out;
1660
- if (seen.has(value)) return out;
1661
- seen.add(value);
1662
- if (Array.isArray(value)) {
1663
- for (const [index, item] of value.entries()) {
1664
- if (index >= 8 || out.length >= 12) break;
1665
- const itemString = typeof item === "string" || typeof item === "number" ? String(item).trim() : "";
1666
- if (identifierKey && isResponseFailedIdentifierKey(identifierKey) && itemString) {
1667
- out.push(`${path}[${index}]=${redactIdentifier(itemString, { len: 12 })}`);
1668
- continue;
1669
- }
1670
- collectResponseFailedIdentifierHashes(item, {
1671
- path: `${path}[${index}]`,
1672
- depth: depth + 1,
1673
- identifierKey,
1674
- out,
1675
- seen
1676
- });
1677
- }
1678
- return out;
1679
- }
1680
- for (const [key, child] of Object.entries(value)) {
1681
- if (out.length >= 12) break;
1682
- const childPath = path ? `${path}.${key}` : key;
1683
- const childString = typeof child === "string" || typeof child === "number" ? String(child).trim() : "";
1684
- if (isResponseFailedIdentifierKey(key) && childString) {
1685
- out.push(`${childPath}=${redactIdentifier(childString, { len: 12 })}`);
1686
- continue;
1687
- }
1688
- collectResponseFailedIdentifierHashes(child, {
1689
- path: childPath,
1690
- depth: depth + 1,
1691
- identifierKey: isResponseFailedIdentifierKey(key) ? key : void 0,
1692
- out,
1693
- seen
1694
- });
1695
- }
1696
- return out;
1697
- }
1698
- function redactResponseFailedDiagnosticValue(value, opts = {}) {
1699
- const key = opts.key ?? "";
1700
- const depth = opts.depth ?? 0;
1701
- if (typeof value === "string" || typeof value === "number") return key && isResponseFailedIdentifierKey(key) ? redactIdentifier(String(value), { len: 12 }) : value;
1702
- if (depth > 6 || !value || typeof value !== "object") return value;
1703
- const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
1704
- if (seen.has(value)) return "<circular>";
1705
- seen.add(value);
1706
- if (Array.isArray(value)) return value.slice(0, 16).map((item) => redactResponseFailedDiagnosticValue(item, {
1707
- key,
1708
- depth: depth + 1,
1709
- seen
1710
- }));
1711
- const out = {};
1712
- for (const [childKey, child] of Object.entries(value)) out[childKey] = redactResponseFailedDiagnosticValue(child, {
1713
- key: childKey,
1714
- depth: depth + 1,
1715
- seen
1716
- });
1717
- return out;
1718
- }
1719
- function buildResponsesFailedFailureFields(response) {
1720
- if (!response) return {};
1721
- const fields = {};
1722
- for (const key of RESPONSE_FAILED_FAILURE_FIELD_KEYS) if (response[key] !== void 0 && response[key] !== null) fields[key] = response[key];
1723
- return fields;
1724
- }
1725
- function buildResponsesFailedNoDetailsObservation(event, model, response = isRecord(event.response) ? event.response : void 0) {
1726
- const failureFields = redactResponseFailedDiagnosticValue(buildResponsesFailedFailureFields(response));
1727
- const metadataKeys = isRecord(response?.metadata) ? Object.keys(response.metadata).toSorted() : [];
1728
- const responsePreview = {
1729
- id: readResponseFailedString(response, "id"),
1730
- status: readResponseFailedString(response, "status"),
1731
- model: readResponseFailedString(response, "model"),
1732
- object: readResponseFailedString(response, "object"),
1733
- failureFields,
1734
- metadataKeys
1735
- };
1736
- return {
1737
- event: "openai_responses_response_failed_without_details",
1738
- provider: model.provider,
1739
- api: model.api,
1740
- transportModel: model.id,
1741
- providerRuntimeFailureKind: "no_error_details",
1742
- responseId: responsePreview.id,
1743
- responseStatus: responsePreview.status,
1744
- responseModel: responsePreview.model,
1745
- responseObject: responsePreview.object,
1746
- metadataKeys,
1747
- requestIdHashes: collectResponseFailedIdentifierHashes(event),
1748
- failureFieldsPreview: stringifyRedactedEvent(failureFields),
1749
- responsePreview: stringifyRedactedEvent(responsePreview)
1750
- };
1751
- }
1752
- function summarizeResponsesFailedNoDetailsObservation(observation) {
1753
- const requestIds = observation.requestIdHashes.join(",");
1754
- const metadataKeys = observation.metadataKeys.join(",");
1755
- return `responseId=${safeDebugValue(observation.responseId || void 0)} responseStatus=${safeDebugValue(observation.responseStatus || void 0)} responseModel=${safeDebugValue(observation.responseModel || void 0)} requestIds=${requestIds || "none"} metadataKeys=${metadataKeys || "none"} failureFields=${observation.failureFieldsPreview}`;
1756
- }
1757
- function normalizeResponsesFailedEvent(event, model) {
1758
- const response = isRecord(event.response) ? event.response : void 0;
1759
- const responseId = readResponseFailedString(response, "id") || void 0;
1760
- const error = isRecord(response?.error) ? response.error : void 0;
1761
- if (error) {
1762
- const code = readResponseFailedString(error, "code").trim();
1763
- const message = readResponseFailedString(error, "message").trim();
1764
- if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId);
1765
- }
1766
- const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
1767
- if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
1768
- return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, buildResponsesFailedNoDetailsObservation(event, model, response));
1769
- }
1770
- function logResponsesFailedNoDetails(observation) {
1771
- log.warn(`[responses] response.failed missing error details provider=${observation.provider} api=${observation.api} model=${observation.transportModel} ` + summarizeResponsesFailedNoDetailsObservation(observation), observation);
1772
- }
1773
- function summarizeResponsesPayload(params) {
1774
- if (!params || typeof params !== "object") return "payload=non-object";
1775
- const record = params;
1776
- const input = record.input;
1777
- const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
1778
- const text = record.text && typeof record.text === "object" ? record.text : void 0;
1779
- const parts = [
1780
- `fields=${Object.keys(record).toSorted().join(",")}`,
1781
- `model=${safeDebugValue(record.model)}`,
1782
- `stream=${safeDebugValue(record.stream)}`,
1783
- `inputItems=${Array.isArray(input) ? input.length : typeof input}`,
1784
- `inputRoles=${responseInputRoles(input) || "none"}`,
1785
- `inputTextChars=${responseInputTextChars(input)}`,
1786
- `tools=${summarizeResponsesTools(record.tools)}`,
1787
- `reasoningEffort=${safeDebugValue(reasoning?.effort)}`,
1788
- `reasoningSummary=${safeDebugValue(reasoning?.summary)}`,
1789
- `textVerbosity=${safeDebugValue(text?.verbosity)}`,
1790
- `serviceTier=${safeDebugValue(record.service_tier)}`,
1791
- `store=${safeDebugValue(record.store)}`,
1792
- `promptCacheKey=${record.prompt_cache_key === void 0 ? "absent" : "present"}`,
1793
- `metadataKeys=${record.metadata && typeof record.metadata === "object" ? Object.keys(record.metadata).toSorted().join(",") : "none"}`
1794
- ];
1795
- if (resolveModelPayloadDebugMode() === "full-redacted") parts.push(`payload=${stringifyRedactedPayload(record)}`);
1796
- return parts.join(" ");
1797
- }
1798
- function summarizeOpenAITransportError(error) {
1799
- if (!error || typeof error !== "object") return `type=${typeof error} message=${safeDebugValue(error)}`;
1800
- const record = error;
1801
- const cause = record.cause && typeof record.cause === "object" ? record.cause : void 0;
1802
- return [
1803
- `name=${safeDebugValue(record.name)}`,
1804
- `status=${safeDebugValue(record.status)}`,
1805
- `code=${safeDebugValue(record.code)}`,
1806
- `type=${safeDebugValue(record.type)}`,
1807
- `causeName=${safeDebugValue(cause?.name)}`,
1808
- `causeCode=${safeDebugValue(cause?.code)}`,
1809
- `message=${error instanceof Error ? error.message : safeDebugValue(error)}`
1810
- ].join(" ");
1811
- }
1812
- //#endregion
1813
- //#region packages/ai/src/transports/openai-responses-replay-internal.ts
1814
- function isInvalidEncryptedContentError(error) {
1815
- if (!error || typeof error !== "object") return false;
1816
- const record = error;
1817
- if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
1818
- const message = typeof record.message === "string" ? record.message : "";
1819
- return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || record.status === 400 && message.toLowerCase().includes("could not decrypt the provided encrypted_content");
1820
- }
1821
- function stripEncryptedContentFields(value) {
1822
- if (!value || typeof value !== "object") return {
1823
- value,
1824
- changed: false
1825
- };
1826
- if (Array.isArray(value)) {
1827
- let changed = false;
1828
- const next = value.map((item) => {
1829
- const stripped = stripEncryptedContentFields(item);
1830
- changed ||= stripped.changed;
1831
- return stripped.value;
1832
- });
1833
- return changed ? {
1834
- value: next,
1835
- changed: true
1836
- } : {
1837
- value,
1838
- changed: false
1839
- };
1840
- }
1841
- let changed = false;
1842
- const next = {};
1843
- for (const [key, child] of Object.entries(value)) {
1844
- if (key === "encrypted_content") {
1845
- changed = true;
1846
- continue;
1847
- }
1848
- const stripped = stripEncryptedContentFields(child);
1849
- changed ||= stripped.changed;
1850
- next[key] = stripped.value;
1851
- }
1852
- return changed ? {
1853
- value: next,
1854
- changed: true
1855
- } : {
1856
- value,
1857
- changed: false
1858
- };
1859
- }
1860
- function stripResponsesRequestEncryptedContent(params) {
1861
- const stripped = stripEncryptedContentFields(params.input);
1862
- if (!stripped.changed) return params;
1863
- return {
1864
- ...params,
1865
- input: stripped.value
1866
- };
1867
- }
1868
- function hashOptionalReplayContextValue(value) {
1869
- const normalized = value?.trim();
1870
- return normalized ? shortHash(normalized) : void 0;
1871
- }
1872
- function buildOpenAIResponsesReplayContext(model, options) {
1873
- return {
1874
- provider: model.provider,
1875
- api: model.api,
1876
- model: model.id,
1877
- baseUrlHash: hashOptionalReplayContextValue(model.baseUrl),
1878
- sessionHash: hashOptionalReplayContextValue(options?.sessionId),
1879
- authProfileHash: hashOptionalReplayContextValue(options?.authProfileId)
1880
- };
1881
- }
1882
- function buildOpenAIResponsesReasoningReplayMetadata(model, options) {
1883
- return {
1884
- v: 1,
1885
- source: "openai-responses",
1886
- ...buildOpenAIResponsesReplayContext(model, options)
1887
- };
1888
- }
1889
- function tagOpenAIResponsesReasoningReplayItem(item, model, options) {
1890
- if (!("encrypted_content" in item)) return item;
1891
- return {
1892
- ...item,
1893
- [OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: buildOpenAIResponsesReasoningReplayMetadata(model, options)
1894
- };
1895
- }
1896
- function isOpenAIResponsesReasoningReplayMetadata(value) {
1897
- if (!value || typeof value !== "object") return false;
1898
- const record = value;
1899
- return record.v === 1 && record.source === "openai-responses" && typeof record.provider === "string" && typeof record.api === "string" && typeof record.model === "string" && (record.baseUrlHash === void 0 || typeof record.baseUrlHash === "string") && (record.sessionHash === void 0 || typeof record.sessionHash === "string") && (record.authProfileHash === void 0 || typeof record.authProfileHash === "string");
1900
- }
1901
- function encryptedReasoningReplayMetadataMatches(metadata, context) {
1902
- if (!metadata) return false;
1903
- return metadata.provider === context.provider && metadata.api === context.api && metadata.model === context.model && metadata.baseUrlHash === context.baseUrlHash && metadata.sessionHash === context.sessionHash && metadata.authProfileHash === context.authProfileHash;
1904
- }
1905
- function readOpenAIResponsesReasoningReplayBlockMetadata(block) {
1906
- const value = block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY];
1907
- return isOpenAIResponsesReasoningReplayMetadata(value) ? value : void 0;
1908
- }
1909
- function normalizeOpenAIResponsesReasoningReplayItem(item) {
1910
- const record = item;
1911
- if (record.type !== "reasoning" || Array.isArray(record.summary)) return item;
1912
- return {
1913
- ...record,
1914
- summary: []
1915
- };
1916
- }
1917
- function prepareOpenAIResponsesReasoningItemForReplay(item, context, blockMetadata) {
1918
- const { [OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: rawMetadata, ...rest } = item;
1919
- if (!("encrypted_content" in rest)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
1920
- if (encryptedReasoningReplayMetadataMatches(blockMetadata ?? (isOpenAIResponsesReasoningReplayMetadata(rawMetadata) ? rawMetadata : void 0), context)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
1921
- return normalizeOpenAIResponsesReasoningReplayItem(stripEncryptedContentFields(rest).value);
1922
- }
1923
- async function createResponsesStreamWithEncryptedContentRetry(params) {
1924
- try {
1925
- return await params.client.responses.create(params.request, params.requestOptions);
1926
- } catch (error) {
1927
- const retryRequest = stripResponsesRequestEncryptedContent(params.request);
1928
- if (!isInvalidEncryptedContentError(error) || retryRequest === params.request) throw error;
1929
- log.warn(`[responses] retrying without encrypted reasoning content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
1930
- return await params.client.responses.create(retryRequest, params.requestOptions);
1931
- }
1932
- }
1933
- function resolveAzureOpenAIApiVersion(env = process.env) {
1934
- return env.AZURE_OPENAI_API_VERSION?.trim() || "preview";
1935
- }
1936
- function normalizeResponsesReplayItemId(id, prefix) {
1937
- if (!id) return;
1938
- if (id.length <= 64) return id;
1939
- return `${prefix}_${shortHash(id)}`;
1940
- }
1941
- function isSafeResponsesReplayItemId(id) {
1942
- return typeof id === "string" && id.length > 0 && id.length <= 64;
1943
- }
1944
- function encodeTextSignatureV1(id, phase) {
1945
- return JSON.stringify({
1946
- v: 1,
1947
- id,
1948
- ...phase ? { phase } : {}
1949
- });
1950
- }
1951
- function parseTextSignature(signature) {
1952
- if (!signature) return;
1953
- if (signature.startsWith("{")) try {
1954
- const parsed = JSON.parse(signature);
1955
- if (parsed.v === 1) {
1956
- const id = typeof parsed.id === "string" ? parsed.id : void 0;
1957
- const phase = parsed.phase === "commentary" || parsed.phase === "final_answer" ? parsed.phase : void 0;
1958
- if (id !== void 0 || phase !== void 0) return {
1959
- id,
1960
- phase
1961
- };
1962
- return;
1963
- }
1964
- } catch {}
1965
- return { id: signature };
1966
- }
1967
- function buildResponsesInputMessage(role, content) {
1968
- return {
1969
- type: "message",
1970
- role,
1971
- content
1972
- };
1973
- }
1974
- function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
1975
- const messages = [];
1976
- const shouldReplayReasoningItems = options?.replayReasoningItems ?? true;
1977
- const shouldReplayResponsesItemIds = options?.replayResponsesItemIds ?? true;
1978
- const replayContext = buildOpenAIResponsesReplayContext(model, {
1979
- sessionId: options?.sessionId,
1980
- authProfileId: options?.authProfileId
1981
- });
1982
- const shouldNormalizeSameModelToolCallIds = model.provider === "github-copilot";
1983
- const sanitizeIdPart = (part) => part.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+$/, "");
1984
- const normalizeIdPart = (part) => {
1985
- const sanitized = sanitizeIdPart(part);
1986
- return (sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized).replace(/_+$/, "");
1987
- };
1988
- const buildForeignResponsesItemId = (itemId) => {
1989
- const normalized = `fc_${shortHash(itemId)}`;
1990
- return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
1991
- };
1992
- const buildSameProviderCopilotResponsesItemId = (itemId) => {
1993
- const sanitized = sanitizeIdPart(itemId);
1994
- const candidate = sanitized.startsWith("fc_") ? sanitized : `fc_${sanitized}`;
1995
- return candidate.length > 64 ? buildForeignResponsesItemId(itemId) : candidate;
1996
- };
1997
- const normalizeToolCallId = (id, _targetModel, source) => {
1998
- if (!allowedToolCallProviders.has(model.provider)) return normalizeIdPart(id);
1999
- if (!id.includes("|")) return normalizeIdPart(id);
2000
- const separatorIndex = id.indexOf("|");
2001
- const callId = id.slice(0, separatorIndex);
2002
- const itemId = id.slice(separatorIndex + 1);
2003
- const normalizedCallId = normalizeIdPart(callId);
2004
- let normalizedItemId = source.provider !== model.provider || source.api !== model.api ? buildForeignResponsesItemId(itemId) : model.provider === "github-copilot" ? buildSameProviderCopilotResponsesItemId(itemId) : normalizeIdPart(itemId);
2005
- if (!normalizedItemId.startsWith("fc_")) normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
2006
- return `${normalizedCallId}|${normalizedItemId}`;
2007
- };
2008
- const transformedMessages = transformTransportMessages(context.messages, model, normalizeToolCallId, { normalizeSameModelToolCallIds: shouldNormalizeSameModelToolCallIds });
2009
- if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && options?.supportsDeveloperRole !== false ? "developer" : "system", [{
2010
- type: "input_text",
2011
- text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
2012
- }]));
2013
- let msgIndex = 0;
2014
- for (const msg of transformedMessages) {
2015
- if (msg.role === "user") if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
2016
- type: "input_text",
2017
- text: sanitizeTransportPayloadText(msg.content)
2018
- }]));
2019
- else {
2020
- const content = msg.content.map((item) => item.type === "text" ? {
2021
- type: "input_text",
2022
- text: sanitizeTransportPayloadText(item.text)
2023
- } : {
2024
- type: "input_image",
2025
- detail: "auto",
2026
- image_url: `data:${item.mimeType};base64,${item.data}`
2027
- }).filter((item) => model.input.includes("image") || item.type !== "input_image");
2028
- if (content.length > 0) messages.push(buildResponsesInputMessage("user", content));
2029
- }
2030
- else if (msg.role === "assistant") {
2031
- const output = [];
2032
- let textFallbackOrdinal = 0;
2033
- let previousReplayItemWasReasoning = false;
2034
- const isDifferentModel = msg.model !== model.id && msg.provider === model.provider && msg.api === model.api;
2035
- for (const block of msg.content) if (block.type === "thinking") {
2036
- if (shouldReplayReasoningItems && block.thinkingSignature && block.thinkingSignature.startsWith("{")) {
2037
- const replayableReasoningItem = prepareOpenAIResponsesReasoningItemForReplay(JSON.parse(block.thinkingSignature), replayContext, readOpenAIResponsesReasoningReplayBlockMetadata(block));
2038
- if (!shouldReplayResponsesItemIds) delete replayableReasoningItem.id;
2039
- if (shouldReplayResponsesItemIds && model.provider === "github-copilot" && !isSafeResponsesReplayItemId(replayableReasoningItem.id)) continue;
2040
- output.push(replayableReasoningItem);
2041
- previousReplayItemWasReasoning = true;
2042
- }
2043
- } else if (block.type === "text") {
2044
- const textSignature = parseTextSignature(block.textSignature);
2045
- let msgId = resolveReplayableResponsesMessageId({
2046
- replayResponsesItemIds: shouldReplayResponsesItemIds,
2047
- textSignatureId: textSignature?.id,
2048
- fallbackId: `msg_${msgIndex}`,
2049
- fallbackOrdinal: textFallbackOrdinal,
2050
- previousReplayItemWasReasoning
2051
- });
2052
- if (!textSignature?.id) textFallbackOrdinal += 1;
2053
- msgId = normalizeResponsesReplayItemId(msgId, "msg");
2054
- const messageItem = {
2055
- type: "message",
2056
- role: "assistant",
2057
- content: [{
2058
- type: "output_text",
2059
- text: sanitizeTransportPayloadText(block.text),
2060
- annotations: []
2061
- }],
2062
- status: "completed",
2063
- ...msgId ? { id: msgId } : {},
2064
- phase: textSignature?.phase
2065
- };
2066
- output.push(messageItem);
2067
- previousReplayItemWasReasoning = false;
2068
- } else if (block.type === "toolCall") {
2069
- const separatorIndex = block.id.indexOf("|");
2070
- const callId = separatorIndex === -1 ? block.id : block.id.slice(0, separatorIndex);
2071
- const itemIdRaw = separatorIndex === -1 ? void 0 : block.id.slice(separatorIndex + 1);
2072
- const itemId = shouldReplayResponsesItemIds && !(isDifferentModel && itemIdRaw?.startsWith("fc_")) ? itemIdRaw : void 0;
2073
- output.push({
2074
- type: "function_call",
2075
- ...itemId ? { id: itemId } : {},
2076
- call_id: callId,
2077
- name: block.name,
2078
- arguments: typeof block.arguments === "string" ? block.arguments : JSON.stringify(block.arguments ?? {})
2079
- });
2080
- previousReplayItemWasReasoning = false;
2081
- }
2082
- if (output.length > 0) messages.push(...output);
2083
- } else if (msg.role === "toolResult") {
2084
- const textResult = extractToolResultText(msg.content);
2085
- const sanitizedTextResult = sanitizeTransportPayloadText(textResult);
2086
- const hasText = sanitizedTextResult.trim().length > 0;
2087
- const mediaPlaceholder = describeToolResultMediaPlaceholder(msg.content);
2088
- const hasImages = msg.content.some(isImageWithMediaPayload);
2089
- const separatorIndex = msg.toolCallId.indexOf("|");
2090
- const callId = separatorIndex === -1 ? msg.toolCallId : msg.toolCallId.slice(0, separatorIndex);
2091
- messages.push({
2092
- type: "function_call_output",
2093
- call_id: callId,
2094
- output: hasImages && model.input.includes("image") ? [...hasText ? [{
2095
- type: "input_text",
2096
- text: sanitizedTextResult
2097
- }] : mediaPlaceholder === "(see attached media)" ? [{
2098
- type: "input_text",
2099
- text: mediaPlaceholder
2100
- }] : [], ...msg.content.filter(isImageWithMediaPayload).map((item) => ({
2101
- type: "input_image",
2102
- detail: "auto",
2103
- image_url: `data:${item.mimeType};base64,${item.data}`
2104
- }))] : sanitizeNonEmptyTransportPayloadText(textResult, mediaPlaceholder ?? "(no output)")
2105
- });
2106
- }
2107
- msgIndex += 1;
2108
- }
2109
- return messages;
2110
- }
2111
- //#endregion
2112
- //#region packages/ai/src/providers/openai-responses-stream-compat.ts
2113
- const OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE = "output_text";
2114
- const AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE = "text";
2115
- const OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE = "response.output_text.delta";
2116
- const AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE = "response.text.delta";
2117
- function isResponsesTextContentPartType(type) {
2118
- return type === "output_text" || type === "text";
2119
- }
2120
- function isResponsesTextDeltaEventType(type) {
2121
- return type === "response.output_text.delta" || type === "response.text.delta";
2122
- }
2123
- function isAzureResponsesTextDeltaEventType(type) {
2124
- return type === AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE;
2125
- }
2126
- function isAzureResponsesTextDeltaEvent(event) {
2127
- return isAzureResponsesTextDeltaEventType(event.type) && typeof event.delta === "string";
2128
- }
2129
- function resolveResponsesMessageSnapshotCollapse(params) {
2130
- const { prior, nextText } = params;
2131
- if (!prior?.text || !nextText || prior.phase !== params.nextPhase) return { kind: "keep" };
2132
- if (nextText.length > prior.text.length && nextText.startsWith(prior.text)) return {
2133
- kind: "extend",
2134
- text: nextText
2135
- };
2136
- return { kind: "keep" };
2137
- }
2138
- //#endregion
2139
- //#region packages/ai/src/providers/openai-responses-tool-call-tracker.ts
2140
- function readIdentityValue(value) {
2141
- return (typeof value === "string" ? value.trim() : "") || void 0;
2142
- }
2143
- function readOutputIndex(event) {
2144
- return typeof event.output_index === "number" && Number.isInteger(event.output_index) && event.output_index >= 0 ? event.output_index : void 0;
2145
- }
2146
- function readEventIdentity(event) {
2147
- return { itemId: readIdentityValue(event.item_id) };
2148
- }
2149
- function readResponsesToolCallItemIdentity(item) {
2150
- return {
2151
- itemId: readIdentityValue(item.id),
2152
- callId: readIdentityValue(item.call_id)
2153
- };
2154
- }
2155
- function createResponsesToolCallTracker() {
2156
- const indexedCalls = /* @__PURE__ */ new Map();
2157
- const unindexedCalls = /* @__PURE__ */ new Set();
2158
- const identitiesConflict = (state, identity) => Boolean(state.itemId && identity.itemId && state.itemId !== identity.itemId || state.callId && identity.callId && state.callId !== identity.callId);
2159
- const sharesIdentity = (state, identity) => Boolean(state.itemId && identity.itemId && state.itemId === identity.itemId || state.callId && identity.callId && state.callId === identity.callId);
2160
- const adoptIdentity = (state, identity) => {
2161
- state.itemId ??= identity.itemId;
2162
- state.callId ??= identity.callId;
2163
- return state;
2164
- };
2165
- const resolveCompatible = (candidates, identity) => {
2166
- const uniqueCandidates = [...new Set(candidates)];
2167
- if (!identity.itemId && !identity.callId) return uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
2168
- const compatible = uniqueCandidates.filter((state) => !identitiesConflict(state, identity));
2169
- const matches = compatible.filter((state) => sharesIdentity(state, identity));
2170
- const matched = matches.length === 1 ? matches.at(0) : void 0;
2171
- if (matched) return adoptIdentity(matched, identity);
2172
- const soleCompatible = uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
2173
- return soleCompatible ? adoptIdentity(soleCompatible, identity) : void 0;
2174
- };
2175
- return {
2176
- register(event, state) {
2177
- const outputIndex = readOutputIndex(event);
2178
- if (outputIndex === void 0) {
2179
- unindexedCalls.add(state);
2180
- return;
2181
- }
2182
- if (indexedCalls.has(outputIndex)) throw new Error(`Responses stream reused active tool-call output index ${outputIndex}`);
2183
- indexedCalls.set(outputIndex, state);
2184
- },
2185
- resolve(event, identity = readEventIdentity(event)) {
2186
- const outputIndex = readOutputIndex(event);
2187
- if (outputIndex !== void 0) {
2188
- const indexed = indexedCalls.get(outputIndex);
2189
- if (indexed) {
2190
- if (indexed.callId && identity.callId && indexed.callId !== identity.callId) return;
2191
- return adoptIdentity(indexed, identity);
2192
- }
2193
- const unindexed = resolveCompatible(unindexedCalls, identity);
2194
- if (unindexed) {
2195
- unindexedCalls.delete(unindexed);
2196
- indexedCalls.set(outputIndex, unindexed);
2197
- }
2198
- return unindexed;
2199
- }
2200
- return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity);
2201
- },
2202
- forget(toolCall) {
2203
- for (const [outputIndex, tracked] of indexedCalls) if (tracked === toolCall) indexedCalls.delete(outputIndex);
2204
- unindexedCalls.delete(toolCall);
2205
- },
2206
- markArgumentsUnreliable() {
2207
- for (const toolCall of /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls])) toolCall.argumentStreamReliable = false;
2208
- },
2209
- hasActive() {
2210
- return indexedCalls.size > 0 || unindexedCalls.size > 0;
2211
- }
2212
- };
2213
- }
2214
- //#endregion
2215
- //#region packages/ai/src/transports/openai-responses-stream-observer-internal.ts
2216
- const STRING_DELTA_EVENTS = /* @__PURE__ */ new Set([
2217
- "response.function_call_arguments.delta",
2218
- "response.output_text.delta",
2219
- "response.reasoning_summary_text.delta",
2220
- "response.reasoning_text.delta",
2221
- "response.refusal.delta",
2222
- "response.text.delta"
2223
- ]);
2224
- async function* adaptResponsesStream(stream, signal) {
2225
- const scheduler = createModelStreamCooperativeScheduler(signal);
2226
- for await (const event of stream) {
2227
- if (signal?.aborted) throw transportAbortError(signal);
2228
- if (!isRecord(event) || typeof event.type !== "string") throw new Error("Responses stream delivered a malformed event without a string type");
2229
- if (STRING_DELTA_EVENTS.has(event.type) && typeof event.delta !== "string") throw new Error(`Responses stream delivered malformed ${event.type} delta`);
2230
- if ((event.type === "response.output_item.added" || event.type === "response.output_item.done") && !isRecord(event.item)) throw new Error(`Responses stream delivered malformed ${event.type} item`);
2231
- if ((event.type === "response.created" || event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed") && !isRecord(event.response)) throw new Error(`Responses stream delivered malformed ${event.type} response`);
2232
- yield event;
2233
- await scheduler.afterEvent();
2234
- }
2235
- }
2236
- async function* observeResponsesStream(stream, model) {
2237
- const startedAt = Date.now();
2238
- const eventTypes = /* @__PURE__ */ new Map();
2239
- const debugMode = resolveModelSseDebugMode();
2240
- let eventCount = 0;
2241
- try {
2242
- for await (const event of stream) {
2243
- const type = isRecord(event) && typeof event.type === "string" ? event.type : "unknown";
2244
- eventCount += 1;
2245
- eventTypes.set(type, (eventTypes.get(type) ?? 0) + 1);
2246
- if (eventCount === 1) emitModelTransportDebug(log, `[responses] first_event provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - startedAt} type=${type}`);
2247
- if (debugMode === "peek" && eventCount <= 5) emitModelTransportDebug(log, `[responses] event_peek provider=${model.provider} api=${model.api} model=${model.id} index=${eventCount} type=${type} event=${stringifyRedactedEvent(event)}`);
2248
- yield event;
2249
- }
2250
- } finally {
2251
- const types = [...eventTypes].map(([type, count]) => `${type}:${count}`).join(",");
2252
- emitModelTransportDebug(log, `[responses] stream_done provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - startedAt} events=${eventCount} types=${types}`);
2253
- }
2254
- }
2255
- //#endregion
2256
- //#region packages/ai/src/transports/openai-responses-stream-slots-internal.ts
2257
- function readResponsesOutputIndex(event) {
2258
- const outputIndex = event.output_index;
2259
- return typeof outputIndex === "number" && Number.isInteger(outputIndex) && outputIndex >= 0 ? outputIndex : void 0;
2260
- }
2261
- function createResponsesOutputSlotTracker() {
2262
- const indexed = /* @__PURE__ */ new Map();
2263
- let unindexed;
2264
- return {
2265
- register(event, slot) {
2266
- const outputIndex = readResponsesOutputIndex(event);
2267
- if (outputIndex === void 0) {
2268
- if (unindexed) throw new Error("Responses stream added overlapping unindexed output items");
2269
- unindexed = slot;
2270
- return;
2271
- }
2272
- if (indexed.has(outputIndex)) throw new Error(`Responses stream reused active output index ${outputIndex}`);
2273
- indexed.set(outputIndex, slot);
2274
- },
2275
- resolve(event, type) {
2276
- const outputIndex = readResponsesOutputIndex(event);
2277
- let slot = outputIndex === void 0 ? unindexed : indexed.get(outputIndex);
2278
- if (outputIndex === void 0 && !slot) {
2279
- const matches = [...indexed.values()].filter((candidate) => candidate.type === type);
2280
- slot = matches.length === 1 ? matches[0] : void 0;
2281
- }
2282
- return slot?.type === type ? slot : void 0;
2283
- },
2284
- get(event) {
2285
- const outputIndex = readResponsesOutputIndex(event);
2286
- return outputIndex === void 0 ? unindexed : indexed.get(outputIndex);
2287
- },
2288
- values() {
2289
- return [.../* @__PURE__ */ new Set([...indexed.values(), ...unindexed ? [unindexed] : []])];
2290
- },
2291
- forget(slot) {
2292
- if (unindexed === slot) unindexed = void 0;
2293
- for (const [outputIndex, candidate] of indexed) if (candidate === slot) indexed.delete(outputIndex);
2294
- }
2295
- };
2296
- }
2297
- //#endregion
2298
- //#region packages/ai/src/providers/openai-responses-terminal-usage.ts
2299
- function readCount(value) {
2300
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
2301
- }
2302
- /**
2303
- * Split a terminal usage payload into the priced buckets.
2304
- *
2305
- * OpenAI includes cache reads and writes in `input_tokens`, so both are subtracted out of the
2306
- * billable input bucket. `total_tokens` comes from the payload, but never below the sum of the
2307
- * split buckets: proxies routinely omit it (reporting 0 would understate the turn), and a payload
2308
- * whose `cached_tokens` exceeds `input_tokens` clamps the input bucket, leaving the reported total
2309
- * short of what the buckets actually price.
2310
- */
2311
- function mapResponsesTerminalUsage(usage) {
2312
- if (!usage) return;
2313
- const cacheRead = readCount(usage.input_tokens_details?.cached_tokens);
2314
- const cacheWrite = readCount(usage.input_tokens_details?.cache_write_tokens);
2315
- const input = Math.max(0, readCount(usage.input_tokens) - cacheRead - cacheWrite);
2316
- const output = readCount(usage.output_tokens);
2317
- const bucketTotal = input + output + cacheRead + cacheWrite;
2318
- return {
2319
- input,
2320
- output,
2321
- cacheRead,
2322
- cacheWrite,
2323
- totalTokens: Math.max(bucketTotal, readCount(usage.total_tokens))
2324
- };
2325
- }
2326
- /** Reasoning tokens are reported by the agent path only; the package path does not track them. */
2327
- function readResponsesReasoningTokens(usage) {
2328
- const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens;
2329
- return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) ? reasoningTokens : void 0;
2330
- }
2331
- function mapResponsesTerminalStopReason(status) {
2332
- if (!status) return "stop";
2333
- switch (status) {
2334
- case "completed": return "stop";
2335
- case "incomplete": return "length";
2336
- case "failed":
2337
- case "cancelled": return "error";
2338
- case "in_progress":
2339
- case "queued": return "stop";
2340
- default: throw new Error(`Unhandled stop reason: ${String(status)}`);
2341
- }
2342
- }
2343
- /**
2344
- * Resolve the terminal stop reason, including the two overrides every Responses path shares: a
2345
- * content-filtered turn is a provider error rather than a truncated answer, and a turn that
2346
- * produced tool calls reports `toolUse` instead of a plain stop.
2347
- */
2348
- function resolveResponsesTerminalStopReason(params) {
2349
- const status = params.status ?? (params.terminalEventType === "response.incomplete" ? "incomplete" : void 0);
2350
- if (status === "incomplete" && params.incompleteReason === "content_filter") return {
2351
- stopReason: "error",
2352
- errorMessage: "Provider incomplete_reason: content_filter"
2353
- };
2354
- const stopReason = mapResponsesTerminalStopReason(status);
2355
- if (stopReason === "stop" && params.hasToolCall) return { stopReason: "toolUse" };
2356
- return { stopReason };
2357
- }
2358
- //#endregion
2359
- //#region packages/ai/src/transports/openai-responses-stream-terminal-internal.ts
2360
- function splitToolCallId(id) {
2361
- const separator = id.indexOf("|");
2362
- return separator === -1 ? [id, void 0] : [id.slice(0, separator), id.slice(separator + 1)];
2363
- }
2364
- function resolveResponsesToolCallId(item, fallbackId) {
2365
- const callId = typeof item.call_id === "string" ? item.call_id.trim() : "";
2366
- const itemId = typeof item.id === "string" ? item.id.trim() : "";
2367
- const [fallbackCallId, fallbackItemId = ""] = splitToolCallId(fallbackId ?? "");
2368
- const resolvedCallId = callId || fallbackCallId;
2369
- const resolvedItemId = itemId || fallbackItemId;
2370
- if (resolvedCallId) return resolvedItemId ? `${resolvedCallId}|${resolvedItemId}` : resolvedCallId;
2371
- const generated = `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`;
2372
- return resolvedItemId ? `${generated}|${resolvedItemId}` : generated;
2373
- }
2374
- function resolveCompletedToolCallName(toolCall, value) {
2375
- const streamedName = toolCall?.block.name.trim() || void 0;
2376
- const completedName = typeof value === "string" ? value.trim() || void 0 : void 0;
2377
- if (streamedName && completedName && streamedName !== completedName) throw new Error(`Responses stream changed tool-call function name from ${streamedName} to ${completedName}`);
2378
- const name = completedName ?? streamedName;
2379
- if (!name) throw new Error("Responses stream completed tool call without a function name");
2380
- return name;
2381
- }
2382
- function createResponsesTerminalController(params) {
2383
- const { output, stream, model, options } = params;
2384
- const blocks = output.content;
2385
- const backfillReasoning = (items) => {
2386
- for (const item of items) {
2387
- if (item.type !== "reasoning" || !item.encrypted_content) continue;
2388
- const block = params.reasoningBlocksById.get(item.id);
2389
- if (!block?.thinkingSignature) continue;
2390
- const stored = JSON.parse(block.thinkingSignature);
2391
- if (!stored.encrypted_content) block.thinkingSignature = JSON.stringify({
2392
- ...stored,
2393
- encrypted_content: item.encrypted_content
2394
- });
2395
- if (options?.reasoningReplayMetadata) block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = options.reasoningReplayMetadata;
2396
- }
2397
- };
2398
- const appendText = (item) => {
2399
- const text = (Array.isArray(item.content) ? item.content : []).map((part) => {
2400
- const content = part;
2401
- return content.type === "output_text" || content.type === "text" ? content.text ?? "" : content.refusal ?? "";
2402
- }).join("");
2403
- if (!text) return;
2404
- const phase = item.phase ?? void 0;
2405
- const previous = params.getLastTextBlock();
2406
- const collapse = resolveResponsesMessageSnapshotCollapse({
2407
- prior: previous && {
2408
- text: previous.block.text,
2409
- phase: previous.phase
2410
- },
2411
- nextText: text,
2412
- nextPhase: phase
2413
- });
2414
- if (collapse.kind === "extend" && previous) {
2415
- previous.block.text = collapse.text;
2416
- previous.block.textSignature = encodeTextSignatureV1(item.id, phase);
2417
- stream.push({
2418
- type: "text_end",
2419
- contentIndex: previous.index,
2420
- content: collapse.text,
2421
- partial: output
2422
- });
2423
- return;
2424
- }
2425
- const block = {
2426
- type: "text",
2427
- text,
2428
- textSignature: encodeTextSignatureV1(item.id, phase)
2429
- };
2430
- blocks.push(block);
2431
- const index = blocks.length - 1;
2432
- params.setLastTextBlock({
2433
- block,
2434
- index,
2435
- phase
2436
- });
2437
- stream.push({
2438
- type: "text_start",
2439
- contentIndex: index,
2440
- partial: output
2441
- });
2442
- stream.push({
2443
- type: "text_end",
2444
- contentIndex: index,
2445
- content: text,
2446
- partial: output
2447
- });
2448
- };
2449
- const appendToolCall = (item) => {
2450
- const toolCall = {
2451
- type: "toolCall",
2452
- id: resolveResponsesToolCallId(item),
2453
- name: resolveCompletedToolCallName(void 0, item.name),
2454
- arguments: parseStreamingJson(item.arguments || "{}")
2455
- };
2456
- blocks.push(toolCall);
2457
- const contentIndex = blocks.length - 1;
2458
- stream.push({
2459
- type: "toolcall_start",
2460
- contentIndex,
2461
- partial: output
2462
- });
2463
- stream.push({
2464
- type: "toolcall_end",
2465
- contentIndex,
2466
- toolCall,
2467
- partial: output
2468
- });
2469
- };
2470
- const recoverTerminalOutput = (items, includeToolCalls) => {
2471
- if (blocks.some((block) => block.type !== "thinking")) return;
2472
- for (const item of items) if (item.type === "message") appendText(item);
2473
- else {
2474
- params.setLastTextBlock(null);
2475
- if (includeToolCalls && item.type === "function_call") appendToolCall(item);
2476
- }
2477
- };
2478
- const finalizeResponse = (response, terminalEventType) => {
2479
- params.markFinalized();
2480
- backfillReasoning(response.output ?? []);
2481
- output.responseId = response.id || output.responseId;
2482
- const usage = mapResponsesTerminalUsage(response.usage);
2483
- const reasoningTokens = readResponsesReasoningTokens(response.usage);
2484
- if (usage) output.usage = {
2485
- ...usage,
2486
- ...reasoningTokens === void 0 ? {} : { reasoningTokens },
2487
- cost: {
2488
- input: 0,
2489
- output: 0,
2490
- cacheRead: 0,
2491
- cacheWrite: 0,
2492
- total: 0
2493
- }
2494
- };
2495
- calculateCost(model, output.usage);
2496
- if (options?.applyServiceTierPricing) {
2497
- const tier = options.resolveServiceTier ? options.resolveServiceTier(response.service_tier, options.serviceTier) : response.service_tier ?? options.serviceTier;
2498
- options.applyServiceTierPricing(output.usage, tier);
2499
- }
2500
- const terminal = resolveResponsesTerminalStopReason({
2501
- status: response.status,
2502
- terminalEventType,
2503
- incompleteReason: response.incomplete_details?.reason,
2504
- hasToolCall: blocks.some((block) => block.type === "toolCall")
2505
- });
2506
- output.stopReason = terminal.stopReason;
2507
- output.errorMessage = terminal.errorMessage;
2508
- };
2509
- return {
2510
- finalizeResponse,
2511
- recoverTerminalOutput
2512
- };
2513
- }
2514
- //#endregion
2515
- //#region packages/ai/src/transports/openai-responses-stream-internal.ts
2516
- var ResponsesStreamFailure = class extends Error {
2517
- constructor(failure, response) {
2518
- super(failure.message);
2519
- this.name = "ResponsesStreamFailure";
2520
- this.responseId = failure.responseId;
2521
- this.response = response;
2522
- this.observation = failure.observation;
2523
- }
2524
- };
2525
- async function processResponsesStream(openaiStream, output, stream, model, options) {
2526
- const streamingToolCalls = createResponsesToolCallTracker();
2527
- const outputSlots = createResponsesOutputSlotTracker();
2528
- const reasoningBlocksById = /* @__PURE__ */ new Map();
2529
- let terminalResponseEvent;
2530
- let lastTextBlock = null;
2531
- const blocks = output.content;
2532
- const blockIndex = () => blocks.length - 1;
2533
- const createOutputSlot = (event, item) => {
2534
- if (item.type === "reasoning") {
2535
- const block = {
2536
- type: "thinking",
2537
- thinking: ""
2538
- };
2539
- const slot = {
2540
- type: "thinking",
2541
- item,
2542
- block,
2543
- contentIndex: blocks.length
2544
- };
2545
- blocks.push(block);
2546
- outputSlots.register(event, slot);
2547
- stream.push({
2548
- type: "thinking_start",
2549
- contentIndex: slot.contentIndex,
2550
- partial: output
2551
- });
2552
- return slot;
2553
- }
2554
- if (item.type === "message") {
2555
- const messageItem = item;
2556
- const collapseCandidate = lastTextBlock;
2557
- const block = collapseCandidate ? null : {
2558
- type: "text",
2559
- text: "",
2560
- ...messageItem.phase ? { textSignature: encodeTextSignatureV1(messageItem.id, messageItem.phase) } : {}
2561
- };
2562
- const slot = {
2563
- type: "text",
2564
- item: messageItem,
2565
- block,
2566
- contentIndex: block ? blocks.length : void 0,
2567
- pendingText: collapseCandidate ? "" : null,
2568
- collapseCandidate
2569
- };
2570
- if (block) blocks.push(block);
2571
- outputSlots.register(event, slot);
2572
- if (slot.contentIndex !== void 0) stream.push({
2573
- type: "text_start",
2574
- contentIndex: slot.contentIndex,
2575
- partial: output
2576
- });
2577
- return slot;
2578
- }
2579
- };
2580
- const resolveOutputItemSlot = (event, item) => {
2581
- if (item.type === "reasoning") return outputSlots.resolve(event, "thinking");
2582
- if (item.type === "message") return outputSlots.resolve(event, "text");
2583
- return readResponsesOutputIndex(event) === void 0 ? void 0 : outputSlots.get(event);
2584
- };
2585
- const getOrCreateOutputSlot = (event, item) => {
2586
- return resolveOutputItemSlot(event, item) ?? createOutputSlot(event, item);
2587
- };
2588
- const materializeDeferredTextSlot = (slot) => {
2589
- if (slot.block || slot.pendingText === null) return;
2590
- const text = slot.pendingText;
2591
- slot.block = {
2592
- type: "text",
2593
- text,
2594
- ...slot.item.phase ? { textSignature: encodeTextSignatureV1(slot.item.id, slot.item.phase) } : {}
2595
- };
2596
- blocks.push(slot.block);
2597
- slot.contentIndex = blockIndex();
2598
- stream.push({
2599
- type: "text_start",
2600
- contentIndex: slot.contentIndex,
2601
- partial: output
2602
- });
2603
- if (text) stream.push({
2604
- type: "text_delta",
2605
- contentIndex: slot.contentIndex,
2606
- delta: text
2607
- });
2608
- if (lastTextBlock === slot.collapseCandidate) lastTextBlock = null;
2609
- slot.pendingText = null;
2610
- slot.collapseCandidate = null;
2611
- };
2612
- const materializeDeferredTextSlots = (except) => {
2613
- for (const slot of outputSlots.values()) if (slot !== except && slot.type === "text") materializeDeferredTextSlot(slot);
2614
- };
2615
- const appendPendingMessageDelta = (slot, delta) => {
2616
- slot.pendingText = `${slot.pendingText ?? ""}${delta}`;
2617
- const priorText = slot.collapseCandidate?.block.text ?? "";
2618
- if (priorText.startsWith(slot.pendingText) || slot.pendingText.startsWith(priorText)) return;
2619
- materializeDeferredTextSlot(slot);
2620
- };
2621
- const { finalizeResponse, recoverTerminalOutput } = createResponsesTerminalController({
2622
- output,
2623
- stream,
2624
- model,
2625
- options,
2626
- reasoningBlocksById,
2627
- getLastTextBlock: () => lastTextBlock,
2628
- setLastTextBlock: (block) => {
2629
- lastTextBlock = block;
2630
- },
2631
- markFinalized: () => {
2632
- terminalResponseEvent = "finalized";
2633
- }
2634
- });
2635
- const guardedStream = adaptResponsesStream(withFirstStreamEventTimeout(openaiStream, {
2636
- provider: model.provider,
2637
- api: model.api,
2638
- model: model.id,
2639
- timeoutMs: options?.firstEventTimeoutMs ?? 0,
2640
- stage: "responses",
2641
- abort: options?.abortFirstEventStream,
2642
- onTimeout: options?.onFirstEventTimeout,
2643
- hint: "The provider may be stalled while parsing the tool payload; retry with a smaller tool surface or enable OPENCLAW_DEBUG_MODEL_PAYLOAD=tools to inspect exposed tools."
2644
- }), options?.signal);
2645
- try {
2646
- for await (const event of guardedStream) if (event.type === "response.created") output.responseId = event.response.id;
2647
- else if (event.type === "response.output_item.added") {
2648
- materializeDeferredTextSlots();
2649
- const item = event.item;
2650
- if (item.type !== "message") lastTextBlock = null;
2651
- if (item.type === "reasoning" || item.type === "message") createOutputSlot(event, item);
2652
- else if (item.type === "function_call") {
2653
- const toolCallBlock = {
2654
- type: "toolCall",
2655
- id: resolveResponsesToolCallId(item),
2656
- name: typeof item.name === "string" ? item.name.trim() : "",
2657
- arguments: {},
2658
- partialJson: item.arguments || ""
2659
- };
2660
- const contentIndex = output.content.length;
2661
- const toolCallState = {
2662
- block: toolCallBlock,
2663
- contentIndex,
2664
- argumentStreamReliable: true,
2665
- ...readResponsesToolCallItemIdentity(item)
2666
- };
2667
- streamingToolCalls.register(event, toolCallState);
2668
- if (readResponsesOutputIndex(event) !== void 0) outputSlots.register(event, {
2669
- type: "toolCall",
2670
- toolCall: toolCallState
2671
- });
2672
- output.content.push(toolCallBlock);
2673
- stream.push({
2674
- type: "toolcall_start",
2675
- contentIndex,
2676
- partial: output
2677
- });
2678
- }
2679
- } else if (event.type === "response.reasoning_summary_part.added") {
2680
- const slot = outputSlots.resolve(event, "thinking");
2681
- if (!slot) continue;
2682
- slot.item.summary = slot.item.summary || [];
2683
- slot.item.summary.push(event.part);
2684
- } else if (event.type === "response.reasoning_summary_text.delta") {
2685
- const slot = outputSlots.resolve(event, "thinking");
2686
- if (!slot) continue;
2687
- slot.item.summary = slot.item.summary || [];
2688
- const lastPart = slot.item.summary[slot.item.summary.length - 1];
2689
- if (!lastPart) continue;
2690
- slot.block.thinking += event.delta;
2691
- lastPart.text += event.delta;
2692
- stream.push({
2693
- type: "thinking_delta",
2694
- contentIndex: slot.contentIndex,
2695
- delta: event.delta,
2696
- partial: output
2697
- });
2698
- } else if (event.type === "response.reasoning_summary_part.done") {
2699
- const slot = outputSlots.resolve(event, "thinking");
2700
- if (!slot) continue;
2701
- slot.item.summary = slot.item.summary || [];
2702
- const lastPart = slot.item.summary[slot.item.summary.length - 1];
2703
- if (!lastPart) continue;
2704
- slot.block.thinking += "\n\n";
2705
- lastPart.text += "\n\n";
2706
- stream.push({
2707
- type: "thinking_delta",
2708
- contentIndex: slot.contentIndex,
2709
- delta: "\n\n",
2710
- partial: output
2711
- });
2712
- } else if (event.type === "response.reasoning_text.delta") {
2713
- const slot = outputSlots.resolve(event, "thinking");
2714
- if (!slot) continue;
2715
- slot.block.thinking += event.delta;
2716
- stream.push({
2717
- type: "thinking_delta",
2718
- contentIndex: slot.contentIndex,
2719
- delta: event.delta,
2720
- partial: output
2721
- });
2722
- } else if (event.type === "response.content_part.added") {
2723
- const slot = outputSlots.resolve(event, "text");
2724
- if (!slot) continue;
2725
- slot.item.content = slot.item.content || [];
2726
- if (event.part.type === "output_text" || event.part.type === "text" || event.part.type === "refusal") slot.item.content.push(event.part);
2727
- } else if (event.type === "response.output_text.delta") {
2728
- const slot = outputSlots.resolve(event, "text");
2729
- if (!slot) continue;
2730
- slot.item.content ||= [];
2731
- let lastPart = slot.item.content[slot.item.content.length - 1];
2732
- if (!isResponsesTextContentPartType(lastPart?.type)) {
2733
- lastPart = {
2734
- type: "output_text",
2735
- text: "",
2736
- annotations: []
2737
- };
2738
- slot.item.content.push(lastPart);
2739
- }
2740
- lastPart.text += event.delta;
2741
- if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
2742
- else if (slot.block && slot.contentIndex !== void 0) {
2743
- slot.block.text += event.delta;
2744
- stream.push({
2745
- type: "text_delta",
2746
- contentIndex: slot.contentIndex,
2747
- delta: event.delta
2748
- });
2749
- }
2750
- } else if (isAzureResponsesTextDeltaEvent(event)) {
2751
- const slot = outputSlots.resolve(event, "text");
2752
- if (!slot) continue;
2753
- slot.item.content = slot.item.content || [];
2754
- let lastPart = slot.item.content[slot.item.content.length - 1];
2755
- if (lastPart?.type !== "text") {
2756
- lastPart = {
2757
- type: "text",
2758
- text: ""
2759
- };
2760
- slot.item.content.push(lastPart);
2761
- }
2762
- lastPart.text += event.delta;
2763
- if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
2764
- else if (slot.block && slot.contentIndex !== void 0) {
2765
- slot.block.text += event.delta;
2766
- stream.push({
2767
- type: "text_delta",
2768
- contentIndex: slot.contentIndex,
2769
- delta: event.delta
2770
- });
2771
- }
2772
- } else if (event.type === "response.refusal.delta") {
2773
- const slot = outputSlots.resolve(event, "text");
2774
- if (!slot) continue;
2775
- slot.item.content ||= [];
2776
- let lastPart = slot.item.content[slot.item.content.length - 1];
2777
- if (lastPart?.type !== "refusal") {
2778
- lastPart = {
2779
- type: "refusal",
2780
- refusal: ""
2781
- };
2782
- slot.item.content.push(lastPart);
2783
- }
2784
- lastPart.refusal += event.delta;
2785
- if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
2786
- else if (slot.block && slot.contentIndex !== void 0) {
2787
- slot.block.text += event.delta;
2788
- stream.push({
2789
- type: "text_delta",
2790
- contentIndex: slot.contentIndex,
2791
- delta: event.delta
2792
- });
2793
- }
2794
- } else if (event.type === "response.function_call_arguments.delta") {
2795
- const toolCall = streamingToolCalls.resolve(event);
2796
- if (toolCall) {
2797
- toolCall.block.partialJson += event.delta;
2798
- toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
2799
- stream.push({
2800
- type: "toolcall_delta",
2801
- contentIndex: toolCall.contentIndex,
2802
- delta: event.delta,
2803
- partial: output
2804
- });
2805
- } else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
2806
- } else if (event.type === "response.function_call_arguments.done") {
2807
- const toolCall = streamingToolCalls.resolve(event);
2808
- if (toolCall) {
2809
- const previousPartialJson = toolCall.block.partialJson;
2810
- const doneArguments = typeof event.arguments === "string" ? event.arguments : void 0;
2811
- if (doneArguments !== void 0 && (doneArguments.length > 0 || previousPartialJson === "")) {
2812
- toolCall.block.partialJson = doneArguments;
2813
- toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
2814
- toolCall.argumentStreamReliable = true;
2815
- }
2816
- if (doneArguments?.startsWith(previousPartialJson)) {
2817
- const delta = doneArguments.slice(previousPartialJson.length);
2818
- if (delta.length > 0) stream.push({
2819
- type: "toolcall_delta",
2820
- contentIndex: toolCall.contentIndex,
2821
- delta,
2822
- partial: output
2823
- });
2824
- }
2825
- } else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
2826
- } else if (event.type === "response.output_item.done") {
2827
- const item = event.item;
2828
- if (item.type !== "message") lastTextBlock = null;
2829
- const existingOutputSlot = resolveOutputItemSlot(event, item);
2830
- materializeDeferredTextSlots(existingOutputSlot);
2831
- const outputSlot = existingOutputSlot ?? getOrCreateOutputSlot(event, item);
2832
- if (item.type === "reasoning" && outputSlot?.type === "thinking") {
2833
- const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
2834
- const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
2835
- outputSlot.block.thinking = summaryText || contentText || outputSlot.block.thinking;
2836
- outputSlot.block.thinkingSignature = JSON.stringify(item);
2837
- if (item.encrypted_content && options?.reasoningReplayMetadata) outputSlot.block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = options.reasoningReplayMetadata;
2838
- if (typeof item.id === "string") reasoningBlocksById.set(item.id, outputSlot.block);
2839
- stream.push({
2840
- type: "thinking_end",
2841
- contentIndex: outputSlot.contentIndex,
2842
- content: outputSlot.block.thinking,
2843
- partial: output
2844
- });
2845
- outputSlots.forget(outputSlot);
2846
- } else if (item.type === "message" && outputSlot?.type === "text" && (outputSlot.block || outputSlot.pendingText !== null)) {
2847
- const streamedText = outputSlot.pendingText ?? outputSlot.block?.text ?? "";
2848
- const finalText = item.content == null ? streamedText : item.content.map((c) => c.type === "output_text" || c.type === "text" ? c.text : c.refusal).join("");
2849
- const phase = item.phase ?? void 0;
2850
- const collapse = outputSlot.pendingText !== null ? resolveResponsesMessageSnapshotCollapse({
2851
- prior: outputSlot.collapseCandidate && {
2852
- text: outputSlot.collapseCandidate.block.text,
2853
- phase: outputSlot.collapseCandidate.phase
2854
- },
2855
- nextText: finalText,
2856
- nextPhase: phase
2857
- }) : { kind: "keep" };
2858
- outputSlot.pendingText = null;
2859
- if (collapse.kind === "extend" && outputSlot.collapseCandidate) {
2860
- outputSlot.collapseCandidate.block.text = collapse.text;
2861
- outputSlot.collapseCandidate.block.textSignature = encodeTextSignatureV1(item.id, phase);
2862
- stream.push({
2863
- type: "text_end",
2864
- contentIndex: outputSlot.collapseCandidate.index,
2865
- content: collapse.text,
2866
- partial: output
2867
- });
2868
- lastTextBlock = outputSlot.collapseCandidate;
2869
- } else {
2870
- if (!outputSlot.block) {
2871
- outputSlot.block = {
2872
- type: "text",
2873
- text: "",
2874
- ...phase ? { textSignature: encodeTextSignatureV1(item.id, phase) } : {}
2875
- };
2876
- blocks.push(outputSlot.block);
2877
- outputSlot.contentIndex = blockIndex();
2878
- stream.push({
2879
- type: "text_start",
2880
- contentIndex: outputSlot.contentIndex,
2881
- partial: output
2882
- });
2883
- }
2884
- outputSlot.block.text = finalText;
2885
- outputSlot.block.textSignature = encodeTextSignatureV1(item.id, phase);
2886
- const contentIndex = outputSlot.contentIndex;
2887
- if (contentIndex === void 0) throw new Error("Responses stream finalized text without a content index");
2888
- lastTextBlock = {
2889
- block: outputSlot.block,
2890
- index: contentIndex,
2891
- phase
2892
- };
2893
- stream.push({
2894
- type: "text_end",
2895
- contentIndex,
2896
- content: outputSlot.block.text,
2897
- partial: output
2898
- });
2899
- }
2900
- outputSlots.forget(outputSlot);
2901
- } else if (item.type === "function_call") {
2902
- const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
2903
- if (!streamingToolCall && streamingToolCalls.hasActive()) continue;
2904
- const completedName = resolveCompletedToolCallName(streamingToolCall, item.name);
2905
- const streamedArguments = streamingToolCall?.block.partialJson ?? "";
2906
- const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
2907
- if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) continue;
2908
- const args = parseStreamingJson(completedArguments !== void 0 && (completedArguments.length > 0 || !streamedArguments) ? completedArguments : streamedArguments || "{}");
2909
- let toolCall;
2910
- let contentIndex;
2911
- if (streamingToolCall) {
2912
- const block = streamingToolCall.block;
2913
- block.id = resolveResponsesToolCallId(item, block.id);
2914
- block.name = completedName;
2915
- block.arguments = args;
2916
- delete block.partialJson;
2917
- toolCall = block;
2918
- contentIndex = streamingToolCall.contentIndex;
2919
- } else {
2920
- toolCall = {
2921
- type: "toolCall",
2922
- id: resolveResponsesToolCallId(item),
2923
- name: completedName,
2924
- arguments: args
2925
- };
2926
- blocks.push(toolCall);
2927
- contentIndex = blockIndex();
2928
- stream.push({
2929
- type: "toolcall_start",
2930
- contentIndex,
2931
- partial: output
2932
- });
2933
- }
2934
- if (streamingToolCall) {
2935
- streamingToolCalls.forget(streamingToolCall);
2936
- for (const slot of outputSlots.values()) if (slot.type === "toolCall" && slot.toolCall === streamingToolCall) outputSlots.forget(slot);
2937
- }
2938
- stream.push({
2939
- type: "toolcall_end",
2940
- contentIndex,
2941
- toolCall,
2942
- partial: output
2943
- });
2944
- }
2945
- } else if (event.type === "response.completed" || event.type === "response.incomplete") {
2946
- if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
2947
- finalizeResponse(event.response, event.type);
2948
- if (event.type === "response.completed" || output.stopReason === "length") recoverTerminalOutput(event.response.output ?? [], event.type === "response.completed");
2949
- if (output.stopReason === "stop" && output.content.some((block) => block.type === "toolCall")) output.stopReason = "toolUse";
2950
- break;
2951
- } else if (event.type === "error") throw new Error(event.message ? `Error Code ${event.code}: ${event.message}` : "Unknown error");
2952
- else if (event.type === "response.failed") {
2953
- const failure = normalizeResponsesFailedEvent(event, model);
2954
- if (failure.responseId) output.responseId = failure.responseId;
2955
- throw new ResponsesStreamFailure(failure, event.response);
2956
- }
2957
- if (streamingToolCalls.hasActive()) throw new Error("Responses stream ended with unresolved tool calls");
2958
- if (!terminalResponseEvent) throw new Error("OpenAI Responses stream ended before a terminal response event");
2959
- } finally {
2960
- for (const block of output.content) delete block.partialJson;
2961
- }
2962
- }
2963
- //#endregion
2964
- export { normalizeOpenAIStrictCompatSchema as $, normalizeResponsesFailedEvent as A, resolveReplayableResponsesMessageId as B, prepareOpenAIResponsesReasoningItemForReplay as C, applyServiceTierPricing as D, tagOpenAIResponsesReasoningReplayItem as E, summarizeResponsesFailedNoDetailsObservation as F, throwIfModelStreamAborted as G, createModelStreamCooperativeScheduler as H, summarizeResponsesPayload as I, isStrictOpenAIJsonSchemaCompatible as J, clearOpenAIToolSchemaCacheForTest as K, summarizeResponsesTools as L, stringifyRedactedEvent as M, stringifyRedactedPayload as N, buildResponsesFailedNoDetailsObservation as O, summarizeOpenAITransportError as P, findOpenAIStrictSchemaViolations as Q, AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS as R, isInvalidEncryptedContentError as S, stripResponsesRequestEncryptedContent as T, log as U, GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP as V, resolvePromptCacheKey as W, normalizeStrictOpenAIJsonSchema as X, normalizeOpenAIStrictToolParameters as Y, resolveOpenAIProjectedToolsStrictToolFlag as Z, resolveResponsesMessageSnapshotCollapse as _, supportsOpenAITemperature as _t, resolveResponsesTerminalStopReason as a, LLAMACPP_GBNF_MAX_REPETITION_THRESHOLD as at, convertResponsesMessages as b, resolveModelPayloadDebugMode as bt, readResponsesToolCallItemIdentity as c, GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS as ct, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as d, isOpenAIGpt55Model as dt, extractToolSchemaModelCompat as et, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as f, isOpenAIGpt56Model as ft, isResponsesTextDeltaEventType as g, supportsOpenAIReasoningEffort as gt, isResponsesTextContentPartType as h, resolveOpenAISupportedReasoningEfforts as ht, readResponsesReasoningTokens as i, stripUnsupportedSchemaKeywords as it, safeDebugValue as j, logResponsesFailedNoDetails as k, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as l, cleanSchemaForGemini as lt, isAzureResponsesTextDeltaEventType as m, resolveOpenAIReasoningEffortForModel as mt, processResponsesStream as n, resolveUnsupportedToolSchemaKeywords as nt, observeResponsesStream as o, cleanSchemaForLlamacppGbnf as ot, isAzureResponsesTextDeltaEvent as p, normalizeOpenAIReasoningEffort as pt, findOpenAIStrictToolProjectionDiagnostics as q, mapResponsesTerminalUsage as r, shouldOmitEmptyArrayItems as rt, createResponsesToolCallTracker as s, findLlamacppGbnfSchemaViolations as st, ResponsesStreamFailure as t, normalizeToolParameterSchema as tt, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as u, isOpenAIGpt54MiniModel as ut, buildOpenAIResponsesReasoningReplayMetadata as v, uniqueStrings as vt, resolveAzureOpenAIApiVersion as w, createResponsesStreamWithEncryptedContentRetry as x, resolveModelSseDebugMode as xt, buildResponsesInputMessage as y, emitModelTransportDebug as yt, OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS as z };