@openclaw/ai 2026.7.2-beta.3 → 2026.7.2-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/{anthropic-D7SyZQ5v.mjs → anthropic-CQVj3le6.mjs} +174 -33
- package/dist/{index-BoTnz8cv.d.mts → anthropic-SrGtwsJu.d.mts} +27 -1
- package/dist/{api-registry-DXBIXISf.d.mts → api-registry-DlMgPR39.d.mts} +1 -2
- package/dist/{azure-openai-responses-BkZgyZ3I.mjs → azure-openai-responses-DIYgsqFM.mjs} +7 -11
- package/dist/{env-api-keys-DnhYpc27.mjs → env-api-keys-DrgeBuva.mjs} +1 -1
- package/dist/error-coercion-DgxlWC0n.mjs +15 -0
- package/dist/{event-stream-BPemFrFW.d.mts → event-stream-YjaPW20U.d.mts} +1 -1
- package/dist/event-stream.d.mts +1 -1
- package/dist/{tool-schema-json-projection-BwNu3nDi.mjs → github-copilot-headers-BCoBNmL7.mjs} +48 -1
- package/dist/{google-C_biGbV7.mjs → google-f-A8xrae.mjs} +4 -4
- package/dist/{google-shared-BK-NoHl8.mjs → google-shared-J6qvYINH.mjs} +5 -4
- package/dist/{google-vertex-CvdzF1U7.mjs → google-vertex-D3yMVXIY.mjs} +3 -3
- package/dist/host-B9GUmcra.d.mts +173 -0
- package/dist/host-XYGZcgO8.mjs +98 -0
- package/dist/index-BVVgDSdq.d.mts +1 -0
- package/dist/index.d.mts +7 -50
- package/dist/index.mjs +3 -3
- package/dist/internal/anthropic.d.mts +15 -11
- package/dist/internal/anthropic.mjs +4 -4
- package/dist/internal/openai.d.mts +3 -280
- package/dist/internal/openai.mjs +5 -7
- package/dist/internal/runtime.d.mts +13 -34
- package/dist/internal/runtime.mjs +19 -11
- package/dist/internal/shared.d.mts +13 -8
- package/dist/internal/shared.mjs +2 -2
- package/dist/{mistral-BdrQmDWp.mjs → mistral-CKV-TOQj.mjs} +7 -6
- package/dist/number-coercion-DvG7SNMg.mjs +129 -0
- package/dist/openai-BPor_3WI.d.mts +358 -0
- package/dist/{openai-responses-shared-CwnGU0KW.mjs → openai-D3PD6PE-.mjs} +1890 -705
- package/dist/{openai-chatgpt-responses-DP63Qt01.mjs → openai-chatgpt-responses-CedIj0hk.mjs} +54 -58
- package/dist/{openai-completions-zLDfebnN.mjs → openai-completions-CiSutyu0.mjs} +459 -104
- package/dist/openai-prompt-cache-2uo_1OR1.d.mts +7 -0
- package/dist/openai-prompt-cache-mZTCdRPo.mjs +12 -0
- package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +13 -0
- package/dist/{provider-error-LMfTEkfO.mjs → provider-error-apVOZI6G.mjs} +9 -2
- package/dist/providers.d.mts +3 -2
- package/dist/providers.mjs +10 -9
- package/dist/{transform-messages-eb6lxDLB.mjs → shared-CdjNZd35.mjs} +273 -161
- package/dist/{src-CXno1H5g.mjs → src-QkygScBs.mjs} +23 -4
- package/dist/stream-first-event-timeout-C3OgBjIk.mjs +12294 -0
- package/dist/stream-first-event-timeout-DvDeSucC.d.mts +29 -0
- package/dist/transport-stream-shared-BbMELSI4.mjs +297 -0
- package/dist/transports.d.mts +593 -0
- package/dist/transports.mjs +4069 -0
- package/dist/{types-CYORveZ7.d.mts → types-bzp5k29J.d.mts} +8 -0
- package/dist/types.d.mts +5 -5
- package/dist/types.mjs +2 -2
- package/dist/{validation-BX8euiDv.d.mts → validation-B-j7cOYp.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/package.json +36 -11
- package/dist/azure-openai-responses-client-compat-C7K7QfUE.mjs +0 -62
- package/dist/github-copilot-headers-BsH5cqGj.mjs +0 -48
- package/dist/host-4t713IeR.mjs +0 -37
- package/dist/model-utils-1GiZ2_rr.mjs +0 -64
- package/dist/openai-responses-ClspWt4I.mjs +0 -136
- package/dist/openai-tool-projection-WHf2nuOI.mjs +0 -200
- package/dist/reasoning-tag-text-partitioner-BlVe67g6.mjs +0 -400
- package/dist/stream-first-event-timeout-RjWszj8c.mjs +0 -106
- package/npm-shrinkwrap.json +0 -645
|
@@ -1,13 +1,57 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
1
|
+
import { n as getEnvApiKey, r as __exportAll } from "./env-api-keys-DrgeBuva.mjs";
|
|
2
|
+
import { t as AssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
|
|
3
|
+
import { n as getAiTransportHost } from "./host-XYGZcgO8.mjs";
|
|
4
|
+
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
5
|
+
import { A as buildBaseOptions, D as truncateUtf16Safe, E as isImageWithMediaPayload, O as isRecord, S as describeToolResultMediaPlaceholder, b as normalizeOptionalString, l as sortPromptCacheToolsByName, o as stripSystemPromptCacheBoundary, u as transformMessages, w as extractToolResultText, y as normalizeLowercaseStringOrEmpty } from "./shared-CdjNZd35.mjs";
|
|
6
|
+
import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
|
|
7
|
+
import { C as transformTransportMessages, a as projectOpenAITools } from "./openai-completions-CiSutyu0.mjs";
|
|
8
|
+
import { c as calculateCost, l as clampThinkingLevel } from "./number-coercion-DvG7SNMg.mjs";
|
|
9
|
+
import { b as redactIdentifier, d as transportAbortError, l as sanitizeNonEmptyTransportPayloadText, u as sanitizeTransportPayloadText, x as redactSensitiveText } from "./transport-stream-shared-BbMELSI4.mjs";
|
|
3
10
|
import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
|
|
4
11
|
import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
|
|
5
|
-
import { t as
|
|
6
|
-
import { D as normalizeLowercaseStringOrEmpty, O as normalizeOptionalString, f as isRecord, i as extractToolResultText, n as describeToolResultMediaPlaceholder, o as isImageWithMediaPayload, t as transformMessages, w as stripSystemPromptCacheBoundary } from "./transform-messages-eb6lxDLB.mjs";
|
|
7
|
-
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-RjWszj8c.mjs";
|
|
8
|
-
import { t as projectOpenAITools } from "./openai-tool-projection-WHf2nuOI.mjs";
|
|
12
|
+
import { a as resolveCacheRetention$1, i as resolveCloudflareBaseUrl, n as hasCopilotVisionInput, r as isCloudflareProvider, t as buildCopilotDynamicHeaders } from "./github-copilot-headers-BCoBNmL7.mjs";
|
|
9
13
|
import { t as shortHash } from "./hash-CHgqbJmD.mjs";
|
|
14
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-C3OgBjIk.mjs";
|
|
10
15
|
import { createHash, randomUUID } from "node:crypto";
|
|
16
|
+
import OpenAI from "openai";
|
|
17
|
+
//#region packages/ai/src/transports/model-transport-debug.ts
|
|
18
|
+
function normalizeEnv(value) {
|
|
19
|
+
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
20
|
+
}
|
|
21
|
+
function isTruthyEnv(value) {
|
|
22
|
+
const normalized = normalizeEnv(value);
|
|
23
|
+
return normalized.length > 0 && normalized !== "0" && normalized !== "false" && normalized !== "off" && normalized !== "no";
|
|
24
|
+
}
|
|
25
|
+
/** Resolves model payload debug verbosity from `OPENCLAW_DEBUG_MODEL_PAYLOAD`. */
|
|
26
|
+
function resolveModelPayloadDebugMode(env = process.env) {
|
|
27
|
+
const normalized = normalizeEnv(env.OPENCLAW_DEBUG_MODEL_PAYLOAD);
|
|
28
|
+
if (normalized === "tools" || normalized === "full-redacted") return normalized;
|
|
29
|
+
if (normalized === "summary") return "summary";
|
|
30
|
+
return "off";
|
|
31
|
+
}
|
|
32
|
+
/** Resolves SSE stream debug verbosity from `OPENCLAW_DEBUG_SSE`. */
|
|
33
|
+
function resolveModelSseDebugMode(env = process.env) {
|
|
34
|
+
const normalized = normalizeEnv(env.OPENCLAW_DEBUG_SSE);
|
|
35
|
+
if (normalized === "peek") return "peek";
|
|
36
|
+
if (normalized === "events" || isTruthyEnv(normalized)) return "events";
|
|
37
|
+
return "off";
|
|
38
|
+
}
|
|
39
|
+
/** Returns whether any model transport debug channel is enabled. */
|
|
40
|
+
function isModelTransportDebugEnabled(env = process.env) {
|
|
41
|
+
return isTruthyEnv(env.OPENCLAW_DEBUG_MODEL_TRANSPORT) || resolveModelPayloadDebugMode(env) !== "off" || resolveModelSseDebugMode(env) !== "off" || isTruthyEnv(env.OPENCLAW_DEBUG_CODE_MODE);
|
|
42
|
+
}
|
|
43
|
+
function isModelFetchMetadataMessage(message) {
|
|
44
|
+
return message.startsWith("[model-fetch]");
|
|
45
|
+
}
|
|
46
|
+
/** Emits model-fetch metadata at info level by default; other diagnostics require debug env. */
|
|
47
|
+
function emitModelTransportDebug(log, message) {
|
|
48
|
+
if (isModelFetchMetadataMessage(message) || isModelTransportDebugEnabled()) {
|
|
49
|
+
log.info(message);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
log.debug(message);
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
11
55
|
//#region packages/normalization-core/src/string-normalization.ts
|
|
12
56
|
/** Coerces entries to strings, trims them, and drops empty results. */
|
|
13
57
|
function normalizeStringEntries(list) {
|
|
@@ -896,6 +940,67 @@ function normalizeToolParameterSchema(schema, options) {
|
|
|
896
940
|
return rememberCachedToolParameterSchema(schema, cacheKey, normalizeToolParameterSchemaUncached(schema, options));
|
|
897
941
|
}
|
|
898
942
|
//#endregion
|
|
943
|
+
//#region packages/ai/src/providers/azure-deployment-map.ts
|
|
944
|
+
/** Parses AZURE_OPENAI_DEPLOYMENT_MAP-style model=deployment entries. */
|
|
945
|
+
function parseAzureDeploymentNameMap(value) {
|
|
946
|
+
const map = /* @__PURE__ */ new Map();
|
|
947
|
+
if (!value) return map;
|
|
948
|
+
for (const entry of value.split(",")) {
|
|
949
|
+
const trimmed = entry.trim();
|
|
950
|
+
if (!trimmed) continue;
|
|
951
|
+
const separator = trimmed.indexOf("=");
|
|
952
|
+
if (separator <= 0) continue;
|
|
953
|
+
const modelId = trimmed.slice(0, separator).trim();
|
|
954
|
+
const deploymentName = trimmed.slice(separator + 1).trim();
|
|
955
|
+
if (!modelId || !deploymentName) continue;
|
|
956
|
+
map.set(modelId, deploymentName);
|
|
957
|
+
}
|
|
958
|
+
return map;
|
|
959
|
+
}
|
|
960
|
+
let cachedDeploymentLookup;
|
|
961
|
+
function getDeploymentLookup(source) {
|
|
962
|
+
const cached = cachedDeploymentLookup;
|
|
963
|
+
if (cached && cached.source === source) return cached;
|
|
964
|
+
const exact = parseAzureDeploymentNameMap(source);
|
|
965
|
+
const folded = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const [modelId, deploymentName] of exact) folded.set(modelId.toLowerCase(), deploymentName);
|
|
967
|
+
cachedDeploymentLookup = {
|
|
968
|
+
source,
|
|
969
|
+
exact,
|
|
970
|
+
folded
|
|
971
|
+
};
|
|
972
|
+
return cachedDeploymentLookup;
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Resolves the Azure deployment name for a model id, falling back to the model id.
|
|
976
|
+
*
|
|
977
|
+
* An exact-case match always wins, so configs that intentionally distinguish keys by
|
|
978
|
+
* case keep their exact mappings; a case-insensitive match is only used as a fallback
|
|
979
|
+
* (e.g. `GPT-4o` against a `gpt-4o=...` map) to avoid 404s from casing differences.
|
|
980
|
+
*/
|
|
981
|
+
function resolveAzureDeploymentNameFromMap(params) {
|
|
982
|
+
const { exact, folded } = getDeploymentLookup(params.deploymentMap);
|
|
983
|
+
return exact.get(params.modelId) ?? folded.get(params.modelId.toLowerCase()) ?? params.modelId;
|
|
984
|
+
}
|
|
985
|
+
//#endregion
|
|
986
|
+
//#region packages/ai/src/providers/azure-openai-responses-client-compat.ts
|
|
987
|
+
function isTraditionalAzureOpenAIHost(hostname) {
|
|
988
|
+
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
989
|
+
}
|
|
990
|
+
function isOpenAICompatibleAzureResponsesBaseUrl(baseUrl) {
|
|
991
|
+
let url;
|
|
992
|
+
try {
|
|
993
|
+
url = new URL(baseUrl);
|
|
994
|
+
} catch {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
if (isTraditionalAzureOpenAIHost(url.hostname)) return false;
|
|
998
|
+
const hostname = url.hostname.toLowerCase();
|
|
999
|
+
if (!(hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".api.cognitive.microsoft.com"))) return false;
|
|
1000
|
+
const normalizedPath = url.pathname.replace(/\/+$/, "");
|
|
1001
|
+
return normalizedPath === "/openai/v1" || normalizedPath.endsWith("/openai/v1");
|
|
1002
|
+
}
|
|
1003
|
+
//#endregion
|
|
899
1004
|
//#region packages/ai/src/providers/openai-reasoning-effort.ts
|
|
900
1005
|
/**
|
|
901
1006
|
* OpenAI-compatible reasoning-effort normalization. Different GPT families
|
|
@@ -1151,188 +1256,1686 @@ function createResponsesToolCallTracker() {
|
|
|
1151
1256
|
}
|
|
1152
1257
|
};
|
|
1153
1258
|
}
|
|
1259
|
+
const OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS = "Follow the user request.";
|
|
1260
|
+
const AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS = 3e4;
|
|
1261
|
+
const RESPONSE_FAILED_NO_DETAILS_MESSAGE = "Unknown error (no error details in response)";
|
|
1262
|
+
const OPENAI_RESPONSES_REASONING_REPLAY_META_KEY = "__openclaw_replay";
|
|
1263
|
+
const OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY = "openclawReasoningReplay";
|
|
1154
1264
|
//#endregion
|
|
1155
|
-
//#region packages/ai/src/
|
|
1156
|
-
/**
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1265
|
+
//#region packages/ai/src/transports/openai-transport-shared.ts
|
|
1266
|
+
/** Shared options, usage shape, cache identity, ordering, and stream scheduling for OpenAI APIs. */
|
|
1267
|
+
const MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS = 12;
|
|
1268
|
+
const MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS = 64;
|
|
1269
|
+
const GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP = "skip_thought_signature_validator";
|
|
1270
|
+
const log = {
|
|
1271
|
+
debug(message, data) {
|
|
1272
|
+
getAiTransportHost().logDebug("openai-transport", () => ({
|
|
1273
|
+
message,
|
|
1274
|
+
data
|
|
1275
|
+
}));
|
|
1276
|
+
},
|
|
1277
|
+
info(message, data) {
|
|
1278
|
+
getAiTransportHost().logInfo("openai-transport", message, data);
|
|
1279
|
+
},
|
|
1280
|
+
warn(message, data) {
|
|
1281
|
+
getAiTransportHost().logWarn("openai-transport", message, data);
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
function throwIfModelStreamAborted(signal) {
|
|
1285
|
+
if (signal?.aborted) throw transportAbortError(signal);
|
|
1286
|
+
}
|
|
1287
|
+
function createModelStreamCooperativeScheduler(signal) {
|
|
1288
|
+
let lastYieldedAt = Date.now();
|
|
1289
|
+
let eventsSinceYield = 0;
|
|
1290
|
+
return { async afterEvent() {
|
|
1291
|
+
throwIfModelStreamAborted(signal);
|
|
1292
|
+
eventsSinceYield += 1;
|
|
1293
|
+
const now = Date.now();
|
|
1294
|
+
if (eventsSinceYield < MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS && now - lastYieldedAt < MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS) return;
|
|
1295
|
+
eventsSinceYield = 0;
|
|
1296
|
+
lastYieldedAt = now;
|
|
1297
|
+
await new Promise((resolve) => {
|
|
1298
|
+
setTimeout(resolve, 0);
|
|
1299
|
+
});
|
|
1300
|
+
throwIfModelStreamAborted(signal);
|
|
1301
|
+
} };
|
|
1171
1302
|
}
|
|
1172
|
-
function
|
|
1173
|
-
|
|
1174
|
-
|
|
1303
|
+
function resolveCacheRetention(cacheRetention) {
|
|
1304
|
+
if (cacheRetention === "short" || cacheRetention === "long" || cacheRetention === "none") return cacheRetention;
|
|
1305
|
+
if (typeof process !== "undefined" && process.env.OPENCLAW_CACHE_RETENTION === "long") return "long";
|
|
1306
|
+
return "short";
|
|
1175
1307
|
}
|
|
1176
|
-
function
|
|
1177
|
-
|
|
1308
|
+
function resolvePromptCacheKey(options, cacheRetention) {
|
|
1309
|
+
if (cacheRetention === "none") return;
|
|
1310
|
+
return clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId);
|
|
1178
1311
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1312
|
+
//#endregion
|
|
1313
|
+
//#region packages/ai/src/transports/openai-responses-debug.ts
|
|
1314
|
+
function stringifyUnknown(value, fallback = "") {
|
|
1315
|
+
if (typeof value === "string") return value;
|
|
1316
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1317
|
+
return fallback;
|
|
1318
|
+
}
|
|
1319
|
+
function getServiceTierCostMultiplier$1(serviceTier) {
|
|
1320
|
+
switch (serviceTier) {
|
|
1321
|
+
case "flex": return .5;
|
|
1322
|
+
case "priority": return 2;
|
|
1323
|
+
default: return 1;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
function applyServiceTierPricing$1(usage, serviceTier) {
|
|
1327
|
+
const multiplier = getServiceTierCostMultiplier$1(serviceTier);
|
|
1328
|
+
if (multiplier === 1) return;
|
|
1329
|
+
usage.cost.input *= multiplier;
|
|
1330
|
+
usage.cost.output *= multiplier;
|
|
1331
|
+
usage.cost.cacheRead *= multiplier;
|
|
1332
|
+
usage.cost.cacheWrite *= multiplier;
|
|
1333
|
+
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
1334
|
+
}
|
|
1335
|
+
function safeDebugValue(value) {
|
|
1336
|
+
if (typeof value === "string") return value;
|
|
1337
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1338
|
+
if (value === null) return "null";
|
|
1339
|
+
if (value === void 0) return "undefined";
|
|
1340
|
+
return Array.isArray(value) ? "array" : typeof value;
|
|
1341
|
+
}
|
|
1342
|
+
function responseInputTextChars(input) {
|
|
1343
|
+
if (typeof input === "string") return input.length;
|
|
1344
|
+
if (Array.isArray(input)) return input.reduce((total, item) => total + responseInputTextChars(item), 0);
|
|
1345
|
+
if (!input || typeof input !== "object") return 0;
|
|
1346
|
+
const record = input;
|
|
1347
|
+
let total = 0;
|
|
1348
|
+
if (typeof record.text === "string") total += record.text.length;
|
|
1349
|
+
if (typeof record.content === "string") total += record.content.length;
|
|
1350
|
+
else if (Array.isArray(record.content)) total += responseInputTextChars(record.content);
|
|
1351
|
+
return total;
|
|
1352
|
+
}
|
|
1353
|
+
function responseInputRoles(input) {
|
|
1354
|
+
if (!Array.isArray(input)) return "";
|
|
1355
|
+
const roles = /* @__PURE__ */ new Set();
|
|
1356
|
+
for (const item of input) if (item && typeof item === "object") {
|
|
1357
|
+
const role = item.role;
|
|
1358
|
+
if (typeof role === "string" && role.trim()) roles.add(role.trim());
|
|
1359
|
+
}
|
|
1360
|
+
return [...roles].toSorted().join(",");
|
|
1361
|
+
}
|
|
1362
|
+
function readToolPayloadField(record, field) {
|
|
1363
|
+
try {
|
|
1364
|
+
return record[field];
|
|
1365
|
+
} catch {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function readResponsesToolDisplayName(tool) {
|
|
1370
|
+
if (!tool || typeof tool !== "object") return "";
|
|
1371
|
+
const record = tool;
|
|
1372
|
+
const name = readToolPayloadField(record, "name");
|
|
1373
|
+
if (typeof name === "string") return name;
|
|
1374
|
+
const fn = readToolPayloadField(record, "function");
|
|
1375
|
+
if (fn && typeof fn === "object") {
|
|
1376
|
+
const fnName = readToolPayloadField(fn, "name");
|
|
1377
|
+
if (typeof fnName === "string") return fnName;
|
|
1378
|
+
}
|
|
1379
|
+
const type = readToolPayloadField(record, "type");
|
|
1380
|
+
return typeof type === "string" && type !== "function" ? type : "";
|
|
1381
|
+
}
|
|
1382
|
+
function summarizeResponsesTools(tools) {
|
|
1383
|
+
if (!Array.isArray(tools)) return "count=0";
|
|
1384
|
+
const names = tools.map(readResponsesToolDisplayName).filter(Boolean);
|
|
1385
|
+
const mode = resolveModelPayloadDebugMode();
|
|
1386
|
+
const maxNames = mode === "tools" || mode === "full-redacted" ? names.length : 12;
|
|
1387
|
+
const label = maxNames >= names.length ? "names" : "sample";
|
|
1388
|
+
const shown = names.slice(0, maxNames).join(",");
|
|
1389
|
+
return `count=${tools.length}${shown ? ` ${label}=${shown}` : ""}`;
|
|
1390
|
+
}
|
|
1391
|
+
function stringifyRedactedPayload(value) {
|
|
1392
|
+
try {
|
|
1393
|
+
const encoded = JSON.stringify(value);
|
|
1394
|
+
if (!encoded) return "<empty>";
|
|
1395
|
+
const redacted = redactSensitiveText(encoded, { mode: "tools" });
|
|
1396
|
+
return redacted.length > 8e3 ? `${truncateUtf16Safe(redacted, 8e3)}…<truncated>` : redacted;
|
|
1397
|
+
} catch {
|
|
1398
|
+
return "<unserializable>";
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function stringifyRedactedEvent(value) {
|
|
1402
|
+
const redacted = stringifyRedactedPayload(value);
|
|
1403
|
+
return redacted.length > 2e3 ? `${truncateUtf16Safe(redacted, 2e3)}…<truncated>` : redacted;
|
|
1404
|
+
}
|
|
1405
|
+
const RESPONSE_FAILED_FAILURE_FIELD_KEYS = [
|
|
1406
|
+
"error",
|
|
1407
|
+
"incomplete_details",
|
|
1408
|
+
"status_details",
|
|
1409
|
+
"failure_reason",
|
|
1410
|
+
"last_error",
|
|
1411
|
+
"provider_error",
|
|
1412
|
+
"error_details"
|
|
1413
|
+
];
|
|
1414
|
+
function readResponseFailedString(record, key) {
|
|
1415
|
+
return stringifyUnknown(record?.[key]);
|
|
1416
|
+
}
|
|
1417
|
+
function buildResponsesFailedEventSummary(message, responseId, observation) {
|
|
1418
|
+
const summary = { message };
|
|
1419
|
+
if (responseId) summary.responseId = responseId;
|
|
1420
|
+
if (observation) summary.observation = observation;
|
|
1421
|
+
return summary;
|
|
1422
|
+
}
|
|
1423
|
+
function isResponseFailedIdentifierKey(key) {
|
|
1424
|
+
const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
|
|
1425
|
+
return normalized === "requestid" || normalized === "xrequestid" || normalized === "providerrequestid" || normalized === "providerresponseid" || normalized === "litellmrequestid" || normalized.includes("request") && normalized.endsWith("id") || normalized.includes("provider") && normalized.endsWith("id");
|
|
1426
|
+
}
|
|
1427
|
+
function collectResponseFailedIdentifierHashes(value, opts = {}) {
|
|
1428
|
+
const path = opts.path ?? "";
|
|
1429
|
+
const depth = opts.depth ?? 0;
|
|
1430
|
+
const identifierKey = opts.identifierKey ?? "";
|
|
1431
|
+
const out = opts.out ?? [];
|
|
1432
|
+
const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
|
|
1433
|
+
if (out.length >= 12 || depth > 4 || !value || typeof value !== "object") return out;
|
|
1434
|
+
if (seen.has(value)) return out;
|
|
1435
|
+
seen.add(value);
|
|
1436
|
+
if (Array.isArray(value)) {
|
|
1437
|
+
for (const [index, item] of value.entries()) {
|
|
1438
|
+
if (index >= 8 || out.length >= 12) break;
|
|
1439
|
+
const itemString = typeof item === "string" || typeof item === "number" ? String(item).trim() : "";
|
|
1440
|
+
if (identifierKey && isResponseFailedIdentifierKey(identifierKey) && itemString) {
|
|
1441
|
+
out.push(`${path}[${index}]=${redactIdentifier(itemString, { len: 12 })}`);
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
collectResponseFailedIdentifierHashes(item, {
|
|
1445
|
+
path: `${path}[${index}]`,
|
|
1446
|
+
depth: depth + 1,
|
|
1447
|
+
identifierKey,
|
|
1448
|
+
out,
|
|
1449
|
+
seen
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
return out;
|
|
1453
|
+
}
|
|
1454
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1455
|
+
if (out.length >= 12) break;
|
|
1456
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
1457
|
+
const childString = typeof child === "string" || typeof child === "number" ? String(child).trim() : "";
|
|
1458
|
+
if (isResponseFailedIdentifierKey(key) && childString) {
|
|
1459
|
+
out.push(`${childPath}=${redactIdentifier(childString, { len: 12 })}`);
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
collectResponseFailedIdentifierHashes(child, {
|
|
1463
|
+
path: childPath,
|
|
1464
|
+
depth: depth + 1,
|
|
1465
|
+
identifierKey: isResponseFailedIdentifierKey(key) ? key : void 0,
|
|
1466
|
+
out,
|
|
1467
|
+
seen
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
return out;
|
|
1471
|
+
}
|
|
1472
|
+
function redactResponseFailedDiagnosticValue(value, opts = {}) {
|
|
1473
|
+
const key = opts.key ?? "";
|
|
1474
|
+
const depth = opts.depth ?? 0;
|
|
1475
|
+
if (typeof value === "string" || typeof value === "number") return key && isResponseFailedIdentifierKey(key) ? redactIdentifier(String(value), { len: 12 }) : value;
|
|
1476
|
+
if (depth > 6 || !value || typeof value !== "object") return value;
|
|
1477
|
+
const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
|
|
1478
|
+
if (seen.has(value)) return "<circular>";
|
|
1479
|
+
seen.add(value);
|
|
1480
|
+
if (Array.isArray(value)) return value.slice(0, 16).map((item) => redactResponseFailedDiagnosticValue(item, {
|
|
1182
1481
|
key,
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1482
|
+
depth: depth + 1,
|
|
1483
|
+
seen
|
|
1484
|
+
}));
|
|
1485
|
+
const out = {};
|
|
1486
|
+
for (const [childKey, child] of Object.entries(value)) out[childKey] = redactResponseFailedDiagnosticValue(child, {
|
|
1487
|
+
key: childKey,
|
|
1488
|
+
depth: depth + 1,
|
|
1489
|
+
seen
|
|
1490
|
+
});
|
|
1491
|
+
return out;
|
|
1492
|
+
}
|
|
1493
|
+
function buildResponsesFailedFailureFields(response) {
|
|
1494
|
+
if (!response) return {};
|
|
1495
|
+
const fields = {};
|
|
1496
|
+
for (const key of RESPONSE_FAILED_FAILURE_FIELD_KEYS) if (response[key] !== void 0 && response[key] !== null) fields[key] = response[key];
|
|
1497
|
+
return fields;
|
|
1498
|
+
}
|
|
1499
|
+
function buildResponsesFailedNoDetailsObservation(event, model, response = isRecord(event.response) ? event.response : void 0) {
|
|
1500
|
+
const failureFields = redactResponseFailedDiagnosticValue(buildResponsesFailedFailureFields(response));
|
|
1501
|
+
const metadataKeys = isRecord(response?.metadata) ? Object.keys(response.metadata).toSorted() : [];
|
|
1502
|
+
const responsePreview = {
|
|
1503
|
+
id: readResponseFailedString(response, "id"),
|
|
1504
|
+
status: readResponseFailedString(response, "status"),
|
|
1505
|
+
model: readResponseFailedString(response, "model"),
|
|
1506
|
+
object: readResponseFailedString(response, "object"),
|
|
1507
|
+
failureFields,
|
|
1508
|
+
metadataKeys
|
|
1509
|
+
};
|
|
1510
|
+
return {
|
|
1511
|
+
event: "openai_responses_response_failed_without_details",
|
|
1512
|
+
provider: model.provider,
|
|
1513
|
+
api: model.api,
|
|
1514
|
+
transportModel: model.id,
|
|
1515
|
+
providerRuntimeFailureKind: "no_error_details",
|
|
1516
|
+
responseId: responsePreview.id,
|
|
1517
|
+
responseStatus: responsePreview.status,
|
|
1518
|
+
responseModel: responsePreview.model,
|
|
1519
|
+
responseObject: responsePreview.object,
|
|
1520
|
+
metadataKeys,
|
|
1521
|
+
requestIdHashes: collectResponseFailedIdentifierHashes(event),
|
|
1522
|
+
failureFieldsPreview: stringifyRedactedEvent(failureFields),
|
|
1523
|
+
responsePreview: stringifyRedactedEvent(responsePreview)
|
|
1524
|
+
};
|
|
1186
1525
|
}
|
|
1187
|
-
function
|
|
1188
|
-
|
|
1526
|
+
function summarizeResponsesFailedNoDetailsObservation(observation) {
|
|
1527
|
+
const requestIds = observation.requestIdHashes.join(",");
|
|
1528
|
+
const metadataKeys = observation.metadataKeys.join(",");
|
|
1529
|
+
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}`;
|
|
1530
|
+
}
|
|
1531
|
+
function normalizeResponsesFailedEvent(event, model) {
|
|
1532
|
+
const response = isRecord(event.response) ? event.response : void 0;
|
|
1533
|
+
const responseId = readResponseFailedString(response, "id") || void 0;
|
|
1534
|
+
const error = isRecord(response?.error) ? response.error : void 0;
|
|
1535
|
+
if (error) {
|
|
1536
|
+
const code = readResponseFailedString(error, "code").trim();
|
|
1537
|
+
const message = readResponseFailedString(error, "message").trim();
|
|
1538
|
+
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId);
|
|
1539
|
+
}
|
|
1540
|
+
const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
|
|
1541
|
+
if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
|
|
1542
|
+
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
1543
|
+
}
|
|
1544
|
+
function logResponsesFailedNoDetails(observation) {
|
|
1545
|
+
log.warn(`[responses] response.failed missing error details provider=${observation.provider} api=${observation.api} model=${observation.transportModel} ` + summarizeResponsesFailedNoDetailsObservation(observation), observation);
|
|
1546
|
+
}
|
|
1547
|
+
function summarizeResponsesPayload(params) {
|
|
1548
|
+
if (!params || typeof params !== "object") return "payload=non-object";
|
|
1549
|
+
const record = params;
|
|
1550
|
+
const input = record.input;
|
|
1551
|
+
const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
|
|
1552
|
+
const text = record.text && typeof record.text === "object" ? record.text : void 0;
|
|
1553
|
+
const parts = [
|
|
1554
|
+
`fields=${Object.keys(record).toSorted().join(",")}`,
|
|
1555
|
+
`model=${safeDebugValue(record.model)}`,
|
|
1556
|
+
`stream=${safeDebugValue(record.stream)}`,
|
|
1557
|
+
`inputItems=${Array.isArray(input) ? input.length : typeof input}`,
|
|
1558
|
+
`inputRoles=${responseInputRoles(input) || "none"}`,
|
|
1559
|
+
`inputTextChars=${responseInputTextChars(input)}`,
|
|
1560
|
+
`tools=${summarizeResponsesTools(record.tools)}`,
|
|
1561
|
+
`reasoningEffort=${safeDebugValue(reasoning?.effort)}`,
|
|
1562
|
+
`reasoningSummary=${safeDebugValue(reasoning?.summary)}`,
|
|
1563
|
+
`textVerbosity=${safeDebugValue(text?.verbosity)}`,
|
|
1564
|
+
`serviceTier=${safeDebugValue(record.service_tier)}`,
|
|
1565
|
+
`store=${safeDebugValue(record.store)}`,
|
|
1566
|
+
`promptCacheKey=${record.prompt_cache_key === void 0 ? "absent" : "present"}`,
|
|
1567
|
+
`metadataKeys=${record.metadata && typeof record.metadata === "object" ? Object.keys(record.metadata).toSorted().join(",") : "none"}`
|
|
1568
|
+
];
|
|
1569
|
+
if (resolveModelPayloadDebugMode() === "full-redacted") parts.push(`payload=${stringifyRedactedPayload(record)}`);
|
|
1570
|
+
return parts.join(" ");
|
|
1571
|
+
}
|
|
1572
|
+
function summarizeOpenAITransportError(error) {
|
|
1573
|
+
if (!error || typeof error !== "object") return `type=${typeof error} message=${safeDebugValue(error)}`;
|
|
1574
|
+
const record = error;
|
|
1575
|
+
const cause = record.cause && typeof record.cause === "object" ? record.cause : void 0;
|
|
1576
|
+
return [
|
|
1577
|
+
`name=${safeDebugValue(record.name)}`,
|
|
1578
|
+
`status=${safeDebugValue(record.status)}`,
|
|
1579
|
+
`code=${safeDebugValue(record.code)}`,
|
|
1580
|
+
`type=${safeDebugValue(record.type)}`,
|
|
1581
|
+
`causeName=${safeDebugValue(cause?.name)}`,
|
|
1582
|
+
`causeCode=${safeDebugValue(cause?.code)}`,
|
|
1583
|
+
`message=${error instanceof Error ? error.message : safeDebugValue(error)}`
|
|
1584
|
+
].join(" ");
|
|
1189
1585
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
return rememberStrictOpenAISchema(schemaInput, cacheKey, normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0));
|
|
1586
|
+
//#endregion
|
|
1587
|
+
//#region packages/ai/src/transports/openai-responses-replay.ts
|
|
1588
|
+
/** Resolves the assistant message id that can be replayed to OpenAI Responses. */
|
|
1589
|
+
function resolveReplayableResponsesMessageId$1(params) {
|
|
1590
|
+
if (!params.replayResponsesItemIds) return;
|
|
1591
|
+
if (!params.textSignatureId) return params.fallbackOrdinal === 0 ? params.fallbackId : `${params.fallbackId}_${params.fallbackOrdinal}`;
|
|
1592
|
+
return params.previousReplayItemWasReasoning ? params.textSignatureId : void 0;
|
|
1198
1593
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region packages/ai/src/transports/openai-responses-replay-internal.ts
|
|
1596
|
+
function isInvalidEncryptedContentError(error) {
|
|
1597
|
+
if (!error || typeof error !== "object") return false;
|
|
1598
|
+
const record = error;
|
|
1599
|
+
if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
|
|
1600
|
+
const message = typeof record.message === "string" ? record.message : "";
|
|
1601
|
+
return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || record.status === 400 && message.toLowerCase().includes("could not decrypt the provided encrypted_content");
|
|
1602
|
+
}
|
|
1603
|
+
function stripEncryptedContentFields(value) {
|
|
1604
|
+
if (!value || typeof value !== "object") return {
|
|
1605
|
+
value,
|
|
1606
|
+
changed: false
|
|
1607
|
+
};
|
|
1608
|
+
if (Array.isArray(value)) {
|
|
1201
1609
|
let changed = false;
|
|
1202
|
-
const
|
|
1203
|
-
const
|
|
1204
|
-
changed ||=
|
|
1205
|
-
return
|
|
1610
|
+
const next = value.map((item) => {
|
|
1611
|
+
const stripped = stripEncryptedContentFields(item);
|
|
1612
|
+
changed ||= stripped.changed;
|
|
1613
|
+
return stripped.value;
|
|
1206
1614
|
});
|
|
1207
|
-
return changed ?
|
|
1615
|
+
return changed ? {
|
|
1616
|
+
value: next,
|
|
1617
|
+
changed: true
|
|
1618
|
+
} : {
|
|
1619
|
+
value,
|
|
1620
|
+
changed: false
|
|
1621
|
+
};
|
|
1208
1622
|
}
|
|
1209
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1210
|
-
const record = schema;
|
|
1211
1623
|
let changed = false;
|
|
1212
|
-
const
|
|
1213
|
-
for (const [key,
|
|
1214
|
-
|
|
1215
|
-
normalized[key] = next;
|
|
1216
|
-
changed ||= next !== value;
|
|
1217
|
-
}
|
|
1218
|
-
if (normalized.type === "object") {
|
|
1219
|
-
const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : void 0;
|
|
1220
|
-
if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
|
|
1221
|
-
normalized.required = [];
|
|
1222
|
-
changed = true;
|
|
1223
|
-
}
|
|
1224
|
-
if (depth === 0 && !("additionalProperties" in normalized)) {
|
|
1225
|
-
normalized.additionalProperties = false;
|
|
1624
|
+
const next = {};
|
|
1625
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1626
|
+
if (key === "encrypted_content") {
|
|
1226
1627
|
changed = true;
|
|
1628
|
+
continue;
|
|
1227
1629
|
}
|
|
1630
|
+
const stripped = stripEncryptedContentFields(child);
|
|
1631
|
+
changed ||= stripped.changed;
|
|
1632
|
+
next[key] = stripped.value;
|
|
1228
1633
|
}
|
|
1229
|
-
return changed ?
|
|
1634
|
+
return changed ? {
|
|
1635
|
+
value: next,
|
|
1636
|
+
changed: true
|
|
1637
|
+
} : {
|
|
1638
|
+
value,
|
|
1639
|
+
changed: false
|
|
1640
|
+
};
|
|
1230
1641
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1642
|
+
function stripResponsesRequestEncryptedContent(params) {
|
|
1643
|
+
const stripped = stripEncryptedContentFields(params.input);
|
|
1644
|
+
if (!stripped.changed) return params;
|
|
1645
|
+
return {
|
|
1646
|
+
...params,
|
|
1647
|
+
input: stripped.value
|
|
1648
|
+
};
|
|
1236
1649
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
return
|
|
1650
|
+
function hashOptionalReplayContextValue(value) {
|
|
1651
|
+
const normalized = value?.trim();
|
|
1652
|
+
return normalized ? shortHash(normalized) : void 0;
|
|
1240
1653
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
toolIndex: tool.toolIndex,
|
|
1251
|
-
toolName: tool.name,
|
|
1252
|
-
violations
|
|
1253
|
-
}] : [];
|
|
1254
|
-
})];
|
|
1654
|
+
function buildOpenAIResponsesReplayContext(model, options) {
|
|
1655
|
+
return {
|
|
1656
|
+
provider: model.provider,
|
|
1657
|
+
api: model.api,
|
|
1658
|
+
model: model.id,
|
|
1659
|
+
baseUrlHash: hashOptionalReplayContextValue(model.baseUrl),
|
|
1660
|
+
sessionHash: hashOptionalReplayContextValue(options?.sessionId),
|
|
1661
|
+
authProfileHash: hashOptionalReplayContextValue(options?.authProfileId)
|
|
1662
|
+
};
|
|
1255
1663
|
}
|
|
1256
|
-
function
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1664
|
+
function buildOpenAIResponsesReasoningReplayMetadata(model, options) {
|
|
1665
|
+
return {
|
|
1666
|
+
v: 1,
|
|
1667
|
+
source: "openai-responses",
|
|
1668
|
+
...buildOpenAIResponsesReplayContext(model, options)
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
function tagOpenAIResponsesReasoningReplayItem(item, model, options) {
|
|
1672
|
+
if (!("encrypted_content" in item)) return item;
|
|
1673
|
+
return {
|
|
1674
|
+
...item,
|
|
1675
|
+
[OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: buildOpenAIResponsesReasoningReplayMetadata(model, options)
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
function isOpenAIResponsesReasoningReplayMetadata(value) {
|
|
1679
|
+
if (!value || typeof value !== "object") return false;
|
|
1680
|
+
const record = value;
|
|
1681
|
+
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");
|
|
1682
|
+
}
|
|
1683
|
+
function encryptedReasoningReplayMetadataMatches(metadata, context) {
|
|
1684
|
+
if (!metadata) return false;
|
|
1685
|
+
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;
|
|
1686
|
+
}
|
|
1687
|
+
function readOpenAIResponsesReasoningReplayBlockMetadata(block) {
|
|
1688
|
+
const value = block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY];
|
|
1689
|
+
return isOpenAIResponsesReasoningReplayMetadata(value) ? value : void 0;
|
|
1690
|
+
}
|
|
1691
|
+
function normalizeOpenAIResponsesReasoningReplayItem(item) {
|
|
1692
|
+
const record = item;
|
|
1693
|
+
if (record.type !== "reasoning" || Array.isArray(record.summary)) return item;
|
|
1694
|
+
return {
|
|
1695
|
+
...record,
|
|
1696
|
+
summary: []
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
function prepareOpenAIResponsesReasoningItemForReplay(item, context, blockMetadata) {
|
|
1700
|
+
const { [OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: rawMetadata, ...rest } = item;
|
|
1701
|
+
if (!("encrypted_content" in rest)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
|
|
1702
|
+
if (encryptedReasoningReplayMetadataMatches(blockMetadata ?? (isOpenAIResponsesReasoningReplayMetadata(rawMetadata) ? rawMetadata : void 0), context)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
|
|
1703
|
+
return normalizeOpenAIResponsesReasoningReplayItem(stripEncryptedContentFields(rest).value);
|
|
1704
|
+
}
|
|
1705
|
+
async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
1706
|
+
try {
|
|
1707
|
+
return await params.client.responses.create(params.request, params.requestOptions);
|
|
1708
|
+
} catch (error) {
|
|
1709
|
+
const retryRequest = stripResponsesRequestEncryptedContent(params.request);
|
|
1710
|
+
if (!isInvalidEncryptedContentError(error) || retryRequest === params.request) throw error;
|
|
1711
|
+
log.warn(`[responses] retrying without encrypted reasoning content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
1712
|
+
return await params.client.responses.create(retryRequest, params.requestOptions);
|
|
1269
1713
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1714
|
+
}
|
|
1715
|
+
function resolveAzureOpenAIApiVersion(env = process.env) {
|
|
1716
|
+
return env.AZURE_OPENAI_API_VERSION?.trim() || "preview";
|
|
1717
|
+
}
|
|
1718
|
+
function normalizeResponsesReplayItemId(id, prefix) {
|
|
1719
|
+
if (!id) return;
|
|
1720
|
+
if (id.length <= 64) return id;
|
|
1721
|
+
return `${prefix}_${shortHash(id)}`;
|
|
1722
|
+
}
|
|
1723
|
+
function isSafeResponsesReplayItemId(id) {
|
|
1724
|
+
return typeof id === "string" && id.length > 0 && id.length <= 64;
|
|
1725
|
+
}
|
|
1726
|
+
function encodeTextSignatureV1(id, phase) {
|
|
1727
|
+
return JSON.stringify({
|
|
1728
|
+
v: 1,
|
|
1729
|
+
id,
|
|
1730
|
+
...phase ? { phase } : {}
|
|
1273
1731
|
});
|
|
1274
1732
|
}
|
|
1275
|
-
function
|
|
1276
|
-
if (
|
|
1277
|
-
if (
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
|
|
1288
|
-
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
|
|
1289
|
-
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
1290
|
-
if (!required) violations.push(`${path}.required`);
|
|
1291
|
-
else {
|
|
1292
|
-
const requiredSet = new Set(required);
|
|
1293
|
-
for (const key of Object.keys(properties)) if (!requiredSet.has(key)) violations.push(`${path}.required.${key}`);
|
|
1733
|
+
function parseTextSignature$1(signature) {
|
|
1734
|
+
if (!signature) return;
|
|
1735
|
+
if (signature.startsWith("{")) try {
|
|
1736
|
+
const parsed = JSON.parse(signature);
|
|
1737
|
+
if (parsed.v === 1) {
|
|
1738
|
+
const id = typeof parsed.id === "string" ? parsed.id : void 0;
|
|
1739
|
+
const phase = parsed.phase === "commentary" || parsed.phase === "final_answer" ? parsed.phase : void 0;
|
|
1740
|
+
if (id !== void 0 || phase !== void 0) return {
|
|
1741
|
+
id,
|
|
1742
|
+
phase
|
|
1743
|
+
};
|
|
1744
|
+
return;
|
|
1294
1745
|
}
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
for (const [key, value] of Object.entries(record)) {
|
|
1298
|
-
if (key === "properties") continue;
|
|
1299
|
-
if (value && typeof value === "object") violations.push(...findStrictOpenAIJsonSchemaViolations(value, `${path}.${key}`));
|
|
1300
|
-
}
|
|
1301
|
-
return violations;
|
|
1302
|
-
}
|
|
1303
|
-
/** Resolves strict mode for the projected tools that will be emitted in the request payload. */
|
|
1304
|
-
function resolveOpenAIProjectedToolsStrictToolFlag(projection, strict) {
|
|
1305
|
-
if (strict !== true) return strict === false ? false : void 0;
|
|
1306
|
-
return projection.tools.every((tool) => isStrictOpenAIJsonSchemaCompatible(tool.parameters));
|
|
1746
|
+
} catch {}
|
|
1747
|
+
return { id: signature };
|
|
1307
1748
|
}
|
|
1308
|
-
|
|
1309
|
-
//#region packages/ai/src/providers/openai-responses-tools.ts
|
|
1310
|
-
const LOG_SUBSYSTEM = "llm/openai-responses";
|
|
1311
|
-
const MAX_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 64;
|
|
1312
|
-
const loggedStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
1313
|
-
/** Converts and returns the projection used to reconcile tool choices. */
|
|
1314
|
-
function convertResponsesToolPayload(tools, options) {
|
|
1315
|
-
const projection = projectOpenAITools(tools);
|
|
1316
|
-
const strict = resolveResponsesStrictToolFlag(projection, resolveResponsesStrictToolSetting(options), options?.model);
|
|
1749
|
+
function buildResponsesInputMessage(role, content) {
|
|
1317
1750
|
return {
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
type: "function",
|
|
1322
|
-
name: tool.name,
|
|
1323
|
-
description: tool.description,
|
|
1324
|
-
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, options?.model?.compat)
|
|
1325
|
-
};
|
|
1326
|
-
if (strict !== void 0) result.strict = strict;
|
|
1327
|
-
return result;
|
|
1328
|
-
})
|
|
1751
|
+
type: "message",
|
|
1752
|
+
role,
|
|
1753
|
+
content
|
|
1329
1754
|
};
|
|
1330
1755
|
}
|
|
1331
|
-
function
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1756
|
+
function convertResponsesMessages$1(model, context, allowedToolCallProviders, options) {
|
|
1757
|
+
const messages = [];
|
|
1758
|
+
const shouldReplayReasoningItems = options?.replayReasoningItems ?? true;
|
|
1759
|
+
const shouldReplayResponsesItemIds = options?.replayResponsesItemIds ?? true;
|
|
1760
|
+
const replayContext = buildOpenAIResponsesReplayContext(model, {
|
|
1761
|
+
sessionId: options?.sessionId,
|
|
1762
|
+
authProfileId: options?.authProfileId
|
|
1763
|
+
});
|
|
1764
|
+
const shouldNormalizeSameModelToolCallIds = model.provider === "github-copilot";
|
|
1765
|
+
const sanitizeIdPart = (part) => part.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+$/, "");
|
|
1766
|
+
const normalizeIdPart = (part) => {
|
|
1767
|
+
const sanitized = sanitizeIdPart(part);
|
|
1768
|
+
return (sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized).replace(/_+$/, "");
|
|
1769
|
+
};
|
|
1770
|
+
const buildForeignResponsesItemId = (itemId) => {
|
|
1771
|
+
const normalized = `fc_${shortHash(itemId)}`;
|
|
1772
|
+
return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
|
|
1773
|
+
};
|
|
1774
|
+
const buildSameProviderCopilotResponsesItemId = (itemId) => {
|
|
1775
|
+
const sanitized = sanitizeIdPart(itemId);
|
|
1776
|
+
const candidate = sanitized.startsWith("fc_") ? sanitized : `fc_${sanitized}`;
|
|
1777
|
+
return candidate.length > 64 ? buildForeignResponsesItemId(itemId) : candidate;
|
|
1778
|
+
};
|
|
1779
|
+
const normalizeToolCallId = (id, _targetModel, source) => {
|
|
1780
|
+
if (!allowedToolCallProviders.has(model.provider)) return normalizeIdPart(id);
|
|
1781
|
+
if (!id.includes("|")) return normalizeIdPart(id);
|
|
1782
|
+
const separatorIndex = id.indexOf("|");
|
|
1783
|
+
const callId = id.slice(0, separatorIndex);
|
|
1784
|
+
const itemId = id.slice(separatorIndex + 1);
|
|
1785
|
+
const normalizedCallId = normalizeIdPart(callId);
|
|
1786
|
+
let normalizedItemId = source.provider !== model.provider || source.api !== model.api ? buildForeignResponsesItemId(itemId) : model.provider === "github-copilot" ? buildSameProviderCopilotResponsesItemId(itemId) : normalizeIdPart(itemId);
|
|
1787
|
+
if (!normalizedItemId.startsWith("fc_")) normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
|
|
1788
|
+
return `${normalizedCallId}|${normalizedItemId}`;
|
|
1789
|
+
};
|
|
1790
|
+
const transformedMessages = transformTransportMessages(context.messages, model, normalizeToolCallId, { normalizeSameModelToolCallIds: shouldNormalizeSameModelToolCallIds });
|
|
1791
|
+
if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && options?.supportsDeveloperRole !== false ? "developer" : "system", [{
|
|
1792
|
+
type: "input_text",
|
|
1793
|
+
text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
|
|
1794
|
+
}]));
|
|
1795
|
+
let msgIndex = 0;
|
|
1796
|
+
for (const msg of transformedMessages) {
|
|
1797
|
+
if (msg.role === "user") if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
|
|
1798
|
+
type: "input_text",
|
|
1799
|
+
text: sanitizeTransportPayloadText(msg.content)
|
|
1800
|
+
}]));
|
|
1801
|
+
else {
|
|
1802
|
+
const content = msg.content.map((item) => item.type === "text" ? {
|
|
1803
|
+
type: "input_text",
|
|
1804
|
+
text: sanitizeTransportPayloadText(item.text)
|
|
1805
|
+
} : {
|
|
1806
|
+
type: "input_image",
|
|
1807
|
+
detail: "auto",
|
|
1808
|
+
image_url: `data:${item.mimeType};base64,${item.data}`
|
|
1809
|
+
}).filter((item) => model.input.includes("image") || item.type !== "input_image");
|
|
1810
|
+
if (content.length > 0) messages.push(buildResponsesInputMessage("user", content));
|
|
1811
|
+
}
|
|
1812
|
+
else if (msg.role === "assistant") {
|
|
1813
|
+
const output = [];
|
|
1814
|
+
let textFallbackOrdinal = 0;
|
|
1815
|
+
let previousReplayItemWasReasoning = false;
|
|
1816
|
+
const isDifferentModel = msg.model !== model.id && msg.provider === model.provider && msg.api === model.api;
|
|
1817
|
+
for (const block of msg.content) if (block.type === "thinking") {
|
|
1818
|
+
if (shouldReplayReasoningItems && block.thinkingSignature && block.thinkingSignature.startsWith("{")) {
|
|
1819
|
+
const replayableReasoningItem = prepareOpenAIResponsesReasoningItemForReplay(JSON.parse(block.thinkingSignature), replayContext, readOpenAIResponsesReasoningReplayBlockMetadata(block));
|
|
1820
|
+
if (!shouldReplayResponsesItemIds) delete replayableReasoningItem.id;
|
|
1821
|
+
if (shouldReplayResponsesItemIds && model.provider === "github-copilot" && !isSafeResponsesReplayItemId(replayableReasoningItem.id)) continue;
|
|
1822
|
+
output.push(replayableReasoningItem);
|
|
1823
|
+
previousReplayItemWasReasoning = true;
|
|
1824
|
+
}
|
|
1825
|
+
} else if (block.type === "text") {
|
|
1826
|
+
const textSignature = parseTextSignature$1(block.textSignature);
|
|
1827
|
+
let msgId = resolveReplayableResponsesMessageId$1({
|
|
1828
|
+
replayResponsesItemIds: shouldReplayResponsesItemIds,
|
|
1829
|
+
textSignatureId: textSignature?.id,
|
|
1830
|
+
fallbackId: `msg_${msgIndex}`,
|
|
1831
|
+
fallbackOrdinal: textFallbackOrdinal,
|
|
1832
|
+
previousReplayItemWasReasoning
|
|
1833
|
+
});
|
|
1834
|
+
if (!textSignature?.id) textFallbackOrdinal += 1;
|
|
1835
|
+
msgId = normalizeResponsesReplayItemId(msgId, "msg");
|
|
1836
|
+
const messageItem = {
|
|
1837
|
+
type: "message",
|
|
1838
|
+
role: "assistant",
|
|
1839
|
+
content: [{
|
|
1840
|
+
type: "output_text",
|
|
1841
|
+
text: sanitizeTransportPayloadText(block.text),
|
|
1842
|
+
annotations: []
|
|
1843
|
+
}],
|
|
1844
|
+
status: "completed",
|
|
1845
|
+
...msgId ? { id: msgId } : {},
|
|
1846
|
+
phase: textSignature?.phase
|
|
1847
|
+
};
|
|
1848
|
+
output.push(messageItem);
|
|
1849
|
+
previousReplayItemWasReasoning = false;
|
|
1850
|
+
} else if (block.type === "toolCall") {
|
|
1851
|
+
const separatorIndex = block.id.indexOf("|");
|
|
1852
|
+
const callId = separatorIndex === -1 ? block.id : block.id.slice(0, separatorIndex);
|
|
1853
|
+
const itemIdRaw = separatorIndex === -1 ? void 0 : block.id.slice(separatorIndex + 1);
|
|
1854
|
+
const itemId = shouldReplayResponsesItemIds && !(isDifferentModel && itemIdRaw?.startsWith("fc_")) ? itemIdRaw : void 0;
|
|
1855
|
+
output.push({
|
|
1856
|
+
type: "function_call",
|
|
1857
|
+
...itemId ? { id: itemId } : {},
|
|
1858
|
+
call_id: callId,
|
|
1859
|
+
name: block.name,
|
|
1860
|
+
arguments: typeof block.arguments === "string" ? block.arguments : JSON.stringify(block.arguments ?? {})
|
|
1861
|
+
});
|
|
1862
|
+
previousReplayItemWasReasoning = false;
|
|
1863
|
+
}
|
|
1864
|
+
if (output.length > 0) messages.push(...output);
|
|
1865
|
+
} else if (msg.role === "toolResult") {
|
|
1866
|
+
const textResult = extractToolResultText(msg.content);
|
|
1867
|
+
const sanitizedTextResult = sanitizeTransportPayloadText(textResult);
|
|
1868
|
+
const hasText = sanitizedTextResult.trim().length > 0;
|
|
1869
|
+
const mediaPlaceholder = describeToolResultMediaPlaceholder(msg.content);
|
|
1870
|
+
const hasImages = msg.content.some(isImageWithMediaPayload);
|
|
1871
|
+
const separatorIndex = msg.toolCallId.indexOf("|");
|
|
1872
|
+
const callId = separatorIndex === -1 ? msg.toolCallId : msg.toolCallId.slice(0, separatorIndex);
|
|
1873
|
+
messages.push({
|
|
1874
|
+
type: "function_call_output",
|
|
1875
|
+
call_id: callId,
|
|
1876
|
+
output: hasImages && model.input.includes("image") ? [...hasText ? [{
|
|
1877
|
+
type: "input_text",
|
|
1878
|
+
text: sanitizedTextResult
|
|
1879
|
+
}] : mediaPlaceholder === "(see attached media)" ? [{
|
|
1880
|
+
type: "input_text",
|
|
1881
|
+
text: mediaPlaceholder
|
|
1882
|
+
}] : [], ...msg.content.filter(isImageWithMediaPayload).map((item) => ({
|
|
1883
|
+
type: "input_image",
|
|
1884
|
+
detail: "auto",
|
|
1885
|
+
image_url: `data:${item.mimeType};base64,${item.data}`
|
|
1886
|
+
}))] : sanitizeNonEmptyTransportPayloadText(textResult, mediaPlaceholder ?? "(no output)")
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
msgIndex += 1;
|
|
1890
|
+
}
|
|
1891
|
+
return messages;
|
|
1892
|
+
}
|
|
1893
|
+
//#endregion
|
|
1894
|
+
//#region packages/ai/src/transports/openai-responses-stream-observer-internal.ts
|
|
1895
|
+
const STRING_DELTA_EVENTS = /* @__PURE__ */ new Set([
|
|
1896
|
+
"response.function_call_arguments.delta",
|
|
1897
|
+
"response.output_text.delta",
|
|
1898
|
+
"response.reasoning_summary_text.delta",
|
|
1899
|
+
"response.reasoning_text.delta",
|
|
1900
|
+
"response.refusal.delta",
|
|
1901
|
+
"response.text.delta"
|
|
1902
|
+
]);
|
|
1903
|
+
async function* adaptResponsesStream(stream, signal) {
|
|
1904
|
+
const scheduler = createModelStreamCooperativeScheduler(signal);
|
|
1905
|
+
for await (const event of stream) {
|
|
1906
|
+
if (signal?.aborted) throw transportAbortError(signal);
|
|
1907
|
+
if (!isRecord(event) || typeof event.type !== "string") throw new Error("Responses stream delivered a malformed event without a string type");
|
|
1908
|
+
if (STRING_DELTA_EVENTS.has(event.type) && typeof event.delta !== "string") throw new Error(`Responses stream delivered malformed ${event.type} delta`);
|
|
1909
|
+
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`);
|
|
1910
|
+
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`);
|
|
1911
|
+
yield event;
|
|
1912
|
+
await scheduler.afterEvent();
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
async function* observeResponsesStream(stream, model) {
|
|
1916
|
+
const startedAt = Date.now();
|
|
1917
|
+
const eventTypes = /* @__PURE__ */ new Map();
|
|
1918
|
+
const debugMode = resolveModelSseDebugMode();
|
|
1919
|
+
let eventCount = 0;
|
|
1920
|
+
try {
|
|
1921
|
+
for await (const event of stream) {
|
|
1922
|
+
const type = isRecord(event) && typeof event.type === "string" ? event.type : "unknown";
|
|
1923
|
+
eventCount += 1;
|
|
1924
|
+
eventTypes.set(type, (eventTypes.get(type) ?? 0) + 1);
|
|
1925
|
+
if (eventCount === 1) emitModelTransportDebug(log, `[responses] first_event provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - startedAt} type=${type}`);
|
|
1926
|
+
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)}`);
|
|
1927
|
+
yield event;
|
|
1928
|
+
}
|
|
1929
|
+
} finally {
|
|
1930
|
+
const types = [...eventTypes].map(([type, count]) => `${type}:${count}`).join(",");
|
|
1931
|
+
emitModelTransportDebug(log, `[responses] stream_done provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - startedAt} events=${eventCount} types=${types}`);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
//#endregion
|
|
1935
|
+
//#region packages/ai/src/transports/openai-responses-stream-slots-internal.ts
|
|
1936
|
+
function readResponsesOutputIndex(event) {
|
|
1937
|
+
const outputIndex = event.output_index;
|
|
1938
|
+
return typeof outputIndex === "number" && Number.isInteger(outputIndex) && outputIndex >= 0 ? outputIndex : void 0;
|
|
1939
|
+
}
|
|
1940
|
+
function createResponsesOutputSlotTracker() {
|
|
1941
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
1942
|
+
let unindexed;
|
|
1943
|
+
return {
|
|
1944
|
+
register(event, slot) {
|
|
1945
|
+
const outputIndex = readResponsesOutputIndex(event);
|
|
1946
|
+
if (outputIndex === void 0) {
|
|
1947
|
+
if (unindexed) throw new Error("Responses stream added overlapping unindexed output items");
|
|
1948
|
+
unindexed = slot;
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
if (indexed.has(outputIndex)) throw new Error(`Responses stream reused active output index ${outputIndex}`);
|
|
1952
|
+
indexed.set(outputIndex, slot);
|
|
1953
|
+
},
|
|
1954
|
+
resolve(event, type) {
|
|
1955
|
+
const outputIndex = readResponsesOutputIndex(event);
|
|
1956
|
+
let slot = outputIndex === void 0 ? unindexed : indexed.get(outputIndex);
|
|
1957
|
+
if (outputIndex === void 0 && !slot) {
|
|
1958
|
+
const matches = [...indexed.values()].filter((candidate) => candidate.type === type);
|
|
1959
|
+
slot = matches.length === 1 ? matches[0] : void 0;
|
|
1960
|
+
}
|
|
1961
|
+
return slot?.type === type ? slot : void 0;
|
|
1962
|
+
},
|
|
1963
|
+
get(event) {
|
|
1964
|
+
const outputIndex = readResponsesOutputIndex(event);
|
|
1965
|
+
return outputIndex === void 0 ? unindexed : indexed.get(outputIndex);
|
|
1966
|
+
},
|
|
1967
|
+
values() {
|
|
1968
|
+
return [.../* @__PURE__ */ new Set([...indexed.values(), ...unindexed ? [unindexed] : []])];
|
|
1969
|
+
},
|
|
1970
|
+
forget(slot) {
|
|
1971
|
+
if (unindexed === slot) unindexed = void 0;
|
|
1972
|
+
for (const [outputIndex, candidate] of indexed) if (candidate === slot) indexed.delete(outputIndex);
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
//#endregion
|
|
1977
|
+
//#region packages/ai/src/providers/openai-responses-terminal-usage.ts
|
|
1978
|
+
function readCount(value) {
|
|
1979
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Split a terminal usage payload into the priced buckets.
|
|
1983
|
+
*
|
|
1984
|
+
* OpenAI includes cache reads and writes in `input_tokens`, so both are subtracted out of the
|
|
1985
|
+
* billable input bucket. `total_tokens` comes from the payload, but never below the sum of the
|
|
1986
|
+
* split buckets: proxies routinely omit it (reporting 0 would understate the turn), and a payload
|
|
1987
|
+
* whose `cached_tokens` exceeds `input_tokens` clamps the input bucket, leaving the reported total
|
|
1988
|
+
* short of what the buckets actually price.
|
|
1989
|
+
*/
|
|
1990
|
+
function mapResponsesTerminalUsage(usage) {
|
|
1991
|
+
if (!usage) return;
|
|
1992
|
+
const cacheRead = readCount(usage.input_tokens_details?.cached_tokens);
|
|
1993
|
+
const cacheWrite = readCount(usage.input_tokens_details?.cache_write_tokens);
|
|
1994
|
+
const input = Math.max(0, readCount(usage.input_tokens) - cacheRead - cacheWrite);
|
|
1995
|
+
const output = readCount(usage.output_tokens);
|
|
1996
|
+
const bucketTotal = input + output + cacheRead + cacheWrite;
|
|
1997
|
+
return {
|
|
1998
|
+
input,
|
|
1999
|
+
output,
|
|
2000
|
+
cacheRead,
|
|
2001
|
+
cacheWrite,
|
|
2002
|
+
totalTokens: Math.max(bucketTotal, readCount(usage.total_tokens))
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
/** Reasoning tokens are reported by the agent path only; the package path does not track them. */
|
|
2006
|
+
function readResponsesReasoningTokens(usage) {
|
|
2007
|
+
const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens;
|
|
2008
|
+
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) ? reasoningTokens : void 0;
|
|
2009
|
+
}
|
|
2010
|
+
function mapResponsesTerminalStopReason(status) {
|
|
2011
|
+
if (!status) return "stop";
|
|
2012
|
+
switch (status) {
|
|
2013
|
+
case "completed": return "stop";
|
|
2014
|
+
case "incomplete": return "length";
|
|
2015
|
+
case "failed":
|
|
2016
|
+
case "cancelled": return "error";
|
|
2017
|
+
case "in_progress":
|
|
2018
|
+
case "queued": return "stop";
|
|
2019
|
+
default: throw new Error(`Unhandled stop reason: ${String(status)}`);
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Resolve the terminal stop reason, including the two overrides every Responses path shares: a
|
|
2024
|
+
* content-filtered turn is a provider error rather than a truncated answer, and a turn that
|
|
2025
|
+
* produced tool calls reports `toolUse` instead of a plain stop.
|
|
2026
|
+
*/
|
|
2027
|
+
function resolveResponsesTerminalStopReason(params) {
|
|
2028
|
+
if (params.status === "incomplete" && params.incompleteReason === "content_filter") return {
|
|
2029
|
+
stopReason: "error",
|
|
2030
|
+
errorMessage: "Provider incomplete_reason: content_filter"
|
|
2031
|
+
};
|
|
2032
|
+
const stopReason = mapResponsesTerminalStopReason(params.status);
|
|
2033
|
+
if (stopReason === "stop" && params.hasToolCall) return { stopReason: "toolUse" };
|
|
2034
|
+
return { stopReason };
|
|
2035
|
+
}
|
|
2036
|
+
//#endregion
|
|
2037
|
+
//#region packages/ai/src/transports/openai-responses-stream-terminal-internal.ts
|
|
2038
|
+
function splitToolCallId(id) {
|
|
2039
|
+
const separator = id.indexOf("|");
|
|
2040
|
+
return separator === -1 ? [id, void 0] : [id.slice(0, separator), id.slice(separator + 1)];
|
|
2041
|
+
}
|
|
2042
|
+
function resolveResponsesToolCallId(item, fallbackId) {
|
|
2043
|
+
const callId = typeof item.call_id === "string" ? item.call_id.trim() : "";
|
|
2044
|
+
const itemId = typeof item.id === "string" ? item.id.trim() : "";
|
|
2045
|
+
const [fallbackCallId, fallbackItemId = ""] = splitToolCallId(fallbackId ?? "");
|
|
2046
|
+
const resolvedCallId = callId || fallbackCallId;
|
|
2047
|
+
const resolvedItemId = itemId || fallbackItemId;
|
|
2048
|
+
if (resolvedCallId) return resolvedItemId ? `${resolvedCallId}|${resolvedItemId}` : resolvedCallId;
|
|
2049
|
+
const generated = `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`;
|
|
2050
|
+
return resolvedItemId ? `${generated}|${resolvedItemId}` : generated;
|
|
2051
|
+
}
|
|
2052
|
+
function resolveCompletedToolCallName(toolCall, value) {
|
|
2053
|
+
const streamedName = toolCall?.block.name.trim() || void 0;
|
|
2054
|
+
const completedName = typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
2055
|
+
if (streamedName && completedName && streamedName !== completedName) throw new Error(`Responses stream changed tool-call function name from ${streamedName} to ${completedName}`);
|
|
2056
|
+
const name = completedName ?? streamedName;
|
|
2057
|
+
if (!name) throw new Error("Responses stream completed tool call without a function name");
|
|
2058
|
+
return name;
|
|
2059
|
+
}
|
|
2060
|
+
function createResponsesTerminalController(params) {
|
|
2061
|
+
const { output, stream, model, options } = params;
|
|
2062
|
+
const blocks = output.content;
|
|
2063
|
+
const backfillReasoning = (items) => {
|
|
2064
|
+
for (const item of items) {
|
|
2065
|
+
if (item.type !== "reasoning" || !item.encrypted_content) continue;
|
|
2066
|
+
const block = params.reasoningBlocksById.get(item.id);
|
|
2067
|
+
if (!block?.thinkingSignature) continue;
|
|
2068
|
+
const stored = JSON.parse(block.thinkingSignature);
|
|
2069
|
+
if (!stored.encrypted_content) block.thinkingSignature = JSON.stringify({
|
|
2070
|
+
...stored,
|
|
2071
|
+
encrypted_content: item.encrypted_content
|
|
2072
|
+
});
|
|
2073
|
+
if (options?.reasoningReplayMetadata) block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = options.reasoningReplayMetadata;
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
const appendText = (item) => {
|
|
2077
|
+
const text = (Array.isArray(item.content) ? item.content : []).map((part) => {
|
|
2078
|
+
const content = part;
|
|
2079
|
+
return content.type === "output_text" || content.type === "text" ? content.text ?? "" : content.refusal ?? "";
|
|
2080
|
+
}).join("");
|
|
2081
|
+
if (!text) return;
|
|
2082
|
+
const phase = item.phase ?? void 0;
|
|
2083
|
+
const previous = params.getLastTextBlock();
|
|
2084
|
+
const collapse = resolveResponsesMessageSnapshotCollapse({
|
|
2085
|
+
prior: previous && {
|
|
2086
|
+
text: previous.block.text,
|
|
2087
|
+
phase: previous.phase
|
|
2088
|
+
},
|
|
2089
|
+
nextText: text,
|
|
2090
|
+
nextPhase: phase
|
|
2091
|
+
});
|
|
2092
|
+
if (collapse.kind === "extend" && previous) {
|
|
2093
|
+
previous.block.text = collapse.text;
|
|
2094
|
+
previous.block.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2095
|
+
stream.push({
|
|
2096
|
+
type: "text_end",
|
|
2097
|
+
contentIndex: previous.index,
|
|
2098
|
+
content: collapse.text,
|
|
2099
|
+
partial: output
|
|
2100
|
+
});
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
const block = {
|
|
2104
|
+
type: "text",
|
|
2105
|
+
text,
|
|
2106
|
+
textSignature: encodeTextSignatureV1(item.id, phase)
|
|
2107
|
+
};
|
|
2108
|
+
blocks.push(block);
|
|
2109
|
+
const index = blocks.length - 1;
|
|
2110
|
+
params.setLastTextBlock({
|
|
2111
|
+
block,
|
|
2112
|
+
index,
|
|
2113
|
+
phase
|
|
2114
|
+
});
|
|
2115
|
+
stream.push({
|
|
2116
|
+
type: "text_start",
|
|
2117
|
+
contentIndex: index,
|
|
2118
|
+
partial: output
|
|
2119
|
+
});
|
|
2120
|
+
stream.push({
|
|
2121
|
+
type: "text_end",
|
|
2122
|
+
contentIndex: index,
|
|
2123
|
+
content: text,
|
|
2124
|
+
partial: output
|
|
2125
|
+
});
|
|
2126
|
+
};
|
|
2127
|
+
const appendToolCall = (item) => {
|
|
2128
|
+
const toolCall = {
|
|
2129
|
+
type: "toolCall",
|
|
2130
|
+
id: resolveResponsesToolCallId(item),
|
|
2131
|
+
name: resolveCompletedToolCallName(void 0, item.name),
|
|
2132
|
+
arguments: parseStreamingJson(item.arguments || "{}")
|
|
2133
|
+
};
|
|
2134
|
+
blocks.push(toolCall);
|
|
2135
|
+
const contentIndex = blocks.length - 1;
|
|
2136
|
+
stream.push({
|
|
2137
|
+
type: "toolcall_start",
|
|
2138
|
+
contentIndex,
|
|
2139
|
+
partial: output
|
|
2140
|
+
});
|
|
2141
|
+
stream.push({
|
|
2142
|
+
type: "toolcall_end",
|
|
2143
|
+
contentIndex,
|
|
2144
|
+
toolCall,
|
|
2145
|
+
partial: output
|
|
2146
|
+
});
|
|
2147
|
+
};
|
|
2148
|
+
const recoverTerminalOutput = (items, includeToolCalls) => {
|
|
2149
|
+
if (blocks.length > 0) return;
|
|
2150
|
+
for (const item of items) if (item.type === "message") appendText(item);
|
|
2151
|
+
else {
|
|
2152
|
+
params.setLastTextBlock(null);
|
|
2153
|
+
if (includeToolCalls && item.type === "function_call") appendToolCall(item);
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
const finalizeResponse = (response) => {
|
|
2157
|
+
params.markFinalized();
|
|
2158
|
+
backfillReasoning(response.output ?? []);
|
|
2159
|
+
output.responseId = response.id || output.responseId;
|
|
2160
|
+
const usage = mapResponsesTerminalUsage(response.usage);
|
|
2161
|
+
const reasoningTokens = readResponsesReasoningTokens(response.usage);
|
|
2162
|
+
if (usage) output.usage = {
|
|
2163
|
+
...usage,
|
|
2164
|
+
...reasoningTokens === void 0 ? {} : { reasoningTokens },
|
|
2165
|
+
cost: {
|
|
2166
|
+
input: 0,
|
|
2167
|
+
output: 0,
|
|
2168
|
+
cacheRead: 0,
|
|
2169
|
+
cacheWrite: 0,
|
|
2170
|
+
total: 0
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
calculateCost(model, output.usage);
|
|
2174
|
+
if (options?.applyServiceTierPricing) {
|
|
2175
|
+
const tier = options.resolveServiceTier ? options.resolveServiceTier(response.service_tier, options.serviceTier) : response.service_tier ?? options.serviceTier;
|
|
2176
|
+
options.applyServiceTierPricing(output.usage, tier);
|
|
2177
|
+
}
|
|
2178
|
+
const terminal = resolveResponsesTerminalStopReason({
|
|
2179
|
+
status: response.status,
|
|
2180
|
+
incompleteReason: response.incomplete_details?.reason,
|
|
2181
|
+
hasToolCall: blocks.some((block) => block.type === "toolCall")
|
|
2182
|
+
});
|
|
2183
|
+
output.stopReason = terminal.stopReason;
|
|
2184
|
+
output.errorMessage = terminal.errorMessage;
|
|
2185
|
+
};
|
|
2186
|
+
return {
|
|
2187
|
+
finalizeResponse,
|
|
2188
|
+
recoverTerminalOutput
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
//#endregion
|
|
2192
|
+
//#region packages/ai/src/transports/openai-responses-stream-internal.ts
|
|
2193
|
+
var ResponsesStreamFailure = class extends Error {
|
|
2194
|
+
constructor(failure, response) {
|
|
2195
|
+
super(failure.message);
|
|
2196
|
+
this.name = "ResponsesStreamFailure";
|
|
2197
|
+
this.responseId = failure.responseId;
|
|
2198
|
+
this.response = response;
|
|
2199
|
+
this.observation = failure.observation;
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2202
|
+
async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
2203
|
+
const streamingToolCalls = createResponsesToolCallTracker();
|
|
2204
|
+
const outputSlots = createResponsesOutputSlotTracker();
|
|
2205
|
+
const reasoningBlocksById = /* @__PURE__ */ new Map();
|
|
2206
|
+
let terminalResponseEvent;
|
|
2207
|
+
let lastTextBlock = null;
|
|
2208
|
+
const blocks = output.content;
|
|
2209
|
+
const blockIndex = () => blocks.length - 1;
|
|
2210
|
+
const createOutputSlot = (event, item) => {
|
|
2211
|
+
if (item.type === "reasoning") {
|
|
2212
|
+
const block = {
|
|
2213
|
+
type: "thinking",
|
|
2214
|
+
thinking: ""
|
|
2215
|
+
};
|
|
2216
|
+
const slot = {
|
|
2217
|
+
type: "thinking",
|
|
2218
|
+
item,
|
|
2219
|
+
block,
|
|
2220
|
+
contentIndex: blocks.length
|
|
2221
|
+
};
|
|
2222
|
+
blocks.push(block);
|
|
2223
|
+
outputSlots.register(event, slot);
|
|
2224
|
+
stream.push({
|
|
2225
|
+
type: "thinking_start",
|
|
2226
|
+
contentIndex: slot.contentIndex,
|
|
2227
|
+
partial: output
|
|
2228
|
+
});
|
|
2229
|
+
return slot;
|
|
2230
|
+
}
|
|
2231
|
+
if (item.type === "message") {
|
|
2232
|
+
const messageItem = item;
|
|
2233
|
+
const collapseCandidate = lastTextBlock;
|
|
2234
|
+
const block = collapseCandidate ? null : {
|
|
2235
|
+
type: "text",
|
|
2236
|
+
text: "",
|
|
2237
|
+
...messageItem.phase ? { textSignature: encodeTextSignatureV1(messageItem.id, messageItem.phase) } : {}
|
|
2238
|
+
};
|
|
2239
|
+
const slot = {
|
|
2240
|
+
type: "text",
|
|
2241
|
+
item: messageItem,
|
|
2242
|
+
block,
|
|
2243
|
+
contentIndex: block ? blocks.length : void 0,
|
|
2244
|
+
pendingText: collapseCandidate ? "" : null,
|
|
2245
|
+
collapseCandidate
|
|
2246
|
+
};
|
|
2247
|
+
if (block) blocks.push(block);
|
|
2248
|
+
outputSlots.register(event, slot);
|
|
2249
|
+
if (slot.contentIndex !== void 0) stream.push({
|
|
2250
|
+
type: "text_start",
|
|
2251
|
+
contentIndex: slot.contentIndex,
|
|
2252
|
+
partial: output
|
|
2253
|
+
});
|
|
2254
|
+
return slot;
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
const resolveOutputItemSlot = (event, item) => {
|
|
2258
|
+
if (item.type === "reasoning") return outputSlots.resolve(event, "thinking");
|
|
2259
|
+
if (item.type === "message") return outputSlots.resolve(event, "text");
|
|
2260
|
+
return readResponsesOutputIndex(event) === void 0 ? void 0 : outputSlots.get(event);
|
|
2261
|
+
};
|
|
2262
|
+
const getOrCreateOutputSlot = (event, item) => {
|
|
2263
|
+
return resolveOutputItemSlot(event, item) ?? createOutputSlot(event, item);
|
|
2264
|
+
};
|
|
2265
|
+
const materializeDeferredTextSlot = (slot) => {
|
|
2266
|
+
if (slot.block || slot.pendingText === null) return;
|
|
2267
|
+
const text = slot.pendingText;
|
|
2268
|
+
slot.block = {
|
|
2269
|
+
type: "text",
|
|
2270
|
+
text,
|
|
2271
|
+
...slot.item.phase ? { textSignature: encodeTextSignatureV1(slot.item.id, slot.item.phase) } : {}
|
|
2272
|
+
};
|
|
2273
|
+
blocks.push(slot.block);
|
|
2274
|
+
slot.contentIndex = blockIndex();
|
|
2275
|
+
stream.push({
|
|
2276
|
+
type: "text_start",
|
|
2277
|
+
contentIndex: slot.contentIndex,
|
|
2278
|
+
partial: output
|
|
2279
|
+
});
|
|
2280
|
+
if (text) stream.push({
|
|
2281
|
+
type: "text_delta",
|
|
2282
|
+
contentIndex: slot.contentIndex,
|
|
2283
|
+
delta: text
|
|
2284
|
+
});
|
|
2285
|
+
if (lastTextBlock === slot.collapseCandidate) lastTextBlock = null;
|
|
2286
|
+
slot.pendingText = null;
|
|
2287
|
+
slot.collapseCandidate = null;
|
|
2288
|
+
};
|
|
2289
|
+
const materializeDeferredTextSlots = (except) => {
|
|
2290
|
+
for (const slot of outputSlots.values()) if (slot !== except && slot.type === "text") materializeDeferredTextSlot(slot);
|
|
2291
|
+
};
|
|
2292
|
+
const appendPendingMessageDelta = (slot, delta) => {
|
|
2293
|
+
slot.pendingText = `${slot.pendingText ?? ""}${delta}`;
|
|
2294
|
+
const priorText = slot.collapseCandidate?.block.text ?? "";
|
|
2295
|
+
if (priorText.startsWith(slot.pendingText) || slot.pendingText.startsWith(priorText)) return;
|
|
2296
|
+
materializeDeferredTextSlot(slot);
|
|
2297
|
+
};
|
|
2298
|
+
const { finalizeResponse, recoverTerminalOutput } = createResponsesTerminalController({
|
|
2299
|
+
output,
|
|
2300
|
+
stream,
|
|
2301
|
+
model,
|
|
2302
|
+
options,
|
|
2303
|
+
reasoningBlocksById,
|
|
2304
|
+
getLastTextBlock: () => lastTextBlock,
|
|
2305
|
+
setLastTextBlock: (block) => {
|
|
2306
|
+
lastTextBlock = block;
|
|
2307
|
+
},
|
|
2308
|
+
markFinalized: () => {
|
|
2309
|
+
terminalResponseEvent = "finalized";
|
|
2310
|
+
}
|
|
2311
|
+
});
|
|
2312
|
+
const guardedStream = adaptResponsesStream(withFirstStreamEventTimeout(openaiStream, {
|
|
2313
|
+
provider: model.provider,
|
|
2314
|
+
api: model.api,
|
|
2315
|
+
model: model.id,
|
|
2316
|
+
timeoutMs: options?.firstEventTimeoutMs ?? 0,
|
|
2317
|
+
stage: "responses",
|
|
2318
|
+
abort: options?.abortFirstEventStream,
|
|
2319
|
+
onTimeout: options?.onFirstEventTimeout,
|
|
2320
|
+
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."
|
|
2321
|
+
}), options?.signal);
|
|
2322
|
+
try {
|
|
2323
|
+
for await (const event of guardedStream) if (event.type === "response.created") output.responseId = event.response.id;
|
|
2324
|
+
else if (event.type === "response.output_item.added") {
|
|
2325
|
+
materializeDeferredTextSlots();
|
|
2326
|
+
const item = event.item;
|
|
2327
|
+
if (item.type !== "message") lastTextBlock = null;
|
|
2328
|
+
if (item.type === "reasoning" || item.type === "message") createOutputSlot(event, item);
|
|
2329
|
+
else if (item.type === "function_call") {
|
|
2330
|
+
const toolCallBlock = {
|
|
2331
|
+
type: "toolCall",
|
|
2332
|
+
id: resolveResponsesToolCallId(item),
|
|
2333
|
+
name: typeof item.name === "string" ? item.name.trim() : "",
|
|
2334
|
+
arguments: {},
|
|
2335
|
+
partialJson: item.arguments || ""
|
|
2336
|
+
};
|
|
2337
|
+
const contentIndex = output.content.length;
|
|
2338
|
+
const toolCallState = {
|
|
2339
|
+
block: toolCallBlock,
|
|
2340
|
+
contentIndex,
|
|
2341
|
+
argumentStreamReliable: true,
|
|
2342
|
+
...readResponsesToolCallItemIdentity(item)
|
|
2343
|
+
};
|
|
2344
|
+
streamingToolCalls.register(event, toolCallState);
|
|
2345
|
+
if (readResponsesOutputIndex(event) !== void 0) outputSlots.register(event, {
|
|
2346
|
+
type: "toolCall",
|
|
2347
|
+
toolCall: toolCallState
|
|
2348
|
+
});
|
|
2349
|
+
output.content.push(toolCallBlock);
|
|
2350
|
+
stream.push({
|
|
2351
|
+
type: "toolcall_start",
|
|
2352
|
+
contentIndex,
|
|
2353
|
+
partial: output
|
|
2354
|
+
});
|
|
2355
|
+
}
|
|
2356
|
+
} else if (event.type === "response.reasoning_summary_part.added") {
|
|
2357
|
+
const slot = outputSlots.resolve(event, "thinking");
|
|
2358
|
+
if (!slot) continue;
|
|
2359
|
+
slot.item.summary = slot.item.summary || [];
|
|
2360
|
+
slot.item.summary.push(event.part);
|
|
2361
|
+
} else if (event.type === "response.reasoning_summary_text.delta") {
|
|
2362
|
+
const slot = outputSlots.resolve(event, "thinking");
|
|
2363
|
+
if (!slot) continue;
|
|
2364
|
+
slot.item.summary = slot.item.summary || [];
|
|
2365
|
+
const lastPart = slot.item.summary[slot.item.summary.length - 1];
|
|
2366
|
+
if (!lastPart) continue;
|
|
2367
|
+
slot.block.thinking += event.delta;
|
|
2368
|
+
lastPart.text += event.delta;
|
|
2369
|
+
stream.push({
|
|
2370
|
+
type: "thinking_delta",
|
|
2371
|
+
contentIndex: slot.contentIndex,
|
|
2372
|
+
delta: event.delta,
|
|
2373
|
+
partial: output
|
|
2374
|
+
});
|
|
2375
|
+
} else if (event.type === "response.reasoning_summary_part.done") {
|
|
2376
|
+
const slot = outputSlots.resolve(event, "thinking");
|
|
2377
|
+
if (!slot) continue;
|
|
2378
|
+
slot.item.summary = slot.item.summary || [];
|
|
2379
|
+
const lastPart = slot.item.summary[slot.item.summary.length - 1];
|
|
2380
|
+
if (!lastPart) continue;
|
|
2381
|
+
slot.block.thinking += "\n\n";
|
|
2382
|
+
lastPart.text += "\n\n";
|
|
2383
|
+
stream.push({
|
|
2384
|
+
type: "thinking_delta",
|
|
2385
|
+
contentIndex: slot.contentIndex,
|
|
2386
|
+
delta: "\n\n",
|
|
2387
|
+
partial: output
|
|
2388
|
+
});
|
|
2389
|
+
} else if (event.type === "response.reasoning_text.delta") {
|
|
2390
|
+
const slot = outputSlots.resolve(event, "thinking");
|
|
2391
|
+
if (!slot) continue;
|
|
2392
|
+
slot.block.thinking += event.delta;
|
|
2393
|
+
stream.push({
|
|
2394
|
+
type: "thinking_delta",
|
|
2395
|
+
contentIndex: slot.contentIndex,
|
|
2396
|
+
delta: event.delta,
|
|
2397
|
+
partial: output
|
|
2398
|
+
});
|
|
2399
|
+
} else if (event.type === "response.content_part.added") {
|
|
2400
|
+
const slot = outputSlots.resolve(event, "text");
|
|
2401
|
+
if (!slot) continue;
|
|
2402
|
+
slot.item.content = slot.item.content || [];
|
|
2403
|
+
if (event.part.type === "output_text" || event.part.type === "text" || event.part.type === "refusal") slot.item.content.push(event.part);
|
|
2404
|
+
} else if (event.type === "response.output_text.delta") {
|
|
2405
|
+
const slot = outputSlots.resolve(event, "text");
|
|
2406
|
+
if (!slot) continue;
|
|
2407
|
+
slot.item.content ||= [];
|
|
2408
|
+
let lastPart = slot.item.content[slot.item.content.length - 1];
|
|
2409
|
+
if (!isResponsesTextContentPartType(lastPart?.type)) {
|
|
2410
|
+
lastPart = {
|
|
2411
|
+
type: "output_text",
|
|
2412
|
+
text: "",
|
|
2413
|
+
annotations: []
|
|
2414
|
+
};
|
|
2415
|
+
slot.item.content.push(lastPart);
|
|
2416
|
+
}
|
|
2417
|
+
lastPart.text += event.delta;
|
|
2418
|
+
if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
|
|
2419
|
+
else if (slot.block && slot.contentIndex !== void 0) {
|
|
2420
|
+
slot.block.text += event.delta;
|
|
2421
|
+
stream.push({
|
|
2422
|
+
type: "text_delta",
|
|
2423
|
+
contentIndex: slot.contentIndex,
|
|
2424
|
+
delta: event.delta
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
} else if (isAzureResponsesTextDeltaEvent(event)) {
|
|
2428
|
+
const slot = outputSlots.resolve(event, "text");
|
|
2429
|
+
if (!slot) continue;
|
|
2430
|
+
slot.item.content = slot.item.content || [];
|
|
2431
|
+
let lastPart = slot.item.content[slot.item.content.length - 1];
|
|
2432
|
+
if (lastPart?.type !== "text") {
|
|
2433
|
+
lastPart = {
|
|
2434
|
+
type: "text",
|
|
2435
|
+
text: ""
|
|
2436
|
+
};
|
|
2437
|
+
slot.item.content.push(lastPart);
|
|
2438
|
+
}
|
|
2439
|
+
lastPart.text += event.delta;
|
|
2440
|
+
if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
|
|
2441
|
+
else if (slot.block && slot.contentIndex !== void 0) {
|
|
2442
|
+
slot.block.text += event.delta;
|
|
2443
|
+
stream.push({
|
|
2444
|
+
type: "text_delta",
|
|
2445
|
+
contentIndex: slot.contentIndex,
|
|
2446
|
+
delta: event.delta
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
} else if (event.type === "response.refusal.delta") {
|
|
2450
|
+
const slot = outputSlots.resolve(event, "text");
|
|
2451
|
+
if (!slot) continue;
|
|
2452
|
+
slot.item.content ||= [];
|
|
2453
|
+
let lastPart = slot.item.content[slot.item.content.length - 1];
|
|
2454
|
+
if (lastPart?.type !== "refusal") {
|
|
2455
|
+
lastPart = {
|
|
2456
|
+
type: "refusal",
|
|
2457
|
+
refusal: ""
|
|
2458
|
+
};
|
|
2459
|
+
slot.item.content.push(lastPart);
|
|
2460
|
+
}
|
|
2461
|
+
lastPart.refusal += event.delta;
|
|
2462
|
+
if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
|
|
2463
|
+
else if (slot.block && slot.contentIndex !== void 0) {
|
|
2464
|
+
slot.block.text += event.delta;
|
|
2465
|
+
stream.push({
|
|
2466
|
+
type: "text_delta",
|
|
2467
|
+
contentIndex: slot.contentIndex,
|
|
2468
|
+
delta: event.delta
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
} else if (event.type === "response.function_call_arguments.delta") {
|
|
2472
|
+
const toolCall = streamingToolCalls.resolve(event);
|
|
2473
|
+
if (toolCall) {
|
|
2474
|
+
toolCall.block.partialJson += event.delta;
|
|
2475
|
+
toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
2476
|
+
stream.push({
|
|
2477
|
+
type: "toolcall_delta",
|
|
2478
|
+
contentIndex: toolCall.contentIndex,
|
|
2479
|
+
delta: event.delta,
|
|
2480
|
+
partial: output
|
|
2481
|
+
});
|
|
2482
|
+
} else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
|
|
2483
|
+
} else if (event.type === "response.function_call_arguments.done") {
|
|
2484
|
+
const toolCall = streamingToolCalls.resolve(event);
|
|
2485
|
+
if (toolCall) {
|
|
2486
|
+
const previousPartialJson = toolCall.block.partialJson;
|
|
2487
|
+
const doneArguments = typeof event.arguments === "string" ? event.arguments : void 0;
|
|
2488
|
+
if (doneArguments !== void 0 && (doneArguments.length > 0 || previousPartialJson === "")) {
|
|
2489
|
+
toolCall.block.partialJson = doneArguments;
|
|
2490
|
+
toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
2491
|
+
toolCall.argumentStreamReliable = true;
|
|
2492
|
+
}
|
|
2493
|
+
if (doneArguments?.startsWith(previousPartialJson)) {
|
|
2494
|
+
const delta = doneArguments.slice(previousPartialJson.length);
|
|
2495
|
+
if (delta.length > 0) stream.push({
|
|
2496
|
+
type: "toolcall_delta",
|
|
2497
|
+
contentIndex: toolCall.contentIndex,
|
|
2498
|
+
delta,
|
|
2499
|
+
partial: output
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
} else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
|
|
2503
|
+
} else if (event.type === "response.output_item.done") {
|
|
2504
|
+
const item = event.item;
|
|
2505
|
+
if (item.type !== "message") lastTextBlock = null;
|
|
2506
|
+
const existingOutputSlot = resolveOutputItemSlot(event, item);
|
|
2507
|
+
materializeDeferredTextSlots(existingOutputSlot);
|
|
2508
|
+
const outputSlot = existingOutputSlot ?? getOrCreateOutputSlot(event, item);
|
|
2509
|
+
if (item.type === "reasoning" && outputSlot?.type === "thinking") {
|
|
2510
|
+
const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
|
|
2511
|
+
const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
|
|
2512
|
+
outputSlot.block.thinking = summaryText || contentText || outputSlot.block.thinking;
|
|
2513
|
+
outputSlot.block.thinkingSignature = JSON.stringify(item);
|
|
2514
|
+
if (item.encrypted_content && options?.reasoningReplayMetadata) outputSlot.block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = options.reasoningReplayMetadata;
|
|
2515
|
+
if (typeof item.id === "string") reasoningBlocksById.set(item.id, outputSlot.block);
|
|
2516
|
+
stream.push({
|
|
2517
|
+
type: "thinking_end",
|
|
2518
|
+
contentIndex: outputSlot.contentIndex,
|
|
2519
|
+
content: outputSlot.block.thinking,
|
|
2520
|
+
partial: output
|
|
2521
|
+
});
|
|
2522
|
+
outputSlots.forget(outputSlot);
|
|
2523
|
+
} else if (item.type === "message" && outputSlot?.type === "text" && (outputSlot.block || outputSlot.pendingText !== null)) {
|
|
2524
|
+
const streamedText = outputSlot.pendingText ?? outputSlot.block?.text ?? "";
|
|
2525
|
+
const finalText = item.content == null ? streamedText : item.content.map((c) => c.type === "output_text" || c.type === "text" ? c.text : c.refusal).join("");
|
|
2526
|
+
const phase = item.phase ?? void 0;
|
|
2527
|
+
const collapse = outputSlot.pendingText !== null ? resolveResponsesMessageSnapshotCollapse({
|
|
2528
|
+
prior: outputSlot.collapseCandidate && {
|
|
2529
|
+
text: outputSlot.collapseCandidate.block.text,
|
|
2530
|
+
phase: outputSlot.collapseCandidate.phase
|
|
2531
|
+
},
|
|
2532
|
+
nextText: finalText,
|
|
2533
|
+
nextPhase: phase
|
|
2534
|
+
}) : { kind: "keep" };
|
|
2535
|
+
outputSlot.pendingText = null;
|
|
2536
|
+
if (collapse.kind === "extend" && outputSlot.collapseCandidate) {
|
|
2537
|
+
outputSlot.collapseCandidate.block.text = collapse.text;
|
|
2538
|
+
outputSlot.collapseCandidate.block.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2539
|
+
stream.push({
|
|
2540
|
+
type: "text_end",
|
|
2541
|
+
contentIndex: outputSlot.collapseCandidate.index,
|
|
2542
|
+
content: collapse.text,
|
|
2543
|
+
partial: output
|
|
2544
|
+
});
|
|
2545
|
+
lastTextBlock = outputSlot.collapseCandidate;
|
|
2546
|
+
} else {
|
|
2547
|
+
if (!outputSlot.block) {
|
|
2548
|
+
outputSlot.block = {
|
|
2549
|
+
type: "text",
|
|
2550
|
+
text: "",
|
|
2551
|
+
...phase ? { textSignature: encodeTextSignatureV1(item.id, phase) } : {}
|
|
2552
|
+
};
|
|
2553
|
+
blocks.push(outputSlot.block);
|
|
2554
|
+
outputSlot.contentIndex = blockIndex();
|
|
2555
|
+
stream.push({
|
|
2556
|
+
type: "text_start",
|
|
2557
|
+
contentIndex: outputSlot.contentIndex,
|
|
2558
|
+
partial: output
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
outputSlot.block.text = finalText;
|
|
2562
|
+
outputSlot.block.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2563
|
+
const contentIndex = outputSlot.contentIndex;
|
|
2564
|
+
if (contentIndex === void 0) throw new Error("Responses stream finalized text without a content index");
|
|
2565
|
+
lastTextBlock = {
|
|
2566
|
+
block: outputSlot.block,
|
|
2567
|
+
index: contentIndex,
|
|
2568
|
+
phase
|
|
2569
|
+
};
|
|
2570
|
+
stream.push({
|
|
2571
|
+
type: "text_end",
|
|
2572
|
+
contentIndex,
|
|
2573
|
+
content: outputSlot.block.text,
|
|
2574
|
+
partial: output
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2577
|
+
outputSlots.forget(outputSlot);
|
|
2578
|
+
} else if (item.type === "function_call") {
|
|
2579
|
+
const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
|
|
2580
|
+
if (!streamingToolCall && streamingToolCalls.hasActive()) continue;
|
|
2581
|
+
const completedName = resolveCompletedToolCallName(streamingToolCall, item.name);
|
|
2582
|
+
const streamedArguments = streamingToolCall?.block.partialJson ?? "";
|
|
2583
|
+
const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
|
|
2584
|
+
if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) continue;
|
|
2585
|
+
const args = parseStreamingJson(completedArguments !== void 0 && (completedArguments.length > 0 || !streamedArguments) ? completedArguments : streamedArguments || "{}");
|
|
2586
|
+
let toolCall;
|
|
2587
|
+
let contentIndex;
|
|
2588
|
+
if (streamingToolCall) {
|
|
2589
|
+
const block = streamingToolCall.block;
|
|
2590
|
+
block.id = resolveResponsesToolCallId(item, block.id);
|
|
2591
|
+
block.name = completedName;
|
|
2592
|
+
block.arguments = args;
|
|
2593
|
+
delete block.partialJson;
|
|
2594
|
+
toolCall = block;
|
|
2595
|
+
contentIndex = streamingToolCall.contentIndex;
|
|
2596
|
+
} else {
|
|
2597
|
+
toolCall = {
|
|
2598
|
+
type: "toolCall",
|
|
2599
|
+
id: resolveResponsesToolCallId(item),
|
|
2600
|
+
name: completedName,
|
|
2601
|
+
arguments: args
|
|
2602
|
+
};
|
|
2603
|
+
blocks.push(toolCall);
|
|
2604
|
+
contentIndex = blockIndex();
|
|
2605
|
+
stream.push({
|
|
2606
|
+
type: "toolcall_start",
|
|
2607
|
+
contentIndex,
|
|
2608
|
+
partial: output
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
if (streamingToolCall) {
|
|
2612
|
+
streamingToolCalls.forget(streamingToolCall);
|
|
2613
|
+
for (const slot of outputSlots.values()) if (slot.type === "toolCall" && slot.toolCall === streamingToolCall) outputSlots.forget(slot);
|
|
2614
|
+
}
|
|
2615
|
+
stream.push({
|
|
2616
|
+
type: "toolcall_end",
|
|
2617
|
+
contentIndex,
|
|
2618
|
+
toolCall,
|
|
2619
|
+
partial: output
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
} else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
2623
|
+
if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
2624
|
+
finalizeResponse(event.response);
|
|
2625
|
+
if (event.type === "response.completed" || output.stopReason === "length") recoverTerminalOutput(event.response.output ?? [], event.type === "response.completed");
|
|
2626
|
+
if (output.stopReason === "stop" && output.content.some((block) => block.type === "toolCall")) output.stopReason = "toolUse";
|
|
2627
|
+
break;
|
|
2628
|
+
} else if (event.type === "error") throw new Error(event.message ? `Error Code ${event.code}: ${event.message}` : "Unknown error");
|
|
2629
|
+
else if (event.type === "response.failed") {
|
|
2630
|
+
const failure = normalizeResponsesFailedEvent(event, model);
|
|
2631
|
+
if (failure.responseId) output.responseId = failure.responseId;
|
|
2632
|
+
throw new ResponsesStreamFailure(failure, event.response);
|
|
2633
|
+
}
|
|
2634
|
+
if (streamingToolCalls.hasActive()) throw new Error("Responses stream ended with unresolved tool calls");
|
|
2635
|
+
if (!terminalResponseEvent) throw new Error("OpenAI Responses stream ended before a terminal response event");
|
|
2636
|
+
} finally {
|
|
2637
|
+
for (const block of output.content) delete block.partialJson;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
//#endregion
|
|
2641
|
+
//#region packages/ai/src/providers/openai-tool-schema-compat.ts
|
|
2642
|
+
const OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
|
|
2643
|
+
"$defs",
|
|
2644
|
+
"definitions",
|
|
2645
|
+
"dependentSchemas",
|
|
2646
|
+
"dependencies",
|
|
2647
|
+
"patternProperties",
|
|
2648
|
+
"properties"
|
|
2649
|
+
]);
|
|
2650
|
+
const OPENAI_NULLABLE_ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
2651
|
+
"default",
|
|
2652
|
+
"description",
|
|
2653
|
+
"examples",
|
|
2654
|
+
"format",
|
|
2655
|
+
"title"
|
|
2656
|
+
]);
|
|
2657
|
+
const OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS = /* @__PURE__ */ new Set([
|
|
2658
|
+
"additionalItems",
|
|
2659
|
+
"additionalProperties",
|
|
2660
|
+
"allOf",
|
|
2661
|
+
"anyOf",
|
|
2662
|
+
"contains",
|
|
2663
|
+
"contentSchema",
|
|
2664
|
+
"else",
|
|
2665
|
+
"if",
|
|
2666
|
+
"items",
|
|
2667
|
+
"not",
|
|
2668
|
+
"oneOf",
|
|
2669
|
+
"prefixItems",
|
|
2670
|
+
"propertyNames",
|
|
2671
|
+
"then",
|
|
2672
|
+
"unevaluatedItems",
|
|
2673
|
+
"unevaluatedProperties"
|
|
2674
|
+
]);
|
|
2675
|
+
function normalizeOpenAIStrictCompatSchemaMap(schema) {
|
|
2676
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
|
|
2677
|
+
let changed = false;
|
|
2678
|
+
const normalized = {};
|
|
2679
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
2680
|
+
const next = normalizeOpenAIStrictCompatSchemaRecursive(value, { promoteEmptyObject: false });
|
|
2681
|
+
normalized[key] = next;
|
|
2682
|
+
changed ||= next !== value;
|
|
2683
|
+
}
|
|
2684
|
+
return changed ? normalized : schema;
|
|
2685
|
+
}
|
|
2686
|
+
function normalizeOpenAIStrictCompatSchemaRecursive(schema, options) {
|
|
2687
|
+
if (Array.isArray(schema)) {
|
|
2688
|
+
let changed = false;
|
|
2689
|
+
const normalized = schema.map((entry) => {
|
|
2690
|
+
const next = normalizeOpenAIStrictCompatSchemaRecursive(entry, { promoteEmptyObject: false });
|
|
2691
|
+
changed ||= next !== entry;
|
|
2692
|
+
return next;
|
|
2693
|
+
});
|
|
2694
|
+
return changed ? normalized : schema;
|
|
2695
|
+
}
|
|
2696
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
2697
|
+
const record = schema;
|
|
2698
|
+
let changed = false;
|
|
2699
|
+
let hadNullType = false;
|
|
2700
|
+
const normalized = {};
|
|
2701
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2702
|
+
if (value === null && OPENAI_NULLABLE_ANNOTATION_KEYS.has(key)) {
|
|
2703
|
+
changed = true;
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
if (value === null && key === "type") {
|
|
2707
|
+
hadNullType = true;
|
|
2708
|
+
changed = true;
|
|
2709
|
+
continue;
|
|
2710
|
+
}
|
|
2711
|
+
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;
|
|
2712
|
+
normalized[key] = next;
|
|
2713
|
+
changed ||= next !== value;
|
|
2714
|
+
}
|
|
2715
|
+
if (Object.keys(normalized).length === 0) {
|
|
2716
|
+
if (!options.promoteEmptyObject) return schema;
|
|
2717
|
+
return {
|
|
2718
|
+
type: "object",
|
|
2719
|
+
properties: {},
|
|
2720
|
+
required: [],
|
|
2721
|
+
additionalProperties: false
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
const hasObjectShapeHints = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) || Array.isArray(normalized.required);
|
|
2725
|
+
const hasArrayShapeHints = "items" in normalized;
|
|
2726
|
+
if (!("type" in normalized) && hasObjectShapeHints !== hasArrayShapeHints) {
|
|
2727
|
+
normalized.type = hasObjectShapeHints ? "object" : "array";
|
|
2728
|
+
changed = true;
|
|
2729
|
+
} else if (hadNullType && !("type" in normalized)) normalized.type = null;
|
|
2730
|
+
if (normalized.type === "object" && !("properties" in normalized)) {
|
|
2731
|
+
normalized.properties = {};
|
|
2732
|
+
changed = true;
|
|
2733
|
+
}
|
|
2734
|
+
const hasEmptyProperties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) && Object.keys(normalized.properties).length === 0;
|
|
2735
|
+
if (normalized.type === "object" && !Array.isArray(normalized.required) && hasEmptyProperties) {
|
|
2736
|
+
normalized.required = [];
|
|
2737
|
+
changed = true;
|
|
2738
|
+
}
|
|
2739
|
+
if (normalized.type === "object" && hasEmptyProperties && !("additionalProperties" in normalized)) {
|
|
2740
|
+
normalized.additionalProperties = false;
|
|
2741
|
+
changed = true;
|
|
2742
|
+
}
|
|
2743
|
+
return changed ? normalized : schema;
|
|
2744
|
+
}
|
|
2745
|
+
/** Repairs recoverable OpenAI tool-schema shapes before canonical normalization. */
|
|
2746
|
+
function normalizeOpenAIStrictCompatSchema(schema) {
|
|
2747
|
+
return normalizeOpenAIStrictCompatSchemaRecursive(schema, { promoteEmptyObject: true });
|
|
2748
|
+
}
|
|
2749
|
+
/** Finds schema paths that violate OpenAI strict tool-schema requirements. */
|
|
2750
|
+
function findOpenAIStrictSchemaViolations(schema, path, options) {
|
|
2751
|
+
if (Array.isArray(schema)) {
|
|
2752
|
+
if (options?.requireObjectRoot) return [`${path}.type`];
|
|
2753
|
+
return schema.flatMap((item, index) => findOpenAIStrictSchemaViolations(item, `${path}[${index}]`));
|
|
2754
|
+
}
|
|
2755
|
+
if (!schema || typeof schema !== "object") return options?.requireObjectRoot ? [`${path}.type`] : [];
|
|
2756
|
+
const record = schema;
|
|
2757
|
+
const violations = [];
|
|
2758
|
+
for (const key of [
|
|
2759
|
+
"anyOf",
|
|
2760
|
+
"oneOf",
|
|
2761
|
+
"allOf"
|
|
2762
|
+
]) if (key in record) violations.push(`${path}.${key}`);
|
|
2763
|
+
if (Array.isArray(record.type)) violations.push(`${path}.type`);
|
|
2764
|
+
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : void 0;
|
|
2765
|
+
if (record.type === "object") {
|
|
2766
|
+
if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
|
|
2767
|
+
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
2768
|
+
if (!required) violations.push(`${path}.required`);
|
|
2769
|
+
else if (properties) {
|
|
2770
|
+
const requiredSet = new Set(required);
|
|
2771
|
+
for (const key of Object.keys(properties)) if (!requiredSet.has(key)) violations.push(`${path}.required.${key}`);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
for (const key of OPENAI_STRICT_COMPAT_SCHEMA_MAP_KEYS) {
|
|
2775
|
+
const schemaMap = record[key];
|
|
2776
|
+
if (!schemaMap || typeof schemaMap !== "object" || Array.isArray(schemaMap)) continue;
|
|
2777
|
+
for (const [entryKey, value] of Object.entries(schemaMap)) violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}.${entryKey}`));
|
|
2778
|
+
}
|
|
2779
|
+
for (const key of OPENAI_STRICT_COMPAT_SCHEMA_NESTED_KEYS) {
|
|
2780
|
+
const value = record[key];
|
|
2781
|
+
if (value && typeof value === "object") violations.push(...findOpenAIStrictSchemaViolations(value, `${path}.${key}`));
|
|
2782
|
+
}
|
|
2783
|
+
return violations;
|
|
2784
|
+
}
|
|
2785
|
+
//#endregion
|
|
2786
|
+
//#region packages/ai/src/providers/openai-tool-schema.ts
|
|
2787
|
+
/**
|
|
2788
|
+
* OpenAI strict JSON-schema normalization for tool inventories and request payloads.
|
|
2789
|
+
*
|
|
2790
|
+
* Caches normalized object inputs by provider compatibility so repeated inventory builds preserve identity.
|
|
2791
|
+
*/
|
|
2792
|
+
const MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
|
|
2793
|
+
let strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
|
|
2794
|
+
function resolveToolSchemaModelCompat(compat) {
|
|
2795
|
+
if (!compat) return;
|
|
2796
|
+
const unsupportedToolSchemaKeywords = Array.isArray(compat.unsupportedToolSchemaKeywords) ? compat.unsupportedToolSchemaKeywords.filter((keyword) => typeof keyword === "string") : [];
|
|
2797
|
+
if (unsupportedToolSchemaKeywords.length === 0 && compat.omitEmptyArrayItems !== true) return;
|
|
2798
|
+
return {
|
|
2799
|
+
...unsupportedToolSchemaKeywords.length > 0 ? { unsupportedToolSchemaKeywords } : {},
|
|
2800
|
+
...compat.omitEmptyArrayItems === true ? { omitEmptyArrayItems: true } : {}
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
function resolveStrictOpenAISchemaCacheKey(modelCompat) {
|
|
2804
|
+
const compat = resolveToolSchemaModelCompat(modelCompat);
|
|
2805
|
+
return JSON.stringify([[...compat?.unsupportedToolSchemaKeywords ?? []].toSorted(), shouldOmitEmptyArrayItems(compat)]);
|
|
2806
|
+
}
|
|
2807
|
+
function readCachedStrictOpenAISchema(schema, key) {
|
|
2808
|
+
return strictOpenAISchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
|
|
2809
|
+
}
|
|
2810
|
+
function rememberStrictOpenAISchema(schema, key, value) {
|
|
2811
|
+
const entries = strictOpenAISchemaCache.get(schema) ?? [];
|
|
2812
|
+
strictOpenAISchemaCache.set(schema, [{
|
|
2813
|
+
key,
|
|
2814
|
+
value
|
|
2815
|
+
}, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
|
|
2816
|
+
return value;
|
|
2817
|
+
}
|
|
2818
|
+
function clearOpenAIToolSchemaCacheForTest() {
|
|
2819
|
+
strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
|
|
2820
|
+
}
|
|
2821
|
+
/** Normalizes a tool parameter schema into the OpenAI strict JSON-schema subset. */
|
|
2822
|
+
function normalizeStrictOpenAIJsonSchema(schema, modelCompat) {
|
|
2823
|
+
const schemaInput = schema ?? {};
|
|
2824
|
+
if (!schemaInput || typeof schemaInput !== "object") return normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0);
|
|
2825
|
+
const cacheKey = resolveStrictOpenAISchemaCacheKey(modelCompat);
|
|
2826
|
+
const cached = readCachedStrictOpenAISchema(schemaInput, cacheKey);
|
|
2827
|
+
if (cached !== void 0) return cached;
|
|
2828
|
+
return rememberStrictOpenAISchema(schemaInput, cacheKey, normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0));
|
|
2829
|
+
}
|
|
2830
|
+
function normalizeStrictOpenAIJsonSchemaRecursive(schema, depth) {
|
|
2831
|
+
if (Array.isArray(schema)) {
|
|
2832
|
+
let changed = false;
|
|
2833
|
+
const normalized = schema.map((entry) => {
|
|
2834
|
+
const next = normalizeStrictOpenAIJsonSchemaRecursive(entry, depth);
|
|
2835
|
+
changed ||= next !== entry;
|
|
2836
|
+
return next;
|
|
2837
|
+
});
|
|
2838
|
+
return changed ? normalized : schema;
|
|
2839
|
+
}
|
|
2840
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
2841
|
+
const record = schema;
|
|
2842
|
+
let changed = false;
|
|
2843
|
+
const normalized = {};
|
|
2844
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2845
|
+
const next = normalizeStrictOpenAIJsonSchemaRecursive(value, key === "properties" ? depth : depth + 1);
|
|
2846
|
+
normalized[key] = next;
|
|
2847
|
+
changed ||= next !== value;
|
|
2848
|
+
}
|
|
2849
|
+
if (normalized.type === "object") {
|
|
2850
|
+
const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : void 0;
|
|
2851
|
+
if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
|
|
2852
|
+
normalized.required = [];
|
|
2853
|
+
changed = true;
|
|
2854
|
+
}
|
|
2855
|
+
if (depth === 0 && !("additionalProperties" in normalized)) {
|
|
2856
|
+
normalized.additionalProperties = false;
|
|
2857
|
+
changed = true;
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
return changed ? normalized : schema;
|
|
2861
|
+
}
|
|
2862
|
+
/** Normalizes tool parameters using strict OpenAI rules only when strict mode is active. */
|
|
2863
|
+
function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
|
|
2864
|
+
const toolSchemaCompat = resolveToolSchemaModelCompat(modelCompat);
|
|
2865
|
+
if (!strict) return normalizeToolParameterSchema(schema ?? {}, { modelCompat: toolSchemaCompat });
|
|
2866
|
+
return normalizeStrictOpenAIJsonSchema(schema, toolSchemaCompat);
|
|
2867
|
+
}
|
|
2868
|
+
/** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
|
|
2869
|
+
function isStrictOpenAIJsonSchemaCompatible(schema) {
|
|
2870
|
+
return isStrictOpenAIJsonSchemaCompatibleRecursive(normalizeStrictOpenAIJsonSchema(schema));
|
|
2871
|
+
}
|
|
2872
|
+
/** Returns strict-schema diagnostics for an already materialized OpenAI tool projection. */
|
|
2873
|
+
function findOpenAIStrictToolProjectionDiagnostics(projection) {
|
|
2874
|
+
return [...projection.diagnostics.map((diagnostic) => ({
|
|
2875
|
+
toolIndex: diagnostic.toolIndex,
|
|
2876
|
+
...diagnostic.toolName ? { toolName: diagnostic.toolName } : {},
|
|
2877
|
+
violations: [...diagnostic.violations]
|
|
2878
|
+
})), ...projection.tools.flatMap((tool) => {
|
|
2879
|
+
const violations = findOpenAIStrictSchemaViolations(normalizeStrictOpenAIJsonSchema(tool.parameters), `${tool.name}.parameters`);
|
|
2880
|
+
return violations.length > 0 ? [{
|
|
2881
|
+
toolIndex: tool.toolIndex,
|
|
2882
|
+
toolName: tool.name,
|
|
2883
|
+
violations
|
|
2884
|
+
}] : [];
|
|
2885
|
+
})];
|
|
2886
|
+
}
|
|
2887
|
+
function isStrictOpenAIJsonSchemaCompatibleRecursive(schema) {
|
|
2888
|
+
if (Array.isArray(schema)) return schema.every((entry) => isStrictOpenAIJsonSchemaCompatibleRecursive(entry));
|
|
2889
|
+
if (!schema || typeof schema !== "object") return true;
|
|
2890
|
+
const record = schema;
|
|
2891
|
+
if ("anyOf" in record || "oneOf" in record || "allOf" in record) return false;
|
|
2892
|
+
if (Array.isArray(record.type)) return false;
|
|
2893
|
+
if (record.type === "object" && record.additionalProperties !== false) return false;
|
|
2894
|
+
if (record.type === "object") {
|
|
2895
|
+
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
|
|
2896
|
+
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
|
|
2897
|
+
if (!required) return false;
|
|
2898
|
+
const requiredSet = new Set(required);
|
|
2899
|
+
if (Object.keys(properties).some((key) => !requiredSet.has(key))) return false;
|
|
2900
|
+
}
|
|
2901
|
+
return Object.entries(record).every(([key, entry]) => {
|
|
2902
|
+
if (key === "properties" && entry && typeof entry === "object" && !Array.isArray(entry)) return Object.values(entry).every((value) => isStrictOpenAIJsonSchemaCompatibleRecursive(value));
|
|
2903
|
+
return isStrictOpenAIJsonSchemaCompatibleRecursive(entry);
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2906
|
+
/** Resolves strict mode for the projected tools that will be emitted in the request payload. */
|
|
2907
|
+
function resolveOpenAIProjectedToolsStrictToolFlag(projection, strict) {
|
|
2908
|
+
if (strict !== true) return strict === false ? false : void 0;
|
|
2909
|
+
return projection.tools.every((tool) => isStrictOpenAIJsonSchemaCompatible(tool.parameters));
|
|
2910
|
+
}
|
|
2911
|
+
//#endregion
|
|
2912
|
+
//#region packages/ai/src/providers/openai-responses-tools.ts
|
|
2913
|
+
const LOG_SUBSYSTEM = "llm/openai-responses";
|
|
2914
|
+
const MAX_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 64;
|
|
2915
|
+
const loggedStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
2916
|
+
/** Converts and returns the projection used to reconcile tool choices. */
|
|
2917
|
+
function convertResponsesToolPayload(tools, options) {
|
|
2918
|
+
const projection = projectOpenAITools(tools);
|
|
2919
|
+
const strict = resolveResponsesStrictToolFlag(projection, resolveResponsesStrictToolSetting(options), options?.model);
|
|
2920
|
+
return {
|
|
2921
|
+
projection,
|
|
2922
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
2923
|
+
const result = {
|
|
2924
|
+
type: "function",
|
|
2925
|
+
name: tool.name,
|
|
2926
|
+
description: tool.description,
|
|
2927
|
+
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, options?.model?.compat)
|
|
2928
|
+
};
|
|
2929
|
+
if (strict !== void 0) result.strict = strict;
|
|
2930
|
+
return result;
|
|
2931
|
+
})
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
function resolveResponsesStrictToolSetting(options) {
|
|
2935
|
+
if (options?.strict !== void 0) return options.strict;
|
|
2936
|
+
if (options?.model) return getAiTransportHost().resolveOpenAIStrictToolSetting(options.model, {
|
|
2937
|
+
transport: "stream",
|
|
2938
|
+
supportsStrictMode: options.supportsStrictMode
|
|
1336
2939
|
});
|
|
1337
2940
|
return false;
|
|
1338
2941
|
}
|
|
@@ -1372,16 +2975,6 @@ function shouldLogStrictToolDowngradeDiagnostic(diagnostics, model) {
|
|
|
1372
2975
|
loggedStrictToolDowngradeDiagnosticKeys.add(key);
|
|
1373
2976
|
return true;
|
|
1374
2977
|
}
|
|
1375
|
-
function compareToolText(left, right) {
|
|
1376
|
-
const leftText = left ?? "";
|
|
1377
|
-
const rightText = right ?? "";
|
|
1378
|
-
if (leftText < rightText) return -1;
|
|
1379
|
-
if (leftText > rightText) return 1;
|
|
1380
|
-
return 0;
|
|
1381
|
-
}
|
|
1382
|
-
function sortResponsesToolsByName(tools) {
|
|
1383
|
-
return tools.toSorted((left, right) => compareToolText(left.name, right.name) || compareToolText(left.description, right.description));
|
|
1384
|
-
}
|
|
1385
2978
|
//#endregion
|
|
1386
2979
|
//#region packages/ai/src/providers/openai-responses-shared.ts
|
|
1387
2980
|
const EMPTY_TOOL_RESULT_TEXT = "(no output)";
|
|
@@ -1389,16 +2982,6 @@ function splitResponsesToolCallId(id) {
|
|
|
1389
2982
|
const separatorIndex = id.indexOf("|");
|
|
1390
2983
|
return separatorIndex === -1 ? [id, void 0] : [id.slice(0, separatorIndex), id.slice(separatorIndex + 1)];
|
|
1391
2984
|
}
|
|
1392
|
-
function resolveResponsesToolCallId(item, fallbackId) {
|
|
1393
|
-
const callId = typeof item.call_id === "string" ? item.call_id.trim() : "";
|
|
1394
|
-
const itemId = typeof item.id === "string" ? item.id.trim() : "";
|
|
1395
|
-
const [fallbackCallId, fallbackItemId = ""] = splitResponsesToolCallId(fallbackId ?? "");
|
|
1396
|
-
const resolvedCallId = callId || fallbackCallId;
|
|
1397
|
-
const resolvedItemId = itemId || fallbackItemId;
|
|
1398
|
-
if (resolvedCallId) return resolvedItemId ? `${resolvedCallId}|${resolvedItemId}` : resolvedCallId;
|
|
1399
|
-
const generatedCallId = `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`;
|
|
1400
|
-
return resolvedItemId ? `${generatedCallId}|${resolvedItemId}` : generatedCallId;
|
|
1401
|
-
}
|
|
1402
2985
|
function sanitizeToolResultText(text, fallback) {
|
|
1403
2986
|
const sanitized = sanitizeSurrogates(text);
|
|
1404
2987
|
return sanitized.trim().length > 0 ? sanitized : fallback;
|
|
@@ -1409,14 +2992,6 @@ function normalizeResponsesReasoningReplayItem(params) {
|
|
|
1409
2992
|
if (!params.replayResponsesItemIds) delete next.id;
|
|
1410
2993
|
return next;
|
|
1411
2994
|
}
|
|
1412
|
-
function encodeTextSignatureV1(id, phase) {
|
|
1413
|
-
const payload = {
|
|
1414
|
-
v: 1,
|
|
1415
|
-
id
|
|
1416
|
-
};
|
|
1417
|
-
if (phase) payload.phase = phase;
|
|
1418
|
-
return JSON.stringify(payload);
|
|
1419
|
-
}
|
|
1420
2995
|
function parseTextSignature(signature) {
|
|
1421
2996
|
if (!signature) return;
|
|
1422
2997
|
if (signature.startsWith("{")) try {
|
|
@@ -1687,9 +3262,10 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
1687
3262
|
...params.processStreamOptions,
|
|
1688
3263
|
firstEventTimeoutMs: params.processStreamOptions?.firstEventTimeoutMs ?? firstEventTimeoutMs,
|
|
1689
3264
|
abortFirstEventStream: params.processStreamOptions?.abortFirstEventStream ?? firstEventAbort.abort,
|
|
1690
|
-
onFirstEventTimeout: params.processStreamOptions?.onFirstEventTimeout ?? onFirstEventTimeout
|
|
3265
|
+
onFirstEventTimeout: params.processStreamOptions?.onFirstEventTimeout ?? onFirstEventTimeout,
|
|
3266
|
+
signal: params.processStreamOptions?.signal ?? options?.signal
|
|
1691
3267
|
} : void 0);
|
|
1692
|
-
if (options?.signal?.aborted) throw
|
|
3268
|
+
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
1693
3269
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1694
3270
|
stream.push({
|
|
1695
3271
|
type: "done",
|
|
@@ -1711,523 +3287,132 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
1711
3287
|
firstEventAbort?.dispose();
|
|
1712
3288
|
}
|
|
1713
3289
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
return typeof outputIndex === "number" && Number.isInteger(outputIndex) && outputIndex >= 0 ? outputIndex : void 0;
|
|
1726
|
-
};
|
|
1727
|
-
const registerOutputSlot = (event, slot) => {
|
|
1728
|
-
const outputIndex = readOutputIndex(event);
|
|
1729
|
-
if (outputIndex === void 0) {
|
|
1730
|
-
if (unindexedOutputSlot) throw new Error("Responses stream added overlapping unindexed output items");
|
|
1731
|
-
unindexedOutputSlot = slot;
|
|
1732
|
-
return;
|
|
1733
|
-
}
|
|
1734
|
-
if (outputSlots.has(outputIndex)) throw new Error(`Responses stream reused active output index ${outputIndex}`);
|
|
1735
|
-
outputSlots.set(outputIndex, slot);
|
|
1736
|
-
};
|
|
1737
|
-
const resolveOutputSlot = (event, type) => {
|
|
1738
|
-
const outputIndex = readOutputIndex(event);
|
|
1739
|
-
let slot = outputIndex === void 0 ? unindexedOutputSlot : outputSlots.get(outputIndex);
|
|
1740
|
-
if (outputIndex === void 0 && !slot) {
|
|
1741
|
-
const matchingSlots = [...outputSlots.values()].filter((candidate) => candidate.type === type);
|
|
1742
|
-
slot = matchingSlots.length === 1 ? matchingSlots[0] : void 0;
|
|
1743
|
-
}
|
|
1744
|
-
return slot?.type === type ? slot : void 0;
|
|
1745
|
-
};
|
|
1746
|
-
const forgetOutputSlot = (event, slot) => {
|
|
1747
|
-
const outputIndex = readOutputIndex(event);
|
|
1748
|
-
if (outputIndex === void 0) {
|
|
1749
|
-
if (unindexedOutputSlot === slot) unindexedOutputSlot = void 0;
|
|
1750
|
-
else for (const [indexedOutput, indexedSlot] of outputSlots) if (indexedSlot === slot) outputSlots.delete(indexedOutput);
|
|
1751
|
-
return;
|
|
1752
|
-
}
|
|
1753
|
-
if (outputSlots.get(outputIndex) === slot) outputSlots.delete(outputIndex);
|
|
1754
|
-
};
|
|
1755
|
-
const forgetToolCallOutputSlot = (toolCall) => {
|
|
1756
|
-
for (const [outputIndex, slot] of outputSlots) if (slot.type === "toolCall" && slot.toolCall === toolCall) outputSlots.delete(outputIndex);
|
|
1757
|
-
};
|
|
1758
|
-
const readIdentityValue = (value) => {
|
|
1759
|
-
return (typeof value === "string" ? value.trim() : "") || void 0;
|
|
1760
|
-
};
|
|
1761
|
-
const resolveCompletedToolCallName = (toolCall, value) => {
|
|
1762
|
-
const streamedName = readIdentityValue(toolCall?.block.name);
|
|
1763
|
-
const completedName = readIdentityValue(value);
|
|
1764
|
-
if (streamedName && completedName && streamedName !== completedName) throw new Error(`Responses stream changed tool-call function name from ${streamedName} to ${completedName}`);
|
|
1765
|
-
const name = completedName ?? streamedName;
|
|
1766
|
-
if (!name) throw new Error("Responses stream completed tool call without a function name");
|
|
1767
|
-
return name;
|
|
1768
|
-
};
|
|
1769
|
-
const createOutputSlot = (event, item) => {
|
|
1770
|
-
if (item.type === "reasoning") {
|
|
1771
|
-
const block = {
|
|
1772
|
-
type: "thinking",
|
|
1773
|
-
thinking: ""
|
|
1774
|
-
};
|
|
1775
|
-
const slot = {
|
|
1776
|
-
type: "thinking",
|
|
1777
|
-
item,
|
|
1778
|
-
block,
|
|
1779
|
-
contentIndex: blocks.length
|
|
1780
|
-
};
|
|
1781
|
-
blocks.push(block);
|
|
1782
|
-
registerOutputSlot(event, slot);
|
|
1783
|
-
stream.push({
|
|
1784
|
-
type: "thinking_start",
|
|
1785
|
-
contentIndex: slot.contentIndex,
|
|
1786
|
-
partial: output
|
|
1787
|
-
});
|
|
1788
|
-
return slot;
|
|
1789
|
-
}
|
|
1790
|
-
if (item.type === "message") {
|
|
1791
|
-
const messageItem = item;
|
|
1792
|
-
const collapseCandidate = lastTextBlock;
|
|
1793
|
-
const block = collapseCandidate ? null : {
|
|
1794
|
-
type: "text",
|
|
1795
|
-
text: "",
|
|
1796
|
-
...messageItem.phase ? { textSignature: encodeTextSignatureV1(messageItem.id, messageItem.phase) } : {}
|
|
1797
|
-
};
|
|
1798
|
-
const slot = {
|
|
1799
|
-
type: "text",
|
|
1800
|
-
item: messageItem,
|
|
1801
|
-
block,
|
|
1802
|
-
contentIndex: block ? blocks.length : void 0,
|
|
1803
|
-
pendingText: collapseCandidate ? "" : null,
|
|
1804
|
-
collapseCandidate
|
|
1805
|
-
};
|
|
1806
|
-
if (block) blocks.push(block);
|
|
1807
|
-
registerOutputSlot(event, slot);
|
|
1808
|
-
if (slot.contentIndex !== void 0) stream.push({
|
|
1809
|
-
type: "text_start",
|
|
1810
|
-
contentIndex: slot.contentIndex,
|
|
1811
|
-
partial: output
|
|
1812
|
-
});
|
|
1813
|
-
return slot;
|
|
1814
|
-
}
|
|
1815
|
-
};
|
|
1816
|
-
const resolveOutputItemSlot = (event, item) => {
|
|
1817
|
-
if (item.type === "reasoning") return resolveOutputSlot(event, "thinking");
|
|
1818
|
-
if (item.type === "message") return resolveOutputSlot(event, "text");
|
|
1819
|
-
const outputIndex = readOutputIndex(event);
|
|
1820
|
-
return outputIndex === void 0 ? void 0 : outputSlots.get(outputIndex);
|
|
1821
|
-
};
|
|
1822
|
-
const getOrCreateOutputSlot = (event, item) => {
|
|
1823
|
-
return resolveOutputItemSlot(event, item) ?? createOutputSlot(event, item);
|
|
1824
|
-
};
|
|
1825
|
-
const materializeDeferredTextSlot = (slot) => {
|
|
1826
|
-
if (slot.block || slot.pendingText === null) return;
|
|
1827
|
-
const text = slot.pendingText;
|
|
1828
|
-
slot.block = {
|
|
1829
|
-
type: "text",
|
|
1830
|
-
text,
|
|
1831
|
-
...slot.item.phase ? { textSignature: encodeTextSignatureV1(slot.item.id, slot.item.phase) } : {}
|
|
1832
|
-
};
|
|
1833
|
-
blocks.push(slot.block);
|
|
1834
|
-
slot.contentIndex = blockIndex();
|
|
1835
|
-
stream.push({
|
|
1836
|
-
type: "text_start",
|
|
1837
|
-
contentIndex: slot.contentIndex,
|
|
1838
|
-
partial: output
|
|
1839
|
-
});
|
|
1840
|
-
if (text) stream.push({
|
|
1841
|
-
type: "text_delta",
|
|
1842
|
-
contentIndex: slot.contentIndex,
|
|
1843
|
-
delta: text,
|
|
1844
|
-
partial: output
|
|
1845
|
-
});
|
|
1846
|
-
if (lastTextBlock === slot.collapseCandidate) lastTextBlock = null;
|
|
1847
|
-
slot.pendingText = null;
|
|
1848
|
-
slot.collapseCandidate = null;
|
|
1849
|
-
};
|
|
1850
|
-
const materializeDeferredTextSlots = (except) => {
|
|
1851
|
-
for (const slot of outputSlots.values()) if (slot !== except && slot.type === "text") materializeDeferredTextSlot(slot);
|
|
1852
|
-
if (unindexedOutputSlot !== except && unindexedOutputSlot?.type === "text") materializeDeferredTextSlot(unindexedOutputSlot);
|
|
1853
|
-
};
|
|
1854
|
-
const appendPendingMessageDelta = (slot, delta) => {
|
|
1855
|
-
slot.pendingText = `${slot.pendingText ?? ""}${delta}`;
|
|
1856
|
-
const priorText = slot.collapseCandidate?.block.text ?? "";
|
|
1857
|
-
if (priorText.startsWith(slot.pendingText) || slot.pendingText.startsWith(priorText)) return;
|
|
1858
|
-
materializeDeferredTextSlot(slot);
|
|
1859
|
-
};
|
|
1860
|
-
const backfillReasoningSignatures = (responseOutput) => {
|
|
1861
|
-
for (const item of responseOutput) {
|
|
1862
|
-
if (item.type !== "reasoning" || !item.encrypted_content) continue;
|
|
1863
|
-
const block = reasoningBlocksById.get(item.id);
|
|
1864
|
-
if (!block?.thinkingSignature) continue;
|
|
1865
|
-
const storedItem = JSON.parse(block.thinkingSignature);
|
|
1866
|
-
if (storedItem.encrypted_content) continue;
|
|
1867
|
-
block.thinkingSignature = JSON.stringify({
|
|
1868
|
-
...storedItem,
|
|
1869
|
-
encrypted_content: item.encrypted_content
|
|
1870
|
-
});
|
|
1871
|
-
}
|
|
1872
|
-
};
|
|
1873
|
-
const finalizeResponse = (response) => {
|
|
1874
|
-
terminalResponseEvent = "finalized";
|
|
1875
|
-
backfillReasoningSignatures(response.output ?? []);
|
|
1876
|
-
if (response.id) output.responseId = response.id;
|
|
1877
|
-
if (response.usage) {
|
|
1878
|
-
const inputTokenDetails = response.usage.input_tokens_details;
|
|
1879
|
-
const cachedTokens = inputTokenDetails?.cached_tokens || 0;
|
|
1880
|
-
const cacheWriteTokens = inputTokenDetails?.cache_write_tokens || 0;
|
|
1881
|
-
output.usage = {
|
|
1882
|
-
input: Math.max(0, (response.usage.input_tokens || 0) - cachedTokens - cacheWriteTokens),
|
|
1883
|
-
output: response.usage.output_tokens || 0,
|
|
1884
|
-
cacheRead: cachedTokens,
|
|
1885
|
-
cacheWrite: cacheWriteTokens,
|
|
1886
|
-
totalTokens: response.usage.total_tokens || 0,
|
|
1887
|
-
cost: {
|
|
1888
|
-
input: 0,
|
|
1889
|
-
output: 0,
|
|
1890
|
-
cacheRead: 0,
|
|
1891
|
-
cacheWrite: 0,
|
|
1892
|
-
total: 0
|
|
1893
|
-
}
|
|
1894
|
-
};
|
|
1895
|
-
}
|
|
1896
|
-
calculateCost(model, output.usage);
|
|
1897
|
-
if (options?.applyServiceTierPricing) {
|
|
1898
|
-
const serviceTier = options.resolveServiceTier ? options.resolveServiceTier(response.service_tier, options.serviceTier) : response.service_tier ?? options.serviceTier;
|
|
1899
|
-
options.applyServiceTierPricing(output.usage, serviceTier);
|
|
1900
|
-
}
|
|
1901
|
-
if (response.status === "incomplete" && response.incomplete_details?.reason === "content_filter") {
|
|
1902
|
-
output.stopReason = "error";
|
|
1903
|
-
output.errorMessage = "Provider incomplete_reason: content_filter";
|
|
1904
|
-
} else output.stopReason = mapStopReason(response.status);
|
|
1905
|
-
if (output.content.some((block) => block.type === "toolCall") && output.stopReason === "stop") output.stopReason = "toolUse";
|
|
3290
|
+
//#endregion
|
|
3291
|
+
//#region packages/ai/src/providers/openai-responses.ts
|
|
3292
|
+
var openai_responses_exports = /* @__PURE__ */ __exportAll({
|
|
3293
|
+
streamOpenAIResponses: () => streamOpenAIResponses,
|
|
3294
|
+
streamSimpleOpenAIResponses: () => streamSimpleOpenAIResponses
|
|
3295
|
+
});
|
|
3296
|
+
const OPENAI_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set(["openai", "opencode"]);
|
|
3297
|
+
function getCompat(model) {
|
|
3298
|
+
return {
|
|
3299
|
+
sendSessionIdHeader: model.compat?.sendSessionIdHeader ?? true,
|
|
3300
|
+
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true
|
|
1906
3301
|
};
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
3302
|
+
}
|
|
3303
|
+
function getPromptCacheRetention(compat, cacheRetention) {
|
|
3304
|
+
return cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : void 0;
|
|
3305
|
+
}
|
|
3306
|
+
function formatOpenAIResponsesError(error) {
|
|
3307
|
+
if (error instanceof Error) {
|
|
3308
|
+
const status = error.status;
|
|
3309
|
+
const statusCode = typeof status === "number" ? status : void 0;
|
|
3310
|
+
if (statusCode !== void 0) return `OpenAI API error (${statusCode}): ${error.message}`;
|
|
3311
|
+
return error.message;
|
|
3312
|
+
}
|
|
3313
|
+
try {
|
|
3314
|
+
return JSON.stringify(error);
|
|
3315
|
+
} catch {
|
|
3316
|
+
return String(error);
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
/**
|
|
3320
|
+
* Generate function for OpenAI Responses API
|
|
3321
|
+
*/
|
|
3322
|
+
const streamOpenAIResponses = (model, context, options) => {
|
|
3323
|
+
const stream = new AssistantMessageEventStream();
|
|
3324
|
+
runResponsesStreamLifecycle({
|
|
3325
|
+
stream,
|
|
3326
|
+
model,
|
|
3327
|
+
output: createResponsesAssistantOutput(model),
|
|
3328
|
+
options,
|
|
3329
|
+
createClient: () => {
|
|
3330
|
+
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
3331
|
+
const cacheSessionId = resolveCacheRetention$1(options?.cacheRetention) === "none" ? void 0 : options?.sessionId;
|
|
3332
|
+
return createClient(model, context, apiKey, options?.headers, cacheSessionId);
|
|
3333
|
+
},
|
|
3334
|
+
buildParams: () => buildParams(model, context, options),
|
|
3335
|
+
processStreamOptions: {
|
|
3336
|
+
serviceTier: options?.serviceTier,
|
|
3337
|
+
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
|
|
3338
|
+
},
|
|
3339
|
+
formatError: formatOpenAIResponsesError
|
|
1916
3340
|
});
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
streamingToolCalls.register(event, toolCallState);
|
|
1939
|
-
if (readOutputIndex(event) !== void 0) registerOutputSlot(event, {
|
|
1940
|
-
type: "toolCall",
|
|
1941
|
-
toolCall: toolCallState
|
|
1942
|
-
});
|
|
1943
|
-
output.content.push(toolCallBlock);
|
|
1944
|
-
stream.push({
|
|
1945
|
-
type: "toolcall_start",
|
|
1946
|
-
contentIndex,
|
|
1947
|
-
partial: output
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
} else if (event.type === "response.reasoning_summary_part.added") {
|
|
1951
|
-
const slot = resolveOutputSlot(event, "thinking");
|
|
1952
|
-
if (!slot) continue;
|
|
1953
|
-
slot.item.summary = slot.item.summary || [];
|
|
1954
|
-
slot.item.summary.push(event.part);
|
|
1955
|
-
} else if (event.type === "response.reasoning_summary_text.delta") {
|
|
1956
|
-
const slot = resolveOutputSlot(event, "thinking");
|
|
1957
|
-
if (!slot) continue;
|
|
1958
|
-
slot.item.summary = slot.item.summary || [];
|
|
1959
|
-
const lastPart = slot.item.summary[slot.item.summary.length - 1];
|
|
1960
|
-
if (!lastPart) continue;
|
|
1961
|
-
slot.block.thinking += event.delta;
|
|
1962
|
-
lastPart.text += event.delta;
|
|
1963
|
-
stream.push({
|
|
1964
|
-
type: "thinking_delta",
|
|
1965
|
-
contentIndex: slot.contentIndex,
|
|
1966
|
-
delta: event.delta,
|
|
1967
|
-
partial: output
|
|
1968
|
-
});
|
|
1969
|
-
} else if (event.type === "response.reasoning_summary_part.done") {
|
|
1970
|
-
const slot = resolveOutputSlot(event, "thinking");
|
|
1971
|
-
if (!slot) continue;
|
|
1972
|
-
slot.item.summary = slot.item.summary || [];
|
|
1973
|
-
const lastPart = slot.item.summary[slot.item.summary.length - 1];
|
|
1974
|
-
if (!lastPart) continue;
|
|
1975
|
-
slot.block.thinking += "\n\n";
|
|
1976
|
-
lastPart.text += "\n\n";
|
|
1977
|
-
stream.push({
|
|
1978
|
-
type: "thinking_delta",
|
|
1979
|
-
contentIndex: slot.contentIndex,
|
|
1980
|
-
delta: "\n\n",
|
|
1981
|
-
partial: output
|
|
1982
|
-
});
|
|
1983
|
-
} else if (event.type === "response.reasoning_text.delta") {
|
|
1984
|
-
const slot = resolveOutputSlot(event, "thinking");
|
|
1985
|
-
if (!slot) continue;
|
|
1986
|
-
slot.block.thinking += event.delta;
|
|
1987
|
-
stream.push({
|
|
1988
|
-
type: "thinking_delta",
|
|
1989
|
-
contentIndex: slot.contentIndex,
|
|
1990
|
-
delta: event.delta,
|
|
1991
|
-
partial: output
|
|
3341
|
+
return stream;
|
|
3342
|
+
};
|
|
3343
|
+
const streamSimpleOpenAIResponses = (model, context, options) => {
|
|
3344
|
+
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
|
|
3345
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
3346
|
+
const base = buildBaseOptions(model, options, apiKey);
|
|
3347
|
+
return streamOpenAIResponses(model, context, {
|
|
3348
|
+
...base,
|
|
3349
|
+
reasoningEffort: resolveResponsesReasoningEffort(model, options?.reasoning),
|
|
3350
|
+
replayResponsesItemIds: options?.replayResponsesItemIds
|
|
3351
|
+
});
|
|
3352
|
+
};
|
|
3353
|
+
function createClient(model, context, apiKey, optionsHeaders, sessionId) {
|
|
3354
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
3355
|
+
const compat = getCompat(model);
|
|
3356
|
+
const headers = { ...model.headers };
|
|
3357
|
+
if (model.provider === "github-copilot") {
|
|
3358
|
+
const hasImages = hasCopilotVisionInput(context.messages);
|
|
3359
|
+
const copilotHeaders = buildCopilotDynamicHeaders({
|
|
3360
|
+
messages: context.messages,
|
|
3361
|
+
hasImages
|
|
1992
3362
|
});
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
}
|
|
2037
|
-
} else if (event.type === "response.refusal.delta") {
|
|
2038
|
-
const slot = resolveOutputSlot(event, "text");
|
|
2039
|
-
if (!slot?.item.content || slot.item.content.length === 0) continue;
|
|
2040
|
-
const lastPart = slot.item.content[slot.item.content.length - 1];
|
|
2041
|
-
if (lastPart?.type !== "refusal") continue;
|
|
2042
|
-
lastPart.refusal += event.delta;
|
|
2043
|
-
if (slot.pendingText !== null) appendPendingMessageDelta(slot, event.delta);
|
|
2044
|
-
else if (slot.block && slot.contentIndex !== void 0) {
|
|
2045
|
-
slot.block.text += event.delta;
|
|
2046
|
-
stream.push({
|
|
2047
|
-
type: "text_delta",
|
|
2048
|
-
contentIndex: slot.contentIndex,
|
|
2049
|
-
delta: event.delta,
|
|
2050
|
-
partial: output
|
|
2051
|
-
});
|
|
2052
|
-
}
|
|
2053
|
-
} else if (event.type === "response.function_call_arguments.delta") {
|
|
2054
|
-
const toolCall = streamingToolCalls.resolve(event);
|
|
2055
|
-
if (toolCall) {
|
|
2056
|
-
toolCall.block.partialJson += event.delta;
|
|
2057
|
-
toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
2058
|
-
stream.push({
|
|
2059
|
-
type: "toolcall_delta",
|
|
2060
|
-
contentIndex: toolCall.contentIndex,
|
|
2061
|
-
delta: event.delta,
|
|
2062
|
-
partial: output
|
|
2063
|
-
});
|
|
2064
|
-
} else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
|
|
2065
|
-
} else if (event.type === "response.function_call_arguments.done") {
|
|
2066
|
-
const toolCall = streamingToolCalls.resolve(event);
|
|
2067
|
-
if (toolCall) {
|
|
2068
|
-
const previousPartialJson = toolCall.block.partialJson;
|
|
2069
|
-
const doneArguments = typeof event.arguments === "string" ? event.arguments : void 0;
|
|
2070
|
-
if (doneArguments !== void 0 && (doneArguments.length > 0 || previousPartialJson === "")) {
|
|
2071
|
-
toolCall.block.partialJson = doneArguments;
|
|
2072
|
-
toolCall.block.arguments = parseStreamingJson(toolCall.block.partialJson);
|
|
2073
|
-
toolCall.argumentStreamReliable = true;
|
|
2074
|
-
}
|
|
2075
|
-
if (doneArguments?.startsWith(previousPartialJson)) {
|
|
2076
|
-
const delta = doneArguments.slice(previousPartialJson.length);
|
|
2077
|
-
if (delta.length > 0) stream.push({
|
|
2078
|
-
type: "toolcall_delta",
|
|
2079
|
-
contentIndex: toolCall.contentIndex,
|
|
2080
|
-
delta,
|
|
2081
|
-
partial: output
|
|
2082
|
-
});
|
|
2083
|
-
}
|
|
2084
|
-
} else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
|
|
2085
|
-
} else if (event.type === "response.output_item.done") {
|
|
2086
|
-
const item = event.item;
|
|
2087
|
-
if (item.type !== "message") lastTextBlock = null;
|
|
2088
|
-
const existingOutputSlot = resolveOutputItemSlot(event, item);
|
|
2089
|
-
materializeDeferredTextSlots(existingOutputSlot);
|
|
2090
|
-
const outputSlot = existingOutputSlot ?? getOrCreateOutputSlot(event, item);
|
|
2091
|
-
if (item.type === "reasoning" && outputSlot?.type === "thinking") {
|
|
2092
|
-
const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
|
|
2093
|
-
const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
|
|
2094
|
-
outputSlot.block.thinking = summaryText || contentText || outputSlot.block.thinking;
|
|
2095
|
-
outputSlot.block.thinkingSignature = JSON.stringify(item);
|
|
2096
|
-
if (typeof item.id === "string") reasoningBlocksById.set(item.id, outputSlot.block);
|
|
2097
|
-
stream.push({
|
|
2098
|
-
type: "thinking_end",
|
|
2099
|
-
contentIndex: outputSlot.contentIndex,
|
|
2100
|
-
content: outputSlot.block.thinking,
|
|
2101
|
-
partial: output
|
|
2102
|
-
});
|
|
2103
|
-
forgetOutputSlot(event, outputSlot);
|
|
2104
|
-
} else if (item.type === "message" && outputSlot?.type === "text" && (outputSlot.block || outputSlot.pendingText !== null)) {
|
|
2105
|
-
const streamedText = outputSlot.pendingText ?? outputSlot.block?.text ?? "";
|
|
2106
|
-
const finalText = item.content == null ? streamedText : item.content.map((c) => c.type === "output_text" || c.type === "text" ? c.text : c.refusal).join("");
|
|
2107
|
-
const phase = item.phase ?? void 0;
|
|
2108
|
-
const collapse = outputSlot.pendingText !== null ? resolveResponsesMessageSnapshotCollapse({
|
|
2109
|
-
prior: outputSlot.collapseCandidate && {
|
|
2110
|
-
text: outputSlot.collapseCandidate.block.text,
|
|
2111
|
-
phase: outputSlot.collapseCandidate.phase
|
|
2112
|
-
},
|
|
2113
|
-
nextText: finalText,
|
|
2114
|
-
nextPhase: phase
|
|
2115
|
-
}) : { kind: "keep" };
|
|
2116
|
-
outputSlot.pendingText = null;
|
|
2117
|
-
if (collapse.kind === "extend" && outputSlot.collapseCandidate) {
|
|
2118
|
-
outputSlot.collapseCandidate.block.text = collapse.text;
|
|
2119
|
-
outputSlot.collapseCandidate.block.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2120
|
-
stream.push({
|
|
2121
|
-
type: "text_end",
|
|
2122
|
-
contentIndex: outputSlot.collapseCandidate.index,
|
|
2123
|
-
content: collapse.text,
|
|
2124
|
-
partial: output
|
|
2125
|
-
});
|
|
2126
|
-
lastTextBlock = outputSlot.collapseCandidate;
|
|
2127
|
-
} else {
|
|
2128
|
-
if (!outputSlot.block) {
|
|
2129
|
-
outputSlot.block = {
|
|
2130
|
-
type: "text",
|
|
2131
|
-
text: "",
|
|
2132
|
-
...phase ? { textSignature: encodeTextSignatureV1(item.id, phase) } : {}
|
|
2133
|
-
};
|
|
2134
|
-
blocks.push(outputSlot.block);
|
|
2135
|
-
outputSlot.contentIndex = blockIndex();
|
|
2136
|
-
stream.push({
|
|
2137
|
-
type: "text_start",
|
|
2138
|
-
contentIndex: outputSlot.contentIndex,
|
|
2139
|
-
partial: output
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
outputSlot.block.text = finalText;
|
|
2143
|
-
outputSlot.block.textSignature = encodeTextSignatureV1(item.id, phase);
|
|
2144
|
-
const contentIndex = outputSlot.contentIndex;
|
|
2145
|
-
if (contentIndex === void 0) throw new Error("Responses stream finalized text without a content index");
|
|
2146
|
-
lastTextBlock = {
|
|
2147
|
-
block: outputSlot.block,
|
|
2148
|
-
index: contentIndex,
|
|
2149
|
-
phase
|
|
2150
|
-
};
|
|
2151
|
-
stream.push({
|
|
2152
|
-
type: "text_end",
|
|
2153
|
-
contentIndex,
|
|
2154
|
-
content: outputSlot.block.text,
|
|
2155
|
-
partial: output
|
|
2156
|
-
});
|
|
2157
|
-
}
|
|
2158
|
-
forgetOutputSlot(event, outputSlot);
|
|
2159
|
-
} else if (item.type === "function_call") {
|
|
2160
|
-
const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
|
|
2161
|
-
if (!streamingToolCall && streamingToolCalls.hasActive()) continue;
|
|
2162
|
-
const completedName = resolveCompletedToolCallName(streamingToolCall, item.name);
|
|
2163
|
-
const streamedArguments = streamingToolCall?.block.partialJson ?? "";
|
|
2164
|
-
const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
|
|
2165
|
-
if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) continue;
|
|
2166
|
-
const args = parseStreamingJson(completedArguments !== void 0 && (completedArguments.length > 0 || !streamedArguments) ? completedArguments : streamedArguments || "{}");
|
|
2167
|
-
let toolCall;
|
|
2168
|
-
let contentIndex;
|
|
2169
|
-
if (streamingToolCall) {
|
|
2170
|
-
const block = streamingToolCall.block;
|
|
2171
|
-
block.id = resolveResponsesToolCallId(item, block.id);
|
|
2172
|
-
block.name = completedName;
|
|
2173
|
-
block.arguments = args;
|
|
2174
|
-
delete block.partialJson;
|
|
2175
|
-
toolCall = block;
|
|
2176
|
-
contentIndex = streamingToolCall.contentIndex;
|
|
2177
|
-
} else {
|
|
2178
|
-
toolCall = {
|
|
2179
|
-
type: "toolCall",
|
|
2180
|
-
id: resolveResponsesToolCallId(item),
|
|
2181
|
-
name: completedName,
|
|
2182
|
-
arguments: args
|
|
2183
|
-
};
|
|
2184
|
-
blocks.push(toolCall);
|
|
2185
|
-
contentIndex = blockIndex();
|
|
2186
|
-
stream.push({
|
|
2187
|
-
type: "toolcall_start",
|
|
2188
|
-
contentIndex,
|
|
2189
|
-
partial: output
|
|
2190
|
-
});
|
|
2191
|
-
}
|
|
2192
|
-
if (streamingToolCall) {
|
|
2193
|
-
streamingToolCalls.forget(streamingToolCall);
|
|
2194
|
-
forgetToolCallOutputSlot(streamingToolCall);
|
|
2195
|
-
}
|
|
2196
|
-
stream.push({
|
|
2197
|
-
type: "toolcall_end",
|
|
2198
|
-
contentIndex,
|
|
2199
|
-
toolCall,
|
|
2200
|
-
partial: output
|
|
2201
|
-
});
|
|
2202
|
-
}
|
|
2203
|
-
} else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
2204
|
-
if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
2205
|
-
finalizeResponse(event.response);
|
|
2206
|
-
} else if (event.type === "error") throw new Error(event.message ? `Error Code ${event.code}: ${event.message}` : "Unknown error");
|
|
2207
|
-
else if (event.type === "response.failed") {
|
|
2208
|
-
const error = event.response?.error;
|
|
2209
|
-
const details = event.response?.incomplete_details;
|
|
2210
|
-
output.responseId = event.response.id;
|
|
2211
|
-
output.stopReason = "error";
|
|
2212
|
-
output.errorMessage = error ? `${error.code || "unknown"}: ${error.message || "no message"}` : details?.reason ? `incomplete: ${details.reason}` : "Unknown error (no error details in response)";
|
|
2213
|
-
terminalResponseEvent = "failed";
|
|
2214
|
-
break;
|
|
2215
|
-
}
|
|
2216
|
-
if (terminalResponseEvent === "failed") return;
|
|
2217
|
-
if (streamingToolCalls.hasActive()) throw new Error("Responses stream ended with unresolved tool calls");
|
|
2218
|
-
if (!terminalResponseEvent) throw new Error("OpenAI Responses stream ended before a terminal response event");
|
|
2219
|
-
}
|
|
2220
|
-
function mapStopReason(status) {
|
|
2221
|
-
if (!status) return "stop";
|
|
2222
|
-
switch (status) {
|
|
2223
|
-
case "completed": return "stop";
|
|
2224
|
-
case "incomplete": return "length";
|
|
2225
|
-
case "failed":
|
|
2226
|
-
case "cancelled": return "error";
|
|
2227
|
-
case "in_progress":
|
|
2228
|
-
case "queued": return "stop";
|
|
2229
|
-
default: throw new Error(`Unhandled stop reason: ${String(status)}`);
|
|
3363
|
+
Object.assign(headers, copilotHeaders);
|
|
3364
|
+
}
|
|
3365
|
+
if (sessionId) {
|
|
3366
|
+
if (compat.sendSessionIdHeader) headers.session_id = sessionId;
|
|
3367
|
+
headers["x-client-request-id"] = sessionId;
|
|
3368
|
+
}
|
|
3369
|
+
if (optionsHeaders) Object.assign(headers, optionsHeaders);
|
|
3370
|
+
const defaultHeaders = model.provider === "cloudflare-ai-gateway" ? {
|
|
3371
|
+
...headers,
|
|
3372
|
+
Authorization: headers.Authorization ?? null,
|
|
3373
|
+
"cf-aig-authorization": `Bearer ${apiKey}`
|
|
3374
|
+
} : headers;
|
|
3375
|
+
return new OpenAI({
|
|
3376
|
+
apiKey,
|
|
3377
|
+
baseURL: isCloudflareProvider(model.provider) ? resolveCloudflareBaseUrl(model) : model.baseUrl,
|
|
3378
|
+
dangerouslyAllowBrowser: true,
|
|
3379
|
+
defaultHeaders,
|
|
3380
|
+
fetch: getAiTransportHost().buildModelFetch(model)
|
|
3381
|
+
});
|
|
3382
|
+
}
|
|
3383
|
+
function buildParams(model, context, options) {
|
|
3384
|
+
const messages = convertResponsesMessages(model, context, OPENAI_TOOL_CALL_PROVIDERS, { replayResponsesItemIds: options?.replayResponsesItemIds ?? false });
|
|
3385
|
+
const cacheRetention = resolveCacheRetention$1(options?.cacheRetention);
|
|
3386
|
+
const compat = getCompat(model);
|
|
3387
|
+
const params = {
|
|
3388
|
+
model: model.id,
|
|
3389
|
+
input: messages,
|
|
3390
|
+
stream: true,
|
|
3391
|
+
prompt_cache_key: cacheRetention === "none" ? void 0 : clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId),
|
|
3392
|
+
prompt_cache_retention: getPromptCacheRetention(compat, cacheRetention),
|
|
3393
|
+
store: false
|
|
3394
|
+
};
|
|
3395
|
+
if (options?.maxTokens) params.max_output_tokens = options?.maxTokens;
|
|
3396
|
+
if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) params.temperature = options?.temperature;
|
|
3397
|
+
if (options?.serviceTier !== void 0) params.service_tier = options.serviceTier;
|
|
3398
|
+
applyCommonResponsesParams(params, model, context, options, { setDefaultReasoningOff: model.provider !== "github-copilot" });
|
|
3399
|
+
return params;
|
|
3400
|
+
}
|
|
3401
|
+
function getServiceTierCostMultiplier(model, serviceTier) {
|
|
3402
|
+
switch (serviceTier) {
|
|
3403
|
+
case "flex": return .5;
|
|
3404
|
+
case "priority": return model.id === "gpt-5.5" ? 2.5 : 2;
|
|
3405
|
+
default: return 1;
|
|
2230
3406
|
}
|
|
2231
3407
|
}
|
|
3408
|
+
function applyServiceTierPricing(usage, serviceTier, model) {
|
|
3409
|
+
const multiplier = getServiceTierCostMultiplier(model, serviceTier);
|
|
3410
|
+
if (multiplier === 1) return;
|
|
3411
|
+
usage.cost.input *= multiplier;
|
|
3412
|
+
usage.cost.output *= multiplier;
|
|
3413
|
+
usage.cost.cacheRead *= multiplier;
|
|
3414
|
+
usage.cost.cacheWrite *= multiplier;
|
|
3415
|
+
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
3416
|
+
}
|
|
2232
3417
|
//#endregion
|
|
2233
|
-
export {
|
|
3418
|
+
export { createResponsesToolCallTracker as $, resolveAzureOpenAIApiVersion as A, emitModelTransportDebug as At, stringifyRedactedPayload as B, observeResponsesStream as C, normalizeToolParameterSchema as Ct, createResponsesStreamWithEncryptedContentRetry as D, GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS as Dt, convertResponsesMessages$1 as E, stripUnsupportedSchemaKeywords as Et, buildResponsesFailedNoDetailsObservation as F, GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP as G, summarizeResponsesFailedNoDetailsObservation as H, logResponsesFailedNoDetails as I, resolveCacheRetention as J, createModelStreamCooperativeScheduler as K, normalizeResponsesFailedEvent as L, tagOpenAIResponsesReasoningReplayItem as M, resolveModelSseDebugMode as Mt, resolveReplayableResponsesMessageId$1 as N, isInvalidEncryptedContentError as O, cleanSchemaForGemini as Ot, applyServiceTierPricing$1 as P, OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS as Q, safeDebugValue as R, resolveResponsesTerminalStopReason as S, extractToolSchemaModelCompat as St, buildResponsesInputMessage as T, shouldOmitEmptyArrayItems as Tt, summarizeResponsesPayload as U, summarizeOpenAITransportError as V, summarizeResponsesTools as W, throwIfModelStreamAborted as X, resolvePromptCacheKey as Y, AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS as Z, normalizeOpenAIStrictCompatSchema as _, supportsOpenAITemperature as _t, convertResponsesMessages as a, isAzureResponsesTextDeltaEvent as at, mapResponsesTerminalUsage as b, parseAzureDeploymentNameMap as bt, runResponsesStreamLifecycle as c, isResponsesTextDeltaEventType as ct, findOpenAIStrictToolProjectionDiagnostics as d, isOpenAIGpt55Model as dt, readResponsesToolCallItemIdentity as et, isStrictOpenAIJsonSchemaCompatible as f, isOpenAIGpt56Model as ft, findOpenAIStrictSchemaViolations as g, supportsOpenAIReasoningEffort as gt, resolveOpenAIProjectedToolsStrictToolFlag as h, resolveOpenAISupportedReasoningEfforts as ht, applyCommonResponsesParams as i, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as it, stripResponsesRequestEncryptedContent as j, resolveModelPayloadDebugMode as jt, prepareOpenAIResponsesReasoningItemForReplay as k, uniqueStrings as kt, convertResponsesToolPayload as l, resolveResponsesMessageSnapshotCollapse as lt, normalizeStrictOpenAIJsonSchema as m, resolveOpenAIReasoningEffortForModel as mt, streamOpenAIResponses as n, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as nt, createResponsesAssistantOutput as o, isAzureResponsesTextDeltaEventType as ot, normalizeOpenAIStrictToolParameters as p, normalizeOpenAIReasoningEffort as pt, log as q, streamSimpleOpenAIResponses as r, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as rt, resolveResponsesReasoningEffort as s, isResponsesTextContentPartType as st, openai_responses_exports as t, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as tt, clearOpenAIToolSchemaCacheForTest as u, isOpenAIGpt54MiniModel as ut, ResponsesStreamFailure as v, isOpenAICompatibleAzureResponsesBaseUrl as vt, buildOpenAIResponsesReasoningReplayMetadata as w, resolveUnsupportedToolSchemaKeywords as wt, readResponsesReasoningTokens as x, resolveAzureDeploymentNameFromMap as xt, processResponsesStream as y, isTraditionalAzureOpenAIHost as yt, stringifyRedactedEvent as z };
|