@openclaw/ai 2026.7.2-beta.4 → 2026.7.2-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{anthropic-CrUDIBpM.mjs → anthropic-CH4UUnZr.mjs} +26 -441
- package/dist/{anthropic-BoTnz8cv.d.mts → anthropic-SrGtwsJu.d.mts} +27 -1
- package/dist/anthropic-usage-DWU-x8MI.mjs +459 -0
- package/dist/{api-registry-BMphkFf1.d.mts → api-registry-DlMgPR39.d.mts} +1 -1
- package/dist/{azure-openai-responses-CbrpVeEv.mjs → azure-openai-responses-CImcwB83.mjs} +4 -4
- package/dist/cache-retention-0x979a5V.mjs +12 -0
- package/dist/deferred-event-buffer-DAvyP7qA.mjs +19 -0
- package/dist/error-coercion-DgxlWC0n.mjs +15 -0
- package/dist/{event-stream-Douf9dob.d.mts → event-stream-YjaPW20U.d.mts} +1 -1
- package/dist/event-stream.d.mts +1 -1
- package/dist/{github-copilot-headers-BsH5cqGj.mjs → github-copilot-headers-NCJtz9i0.mjs} +1 -12
- package/dist/{google-Cs62KA7t.mjs → google-CtSg0iTS.mjs} +3 -3
- package/dist/{google-shared-CMLI-tCZ.mjs → google-shared-DNBz5rcD.mjs} +7 -4
- package/dist/{google-vertex-B4SD3U0f.mjs → google-vertex-31f1uS9L.mjs} +3 -3
- package/dist/{host-WvWBo4h8.d.mts → host-B9GUmcra.d.mts} +2 -2
- package/dist/host-Dog2WQiR.mjs +369 -0
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +3 -3
- package/dist/internal/anthropic.d.mts +16 -62
- package/dist/internal/anthropic.mjs +5 -4
- package/dist/internal/openai.d.mts +260 -2
- package/dist/internal/openai.mjs +7 -5
- package/dist/internal/runtime.d.mts +8 -32
- package/dist/internal/runtime.mjs +7 -6
- package/dist/internal/shared.d.mts +14 -9
- package/dist/internal/shared.mjs +6 -2
- package/dist/{llm-request-activity-CehVkZP-.mjs → llm-request-activity-BjtkplhG.mjs} +1 -19
- package/dist/{mistral-D5Ps7Led.mjs → mistral-CWmpvWYh.mjs} +7 -4
- package/dist/number-coercion-DvG7SNMg.mjs +129 -0
- package/dist/{openai-chatgpt-responses-h0o5yV8Y.mjs → openai-chatgpt-responses-B84Ibtrd.mjs} +33 -29
- package/dist/{openai-completions-exO8NFQf.mjs → openai-completions-DsOxhOD1.mjs} +28 -482
- package/dist/openai-completions-compat-DBWjXoMZ.d.mts +43 -0
- package/dist/openai-prompt-cache-2uo_1OR1.d.mts +7 -0
- package/dist/openai-prompt-cache-mZTCdRPo.mjs +12 -0
- package/dist/openai-reasoning-compat-YgeLncHw.mjs +396 -0
- package/dist/{openai-responses-BHpmtUKo.mjs → openai-responses-BT7A3sLu.mjs} +7 -5
- package/dist/openai-responses-shared-pXl6Wd8S.mjs +392 -0
- package/dist/{openai-responses-shared-CzCurmY1.mjs → openai-responses-stream-internal-Cw5txaGW.mjs} +1609 -1037
- package/dist/{openai-tool-projection-ITOU9bG1.mjs → openai-tool-projection-OhX64DoP.mjs} +26 -11
- package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +13 -0
- package/dist/{provider-error-C4VvV_3t.mjs → provider-error-CAEvRjry.mjs} +8 -2
- package/dist/provider-options-D8bB3z9b.d.mts +144 -0
- package/dist/providers.d.mts +3 -2
- package/dist/providers.mjs +10 -9
- package/dist/reasoning-tag-text-partitioner-CGDyLWUR.mjs +12209 -0
- package/dist/simple-options-9lhRrN73.mjs +50 -0
- package/dist/{src-CXno1H5g.mjs → src-QkygScBs.mjs} +23 -4
- package/dist/stream-first-event-timeout-BBys9hSb.mjs +86 -0
- package/dist/stream-first-event-timeout-DvDeSucC.d.mts +29 -0
- package/dist/tls-certificate-errors-DXSpluKI.mjs +93 -0
- package/dist/tool-result-text-CTpIRbYd.mjs +225 -0
- package/dist/transform-messages-C8mBqZxF.mjs +2 -0
- package/dist/transport-stream-shared-D81p90xq.mjs +297 -0
- package/dist/transports.d.mts +115 -132
- package/dist/transports.mjs +400 -1991
- package/dist/{types-AFwwWium.d.mts → types-bzp5k29J.d.mts} +7 -0
- package/dist/types.d.mts +5 -5
- package/dist/types.mjs +2 -2
- package/dist/{validation-sxvxC8J-.d.mts → validation-B-j7cOYp.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/package.json +4 -5
- package/dist/host-XYGZcgO8.mjs +0 -98
- package/dist/model-utils-1GiZ2_rr.mjs +0 -64
- package/dist/openai-CoGicoDt.d.mts +0 -332
- package/dist/reasoning-tag-text-partitioner-BlVe67g6.mjs +0 -400
- package/dist/stream-first-event-timeout-DP4xEyBY.mjs +0 -150
- package/dist/system-prompt-cache-boundary-CbHeV4_l.mjs +0 -526
- package/npm-shrinkwrap.json +0 -638
package/dist/transports.mjs
CHANGED
|
@@ -1,52 +1,25 @@
|
|
|
1
1
|
import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, h as supportsClaudeNativeMaxEffort, l as resolveClaudeNativeThinkingLevelMap, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./src-QkygScBs.mjs";
|
|
3
3
|
import { r as createAssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
|
|
4
|
-
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-
|
|
5
|
-
import {
|
|
6
|
-
import { t as
|
|
7
|
-
import "./
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { r as
|
|
4
|
+
import { a as applyClaudeRequestContract, c as requiresClaudeAdaptiveThinking, d as usesClaudeStreamingRefusalContract, f as hasNonEmptyString, g as isRecord, h as readStringValue, n as getAiTransportHost, o as defaultsClaudeAdaptiveThinking, p as normalizeLowercaseStringOrEmpty, r as resolveAiTransportHeaderSentinels, s as prepareClaudeNoPrefillRequestContext, u as usesClaudeFable5MessagesContract } from "./host-Dog2WQiR.mjs";
|
|
5
|
+
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
6
|
+
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
7
|
+
import { a as isImageWithMediaPayload, d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, n as extractToolResultBlockText, r as extractToolResultText, t as describeToolResultMediaPlaceholder, u as splitSystemPromptCacheBoundary } from "./tool-result-text-CTpIRbYd.mjs";
|
|
8
|
+
import { a as resolveModelRequestTimeoutMs, c as resolveProviderRequestCapabilities, i as buildGuardedModelFetch, l as resolveProviderRequestPolicyConfig, n as reconcileOpenAICompletionsToolChoice, o as resolveOpenAIStrictToolSetting, r as reconcileOpenAIResponsesToolChoice, s as resolveProviderEndpoint, t as projectOpenAITools, u as transformTransportMessages } from "./openai-tool-projection-OhX64DoP.mjs";
|
|
9
|
+
import { t as toErrorObject } from "./error-coercion-DgxlWC0n.mjs";
|
|
10
|
+
import { S as canonicalizeBase64, _ as omitFoundryBearerCredentialHeaders, a as projectAnthropicTools, b as normalizeAnthropicInlineContent, c as ANTHROPIC_OMITTED_REASONING_TEXT, d as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, g as applyAnthropicRefusal, h as resolveAnthropicFallbackServingModelCost, i as readLastAnthropicIterationUsage, l as findActiveAnthropicToolTurnAssistantIndex, m as readAnthropicFallbackBoundary, n as readAnthropicPromptUsageSnapshot, o as reconcileAnthropicToolChoice, p as applyAnthropicFallbackBoundary, r as readAnthropicUsageTokenCount, s as resolveOriginalAnthropicToolName, u as ANTHROPIC_SERVER_SIDE_FALLBACKS, v as usesFoundryBearerAuth, x as resolveAnthropicImageMediaType, y as createAnthropicInlineImageBudget } from "./anthropic-usage-DWU-x8MI.mjs";
|
|
11
|
+
import { a as parseStrictPositiveInteger, c as calculateCost, l as clampThinkingLevel, s as applyProviderReportedUsageCost } from "./number-coercion-DvG7SNMg.mjs";
|
|
12
|
+
import { a as resolveOpenAICompletionsCompat, c as clearPendingCommentaryText, i as detectOpenAICompletionsCompat, l as rememberPendingCommentaryTags, n as mapOpenAIStopReason, o as resolveOpenAICompletionsResponseFormat, r as convertMessages, s as shouldOmitOllamaCompatResponseFormat, t as resolveOpenAIReasoningEffortMap, u as tagPendingCommentaryText } from "./openai-reasoning-compat-YgeLncHw.mjs";
|
|
13
|
+
import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
|
|
11
14
|
import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
|
|
12
|
-
import "./
|
|
13
|
-
import { t as
|
|
14
|
-
import {
|
|
15
|
-
import "./
|
|
16
|
-
import { a as
|
|
17
|
-
import { parseRetryAfterHttpDateMs } from "./internal/retry-after.mjs";
|
|
18
|
-
import { A as isOpenAIGpt54MiniModel, D as isResponsesTextContentPartType, G as uniqueStrings, I as supportsOpenAIReasoningEffort, M as isOpenAIGpt56Model, N as normalizeOpenAIReasoningEffort, O as isResponsesTextDeltaEventType, P as resolveOpenAIReasoningEffortForModel, _ as readResponsesToolCallItemIdentity, b as resolveResponsesTerminalStopReason, d as normalizeOpenAIStrictToolParameters, g as createResponsesToolCallTracker, j as isOpenAIGpt55Model, k as resolveResponsesMessageSnapshotCollapse, l as findOpenAIStrictToolProjectionDiagnostics, p as resolveOpenAIProjectedToolsStrictToolFlag, v as mapResponsesTerminalUsage, y as readResponsesReasoningTokens } from "./openai-responses-shared-CzCurmY1.mjs";
|
|
15
|
+
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
16
|
+
import { C as resolveSecretSentinel, S as resolveModelHeaderSentinels$1, T as supportsModelTools, _ as isGoogleGemini3ProModel, a as failTransportStream, c as mergeTransportMetadata, d as transportAbortError, f as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, g as isGoogleGemini3FlashModel, h as isCodeModeModelVisibleToolName, i as createWritableTransportEventStream, l as sanitizeNonEmptyTransportPayloadText, m as estimateStringChars, n as coerceTransportToolCallArguments, o as finalizeTransportStream, p as createAbortError$1, r as createEmptyTransportUsage, s as mergeTransportHeaders, t as assignTransportErrorDetails, u as sanitizeTransportPayloadText, v as parseRetryAfterSeconds, w as sha256Hex, y as readResponseTextSnippet } from "./transport-stream-shared-D81p90xq.mjs";
|
|
17
|
+
import { A as normalizeResponsesFailedEvent, B as resolveReplayableResponsesMessageId, C as prepareOpenAIResponsesReasoningItemForReplay, D as applyServiceTierPricing, E as tagOpenAIResponsesReasoningReplayItem, F as summarizeResponsesFailedNoDetailsObservation, G as throwIfModelStreamAborted, H as createModelStreamCooperativeScheduler, I as summarizeResponsesPayload, L as summarizeResponsesTools, M as stringifyRedactedEvent, N as stringifyRedactedPayload, O as buildResponsesFailedNoDetailsObservation, P as summarizeOpenAITransportError, R as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, S as isInvalidEncryptedContentError, T as stripResponsesRequestEncryptedContent, U as log, V as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, W as resolvePromptCacheKey, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, b as convertResponsesMessages, bt as resolveModelPayloadDebugMode, dt as isOpenAIGpt55Model, ft as isOpenAIGpt56Model, gt as supportsOpenAIReasoningEffort, j as safeDebugValue, k as logResponsesFailedNoDetails, mt as resolveOpenAIReasoningEffortForModel, n as processResponsesStream, o as observeResponsesStream, pt as normalizeOpenAIReasoningEffort, q as findOpenAIStrictToolProjectionDiagnostics, t as ResponsesStreamFailure, ut as isOpenAIGpt54MiniModel, v as buildOpenAIResponsesReasoningReplayMetadata, vt as uniqueStrings, w as resolveAzureOpenAIApiVersion, x as createResponsesStreamWithEncryptedContentRetry, xt as resolveModelSseDebugMode, y as buildResponsesInputMessage, yt as emitModelTransportDebug, z as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS } from "./openai-responses-stream-internal-Cw5txaGW.mjs";
|
|
18
|
+
import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-CGDyLWUR.mjs";
|
|
19
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-BBys9hSb.mjs";
|
|
19
20
|
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
20
|
-
import {
|
|
21
|
-
import "./internal/openai.mjs";
|
|
22
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
21
|
+
import { randomUUID } from "node:crypto";
|
|
23
22
|
import OpenAI, { AzureOpenAI } from "openai";
|
|
24
|
-
//#region packages/ai/src/transports/host-policy.ts
|
|
25
|
-
function buildGuardedModelFetch(model, timeoutMs, options) {
|
|
26
|
-
const host = getAiTransportHost();
|
|
27
|
-
if (options !== void 0) return host.buildModelFetch(model, timeoutMs, options) ?? globalThis.fetch;
|
|
28
|
-
if (timeoutMs !== void 0) return host.buildModelFetch(model, timeoutMs) ?? globalThis.fetch;
|
|
29
|
-
return host.buildModelFetch(model) ?? globalThis.fetch;
|
|
30
|
-
}
|
|
31
|
-
function resolveProviderEndpoint(baseUrl) {
|
|
32
|
-
return { endpointClass: getAiTransportHost().resolveProviderEndpointClass(baseUrl) };
|
|
33
|
-
}
|
|
34
|
-
function resolveProviderRequestCapabilities(input) {
|
|
35
|
-
return getAiTransportHost().resolveProviderRequestCapabilities(input);
|
|
36
|
-
}
|
|
37
|
-
function resolveProviderRequestPolicyConfig(input) {
|
|
38
|
-
return { headers: getAiTransportHost().resolveProviderRequestHeaders(input) };
|
|
39
|
-
}
|
|
40
|
-
function resolveModelRequestTimeoutMs(model, timeoutMs) {
|
|
41
|
-
return timeoutMs ?? getAiTransportHost().resolveModelRequestTimeoutMs(model);
|
|
42
|
-
}
|
|
43
|
-
function resolveOpenAIStrictToolSetting(model, options) {
|
|
44
|
-
return getAiTransportHost().resolveOpenAIStrictToolSetting(model, options);
|
|
45
|
-
}
|
|
46
|
-
function transformTransportMessages(messages, model, normalizeToolCallId, options) {
|
|
47
|
-
return getAiTransportHost().transformTransportMessages(messages, model, normalizeToolCallId, options);
|
|
48
|
-
}
|
|
49
|
-
//#endregion
|
|
50
23
|
//#region packages/ai/src/transports/anthropic-payload-policy.ts
|
|
51
24
|
/**
|
|
52
25
|
* Anthropic-family request payload policy helpers.
|
|
@@ -69,7 +42,7 @@ function isLongTtlEligibleEndpoint(baseUrl) {
|
|
|
69
42
|
}
|
|
70
43
|
/** Resolve Anthropic cache-control marker retention for a request endpoint. */
|
|
71
44
|
function resolveAnthropicEphemeralCacheControl(baseUrl, cacheRetention) {
|
|
72
|
-
const retention = cacheRetention
|
|
45
|
+
const retention = resolveCacheRetention(cacheRetention);
|
|
73
46
|
if (retention === "none") return;
|
|
74
47
|
const ttl = retention === "long" && (cacheRetention === "long" || isLongTtlEligibleEndpoint(baseUrl)) ? "1h" : void 0;
|
|
75
48
|
return {
|
|
@@ -219,20 +192,6 @@ function applyAnthropicEphemeralCacheControlMarkers(payloadObj, cacheControl = {
|
|
|
219
192
|
}
|
|
220
193
|
}
|
|
221
194
|
//#endregion
|
|
222
|
-
//#region packages/normalization-core/src/error-coercion.ts
|
|
223
|
-
/**
|
|
224
|
-
* Normalizes an unknown thrown value into an Error. Non-Error objects become
|
|
225
|
-
* the `cause` and have their enumerable fields copied so structured details
|
|
226
|
-
* (codes, statuses) survive the coercion.
|
|
227
|
-
*/
|
|
228
|
-
function toErrorObject(value, fallbackMessage) {
|
|
229
|
-
if (value instanceof Error) return value;
|
|
230
|
-
if (typeof value === "string") return new Error(value);
|
|
231
|
-
const error = new Error(fallbackMessage, { cause: value });
|
|
232
|
-
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
|
|
233
|
-
return error;
|
|
234
|
-
}
|
|
235
|
-
//#endregion
|
|
236
195
|
//#region packages/ai/src/transports/json-unsafe-integers.ts
|
|
237
196
|
/**
|
|
238
197
|
* JSON parsing helpers that preserve integer literals larger than
|
|
@@ -330,270 +289,6 @@ function parseJsonObjectPreservingUnsafeIntegers(value) {
|
|
|
330
289
|
return null;
|
|
331
290
|
}
|
|
332
291
|
//#endregion
|
|
333
|
-
//#region packages/ai/src/transports/transport-utils.ts
|
|
334
|
-
const MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE = "OpenClaw transport error: malformed_streaming_fragment";
|
|
335
|
-
const NON_LATIN_RE = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7AF\uF900-\uFAFF\uFF01-\uFF60\uFFE0-\uFFE6\u{20000}-\u{2FA1F}]/gu;
|
|
336
|
-
const CJK_SURROGATE_HIGH_RE = /[\uD840-\uD87E][\uDC00-\uDFFF]/g;
|
|
337
|
-
function sha256Hex(value) {
|
|
338
|
-
return createHash("sha256").update(value).digest("hex");
|
|
339
|
-
}
|
|
340
|
-
function sha256HexPrefix(value, length) {
|
|
341
|
-
return sha256Hex(value).slice(0, length);
|
|
342
|
-
}
|
|
343
|
-
function redactIdentifier(value, opts) {
|
|
344
|
-
const trimmed = normalizeOptionalString(value);
|
|
345
|
-
if (!trimmed) return "-";
|
|
346
|
-
return `sha256:${sha256HexPrefix(trimmed, Number.isFinite(opts?.len) ? Math.max(1, Math.floor(opts?.len ?? 12)) : 12)}`;
|
|
347
|
-
}
|
|
348
|
-
function redactSensitiveText(text, _options) {
|
|
349
|
-
return getAiTransportHost().redactToolPayloadText(text);
|
|
350
|
-
}
|
|
351
|
-
function resolveSecretSentinel(value) {
|
|
352
|
-
return getAiTransportHost().resolveSecretSentinel(value);
|
|
353
|
-
}
|
|
354
|
-
function resolveModelHeaderSentinels$1(model) {
|
|
355
|
-
if (!model.headers) return model;
|
|
356
|
-
let headers;
|
|
357
|
-
for (const [name, value] of Object.entries(model.headers)) {
|
|
358
|
-
const resolved = resolveSecretSentinel(value);
|
|
359
|
-
if (resolved !== value) {
|
|
360
|
-
headers ??= { ...model.headers };
|
|
361
|
-
headers[name] = resolved;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return headers ? {
|
|
365
|
-
...model,
|
|
366
|
-
headers
|
|
367
|
-
} : model;
|
|
368
|
-
}
|
|
369
|
-
function createAbortError$1(message, options) {
|
|
370
|
-
const error = new Error(message, options);
|
|
371
|
-
error.name = "AbortError";
|
|
372
|
-
return error;
|
|
373
|
-
}
|
|
374
|
-
function estimateStringChars(text) {
|
|
375
|
-
if (!text) return 0;
|
|
376
|
-
const nonLatinCount = (text.match(NON_LATIN_RE) ?? []).length;
|
|
377
|
-
return (nonLatinCount === 0 ? text.length : text.length - (text.match(CJK_SURROGATE_HIGH_RE) ?? []).length) + nonLatinCount * 3;
|
|
378
|
-
}
|
|
379
|
-
function supportsModelTools(model) {
|
|
380
|
-
return (model.compat && typeof model.compat === "object" ? model.compat : void 0)?.supportsTools !== false;
|
|
381
|
-
}
|
|
382
|
-
function isCodeModeModelVisibleToolName(name) {
|
|
383
|
-
return name === "exec" || name === "wait" || name === "computer" || name === "image";
|
|
384
|
-
}
|
|
385
|
-
function isGoogleGemini3Model(modelId, family) {
|
|
386
|
-
const normalized = modelId.trim().toLowerCase();
|
|
387
|
-
const suffix = family === "pro" ? "pro" : "flash";
|
|
388
|
-
return new RegExp(`(?:^|/)gemini-(?:3(?:\\.\\d+)?-${suffix}|${suffix}${family === "flash" ? "(?:-lite)?" : ""}-latest)(?:-|$)`).test(normalized);
|
|
389
|
-
}
|
|
390
|
-
function isGoogleGemini3ProModel(modelId) {
|
|
391
|
-
return isGoogleGemini3Model(modelId, "pro");
|
|
392
|
-
}
|
|
393
|
-
function isGoogleGemini3FlashModel(modelId) {
|
|
394
|
-
return isGoogleGemini3Model(modelId, "flash");
|
|
395
|
-
}
|
|
396
|
-
function parseRetryAfterSeconds(headers) {
|
|
397
|
-
const retryAfterMs = headers.get("retry-after-ms");
|
|
398
|
-
if (retryAfterMs) {
|
|
399
|
-
const trimmed = retryAfterMs.trim();
|
|
400
|
-
if (/^\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
401
|
-
const milliseconds = asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
|
|
402
|
-
min: 0,
|
|
403
|
-
max: Number.MAX_SAFE_INTEGER
|
|
404
|
-
});
|
|
405
|
-
return milliseconds === void 0 ? Number.POSITIVE_INFINITY : milliseconds / 1e3;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
const retryAfter = headers.get("retry-after")?.trim();
|
|
409
|
-
if (!retryAfter) return;
|
|
410
|
-
if (/^\d+$/.test(retryAfter)) return parseStrictNonNegativeInteger(retryAfter) ?? Number.POSITIVE_INFINITY;
|
|
411
|
-
const retryAt = parseRetryAfterHttpDateMs(retryAfter);
|
|
412
|
-
return retryAt === void 0 ? void 0 : Math.max(0, (retryAt - Date.now()) / 1e3);
|
|
413
|
-
}
|
|
414
|
-
async function readChunkWithIdleTimeout(reader, timeoutMs, onIdleTimeout) {
|
|
415
|
-
let timer;
|
|
416
|
-
try {
|
|
417
|
-
return await Promise.race([reader.read(), new Promise((_resolve, reject) => {
|
|
418
|
-
timer = setTimeout(() => reject(onIdleTimeout?.({ chunkTimeoutMs: timeoutMs }) ?? /* @__PURE__ */ new Error("Read timed out")), timeoutMs);
|
|
419
|
-
})]);
|
|
420
|
-
} finally {
|
|
421
|
-
if (timer) clearTimeout(timer);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
async function readResponseTextSnippet(response, options) {
|
|
425
|
-
const maxBytes = options?.maxBytes ?? 8 * 1024;
|
|
426
|
-
const maxChars = options?.maxChars ?? 200;
|
|
427
|
-
const reader = response.body?.getReader();
|
|
428
|
-
if (!reader) return;
|
|
429
|
-
const chunks = [];
|
|
430
|
-
let bytes = 0;
|
|
431
|
-
let truncated = false;
|
|
432
|
-
try {
|
|
433
|
-
while (bytes < maxBytes) {
|
|
434
|
-
const result = options?.chunkTimeoutMs ? await readChunkWithIdleTimeout(reader, options.chunkTimeoutMs, options.onIdleTimeout) : await reader.read();
|
|
435
|
-
if (result.done) break;
|
|
436
|
-
if (!result.value?.length) continue;
|
|
437
|
-
const remaining = maxBytes - bytes;
|
|
438
|
-
chunks.push(result.value.subarray(0, remaining));
|
|
439
|
-
bytes += Math.min(result.value.length, remaining);
|
|
440
|
-
if (result.value.length >= remaining) {
|
|
441
|
-
truncated = true;
|
|
442
|
-
await reader.cancel().catch(() => void 0);
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
} catch (error) {
|
|
447
|
-
await reader.cancel(error).catch(() => void 0);
|
|
448
|
-
throw error;
|
|
449
|
-
} finally {
|
|
450
|
-
reader.releaseLock();
|
|
451
|
-
}
|
|
452
|
-
const merged = new Uint8Array(bytes);
|
|
453
|
-
let offset = 0;
|
|
454
|
-
for (const chunk of chunks) {
|
|
455
|
-
merged.set(chunk, offset);
|
|
456
|
-
offset += chunk.length;
|
|
457
|
-
}
|
|
458
|
-
const collapsed = new TextDecoder().decode(merged).replace(/\s+/g, " ").trim();
|
|
459
|
-
if (!collapsed) return;
|
|
460
|
-
if (collapsed.length > maxChars) return `${truncateUtf16Safe(collapsed, maxChars)}…`;
|
|
461
|
-
return truncated ? `${collapsed}…` : collapsed;
|
|
462
|
-
}
|
|
463
|
-
//#endregion
|
|
464
|
-
//#region packages/ai/src/transports/transport-stream-shared.ts
|
|
465
|
-
const EMPTY_TOOL_RESULT_TEXT = "(no output)";
|
|
466
|
-
function sanitizeTransportPayloadText(text) {
|
|
467
|
-
if (typeof text !== "string") return "";
|
|
468
|
-
return sanitizeSurrogates(text);
|
|
469
|
-
}
|
|
470
|
-
function sanitizeNonEmptyTransportPayloadText(text, fallback = EMPTY_TOOL_RESULT_TEXT) {
|
|
471
|
-
const sanitized = sanitizeTransportPayloadText(text);
|
|
472
|
-
return sanitized.trim().length > 0 ? sanitized : fallback;
|
|
473
|
-
}
|
|
474
|
-
function coerceTransportToolCallArguments(argumentsValue) {
|
|
475
|
-
if (argumentsValue && typeof argumentsValue === "object" && !Array.isArray(argumentsValue)) return argumentsValue;
|
|
476
|
-
if (typeof argumentsValue === "string") try {
|
|
477
|
-
const parsed = JSON.parse(argumentsValue);
|
|
478
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
479
|
-
} catch {}
|
|
480
|
-
return {};
|
|
481
|
-
}
|
|
482
|
-
function mergeTransportHeaders(...headerSources) {
|
|
483
|
-
const merged = {};
|
|
484
|
-
for (const headers of headerSources) if (headers) Object.assign(merged, headers);
|
|
485
|
-
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
486
|
-
}
|
|
487
|
-
function mergeTransportMetadata(payload, metadata) {
|
|
488
|
-
if (!metadata || Object.keys(metadata).length === 0) return payload;
|
|
489
|
-
const existingMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : void 0;
|
|
490
|
-
return {
|
|
491
|
-
...payload,
|
|
492
|
-
metadata: {
|
|
493
|
-
...existingMetadata,
|
|
494
|
-
...metadata
|
|
495
|
-
}
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
function createEmptyTransportUsage() {
|
|
499
|
-
return {
|
|
500
|
-
input: 0,
|
|
501
|
-
output: 0,
|
|
502
|
-
cacheRead: 0,
|
|
503
|
-
cacheWrite: 0,
|
|
504
|
-
totalTokens: 0,
|
|
505
|
-
cost: {
|
|
506
|
-
input: 0,
|
|
507
|
-
output: 0,
|
|
508
|
-
cacheRead: 0,
|
|
509
|
-
cacheWrite: 0,
|
|
510
|
-
total: 0
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
function createWritableTransportEventStream() {
|
|
515
|
-
const eventStream = createAssistantMessageEventStream();
|
|
516
|
-
return {
|
|
517
|
-
eventStream,
|
|
518
|
-
stream: eventStream
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
function finalizeTransportStream(params) {
|
|
522
|
-
const { stream, output, signal } = params;
|
|
523
|
-
if (signal?.aborted) throw new Error("Request was aborted");
|
|
524
|
-
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
525
|
-
stream.push({
|
|
526
|
-
type: "done",
|
|
527
|
-
reason: output.stopReason,
|
|
528
|
-
message: output
|
|
529
|
-
});
|
|
530
|
-
stream.end();
|
|
531
|
-
}
|
|
532
|
-
function readStringLikeProperty(value, key) {
|
|
533
|
-
if (!value || typeof value !== "object") return;
|
|
534
|
-
const raw = value[key];
|
|
535
|
-
if (typeof raw === "string") return raw.trim() || void 0;
|
|
536
|
-
if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
|
|
537
|
-
}
|
|
538
|
-
function readObjectProperty(value, key) {
|
|
539
|
-
if (!value || typeof value !== "object") return;
|
|
540
|
-
const raw = value[key];
|
|
541
|
-
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : void 0;
|
|
542
|
-
}
|
|
543
|
-
function stringifyErrorBody(value) {
|
|
544
|
-
if (typeof value === "string") return value;
|
|
545
|
-
if (value === void 0 || value === null) return;
|
|
546
|
-
try {
|
|
547
|
-
return JSON.stringify(value);
|
|
548
|
-
} catch {
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
function stringifyTransportErrorMessage(value) {
|
|
553
|
-
if (value instanceof Error) return value.message;
|
|
554
|
-
const encoded = stringifyErrorBody(value);
|
|
555
|
-
if (encoded !== void 0) return encoded;
|
|
556
|
-
try {
|
|
557
|
-
return String(value);
|
|
558
|
-
} catch {
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
function normalizeTransportErrorBody(value) {
|
|
563
|
-
const text = stringifyErrorBody(value);
|
|
564
|
-
if (!text?.trim()) return;
|
|
565
|
-
const redacted = redactSensitiveText(text);
|
|
566
|
-
return redacted.length > 500 ? `${truncateUtf16Safe(redacted, 499)}…` : redacted;
|
|
567
|
-
}
|
|
568
|
-
function extractTransportErrorDetails(error) {
|
|
569
|
-
const errorObject = error && typeof error === "object" ? error : void 0;
|
|
570
|
-
const nestedError = readObjectProperty(errorObject, "error");
|
|
571
|
-
const errorCode = readStringLikeProperty(errorObject, "errorCode") ?? readStringLikeProperty(errorObject, "code") ?? readStringLikeProperty(nestedError, "code");
|
|
572
|
-
const errorType = readStringLikeProperty(errorObject, "errorType") ?? readStringLikeProperty(errorObject, "type") ?? readStringLikeProperty(nestedError, "type");
|
|
573
|
-
const errorBody = normalizeTransportErrorBody(readStringLikeProperty(errorObject, "errorBody")) ?? normalizeTransportErrorBody(readStringLikeProperty(errorObject, "body")) ?? normalizeTransportErrorBody(readObjectProperty(errorObject, "body")) ?? normalizeTransportErrorBody(nestedError);
|
|
574
|
-
return {
|
|
575
|
-
...errorCode ? { errorCode } : {},
|
|
576
|
-
...errorType ? { errorType } : {},
|
|
577
|
-
...errorBody ? { errorBody } : {}
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
function assignTransportErrorDetails(output, error, signal) {
|
|
581
|
-
output.stopReason = signal?.aborted ? "aborted" : "error";
|
|
582
|
-
output.errorMessage = stringifyTransportErrorMessage(error);
|
|
583
|
-
Object.assign(output, extractTransportErrorDetails(error));
|
|
584
|
-
}
|
|
585
|
-
function failTransportStream(params) {
|
|
586
|
-
const { stream, output, signal, error, cleanup } = params;
|
|
587
|
-
cleanup?.();
|
|
588
|
-
assignTransportErrorDetails(output, error, signal);
|
|
589
|
-
stream.push({
|
|
590
|
-
type: "error",
|
|
591
|
-
reason: output.stopReason,
|
|
592
|
-
error: output
|
|
593
|
-
});
|
|
594
|
-
stream.end();
|
|
595
|
-
}
|
|
596
|
-
//#endregion
|
|
597
292
|
//#region packages/ai/src/transports/anthropic-transport-stream.ts
|
|
598
293
|
/**
|
|
599
294
|
* Native Anthropic Messages streaming transport.
|
|
@@ -712,7 +407,7 @@ function isKimiAnthropicProvider(provider) {
|
|
|
712
407
|
* identity) requests are excluded until the beta is verified there.
|
|
713
408
|
*/
|
|
714
409
|
function useAnthropicServerSideFallback(model) {
|
|
715
|
-
return usesClaudeFable5MessagesContract(model) && isDirectAnthropicModel(model);
|
|
410
|
+
return (usesClaudeFable5MessagesContract(model) || resolveClaudeOpus5ModelIdentity(model) !== void 0) && isDirectAnthropicModel(model);
|
|
716
411
|
}
|
|
717
412
|
function supportsReasoningContentReplay(model) {
|
|
718
413
|
return resolveProviderEndpoint(model.baseUrl).endpointClass === "xiaomi-native";
|
|
@@ -1200,7 +895,7 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
1200
895
|
max_tokens: maxTokens,
|
|
1201
896
|
stream: true
|
|
1202
897
|
};
|
|
1203
|
-
if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks =
|
|
898
|
+
if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks = ANTHROPIC_SERVER_SIDE_FALLBACKS;
|
|
1204
899
|
if (isOAuthToken) params.system = [
|
|
1205
900
|
{
|
|
1206
901
|
type: "text",
|
|
@@ -1258,7 +953,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
1258
953
|
modelContextWindow: model.contextWindow,
|
|
1259
954
|
modelMaxTokens: model.maxTokens,
|
|
1260
955
|
requestedMaxTokens: options?.maxTokens,
|
|
1261
|
-
useModelDefault: resolveClaudeSonnet5ModelIdentity(model) !== void 0
|
|
956
|
+
useModelDefault: resolveClaudeSonnet5ModelIdentity(model) !== void 0 || resolveClaudeOpus5ModelIdentity(model) !== void 0
|
|
1262
957
|
});
|
|
1263
958
|
if (baseMaxTokens === void 0) throw new Error(`Anthropic Messages transport requires a positive maxTokens value for ${model.provider}/${model.id}`);
|
|
1264
959
|
const reasoningModelMaxTokens = resolvePositiveAnthropicTokenLimit(model.maxTokens) ?? baseMaxTokens;
|
|
@@ -1332,7 +1027,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1332
1027
|
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
|
|
1333
1028
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
1334
1029
|
const transportOptions = resolveAnthropicTransportOptions(model, options, apiKey);
|
|
1335
|
-
const requestContext =
|
|
1030
|
+
const requestContext = prepareClaudeNoPrefillRequestContext(model, context);
|
|
1336
1031
|
const { client, isOAuthToken } = createAnthropicTransportClient({
|
|
1337
1032
|
model,
|
|
1338
1033
|
context: requestContext,
|
|
@@ -1502,7 +1197,11 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1502
1197
|
});
|
|
1503
1198
|
costModel = {
|
|
1504
1199
|
...model,
|
|
1505
|
-
cost:
|
|
1200
|
+
cost: resolveAnthropicFallbackServingModelCost({
|
|
1201
|
+
requestedModelId: model.id,
|
|
1202
|
+
servingModelId: fallbackBoundary.toModel,
|
|
1203
|
+
requestedCost: model.cost
|
|
1204
|
+
})
|
|
1506
1205
|
};
|
|
1507
1206
|
calculateCost(costModel, output.usage);
|
|
1508
1207
|
eventSink.push({
|
|
@@ -1779,7 +1478,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1779
1478
|
}
|
|
1780
1479
|
}
|
|
1781
1480
|
if (refusalBuffer && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
|
|
1782
|
-
if (transportOptions.signal?.aborted) throw
|
|
1481
|
+
if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
|
|
1783
1482
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1784
1483
|
refusalBuffer?.flush();
|
|
1785
1484
|
if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
|
|
@@ -1936,44 +1635,6 @@ function canonicalizeMaxTokensParam(params) {
|
|
|
1936
1635
|
params.merged.maxTokens = resolved;
|
|
1937
1636
|
}
|
|
1938
1637
|
//#endregion
|
|
1939
|
-
//#region packages/ai/src/transports/model-transport-debug.ts
|
|
1940
|
-
function normalizeEnv(value) {
|
|
1941
|
-
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
1942
|
-
}
|
|
1943
|
-
function isTruthyEnv(value) {
|
|
1944
|
-
const normalized = normalizeEnv(value);
|
|
1945
|
-
return normalized.length > 0 && normalized !== "0" && normalized !== "false" && normalized !== "off" && normalized !== "no";
|
|
1946
|
-
}
|
|
1947
|
-
/** Resolves model payload debug verbosity from `OPENCLAW_DEBUG_MODEL_PAYLOAD`. */
|
|
1948
|
-
function resolveModelPayloadDebugMode(env = process.env) {
|
|
1949
|
-
const normalized = normalizeEnv(env.OPENCLAW_DEBUG_MODEL_PAYLOAD);
|
|
1950
|
-
if (normalized === "tools" || normalized === "full-redacted") return normalized;
|
|
1951
|
-
if (normalized === "summary") return "summary";
|
|
1952
|
-
return "off";
|
|
1953
|
-
}
|
|
1954
|
-
/** Resolves SSE stream debug verbosity from `OPENCLAW_DEBUG_SSE`. */
|
|
1955
|
-
function resolveModelSseDebugMode(env = process.env) {
|
|
1956
|
-
const normalized = normalizeEnv(env.OPENCLAW_DEBUG_SSE);
|
|
1957
|
-
if (normalized === "peek") return "peek";
|
|
1958
|
-
if (normalized === "events" || isTruthyEnv(normalized)) return "events";
|
|
1959
|
-
return "off";
|
|
1960
|
-
}
|
|
1961
|
-
/** Returns whether any model transport debug channel is enabled. */
|
|
1962
|
-
function isModelTransportDebugEnabled(env = process.env) {
|
|
1963
|
-
return isTruthyEnv(env.OPENCLAW_DEBUG_MODEL_TRANSPORT) || resolveModelPayloadDebugMode(env) !== "off" || resolveModelSseDebugMode(env) !== "off" || isTruthyEnv(env.OPENCLAW_DEBUG_CODE_MODE);
|
|
1964
|
-
}
|
|
1965
|
-
function isModelFetchMetadataMessage(message) {
|
|
1966
|
-
return message.startsWith("[model-fetch]");
|
|
1967
|
-
}
|
|
1968
|
-
/** Emits model-fetch metadata at info level by default; other diagnostics require debug env. */
|
|
1969
|
-
function emitModelTransportDebug(log, message) {
|
|
1970
|
-
if (isModelFetchMetadataMessage(message) || isModelTransportDebugEnabled()) {
|
|
1971
|
-
log.info(message);
|
|
1972
|
-
return;
|
|
1973
|
-
}
|
|
1974
|
-
log.debug(message);
|
|
1975
|
-
}
|
|
1976
|
-
//#endregion
|
|
1977
1638
|
//#region packages/ai/src/transports/model-transport-url.ts
|
|
1978
1639
|
/**
|
|
1979
1640
|
* Debug formatting helpers for model transport endpoints.
|
|
@@ -2004,9 +1665,6 @@ function formatModelTransportDebugBaseUrl(rawUrl) {
|
|
|
2004
1665
|
* Some providers reject requests without a non-empty user/assistant turn; this
|
|
2005
1666
|
* helper checks the loose message payload shape before transport submission.
|
|
2006
1667
|
*/
|
|
2007
|
-
function hasNonEmptyString(value) {
|
|
2008
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
2009
|
-
}
|
|
2010
1668
|
function hasNonEmptyContentPart(part) {
|
|
2011
1669
|
if (!part || typeof part !== "object") return false;
|
|
2012
1670
|
const record = part;
|
|
@@ -2036,62 +1694,6 @@ function hasOpenAICompatibleConversationTurn(messages) {
|
|
|
2036
1694
|
});
|
|
2037
1695
|
}
|
|
2038
1696
|
//#endregion
|
|
2039
|
-
//#region packages/ai/src/transports/openai-completions-compat.ts
|
|
2040
|
-
function isDefaultRouteProvider(provider, ...ids) {
|
|
2041
|
-
return provider !== void 0 && ids.includes(provider);
|
|
2042
|
-
}
|
|
2043
|
-
/** Resolves default request flags for an OpenAI-compatible completions endpoint. */
|
|
2044
|
-
function resolveOpenAICompletionsCompatDefaults(input) {
|
|
2045
|
-
const { provider, endpointClass, knownProviderFamily, supportsNativeStreamingUsageCompat = false, supportsOpenAICompletionsStreamingUsageCompat = false, usesExplicitProxyLikeEndpoint = false } = input;
|
|
2046
|
-
const isDefaultRoute = endpointClass === "default";
|
|
2047
|
-
const usesConfiguredNonOpenAIEndpoint = endpointClass !== "default" && endpointClass !== "openai-public";
|
|
2048
|
-
const isMoonshotLike = knownProviderFamily === "moonshot" || knownProviderFamily === "modelstudio" || endpointClass === "moonshot-native" || endpointClass === "modelstudio-native";
|
|
2049
|
-
const isModelStudioLike = knownProviderFamily === "modelstudio" || endpointClass === "modelstudio-native" || isDefaultRoute && isDefaultRouteProvider(provider, "dashscope", "modelstudio", "qwen");
|
|
2050
|
-
const isZai = endpointClass === "zai-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "zai");
|
|
2051
|
-
const isDeepSeek = endpointClass === "deepseek-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "deepseek");
|
|
2052
|
-
const isTogether = knownProviderFamily === "together" || isDefaultRoute && isDefaultRouteProvider(input.provider, "together");
|
|
2053
|
-
const isXiaomi = endpointClass === "xiaomi-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "xiaomi");
|
|
2054
|
-
const isNonStandard = endpointClass === "cerebras-native" || endpointClass === "chutes-native" || endpointClass === "deepseek-native" || endpointClass === "mistral-public" || endpointClass === "opencode-native" || endpointClass === "xai-native" || isXiaomi || isZai || isDefaultRoute && isDefaultRouteProvider(input.provider, "cerebras", "chutes", "deepseek", "opencode", "xai");
|
|
2055
|
-
const isOpenRouterLike = input.provider === "openrouter" || endpointClass === "openrouter";
|
|
2056
|
-
const isLocalEndpoint = endpointClass === "local";
|
|
2057
|
-
const usesMaxTokens = endpointClass === "chutes-native" || endpointClass === "mistral-public" || knownProviderFamily === "mistral" || isZai || isTogether || isDefaultRoute && isDefaultRouteProvider(provider, "chutes");
|
|
2058
|
-
return {
|
|
2059
|
-
supportsStore: !isNonStandard && knownProviderFamily !== "mistral" && !usesExplicitProxyLikeEndpoint,
|
|
2060
|
-
supportsDeveloperRole: !isNonStandard && !isMoonshotLike && !usesConfiguredNonOpenAIEndpoint,
|
|
2061
|
-
supportsReasoningEffort: !isZai && !isTogether && knownProviderFamily !== "mistral" && endpointClass !== "xai-native" && !usesExplicitProxyLikeEndpoint,
|
|
2062
|
-
supportsUsageInStreaming: supportsOpenAICompletionsStreamingUsageCompat || !isNonStandard && (isLocalEndpoint || !usesConfiguredNonOpenAIEndpoint || supportsNativeStreamingUsageCompat),
|
|
2063
|
-
maxTokensField: usesMaxTokens ? "max_tokens" : "max_completion_tokens",
|
|
2064
|
-
thinkingFormat: isDeepSeek || isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : isOpenRouterLike ? "openrouter" : "openai",
|
|
2065
|
-
visibleReasoningDetailTypes: isOpenRouterLike ? ["response.output_text", "response.text"] : [],
|
|
2066
|
-
supportsStrictMode: !isZai && !usesConfiguredNonOpenAIEndpoint,
|
|
2067
|
-
requiresReasoningContentOnAssistantMessages: isDeepSeek || isXiaomi,
|
|
2068
|
-
requiresNonEmptyUserOrAssistantMessage: isModelStudioLike
|
|
2069
|
-
};
|
|
2070
|
-
}
|
|
2071
|
-
function resolveOpenAICompletionsCompatDefaultsFromCapabilities(input) {
|
|
2072
|
-
return resolveOpenAICompletionsCompatDefaults(input);
|
|
2073
|
-
}
|
|
2074
|
-
/** Detects endpoint capabilities and defaults for an OpenAI-completions model. */
|
|
2075
|
-
function detectOpenAICompletionsCompat(model, resolveCapabilities = resolveProviderRequestCapabilities) {
|
|
2076
|
-
const capabilities = resolveCapabilities({
|
|
2077
|
-
provider: model.provider,
|
|
2078
|
-
api: "openai-completions",
|
|
2079
|
-
baseUrl: model.baseUrl,
|
|
2080
|
-
capability: "llm",
|
|
2081
|
-
transport: "stream",
|
|
2082
|
-
modelId: model.id,
|
|
2083
|
-
compat: model.compat && typeof model.compat === "object" ? model.compat : void 0
|
|
2084
|
-
});
|
|
2085
|
-
return {
|
|
2086
|
-
capabilities,
|
|
2087
|
-
defaults: resolveOpenAICompletionsCompatDefaultsFromCapabilities({
|
|
2088
|
-
provider: model.provider,
|
|
2089
|
-
...capabilities
|
|
2090
|
-
})
|
|
2091
|
-
};
|
|
2092
|
-
}
|
|
2093
|
-
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.openAICompletionsCompatTestApi")] = { resolveOpenAICompletionsCompatDefaults };
|
|
2094
|
-
//#endregion
|
|
2095
1697
|
//#region packages/ai/src/transports/openai-completions-string-content.ts
|
|
2096
1698
|
/**
|
|
2097
1699
|
* OpenAI Chat Completions compatibility helpers. Some providers only accept
|
|
@@ -2135,33 +1737,60 @@ function stripCompletionMessagesToRoleContent(messages) {
|
|
|
2135
1737
|
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
2136
1738
|
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
2137
1739
|
const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
2138
|
-
function readToolPayloadField
|
|
1740
|
+
function readToolPayloadField(record, field) {
|
|
2139
1741
|
try {
|
|
2140
|
-
return record[field];
|
|
1742
|
+
return Object.hasOwn(record, field) ? record[field] : void 0;
|
|
2141
1743
|
} catch {
|
|
2142
1744
|
return;
|
|
2143
1745
|
}
|
|
2144
1746
|
}
|
|
2145
|
-
function
|
|
1747
|
+
function readCodeModePayloadToolName(tool) {
|
|
2146
1748
|
if (!isRecord(tool)) return;
|
|
2147
|
-
const name = readToolPayloadField
|
|
1749
|
+
const name = readToolPayloadField(tool, "name");
|
|
2148
1750
|
if (typeof name === "string") return name;
|
|
2149
|
-
const fn = readToolPayloadField
|
|
1751
|
+
const fn = readToolPayloadField(tool, "function");
|
|
2150
1752
|
if (!isRecord(fn)) return;
|
|
2151
|
-
const fnName = readToolPayloadField
|
|
1753
|
+
const fnName = readToolPayloadField(fn, "name");
|
|
2152
1754
|
return typeof fnName === "string" ? fnName : void 0;
|
|
2153
1755
|
}
|
|
2154
|
-
function
|
|
2155
|
-
if (!isRecord(payload)
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
1756
|
+
function filterCodeModePayloadTools(payload, visibleToolNames) {
|
|
1757
|
+
if (!isRecord(payload)) return;
|
|
1758
|
+
const tools = readToolPayloadField(payload, "tools");
|
|
1759
|
+
if (!Array.isArray(tools)) return;
|
|
1760
|
+
payload.tools = tools.flatMap((tool) => {
|
|
1761
|
+
const name = readCodeModePayloadToolName(tool);
|
|
1762
|
+
if (typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames)) return [tool];
|
|
1763
|
+
if (!isRecord(tool)) return [];
|
|
1764
|
+
const filteredGroups = {};
|
|
1765
|
+
for (const key of ["functionDeclarations", "function_declarations"]) {
|
|
1766
|
+
const declarations = readToolPayloadField(tool, key);
|
|
1767
|
+
if (!Array.isArray(declarations)) continue;
|
|
1768
|
+
const filtered = declarations.filter((declaration) => {
|
|
1769
|
+
const declarationName = readCodeModePayloadToolName(declaration);
|
|
1770
|
+
return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
|
|
1771
|
+
});
|
|
1772
|
+
if (filtered.length > 0) filteredGroups[key] = filtered;
|
|
1773
|
+
}
|
|
1774
|
+
return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
function resolveCodeModeResponsesVisibleToolNames(context) {
|
|
1778
|
+
return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
|
|
1779
|
+
}
|
|
1780
|
+
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
|
|
1781
|
+
if (!isRecord(payload)) return;
|
|
1782
|
+
const tools = readToolPayloadField(payload, "tools");
|
|
1783
|
+
if (!Array.isArray(tools)) return;
|
|
1784
|
+
payload.tools = tools.filter((tool) => {
|
|
1785
|
+
const name = readCodeModePayloadToolName(tool);
|
|
1786
|
+
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames);
|
|
2159
1787
|
});
|
|
2160
1788
|
}
|
|
2161
|
-
function assertCodeModeResponsesToolSurface(payload) {
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
1789
|
+
function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
|
|
1790
|
+
const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
|
|
1791
|
+
if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
|
|
1792
|
+
const names = tools.map(readCodeModePayloadToolName).filter((name) => typeof name === "string" && name.length > 0).toSorted((left, right) => left.localeCompare(right));
|
|
1793
|
+
if (names.length >= 2 && names.length === tools.length && new Set(names).size === names.length && names.includes("exec") && names.includes("wait") && names.every((name) => isCodeModeModelVisibleToolName(name, visibleToolNames))) return;
|
|
2165
1794
|
throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
|
|
2166
1795
|
}
|
|
2167
1796
|
function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
|
|
@@ -2248,134 +1877,41 @@ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, se
|
|
|
2248
1877
|
callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
|
|
2249
1878
|
precedence: "caller-wins"
|
|
2250
1879
|
}).headers ?? {};
|
|
2251
|
-
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = sessionId;
|
|
1880
|
+
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
|
|
2252
1881
|
return resolvedHeaders;
|
|
2253
1882
|
}
|
|
2254
|
-
function resolveOpenAISdkTimeoutMs(model) {
|
|
2255
|
-
return resolveModelRequestTimeoutMs(model,
|
|
1883
|
+
function resolveOpenAISdkTimeoutMs(model, timeoutMs) {
|
|
1884
|
+
return resolveModelRequestTimeoutMs(model, timeoutMs);
|
|
2256
1885
|
}
|
|
2257
1886
|
function buildOpenAISdkClientOptions(model) {
|
|
2258
1887
|
const timeout = resolveOpenAISdkTimeoutMs(model);
|
|
2259
1888
|
return timeout === void 0 ? {} : { timeout };
|
|
2260
1889
|
}
|
|
2261
1890
|
function buildOpenAISdkRequestOptions(model, signal, options) {
|
|
2262
|
-
const timeout = resolveOpenAISdkTimeoutMs(model);
|
|
1891
|
+
const timeout = resolveOpenAISdkTimeoutMs(model, options?.timeoutMs);
|
|
2263
1892
|
const headers = options?.stream === true && usesNativeOpenAICodexResponsesBackend(model) ? { Accept: "text/event-stream" } : void 0;
|
|
2264
|
-
if (timeout === void 0 && !signal && !headers) return;
|
|
1893
|
+
if (timeout === void 0 && options?.maxRetries === void 0 && !signal && !headers) return;
|
|
2265
1894
|
return {
|
|
2266
1895
|
...headers ? { headers } : {},
|
|
2267
1896
|
...signal ? { signal } : {},
|
|
2268
|
-
...timeout !== void 0 ? { timeout } : {}
|
|
2269
|
-
|
|
2270
|
-
}
|
|
2271
|
-
function detectCompat(model) {
|
|
2272
|
-
const { defaults } = detectOpenAICompletionsCompat(model);
|
|
2273
|
-
return {
|
|
2274
|
-
supportsStore: defaults.supportsStore,
|
|
2275
|
-
supportsDeveloperRole: defaults.supportsDeveloperRole,
|
|
2276
|
-
supportsReasoningEffort: defaults.supportsReasoningEffort,
|
|
2277
|
-
reasoningEffortMap: {},
|
|
2278
|
-
supportsUsageInStreaming: defaults.supportsUsageInStreaming,
|
|
2279
|
-
maxTokensField: defaults.maxTokensField,
|
|
2280
|
-
requiresToolResultName: false,
|
|
2281
|
-
requiresAssistantAfterToolResult: false,
|
|
2282
|
-
requiresThinkingAsText: false,
|
|
2283
|
-
thinkingFormat: defaults.thinkingFormat,
|
|
2284
|
-
visibleReasoningDetailTypes: defaults.visibleReasoningDetailTypes,
|
|
2285
|
-
openRouterRouting: {},
|
|
2286
|
-
vercelGatewayRouting: {},
|
|
2287
|
-
supportsStrictMode: defaults.supportsStrictMode,
|
|
2288
|
-
requiresReasoningContentOnAssistantMessages: defaults.requiresReasoningContentOnAssistantMessages,
|
|
2289
|
-
requiresNonEmptyUserOrAssistantMessage: defaults.requiresNonEmptyUserOrAssistantMessage
|
|
1897
|
+
...timeout !== void 0 ? { timeout } : {},
|
|
1898
|
+
...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
|
|
2290
1899
|
};
|
|
2291
1900
|
}
|
|
2292
1901
|
function getCompat(model) {
|
|
2293
|
-
const
|
|
1902
|
+
const resolved = resolveOpenAICompletionsCompat(model);
|
|
2294
1903
|
const compat = model.compat ?? {};
|
|
2295
|
-
const supportsStore = typeof compat.supportsStore === "boolean" ? compat.supportsStore : detected.supportsStore;
|
|
2296
|
-
const supportsReasoningEffort = typeof compat.supportsReasoningEffort === "boolean" ? compat.supportsReasoningEffort : detected.supportsReasoningEffort;
|
|
2297
1904
|
return {
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
maxTokensField: compat.maxTokensField ?? detected.maxTokensField,
|
|
2304
|
-
requiresToolResultName: compat.requiresToolResultName ?? detected.requiresToolResultName,
|
|
2305
|
-
requiresAssistantAfterToolResult: compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
|
|
2306
|
-
requiresThinkingAsText: compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
|
|
2307
|
-
thinkingFormat: compat.thinkingFormat ?? detected.thinkingFormat,
|
|
2308
|
-
openRouterRouting: compat.openRouterRouting ?? {},
|
|
2309
|
-
vercelGatewayRouting: compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
|
|
2310
|
-
supportsStrictMode: compat.supportsStrictMode ?? detected.supportsStrictMode,
|
|
2311
|
-
supportsPromptCacheKey: compat.supportsPromptCacheKey === true,
|
|
2312
|
-
supportsLongCacheRetention: compat.supportsLongCacheRetention !== false,
|
|
1905
|
+
...resolved,
|
|
1906
|
+
cacheControlFormat: resolved.cacheControlFormat,
|
|
1907
|
+
reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, {}),
|
|
1908
|
+
openRouterRouting: resolved.openRouterRouting ?? {},
|
|
1909
|
+
vercelGatewayRouting: resolved.vercelGatewayRouting,
|
|
2313
1910
|
requiresStringContent: compat.requiresStringContent ?? false,
|
|
2314
|
-
strictMessageKeys: compat.strictMessageKeys === true
|
|
2315
|
-
visibleReasoningDetailTypes: compat.visibleReasoningDetailTypes ?? detected.visibleReasoningDetailTypes,
|
|
2316
|
-
requiresReasoningContentOnAssistantMessages: compat.requiresReasoningContentOnAssistantMessages ?? detected.requiresReasoningContentOnAssistantMessages,
|
|
2317
|
-
requiresNonEmptyUserOrAssistantMessage: detected.requiresNonEmptyUserOrAssistantMessage
|
|
1911
|
+
strictMessageKeys: compat.strictMessageKeys === true
|
|
2318
1912
|
};
|
|
2319
1913
|
}
|
|
2320
1914
|
//#endregion
|
|
2321
|
-
//#region packages/ai/src/transports/openai-transport-shared.ts
|
|
2322
|
-
/** Shared options, usage shape, cache identity, ordering, and stream scheduling for OpenAI APIs. */
|
|
2323
|
-
const MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS = 12;
|
|
2324
|
-
const MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS = 64;
|
|
2325
|
-
const GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP = "skip_thought_signature_validator";
|
|
2326
|
-
const log = {
|
|
2327
|
-
debug(message, data) {
|
|
2328
|
-
getAiTransportHost().logDebug("openai-transport", () => ({
|
|
2329
|
-
message,
|
|
2330
|
-
data
|
|
2331
|
-
}));
|
|
2332
|
-
},
|
|
2333
|
-
info(message, data) {
|
|
2334
|
-
getAiTransportHost().logInfo("openai-transport", message, data);
|
|
2335
|
-
},
|
|
2336
|
-
warn(message, data) {
|
|
2337
|
-
getAiTransportHost().logWarn("openai-transport", message, data);
|
|
2338
|
-
}
|
|
2339
|
-
};
|
|
2340
|
-
function throwIfModelStreamAborted(signal) {
|
|
2341
|
-
if (signal?.aborted) throw new Error("Request was aborted");
|
|
2342
|
-
}
|
|
2343
|
-
function createModelStreamCooperativeScheduler(signal) {
|
|
2344
|
-
let lastYieldedAt = Date.now();
|
|
2345
|
-
let eventsSinceYield = 0;
|
|
2346
|
-
return { async afterEvent() {
|
|
2347
|
-
throwIfModelStreamAborted(signal);
|
|
2348
|
-
eventsSinceYield += 1;
|
|
2349
|
-
const now = Date.now();
|
|
2350
|
-
if (eventsSinceYield < MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS && now - lastYieldedAt < MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS) return;
|
|
2351
|
-
eventsSinceYield = 0;
|
|
2352
|
-
lastYieldedAt = now;
|
|
2353
|
-
await new Promise((resolve) => {
|
|
2354
|
-
setTimeout(resolve, 0);
|
|
2355
|
-
});
|
|
2356
|
-
throwIfModelStreamAborted(signal);
|
|
2357
|
-
} };
|
|
2358
|
-
}
|
|
2359
|
-
function resolveCacheRetention(cacheRetention) {
|
|
2360
|
-
if (cacheRetention === "short" || cacheRetention === "long" || cacheRetention === "none") return cacheRetention;
|
|
2361
|
-
if (typeof process !== "undefined" && process.env.OPENCLAW_CACHE_RETENTION === "long") return "long";
|
|
2362
|
-
return "short";
|
|
2363
|
-
}
|
|
2364
|
-
function resolvePromptCacheKey(options, cacheRetention) {
|
|
2365
|
-
if (cacheRetention === "none") return;
|
|
2366
|
-
return clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId);
|
|
2367
|
-
}
|
|
2368
|
-
function compareTransportToolText(left, right) {
|
|
2369
|
-
const leftText = left ?? "";
|
|
2370
|
-
const rightText = right ?? "";
|
|
2371
|
-
if (leftText < rightText) return -1;
|
|
2372
|
-
if (leftText > rightText) return 1;
|
|
2373
|
-
return 0;
|
|
2374
|
-
}
|
|
2375
|
-
function sortTransportToolsByName(tools) {
|
|
2376
|
-
return tools.toSorted((left, right) => compareTransportToolText(left.name, right.name) || compareTransportToolText(left.description, right.description));
|
|
2377
|
-
}
|
|
2378
|
-
//#endregion
|
|
2379
1915
|
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
2380
1916
|
function hasToolHistory(messages) {
|
|
2381
1917
|
return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
|
|
@@ -2529,13 +2065,17 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
2529
2065
|
const nextParams = await options?.onPayload?.(params, model);
|
|
2530
2066
|
if (nextParams !== void 0) params = nextParams;
|
|
2531
2067
|
if (options?.openclawCodeModeToolSurface === true) {
|
|
2532
|
-
|
|
2533
|
-
|
|
2068
|
+
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
2069
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
2070
|
+
assertCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
2534
2071
|
}
|
|
2535
2072
|
if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
|
|
2536
2073
|
const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
|
|
2537
2074
|
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
2538
|
-
const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal
|
|
2075
|
+
const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
|
|
2076
|
+
timeoutMs: options?.timeoutMs,
|
|
2077
|
+
maxRetries: options?.maxRetries
|
|
2078
|
+
}));
|
|
2539
2079
|
stream.push({
|
|
2540
2080
|
type: "start",
|
|
2541
2081
|
partial: output
|
|
@@ -2782,6 +2322,7 @@ async function processOpenAICompletionsStream(responseStream, output, model, str
|
|
|
2782
2322
|
await cooperativeScheduler.afterEvent();
|
|
2783
2323
|
continue;
|
|
2784
2324
|
}
|
|
2325
|
+
notifyLlmRequestActivity(options?.signal);
|
|
2785
2326
|
const chunk = rawChunk;
|
|
2786
2327
|
output.responseId ||= chunk.id;
|
|
2787
2328
|
let hasReasoningUsageActivity = false;
|
|
@@ -3288,7 +2829,7 @@ function convertTools(tools, compat, model) {
|
|
|
3288
2829
|
});
|
|
3289
2830
|
return {
|
|
3290
2831
|
projection,
|
|
3291
|
-
tools:
|
|
2832
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
3292
2833
|
const functionTool = {
|
|
3293
2834
|
name: tool.name,
|
|
3294
2835
|
description: tool.description,
|
|
@@ -3485,7 +3026,12 @@ function buildOpenAICompletionsParams(model, context, options) {
|
|
|
3485
3026
|
}
|
|
3486
3027
|
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
3487
3028
|
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
3488
|
-
|
|
3029
|
+
const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
3030
|
+
provider: model.provider,
|
|
3031
|
+
baseUrl: model.baseUrl,
|
|
3032
|
+
hasTools: () => Boolean(context.tools?.length)
|
|
3033
|
+
}) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
3034
|
+
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
3489
3035
|
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
3490
3036
|
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
3491
3037
|
if (options?.seed !== void 0) params.seed = options.seed;
|
|
@@ -3784,1443 +3330,96 @@ function applyOpenAIResponsesPayloadPolicy(payloadObj, policy) {
|
|
|
3784
3330
|
if (policy.shouldStripInputStatus) stripInputItemStatuses(payloadObj.input);
|
|
3785
3331
|
}
|
|
3786
3332
|
//#endregion
|
|
3787
|
-
//#region packages/ai/src/transports/openai-responses-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3333
|
+
//#region packages/ai/src/transports/openai-responses-params-internal.ts
|
|
3334
|
+
function convertResponsesTools(tools, model, options) {
|
|
3335
|
+
const projection = projectOpenAITools(tools);
|
|
3336
|
+
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
|
|
3337
|
+
transport: "responses",
|
|
3338
|
+
model
|
|
3339
|
+
});
|
|
3340
|
+
return {
|
|
3341
|
+
projection,
|
|
3342
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
3343
|
+
const result = {
|
|
3344
|
+
type: "function",
|
|
3345
|
+
name: tool.name,
|
|
3346
|
+
description: tool.description,
|
|
3347
|
+
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
3348
|
+
};
|
|
3349
|
+
if (strict !== void 0) result.strict = strict;
|
|
3350
|
+
return result;
|
|
3351
|
+
})
|
|
3352
|
+
};
|
|
3793
3353
|
}
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
return
|
|
3800
|
-
}
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
const
|
|
3808
|
-
|
|
3809
|
-
...mappedUsage,
|
|
3810
|
-
...reasoningTokens === void 0 ? {} : { reasoningTokens },
|
|
3811
|
-
cost: {
|
|
3812
|
-
input: 0,
|
|
3813
|
-
output: 0,
|
|
3814
|
-
cacheRead: 0,
|
|
3815
|
-
cacheWrite: 0,
|
|
3816
|
-
total: 0
|
|
3817
|
-
}
|
|
3818
|
-
};
|
|
3819
|
-
}
|
|
3820
|
-
calculateCost(model, output.usage);
|
|
3821
|
-
if (params.applyServiceTierPricing) params.applyServiceTierPricing(output.usage, response?.service_tier ?? params.serviceTier);
|
|
3822
|
-
const terminal = resolveResponsesTerminalStopReason({
|
|
3823
|
-
status: response?.status,
|
|
3824
|
-
incompleteReason: readIncompleteReason(response),
|
|
3825
|
-
hasToolCall: output.content.some((block) => block.type === "toolCall")
|
|
3354
|
+
function getPromptCacheRetention(baseUrl, cacheRetention) {
|
|
3355
|
+
if (cacheRetention !== "long") return;
|
|
3356
|
+
return baseUrl?.includes("api.openai.com") ? "24h" : void 0;
|
|
3357
|
+
}
|
|
3358
|
+
function resolveOpenAIReasoningEffort(options) {
|
|
3359
|
+
return normalizeOpenAIReasoningEffort(options?.reasoningEffort ?? options?.reasoning ?? "high");
|
|
3360
|
+
}
|
|
3361
|
+
function hasResponsesWebSearchTool(tools) {
|
|
3362
|
+
if (!Array.isArray(tools)) return false;
|
|
3363
|
+
return tools.some((tool) => {
|
|
3364
|
+
if (!isRecord(tool)) return false;
|
|
3365
|
+
if (tool.type === "web_search") return true;
|
|
3366
|
+
if (tool.type === "function" && tool.name === "web_search") return true;
|
|
3367
|
+
const fn = tool.function;
|
|
3368
|
+
return isRecord(fn) && fn.name === "web_search";
|
|
3826
3369
|
});
|
|
3827
|
-
output.stopReason = terminal.stopReason;
|
|
3828
|
-
if (terminal.errorMessage) output.errorMessage = terminal.errorMessage;
|
|
3829
3370
|
}
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
},
|
|
3843
|
-
{
|
|
3844
|
-
mime: "image/webp",
|
|
3845
|
-
matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
3846
|
-
},
|
|
3847
|
-
{
|
|
3848
|
-
mime: "image/gif",
|
|
3849
|
-
matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
|
|
3850
|
-
},
|
|
3851
|
-
{
|
|
3852
|
-
mime: "image/bmp",
|
|
3853
|
-
matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
|
|
3371
|
+
function raiseMinimalReasoningForResponsesWebSearch(params) {
|
|
3372
|
+
if (params.effort !== "minimal" || !hasResponsesWebSearchTool(params.tools)) return params.effort;
|
|
3373
|
+
for (const effort of [
|
|
3374
|
+
"low",
|
|
3375
|
+
"medium",
|
|
3376
|
+
"high"
|
|
3377
|
+
]) {
|
|
3378
|
+
const resolved = resolveOpenAIReasoningEffortForModel({
|
|
3379
|
+
model: params.model,
|
|
3380
|
+
effort
|
|
3381
|
+
});
|
|
3382
|
+
if (resolved && resolved !== "none" && resolved !== "minimal") return resolved;
|
|
3854
3383
|
}
|
|
3384
|
+
return params.effort;
|
|
3385
|
+
}
|
|
3386
|
+
const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
|
|
3387
|
+
"max_output_tokens",
|
|
3388
|
+
"metadata",
|
|
3389
|
+
"prompt_cache_retention",
|
|
3390
|
+
"service_tier",
|
|
3391
|
+
"temperature",
|
|
3392
|
+
"top_p"
|
|
3855
3393
|
];
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
"
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
"hevm",
|
|
3864
|
-
"hevs"
|
|
3865
|
-
]);
|
|
3866
|
-
const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
|
|
3867
|
-
const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
|
|
3868
|
-
const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
|
|
3869
|
-
"image/png",
|
|
3870
|
-
"image/jpeg",
|
|
3871
|
-
"image/webp",
|
|
3872
|
-
"image/gif"
|
|
3873
|
-
]);
|
|
3874
|
-
function startsWithDataUrl(value) {
|
|
3875
|
-
return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
|
|
3394
|
+
function stripOpenAICodexResponsesUnsupportedTextFields(params) {
|
|
3395
|
+
const text = params.text;
|
|
3396
|
+
if (!text || typeof text !== "object" || Array.isArray(text)) return;
|
|
3397
|
+
const sanitizedText = { ...text };
|
|
3398
|
+
delete sanitizedText.format;
|
|
3399
|
+
if (Object.keys(sanitizedText).length > 0) params.text = sanitizedText;
|
|
3400
|
+
else delete params.text;
|
|
3876
3401
|
}
|
|
3877
|
-
function
|
|
3878
|
-
if (
|
|
3879
|
-
const
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
|
|
3402
|
+
function sanitizeOpenAICodexResponsesParams(model, params) {
|
|
3403
|
+
if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
|
|
3404
|
+
for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
|
|
3405
|
+
stripOpenAICodexResponsesUnsupportedTextFields(params);
|
|
3406
|
+
return params;
|
|
3883
3407
|
}
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
return
|
|
3408
|
+
function buildOpenAICodexResponsesInstructions(context) {
|
|
3409
|
+
if (!context.systemPrompt) return;
|
|
3410
|
+
return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
|
|
3887
3411
|
}
|
|
3888
|
-
function
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
function sanitizeInlineImageBase64(params) {
|
|
3893
|
-
if (!isImageMimeType(params.mimeType)) return;
|
|
3894
|
-
const canonicalPayload = canonicalizeBase64(params.base64);
|
|
3895
|
-
if (!canonicalPayload) return;
|
|
3896
|
-
const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
|
|
3897
|
-
if (!sniffedMimeType) return;
|
|
3898
|
-
return {
|
|
3899
|
-
mimeType: sniffedMimeType,
|
|
3900
|
-
base64: canonicalPayload
|
|
3901
|
-
};
|
|
3902
|
-
}
|
|
3903
|
-
function parseInlineImageDataUrl(value) {
|
|
3904
|
-
if (!startsWithDataUrl(value)) return {
|
|
3905
|
-
metadata: [],
|
|
3906
|
-
payload: value
|
|
3907
|
-
};
|
|
3908
|
-
const commaIndex = value.indexOf(",");
|
|
3909
|
-
if (commaIndex < 0) return;
|
|
3910
|
-
return {
|
|
3911
|
-
metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
|
|
3912
|
-
payload: value.slice(commaIndex + 1)
|
|
3913
|
-
};
|
|
3914
|
-
}
|
|
3915
|
-
function metadataAllowsImageBase64(metadata) {
|
|
3916
|
-
const [mimeType, ...options] = metadata;
|
|
3917
|
-
return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
|
|
3918
|
-
}
|
|
3919
|
-
function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
|
|
3920
|
-
const parsed = parseInlineImageDataUrl(imageUrl);
|
|
3921
|
-
if (!parsed) return;
|
|
3922
|
-
if (parsed.metadata.length === 0) return imageUrl;
|
|
3923
|
-
if (!metadataAllowsImageBase64(parsed.metadata)) return;
|
|
3924
|
-
const [mimeType] = parsed.metadata;
|
|
3925
|
-
const sanitized = sanitizeInlineImageBase64({
|
|
3926
|
-
mimeType: mimeType ?? "",
|
|
3927
|
-
base64: parsed.payload
|
|
3928
|
-
});
|
|
3929
|
-
if (!sanitized) return;
|
|
3930
|
-
if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
|
|
3931
|
-
return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
|
|
3932
|
-
}
|
|
3933
|
-
/** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
|
|
3934
|
-
function sanitizeInlineImageDataUrl(imageUrl) {
|
|
3935
|
-
return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
|
|
3936
|
-
}
|
|
3937
|
-
//#endregion
|
|
3938
|
-
//#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
|
|
3939
|
-
/**
|
|
3940
|
-
* Sanitizes OpenAI Responses payloads before transport. Invalid inline images
|
|
3941
|
-
* are replaced with text placeholders so the request remains valid and
|
|
3942
|
-
* auditable.
|
|
3943
|
-
*/
|
|
3944
|
-
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
|
|
3945
|
-
function invalidSnakeImage() {
|
|
3946
|
-
return {
|
|
3947
|
-
type: "input_text",
|
|
3948
|
-
text: `[${IMAGE_OMITTED_TEXT}]`
|
|
3949
|
-
};
|
|
3950
|
-
}
|
|
3951
|
-
function sanitizeValue(value) {
|
|
3952
|
-
if (Array.isArray(value)) return value.map(sanitizeValue);
|
|
3953
|
-
if (!isRecord(value)) return value;
|
|
3954
|
-
if (value.type === "input_image" && typeof value.image_url === "string") {
|
|
3955
|
-
const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
|
|
3956
|
-
return imageUrl ? {
|
|
3957
|
-
...value,
|
|
3958
|
-
image_url: imageUrl
|
|
3959
|
-
} : invalidSnakeImage();
|
|
3960
|
-
}
|
|
3961
|
-
const next = {};
|
|
3962
|
-
for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
|
|
3963
|
-
return next;
|
|
3964
|
-
}
|
|
3965
|
-
/** Sanitize inline image fields inside a Responses API payload. */
|
|
3966
|
-
function sanitizeResponsesImagePayload(params) {
|
|
3967
|
-
if (!Array.isArray(params.input)) return params;
|
|
3968
|
-
return {
|
|
3969
|
-
...params,
|
|
3970
|
-
input: sanitizeValue(params.input)
|
|
3971
|
-
};
|
|
3972
|
-
}
|
|
3973
|
-
//#endregion
|
|
3974
|
-
//#region packages/ai/src/transports/openai-responses-transport.ts
|
|
3975
|
-
/**
|
|
3976
|
-
* OpenAI Responses transport for Azure variants, replay, reasoning, and payload policy.
|
|
3977
|
-
*/
|
|
3978
|
-
const DEFAULT_AZURE_OPENAI_API_VERSION = "preview";
|
|
3979
|
-
const OPENAI_CODEX_RESPONSES_EMPTY_INPUT_TEXT = " ";
|
|
3980
|
-
const OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS = "Follow the user request.";
|
|
3981
|
-
const AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS = 3e4;
|
|
3982
|
-
const RESPONSE_FAILED_NO_DETAILS_MESSAGE = "Unknown error (no error details in response)";
|
|
3983
|
-
const OPENAI_RESPONSES_REASONING_REPLAY_META_KEY = "__openclaw_replay";
|
|
3984
|
-
const OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY = "openclawReasoningReplay";
|
|
3985
|
-
const OPENAI_RESPONSES_REPLAY_ITEM_ID_MAX_LENGTH = 64;
|
|
3986
|
-
function stringifyUnknown(value, fallback = "") {
|
|
3987
|
-
if (typeof value === "string") return value;
|
|
3988
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
3989
|
-
return fallback;
|
|
3990
|
-
}
|
|
3991
|
-
function stringifyJsonLike(value, fallback = "") {
|
|
3992
|
-
if (typeof value === "string") return value;
|
|
3993
|
-
if (value && typeof value === "object") return JSON.stringify(value);
|
|
3994
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
3995
|
-
return fallback;
|
|
3996
|
-
}
|
|
3997
|
-
function getServiceTierCostMultiplier(serviceTier) {
|
|
3998
|
-
switch (serviceTier) {
|
|
3999
|
-
case "flex": return .5;
|
|
4000
|
-
case "priority": return 2;
|
|
4001
|
-
default: return 1;
|
|
4002
|
-
}
|
|
4003
|
-
}
|
|
4004
|
-
function applyServiceTierPricing(usage, serviceTier) {
|
|
4005
|
-
const multiplier = getServiceTierCostMultiplier(serviceTier);
|
|
4006
|
-
if (multiplier === 1) return;
|
|
4007
|
-
usage.cost.input *= multiplier;
|
|
4008
|
-
usage.cost.output *= multiplier;
|
|
4009
|
-
usage.cost.cacheRead *= multiplier;
|
|
4010
|
-
usage.cost.cacheWrite *= multiplier;
|
|
4011
|
-
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
4012
|
-
}
|
|
4013
|
-
function safeDebugValue(value) {
|
|
4014
|
-
if (typeof value === "string") return value;
|
|
4015
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
4016
|
-
if (value === null) return "null";
|
|
4017
|
-
if (value === void 0) return "undefined";
|
|
4018
|
-
return Array.isArray(value) ? "array" : typeof value;
|
|
4019
|
-
}
|
|
4020
|
-
function responseInputTextChars(input) {
|
|
4021
|
-
if (typeof input === "string") return input.length;
|
|
4022
|
-
if (Array.isArray(input)) return input.reduce((total, item) => total + responseInputTextChars(item), 0);
|
|
4023
|
-
if (!input || typeof input !== "object") return 0;
|
|
4024
|
-
const record = input;
|
|
4025
|
-
let total = 0;
|
|
4026
|
-
if (typeof record.text === "string") total += record.text.length;
|
|
4027
|
-
if (typeof record.content === "string") total += record.content.length;
|
|
4028
|
-
else if (Array.isArray(record.content)) total += responseInputTextChars(record.content);
|
|
4029
|
-
return total;
|
|
4030
|
-
}
|
|
4031
|
-
function responseInputRoles(input) {
|
|
4032
|
-
if (!Array.isArray(input)) return "";
|
|
4033
|
-
const roles = /* @__PURE__ */ new Set();
|
|
4034
|
-
for (const item of input) if (item && typeof item === "object") {
|
|
4035
|
-
const role = item.role;
|
|
4036
|
-
if (typeof role === "string" && role.trim()) roles.add(role.trim());
|
|
4037
|
-
}
|
|
4038
|
-
return [...roles].toSorted().join(",");
|
|
4039
|
-
}
|
|
4040
|
-
function readToolPayloadField(record, field) {
|
|
4041
|
-
try {
|
|
4042
|
-
return record[field];
|
|
4043
|
-
} catch {
|
|
4044
|
-
return;
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
function readResponsesToolDisplayName(tool) {
|
|
4048
|
-
if (!tool || typeof tool !== "object") return "";
|
|
4049
|
-
const record = tool;
|
|
4050
|
-
const name = readToolPayloadField(record, "name");
|
|
4051
|
-
if (typeof name === "string") return name;
|
|
4052
|
-
const fn = readToolPayloadField(record, "function");
|
|
4053
|
-
if (fn && typeof fn === "object") {
|
|
4054
|
-
const fnName = readToolPayloadField(fn, "name");
|
|
4055
|
-
if (typeof fnName === "string") return fnName;
|
|
4056
|
-
}
|
|
4057
|
-
const type = readToolPayloadField(record, "type");
|
|
4058
|
-
return typeof type === "string" && type !== "function" ? type : "";
|
|
4059
|
-
}
|
|
4060
|
-
function summarizeResponsesTools(tools) {
|
|
4061
|
-
if (!Array.isArray(tools)) return "count=0";
|
|
4062
|
-
const names = tools.map(readResponsesToolDisplayName).filter(Boolean);
|
|
4063
|
-
const mode = resolveModelPayloadDebugMode();
|
|
4064
|
-
const maxNames = mode === "tools" || mode === "full-redacted" ? names.length : 12;
|
|
4065
|
-
const label = maxNames >= names.length ? "names" : "sample";
|
|
4066
|
-
const shown = names.slice(0, maxNames).join(",");
|
|
4067
|
-
return `count=${tools.length}${shown ? ` ${label}=${shown}` : ""}`;
|
|
4068
|
-
}
|
|
4069
|
-
function stringifyRedactedPayload(value) {
|
|
4070
|
-
try {
|
|
4071
|
-
const encoded = JSON.stringify(value);
|
|
4072
|
-
if (!encoded) return "<empty>";
|
|
4073
|
-
const redacted = redactSensitiveText(encoded, { mode: "tools" });
|
|
4074
|
-
return redacted.length > 8e3 ? `${truncateUtf16Safe(redacted, 8e3)}…<truncated>` : redacted;
|
|
4075
|
-
} catch {
|
|
4076
|
-
return "<unserializable>";
|
|
4077
|
-
}
|
|
4078
|
-
}
|
|
4079
|
-
function stringifyRedactedEvent(value) {
|
|
4080
|
-
const redacted = stringifyRedactedPayload(value);
|
|
4081
|
-
return redacted.length > 2e3 ? `${truncateUtf16Safe(redacted, 2e3)}…<truncated>` : redacted;
|
|
4082
|
-
}
|
|
4083
|
-
const RESPONSE_FAILED_FAILURE_FIELD_KEYS = [
|
|
4084
|
-
"error",
|
|
4085
|
-
"incomplete_details",
|
|
4086
|
-
"status_details",
|
|
4087
|
-
"failure_reason",
|
|
4088
|
-
"last_error",
|
|
4089
|
-
"provider_error",
|
|
4090
|
-
"error_details"
|
|
4091
|
-
];
|
|
4092
|
-
function readResponseFailedString(record, key) {
|
|
4093
|
-
return stringifyUnknown(record?.[key]);
|
|
4094
|
-
}
|
|
4095
|
-
function buildResponsesFailedEventSummary(message, responseId, observation) {
|
|
4096
|
-
const summary = { message };
|
|
4097
|
-
if (responseId) summary.responseId = responseId;
|
|
4098
|
-
if (observation) summary.observation = observation;
|
|
4099
|
-
return summary;
|
|
4100
|
-
}
|
|
4101
|
-
function isResponseFailedIdentifierKey(key) {
|
|
4102
|
-
const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
|
|
4103
|
-
return normalized === "requestid" || normalized === "xrequestid" || normalized === "providerrequestid" || normalized === "providerresponseid" || normalized === "litellmrequestid" || normalized.includes("request") && normalized.endsWith("id") || normalized.includes("provider") && normalized.endsWith("id");
|
|
4104
|
-
}
|
|
4105
|
-
function collectResponseFailedIdentifierHashes(value, opts = {}) {
|
|
4106
|
-
const path = opts.path ?? "";
|
|
4107
|
-
const depth = opts.depth ?? 0;
|
|
4108
|
-
const identifierKey = opts.identifierKey ?? "";
|
|
4109
|
-
const out = opts.out ?? [];
|
|
4110
|
-
const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
|
|
4111
|
-
if (out.length >= 12 || depth > 4 || !value || typeof value !== "object") return out;
|
|
4112
|
-
if (seen.has(value)) return out;
|
|
4113
|
-
seen.add(value);
|
|
4114
|
-
if (Array.isArray(value)) {
|
|
4115
|
-
for (const [index, item] of value.entries()) {
|
|
4116
|
-
if (index >= 8 || out.length >= 12) break;
|
|
4117
|
-
const itemString = typeof item === "string" || typeof item === "number" ? String(item).trim() : "";
|
|
4118
|
-
if (identifierKey && isResponseFailedIdentifierKey(identifierKey) && itemString) {
|
|
4119
|
-
out.push(`${path}[${index}]=${redactIdentifier(itemString, { len: 12 })}`);
|
|
4120
|
-
continue;
|
|
4121
|
-
}
|
|
4122
|
-
collectResponseFailedIdentifierHashes(item, {
|
|
4123
|
-
path: `${path}[${index}]`,
|
|
4124
|
-
depth: depth + 1,
|
|
4125
|
-
identifierKey,
|
|
4126
|
-
out,
|
|
4127
|
-
seen
|
|
4128
|
-
});
|
|
4129
|
-
}
|
|
4130
|
-
return out;
|
|
4131
|
-
}
|
|
4132
|
-
for (const [key, child] of Object.entries(value)) {
|
|
4133
|
-
if (out.length >= 12) break;
|
|
4134
|
-
const childPath = path ? `${path}.${key}` : key;
|
|
4135
|
-
const childString = typeof child === "string" || typeof child === "number" ? String(child).trim() : "";
|
|
4136
|
-
if (isResponseFailedIdentifierKey(key) && childString) {
|
|
4137
|
-
out.push(`${childPath}=${redactIdentifier(childString, { len: 12 })}`);
|
|
4138
|
-
continue;
|
|
4139
|
-
}
|
|
4140
|
-
collectResponseFailedIdentifierHashes(child, {
|
|
4141
|
-
path: childPath,
|
|
4142
|
-
depth: depth + 1,
|
|
4143
|
-
identifierKey: isResponseFailedIdentifierKey(key) ? key : void 0,
|
|
4144
|
-
out,
|
|
4145
|
-
seen
|
|
4146
|
-
});
|
|
4147
|
-
}
|
|
4148
|
-
return out;
|
|
4149
|
-
}
|
|
4150
|
-
function redactResponseFailedDiagnosticValue(value, opts = {}) {
|
|
4151
|
-
const key = opts.key ?? "";
|
|
4152
|
-
const depth = opts.depth ?? 0;
|
|
4153
|
-
if (typeof value === "string" || typeof value === "number") return key && isResponseFailedIdentifierKey(key) ? redactIdentifier(String(value), { len: 12 }) : value;
|
|
4154
|
-
if (depth > 6 || !value || typeof value !== "object") return value;
|
|
4155
|
-
const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
|
|
4156
|
-
if (seen.has(value)) return "<circular>";
|
|
4157
|
-
seen.add(value);
|
|
4158
|
-
if (Array.isArray(value)) return value.slice(0, 16).map((item) => redactResponseFailedDiagnosticValue(item, {
|
|
4159
|
-
key,
|
|
4160
|
-
depth: depth + 1,
|
|
4161
|
-
seen
|
|
4162
|
-
}));
|
|
4163
|
-
const out = {};
|
|
4164
|
-
for (const [childKey, child] of Object.entries(value)) out[childKey] = redactResponseFailedDiagnosticValue(child, {
|
|
4165
|
-
key: childKey,
|
|
4166
|
-
depth: depth + 1,
|
|
4167
|
-
seen
|
|
4168
|
-
});
|
|
4169
|
-
return out;
|
|
4170
|
-
}
|
|
4171
|
-
function buildResponsesFailedFailureFields(response) {
|
|
4172
|
-
if (!response) return {};
|
|
4173
|
-
const fields = {};
|
|
4174
|
-
for (const key of RESPONSE_FAILED_FAILURE_FIELD_KEYS) if (response[key] !== void 0 && response[key] !== null) fields[key] = response[key];
|
|
4175
|
-
return fields;
|
|
4176
|
-
}
|
|
4177
|
-
function buildResponsesFailedNoDetailsObservation(event, model, response = isRecord(event.response) ? event.response : void 0) {
|
|
4178
|
-
const failureFields = redactResponseFailedDiagnosticValue(buildResponsesFailedFailureFields(response));
|
|
4179
|
-
const metadataKeys = isRecord(response?.metadata) ? Object.keys(response.metadata).toSorted() : [];
|
|
4180
|
-
const responsePreview = {
|
|
4181
|
-
id: readResponseFailedString(response, "id"),
|
|
4182
|
-
status: readResponseFailedString(response, "status"),
|
|
4183
|
-
model: readResponseFailedString(response, "model"),
|
|
4184
|
-
object: readResponseFailedString(response, "object"),
|
|
4185
|
-
failureFields,
|
|
4186
|
-
metadataKeys
|
|
4187
|
-
};
|
|
4188
|
-
return {
|
|
4189
|
-
event: "openai_responses_response_failed_without_details",
|
|
4190
|
-
provider: model.provider,
|
|
4191
|
-
api: model.api,
|
|
4192
|
-
transportModel: model.id,
|
|
4193
|
-
providerRuntimeFailureKind: "no_error_details",
|
|
4194
|
-
responseId: responsePreview.id,
|
|
4195
|
-
responseStatus: responsePreview.status,
|
|
4196
|
-
responseModel: responsePreview.model,
|
|
4197
|
-
responseObject: responsePreview.object,
|
|
4198
|
-
metadataKeys,
|
|
4199
|
-
requestIdHashes: collectResponseFailedIdentifierHashes(event),
|
|
4200
|
-
failureFieldsPreview: stringifyRedactedEvent(failureFields),
|
|
4201
|
-
responsePreview: stringifyRedactedEvent(responsePreview)
|
|
4202
|
-
};
|
|
4203
|
-
}
|
|
4204
|
-
function summarizeResponsesFailedNoDetailsObservation(observation) {
|
|
4205
|
-
const requestIds = observation.requestIdHashes.join(",");
|
|
4206
|
-
const metadataKeys = observation.metadataKeys.join(",");
|
|
4207
|
-
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}`;
|
|
4208
|
-
}
|
|
4209
|
-
function normalizeResponsesFailedEvent(event, model) {
|
|
4210
|
-
const response = isRecord(event.response) ? event.response : void 0;
|
|
4211
|
-
const responseId = readResponseFailedString(response, "id") || void 0;
|
|
4212
|
-
const error = isRecord(response?.error) ? response.error : void 0;
|
|
4213
|
-
if (error) {
|
|
4214
|
-
const code = readResponseFailedString(error, "code").trim();
|
|
4215
|
-
const message = readResponseFailedString(error, "message").trim();
|
|
4216
|
-
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId);
|
|
4217
|
-
}
|
|
4218
|
-
const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
|
|
4219
|
-
if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
|
|
4220
|
-
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
4221
|
-
}
|
|
4222
|
-
function logResponsesFailedNoDetails(observation) {
|
|
4223
|
-
log.warn(`[responses] response.failed missing error details provider=${observation.provider} api=${observation.api} model=${observation.transportModel} ` + summarizeResponsesFailedNoDetailsObservation(observation), observation);
|
|
4224
|
-
}
|
|
4225
|
-
function summarizeResponsesPayload(params) {
|
|
4226
|
-
if (!params || typeof params !== "object") return "payload=non-object";
|
|
4227
|
-
const record = params;
|
|
4228
|
-
const input = record.input;
|
|
4229
|
-
const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
|
|
4230
|
-
const text = record.text && typeof record.text === "object" ? record.text : void 0;
|
|
4231
|
-
const parts = [
|
|
4232
|
-
`fields=${Object.keys(record).toSorted().join(",")}`,
|
|
4233
|
-
`model=${safeDebugValue(record.model)}`,
|
|
4234
|
-
`stream=${safeDebugValue(record.stream)}`,
|
|
4235
|
-
`inputItems=${Array.isArray(input) ? input.length : typeof input}`,
|
|
4236
|
-
`inputRoles=${responseInputRoles(input) || "none"}`,
|
|
4237
|
-
`inputTextChars=${responseInputTextChars(input)}`,
|
|
4238
|
-
`tools=${summarizeResponsesTools(record.tools)}`,
|
|
4239
|
-
`reasoningEffort=${safeDebugValue(reasoning?.effort)}`,
|
|
4240
|
-
`reasoningSummary=${safeDebugValue(reasoning?.summary)}`,
|
|
4241
|
-
`textVerbosity=${safeDebugValue(text?.verbosity)}`,
|
|
4242
|
-
`serviceTier=${safeDebugValue(record.service_tier)}`,
|
|
4243
|
-
`store=${safeDebugValue(record.store)}`,
|
|
4244
|
-
`promptCacheKey=${record.prompt_cache_key === void 0 ? "absent" : "present"}`,
|
|
4245
|
-
`metadataKeys=${record.metadata && typeof record.metadata === "object" ? Object.keys(record.metadata).toSorted().join(",") : "none"}`
|
|
4246
|
-
];
|
|
4247
|
-
if (resolveModelPayloadDebugMode() === "full-redacted") parts.push(`payload=${stringifyRedactedPayload(record)}`);
|
|
4248
|
-
return parts.join(" ");
|
|
4249
|
-
}
|
|
4250
|
-
function summarizeOpenAITransportError(error) {
|
|
4251
|
-
if (!error || typeof error !== "object") return `type=${typeof error} message=${safeDebugValue(error)}`;
|
|
4252
|
-
const record = error;
|
|
4253
|
-
const cause = record.cause && typeof record.cause === "object" ? record.cause : void 0;
|
|
4254
|
-
return [
|
|
4255
|
-
`name=${safeDebugValue(record.name)}`,
|
|
4256
|
-
`status=${safeDebugValue(record.status)}`,
|
|
4257
|
-
`code=${safeDebugValue(record.code)}`,
|
|
4258
|
-
`type=${safeDebugValue(record.type)}`,
|
|
4259
|
-
`causeName=${safeDebugValue(cause?.name)}`,
|
|
4260
|
-
`causeCode=${safeDebugValue(cause?.code)}`,
|
|
4261
|
-
`message=${error instanceof Error ? error.message : safeDebugValue(error)}`
|
|
4262
|
-
].join(" ");
|
|
4263
|
-
}
|
|
4264
|
-
function isInvalidEncryptedContentError(error) {
|
|
4265
|
-
if (!error || typeof error !== "object") return false;
|
|
4266
|
-
const record = error;
|
|
4267
|
-
if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
|
|
4268
|
-
const message = typeof record.message === "string" ? record.message : "";
|
|
4269
|
-
return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || record.status === 400 && message.toLowerCase().includes("could not decrypt the provided encrypted_content");
|
|
4270
|
-
}
|
|
4271
|
-
function stripEncryptedContentFields(value) {
|
|
4272
|
-
if (!value || typeof value !== "object") return {
|
|
4273
|
-
value,
|
|
4274
|
-
changed: false
|
|
4275
|
-
};
|
|
4276
|
-
if (Array.isArray(value)) {
|
|
4277
|
-
let changed = false;
|
|
4278
|
-
const next = value.map((item) => {
|
|
4279
|
-
const stripped = stripEncryptedContentFields(item);
|
|
4280
|
-
changed ||= stripped.changed;
|
|
4281
|
-
return stripped.value;
|
|
4282
|
-
});
|
|
4283
|
-
return changed ? {
|
|
4284
|
-
value: next,
|
|
4285
|
-
changed: true
|
|
4286
|
-
} : {
|
|
4287
|
-
value,
|
|
4288
|
-
changed: false
|
|
4289
|
-
};
|
|
4290
|
-
}
|
|
4291
|
-
let changed = false;
|
|
4292
|
-
const next = {};
|
|
4293
|
-
for (const [key, child] of Object.entries(value)) {
|
|
4294
|
-
if (key === "encrypted_content") {
|
|
4295
|
-
changed = true;
|
|
4296
|
-
continue;
|
|
4297
|
-
}
|
|
4298
|
-
const stripped = stripEncryptedContentFields(child);
|
|
4299
|
-
changed ||= stripped.changed;
|
|
4300
|
-
next[key] = stripped.value;
|
|
4301
|
-
}
|
|
4302
|
-
return changed ? {
|
|
4303
|
-
value: next,
|
|
4304
|
-
changed: true
|
|
4305
|
-
} : {
|
|
4306
|
-
value,
|
|
4307
|
-
changed: false
|
|
4308
|
-
};
|
|
4309
|
-
}
|
|
4310
|
-
function stripResponsesRequestEncryptedContent(params) {
|
|
4311
|
-
const stripped = stripEncryptedContentFields(params.input);
|
|
4312
|
-
if (!stripped.changed) return params;
|
|
4313
|
-
return {
|
|
4314
|
-
...params,
|
|
4315
|
-
input: stripped.value
|
|
4316
|
-
};
|
|
4317
|
-
}
|
|
4318
|
-
function hashOptionalReplayContextValue(value) {
|
|
4319
|
-
const normalized = value?.trim();
|
|
4320
|
-
return normalized ? shortHash(normalized) : void 0;
|
|
4321
|
-
}
|
|
4322
|
-
function buildOpenAIResponsesReplayContext(model, options) {
|
|
4323
|
-
return {
|
|
4324
|
-
provider: model.provider,
|
|
4325
|
-
api: model.api,
|
|
4326
|
-
model: model.id,
|
|
4327
|
-
baseUrlHash: hashOptionalReplayContextValue(model.baseUrl),
|
|
4328
|
-
sessionHash: hashOptionalReplayContextValue(options?.sessionId),
|
|
4329
|
-
authProfileHash: hashOptionalReplayContextValue(options?.authProfileId)
|
|
4330
|
-
};
|
|
4331
|
-
}
|
|
4332
|
-
function buildOpenAIResponsesReasoningReplayMetadata(model, options) {
|
|
4333
|
-
return {
|
|
4334
|
-
v: 1,
|
|
4335
|
-
source: "openai-responses",
|
|
4336
|
-
...buildOpenAIResponsesReplayContext(model, options)
|
|
4337
|
-
};
|
|
4338
|
-
}
|
|
4339
|
-
function tagOpenAIResponsesReasoningReplayItem(item, model, options) {
|
|
4340
|
-
if (!("encrypted_content" in item)) return item;
|
|
4341
|
-
return {
|
|
4342
|
-
...item,
|
|
4343
|
-
[OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: buildOpenAIResponsesReasoningReplayMetadata(model, options)
|
|
4344
|
-
};
|
|
4345
|
-
}
|
|
4346
|
-
function isOpenAIResponsesReasoningReplayMetadata(value) {
|
|
4347
|
-
if (!value || typeof value !== "object") return false;
|
|
4348
|
-
const record = value;
|
|
4349
|
-
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");
|
|
4350
|
-
}
|
|
4351
|
-
function encryptedReasoningReplayMetadataMatches(metadata, context) {
|
|
4352
|
-
if (!metadata) return false;
|
|
4353
|
-
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;
|
|
4354
|
-
}
|
|
4355
|
-
function readOpenAIResponsesReasoningReplayBlockMetadata(block) {
|
|
4356
|
-
const value = block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY];
|
|
4357
|
-
return isOpenAIResponsesReasoningReplayMetadata(value) ? value : void 0;
|
|
4358
|
-
}
|
|
4359
|
-
function normalizeOpenAIResponsesReasoningReplayItem(item) {
|
|
4360
|
-
const record = item;
|
|
4361
|
-
if (record.type !== "reasoning" || Array.isArray(record.summary)) return item;
|
|
4362
|
-
return {
|
|
4363
|
-
...record,
|
|
4364
|
-
summary: []
|
|
4365
|
-
};
|
|
4366
|
-
}
|
|
4367
|
-
function prepareOpenAIResponsesReasoningItemForReplay(item, context, blockMetadata) {
|
|
4368
|
-
const { [OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: rawMetadata, ...rest } = item;
|
|
4369
|
-
if (!("encrypted_content" in rest)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
|
|
4370
|
-
if (encryptedReasoningReplayMetadataMatches(blockMetadata ?? (isOpenAIResponsesReasoningReplayMetadata(rawMetadata) ? rawMetadata : void 0), context)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
|
|
4371
|
-
return normalizeOpenAIResponsesReasoningReplayItem(stripEncryptedContentFields(rest).value);
|
|
4372
|
-
}
|
|
4373
|
-
async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
4374
|
-
try {
|
|
4375
|
-
return await params.client.responses.create(params.request, params.requestOptions);
|
|
4376
|
-
} catch (error) {
|
|
4377
|
-
const retryRequest = stripResponsesRequestEncryptedContent(params.request);
|
|
4378
|
-
if (!isInvalidEncryptedContentError(error) || retryRequest === params.request) throw error;
|
|
4379
|
-
log.warn(`[responses] retrying without encrypted reasoning content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
4380
|
-
return await params.client.responses.create(retryRequest, params.requestOptions);
|
|
4381
|
-
}
|
|
4382
|
-
}
|
|
4383
|
-
function resolveAzureOpenAIApiVersion(env = process.env) {
|
|
4384
|
-
return env.AZURE_OPENAI_API_VERSION?.trim() || DEFAULT_AZURE_OPENAI_API_VERSION;
|
|
4385
|
-
}
|
|
4386
|
-
function shortHash(value) {
|
|
4387
|
-
return sha256HexPrefix(value, 16);
|
|
4388
|
-
}
|
|
4389
|
-
function normalizeResponsesReplayItemId(id, prefix) {
|
|
4390
|
-
if (!id) return;
|
|
4391
|
-
if (id.length <= OPENAI_RESPONSES_REPLAY_ITEM_ID_MAX_LENGTH) return id;
|
|
4392
|
-
return `${prefix}_${shortHash(id)}`;
|
|
4393
|
-
}
|
|
4394
|
-
function isSafeResponsesReplayItemId(id) {
|
|
4395
|
-
return typeof id === "string" && id.length > 0 && id.length <= OPENAI_RESPONSES_REPLAY_ITEM_ID_MAX_LENGTH;
|
|
4396
|
-
}
|
|
4397
|
-
function encodeTextSignatureV1(id, phase) {
|
|
4398
|
-
return JSON.stringify({
|
|
4399
|
-
v: 1,
|
|
4400
|
-
id,
|
|
4401
|
-
...phase ? { phase } : {}
|
|
4402
|
-
});
|
|
4403
|
-
}
|
|
4404
|
-
function parseTextSignature(signature) {
|
|
4405
|
-
if (!signature) return;
|
|
4406
|
-
if (signature.startsWith("{")) try {
|
|
4407
|
-
const parsed = JSON.parse(signature);
|
|
4408
|
-
if (parsed.v === 1) {
|
|
4409
|
-
const id = typeof parsed.id === "string" ? parsed.id : void 0;
|
|
4410
|
-
const phase = parsed.phase === "commentary" || parsed.phase === "final_answer" ? parsed.phase : void 0;
|
|
4411
|
-
if (id !== void 0 || phase !== void 0) return {
|
|
4412
|
-
id,
|
|
4413
|
-
phase
|
|
4414
|
-
};
|
|
4415
|
-
return;
|
|
4416
|
-
}
|
|
4417
|
-
} catch {}
|
|
4418
|
-
return { id: signature };
|
|
4419
|
-
}
|
|
4420
|
-
function buildResponsesInputMessage(role, content) {
|
|
4421
|
-
return {
|
|
4422
|
-
type: "message",
|
|
4423
|
-
role,
|
|
4424
|
-
content
|
|
4425
|
-
};
|
|
4426
|
-
}
|
|
4427
|
-
function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
4428
|
-
const messages = [];
|
|
4429
|
-
const shouldReplayReasoningItems = options?.replayReasoningItems ?? true;
|
|
4430
|
-
const shouldReplayResponsesItemIds = options?.replayResponsesItemIds ?? true;
|
|
4431
|
-
const replayContext = buildOpenAIResponsesReplayContext(model, {
|
|
4432
|
-
sessionId: options?.sessionId,
|
|
4433
|
-
authProfileId: options?.authProfileId
|
|
4434
|
-
});
|
|
4435
|
-
const shouldNormalizeSameModelToolCallIds = model.provider === "github-copilot";
|
|
4436
|
-
const sanitizeIdPart = (part) => part.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+$/, "");
|
|
4437
|
-
const normalizeIdPart = (part) => {
|
|
4438
|
-
const sanitized = sanitizeIdPart(part);
|
|
4439
|
-
return (sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized).replace(/_+$/, "");
|
|
4440
|
-
};
|
|
4441
|
-
const buildForeignResponsesItemId = (itemId) => {
|
|
4442
|
-
const normalized = `fc_${shortHash(itemId)}`;
|
|
4443
|
-
return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
|
|
4444
|
-
};
|
|
4445
|
-
const buildSameProviderCopilotResponsesItemId = (itemId) => {
|
|
4446
|
-
const sanitized = sanitizeIdPart(itemId);
|
|
4447
|
-
const candidate = sanitized.startsWith("fc_") ? sanitized : `fc_${sanitized}`;
|
|
4448
|
-
return candidate.length > 64 ? buildForeignResponsesItemId(itemId) : candidate;
|
|
4449
|
-
};
|
|
4450
|
-
const normalizeToolCallId = (id, _targetModel, source) => {
|
|
4451
|
-
if (!allowedToolCallProviders.has(model.provider)) return normalizeIdPart(id);
|
|
4452
|
-
if (!id.includes("|")) return normalizeIdPart(id);
|
|
4453
|
-
const separatorIndex = id.indexOf("|");
|
|
4454
|
-
const callId = id.slice(0, separatorIndex);
|
|
4455
|
-
const itemId = id.slice(separatorIndex + 1);
|
|
4456
|
-
const normalizedCallId = normalizeIdPart(callId);
|
|
4457
|
-
let normalizedItemId = source.provider !== model.provider || source.api !== model.api ? buildForeignResponsesItemId(itemId) : model.provider === "github-copilot" ? buildSameProviderCopilotResponsesItemId(itemId) : normalizeIdPart(itemId);
|
|
4458
|
-
if (!normalizedItemId.startsWith("fc_")) normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
|
|
4459
|
-
return `${normalizedCallId}|${normalizedItemId}`;
|
|
4460
|
-
};
|
|
4461
|
-
const transformedMessages = transformTransportMessages(context.messages, model, normalizeToolCallId, { normalizeSameModelToolCallIds: shouldNormalizeSameModelToolCallIds });
|
|
4462
|
-
if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && options?.supportsDeveloperRole !== false ? "developer" : "system", [{
|
|
4463
|
-
type: "input_text",
|
|
4464
|
-
text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
|
|
4465
|
-
}]));
|
|
4466
|
-
let msgIndex = 0;
|
|
4467
|
-
for (const msg of transformedMessages) {
|
|
4468
|
-
if (msg.role === "user") if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
|
|
4469
|
-
type: "input_text",
|
|
4470
|
-
text: sanitizeTransportPayloadText(msg.content)
|
|
4471
|
-
}]));
|
|
4472
|
-
else {
|
|
4473
|
-
const content = msg.content.map((item) => item.type === "text" ? {
|
|
4474
|
-
type: "input_text",
|
|
4475
|
-
text: sanitizeTransportPayloadText(item.text)
|
|
4476
|
-
} : {
|
|
4477
|
-
type: "input_image",
|
|
4478
|
-
detail: "auto",
|
|
4479
|
-
image_url: `data:${item.mimeType};base64,${item.data}`
|
|
4480
|
-
}).filter((item) => model.input.includes("image") || item.type !== "input_image");
|
|
4481
|
-
if (content.length > 0) messages.push(buildResponsesInputMessage("user", content));
|
|
4482
|
-
}
|
|
4483
|
-
else if (msg.role === "assistant") {
|
|
4484
|
-
const output = [];
|
|
4485
|
-
let textFallbackOrdinal = 0;
|
|
4486
|
-
let previousReplayItemWasReasoning = false;
|
|
4487
|
-
const isDifferentModel = msg.model !== model.id && msg.provider === model.provider && msg.api === model.api;
|
|
4488
|
-
for (const block of msg.content) if (block.type === "thinking") {
|
|
4489
|
-
if (shouldReplayReasoningItems && block.thinkingSignature && block.thinkingSignature.startsWith("{")) {
|
|
4490
|
-
const replayableReasoningItem = prepareOpenAIResponsesReasoningItemForReplay(JSON.parse(block.thinkingSignature), replayContext, readOpenAIResponsesReasoningReplayBlockMetadata(block));
|
|
4491
|
-
if (!shouldReplayResponsesItemIds) delete replayableReasoningItem.id;
|
|
4492
|
-
if (shouldReplayResponsesItemIds && model.provider === "github-copilot" && !isSafeResponsesReplayItemId(replayableReasoningItem.id)) continue;
|
|
4493
|
-
output.push(replayableReasoningItem);
|
|
4494
|
-
previousReplayItemWasReasoning = true;
|
|
4495
|
-
}
|
|
4496
|
-
} else if (block.type === "text") {
|
|
4497
|
-
const textSignature = parseTextSignature(block.textSignature);
|
|
4498
|
-
let msgId = resolveReplayableResponsesMessageId({
|
|
4499
|
-
replayResponsesItemIds: shouldReplayResponsesItemIds,
|
|
4500
|
-
textSignatureId: textSignature?.id,
|
|
4501
|
-
fallbackId: `msg_${msgIndex}`,
|
|
4502
|
-
fallbackOrdinal: textFallbackOrdinal,
|
|
4503
|
-
previousReplayItemWasReasoning
|
|
4504
|
-
});
|
|
4505
|
-
if (!textSignature?.id) textFallbackOrdinal += 1;
|
|
4506
|
-
msgId = normalizeResponsesReplayItemId(msgId, "msg");
|
|
4507
|
-
const messageItem = {
|
|
4508
|
-
type: "message",
|
|
4509
|
-
role: "assistant",
|
|
4510
|
-
content: [{
|
|
4511
|
-
type: "output_text",
|
|
4512
|
-
text: sanitizeTransportPayloadText(block.text),
|
|
4513
|
-
annotations: []
|
|
4514
|
-
}],
|
|
4515
|
-
status: "completed",
|
|
4516
|
-
...msgId ? { id: msgId } : {},
|
|
4517
|
-
phase: textSignature?.phase
|
|
4518
|
-
};
|
|
4519
|
-
output.push(messageItem);
|
|
4520
|
-
previousReplayItemWasReasoning = false;
|
|
4521
|
-
} else if (block.type === "toolCall") {
|
|
4522
|
-
const separatorIndex = block.id.indexOf("|");
|
|
4523
|
-
const callId = separatorIndex === -1 ? block.id : block.id.slice(0, separatorIndex);
|
|
4524
|
-
const itemIdRaw = separatorIndex === -1 ? void 0 : block.id.slice(separatorIndex + 1);
|
|
4525
|
-
const itemId = shouldReplayResponsesItemIds && !(isDifferentModel && itemIdRaw?.startsWith("fc_")) ? itemIdRaw : void 0;
|
|
4526
|
-
output.push({
|
|
4527
|
-
type: "function_call",
|
|
4528
|
-
...itemId ? { id: itemId } : {},
|
|
4529
|
-
call_id: callId,
|
|
4530
|
-
name: block.name,
|
|
4531
|
-
arguments: typeof block.arguments === "string" ? block.arguments : JSON.stringify(block.arguments ?? {})
|
|
4532
|
-
});
|
|
4533
|
-
previousReplayItemWasReasoning = false;
|
|
4534
|
-
}
|
|
4535
|
-
if (output.length > 0) messages.push(...output);
|
|
4536
|
-
} else if (msg.role === "toolResult") {
|
|
4537
|
-
const textResult = extractToolResultText(msg.content);
|
|
4538
|
-
const sanitizedTextResult = sanitizeTransportPayloadText(textResult);
|
|
4539
|
-
const hasText = sanitizedTextResult.trim().length > 0;
|
|
4540
|
-
const mediaPlaceholder = describeToolResultMediaPlaceholder(msg.content);
|
|
4541
|
-
const hasImages = msg.content.some(isImageWithMediaPayload);
|
|
4542
|
-
const separatorIndex = msg.toolCallId.indexOf("|");
|
|
4543
|
-
const callId = separatorIndex === -1 ? msg.toolCallId : msg.toolCallId.slice(0, separatorIndex);
|
|
4544
|
-
messages.push({
|
|
4545
|
-
type: "function_call_output",
|
|
4546
|
-
call_id: callId,
|
|
4547
|
-
output: hasImages && model.input.includes("image") ? [...hasText ? [{
|
|
4548
|
-
type: "input_text",
|
|
4549
|
-
text: sanitizedTextResult
|
|
4550
|
-
}] : mediaPlaceholder === "(see attached media)" ? [{
|
|
4551
|
-
type: "input_text",
|
|
4552
|
-
text: mediaPlaceholder
|
|
4553
|
-
}] : [], ...msg.content.filter(isImageWithMediaPayload).map((item) => ({
|
|
4554
|
-
type: "input_image",
|
|
4555
|
-
detail: "auto",
|
|
4556
|
-
image_url: `data:${item.mimeType};base64,${item.data}`
|
|
4557
|
-
}))] : sanitizeNonEmptyTransportPayloadText(textResult, mediaPlaceholder ?? "(no output)")
|
|
4558
|
-
});
|
|
4559
|
-
}
|
|
4560
|
-
msgIndex += 1;
|
|
4561
|
-
}
|
|
4562
|
-
return messages;
|
|
4563
|
-
}
|
|
4564
|
-
function convertResponsesTools(tools, model, options) {
|
|
4565
|
-
const projection = projectOpenAITools(tools);
|
|
4566
|
-
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
|
|
4567
|
-
transport: "responses",
|
|
4568
|
-
model
|
|
4569
|
-
});
|
|
4570
|
-
return {
|
|
4571
|
-
projection,
|
|
4572
|
-
tools: sortTransportToolsByName(projection.tools).map((tool) => {
|
|
4573
|
-
const result = {
|
|
4574
|
-
type: "function",
|
|
4575
|
-
name: tool.name,
|
|
4576
|
-
description: tool.description,
|
|
4577
|
-
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
4578
|
-
};
|
|
4579
|
-
if (strict !== void 0) result.strict = strict;
|
|
4580
|
-
return result;
|
|
4581
|
-
})
|
|
4582
|
-
};
|
|
4583
|
-
}
|
|
4584
|
-
async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
4585
|
-
const resolveToolCallId = (item, fallbackId) => {
|
|
4586
|
-
const callId = stringifyUnknown(item.call_id).trim();
|
|
4587
|
-
const itemId = stringifyUnknown(item.id).trim();
|
|
4588
|
-
const [fallbackCallId = "", fallbackItemId = ""] = (fallbackId ?? "").split("|");
|
|
4589
|
-
const resolvedCallId = callId || fallbackCallId;
|
|
4590
|
-
const resolvedItemId = itemId || fallbackItemId;
|
|
4591
|
-
if (resolvedCallId) return resolvedItemId ? `${resolvedCallId}|${resolvedItemId}` : resolvedCallId;
|
|
4592
|
-
const generatedCallId = `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`;
|
|
4593
|
-
return resolvedItemId ? `${generatedCallId}|${resolvedItemId}` : generatedCallId;
|
|
4594
|
-
};
|
|
4595
|
-
let currentItem = null;
|
|
4596
|
-
let currentBlock = null;
|
|
4597
|
-
const streamingToolCalls = createResponsesToolCallTracker();
|
|
4598
|
-
let lastTextBlock = null;
|
|
4599
|
-
let pendingMessageText = null;
|
|
4600
|
-
const streamStartedAt = Date.now();
|
|
4601
|
-
let eventCount = 0;
|
|
4602
|
-
const eventTypes = /* @__PURE__ */ new Map();
|
|
4603
|
-
const sseDebugMode = resolveModelSseDebugMode();
|
|
4604
|
-
const blockIndex = () => output.content.length - 1;
|
|
4605
|
-
const readIdentityValue = (value) => {
|
|
4606
|
-
return (typeof value === "string" ? value.trim() : "") || void 0;
|
|
4607
|
-
};
|
|
4608
|
-
const resolveCompletedToolCallName = (toolCall, value) => {
|
|
4609
|
-
const streamedName = readIdentityValue(toolCall?.block.name);
|
|
4610
|
-
const completedName = readIdentityValue(value);
|
|
4611
|
-
if (streamedName && completedName && streamedName !== completedName) throw new Error(`Responses stream changed tool-call function name from ${streamedName} to ${completedName}`);
|
|
4612
|
-
const name = completedName ?? streamedName;
|
|
4613
|
-
if (!name) throw new Error("Responses stream completed tool call without a function name");
|
|
4614
|
-
return name;
|
|
4615
|
-
};
|
|
4616
|
-
const appendPendingMessageDelta = (delta) => {
|
|
4617
|
-
pendingMessageText = `${pendingMessageText ?? ""}${delta}`;
|
|
4618
|
-
const priorText = stringifyUnknown(lastTextBlock?.block.text);
|
|
4619
|
-
if (priorText.startsWith(pendingMessageText) || pendingMessageText.startsWith(priorText)) return;
|
|
4620
|
-
const phase = currentItem?.type === "message" ? currentItem.phase ?? void 0 : void 0;
|
|
4621
|
-
currentBlock = {
|
|
4622
|
-
type: "text",
|
|
4623
|
-
text: pendingMessageText,
|
|
4624
|
-
...currentItem?.type === "message" && phase ? { textSignature: encodeTextSignatureV1(stringifyUnknown(currentItem.id), phase) } : {}
|
|
4625
|
-
};
|
|
4626
|
-
output.content.push(currentBlock);
|
|
4627
|
-
stream.push({
|
|
4628
|
-
type: "text_start",
|
|
4629
|
-
contentIndex: blockIndex(),
|
|
4630
|
-
partial: output
|
|
4631
|
-
});
|
|
4632
|
-
stream.push({
|
|
4633
|
-
type: "text_delta",
|
|
4634
|
-
contentIndex: blockIndex(),
|
|
4635
|
-
delta: pendingMessageText
|
|
4636
|
-
});
|
|
4637
|
-
pendingMessageText = null;
|
|
4638
|
-
};
|
|
4639
|
-
const appendTerminalResponseTextItem = (item) => {
|
|
4640
|
-
const text = readResponsesOutputMessageText(item);
|
|
4641
|
-
if (!text) return;
|
|
4642
|
-
const phase = item.phase ?? void 0;
|
|
4643
|
-
const collapse = resolveResponsesMessageSnapshotCollapse({
|
|
4644
|
-
prior: lastTextBlock && {
|
|
4645
|
-
text: stringifyUnknown(lastTextBlock.block.text),
|
|
4646
|
-
phase: lastTextBlock.phase
|
|
4647
|
-
},
|
|
4648
|
-
nextText: text,
|
|
4649
|
-
nextPhase: phase
|
|
4650
|
-
});
|
|
4651
|
-
if (collapse.kind === "extend" && lastTextBlock) {
|
|
4652
|
-
lastTextBlock.block.text = collapse.text;
|
|
4653
|
-
lastTextBlock.block.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), phase);
|
|
4654
|
-
stream.push({
|
|
4655
|
-
type: "text_end",
|
|
4656
|
-
contentIndex: lastTextBlock.index,
|
|
4657
|
-
content: collapse.text,
|
|
4658
|
-
partial: output
|
|
4659
|
-
});
|
|
4660
|
-
return;
|
|
4661
|
-
}
|
|
4662
|
-
const block = {
|
|
4663
|
-
type: "text",
|
|
4664
|
-
text,
|
|
4665
|
-
textSignature: encodeTextSignatureV1(stringifyUnknown(item.id), phase)
|
|
4666
|
-
};
|
|
4667
|
-
output.content.push(block);
|
|
4668
|
-
lastTextBlock = {
|
|
4669
|
-
block,
|
|
4670
|
-
index: blockIndex(),
|
|
4671
|
-
phase
|
|
4672
|
-
};
|
|
4673
|
-
stream.push({
|
|
4674
|
-
type: "text_start",
|
|
4675
|
-
contentIndex: blockIndex(),
|
|
4676
|
-
partial: output
|
|
4677
|
-
});
|
|
4678
|
-
stream.push({
|
|
4679
|
-
type: "text_end",
|
|
4680
|
-
contentIndex: blockIndex(),
|
|
4681
|
-
content: text,
|
|
4682
|
-
partial: output
|
|
4683
|
-
});
|
|
4684
|
-
};
|
|
4685
|
-
const appendCompletedResponseToolCallItem = (item) => {
|
|
4686
|
-
const args = parseStreamingJson(stringifyJsonLike(item.arguments, "{}"));
|
|
4687
|
-
const name = resolveCompletedToolCallName(void 0, item.name);
|
|
4688
|
-
const block = {
|
|
4689
|
-
type: "toolCall",
|
|
4690
|
-
id: resolveToolCallId(item),
|
|
4691
|
-
name,
|
|
4692
|
-
arguments: args,
|
|
4693
|
-
partialJson: stringifyJsonLike(item.arguments, "{}")
|
|
4694
|
-
};
|
|
4695
|
-
output.content.push(block);
|
|
4696
|
-
stream.push({
|
|
4697
|
-
type: "toolcall_start",
|
|
4698
|
-
contentIndex: blockIndex(),
|
|
4699
|
-
partial: output
|
|
4700
|
-
});
|
|
4701
|
-
stream.push({
|
|
4702
|
-
type: "toolcall_end",
|
|
4703
|
-
contentIndex: blockIndex(),
|
|
4704
|
-
toolCall: {
|
|
4705
|
-
type: "toolCall",
|
|
4706
|
-
id: block.id,
|
|
4707
|
-
name: block.name,
|
|
4708
|
-
arguments: args
|
|
4709
|
-
},
|
|
4710
|
-
partial: output
|
|
4711
|
-
});
|
|
4712
|
-
};
|
|
4713
|
-
const backfillTerminalResponseOutput = (response, backfillOptions) => {
|
|
4714
|
-
if (output.content.length > 0 || !Array.isArray(response?.output)) return;
|
|
4715
|
-
for (const rawItem of response.output) {
|
|
4716
|
-
if (!isRecord(rawItem)) continue;
|
|
4717
|
-
if (rawItem.type === "message") {
|
|
4718
|
-
appendTerminalResponseTextItem(rawItem);
|
|
4719
|
-
continue;
|
|
4720
|
-
}
|
|
4721
|
-
lastTextBlock = null;
|
|
4722
|
-
if (backfillOptions.includeToolCalls && rawItem.type === "function_call") appendCompletedResponseToolCallItem(rawItem);
|
|
4723
|
-
}
|
|
4724
|
-
};
|
|
4725
|
-
const guardedStream = withFirstStreamEventTimeout(openaiStream, {
|
|
4726
|
-
provider: model.provider,
|
|
4727
|
-
api: model.api,
|
|
4728
|
-
model: model.id,
|
|
4729
|
-
timeoutMs: options?.firstEventTimeoutMs ?? 0,
|
|
4730
|
-
stage: "responses",
|
|
4731
|
-
abort: options?.abortFirstEventStream,
|
|
4732
|
-
onTimeout: options?.onFirstEventTimeout,
|
|
4733
|
-
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."
|
|
4734
|
-
});
|
|
4735
|
-
const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
|
|
4736
|
-
for await (const rawEvent of guardedStream) {
|
|
4737
|
-
throwIfModelStreamAborted(options?.signal);
|
|
4738
|
-
const event = rawEvent;
|
|
4739
|
-
const type = stringifyUnknown(event.type);
|
|
4740
|
-
eventCount += 1;
|
|
4741
|
-
eventTypes.set(type, (eventTypes.get(type) ?? 0) + 1);
|
|
4742
|
-
if (eventCount === 1) emitModelTransportDebug(log, `[responses] first_event provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - streamStartedAt} type=${type}`);
|
|
4743
|
-
if (sseDebugMode === "peek" && eventCount <= 5) emitModelTransportDebug(log, `[responses] event_peek provider=${model.provider} api=${model.api} model=${model.id} index=${eventCount} type=${type} event=${stringifyRedactedEvent(event)}`);
|
|
4744
|
-
if (type === "response.created") output.responseId = stringifyUnknown(event.response?.id);
|
|
4745
|
-
else if (type === "response.output_item.added") {
|
|
4746
|
-
const item = event.item;
|
|
4747
|
-
if (item.type !== "message") {
|
|
4748
|
-
lastTextBlock = null;
|
|
4749
|
-
pendingMessageText = null;
|
|
4750
|
-
}
|
|
4751
|
-
if (item.type === "reasoning") {
|
|
4752
|
-
currentItem = item;
|
|
4753
|
-
currentBlock = {
|
|
4754
|
-
type: "thinking",
|
|
4755
|
-
thinking: ""
|
|
4756
|
-
};
|
|
4757
|
-
output.content.push(currentBlock);
|
|
4758
|
-
stream.push({
|
|
4759
|
-
type: "thinking_start",
|
|
4760
|
-
contentIndex: blockIndex(),
|
|
4761
|
-
partial: output
|
|
4762
|
-
});
|
|
4763
|
-
} else if (item.type === "message") {
|
|
4764
|
-
currentItem = item;
|
|
4765
|
-
if (lastTextBlock) {
|
|
4766
|
-
currentBlock = null;
|
|
4767
|
-
pendingMessageText = "";
|
|
4768
|
-
} else {
|
|
4769
|
-
const phase = item.phase ?? void 0;
|
|
4770
|
-
currentBlock = {
|
|
4771
|
-
type: "text",
|
|
4772
|
-
text: "",
|
|
4773
|
-
...phase ? { textSignature: encodeTextSignatureV1(stringifyUnknown(item.id), phase) } : {}
|
|
4774
|
-
};
|
|
4775
|
-
output.content.push(currentBlock);
|
|
4776
|
-
stream.push({
|
|
4777
|
-
type: "text_start",
|
|
4778
|
-
contentIndex: blockIndex(),
|
|
4779
|
-
partial: output
|
|
4780
|
-
});
|
|
4781
|
-
}
|
|
4782
|
-
} else if (item.type === "function_call") {
|
|
4783
|
-
const toolCallBlock = {
|
|
4784
|
-
type: "toolCall",
|
|
4785
|
-
id: resolveToolCallId(item),
|
|
4786
|
-
name: readIdentityValue(item.name) ?? "",
|
|
4787
|
-
arguments: {},
|
|
4788
|
-
partialJson: stringifyJsonLike(item.arguments)
|
|
4789
|
-
};
|
|
4790
|
-
const contentIndex = output.content.length;
|
|
4791
|
-
const toolCallState = {
|
|
4792
|
-
block: toolCallBlock,
|
|
4793
|
-
contentIndex,
|
|
4794
|
-
argumentStreamReliable: true,
|
|
4795
|
-
...readResponsesToolCallItemIdentity(item)
|
|
4796
|
-
};
|
|
4797
|
-
streamingToolCalls.register(event, toolCallState);
|
|
4798
|
-
currentItem = item;
|
|
4799
|
-
currentBlock = toolCallBlock;
|
|
4800
|
-
output.content.push(toolCallBlock);
|
|
4801
|
-
stream.push({
|
|
4802
|
-
type: "toolcall_start",
|
|
4803
|
-
contentIndex,
|
|
4804
|
-
partial: output
|
|
4805
|
-
});
|
|
4806
|
-
}
|
|
4807
|
-
} else if (type === "response.reasoning_summary_text.delta") {
|
|
4808
|
-
if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
|
|
4809
|
-
currentBlock.thinking = `${stringifyUnknown(currentBlock.thinking)}${stringifyUnknown(event.delta)}`;
|
|
4810
|
-
stream.push({
|
|
4811
|
-
type: "thinking_delta",
|
|
4812
|
-
contentIndex: blockIndex(),
|
|
4813
|
-
delta: stringifyUnknown(event.delta),
|
|
4814
|
-
partial: output
|
|
4815
|
-
});
|
|
4816
|
-
}
|
|
4817
|
-
} else if (isResponsesTextDeltaEventType(type) || type === "response.refusal.delta") {
|
|
4818
|
-
if (currentItem?.type === "message") {
|
|
4819
|
-
if (pendingMessageText !== null) appendPendingMessageDelta(stringifyUnknown(event.delta));
|
|
4820
|
-
else if (currentBlock?.type === "text") {
|
|
4821
|
-
currentBlock.text = `${stringifyUnknown(currentBlock.text)}${stringifyUnknown(event.delta)}`;
|
|
4822
|
-
stream.push({
|
|
4823
|
-
type: "text_delta",
|
|
4824
|
-
contentIndex: blockIndex(),
|
|
4825
|
-
delta: stringifyUnknown(event.delta)
|
|
4826
|
-
});
|
|
4827
|
-
}
|
|
4828
|
-
}
|
|
4829
|
-
} else if (type === "response.function_call_arguments.delta") {
|
|
4830
|
-
const toolCall = streamingToolCalls.resolve(event);
|
|
4831
|
-
if (toolCall) {
|
|
4832
|
-
toolCall.block.partialJson = `${stringifyJsonLike(toolCall.block.partialJson)}${stringifyJsonLike(event.delta)}`;
|
|
4833
|
-
toolCall.block.arguments = parseStreamingJson(stringifyJsonLike(toolCall.block.partialJson));
|
|
4834
|
-
stream.push({
|
|
4835
|
-
type: "toolcall_delta",
|
|
4836
|
-
contentIndex: toolCall.contentIndex,
|
|
4837
|
-
delta: stringifyJsonLike(event.delta),
|
|
4838
|
-
partial: output
|
|
4839
|
-
});
|
|
4840
|
-
} else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
|
|
4841
|
-
} else if (type === "response.function_call_arguments.done") {
|
|
4842
|
-
const toolCall = streamingToolCalls.resolve(event);
|
|
4843
|
-
if (toolCall) {
|
|
4844
|
-
const previousPartialJson = stringifyJsonLike(toolCall.block.partialJson);
|
|
4845
|
-
const doneArguments = typeof event.arguments === "string" ? event.arguments : void 0;
|
|
4846
|
-
if (doneArguments !== void 0 && (doneArguments.length > 0 || previousPartialJson === "")) {
|
|
4847
|
-
toolCall.block.partialJson = doneArguments;
|
|
4848
|
-
toolCall.block.arguments = parseStreamingJson(doneArguments);
|
|
4849
|
-
toolCall.argumentStreamReliable = true;
|
|
4850
|
-
}
|
|
4851
|
-
if (doneArguments?.startsWith(previousPartialJson)) {
|
|
4852
|
-
const delta = doneArguments.slice(previousPartialJson.length);
|
|
4853
|
-
if (delta.length > 0) stream.push({
|
|
4854
|
-
type: "toolcall_delta",
|
|
4855
|
-
contentIndex: toolCall.contentIndex,
|
|
4856
|
-
delta,
|
|
4857
|
-
partial: output
|
|
4858
|
-
});
|
|
4859
|
-
}
|
|
4860
|
-
} else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
|
|
4861
|
-
} else if (type === "response.output_item.done") {
|
|
4862
|
-
const item = event.item;
|
|
4863
|
-
if (item.type !== "message") {
|
|
4864
|
-
lastTextBlock = null;
|
|
4865
|
-
pendingMessageText = null;
|
|
4866
|
-
}
|
|
4867
|
-
if (item.type === "reasoning" && currentBlock?.type === "thinking") {
|
|
4868
|
-
const summary = Array.isArray(item.summary) ? item.summary.map((part) => {
|
|
4869
|
-
return part.text ?? "";
|
|
4870
|
-
}).join("\n\n") : "";
|
|
4871
|
-
currentBlock.thinking = summary;
|
|
4872
|
-
currentBlock.thinkingSignature = JSON.stringify(item);
|
|
4873
|
-
if ("encrypted_content" in item) currentBlock[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
4874
|
-
authProfileId: options?.authProfileId,
|
|
4875
|
-
sessionId: options?.sessionId
|
|
4876
|
-
});
|
|
4877
|
-
stream.push({
|
|
4878
|
-
type: "thinking_end",
|
|
4879
|
-
contentIndex: blockIndex(),
|
|
4880
|
-
content: stringifyUnknown(currentBlock.thinking),
|
|
4881
|
-
partial: output
|
|
4882
|
-
});
|
|
4883
|
-
currentBlock = null;
|
|
4884
|
-
} else if (item.type === "message" && (currentBlock?.type === "text" || pendingMessageText !== null)) {
|
|
4885
|
-
const finalText = (Array.isArray(item.content) ? item.content : []).map((part) => {
|
|
4886
|
-
const contentPart = part;
|
|
4887
|
-
return isResponsesTextContentPartType(contentPart.type) ? contentPart.text ?? "" : contentPart.refusal ?? "";
|
|
4888
|
-
}).join("");
|
|
4889
|
-
const phase = item.phase ?? void 0;
|
|
4890
|
-
const collapse = pendingMessageText !== null ? resolveResponsesMessageSnapshotCollapse({
|
|
4891
|
-
prior: lastTextBlock && {
|
|
4892
|
-
text: stringifyUnknown(lastTextBlock.block.text),
|
|
4893
|
-
phase: lastTextBlock.phase
|
|
4894
|
-
},
|
|
4895
|
-
nextText: finalText,
|
|
4896
|
-
nextPhase: phase
|
|
4897
|
-
}) : { kind: "keep" };
|
|
4898
|
-
pendingMessageText = null;
|
|
4899
|
-
if (collapse.kind === "extend" && lastTextBlock) {
|
|
4900
|
-
lastTextBlock.block.text = collapse.text;
|
|
4901
|
-
lastTextBlock.block.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), phase);
|
|
4902
|
-
stream.push({
|
|
4903
|
-
type: "text_end",
|
|
4904
|
-
contentIndex: lastTextBlock.index,
|
|
4905
|
-
content: collapse.text,
|
|
4906
|
-
partial: output
|
|
4907
|
-
});
|
|
4908
|
-
} else {
|
|
4909
|
-
if (currentBlock?.type !== "text") {
|
|
4910
|
-
currentBlock = {
|
|
4911
|
-
type: "text",
|
|
4912
|
-
text: "",
|
|
4913
|
-
...phase ? { textSignature: encodeTextSignatureV1(stringifyUnknown(item.id), phase) } : {}
|
|
4914
|
-
};
|
|
4915
|
-
output.content.push(currentBlock);
|
|
4916
|
-
stream.push({
|
|
4917
|
-
type: "text_start",
|
|
4918
|
-
contentIndex: blockIndex(),
|
|
4919
|
-
partial: output
|
|
4920
|
-
});
|
|
4921
|
-
}
|
|
4922
|
-
currentBlock.text = finalText;
|
|
4923
|
-
currentBlock.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), phase);
|
|
4924
|
-
lastTextBlock = {
|
|
4925
|
-
block: currentBlock,
|
|
4926
|
-
index: blockIndex(),
|
|
4927
|
-
phase
|
|
4928
|
-
};
|
|
4929
|
-
stream.push({
|
|
4930
|
-
type: "text_end",
|
|
4931
|
-
contentIndex: blockIndex(),
|
|
4932
|
-
content: stringifyUnknown(currentBlock.text),
|
|
4933
|
-
partial: output
|
|
4934
|
-
});
|
|
4935
|
-
}
|
|
4936
|
-
currentBlock = null;
|
|
4937
|
-
} else if (item.type === "function_call") {
|
|
4938
|
-
const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
|
|
4939
|
-
if (!streamingToolCall && streamingToolCalls.hasActive()) {
|
|
4940
|
-
await cooperativeScheduler.afterEvent();
|
|
4941
|
-
continue;
|
|
4942
|
-
}
|
|
4943
|
-
const completedName = resolveCompletedToolCallName(streamingToolCall, item.name);
|
|
4944
|
-
const streamedPartialJson = streamingToolCall ? stringifyJsonLike(streamingToolCall.block.partialJson) : "";
|
|
4945
|
-
const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
|
|
4946
|
-
if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) {
|
|
4947
|
-
await cooperativeScheduler.afterEvent();
|
|
4948
|
-
continue;
|
|
4949
|
-
}
|
|
4950
|
-
const finalPartialJson = completedArguments !== void 0 && (completedArguments.length > 0 || !streamedPartialJson) ? completedArguments : streamedPartialJson || "{}";
|
|
4951
|
-
const args = parseStreamingJson(finalPartialJson);
|
|
4952
|
-
let toolCallBlock;
|
|
4953
|
-
let contentIndex;
|
|
4954
|
-
if (streamingToolCall) {
|
|
4955
|
-
toolCallBlock = streamingToolCall.block;
|
|
4956
|
-
contentIndex = streamingToolCall.contentIndex;
|
|
4957
|
-
} else {
|
|
4958
|
-
toolCallBlock = {
|
|
4959
|
-
type: "toolCall",
|
|
4960
|
-
id: resolveToolCallId(item),
|
|
4961
|
-
name: completedName,
|
|
4962
|
-
arguments: args,
|
|
4963
|
-
partialJson: finalPartialJson
|
|
4964
|
-
};
|
|
4965
|
-
output.content.push(toolCallBlock);
|
|
4966
|
-
contentIndex = blockIndex();
|
|
4967
|
-
stream.push({
|
|
4968
|
-
type: "toolcall_start",
|
|
4969
|
-
contentIndex,
|
|
4970
|
-
partial: output
|
|
4971
|
-
});
|
|
4972
|
-
}
|
|
4973
|
-
const currentToolCallId = resolveToolCallId(item, typeof toolCallBlock.id === "string" ? toolCallBlock.id : void 0);
|
|
4974
|
-
toolCallBlock.id = currentToolCallId;
|
|
4975
|
-
toolCallBlock.name = completedName;
|
|
4976
|
-
toolCallBlock.arguments = args;
|
|
4977
|
-
toolCallBlock.partialJson = finalPartialJson;
|
|
4978
|
-
stream.push({
|
|
4979
|
-
type: "toolcall_end",
|
|
4980
|
-
contentIndex,
|
|
4981
|
-
toolCall: {
|
|
4982
|
-
type: "toolCall",
|
|
4983
|
-
id: currentToolCallId,
|
|
4984
|
-
name: completedName,
|
|
4985
|
-
arguments: args
|
|
4986
|
-
},
|
|
4987
|
-
partial: output
|
|
4988
|
-
});
|
|
4989
|
-
if (streamingToolCall) streamingToolCalls.forget(streamingToolCall);
|
|
4990
|
-
if (currentBlock === toolCallBlock) {
|
|
4991
|
-
currentBlock = null;
|
|
4992
|
-
currentItem = null;
|
|
4993
|
-
}
|
|
4994
|
-
}
|
|
4995
|
-
} else if (type === "response.completed" || type === "response.incomplete") {
|
|
4996
|
-
if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
|
|
4997
|
-
const response = event.response;
|
|
4998
|
-
if (typeof response?.id === "string") output.responseId = response.id;
|
|
4999
|
-
if (type === "response.completed") backfillTerminalResponseOutput(response, { includeToolCalls: true });
|
|
5000
|
-
recordResponsesTerminalOutcome({
|
|
5001
|
-
response,
|
|
5002
|
-
output,
|
|
5003
|
-
model,
|
|
5004
|
-
serviceTier: options?.serviceTier,
|
|
5005
|
-
applyServiceTierPricing: options?.applyServiceTierPricing
|
|
5006
|
-
});
|
|
5007
|
-
if (type === "response.incomplete" && output.stopReason === "length") backfillTerminalResponseOutput(response, { includeToolCalls: false });
|
|
5008
|
-
} else if (type === "error") throw new Error(`Error Code ${stringifyUnknown(event.code, "unknown")}: ${stringifyUnknown(event.message, "Unknown error")}`);
|
|
5009
|
-
else if (type === "response.failed") {
|
|
5010
|
-
const failure = normalizeResponsesFailedEvent(event, model);
|
|
5011
|
-
if (failure.responseId) output.responseId = failure.responseId;
|
|
5012
|
-
if (failure.observation) logResponsesFailedNoDetails(failure.observation);
|
|
5013
|
-
throw new Error(failure.message);
|
|
5014
|
-
}
|
|
5015
|
-
await cooperativeScheduler.afterEvent();
|
|
5016
|
-
}
|
|
5017
|
-
if (streamingToolCalls.hasActive()) throw new Error("Responses stream ended with unresolved tool calls");
|
|
5018
|
-
const eventTypeSummary = [...eventTypes.entries()].slice(0, 12).map(([eventType, count]) => `${eventType}:${count}`).join(",");
|
|
5019
|
-
emitModelTransportDebug(log, `[responses] stream_done provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - streamStartedAt} events=${eventCount} types=${eventTypeSummary} stopReason=${output.stopReason ?? "unset"} contentBlocks=${output.content.length}`);
|
|
5020
|
-
}
|
|
5021
|
-
function readResponsesOutputMessageText(item) {
|
|
5022
|
-
return (Array.isArray(item.content) ? item.content : []).map((part) => {
|
|
5023
|
-
if (!isRecord(part)) return "";
|
|
5024
|
-
if (part.type === "output_text" || part.type === "text") return stringifyUnknown(part.text);
|
|
5025
|
-
if (part.type === "refusal") return stringifyUnknown(part.refusal);
|
|
5026
|
-
return "";
|
|
5027
|
-
}).join("");
|
|
5028
|
-
}
|
|
5029
|
-
function resolveProviderTransportTurnState(model, params) {
|
|
5030
|
-
const normalizedProvider = model.provider.trim().toLowerCase();
|
|
5031
|
-
const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
|
|
5032
|
-
return getAiTransportHost().plugin.resolveTransportTurnState({
|
|
5033
|
-
provider: model.provider,
|
|
5034
|
-
modelId: model.id,
|
|
5035
|
-
allowRuntimePluginLoad,
|
|
5036
|
-
context: {
|
|
5037
|
-
provider: model.provider,
|
|
5038
|
-
modelId: model.id,
|
|
5039
|
-
model,
|
|
5040
|
-
sessionId: params.sessionId,
|
|
5041
|
-
turnId: params.turnId,
|
|
5042
|
-
attempt: params.attempt,
|
|
5043
|
-
transport: params.transport
|
|
5044
|
-
}
|
|
5045
|
-
});
|
|
5046
|
-
}
|
|
5047
|
-
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
|
|
5048
|
-
return new OpenAI({
|
|
5049
|
-
apiKey,
|
|
5050
|
-
baseURL: model.baseUrl,
|
|
5051
|
-
dangerouslyAllowBrowser: true,
|
|
5052
|
-
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
|
|
5053
|
-
fetch: buildGuardedModelFetch(model),
|
|
5054
|
-
...buildOpenAISdkClientOptions(model)
|
|
5055
|
-
});
|
|
5056
|
-
}
|
|
5057
|
-
function createOpenAIResponsesTransportStreamFn() {
|
|
5058
|
-
return (model, context, options) => {
|
|
5059
|
-
const responsesOptions = options;
|
|
5060
|
-
const eventStream = createAssistantMessageEventStream();
|
|
5061
|
-
const stream = eventStream;
|
|
5062
|
-
(async () => {
|
|
5063
|
-
const output = {
|
|
5064
|
-
role: "assistant",
|
|
5065
|
-
content: [],
|
|
5066
|
-
api: model.api,
|
|
5067
|
-
provider: model.provider,
|
|
5068
|
-
model: model.id,
|
|
5069
|
-
usage: {
|
|
5070
|
-
input: 0,
|
|
5071
|
-
output: 0,
|
|
5072
|
-
cacheRead: 0,
|
|
5073
|
-
cacheWrite: 0,
|
|
5074
|
-
totalTokens: 0,
|
|
5075
|
-
cost: {
|
|
5076
|
-
input: 0,
|
|
5077
|
-
output: 0,
|
|
5078
|
-
cacheRead: 0,
|
|
5079
|
-
cacheWrite: 0,
|
|
5080
|
-
total: 0
|
|
5081
|
-
}
|
|
5082
|
-
},
|
|
5083
|
-
stopReason: "stop",
|
|
5084
|
-
timestamp: Date.now()
|
|
5085
|
-
};
|
|
5086
|
-
let firstEventAbort;
|
|
5087
|
-
try {
|
|
5088
|
-
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
5089
|
-
const turnState = resolveProviderTransportTurnState(model, {
|
|
5090
|
-
sessionId: options?.sessionId,
|
|
5091
|
-
turnId: randomUUID(),
|
|
5092
|
-
attempt: 1,
|
|
5093
|
-
transport: "stream"
|
|
5094
|
-
});
|
|
5095
|
-
const client = createOpenAIResponsesClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
|
|
5096
|
-
let params = buildOpenAIResponsesParams(model, context, responsesOptions, turnState?.metadata);
|
|
5097
|
-
const nextParams = await options?.onPayload?.(params, model);
|
|
5098
|
-
if (nextParams !== void 0) params = nextParams;
|
|
5099
|
-
if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
|
|
5100
|
-
params = sanitizeOpenAICodexResponsesParams(model, params);
|
|
5101
|
-
params = sanitizeResponsesImagePayload(params);
|
|
5102
|
-
if (options?.openclawCodeModeToolSurface === true) {
|
|
5103
|
-
enforceCodeModeResponsesToolSurface(params);
|
|
5104
|
-
assertCodeModeResponsesToolSurface(params);
|
|
5105
|
-
}
|
|
5106
|
-
const requestStartedAt = Date.now();
|
|
5107
|
-
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
5108
|
-
const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, { stream: true });
|
|
5109
|
-
emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
|
|
5110
|
-
const responseStream = await createResponsesStreamWithEncryptedContentRetry({
|
|
5111
|
-
client,
|
|
5112
|
-
request: params,
|
|
5113
|
-
requestOptions,
|
|
5114
|
-
model
|
|
5115
|
-
});
|
|
5116
|
-
emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
|
|
5117
|
-
stream.push({
|
|
5118
|
-
type: "start",
|
|
5119
|
-
partial: output
|
|
5120
|
-
});
|
|
5121
|
-
await processResponsesStream(responseStream, output, stream, model, {
|
|
5122
|
-
serviceTier: responsesOptions?.serviceTier,
|
|
5123
|
-
applyServiceTierPricing,
|
|
5124
|
-
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
5125
|
-
abortFirstEventStream: firstEventAbort.abort,
|
|
5126
|
-
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
5127
|
-
signal: options?.signal,
|
|
5128
|
-
authProfileId: responsesOptions?.authProfileId,
|
|
5129
|
-
sessionId: options?.sessionId
|
|
5130
|
-
});
|
|
5131
|
-
if (options?.signal?.aborted) throw new Error("Request was aborted");
|
|
5132
|
-
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
|
|
5133
|
-
stream.push({
|
|
5134
|
-
type: "done",
|
|
5135
|
-
reason: output.stopReason,
|
|
5136
|
-
message: output
|
|
5137
|
-
});
|
|
5138
|
-
stream.end();
|
|
5139
|
-
} catch (error) {
|
|
5140
|
-
log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
|
|
5141
|
-
assignTransportErrorDetails(output, error, options?.signal);
|
|
5142
|
-
stream.push({
|
|
5143
|
-
type: "error",
|
|
5144
|
-
reason: output.stopReason,
|
|
5145
|
-
error: output
|
|
5146
|
-
});
|
|
5147
|
-
stream.end();
|
|
5148
|
-
} finally {
|
|
5149
|
-
firstEventAbort?.dispose();
|
|
5150
|
-
}
|
|
5151
|
-
})();
|
|
5152
|
-
return eventStream;
|
|
5153
|
-
};
|
|
5154
|
-
}
|
|
5155
|
-
function getPromptCacheRetention(baseUrl, cacheRetention) {
|
|
5156
|
-
if (cacheRetention !== "long") return;
|
|
5157
|
-
return baseUrl?.includes("api.openai.com") ? "24h" : void 0;
|
|
5158
|
-
}
|
|
5159
|
-
function resolveOpenAIReasoningEffort(options) {
|
|
5160
|
-
return normalizeOpenAIReasoningEffort(options?.reasoningEffort ?? options?.reasoning ?? "high");
|
|
5161
|
-
}
|
|
5162
|
-
function hasResponsesWebSearchTool(tools) {
|
|
5163
|
-
if (!Array.isArray(tools)) return false;
|
|
5164
|
-
return tools.some((tool) => {
|
|
5165
|
-
if (!isRecord(tool)) return false;
|
|
5166
|
-
if (tool.type === "web_search") return true;
|
|
5167
|
-
if (tool.type === "function" && tool.name === "web_search") return true;
|
|
5168
|
-
const fn = tool.function;
|
|
5169
|
-
return isRecord(fn) && fn.name === "web_search";
|
|
5170
|
-
});
|
|
5171
|
-
}
|
|
5172
|
-
function raiseMinimalReasoningForResponsesWebSearch(params) {
|
|
5173
|
-
if (params.effort !== "minimal" || !hasResponsesWebSearchTool(params.tools)) return params.effort;
|
|
5174
|
-
for (const effort of [
|
|
5175
|
-
"low",
|
|
5176
|
-
"medium",
|
|
5177
|
-
"high"
|
|
5178
|
-
]) {
|
|
5179
|
-
const resolved = resolveOpenAIReasoningEffortForModel({
|
|
5180
|
-
model: params.model,
|
|
5181
|
-
effort
|
|
5182
|
-
});
|
|
5183
|
-
if (resolved && resolved !== "none" && resolved !== "minimal") return resolved;
|
|
5184
|
-
}
|
|
5185
|
-
return params.effort;
|
|
5186
|
-
}
|
|
5187
|
-
const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
|
|
5188
|
-
"max_output_tokens",
|
|
5189
|
-
"metadata",
|
|
5190
|
-
"prompt_cache_retention",
|
|
5191
|
-
"service_tier",
|
|
5192
|
-
"temperature",
|
|
5193
|
-
"top_p"
|
|
5194
|
-
];
|
|
5195
|
-
function stripOpenAICodexResponsesUnsupportedTextFields(params) {
|
|
5196
|
-
const text = params.text;
|
|
5197
|
-
if (!text || typeof text !== "object" || Array.isArray(text)) return;
|
|
5198
|
-
const sanitizedText = { ...text };
|
|
5199
|
-
delete sanitizedText.format;
|
|
5200
|
-
if (Object.keys(sanitizedText).length > 0) params.text = sanitizedText;
|
|
5201
|
-
else delete params.text;
|
|
5202
|
-
}
|
|
5203
|
-
function sanitizeOpenAICodexResponsesParams(model, params) {
|
|
5204
|
-
if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
|
|
5205
|
-
for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
|
|
5206
|
-
stripOpenAICodexResponsesUnsupportedTextFields(params);
|
|
5207
|
-
return params;
|
|
5208
|
-
}
|
|
5209
|
-
function buildOpenAICodexResponsesInstructions(context) {
|
|
5210
|
-
if (!context.systemPrompt) return;
|
|
5211
|
-
return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
|
|
5212
|
-
}
|
|
5213
|
-
function resolveOpenAICodexResponsesInstructions(model, context) {
|
|
5214
|
-
const instructions = buildOpenAICodexResponsesInstructions(context);
|
|
5215
|
-
if (instructions && instructions.trim().length > 0) return instructions;
|
|
5216
|
-
return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
|
|
3412
|
+
function resolveOpenAICodexResponsesInstructions(model, context) {
|
|
3413
|
+
const instructions = buildOpenAICodexResponsesInstructions(context);
|
|
3414
|
+
if (instructions && instructions.trim().length > 0) return instructions;
|
|
3415
|
+
return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
|
|
5217
3416
|
}
|
|
5218
3417
|
function ensureOpenAICodexResponsesInput(messages, context) {
|
|
5219
3418
|
if (messages.length > 0 || !context.systemPrompt) return;
|
|
5220
3419
|
if (!buildOpenAICodexResponsesInstructions(context)) throw new Error("OpenAI Codex Responses requires non-empty input when only systemPrompt is provided.");
|
|
5221
3420
|
messages.push(buildResponsesInputMessage("user", [{
|
|
5222
3421
|
type: "input_text",
|
|
5223
|
-
text:
|
|
3422
|
+
text: " "
|
|
5224
3423
|
}]));
|
|
5225
3424
|
}
|
|
5226
3425
|
function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
@@ -5309,7 +3508,180 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
|
|
|
5309
3508
|
applyOpenAIResponsesPayloadPolicy(params, payloadPolicy);
|
|
5310
3509
|
return sanitizeOpenAICodexResponsesParams(model, params);
|
|
5311
3510
|
}
|
|
5312
|
-
|
|
3511
|
+
//#endregion
|
|
3512
|
+
//#region packages/media-core/src/inline-image-data-url.ts
|
|
3513
|
+
/** Prefix used to distinguish inline data URLs from remote/local image references. */
|
|
3514
|
+
const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
|
|
3515
|
+
const IMAGE_SIGNATURES = [
|
|
3516
|
+
{
|
|
3517
|
+
mime: "image/png",
|
|
3518
|
+
matches: (buffer) => buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10
|
|
3519
|
+
},
|
|
3520
|
+
{
|
|
3521
|
+
mime: "image/jpeg",
|
|
3522
|
+
matches: (buffer) => buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255
|
|
3523
|
+
},
|
|
3524
|
+
{
|
|
3525
|
+
mime: "image/webp",
|
|
3526
|
+
matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
3527
|
+
},
|
|
3528
|
+
{
|
|
3529
|
+
mime: "image/gif",
|
|
3530
|
+
matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
|
|
3531
|
+
},
|
|
3532
|
+
{
|
|
3533
|
+
mime: "image/bmp",
|
|
3534
|
+
matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
|
|
3535
|
+
}
|
|
3536
|
+
];
|
|
3537
|
+
const HEIC_BRANDS = /* @__PURE__ */ new Set([
|
|
3538
|
+
"heic",
|
|
3539
|
+
"heix",
|
|
3540
|
+
"hevc",
|
|
3541
|
+
"hevx",
|
|
3542
|
+
"heis",
|
|
3543
|
+
"heim",
|
|
3544
|
+
"hevm",
|
|
3545
|
+
"hevs"
|
|
3546
|
+
]);
|
|
3547
|
+
const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
|
|
3548
|
+
const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
|
|
3549
|
+
const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
|
|
3550
|
+
"image/png",
|
|
3551
|
+
"image/jpeg",
|
|
3552
|
+
"image/webp",
|
|
3553
|
+
"image/gif"
|
|
3554
|
+
]);
|
|
3555
|
+
function startsWithDataUrl(value) {
|
|
3556
|
+
return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
|
|
3557
|
+
}
|
|
3558
|
+
function sniffIsoBmffImageMime(buffer) {
|
|
3559
|
+
if (buffer.length < 12 || buffer.subarray(4, 8).toString("ascii") !== "ftyp") return;
|
|
3560
|
+
const brands = [buffer.subarray(8, 12).toString("ascii")];
|
|
3561
|
+
for (let offset = 16; offset + 4 <= buffer.length; offset += 4) brands.push(buffer.subarray(offset, offset + 4).toString("ascii"));
|
|
3562
|
+
if (brands.some((brand) => HEIC_BRANDS.has(brand))) return "image/heic";
|
|
3563
|
+
if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
|
|
3564
|
+
}
|
|
3565
|
+
/** Sniffs supported inline image formats from decoded bytes. */
|
|
3566
|
+
function sniffInlineImageMime(buffer) {
|
|
3567
|
+
return IMAGE_SIGNATURES.find((signature) => signature.matches(buffer))?.mime ?? sniffIsoBmffImageMime(buffer);
|
|
3568
|
+
}
|
|
3569
|
+
function isImageMimeType(value) {
|
|
3570
|
+
return value.trim().toLowerCase().startsWith("image/");
|
|
3571
|
+
}
|
|
3572
|
+
/** Canonicalizes trusted inline image base64 and rejects malformed or non-image payloads. */
|
|
3573
|
+
function sanitizeInlineImageBase64(params) {
|
|
3574
|
+
if (!isImageMimeType(params.mimeType)) return;
|
|
3575
|
+
const canonicalPayload = canonicalizeBase64(params.base64);
|
|
3576
|
+
if (!canonicalPayload) return;
|
|
3577
|
+
const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
|
|
3578
|
+
if (!sniffedMimeType) return;
|
|
3579
|
+
return {
|
|
3580
|
+
mimeType: sniffedMimeType,
|
|
3581
|
+
base64: canonicalPayload
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3584
|
+
function parseInlineImageDataUrl(value) {
|
|
3585
|
+
if (!startsWithDataUrl(value)) return {
|
|
3586
|
+
metadata: [],
|
|
3587
|
+
payload: value
|
|
3588
|
+
};
|
|
3589
|
+
const commaIndex = value.indexOf(",");
|
|
3590
|
+
if (commaIndex < 0) return;
|
|
3591
|
+
return {
|
|
3592
|
+
metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
|
|
3593
|
+
payload: value.slice(commaIndex + 1)
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
function metadataAllowsImageBase64(metadata) {
|
|
3597
|
+
const [mimeType, ...options] = metadata;
|
|
3598
|
+
return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
|
|
3599
|
+
}
|
|
3600
|
+
function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
|
|
3601
|
+
const parsed = parseInlineImageDataUrl(imageUrl);
|
|
3602
|
+
if (!parsed) return;
|
|
3603
|
+
if (parsed.metadata.length === 0) return imageUrl;
|
|
3604
|
+
if (!metadataAllowsImageBase64(parsed.metadata)) return;
|
|
3605
|
+
const [mimeType] = parsed.metadata;
|
|
3606
|
+
const sanitized = sanitizeInlineImageBase64({
|
|
3607
|
+
mimeType: mimeType ?? "",
|
|
3608
|
+
base64: parsed.payload
|
|
3609
|
+
});
|
|
3610
|
+
if (!sanitized) return;
|
|
3611
|
+
if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
|
|
3612
|
+
return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
|
|
3613
|
+
}
|
|
3614
|
+
/** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
|
|
3615
|
+
function sanitizeInlineImageDataUrl(imageUrl) {
|
|
3616
|
+
return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
|
|
3617
|
+
}
|
|
3618
|
+
//#endregion
|
|
3619
|
+
//#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
|
|
3620
|
+
/**
|
|
3621
|
+
* Sanitizes OpenAI Responses payloads before transport. Invalid inline images
|
|
3622
|
+
* are replaced with text placeholders so the request remains valid and
|
|
3623
|
+
* auditable.
|
|
3624
|
+
*/
|
|
3625
|
+
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
|
|
3626
|
+
function invalidSnakeImage() {
|
|
3627
|
+
return {
|
|
3628
|
+
type: "input_text",
|
|
3629
|
+
text: `[${IMAGE_OMITTED_TEXT}]`
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3632
|
+
function sanitizeValue(value) {
|
|
3633
|
+
if (Array.isArray(value)) return value.map(sanitizeValue);
|
|
3634
|
+
if (!isRecord(value)) return value;
|
|
3635
|
+
if (value.type === "input_image" && typeof value.image_url === "string") {
|
|
3636
|
+
const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
|
|
3637
|
+
return imageUrl ? {
|
|
3638
|
+
...value,
|
|
3639
|
+
image_url: imageUrl
|
|
3640
|
+
} : invalidSnakeImage();
|
|
3641
|
+
}
|
|
3642
|
+
const next = {};
|
|
3643
|
+
for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
|
|
3644
|
+
return next;
|
|
3645
|
+
}
|
|
3646
|
+
/** Sanitize inline image fields inside a Responses API payload. */
|
|
3647
|
+
function sanitizeResponsesImagePayload(params) {
|
|
3648
|
+
if (!Array.isArray(params.input)) return params;
|
|
3649
|
+
return {
|
|
3650
|
+
...params,
|
|
3651
|
+
input: sanitizeValue(params.input)
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3654
|
+
//#endregion
|
|
3655
|
+
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
3656
|
+
function resolveProviderTransportTurnState(model, params) {
|
|
3657
|
+
const normalizedProvider = model.provider.trim().toLowerCase();
|
|
3658
|
+
const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
|
|
3659
|
+
return getAiTransportHost().plugin.resolveTransportTurnState({
|
|
3660
|
+
provider: model.provider,
|
|
3661
|
+
modelId: model.id,
|
|
3662
|
+
allowRuntimePluginLoad,
|
|
3663
|
+
context: {
|
|
3664
|
+
provider: model.provider,
|
|
3665
|
+
modelId: model.id,
|
|
3666
|
+
model,
|
|
3667
|
+
sessionId: params.sessionId,
|
|
3668
|
+
turnId: params.turnId,
|
|
3669
|
+
attempt: params.attempt,
|
|
3670
|
+
transport: params.transport
|
|
3671
|
+
}
|
|
3672
|
+
});
|
|
3673
|
+
}
|
|
3674
|
+
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
|
|
3675
|
+
return new OpenAI({
|
|
3676
|
+
apiKey,
|
|
3677
|
+
baseURL: model.baseUrl,
|
|
3678
|
+
dangerouslyAllowBrowser: true,
|
|
3679
|
+
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
|
|
3680
|
+
fetch: buildGuardedModelFetch(model),
|
|
3681
|
+
...buildOpenAISdkClientOptions(model)
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3684
|
+
function createResponsesTransportExecutor(config) {
|
|
5313
3685
|
return (model, context, options) => {
|
|
5314
3686
|
const responsesOptions = options;
|
|
5315
3687
|
const eventStream = createAssistantMessageEventStream();
|
|
@@ -5318,7 +3690,7 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5318
3690
|
const output = {
|
|
5319
3691
|
role: "assistant",
|
|
5320
3692
|
content: [],
|
|
5321
|
-
api:
|
|
3693
|
+
api: config.outputApi ?? model.api,
|
|
5322
3694
|
provider: model.provider,
|
|
5323
3695
|
model: model.id,
|
|
5324
3696
|
usage: {
|
|
@@ -5347,37 +3719,49 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5347
3719
|
attempt: 1,
|
|
5348
3720
|
transport: "stream"
|
|
5349
3721
|
});
|
|
5350
|
-
const client =
|
|
5351
|
-
|
|
5352
|
-
let params = buildAzureOpenAIResponsesParams(model, context, responsesOptions, deploymentName, turnState?.metadata);
|
|
3722
|
+
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
|
|
3723
|
+
let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata);
|
|
5353
3724
|
const nextParams = await options?.onPayload?.(params, model);
|
|
5354
3725
|
if (nextParams !== void 0) params = nextParams;
|
|
5355
3726
|
if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
|
|
5356
3727
|
params = sanitizeOpenAICodexResponsesParams(model, params);
|
|
5357
3728
|
params = sanitizeResponsesImagePayload(params);
|
|
5358
3729
|
if (options?.openclawCodeModeToolSurface === true) {
|
|
5359
|
-
|
|
5360
|
-
|
|
3730
|
+
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
3731
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
3732
|
+
assertCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
5361
3733
|
}
|
|
5362
3734
|
const requestStartedAt = Date.now();
|
|
5363
3735
|
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
5364
|
-
const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal
|
|
3736
|
+
const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
|
|
3737
|
+
stream: config.streamRequest,
|
|
3738
|
+
timeoutMs: options?.timeoutMs,
|
|
3739
|
+
maxRetries: options?.maxRetries
|
|
3740
|
+
});
|
|
5365
3741
|
emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
|
|
5366
|
-
const responseStream = await
|
|
3742
|
+
const responseStream = await config.createResponseStream({
|
|
3743
|
+
client,
|
|
3744
|
+
request: params,
|
|
3745
|
+
requestOptions,
|
|
3746
|
+
model
|
|
3747
|
+
});
|
|
5367
3748
|
emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
|
|
5368
3749
|
stream.push({
|
|
5369
3750
|
type: "start",
|
|
5370
3751
|
partial: output
|
|
5371
3752
|
});
|
|
5372
|
-
await processResponsesStream(responseStream, output, stream, model, {
|
|
5373
|
-
|
|
3753
|
+
await processResponsesStream(observeResponsesStream(responseStream, model), output, stream, model, {
|
|
3754
|
+
...config.pricingOptions?.(responsesOptions),
|
|
3755
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
|
|
5374
3756
|
abortFirstEventStream: firstEventAbort.abort,
|
|
5375
3757
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
5376
3758
|
signal: options?.signal,
|
|
5377
|
-
|
|
5378
|
-
|
|
3759
|
+
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
3760
|
+
authProfileId: responsesOptions?.authProfileId,
|
|
3761
|
+
sessionId: options?.sessionId
|
|
3762
|
+
})
|
|
5379
3763
|
});
|
|
5380
|
-
if (options?.signal?.aborted) throw
|
|
3764
|
+
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
5381
3765
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
|
|
5382
3766
|
stream.push({
|
|
5383
3767
|
type: "done",
|
|
@@ -5386,6 +3770,7 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5386
3770
|
});
|
|
5387
3771
|
stream.end();
|
|
5388
3772
|
} catch (error) {
|
|
3773
|
+
if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
|
|
5389
3774
|
log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
|
|
5390
3775
|
assignTransportErrorDetails(output, error, options?.signal);
|
|
5391
3776
|
stream.push({
|
|
@@ -5401,6 +3786,27 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5401
3786
|
return eventStream;
|
|
5402
3787
|
};
|
|
5403
3788
|
}
|
|
3789
|
+
function createOpenAIResponsesTransportStreamFn() {
|
|
3790
|
+
return createResponsesTransportExecutor({
|
|
3791
|
+
streamRequest: true,
|
|
3792
|
+
createClient: createOpenAIResponsesClient,
|
|
3793
|
+
buildRequest: buildOpenAIResponsesParams,
|
|
3794
|
+
createResponseStream: createResponsesStreamWithEncryptedContentRetry,
|
|
3795
|
+
pricingOptions: (options) => ({
|
|
3796
|
+
serviceTier: options?.serviceTier,
|
|
3797
|
+
applyServiceTierPricing
|
|
3798
|
+
})
|
|
3799
|
+
});
|
|
3800
|
+
}
|
|
3801
|
+
function createAzureOpenAIResponsesTransportStreamFn() {
|
|
3802
|
+
return createResponsesTransportExecutor({
|
|
3803
|
+
outputApi: "azure-openai-responses",
|
|
3804
|
+
firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
|
|
3805
|
+
createClient: createAzureOpenAIClient,
|
|
3806
|
+
buildRequest: (model, context, options, metadata) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata),
|
|
3807
|
+
createResponseStream: async ({ client, request, requestOptions }) => await client.responses.create(request, requestOptions)
|
|
3808
|
+
});
|
|
3809
|
+
}
|
|
5404
3810
|
function normalizeAzureBaseUrl(baseUrl) {
|
|
5405
3811
|
return baseUrl.replace(/\/+$/, "");
|
|
5406
3812
|
}
|
|
@@ -5432,6 +3838,9 @@ function buildAzureOpenAIResponsesParams(model, context, options, deploymentName
|
|
|
5432
3838
|
delete params.store;
|
|
5433
3839
|
return params;
|
|
5434
3840
|
}
|
|
3841
|
+
//#endregion
|
|
3842
|
+
//#region packages/ai/src/transports/openai-responses-transport.ts
|
|
3843
|
+
/** OpenAI Responses transport facade. */
|
|
5435
3844
|
const responsesTesting = {
|
|
5436
3845
|
getCompat,
|
|
5437
3846
|
assertCodeModeResponsesToolSurface,
|
|
@@ -5698,4 +4107,4 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
5698
4107
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
5699
4108
|
}
|
|
5700
4109
|
//#endregion
|
|
5701
|
-
export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, hasOpenAICompatibleConversationTurn, isOpenAICodexResponsesModel, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy,
|
|
4110
|
+
export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isOpenAICodexResponsesModel, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend };
|