@openclaw/ai 2026.7.2-beta.4 → 2026.7.2-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{anthropic-CrUDIBpM.mjs → anthropic-CQVj3le6.mjs} +53 -19
- package/dist/{anthropic-BoTnz8cv.d.mts → anthropic-SrGtwsJu.d.mts} +27 -1
- package/dist/{api-registry-BMphkFf1.d.mts → api-registry-DlMgPR39.d.mts} +1 -1
- package/dist/{azure-openai-responses-CbrpVeEv.mjs → azure-openai-responses-DIYgsqFM.mjs} +3 -4
- 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/{tool-schema-json-projection-BwNu3nDi.mjs → github-copilot-headers-BCoBNmL7.mjs} +48 -1
- package/dist/{google-Cs62KA7t.mjs → google-f-A8xrae.mjs} +2 -2
- package/dist/{google-shared-CMLI-tCZ.mjs → google-shared-J6qvYINH.mjs} +5 -4
- package/dist/{google-vertex-B4SD3U0f.mjs → google-vertex-D3yMVXIY.mjs} +2 -2
- package/dist/{host-WvWBo4h8.d.mts → host-B9GUmcra.d.mts} +2 -2
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +2 -2
- package/dist/internal/anthropic.d.mts +15 -11
- package/dist/internal/anthropic.mjs +4 -4
- package/dist/internal/openai.d.mts +2 -1
- package/dist/internal/openai.mjs +4 -6
- package/dist/internal/runtime.d.mts +8 -32
- package/dist/internal/runtime.mjs +2 -3
- package/dist/internal/shared.d.mts +13 -8
- package/dist/internal/shared.mjs +2 -2
- package/dist/{mistral-D5Ps7Led.mjs → mistral-CKV-TOQj.mjs} +4 -3
- package/dist/number-coercion-DvG7SNMg.mjs +129 -0
- package/dist/{openai-CoGicoDt.d.mts → openai-BPor_3WI.d.mts} +35 -9
- package/dist/{openai-responses-shared-CzCurmY1.mjs → openai-D3PD6PE-.mjs} +1771 -745
- package/dist/{openai-chatgpt-responses-h0o5yV8Y.mjs → openai-chatgpt-responses-CedIj0hk.mjs} +19 -20
- package/dist/{openai-completions-exO8NFQf.mjs → openai-completions-CiSutyu0.mjs} +366 -227
- package/dist/openai-prompt-cache-2uo_1OR1.d.mts +7 -0
- package/dist/openai-prompt-cache-mZTCdRPo.mjs +12 -0
- package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +13 -0
- package/dist/{provider-error-C4VvV_3t.mjs → provider-error-apVOZI6G.mjs} +8 -2
- package/dist/providers.d.mts +3 -2
- package/dist/providers.mjs +10 -9
- package/dist/{system-prompt-cache-boundary-CbHeV4_l.mjs → shared-CdjNZd35.mjs} +117 -9
- package/dist/{src-CXno1H5g.mjs → src-QkygScBs.mjs} +23 -4
- package/dist/stream-first-event-timeout-C3OgBjIk.mjs +12294 -0
- package/dist/stream-first-event-timeout-DvDeSucC.d.mts +29 -0
- package/dist/transport-stream-shared-BbMELSI4.mjs +297 -0
- package/dist/transports.d.mts +105 -102
- package/dist/transports.mjs +334 -1966
- 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 +1 -2
- package/dist/azure-openai-responses-client-compat-C7K7QfUE.mjs +0 -62
- package/dist/github-copilot-headers-BsH5cqGj.mjs +0 -48
- package/dist/model-utils-1GiZ2_rr.mjs +0 -64
- package/dist/openai-responses-BHpmtUKo.mjs +0 -136
- package/dist/openai-tool-projection-ITOU9bG1.mjs +0 -200
- package/dist/reasoning-tag-text-partitioner-BlVe67g6.mjs +0 -400
- package/dist/stream-first-event-timeout-DP4xEyBY.mjs +0 -150
- package/npm-shrinkwrap.json +0 -638
package/dist/transports.mjs
CHANGED
|
@@ -1,52 +1,22 @@
|
|
|
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
4
|
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-XYGZcgO8.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import "./
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
6
|
+
import { C as extractToolResultBlockText, E as isImageWithMediaPayload, O as isRecord, S as describeToolResultMediaPlaceholder, _ as usesClaudeStreamingRefusalContract, a as splitSystemPromptCacheBoundary, d as applyClaudeRequestContract, f as defaultsClaudeAdaptiveThinking, g as usesClaudeFable5MessagesContract, l as sortPromptCacheToolsByName, m as requiresClaudeAdaptiveThinking, o as stripSystemPromptCacheBoundary, p as prepareClaudeNoPrefillRequestContext, v as hasNonEmptyString, w as extractToolResultText, x as readStringValue, y as normalizeLowercaseStringOrEmpty } from "./shared-CdjNZd35.mjs";
|
|
7
|
+
import { C as transformTransportMessages, S as resolveProviderRequestPolicyConfig, _ as buildGuardedModelFetch, a as projectOpenAITools, b as resolveProviderEndpoint, c as mapOpenAIStopReason, d as resolveOpenAICompletionsCompat, f as resolveOpenAICompletionsResponseFormat, g as tagPendingCommentaryText, h as rememberPendingCommentaryTags, l as resolveOpenAIReasoningEffortMap, m as clearPendingCommentaryText, o as reconcileOpenAICompletionsToolChoice, p as shouldOmitOllamaCompatResponseFormat, s as reconcileOpenAIResponsesToolChoice, t as convertMessages, u as detectOpenAICompletionsCompat, v as resolveModelRequestTimeoutMs, x as resolveProviderRequestCapabilities, y as resolveOpenAIStrictToolSetting } from "./openai-completions-CiSutyu0.mjs";
|
|
8
|
+
import { t as toErrorObject } from "./error-coercion-DgxlWC0n.mjs";
|
|
9
|
+
import { C as normalizeAnthropicInlineContent, S as createAnthropicInlineImageBudget, T as canonicalizeBase64, _ as readAnthropicFallbackBoundary, a as readAnthropicPromptUsageSnapshot, b as omitFoundryBearerCredentialHeaders, c as projectAnthropicTools, d as ANTHROPIC_OMITTED_REASONING_TEXT, f as findActiveAnthropicToolTurnAssistantIndex, g as applyAnthropicFallbackBoundary, l as reconcileAnthropicToolChoice, m as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, o as readAnthropicUsageTokenCount, p as ANTHROPIC_SERVER_SIDE_FALLBACKS, s as readLastAnthropicIterationUsage, u as resolveOriginalAnthropicToolName, v as resolveAnthropicFallbackServingModelCost, w as resolveAnthropicImageMediaType, x as usesFoundryBearerAuth, y as applyAnthropicRefusal } from "./anthropic-CQVj3le6.mjs";
|
|
10
|
+
import { a as parseStrictPositiveInteger, c as calculateCost, l as clampThinkingLevel, s as applyProviderReportedUsageCost } from "./number-coercion-DvG7SNMg.mjs";
|
|
11
|
+
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-BbMELSI4.mjs";
|
|
10
12
|
import { r as createDeferredEventBuffer, t as notifyLlmRequestActivity } from "./llm-request-activity-CehVkZP-.mjs";
|
|
11
13
|
import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
|
|
12
14
|
import "./internal/anthropic.mjs";
|
|
13
|
-
import {
|
|
14
|
-
import { a as withFirstStreamEventTimeout, c as parseStrictFiniteNumber, i as getFirstStreamEventTimeoutMs, l as parseStrictNonNegativeInteger, o as asFiniteNumberInRange, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController, u as parseStrictPositiveInteger } from "./stream-first-event-timeout-DP4xEyBY.mjs";
|
|
15
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, o as createReasoningTagTextPartitioner, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-C3OgBjIk.mjs";
|
|
15
16
|
import "./internal/runtime.mjs";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
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";
|
|
19
|
-
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
20
|
-
import { a as clampOpenAIPromptCacheKey, n as reconcileOpenAICompletionsToolChoice, r as reconcileOpenAIResponsesToolChoice, t as projectOpenAITools } from "./openai-tool-projection-ITOU9bG1.mjs";
|
|
21
|
-
import "./internal/openai.mjs";
|
|
22
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
17
|
+
import { A as resolveAzureOpenAIApiVersion, At as emitModelTransportDebug, B as stringifyRedactedPayload, C as observeResponsesStream, D as createResponsesStreamWithEncryptedContentRetry, E as convertResponsesMessages, F as buildResponsesFailedNoDetailsObservation, G as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, H as summarizeResponsesFailedNoDetailsObservation, I as logResponsesFailedNoDetails, J as resolveCacheRetention, K as createModelStreamCooperativeScheduler, L as normalizeResponsesFailedEvent, M as tagOpenAIResponsesReasoningReplayItem, Mt as resolveModelSseDebugMode, N as resolveReplayableResponsesMessageId, O as isInvalidEncryptedContentError, P as applyServiceTierPricing, Q as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, R as safeDebugValue, T as buildResponsesInputMessage, U as summarizeResponsesPayload, V as summarizeOpenAITransportError, W as summarizeResponsesTools, X as throwIfModelStreamAborted, Y as resolvePromptCacheKey, Z as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, d as findOpenAIStrictToolProjectionDiagnostics, dt as isOpenAIGpt55Model, ft as isOpenAIGpt56Model, gt as supportsOpenAIReasoningEffort, h as resolveOpenAIProjectedToolsStrictToolFlag, j as stripResponsesRequestEncryptedContent, jt as resolveModelPayloadDebugMode, k as prepareOpenAIResponsesReasoningItemForReplay, kt as uniqueStrings, mt as resolveOpenAIReasoningEffortForModel, p as normalizeOpenAIStrictToolParameters, pt as normalizeOpenAIReasoningEffort, q as log, ut as isOpenAIGpt54MiniModel, v as ResponsesStreamFailure, vt as isOpenAICompatibleAzureResponsesBaseUrl, w as buildOpenAIResponsesReasoningReplayMetadata, xt as resolveAzureDeploymentNameFromMap, y as processResponsesStream, z as stringifyRedactedEvent } from "./openai-D3PD6PE-.mjs";
|
|
18
|
+
import { randomUUID } from "node:crypto";
|
|
23
19
|
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
20
|
//#region packages/ai/src/transports/anthropic-payload-policy.ts
|
|
51
21
|
/**
|
|
52
22
|
* Anthropic-family request payload policy helpers.
|
|
@@ -219,20 +189,6 @@ function applyAnthropicEphemeralCacheControlMarkers(payloadObj, cacheControl = {
|
|
|
219
189
|
}
|
|
220
190
|
}
|
|
221
191
|
//#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
192
|
//#region packages/ai/src/transports/json-unsafe-integers.ts
|
|
237
193
|
/**
|
|
238
194
|
* JSON parsing helpers that preserve integer literals larger than
|
|
@@ -330,270 +286,6 @@ function parseJsonObjectPreservingUnsafeIntegers(value) {
|
|
|
330
286
|
return null;
|
|
331
287
|
}
|
|
332
288
|
//#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
289
|
//#region packages/ai/src/transports/anthropic-transport-stream.ts
|
|
598
290
|
/**
|
|
599
291
|
* Native Anthropic Messages streaming transport.
|
|
@@ -712,7 +404,7 @@ function isKimiAnthropicProvider(provider) {
|
|
|
712
404
|
* identity) requests are excluded until the beta is verified there.
|
|
713
405
|
*/
|
|
714
406
|
function useAnthropicServerSideFallback(model) {
|
|
715
|
-
return usesClaudeFable5MessagesContract(model) && isDirectAnthropicModel(model);
|
|
407
|
+
return (usesClaudeFable5MessagesContract(model) || resolveClaudeOpus5ModelIdentity(model) !== void 0) && isDirectAnthropicModel(model);
|
|
716
408
|
}
|
|
717
409
|
function supportsReasoningContentReplay(model) {
|
|
718
410
|
return resolveProviderEndpoint(model.baseUrl).endpointClass === "xiaomi-native";
|
|
@@ -1200,7 +892,7 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
1200
892
|
max_tokens: maxTokens,
|
|
1201
893
|
stream: true
|
|
1202
894
|
};
|
|
1203
|
-
if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks =
|
|
895
|
+
if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks = ANTHROPIC_SERVER_SIDE_FALLBACKS;
|
|
1204
896
|
if (isOAuthToken) params.system = [
|
|
1205
897
|
{
|
|
1206
898
|
type: "text",
|
|
@@ -1258,7 +950,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
1258
950
|
modelContextWindow: model.contextWindow,
|
|
1259
951
|
modelMaxTokens: model.maxTokens,
|
|
1260
952
|
requestedMaxTokens: options?.maxTokens,
|
|
1261
|
-
useModelDefault: resolveClaudeSonnet5ModelIdentity(model) !== void 0
|
|
953
|
+
useModelDefault: resolveClaudeSonnet5ModelIdentity(model) !== void 0 || resolveClaudeOpus5ModelIdentity(model) !== void 0
|
|
1262
954
|
});
|
|
1263
955
|
if (baseMaxTokens === void 0) throw new Error(`Anthropic Messages transport requires a positive maxTokens value for ${model.provider}/${model.id}`);
|
|
1264
956
|
const reasoningModelMaxTokens = resolvePositiveAnthropicTokenLimit(model.maxTokens) ?? baseMaxTokens;
|
|
@@ -1332,7 +1024,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1332
1024
|
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
|
|
1333
1025
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
1334
1026
|
const transportOptions = resolveAnthropicTransportOptions(model, options, apiKey);
|
|
1335
|
-
const requestContext =
|
|
1027
|
+
const requestContext = prepareClaudeNoPrefillRequestContext(model, context);
|
|
1336
1028
|
const { client, isOAuthToken } = createAnthropicTransportClient({
|
|
1337
1029
|
model,
|
|
1338
1030
|
context: requestContext,
|
|
@@ -1502,7 +1194,11 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1502
1194
|
});
|
|
1503
1195
|
costModel = {
|
|
1504
1196
|
...model,
|
|
1505
|
-
cost:
|
|
1197
|
+
cost: resolveAnthropicFallbackServingModelCost({
|
|
1198
|
+
requestedModelId: model.id,
|
|
1199
|
+
servingModelId: fallbackBoundary.toModel,
|
|
1200
|
+
requestedCost: model.cost
|
|
1201
|
+
})
|
|
1506
1202
|
};
|
|
1507
1203
|
calculateCost(costModel, output.usage);
|
|
1508
1204
|
eventSink.push({
|
|
@@ -1779,7 +1475,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1779
1475
|
}
|
|
1780
1476
|
}
|
|
1781
1477
|
if (refusalBuffer && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
|
|
1782
|
-
if (transportOptions.signal?.aborted) throw
|
|
1478
|
+
if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
|
|
1783
1479
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1784
1480
|
refusalBuffer?.flush();
|
|
1785
1481
|
if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
|
|
@@ -1936,44 +1632,6 @@ function canonicalizeMaxTokensParam(params) {
|
|
|
1936
1632
|
params.merged.maxTokens = resolved;
|
|
1937
1633
|
}
|
|
1938
1634
|
//#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
1635
|
//#region packages/ai/src/transports/model-transport-url.ts
|
|
1978
1636
|
/**
|
|
1979
1637
|
* Debug formatting helpers for model transport endpoints.
|
|
@@ -2004,9 +1662,6 @@ function formatModelTransportDebugBaseUrl(rawUrl) {
|
|
|
2004
1662
|
* Some providers reject requests without a non-empty user/assistant turn; this
|
|
2005
1663
|
* helper checks the loose message payload shape before transport submission.
|
|
2006
1664
|
*/
|
|
2007
|
-
function hasNonEmptyString(value) {
|
|
2008
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
2009
|
-
}
|
|
2010
1665
|
function hasNonEmptyContentPart(part) {
|
|
2011
1666
|
if (!part || typeof part !== "object") return false;
|
|
2012
1667
|
const record = part;
|
|
@@ -2036,62 +1691,6 @@ function hasOpenAICompatibleConversationTurn(messages) {
|
|
|
2036
1691
|
});
|
|
2037
1692
|
}
|
|
2038
1693
|
//#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
1694
|
//#region packages/ai/src/transports/openai-completions-string-content.ts
|
|
2096
1695
|
/**
|
|
2097
1696
|
* OpenAI Chat Completions compatibility helpers. Some providers only accept
|
|
@@ -2135,7 +1734,7 @@ function stripCompletionMessagesToRoleContent(messages) {
|
|
|
2135
1734
|
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
2136
1735
|
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
2137
1736
|
const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
2138
|
-
function readToolPayloadField
|
|
1737
|
+
function readToolPayloadField(record, field) {
|
|
2139
1738
|
try {
|
|
2140
1739
|
return record[field];
|
|
2141
1740
|
} catch {
|
|
@@ -2144,11 +1743,11 @@ function readToolPayloadField$1(record, field) {
|
|
|
2144
1743
|
}
|
|
2145
1744
|
function transportPayloadToolName(tool) {
|
|
2146
1745
|
if (!isRecord(tool)) return;
|
|
2147
|
-
const name = readToolPayloadField
|
|
1746
|
+
const name = readToolPayloadField(tool, "name");
|
|
2148
1747
|
if (typeof name === "string") return name;
|
|
2149
|
-
const fn = readToolPayloadField
|
|
1748
|
+
const fn = readToolPayloadField(tool, "function");
|
|
2150
1749
|
if (!isRecord(fn)) return;
|
|
2151
|
-
const fnName = readToolPayloadField
|
|
1750
|
+
const fnName = readToolPayloadField(fn, "name");
|
|
2152
1751
|
return typeof fnName === "string" ? fnName : void 0;
|
|
2153
1752
|
}
|
|
2154
1753
|
function enforceCodeModeResponsesToolSurface(payload) {
|
|
@@ -2248,7 +1847,7 @@ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, se
|
|
|
2248
1847
|
callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
|
|
2249
1848
|
precedence: "caller-wins"
|
|
2250
1849
|
}).headers ?? {};
|
|
2251
|
-
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = sessionId;
|
|
1850
|
+
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
|
|
2252
1851
|
return resolvedHeaders;
|
|
2253
1852
|
}
|
|
2254
1853
|
function resolveOpenAISdkTimeoutMs(model) {
|
|
@@ -2268,114 +1867,20 @@ function buildOpenAISdkRequestOptions(model, signal, options) {
|
|
|
2268
1867
|
...timeout !== void 0 ? { timeout } : {}
|
|
2269
1868
|
};
|
|
2270
1869
|
}
|
|
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
|
|
2290
|
-
};
|
|
2291
|
-
}
|
|
2292
1870
|
function getCompat(model) {
|
|
2293
|
-
const
|
|
1871
|
+
const resolved = resolveOpenAICompletionsCompat(model);
|
|
2294
1872
|
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
1873
|
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,
|
|
1874
|
+
...resolved,
|
|
1875
|
+
cacheControlFormat: resolved.cacheControlFormat,
|
|
1876
|
+
reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, {}),
|
|
1877
|
+
openRouterRouting: resolved.openRouterRouting ?? {},
|
|
1878
|
+
vercelGatewayRouting: resolved.vercelGatewayRouting,
|
|
2313
1879
|
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
|
|
1880
|
+
strictMessageKeys: compat.strictMessageKeys === true
|
|
2318
1881
|
};
|
|
2319
1882
|
}
|
|
2320
1883
|
//#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
1884
|
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
2380
1885
|
function hasToolHistory(messages) {
|
|
2381
1886
|
return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
|
|
@@ -3288,7 +2793,7 @@ function convertTools(tools, compat, model) {
|
|
|
3288
2793
|
});
|
|
3289
2794
|
return {
|
|
3290
2795
|
projection,
|
|
3291
|
-
tools:
|
|
2796
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
3292
2797
|
const functionTool = {
|
|
3293
2798
|
name: tool.name,
|
|
3294
2799
|
description: tool.description,
|
|
@@ -3485,7 +2990,12 @@ function buildOpenAICompletionsParams(model, context, options) {
|
|
|
3485
2990
|
}
|
|
3486
2991
|
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
3487
2992
|
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
3488
|
-
|
|
2993
|
+
const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
2994
|
+
provider: model.provider,
|
|
2995
|
+
baseUrl: model.baseUrl,
|
|
2996
|
+
hasTools: () => Boolean(context.tools?.length)
|
|
2997
|
+
}) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
2998
|
+
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
3489
2999
|
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
3490
3000
|
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
3491
3001
|
if (options?.seed !== void 0) params.seed = options.seed;
|
|
@@ -3784,1443 +3294,96 @@ function applyOpenAIResponsesPayloadPolicy(payloadObj, policy) {
|
|
|
3784
3294
|
if (policy.shouldStripInputStatus) stripInputItemStatuses(payloadObj.input);
|
|
3785
3295
|
}
|
|
3786
3296
|
//#endregion
|
|
3787
|
-
//#region packages/ai/src/transports/openai-responses-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3297
|
+
//#region packages/ai/src/transports/openai-responses-params-internal.ts
|
|
3298
|
+
function convertResponsesTools(tools, model, options) {
|
|
3299
|
+
const projection = projectOpenAITools(tools);
|
|
3300
|
+
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
|
|
3301
|
+
transport: "responses",
|
|
3302
|
+
model
|
|
3303
|
+
});
|
|
3304
|
+
return {
|
|
3305
|
+
projection,
|
|
3306
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
3307
|
+
const result = {
|
|
3308
|
+
type: "function",
|
|
3309
|
+
name: tool.name,
|
|
3310
|
+
description: tool.description,
|
|
3311
|
+
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
3312
|
+
};
|
|
3313
|
+
if (strict !== void 0) result.strict = strict;
|
|
3314
|
+
return result;
|
|
3315
|
+
})
|
|
3316
|
+
};
|
|
3793
3317
|
}
|
|
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")
|
|
3318
|
+
function getPromptCacheRetention(baseUrl, cacheRetention) {
|
|
3319
|
+
if (cacheRetention !== "long") return;
|
|
3320
|
+
return baseUrl?.includes("api.openai.com") ? "24h" : void 0;
|
|
3321
|
+
}
|
|
3322
|
+
function resolveOpenAIReasoningEffort(options) {
|
|
3323
|
+
return normalizeOpenAIReasoningEffort(options?.reasoningEffort ?? options?.reasoning ?? "high");
|
|
3324
|
+
}
|
|
3325
|
+
function hasResponsesWebSearchTool(tools) {
|
|
3326
|
+
if (!Array.isArray(tools)) return false;
|
|
3327
|
+
return tools.some((tool) => {
|
|
3328
|
+
if (!isRecord(tool)) return false;
|
|
3329
|
+
if (tool.type === "web_search") return true;
|
|
3330
|
+
if (tool.type === "function" && tool.name === "web_search") return true;
|
|
3331
|
+
const fn = tool.function;
|
|
3332
|
+
return isRecord(fn) && fn.name === "web_search";
|
|
3826
3333
|
});
|
|
3827
|
-
output.stopReason = terminal.stopReason;
|
|
3828
|
-
if (terminal.errorMessage) output.errorMessage = terminal.errorMessage;
|
|
3829
3334
|
}
|
|
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
|
|
3335
|
+
function raiseMinimalReasoningForResponsesWebSearch(params) {
|
|
3336
|
+
if (params.effort !== "minimal" || !hasResponsesWebSearchTool(params.tools)) return params.effort;
|
|
3337
|
+
for (const effort of [
|
|
3338
|
+
"low",
|
|
3339
|
+
"medium",
|
|
3340
|
+
"high"
|
|
3341
|
+
]) {
|
|
3342
|
+
const resolved = resolveOpenAIReasoningEffortForModel({
|
|
3343
|
+
model: params.model,
|
|
3344
|
+
effort
|
|
3345
|
+
});
|
|
3346
|
+
if (resolved && resolved !== "none" && resolved !== "minimal") return resolved;
|
|
3854
3347
|
}
|
|
3348
|
+
return params.effort;
|
|
3349
|
+
}
|
|
3350
|
+
const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
|
|
3351
|
+
"max_output_tokens",
|
|
3352
|
+
"metadata",
|
|
3353
|
+
"prompt_cache_retention",
|
|
3354
|
+
"service_tier",
|
|
3355
|
+
"temperature",
|
|
3356
|
+
"top_p"
|
|
3855
3357
|
];
|
|
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;
|
|
3358
|
+
function stripOpenAICodexResponsesUnsupportedTextFields(params) {
|
|
3359
|
+
const text = params.text;
|
|
3360
|
+
if (!text || typeof text !== "object" || Array.isArray(text)) return;
|
|
3361
|
+
const sanitizedText = { ...text };
|
|
3362
|
+
delete sanitizedText.format;
|
|
3363
|
+
if (Object.keys(sanitizedText).length > 0) params.text = sanitizedText;
|
|
3364
|
+
else delete params.text;
|
|
3876
3365
|
}
|
|
3877
|
-
function
|
|
3878
|
-
if (
|
|
3879
|
-
const
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
|
|
3366
|
+
function sanitizeOpenAICodexResponsesParams(model, params) {
|
|
3367
|
+
if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
|
|
3368
|
+
for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
|
|
3369
|
+
stripOpenAICodexResponsesUnsupportedTextFields(params);
|
|
3370
|
+
return params;
|
|
3883
3371
|
}
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
return
|
|
3372
|
+
function buildOpenAICodexResponsesInstructions(context) {
|
|
3373
|
+
if (!context.systemPrompt) return;
|
|
3374
|
+
return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
|
|
3887
3375
|
}
|
|
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;
|
|
3376
|
+
function resolveOpenAICodexResponsesInstructions(model, context) {
|
|
3377
|
+
const instructions = buildOpenAICodexResponsesInstructions(context);
|
|
3378
|
+
if (instructions && instructions.trim().length > 0) return instructions;
|
|
3379
|
+
return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
|
|
5217
3380
|
}
|
|
5218
3381
|
function ensureOpenAICodexResponsesInput(messages, context) {
|
|
5219
3382
|
if (messages.length > 0 || !context.systemPrompt) return;
|
|
5220
3383
|
if (!buildOpenAICodexResponsesInstructions(context)) throw new Error("OpenAI Codex Responses requires non-empty input when only systemPrompt is provided.");
|
|
5221
3384
|
messages.push(buildResponsesInputMessage("user", [{
|
|
5222
3385
|
type: "input_text",
|
|
5223
|
-
text:
|
|
3386
|
+
text: " "
|
|
5224
3387
|
}]));
|
|
5225
3388
|
}
|
|
5226
3389
|
function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
@@ -5309,7 +3472,180 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
|
|
|
5309
3472
|
applyOpenAIResponsesPayloadPolicy(params, payloadPolicy);
|
|
5310
3473
|
return sanitizeOpenAICodexResponsesParams(model, params);
|
|
5311
3474
|
}
|
|
5312
|
-
|
|
3475
|
+
//#endregion
|
|
3476
|
+
//#region packages/media-core/src/inline-image-data-url.ts
|
|
3477
|
+
/** Prefix used to distinguish inline data URLs from remote/local image references. */
|
|
3478
|
+
const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
|
|
3479
|
+
const IMAGE_SIGNATURES = [
|
|
3480
|
+
{
|
|
3481
|
+
mime: "image/png",
|
|
3482
|
+
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
|
|
3483
|
+
},
|
|
3484
|
+
{
|
|
3485
|
+
mime: "image/jpeg",
|
|
3486
|
+
matches: (buffer) => buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255
|
|
3487
|
+
},
|
|
3488
|
+
{
|
|
3489
|
+
mime: "image/webp",
|
|
3490
|
+
matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
3491
|
+
},
|
|
3492
|
+
{
|
|
3493
|
+
mime: "image/gif",
|
|
3494
|
+
matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
|
|
3495
|
+
},
|
|
3496
|
+
{
|
|
3497
|
+
mime: "image/bmp",
|
|
3498
|
+
matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
|
|
3499
|
+
}
|
|
3500
|
+
];
|
|
3501
|
+
const HEIC_BRANDS = /* @__PURE__ */ new Set([
|
|
3502
|
+
"heic",
|
|
3503
|
+
"heix",
|
|
3504
|
+
"hevc",
|
|
3505
|
+
"hevx",
|
|
3506
|
+
"heis",
|
|
3507
|
+
"heim",
|
|
3508
|
+
"hevm",
|
|
3509
|
+
"hevs"
|
|
3510
|
+
]);
|
|
3511
|
+
const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
|
|
3512
|
+
const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
|
|
3513
|
+
const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
|
|
3514
|
+
"image/png",
|
|
3515
|
+
"image/jpeg",
|
|
3516
|
+
"image/webp",
|
|
3517
|
+
"image/gif"
|
|
3518
|
+
]);
|
|
3519
|
+
function startsWithDataUrl(value) {
|
|
3520
|
+
return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
|
|
3521
|
+
}
|
|
3522
|
+
function sniffIsoBmffImageMime(buffer) {
|
|
3523
|
+
if (buffer.length < 12 || buffer.subarray(4, 8).toString("ascii") !== "ftyp") return;
|
|
3524
|
+
const brands = [buffer.subarray(8, 12).toString("ascii")];
|
|
3525
|
+
for (let offset = 16; offset + 4 <= buffer.length; offset += 4) brands.push(buffer.subarray(offset, offset + 4).toString("ascii"));
|
|
3526
|
+
if (brands.some((brand) => HEIC_BRANDS.has(brand))) return "image/heic";
|
|
3527
|
+
if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
|
|
3528
|
+
}
|
|
3529
|
+
/** Sniffs supported inline image formats from decoded bytes. */
|
|
3530
|
+
function sniffInlineImageMime(buffer) {
|
|
3531
|
+
return IMAGE_SIGNATURES.find((signature) => signature.matches(buffer))?.mime ?? sniffIsoBmffImageMime(buffer);
|
|
3532
|
+
}
|
|
3533
|
+
function isImageMimeType(value) {
|
|
3534
|
+
return value.trim().toLowerCase().startsWith("image/");
|
|
3535
|
+
}
|
|
3536
|
+
/** Canonicalizes trusted inline image base64 and rejects malformed or non-image payloads. */
|
|
3537
|
+
function sanitizeInlineImageBase64(params) {
|
|
3538
|
+
if (!isImageMimeType(params.mimeType)) return;
|
|
3539
|
+
const canonicalPayload = canonicalizeBase64(params.base64);
|
|
3540
|
+
if (!canonicalPayload) return;
|
|
3541
|
+
const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
|
|
3542
|
+
if (!sniffedMimeType) return;
|
|
3543
|
+
return {
|
|
3544
|
+
mimeType: sniffedMimeType,
|
|
3545
|
+
base64: canonicalPayload
|
|
3546
|
+
};
|
|
3547
|
+
}
|
|
3548
|
+
function parseInlineImageDataUrl(value) {
|
|
3549
|
+
if (!startsWithDataUrl(value)) return {
|
|
3550
|
+
metadata: [],
|
|
3551
|
+
payload: value
|
|
3552
|
+
};
|
|
3553
|
+
const commaIndex = value.indexOf(",");
|
|
3554
|
+
if (commaIndex < 0) return;
|
|
3555
|
+
return {
|
|
3556
|
+
metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
|
|
3557
|
+
payload: value.slice(commaIndex + 1)
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
function metadataAllowsImageBase64(metadata) {
|
|
3561
|
+
const [mimeType, ...options] = metadata;
|
|
3562
|
+
return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
|
|
3563
|
+
}
|
|
3564
|
+
function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
|
|
3565
|
+
const parsed = parseInlineImageDataUrl(imageUrl);
|
|
3566
|
+
if (!parsed) return;
|
|
3567
|
+
if (parsed.metadata.length === 0) return imageUrl;
|
|
3568
|
+
if (!metadataAllowsImageBase64(parsed.metadata)) return;
|
|
3569
|
+
const [mimeType] = parsed.metadata;
|
|
3570
|
+
const sanitized = sanitizeInlineImageBase64({
|
|
3571
|
+
mimeType: mimeType ?? "",
|
|
3572
|
+
base64: parsed.payload
|
|
3573
|
+
});
|
|
3574
|
+
if (!sanitized) return;
|
|
3575
|
+
if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
|
|
3576
|
+
return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
|
|
3577
|
+
}
|
|
3578
|
+
/** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
|
|
3579
|
+
function sanitizeInlineImageDataUrl(imageUrl) {
|
|
3580
|
+
return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
|
|
3581
|
+
}
|
|
3582
|
+
//#endregion
|
|
3583
|
+
//#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
|
|
3584
|
+
/**
|
|
3585
|
+
* Sanitizes OpenAI Responses payloads before transport. Invalid inline images
|
|
3586
|
+
* are replaced with text placeholders so the request remains valid and
|
|
3587
|
+
* auditable.
|
|
3588
|
+
*/
|
|
3589
|
+
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
|
|
3590
|
+
function invalidSnakeImage() {
|
|
3591
|
+
return {
|
|
3592
|
+
type: "input_text",
|
|
3593
|
+
text: `[${IMAGE_OMITTED_TEXT}]`
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
function sanitizeValue(value) {
|
|
3597
|
+
if (Array.isArray(value)) return value.map(sanitizeValue);
|
|
3598
|
+
if (!isRecord(value)) return value;
|
|
3599
|
+
if (value.type === "input_image" && typeof value.image_url === "string") {
|
|
3600
|
+
const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
|
|
3601
|
+
return imageUrl ? {
|
|
3602
|
+
...value,
|
|
3603
|
+
image_url: imageUrl
|
|
3604
|
+
} : invalidSnakeImage();
|
|
3605
|
+
}
|
|
3606
|
+
const next = {};
|
|
3607
|
+
for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
|
|
3608
|
+
return next;
|
|
3609
|
+
}
|
|
3610
|
+
/** Sanitize inline image fields inside a Responses API payload. */
|
|
3611
|
+
function sanitizeResponsesImagePayload(params) {
|
|
3612
|
+
if (!Array.isArray(params.input)) return params;
|
|
3613
|
+
return {
|
|
3614
|
+
...params,
|
|
3615
|
+
input: sanitizeValue(params.input)
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
//#endregion
|
|
3619
|
+
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
3620
|
+
function resolveProviderTransportTurnState(model, params) {
|
|
3621
|
+
const normalizedProvider = model.provider.trim().toLowerCase();
|
|
3622
|
+
const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
|
|
3623
|
+
return getAiTransportHost().plugin.resolveTransportTurnState({
|
|
3624
|
+
provider: model.provider,
|
|
3625
|
+
modelId: model.id,
|
|
3626
|
+
allowRuntimePluginLoad,
|
|
3627
|
+
context: {
|
|
3628
|
+
provider: model.provider,
|
|
3629
|
+
modelId: model.id,
|
|
3630
|
+
model,
|
|
3631
|
+
sessionId: params.sessionId,
|
|
3632
|
+
turnId: params.turnId,
|
|
3633
|
+
attempt: params.attempt,
|
|
3634
|
+
transport: params.transport
|
|
3635
|
+
}
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
|
|
3639
|
+
return new OpenAI({
|
|
3640
|
+
apiKey,
|
|
3641
|
+
baseURL: model.baseUrl,
|
|
3642
|
+
dangerouslyAllowBrowser: true,
|
|
3643
|
+
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
|
|
3644
|
+
fetch: buildGuardedModelFetch(model),
|
|
3645
|
+
...buildOpenAISdkClientOptions(model)
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
function createResponsesTransportExecutor(config) {
|
|
5313
3649
|
return (model, context, options) => {
|
|
5314
3650
|
const responsesOptions = options;
|
|
5315
3651
|
const eventStream = createAssistantMessageEventStream();
|
|
@@ -5318,7 +3654,7 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5318
3654
|
const output = {
|
|
5319
3655
|
role: "assistant",
|
|
5320
3656
|
content: [],
|
|
5321
|
-
api:
|
|
3657
|
+
api: config.outputApi ?? model.api,
|
|
5322
3658
|
provider: model.provider,
|
|
5323
3659
|
model: model.id,
|
|
5324
3660
|
usage: {
|
|
@@ -5347,9 +3683,8 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5347
3683
|
attempt: 1,
|
|
5348
3684
|
transport: "stream"
|
|
5349
3685
|
});
|
|
5350
|
-
const client =
|
|
5351
|
-
|
|
5352
|
-
let params = buildAzureOpenAIResponsesParams(model, context, responsesOptions, deploymentName, turnState?.metadata);
|
|
3686
|
+
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
|
|
3687
|
+
let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata);
|
|
5353
3688
|
const nextParams = await options?.onPayload?.(params, model);
|
|
5354
3689
|
if (nextParams !== void 0) params = nextParams;
|
|
5355
3690
|
if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
|
|
@@ -5361,23 +3696,31 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5361
3696
|
}
|
|
5362
3697
|
const requestStartedAt = Date.now();
|
|
5363
3698
|
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
5364
|
-
const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal);
|
|
3699
|
+
const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, config.streamRequest ? { stream: true } : void 0);
|
|
5365
3700
|
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
|
|
3701
|
+
const responseStream = await config.createResponseStream({
|
|
3702
|
+
client,
|
|
3703
|
+
request: params,
|
|
3704
|
+
requestOptions,
|
|
3705
|
+
model
|
|
3706
|
+
});
|
|
5367
3707
|
emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
|
|
5368
3708
|
stream.push({
|
|
5369
3709
|
type: "start",
|
|
5370
3710
|
partial: output
|
|
5371
3711
|
});
|
|
5372
|
-
await processResponsesStream(responseStream, output, stream, model, {
|
|
5373
|
-
|
|
3712
|
+
await processResponsesStream(observeResponsesStream(responseStream, model), output, stream, model, {
|
|
3713
|
+
...config.pricingOptions?.(responsesOptions),
|
|
3714
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
|
|
5374
3715
|
abortFirstEventStream: firstEventAbort.abort,
|
|
5375
3716
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
5376
3717
|
signal: options?.signal,
|
|
5377
|
-
|
|
5378
|
-
|
|
3718
|
+
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
3719
|
+
authProfileId: responsesOptions?.authProfileId,
|
|
3720
|
+
sessionId: options?.sessionId
|
|
3721
|
+
})
|
|
5379
3722
|
});
|
|
5380
|
-
if (options?.signal?.aborted) throw
|
|
3723
|
+
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
5381
3724
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
|
|
5382
3725
|
stream.push({
|
|
5383
3726
|
type: "done",
|
|
@@ -5386,6 +3729,7 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5386
3729
|
});
|
|
5387
3730
|
stream.end();
|
|
5388
3731
|
} catch (error) {
|
|
3732
|
+
if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
|
|
5389
3733
|
log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
|
|
5390
3734
|
assignTransportErrorDetails(output, error, options?.signal);
|
|
5391
3735
|
stream.push({
|
|
@@ -5401,6 +3745,27 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
5401
3745
|
return eventStream;
|
|
5402
3746
|
};
|
|
5403
3747
|
}
|
|
3748
|
+
function createOpenAIResponsesTransportStreamFn() {
|
|
3749
|
+
return createResponsesTransportExecutor({
|
|
3750
|
+
streamRequest: true,
|
|
3751
|
+
createClient: createOpenAIResponsesClient,
|
|
3752
|
+
buildRequest: buildOpenAIResponsesParams,
|
|
3753
|
+
createResponseStream: createResponsesStreamWithEncryptedContentRetry,
|
|
3754
|
+
pricingOptions: (options) => ({
|
|
3755
|
+
serviceTier: options?.serviceTier,
|
|
3756
|
+
applyServiceTierPricing
|
|
3757
|
+
})
|
|
3758
|
+
});
|
|
3759
|
+
}
|
|
3760
|
+
function createAzureOpenAIResponsesTransportStreamFn() {
|
|
3761
|
+
return createResponsesTransportExecutor({
|
|
3762
|
+
outputApi: "azure-openai-responses",
|
|
3763
|
+
firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
|
|
3764
|
+
createClient: createAzureOpenAIClient,
|
|
3765
|
+
buildRequest: (model, context, options, metadata) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata),
|
|
3766
|
+
createResponseStream: async ({ client, request, requestOptions }) => await client.responses.create(request, requestOptions)
|
|
3767
|
+
});
|
|
3768
|
+
}
|
|
5404
3769
|
function normalizeAzureBaseUrl(baseUrl) {
|
|
5405
3770
|
return baseUrl.replace(/\/+$/, "");
|
|
5406
3771
|
}
|
|
@@ -5432,6 +3797,9 @@ function buildAzureOpenAIResponsesParams(model, context, options, deploymentName
|
|
|
5432
3797
|
delete params.store;
|
|
5433
3798
|
return params;
|
|
5434
3799
|
}
|
|
3800
|
+
//#endregion
|
|
3801
|
+
//#region packages/ai/src/transports/openai-responses-transport.ts
|
|
3802
|
+
/** OpenAI Responses transport facade. */
|
|
5435
3803
|
const responsesTesting = {
|
|
5436
3804
|
getCompat,
|
|
5437
3805
|
assertCodeModeResponsesToolSurface,
|
|
@@ -5698,4 +4066,4 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
5698
4066
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
5699
4067
|
}
|
|
5700
4068
|
//#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, resolveCacheRetention, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, usesNativeOpenAICodexResponsesBackend };
|
|
4069
|
+
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, isCodeModeModelVisibleToolName, isOpenAICodexResponsesModel, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCacheRetention, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend };
|