@openclaw/ai 2026.8.1-beta.2 → 2026.8.1
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-B6dLpq5L.mjs → anthropic-Djj7L1rx.mjs} +49 -39
- package/dist/{anthropic-compaction-replay-8lJNKXOE.mjs → anthropic-compaction-replay-CY4tkD4V.mjs} +42 -44
- package/dist/{anthropic-payload-policy-CiEuQS72.d.mts → anthropic-payload-policy-i_5_inWe.d.mts} +4 -2
- package/dist/{api-registry-k3zTz0cV.d.mts → api-registry-C9-wZd_W.d.mts} +2 -1
- package/dist/{azure-openai-responses-mxIOtUnn.mjs → azure-openai-responses-Bj3czHNa.mjs} +4 -4
- package/dist/{number-coercion-H9qHik3g.mjs → base64-CEFBpSkN.mjs} +73 -1
- package/dist/diagnostics.d.mts +2 -3
- package/dist/diagnostics.mjs +1 -1
- package/dist/{env-api-keys-DrgeBuva.mjs → env-api-keys-bktO00EJ.mjs} +2 -15
- package/dist/event-stream-C988TSLe.d.mts +1 -0
- package/dist/{event-stream-BP6AWT8j.d.mts → event-stream-UhnxHuqt.d.mts} +1 -2
- package/dist/event-stream.d.mts +2 -1
- package/dist/{google-C7h2QzDX.mjs → google-0Ca6TYbw.mjs} +4 -4
- package/dist/{google-shared-B0Qr9OR7.mjs → google-shared-DVfL9COU.mjs} +22 -8
- package/dist/{google-vertex-Bhc3TjDl.mjs → google-vertex-5dAMxprN.mjs} +4 -4
- package/dist/{host-DTqNc7ad.mjs → host-D51fmkH6.mjs} +40 -33
- package/dist/{host-vWgMMhiJ.d.mts → host-DoPJJ1KD.d.mts} +7 -7
- package/dist/index-N3zr6__t.d.mts +105 -0
- package/dist/index.d.mts +9 -10
- package/dist/index.mjs +2 -3
- package/dist/internal/anthropic.d.mts +57 -49
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/openai-responses-payload-policy.d.mts +3 -3
- package/dist/internal/openai-responses-payload-policy.mjs +2 -3
- package/dist/internal/openai.d.mts +65 -63
- package/dist/internal/openai.mjs +10 -8
- package/dist/internal/retry-after.d.mts +2 -3
- package/dist/internal/runtime.d.mts +53 -40
- package/dist/internal/runtime.mjs +10 -9
- package/dist/internal/shared.d.mts +37 -23
- package/dist/internal/shared.mjs +4 -4
- package/dist/{json-parse-CDnesDM_.mjs → json-parse-BuAJEbdW.mjs} +18 -2
- package/dist/{headers-DdOQtGuU.mjs → llm-request-activity-BjtkplhG.mjs} +1 -9
- package/dist/{mistral-CEWoQI_g.mjs → mistral-BCnrI-kV.mjs} +64 -53
- package/dist/{openai-chatgpt-responses-CrPmqERt.mjs → openai-chatgpt-responses-CGBGWSqr.mjs} +45 -20
- package/dist/{openai-completions-BPnt4Sml.mjs → openai-completions-BaTuwID7.mjs} +63 -258
- package/dist/{openai-completions-compat-Dt3dcawL.d.mts → openai-completions-compat-DB7FFq7k.d.mts} +3 -3
- package/dist/openai-completions-stream-Bpns6uNc.mjs +1622 -0
- package/dist/{openai-responses-DhIKtOup.mjs → openai-responses-1GBcQujk.mjs} +8 -6
- package/dist/openai-responses-compaction-window-DwydGZI8.mjs +686 -0
- package/dist/{openai-responses-contracts-XpZJxrRG.d.mts → openai-responses-contracts-DgaR3t5T.d.mts} +14 -5
- package/dist/{openai-responses-payload-policy-BSs371VM.d.mts → openai-responses-payload-policy-rLRPsSmB.d.mts} +2 -0
- package/dist/{openai-responses-prompt-observer-internal-DgNTYnRY.mjs → openai-responses-prompt-observer-internal-_nhGTn2b.mjs} +3 -3
- package/dist/{openai-responses-shared-DXIt3iY5.mjs → openai-responses-shared-zPmKnhF5.mjs} +345 -1553
- package/dist/openai-stop-reason-Drnn_6Qj.mjs +28 -0
- package/dist/openai-tool-schema-Dgl4H2MU.mjs +1695 -0
- package/dist/{provider-error-BUwEnjXq.mjs → provider-error-jZP5NMTZ.mjs} +3 -82
- package/dist/{provider-options-B96RdNpH.d.mts → provider-options-HvA_slFa.d.mts} +23 -4
- package/dist/provider-transcript-transform-C01bQ5a3.mjs +32 -0
- package/dist/provider-types.d.mts +18 -17
- package/dist/providers.d.mts +5 -6
- package/dist/providers.mjs +9 -9
- package/dist/{reasoning-tag-text-partitioner-rnPwX2pg.mjs → reasoning-tag-text-partitioner-CwzKb0bA.mjs} +4 -1
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{sanitize-unicode-BYqrYtC_.mjs → sanitize-unicode-DCkltN94.mjs} +1 -2
- package/dist/{simple-options-D58D5Kvw.mjs → simple-options-DKx86yX9.mjs} +42 -4
- package/dist/{anthropic-JsNA5KCu.mjs → src-Bu3FiFAG.mjs} +1 -1
- package/dist/{stream-first-event-timeout-MK28puvq.mjs → stream-first-event-timeout-DcNjoFQE.mjs} +1 -1
- package/dist/{streaming-byte-guard-BrbkbwUu.mjs → streaming-byte-guard-CC-HMn_u.mjs} +5 -6
- package/dist/string-normalization--fwJ4S2q.mjs +16 -0
- package/dist/{tool-schema-json-projection-q5d7QX5c.mjs → tool-schema-json-projection-qvTEs5Am.mjs} +17 -8
- package/dist/transport-stream-shared-CT5ycdzn.mjs +342 -0
- package/dist/transport-stream-shared-NjwWNaRY.d.mts +138 -0
- package/dist/{transport-utils-DJqkxbhC.mjs → transport-utils-BHx9z8Cl.mjs} +9 -9
- package/dist/transports.d.mts +140 -169
- package/dist/transports.mjs +269 -1262
- package/dist/types-DtGu2d3C.d.mts +1 -0
- package/dist/{types-BHNrPS1l.d.mts → types-h38Azne1.d.mts} +53 -17
- package/dist/types.d.mts +6 -5
- package/dist/types.mjs +1 -2
- package/dist/utf16-slice-qz3nsy87.mjs +84 -0
- package/dist/{validation-DT9SrFn3.d.mts → validation-C68lP2uy.d.mts} +1 -2
- package/dist/validation.d.mts +1 -1
- package/package.json +6 -6
- package/dist/anthropic-SrGtwsJu.d.mts +0 -100
- package/dist/index-BVVgDSdq.d.mts +0 -1
- package/dist/openai-responses-contracts-CyfIkQi5.mjs +0 -253
- package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +0 -206
- package/dist/openai-stop-reason-BkFkqqK0.mjs +0 -563
- package/dist/openai-tool-projection-CY04OcvQ.mjs +0 -338
- package/dist/provider-transcript-transform-ePx-Bbfr.mjs +0 -155
- package/dist/record-coerce-DdXsgUd_.mjs +0 -23
- package/dist/src-D2H6yKkH.mjs +0 -2
- package/dist/stream-first-event-timeout-DvDeSucC.d.mts +0 -29
- package/dist/string-coerce-fsri9iCu.mjs +0 -34
- package/dist/types-BVVgDSdq.d.mts +0 -1
- package/dist/utf16-slice-CvGodqok.mjs +0 -29
package/dist/transports.mjs
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { r as
|
|
4
|
-
import { n as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { a as
|
|
8
|
-
import {
|
|
9
|
-
import { n as
|
|
10
|
-
import { r as asNonNegativeFiniteNumber } from "./number-coercion-H9qHik3g.mjs";
|
|
11
|
-
import { _ as isOpenAIGpt56Model, c as OpenAIResponsesWebSocketPostDispatchError, d as OpenAIResponsesWebSocketSafeRetryError, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel, i as OPENAI_RESPONSES_APIS, l as OpenAIResponsesWebSocketPreDispatchError, p as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketResponseFailedError, v as normalizeOpenAIReasoningEffort, w as uniqueStrings, y as resolveOpenAIReasoningEffortForModel } from "./openai-responses-contracts-CyfIkQi5.mjs";
|
|
12
|
-
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
1
|
+
import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./src-Bu3FiFAG.mjs";
|
|
2
|
+
import { c as normalizeLowercaseStringOrEmpty, o as isRecord, s as hasNonEmptyString } from "./utf16-slice-qz3nsy87.mjs";
|
|
3
|
+
import { r as calculateCost } from "./sanitize-unicode-DCkltN94.mjs";
|
|
4
|
+
import { S as usesClaudeStreamingRefusalContract, _ as prepareClaudeNoPrefillRequestContext, a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload, f as ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK, g as mapAnthropicStopReason, h as defaultsClaudeAdaptiveThinking, m as applyClaudeRequestContract, n as getAiTransportHost, p as ANTHROPIC_CLAUDE_CODE_VERSION, r as resolveAiTransportHeaderSentinels, s as extractToolResultBlockText, v as requiresClaudeAdaptiveThinking, x as usesClaudeFable5MessagesContract, y as resolveAnthropicThinkingEffort } from "./host-D51fmkH6.mjs";
|
|
5
|
+
import { i as stableStringify } from "./provider-error-jZP5NMTZ.mjs";
|
|
6
|
+
import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-_nhGTn2b.mjs";
|
|
7
|
+
import { a as asNonNegativeFiniteNumber } from "./base64-CEFBpSkN.mjs";
|
|
8
|
+
import { n as uniqueStrings } from "./string-normalization--fwJ4S2q.mjs";
|
|
9
|
+
import { A as applyAnthropicCacheControlToMessages, C as applyAnthropicFallbackBoundary, D as createAnthropicInlineImageBudget, E as applyAnthropicRefusal, F as resolveAnthropicServerCompactionPlan, I as isAnthropicOAuthApiKey, L as omitFoundryBearerCredentialHeaders, M as applyAnthropicPayloadPolicyToParams, N as resolveAnthropicEphemeralCacheControl, O as normalizeAnthropicInlineContent, P as resolveAnthropicPayloadPolicy, R as usesFoundryBearerAuth, T as resolveAnthropicFallbackServingModelCost, _ as toClaudeCodeToolName, a as suppressAnthropicCompaction, b as ANTHROPIC_SERVER_SIDE_FALLBACKS, f as normalizeAnthropicToolCallId, g as resolveOriginalAnthropicToolName, h as reconcileAnthropicToolChoice, i as resolveNewestAnthropicCompaction, j as applyAnthropicEphemeralCacheControlMarkers, k as resolveAnthropicImageMediaType, m as projectAnthropicTools, n as createCompactionCapture, o as applyAnthropicMessageDeltaUsage, p as normalizeAnthropicToolChoice, r as isAnthropicReplayRejection, s as applyAnthropicMessageStartUsage, t as buildAnthropicReplayPlan, v as ANTHROPIC_OMITTED_REASONING_TEXT, w as readAnthropicFallbackBoundary, x as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, y as findActiveAnthropicToolTurnAssistantIndex } from "./anthropic-compaction-replay-CY4tkD4V.mjs";
|
|
13
10
|
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
11
|
+
import { a as codeModeToolSurfaceObserver, d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, o as reasoningTagTextPolicy, t as adjustMaxTokensForThinking } from "./simple-options-DKx86yX9.mjs";
|
|
12
|
+
import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch } from "./tool-schema-json-projection-qvTEs5Am.mjs";
|
|
13
|
+
import { a as isGoogleGemini3FlashModel, c as parseRetryAfterSeconds, f as resolveModelHeaderSentinels$1, i as isCodeModeModelVisibleToolName, l as readResponseTextSnippet, m as supportsModelTools, n as createAbortError$1, o as isGoogleGemini3ProModel, p as sha256Hex, r as estimateStringChars, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, u as redactIdentifier } from "./transport-utils-BHx9z8Cl.mjs";
|
|
14
|
+
import { n as getEnvApiKey } from "./env-api-keys-bktO00EJ.mjs";
|
|
15
|
+
import { S as quoteUnsafeIntegerLiterals, _ as transportAbortError, a as createWritableTransportEventStream, b as parseJsonObjectPreservingUnsafeIntegers, c as finalizeTransportStream, d as notifyProviderHttpMetadata, f as notifyProviderHttpResponse, g as sanitizeTransportPayloadText, h as sanitizeNonEmptyTransportPayloadText, i as createEmptyTransportUsage, l as mergeTransportHeaders, m as parseTerminalToolCallArguments, n as coerceTransportToolCallArguments, o as failTransportStream, p as notifyProviderStreamOpened, r as copyProviderAcceptanceObserver, s as finalizeTerminalToolCallArguments, t as assignTransportErrorDetails, u as mergeTransportMetadata, v as withProviderAcceptanceObserver, x as parseJsonPreservingUnsafeIntegers, y as withProviderResponseHook } from "./transport-stream-shared-CT5ycdzn.mjs";
|
|
16
|
+
import { C as createDeepSeekTextFilter, E as tagUnresolvedTextAsCommentary, S as shouldOmitOllamaCompatResponseFormat, T as tagPendingCommentaryText, _ as hasToolCallHistory, a as buildOpenAISdkClientOptions, b as resolveOpenAICompletionsCompat, c as filterCodeModePayloadTools, d as readCodeModePayloadToolName, f as resolveCodeModeResponsesVisibleToolNames, g as convertMessages, h as resolveOpenAIReasoningEffortMap, i as buildOpenAIClientHeaders, l as getCompat, m as usesNativeOpenAICodexResponsesBackend, n as shouldEmitOpenAICompletionsReasoning, o as buildOpenAISdkRequestOptions, p as resolveOpenAIStrictToolFlagWithDiagnostics, r as assertCodeModeResponsesToolSurface, s as enforceCodeModeResponsesToolSurface, t as processCompletionsStream, u as isOpenAICodexResponsesModel, v as finalizeOpenAICompletionsToolCalls, x as resolveOpenAICompletionsResponseFormat, y as detectOpenAICompletionsCompat } from "./openai-completions-stream-Bpns6uNc.mjs";
|
|
18
17
|
import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as suppressOpenAIResponsesCompaction, H as createOpenAIResponsesAssistantOutput, I as createResponsesStreamWithEncryptedContentRetry, K as resolveReplayableResponsesMessageId, L as isInvalidEncryptedContentError, R as resolveAzureOpenAIApiVersion, S as summarizeResponsesPayload, U as buildOpenAIResponsesReasoningReplayMetadata, V as convertResponsesMessages, W as captureOpenAIResponsesCompaction, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, _ as safeDebugValue, b as summarizeOpenAITransportError, c as processResponsesStream, dt as resolveModelPayloadDebugMode, f as observeResponsesStream, ft as resolveModelSseDebugMode, g as normalizeResponsesFailedEvent, h as logResponsesFailedNoDetails, ht as quoteUnsafeIntegerLiterals, m as buildResponsesFailedNoDetailsObservation, mt as parseJsonPreservingUnsafeIntegers, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, pt as parseJsonObjectPreservingUnsafeIntegers, q as findOpenAIStrictToolProjectionDiagnostics, ut as emitModelTransportDebug, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-DXIt3iY5.mjs";
|
|
22
|
-
import { a as createWritableTransportEventStream, c as mergeTransportHeaders, d as sanitizeTransportPayloadText, f as transportAbortError, i as createEmptyTransportUsage, l as mergeTransportMetadata, n as assignTransportErrorDetails, o as failTransportStream, p as withProviderResponseHook, r as coerceTransportToolCallArguments, s as finalizeTransportStream, u as sanitizeNonEmptyTransportPayloadText } from "./provider-transcript-transform-ePx-Bbfr.mjs";
|
|
23
|
-
import { a as isGoogleGemini3FlashModel, c as readResponseTextSnippet, d as resolveModelHeaderSentinels$1, f as resolveSecretSentinel, i as isCodeModeModelVisibleToolName, l as redactIdentifier, m as supportsModelTools, n as createAbortError$1, o as isGoogleGemini3ProModel, p as sha256Hex, r as estimateStringChars, s as parseRetryAfterSeconds, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "./transport-utils-DJqkxbhC.mjs";
|
|
18
|
+
import { r as parseStreamingJson, t as createToolArgumentPreviewSchedule } from "./json-parse-BuAJEbdW.mjs";
|
|
19
|
+
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
24
20
|
import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { t as
|
|
28
|
-
import {
|
|
21
|
+
import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as captureOpenAIResponsesCompaction, H as createOpenAIResponsesAssistantOutput, I as createResponsesStreamWithEncryptedContentRetry, J as resolveReplayableResponsesMessageId, K as resolveNewestOpenAIResponsesCompactionReplay, L as isInvalidEncryptedContentError, R as resolveAzureOpenAIApiVersion, S as summarizeResponsesPayload, U as CompactionReplayRefreshRequiredError, V as convertResponsesMessages, W as buildOpenAIResponsesReasoningReplayMetadata, X as resolveModelPayloadDebugMode, Y as emitModelTransportDebug, Z as resolveModelSseDebugMode, _ as safeDebugValue, b as summarizeOpenAITransportError, c as processResponsesStream, f as observeResponsesStream, g as normalizeResponsesFailedEvent, h as logResponsesFailedNoDetails, m as buildResponsesFailedNoDetailsObservation, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, q as suppressOpenAIResponsesCompaction, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-zPmKnhF5.mjs";
|
|
22
|
+
import { A as readOpenAICompletionsReasoningBatch, C as createOpenAIProviderAcceptanceHook, D as measureUtf8AppendBytes, E as log, M as resolvePromptCacheKey, N as throwIfModelStreamAborted, O as parseOpenAICompletionsUsage, S as createModelStreamCooperativeScheduler, T as isOpenAICompletionsThinkingEnabled, b as reconcileOpenAIResponsesToolChoice, j as resolveOpenAIClientBaseUrl, k as readOpenAICompletionsContentDeltas, r as normalizeOpenAIStrictToolParameters, v as projectOpenAITools, w as createOpenAIResponseHook, x as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, y as reconcileOpenAICompletionsToolChoice } from "./openai-tool-schema-Dgl4H2MU.mjs";
|
|
23
|
+
import { i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-DcNjoFQE.mjs";
|
|
24
|
+
import { C as isOpenAIGpt54MiniModel, D as resolveOpenAIReasoningEffortForModel, E as normalizeOpenAIReasoningEffort, T as isOpenAIGpt56Model, _ as OpenAIResponsesWebSocketPostDispatchError, a as applyOpenAIResponsesPayloadPolicy, c as resolveOpenAIResponsesServerCompactionPlan, d as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, f as OPENAI_RESPONSES_APIS, i as sanitizeResponsesImagePayload, l as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, n as isOpenAIResponsesCompactionOutput, o as resolveOpenAIResponsesCompactEndpointPlan, s as resolveOpenAIResponsesPayloadPolicy, t as createBoundedOpenAIResponsesCompactionFetch, v as OpenAIResponsesWebSocketPreDispatchError, w as isOpenAIGpt55Model, x as parseOpenAIResponsesWebSocketServerError, y as OpenAIResponsesWebSocketSafeRetryError } from "./openai-responses-compaction-window-DwydGZI8.mjs";
|
|
29
25
|
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
30
26
|
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
31
27
|
import { randomUUID } from "node:crypto";
|
|
@@ -62,12 +58,9 @@ function resolveAnthropicMessagesMaxTokens(params) {
|
|
|
62
58
|
const contextWindow = resolvePositiveAnthropicTokenLimit(params.modelContextWindow);
|
|
63
59
|
return contextWindow === void 0 ? ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS : Math.max(1, Math.min(ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS, Math.floor(contextWindow / ANTHROPIC_MESSAGES_FALLBACK_CONTEXT_DIVISOR)));
|
|
64
60
|
}
|
|
65
|
-
function isAnthropicOAuthToken(apiKey) {
|
|
66
|
-
return (resolveSecretSentinel(apiKey) ?? apiKey).includes("sk-ant-oat");
|
|
67
|
-
}
|
|
68
61
|
function isDirectAnthropicModel(model) {
|
|
69
62
|
if (normalizeLowercaseStringOrEmpty(model.provider) !== "anthropic") return false;
|
|
70
|
-
const endpointClass = resolveProviderEndpoint(model
|
|
63
|
+
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
71
64
|
return endpointClass === "default" || endpointClass === "anthropic-public";
|
|
72
65
|
}
|
|
73
66
|
function isKimiAnthropicProvider(provider) {
|
|
@@ -82,7 +75,7 @@ function useAnthropicServerSideFallback(model) {
|
|
|
82
75
|
return (usesClaudeFable5MessagesContract(model) || resolveClaudeOpus5ModelIdentity(model) !== void 0) && isDirectAnthropicModel(model);
|
|
83
76
|
}
|
|
84
77
|
function supportsReasoningContentReplay(model) {
|
|
85
|
-
return resolveProviderEndpoint(model
|
|
78
|
+
return resolveProviderEndpoint(model).endpointClass === "xiaomi-native";
|
|
86
79
|
}
|
|
87
80
|
function buildAnthropicBetaHeader(model, betaFeatures, params) {
|
|
88
81
|
if (!isDirectAnthropicModel(model)) return;
|
|
@@ -304,7 +297,7 @@ function convertAnthropicTools(tools, isOAuthToken) {
|
|
|
304
297
|
};
|
|
305
298
|
}
|
|
306
299
|
function parseAnthropicToolCallArguments(inputJson) {
|
|
307
|
-
return parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson);
|
|
300
|
+
return coerceTransportToolCallArguments(parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson));
|
|
308
301
|
}
|
|
309
302
|
const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
310
303
|
/** Resolve the effective Anthropic API base URL from model or environment. */
|
|
@@ -407,7 +400,7 @@ async function* parseAnthropicSseBody(body, signal) {
|
|
|
407
400
|
}
|
|
408
401
|
function createAnthropicMessagesClient(params) {
|
|
409
402
|
const url = resolveAnthropicMessagesUrl(params.baseURL);
|
|
410
|
-
return { messages: { async
|
|
403
|
+
return { messages: { async stream(body, options) {
|
|
411
404
|
const headers = mergeTransportHeaders({
|
|
412
405
|
"content-type": "application/json",
|
|
413
406
|
"anthropic-version": "2023-06-01",
|
|
@@ -420,12 +413,10 @@ function createAnthropicMessagesClient(params) {
|
|
|
420
413
|
body: JSON.stringify(body),
|
|
421
414
|
signal: options?.signal
|
|
422
415
|
});
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
if (!response.body) return;
|
|
428
|
-
yield* parseAnthropicSseBody(response.body, options?.signal);
|
|
416
|
+
return {
|
|
417
|
+
response,
|
|
418
|
+
stream: response.body ? parseAnthropicSseBody(response.body, options?.signal) : []
|
|
419
|
+
};
|
|
429
420
|
} } };
|
|
430
421
|
}
|
|
431
422
|
function formatAnthropicMessagesHttpError(response, detail) {
|
|
@@ -486,7 +477,7 @@ function createAnthropicTransportClient(params) {
|
|
|
486
477
|
}
|
|
487
478
|
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
|
|
488
479
|
if (needsInterleavedBeta) betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
489
|
-
if (
|
|
480
|
+
if (isAnthropicOAuthApiKey(apiKey)) {
|
|
490
481
|
const betaHeader = buildAnthropicBetaHeader(model, betaFeatures, { oauth: true });
|
|
491
482
|
return {
|
|
492
483
|
client: createAnthropicMessagesClient({
|
|
@@ -536,7 +527,7 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
536
527
|
baseUrl: model.baseUrl,
|
|
537
528
|
cacheRetention: options?.cacheRetention,
|
|
538
529
|
enableCacheControl: true
|
|
539
|
-
});
|
|
530
|
+
}, model);
|
|
540
531
|
const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
|
|
541
532
|
const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
|
|
542
533
|
enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
|
|
@@ -620,7 +611,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
620
611
|
const reasoningModelMaxTokens = resolvePositiveAnthropicTokenLimit(model.maxTokens) ?? baseMaxTokens;
|
|
621
612
|
const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
|
|
622
613
|
const reasoning = options?.reasoning === "off" && mandatoryAdaptiveThinking ? "low" : options?.reasoning;
|
|
623
|
-
const resolved = {
|
|
614
|
+
const resolved = copyProviderAcceptanceObserver(options, {
|
|
624
615
|
temperature: options?.temperature,
|
|
625
616
|
stop: options?.stop,
|
|
626
617
|
maxTokens: baseMaxTokens,
|
|
@@ -630,6 +621,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
630
621
|
sessionId: options?.sessionId,
|
|
631
622
|
headers: options?.headers,
|
|
632
623
|
onPayload: options?.onPayload,
|
|
624
|
+
onResponse: options?.onResponse,
|
|
633
625
|
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
634
626
|
metadata: options?.metadata,
|
|
635
627
|
interleavedThinking: options?.interleavedThinking,
|
|
@@ -638,7 +630,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
638
630
|
reasoning,
|
|
639
631
|
...options?.anthropicServerCompaction === true ? { anthropicServerCompaction: true } : {},
|
|
640
632
|
...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
|
|
641
|
-
};
|
|
633
|
+
});
|
|
642
634
|
if (reasoning === "off") {
|
|
643
635
|
resolved.thinkingEnabled = false;
|
|
644
636
|
return resolved;
|
|
@@ -700,12 +692,23 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
700
692
|
const nextParams = await transportOptions.onPayload?.(params, model);
|
|
701
693
|
if (nextParams !== void 0) params = nextParams;
|
|
702
694
|
applyClaudeRequestContract(params, model);
|
|
703
|
-
const anthropicStream = client.messages.stream({
|
|
695
|
+
const { response, stream: anthropicStream } = await client.messages.stream({
|
|
704
696
|
...params,
|
|
705
697
|
stream: true
|
|
706
698
|
}, transportOptions.signal ? { signal: transportOptions.signal } : void 0);
|
|
699
|
+
await notifyProviderHttpResponse({
|
|
700
|
+
options: transportOptions,
|
|
701
|
+
response,
|
|
702
|
+
model
|
|
703
|
+
});
|
|
704
|
+
if (!response.ok) {
|
|
705
|
+
const detail = await readAnthropicMessagesErrorBodySnippet(response);
|
|
706
|
+
throw new Error(formatAnthropicMessagesHttpError(response, detail));
|
|
707
|
+
}
|
|
707
708
|
const blocks = output.content;
|
|
708
709
|
const blockIndexes = /* @__PURE__ */ new Map();
|
|
710
|
+
const toolArgumentPreviewSchedules = /* @__PURE__ */ new WeakMap();
|
|
711
|
+
const sealedToolCalls = [];
|
|
709
712
|
const compactionCapture = createCompactionCapture(output, model, transportOptions);
|
|
710
713
|
const pendingThinkingSignatures = /* @__PURE__ */ new Map();
|
|
711
714
|
const allowReasoningContentReplay = supportsReasoningContentReplay(model);
|
|
@@ -740,6 +743,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
740
743
|
partial: output
|
|
741
744
|
});
|
|
742
745
|
}
|
|
746
|
+
if (contentIndex === void 0) return false;
|
|
743
747
|
block.thinking += text;
|
|
744
748
|
block.thinkingSignature = "reasoning_content";
|
|
745
749
|
eventSink.push({
|
|
@@ -771,6 +775,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
771
775
|
partial: output
|
|
772
776
|
});
|
|
773
777
|
}
|
|
778
|
+
if (contentIndex === void 0) return false;
|
|
774
779
|
block.text += text;
|
|
775
780
|
eventSink.push({
|
|
776
781
|
type: "text_delta",
|
|
@@ -833,6 +838,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
833
838
|
const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
|
|
834
839
|
if (fallbackBoundary) {
|
|
835
840
|
refusalBuffer?.discard();
|
|
841
|
+
sealedToolCalls.length = 0;
|
|
836
842
|
pendingTextEnds.length = 0;
|
|
837
843
|
blockIndexes.clear();
|
|
838
844
|
pendingThinkingSignatures.clear();
|
|
@@ -955,6 +961,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
955
961
|
};
|
|
956
962
|
output.content.push(block);
|
|
957
963
|
blockIndexes.set(index, output.content.length - 1);
|
|
964
|
+
toolArgumentPreviewSchedules.set(block, createToolArgumentPreviewSchedule());
|
|
958
965
|
eventSink.push({
|
|
959
966
|
type: "toolcall_start",
|
|
960
967
|
contentIndex: output.content.length - 1,
|
|
@@ -975,7 +982,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
975
982
|
let appendedContent = false;
|
|
976
983
|
if (!hasNativeAnthropicDelta && typeof delta?.content === "string" && delta.content.length > 0) {
|
|
977
984
|
const text = sanitizeTransportPayloadText(delta.content);
|
|
978
|
-
if (text.length > 0) if (block?.type === "text") {
|
|
985
|
+
if (text.length > 0) if (block?.type === "text" && index !== void 0) {
|
|
979
986
|
block.text += text;
|
|
980
987
|
eventSink.push({
|
|
981
988
|
type: "text_delta",
|
|
@@ -1003,6 +1010,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1003
1010
|
partial: output
|
|
1004
1011
|
});
|
|
1005
1012
|
}
|
|
1013
|
+
if (index === void 0) continue;
|
|
1006
1014
|
if (block?.type === "text" && delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1007
1015
|
block.text += delta.text;
|
|
1008
1016
|
eventSink.push({
|
|
@@ -1026,7 +1034,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1026
1034
|
if (block?.type === "toolCall" && delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
1027
1035
|
const partialJson = `${block.partialJson ?? ""}${delta.partial_json}`;
|
|
1028
1036
|
block.partialJson = partialJson;
|
|
1029
|
-
block.arguments = parseAnthropicToolCallArguments(partialJson);
|
|
1037
|
+
if (toolArgumentPreviewSchedules.get(block)?.(partialJson.length)) block.arguments = parseAnthropicToolCallArguments(partialJson);
|
|
1030
1038
|
eventSink.push({
|
|
1031
1039
|
type: "toolcall_delta",
|
|
1032
1040
|
contentIndex: index,
|
|
@@ -1080,12 +1088,9 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1080
1088
|
continue;
|
|
1081
1089
|
}
|
|
1082
1090
|
if (block.type === "toolCall") {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
contentIndex: index,
|
|
1087
|
-
toolCall: block,
|
|
1088
|
-
partial: output
|
|
1091
|
+
sealedToolCalls.push({
|
|
1092
|
+
block,
|
|
1093
|
+
contentIndex: index
|
|
1089
1094
|
});
|
|
1090
1095
|
finishReasoningContentSidecars(event.index);
|
|
1091
1096
|
}
|
|
@@ -1105,6 +1110,17 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1105
1110
|
if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
|
|
1106
1111
|
if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
|
|
1107
1112
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1113
|
+
if ([...blockIndexes.values()].some((index) => blocks[index]?.type === "toolCall")) throw new Error("Provider completed stream with an incomplete tool call");
|
|
1114
|
+
finalizeTerminalToolCallArguments(sealedToolCalls.map(({ block }) => block), (block) => block.partialJson && block.partialJson.length > 0 ? block.partialJson : block.arguments);
|
|
1115
|
+
for (const sealed of sealedToolCalls) {
|
|
1116
|
+
delete sealed.block.partialJson;
|
|
1117
|
+
eventSink.push({
|
|
1118
|
+
type: "toolcall_end",
|
|
1119
|
+
contentIndex: sealed.contentIndex,
|
|
1120
|
+
toolCall: sealed.block,
|
|
1121
|
+
partial: output
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1108
1124
|
refusalBuffer?.flush();
|
|
1109
1125
|
if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
|
|
1110
1126
|
flushPendingTextEnds();
|
|
@@ -1116,7 +1132,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1116
1132
|
if (refusalBuffer) {
|
|
1117
1133
|
refusalBuffer.discard();
|
|
1118
1134
|
output.content = [];
|
|
1119
|
-
}
|
|
1135
|
+
} else output.content = output.content.filter((block) => block.type !== "toolCall");
|
|
1120
1136
|
if (usedCompactionReplay && isAnthropicReplayRejection(error)) suppressAnthropicCompaction(output, model, options);
|
|
1121
1137
|
failTransportStream({
|
|
1122
1138
|
stream,
|
|
@@ -1133,96 +1149,6 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1133
1149
|
};
|
|
1134
1150
|
}
|
|
1135
1151
|
//#endregion
|
|
1136
|
-
//#region packages/ai/src/transports/deepseek-text-filter.ts
|
|
1137
|
-
/**
|
|
1138
|
-
* DeepSeek DSML streaming text filter.
|
|
1139
|
-
* Removes provider-emitted DSML tool markup while buffering split tag prefixes
|
|
1140
|
-
* across streamed chunks.
|
|
1141
|
-
*/
|
|
1142
|
-
const DSML_KINDS = [
|
|
1143
|
-
"tool_use_error",
|
|
1144
|
-
"tool_calls",
|
|
1145
|
-
"tool_call",
|
|
1146
|
-
"function_calls"
|
|
1147
|
-
];
|
|
1148
|
-
const DSML_BARS = ["|", "|"];
|
|
1149
|
-
const DSML_OPEN_TOKENS = DSML_BARS.flatMap((bar) => DSML_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
|
|
1150
|
-
const DSML_CLOSE_TOKENS = DSML_BARS.flatMap((bar) => DSML_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
|
|
1151
|
-
const MAX_OPEN_TOKEN_LEN = Math.max(...DSML_OPEN_TOKENS.map((token) => token.length));
|
|
1152
|
-
const MAX_CLOSE_TOKEN_LEN = Math.max(...DSML_CLOSE_TOKENS.map((token) => token.length));
|
|
1153
|
-
/** Create an incremental text filter that strips DeepSeek DSML tool blocks. */
|
|
1154
|
-
function createDeepSeekTextFilter() {
|
|
1155
|
-
let buffer = "";
|
|
1156
|
-
let insideDsml = false;
|
|
1157
|
-
const consume = (final) => {
|
|
1158
|
-
const output = [];
|
|
1159
|
-
const emit = (text) => {
|
|
1160
|
-
if (text) output.push(text);
|
|
1161
|
-
};
|
|
1162
|
-
while (buffer) {
|
|
1163
|
-
if (insideDsml) {
|
|
1164
|
-
const close = findEarliestToken(buffer, DSML_CLOSE_TOKENS);
|
|
1165
|
-
if (close) {
|
|
1166
|
-
buffer = buffer.slice(close.index + close.token.length);
|
|
1167
|
-
insideDsml = false;
|
|
1168
|
-
continue;
|
|
1169
|
-
}
|
|
1170
|
-
const keep = final ? 0 : Math.min(buffer.length, MAX_CLOSE_TOKEN_LEN - 1);
|
|
1171
|
-
buffer = buffer.slice(buffer.length - keep);
|
|
1172
|
-
if (final) insideDsml = false;
|
|
1173
|
-
return output;
|
|
1174
|
-
}
|
|
1175
|
-
const open = findEarliestToken(buffer, DSML_OPEN_TOKENS);
|
|
1176
|
-
if (open) {
|
|
1177
|
-
emit(buffer.slice(0, open.index));
|
|
1178
|
-
buffer = buffer.slice(open.index + open.token.length);
|
|
1179
|
-
insideDsml = true;
|
|
1180
|
-
continue;
|
|
1181
|
-
}
|
|
1182
|
-
if (final) {
|
|
1183
|
-
emit(buffer);
|
|
1184
|
-
buffer = "";
|
|
1185
|
-
return output;
|
|
1186
|
-
}
|
|
1187
|
-
const keep = longestDsmlOpenPrefixSuffixLength(buffer);
|
|
1188
|
-
const emitLength = buffer.length - keep;
|
|
1189
|
-
if (emitLength <= 0) return output;
|
|
1190
|
-
emit(buffer.slice(0, emitLength));
|
|
1191
|
-
buffer = buffer.slice(emitLength);
|
|
1192
|
-
return output;
|
|
1193
|
-
}
|
|
1194
|
-
return output;
|
|
1195
|
-
};
|
|
1196
|
-
return {
|
|
1197
|
-
push(chunk) {
|
|
1198
|
-
buffer += chunk;
|
|
1199
|
-
return consume(false);
|
|
1200
|
-
},
|
|
1201
|
-
flush() {
|
|
1202
|
-
return consume(true);
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1206
|
-
function findEarliestToken(text, tokens) {
|
|
1207
|
-
let best = null;
|
|
1208
|
-
for (const token of tokens) {
|
|
1209
|
-
const index = text.indexOf(token);
|
|
1210
|
-
if (index !== -1 && (!best || index < best.index)) best = {
|
|
1211
|
-
index,
|
|
1212
|
-
token
|
|
1213
|
-
};
|
|
1214
|
-
}
|
|
1215
|
-
return best;
|
|
1216
|
-
}
|
|
1217
|
-
function longestDsmlOpenPrefixSuffixLength(text) {
|
|
1218
|
-
const maxLength = Math.min(text.length, MAX_OPEN_TOKEN_LEN - 1);
|
|
1219
|
-
for (let length = maxLength; length > 0; length--) {
|
|
1220
|
-
const suffix = text.slice(text.length - length);
|
|
1221
|
-
if (DSML_OPEN_TOKENS.some((token) => token.startsWith(suffix))) return length;
|
|
1222
|
-
}
|
|
1223
|
-
return 0;
|
|
1224
|
-
}
|
|
1225
|
-
//#endregion
|
|
1226
1152
|
//#region packages/ai/src/transports/model-max-tokens-params.ts
|
|
1227
1153
|
/**
|
|
1228
1154
|
* Max-token parameter normalization across provider/native naming variants.
|
|
@@ -1521,202 +1447,10 @@ function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
|
1521
1447
|
});
|
|
1522
1448
|
}
|
|
1523
1449
|
//#endregion
|
|
1524
|
-
//#region packages/ai/src/transports/openai-transport-params.ts
|
|
1525
|
-
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
1526
|
-
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
1527
|
-
const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
1528
|
-
function readToolPayloadField(record, field) {
|
|
1529
|
-
try {
|
|
1530
|
-
return Object.hasOwn(record, field) ? record[field] : void 0;
|
|
1531
|
-
} catch {
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
function readCodeModePayloadToolName(tool) {
|
|
1536
|
-
if (!isRecord(tool)) return;
|
|
1537
|
-
const name = readToolPayloadField(tool, "name");
|
|
1538
|
-
if (typeof name === "string") return name;
|
|
1539
|
-
const fn = readToolPayloadField(tool, "function");
|
|
1540
|
-
if (!isRecord(fn)) return;
|
|
1541
|
-
const fnName = readToolPayloadField(fn, "name");
|
|
1542
|
-
return typeof fnName === "string" ? fnName : void 0;
|
|
1543
|
-
}
|
|
1544
|
-
function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
|
|
1545
|
-
if (!isRecord(tool)) return;
|
|
1546
|
-
const type = readToolPayloadField(tool, "type");
|
|
1547
|
-
if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
|
|
1548
|
-
try {
|
|
1549
|
-
if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
|
|
1550
|
-
} catch {
|
|
1551
|
-
return false;
|
|
1552
|
-
}
|
|
1553
|
-
return `hosted:${type}`;
|
|
1554
|
-
}
|
|
1555
|
-
const name = readCodeModePayloadToolName(tool);
|
|
1556
|
-
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
|
|
1557
|
-
}
|
|
1558
|
-
function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1559
|
-
if (!isRecord(payload)) return;
|
|
1560
|
-
const tools = readToolPayloadField(payload, "tools");
|
|
1561
|
-
if (!Array.isArray(tools)) return;
|
|
1562
|
-
payload.tools = tools.flatMap((tool) => {
|
|
1563
|
-
const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
|
|
1564
|
-
if (identity) return [tool];
|
|
1565
|
-
if (identity === false) return [];
|
|
1566
|
-
if (!isRecord(tool)) return [];
|
|
1567
|
-
const filteredGroups = {};
|
|
1568
|
-
for (const key of ["functionDeclarations", "function_declarations"]) {
|
|
1569
|
-
const declarations = readToolPayloadField(tool, key);
|
|
1570
|
-
if (!Array.isArray(declarations)) continue;
|
|
1571
|
-
const filtered = declarations.filter((declaration) => {
|
|
1572
|
-
const declarationName = readCodeModePayloadToolName(declaration);
|
|
1573
|
-
return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
|
|
1574
|
-
});
|
|
1575
|
-
if (filtered.length > 0) filteredGroups[key] = filtered;
|
|
1576
|
-
}
|
|
1577
|
-
return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
|
|
1578
|
-
});
|
|
1579
|
-
}
|
|
1580
|
-
function resolveCodeModeResponsesVisibleToolNames(context) {
|
|
1581
|
-
return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
|
|
1582
|
-
}
|
|
1583
|
-
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1584
|
-
if (!isRecord(payload)) return;
|
|
1585
|
-
const tools = readToolPayloadField(payload, "tools");
|
|
1586
|
-
if (!Array.isArray(tools)) return;
|
|
1587
|
-
payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
|
|
1588
|
-
}
|
|
1589
|
-
function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1590
|
-
const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
|
|
1591
|
-
if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
|
|
1592
|
-
const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
|
|
1593
|
-
const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
|
|
1594
|
-
if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
|
|
1595
|
-
throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
|
|
1596
|
-
}
|
|
1597
|
-
function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
|
|
1598
|
-
return sha256Hex(JSON.stringify({
|
|
1599
|
-
transport: context.transport,
|
|
1600
|
-
provider: context.model.provider ?? null,
|
|
1601
|
-
model: context.model.id ?? null,
|
|
1602
|
-
diagnostics: diagnostics.map((entry) => ({
|
|
1603
|
-
toolIndex: entry.toolIndex,
|
|
1604
|
-
toolName: entry.toolName ?? null,
|
|
1605
|
-
violations: entry.violations
|
|
1606
|
-
}))
|
|
1607
|
-
}));
|
|
1608
|
-
}
|
|
1609
|
-
function shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context) {
|
|
1610
|
-
const key = buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context);
|
|
1611
|
-
if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.has(key)) return false;
|
|
1612
|
-
if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.size >= MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS) loggedOpenAIStrictToolDowngradeDiagnosticKeys.clear();
|
|
1613
|
-
loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
|
|
1614
|
-
return true;
|
|
1615
|
-
}
|
|
1616
|
-
function resolveOpenAIStrictToolFlagWithDiagnostics(projection, strictSetting, context) {
|
|
1617
|
-
const strict = resolveOpenAIProjectedToolsStrictToolFlag(projection, strictSetting);
|
|
1618
|
-
if (strictSetting === true && strict === false) {
|
|
1619
|
-
const diagnostics = findOpenAIStrictToolProjectionDiagnostics(projection);
|
|
1620
|
-
getAiTransportHost().logDebug("openai-transport", () => {
|
|
1621
|
-
if (!shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context)) return null;
|
|
1622
|
-
const sample = diagnostics.slice(0, 5).map((entry) => ({
|
|
1623
|
-
tool: entry.toolName ?? `tool[${entry.toolIndex}]`,
|
|
1624
|
-
violations: entry.violations.slice(0, 8)
|
|
1625
|
-
}));
|
|
1626
|
-
return {
|
|
1627
|
-
message: `OpenAI ${context.transport} tool schema strict mode downgraded to strict=false for ${context.model.provider ?? "unknown"}/${context.model.id ?? "unknown"} because ${diagnostics.length} tool schema(s) are not strict-compatible`,
|
|
1628
|
-
data: {
|
|
1629
|
-
transport: context.transport,
|
|
1630
|
-
provider: context.model.provider,
|
|
1631
|
-
model: context.model.id,
|
|
1632
|
-
incompatibleToolCount: diagnostics.length,
|
|
1633
|
-
sample
|
|
1634
|
-
}
|
|
1635
|
-
};
|
|
1636
|
-
});
|
|
1637
|
-
}
|
|
1638
|
-
return strict;
|
|
1639
|
-
}
|
|
1640
|
-
function isOpenAICodexResponsesModel(model) {
|
|
1641
|
-
return OPENAI_CODEX_RESPONSES_PROVIDERS.has(model.provider) && (model.api === "openai-chatgpt-responses" || model.api === "openclaw-openai-chatgpt-responses-transport");
|
|
1642
|
-
}
|
|
1643
|
-
function isNativeOpenAICodexResponsesBaseUrl(baseUrl) {
|
|
1644
|
-
const trimmed = typeof baseUrl === "string" ? baseUrl.trim() : "";
|
|
1645
|
-
if (!trimmed) return false;
|
|
1646
|
-
try {
|
|
1647
|
-
const url = new URL(trimmed);
|
|
1648
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
1649
|
-
if (url.hostname.toLowerCase() !== "chatgpt.com") return false;
|
|
1650
|
-
const pathname = url.pathname.replace(/\/+$/u, "").toLowerCase();
|
|
1651
|
-
return [
|
|
1652
|
-
"/backend-api",
|
|
1653
|
-
"/backend-api/v1",
|
|
1654
|
-
"/backend-api/codex",
|
|
1655
|
-
"/backend-api/codex/v1"
|
|
1656
|
-
].includes(pathname);
|
|
1657
|
-
} catch {
|
|
1658
|
-
return false;
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
function usesNativeOpenAICodexResponsesBackend(model) {
|
|
1662
|
-
return isOpenAICodexResponsesModel(model) && isNativeOpenAICodexResponsesBaseUrl(model.baseUrl);
|
|
1663
|
-
}
|
|
1664
|
-
function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId) {
|
|
1665
|
-
const providerHeaders = { ...model.headers };
|
|
1666
|
-
if (model.provider === "github-copilot") Object.assign(providerHeaders, getAiTransportHost().buildCopilotDynamicHeaders(context.messages));
|
|
1667
|
-
const callerHeaders = {
|
|
1668
|
-
...optionHeaders,
|
|
1669
|
-
...turnHeaders
|
|
1670
|
-
};
|
|
1671
|
-
const resolvedHeaders = resolveProviderRequestPolicyConfig({
|
|
1672
|
-
provider: model.provider,
|
|
1673
|
-
api: model.api,
|
|
1674
|
-
baseUrl: model.baseUrl,
|
|
1675
|
-
capability: "llm",
|
|
1676
|
-
transport: "stream",
|
|
1677
|
-
providerHeaders,
|
|
1678
|
-
callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
|
|
1679
|
-
precedence: "caller-wins"
|
|
1680
|
-
}).headers ?? {};
|
|
1681
|
-
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
|
|
1682
|
-
return resolvedHeaders;
|
|
1683
|
-
}
|
|
1684
|
-
function resolveOpenAISdkTimeoutMs(model, timeoutMs) {
|
|
1685
|
-
return resolveModelRequestTimeoutMs(model, timeoutMs);
|
|
1686
|
-
}
|
|
1687
|
-
function buildOpenAISdkClientOptions(model) {
|
|
1688
|
-
const timeout = resolveOpenAISdkTimeoutMs(model);
|
|
1689
|
-
return timeout === void 0 ? {} : { timeout };
|
|
1690
|
-
}
|
|
1691
|
-
function buildOpenAISdkRequestOptions(model, signal, options) {
|
|
1692
|
-
const timeout = resolveOpenAISdkTimeoutMs(model, options?.timeoutMs);
|
|
1693
|
-
const headers = options?.stream === true && usesNativeOpenAICodexResponsesBackend(model) ? { Accept: "text/event-stream" } : void 0;
|
|
1694
|
-
if (timeout === void 0 && options?.maxRetries === void 0 && !signal && !headers) return;
|
|
1695
|
-
return {
|
|
1696
|
-
...headers ? { headers } : {},
|
|
1697
|
-
...signal ? { signal } : {},
|
|
1698
|
-
...timeout !== void 0 ? { timeout } : {},
|
|
1699
|
-
...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
function getCompat(model) {
|
|
1703
|
-
const resolved = resolveOpenAICompletionsCompat(model);
|
|
1704
|
-
const compat = model.compat ?? {};
|
|
1705
|
-
return {
|
|
1706
|
-
...resolved,
|
|
1707
|
-
cacheControlFormat: resolved.cacheControlFormat,
|
|
1708
|
-
reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, {}),
|
|
1709
|
-
openRouterRouting: resolved.openRouterRouting ?? {},
|
|
1710
|
-
vercelGatewayRouting: resolved.vercelGatewayRouting,
|
|
1711
|
-
requiresStringContent: compat.requiresStringContent ?? false,
|
|
1712
|
-
strictMessageKeys: compat.strictMessageKeys === true
|
|
1713
|
-
};
|
|
1714
|
-
}
|
|
1715
|
-
//#endregion
|
|
1716
1450
|
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
1717
1451
|
function isKnownOpenAICompletionsEndpoint(model) {
|
|
1718
1452
|
if (!model.baseUrl.trim()) return true;
|
|
1719
|
-
const endpointClass = resolveProviderEndpoint(model
|
|
1453
|
+
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
1720
1454
|
if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
|
|
1721
1455
|
try {
|
|
1722
1456
|
return isAzureOpenAICompatibleHost(new URL(model.baseUrl).hostname.toLowerCase());
|
|
@@ -1724,7 +1458,7 @@ function isKnownOpenAICompletionsEndpoint(model) {
|
|
|
1724
1458
|
return false;
|
|
1725
1459
|
}
|
|
1726
1460
|
}
|
|
1727
|
-
function resolveOpenAICompletionsReasoningEffort
|
|
1461
|
+
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
1728
1462
|
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1729
1463
|
}
|
|
1730
1464
|
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
@@ -1923,7 +1657,7 @@ function buildOpenAICompletionsParams(model, context, options) {
|
|
|
1923
1657
|
if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1924
1658
|
else params.max_completion_tokens = clampedMaxTokens;
|
|
1925
1659
|
}
|
|
1926
|
-
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort
|
|
1660
|
+
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
|
|
1927
1661
|
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1928
1662
|
model,
|
|
1929
1663
|
effort: completionsReasoningEffort,
|
|
@@ -1949,713 +1683,6 @@ function buildOpenAICompletionsParams(model, context, options) {
|
|
|
1949
1683
|
return params;
|
|
1950
1684
|
}
|
|
1951
1685
|
//#endregion
|
|
1952
|
-
//#region packages/ai/src/transports/openai-completions-dsml.ts
|
|
1953
|
-
const DEEPSEEK_DSML_BARS = ["|", "|"];
|
|
1954
|
-
const DEEPSEEK_DSML_TOOL_KINDS = [
|
|
1955
|
-
"tool_calls",
|
|
1956
|
-
"tool_call",
|
|
1957
|
-
"function_calls"
|
|
1958
|
-
];
|
|
1959
|
-
const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
|
|
1960
|
-
const DEEPSEEK_DSML_TOOL_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
|
|
1961
|
-
const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_BARS.map((bar) => `<${bar}DSML${bar}invoke`);
|
|
1962
|
-
const DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.map((bar) => `</${bar}DSML${bar}invoke>`);
|
|
1963
|
-
const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
|
|
1964
|
-
const DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_TOOL_CLOSE_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS.map((token) => token.length));
|
|
1965
|
-
const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
|
|
1966
|
-
const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
|
|
1967
|
-
function createDsmlRecoverer() {
|
|
1968
|
-
let buffer = "";
|
|
1969
|
-
let bufferBytes = 0;
|
|
1970
|
-
let bufferEndsWithHighSurrogate = false;
|
|
1971
|
-
let pendingScanChars = 0;
|
|
1972
|
-
let activeOpenToken = null;
|
|
1973
|
-
let blockScanState = {
|
|
1974
|
-
offset: 0,
|
|
1975
|
-
mode: "outer",
|
|
1976
|
-
invokeOpenStart: -1
|
|
1977
|
-
};
|
|
1978
|
-
const resetBlockScan = () => {
|
|
1979
|
-
activeOpenToken = null;
|
|
1980
|
-
pendingScanChars = 0;
|
|
1981
|
-
blockScanState = {
|
|
1982
|
-
offset: 0,
|
|
1983
|
-
mode: "outer",
|
|
1984
|
-
invokeOpenStart: -1
|
|
1985
|
-
};
|
|
1986
|
-
};
|
|
1987
|
-
const consume = (final) => {
|
|
1988
|
-
const output = [];
|
|
1989
|
-
while (buffer) {
|
|
1990
|
-
const open = activeOpenToken ? {
|
|
1991
|
-
index: 0,
|
|
1992
|
-
token: activeOpenToken
|
|
1993
|
-
} : findEarliestStringToken(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS);
|
|
1994
|
-
if (!open) {
|
|
1995
|
-
resetBlockScan();
|
|
1996
|
-
if (final) {
|
|
1997
|
-
output.push({
|
|
1998
|
-
kind: "text",
|
|
1999
|
-
text: buffer
|
|
2000
|
-
});
|
|
2001
|
-
buffer = "";
|
|
2002
|
-
bufferBytes = 0;
|
|
2003
|
-
bufferEndsWithHighSurrogate = false;
|
|
2004
|
-
return output;
|
|
2005
|
-
}
|
|
2006
|
-
const keep = longestDeepSeekDsmlToolOpenPrefixSuffixLength(buffer);
|
|
2007
|
-
const emitLength = buffer.length - keep;
|
|
2008
|
-
if (emitLength > 0) {
|
|
2009
|
-
const emitted = buffer.slice(0, emitLength);
|
|
2010
|
-
output.push({
|
|
2011
|
-
kind: "text",
|
|
2012
|
-
text: emitted
|
|
2013
|
-
});
|
|
2014
|
-
bufferBytes -= Buffer.byteLength(emitted, "utf8");
|
|
2015
|
-
buffer = buffer.slice(emitted.length);
|
|
2016
|
-
if (!buffer) bufferEndsWithHighSurrogate = false;
|
|
2017
|
-
}
|
|
2018
|
-
return output;
|
|
2019
|
-
}
|
|
2020
|
-
if (open.index > 0) {
|
|
2021
|
-
const prefix = buffer.slice(0, open.index);
|
|
2022
|
-
output.push({
|
|
2023
|
-
kind: "text",
|
|
2024
|
-
text: prefix
|
|
2025
|
-
});
|
|
2026
|
-
bufferBytes -= Buffer.byteLength(prefix, "utf8");
|
|
2027
|
-
buffer = buffer.slice(prefix.length);
|
|
2028
|
-
resetBlockScan();
|
|
2029
|
-
}
|
|
2030
|
-
activeOpenToken = open.token;
|
|
2031
|
-
if (blockScanState.offset === 0) blockScanState.offset = open.token.length;
|
|
2032
|
-
const blockScan = scanDeepSeekDsmlToolBlock(buffer, open.token.replace("<", "</"), open.token.length, blockScanState);
|
|
2033
|
-
if (blockScan.kind === "nested-open") throw new Error("Nested DeepSeek DSML recovery wrappers are not supported");
|
|
2034
|
-
const close = blockScan.kind === "close" ? blockScan : null;
|
|
2035
|
-
if (!close) {
|
|
2036
|
-
if (final) {
|
|
2037
|
-
output.push({
|
|
2038
|
-
kind: "text",
|
|
2039
|
-
text: buffer
|
|
2040
|
-
});
|
|
2041
|
-
buffer = "";
|
|
2042
|
-
bufferBytes = 0;
|
|
2043
|
-
bufferEndsWithHighSurrogate = false;
|
|
2044
|
-
return output;
|
|
2045
|
-
}
|
|
2046
|
-
if (bufferBytes > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
|
|
2047
|
-
return output;
|
|
2048
|
-
}
|
|
2049
|
-
resetBlockScan();
|
|
2050
|
-
const body = buffer.slice(open.token.length, close.index);
|
|
2051
|
-
const blockText = buffer.slice(0, close.index + close.token.length);
|
|
2052
|
-
if (Buffer.byteLength(blockText, "utf8") > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
|
|
2053
|
-
const recoveredToolCalls = parseDeepSeekDsmlToolCallBlock(body);
|
|
2054
|
-
if (recoveredToolCalls.length > 0) output.push(...recoveredToolCalls);
|
|
2055
|
-
else output.push({
|
|
2056
|
-
kind: "text",
|
|
2057
|
-
text: blockText
|
|
2058
|
-
});
|
|
2059
|
-
bufferBytes -= Buffer.byteLength(blockText, "utf8");
|
|
2060
|
-
buffer = buffer.slice(blockText.length);
|
|
2061
|
-
if (!buffer) bufferEndsWithHighSurrogate = false;
|
|
2062
|
-
}
|
|
2063
|
-
return output;
|
|
2064
|
-
};
|
|
2065
|
-
return {
|
|
2066
|
-
push(chunk) {
|
|
2067
|
-
const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
|
|
2068
|
-
bufferBytes += append.bytes;
|
|
2069
|
-
bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
|
|
2070
|
-
buffer += chunk;
|
|
2071
|
-
pendingScanChars += chunk.length;
|
|
2072
|
-
if (activeOpenToken && pendingScanChars < DEEPSEEK_DSML_SCAN_BATCH_CHARS && !chunk.includes("<") && !chunk.includes(">") && bufferBytes <= MAX_DSML_RECOVERY_BUFFER_BYTES) return [];
|
|
2073
|
-
pendingScanChars = 0;
|
|
2074
|
-
return consume(false);
|
|
2075
|
-
},
|
|
2076
|
-
flush() {
|
|
2077
|
-
return consume(true);
|
|
2078
|
-
}
|
|
2079
|
-
};
|
|
2080
|
-
}
|
|
2081
|
-
function parseDeepSeekDsmlToolCallBlock(body) {
|
|
2082
|
-
const toolCalls = [];
|
|
2083
|
-
const invokeOpenRegex = /<[||]DSML[||]invoke\b([^<>]*)>/g;
|
|
2084
|
-
let openMatch;
|
|
2085
|
-
while ((openMatch = invokeOpenRegex.exec(body)) !== null) {
|
|
2086
|
-
const invokeBodyStart = openMatch.index + openMatch[0].length;
|
|
2087
|
-
const invokeClose = findEarliestStringToken(body.slice(invokeBodyStart), ["</|DSML|invoke>", "</|DSML|invoke>"]);
|
|
2088
|
-
if (!invokeClose) break;
|
|
2089
|
-
const invokeBody = body.slice(invokeBodyStart, invokeBodyStart + invokeClose.index);
|
|
2090
|
-
invokeOpenRegex.lastIndex = invokeBodyStart + invokeClose.index + invokeClose.token.length;
|
|
2091
|
-
const invokeName = parseXmlAttribute(openMatch[1] ?? "", "name");
|
|
2092
|
-
if (!invokeName) continue;
|
|
2093
|
-
const parsedArguments = parseDeepSeekDsmlInvokeArguments(invokeBody);
|
|
2094
|
-
if (!parsedArguments) continue;
|
|
2095
|
-
toolCalls.push({
|
|
2096
|
-
kind: "toolCall",
|
|
2097
|
-
name: invokeName,
|
|
2098
|
-
arguments: parsedArguments,
|
|
2099
|
-
partialArgs: JSON.stringify(parsedArguments)
|
|
2100
|
-
});
|
|
2101
|
-
}
|
|
2102
|
-
return toolCalls;
|
|
2103
|
-
}
|
|
2104
|
-
function parseDeepSeekDsmlInvokeArguments(body) {
|
|
2105
|
-
const args = {};
|
|
2106
|
-
const parameterRegex = /<[||]DSML[||]parameter\b([^>]*)>([\s\S]*?)<\/[||]DSML[||]parameter>/g;
|
|
2107
|
-
let parameterMatch;
|
|
2108
|
-
while ((parameterMatch = parameterRegex.exec(body)) !== null) {
|
|
2109
|
-
const name = parseXmlAttribute(parameterMatch[1] ?? "", "name");
|
|
2110
|
-
if (!name) continue;
|
|
2111
|
-
const rawValue = parameterMatch[2] ?? "";
|
|
2112
|
-
if (rawValue.length === 0) continue;
|
|
2113
|
-
args[name] = decodeDeepSeekDsmlText(rawValue);
|
|
2114
|
-
}
|
|
2115
|
-
if (Object.keys(args).length > 0) return args;
|
|
2116
|
-
const trimmed = body.trim();
|
|
2117
|
-
if (!trimmed.startsWith("{")) return null;
|
|
2118
|
-
try {
|
|
2119
|
-
const parsed = JSON.parse(trimmed);
|
|
2120
|
-
if (isRecord(parsed) && Object.keys(parsed).length > 0) return parsed;
|
|
2121
|
-
} catch {
|
|
2122
|
-
return null;
|
|
2123
|
-
}
|
|
2124
|
-
return null;
|
|
2125
|
-
}
|
|
2126
|
-
const xmlAttributeRegexCache = /* @__PURE__ */ new Map();
|
|
2127
|
-
function xmlAttributeRegex(name) {
|
|
2128
|
-
const cached = xmlAttributeRegexCache.get(name);
|
|
2129
|
-
if (cached) return cached;
|
|
2130
|
-
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2131
|
-
const pattern = new RegExp(`\\b${escaped}=("([^"]*)"|'([^']*)'|([^\\s>]+))`);
|
|
2132
|
-
xmlAttributeRegexCache.set(name, pattern);
|
|
2133
|
-
return pattern;
|
|
2134
|
-
}
|
|
2135
|
-
function parseXmlAttribute(attributes, name) {
|
|
2136
|
-
const match = xmlAttributeRegex(name).exec(attributes);
|
|
2137
|
-
const value = match?.[2] ?? match?.[3] ?? match?.[4];
|
|
2138
|
-
return value ? decodeDeepSeekDsmlText(value) : null;
|
|
2139
|
-
}
|
|
2140
|
-
function decodeDeepSeekDsmlText(value) {
|
|
2141
|
-
return value.replaceAll(""", "\"").replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
2142
|
-
}
|
|
2143
|
-
function findEarliestStringToken(text, tokens, fromIndex = 0) {
|
|
2144
|
-
let best = null;
|
|
2145
|
-
for (const token of tokens) {
|
|
2146
|
-
const index = text.indexOf(token, fromIndex);
|
|
2147
|
-
if (index !== -1 && (!best || index < best.index)) best = {
|
|
2148
|
-
index,
|
|
2149
|
-
token
|
|
2150
|
-
};
|
|
2151
|
-
}
|
|
2152
|
-
return best;
|
|
2153
|
-
}
|
|
2154
|
-
function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
|
|
2155
|
-
while (state.offset < text.length) {
|
|
2156
|
-
if (state.mode === "invoke-open") {
|
|
2157
|
-
const nextOpen = text.indexOf("<", state.offset);
|
|
2158
|
-
const nextClose = text.indexOf(">", state.offset);
|
|
2159
|
-
if (nextClose === -1 && nextOpen === -1) {
|
|
2160
|
-
state.offset = text.length;
|
|
2161
|
-
return { kind: "incomplete" };
|
|
2162
|
-
}
|
|
2163
|
-
if (nextOpen !== -1 && (nextClose === -1 || nextOpen < nextClose)) {
|
|
2164
|
-
state.mode = "outer";
|
|
2165
|
-
state.offset = nextOpen;
|
|
2166
|
-
state.invokeOpenStart = -1;
|
|
2167
|
-
continue;
|
|
2168
|
-
}
|
|
2169
|
-
const invokeOpenTag = text.slice(state.invokeOpenStart, nextClose + 1);
|
|
2170
|
-
if (!/^<[||]DSML[||]invoke\b[^<>]*>$/.test(invokeOpenTag)) {
|
|
2171
|
-
state.mode = "outer";
|
|
2172
|
-
state.offset = state.invokeOpenStart + 1;
|
|
2173
|
-
state.invokeOpenStart = -1;
|
|
2174
|
-
continue;
|
|
2175
|
-
}
|
|
2176
|
-
state.mode = "invoke-body";
|
|
2177
|
-
state.offset = nextClose + 1;
|
|
2178
|
-
state.invokeOpenStart = -1;
|
|
2179
|
-
continue;
|
|
2180
|
-
}
|
|
2181
|
-
if (state.mode === "invoke-body") {
|
|
2182
|
-
const invokeClose = findEarliestStringToken(text, DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS, state.offset);
|
|
2183
|
-
if (!invokeClose) {
|
|
2184
|
-
state.offset = Math.max(0, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
|
|
2185
|
-
return { kind: "incomplete" };
|
|
2186
|
-
}
|
|
2187
|
-
state.mode = "outer";
|
|
2188
|
-
state.offset = invokeClose.index + invokeClose.token.length;
|
|
2189
|
-
continue;
|
|
2190
|
-
}
|
|
2191
|
-
const toolOpen = findEarliestStringToken(text, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, state.offset);
|
|
2192
|
-
const toolCloseIndex = text.indexOf(closeToken, state.offset);
|
|
2193
|
-
const invokeOpen = findEarliestStringToken(text, DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES, state.offset);
|
|
2194
|
-
const next = [
|
|
2195
|
-
toolOpen ? {
|
|
2196
|
-
kind: "nested-open",
|
|
2197
|
-
...toolOpen
|
|
2198
|
-
} : null,
|
|
2199
|
-
toolCloseIndex === -1 ? null : {
|
|
2200
|
-
kind: "close",
|
|
2201
|
-
index: toolCloseIndex,
|
|
2202
|
-
token: closeToken
|
|
2203
|
-
},
|
|
2204
|
-
invokeOpen ? {
|
|
2205
|
-
kind: "invoke-open",
|
|
2206
|
-
...invokeOpen
|
|
2207
|
-
} : null
|
|
2208
|
-
].filter((candidate) => candidate !== null).toSorted((left, right) => left.index - right.index)[0];
|
|
2209
|
-
if (!next) {
|
|
2210
|
-
state.offset = Math.max(contentStartIndex, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
|
|
2211
|
-
return { kind: "incomplete" };
|
|
2212
|
-
}
|
|
2213
|
-
if (next.kind === "invoke-open") {
|
|
2214
|
-
state.mode = "invoke-open";
|
|
2215
|
-
state.invokeOpenStart = next.index;
|
|
2216
|
-
state.offset = next.index + next.token.length;
|
|
2217
|
-
continue;
|
|
2218
|
-
}
|
|
2219
|
-
return next;
|
|
2220
|
-
}
|
|
2221
|
-
return { kind: "incomplete" };
|
|
2222
|
-
}
|
|
2223
|
-
function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
|
|
2224
|
-
const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
|
|
2225
|
-
for (let length = maxLength; length > 0; length -= 1) {
|
|
2226
|
-
const suffix = text.slice(text.length - length);
|
|
2227
|
-
if (DEEPSEEK_DSML_TOOL_OPEN_TOKENS.some((token) => token.startsWith(suffix))) return length;
|
|
2228
|
-
}
|
|
2229
|
-
return 0;
|
|
2230
|
-
}
|
|
2231
|
-
//#endregion
|
|
2232
|
-
//#region packages/ai/src/transports/openai-completions-stream.ts
|
|
2233
|
-
function extractToolCallThoughtSignature(toolCall) {
|
|
2234
|
-
const tc = toolCall;
|
|
2235
|
-
if (!tc) return;
|
|
2236
|
-
const fromExtra = (tc.extra_content?.google)?.thought_signature;
|
|
2237
|
-
if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
|
|
2238
|
-
const fromFunction = tc.function?.thought_signature;
|
|
2239
|
-
if (typeof fromFunction === "string" && fromFunction.length > 0) return fromFunction;
|
|
2240
|
-
const fromToolCall = tc.thought_signature;
|
|
2241
|
-
return typeof fromToolCall === "string" && fromToolCall.length > 0 ? fromToolCall : void 0;
|
|
2242
|
-
}
|
|
2243
|
-
async function processCompletionsStream(responseStream, output, model, stream, options) {
|
|
2244
|
-
const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
|
|
2245
|
-
const emitReasoning = options?.emitReasoning ?? true;
|
|
2246
|
-
const compat = getCompat(model);
|
|
2247
|
-
const shouldFilterDeepSeekDsmlText = compat.thinkingFormat === "deepseek";
|
|
2248
|
-
const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
|
|
2249
|
-
const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
|
|
2250
|
-
const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
|
|
2251
|
-
let currentBlock = null;
|
|
2252
|
-
let pendingPostToolCallDeltas = [];
|
|
2253
|
-
let pendingPostToolCallBytes = 0;
|
|
2254
|
-
let isFlushingPendingPostToolCallDeltas = false;
|
|
2255
|
-
const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
|
|
2256
|
-
const toolCallBlocksById = /* @__PURE__ */ new Map();
|
|
2257
|
-
const provisionalCommentaryTags = /* @__PURE__ */ new Map();
|
|
2258
|
-
const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
|
|
2259
|
-
const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
|
|
2260
|
-
let sawStopFinishReason = false;
|
|
2261
|
-
let sawNativeToolCallDelta = false;
|
|
2262
|
-
const blockIndex = () => output.content.length - 1;
|
|
2263
|
-
const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
|
|
2264
|
-
let chunkPushedEvent = false;
|
|
2265
|
-
const pushStreamEvent = (event) => {
|
|
2266
|
-
chunkPushedEvent = true;
|
|
2267
|
-
stream.push(event);
|
|
2268
|
-
};
|
|
2269
|
-
const queuePostToolCallDelta = (next) => {
|
|
2270
|
-
const nextBytes = measureUtf8Bytes(next.text);
|
|
2271
|
-
if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
|
|
2272
|
-
pendingPostToolCallBytes += nextBytes;
|
|
2273
|
-
const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
|
|
2274
|
-
if (!previous || previous.kind !== next.kind) {
|
|
2275
|
-
pendingPostToolCallDeltas.push(next);
|
|
2276
|
-
return;
|
|
2277
|
-
}
|
|
2278
|
-
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
2279
|
-
if (previous.signature !== next.signature) {
|
|
2280
|
-
pendingPostToolCallDeltas.push(next);
|
|
2281
|
-
return;
|
|
2282
|
-
}
|
|
2283
|
-
previous.text += next.text;
|
|
2284
|
-
return;
|
|
2285
|
-
}
|
|
2286
|
-
previous.text += next.text;
|
|
2287
|
-
};
|
|
2288
|
-
const appendThinkingDeltaInternal = (reasoningDelta) => {
|
|
2289
|
-
if (!currentBlock || currentBlock.type !== "thinking") {
|
|
2290
|
-
currentBlock = {
|
|
2291
|
-
type: "thinking",
|
|
2292
|
-
thinking: "",
|
|
2293
|
-
...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
|
|
2294
|
-
};
|
|
2295
|
-
output.content.push(currentBlock);
|
|
2296
|
-
pushStreamEvent({
|
|
2297
|
-
type: "thinking_start",
|
|
2298
|
-
contentIndex: blockIndex(),
|
|
2299
|
-
partial: output
|
|
2300
|
-
});
|
|
2301
|
-
}
|
|
2302
|
-
currentBlock.thinking += reasoningDelta.text;
|
|
2303
|
-
pushStreamEvent({
|
|
2304
|
-
type: "thinking_delta",
|
|
2305
|
-
contentIndex: blockIndex(),
|
|
2306
|
-
delta: reasoningDelta.text,
|
|
2307
|
-
partial: output
|
|
2308
|
-
});
|
|
2309
|
-
};
|
|
2310
|
-
const appendTextDeltaInternal = (text) => {
|
|
2311
|
-
if (!currentBlock || currentBlock.type !== "text") {
|
|
2312
|
-
currentBlock = {
|
|
2313
|
-
type: "text",
|
|
2314
|
-
text: ""
|
|
2315
|
-
};
|
|
2316
|
-
output.content.push(currentBlock);
|
|
2317
|
-
pushStreamEvent({
|
|
2318
|
-
type: "text_start",
|
|
2319
|
-
contentIndex: blockIndex(),
|
|
2320
|
-
partial: output
|
|
2321
|
-
});
|
|
2322
|
-
}
|
|
2323
|
-
currentBlock.text += text;
|
|
2324
|
-
pushStreamEvent({
|
|
2325
|
-
type: "text_delta",
|
|
2326
|
-
contentIndex: blockIndex(),
|
|
2327
|
-
delta: text
|
|
2328
|
-
});
|
|
2329
|
-
};
|
|
2330
|
-
const flushPendingPostToolCallDeltas = () => {
|
|
2331
|
-
if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
|
|
2332
|
-
isFlushingPendingPostToolCallDeltas = true;
|
|
2333
|
-
const bufferedDeltas = pendingPostToolCallDeltas;
|
|
2334
|
-
pendingPostToolCallDeltas = [];
|
|
2335
|
-
pendingPostToolCallBytes = 0;
|
|
2336
|
-
for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
|
|
2337
|
-
else if (emitReasoning) appendThinkingDeltaInternal(delta);
|
|
2338
|
-
isFlushingPendingPostToolCallDeltas = false;
|
|
2339
|
-
};
|
|
2340
|
-
const appendThinkingDelta = (reasoningDelta) => {
|
|
2341
|
-
flushPendingPostToolCallDeltas();
|
|
2342
|
-
appendThinkingDeltaInternal(reasoningDelta);
|
|
2343
|
-
};
|
|
2344
|
-
const appendTextDelta = (text) => {
|
|
2345
|
-
flushPendingPostToolCallDeltas();
|
|
2346
|
-
appendTextDeltaInternal(text);
|
|
2347
|
-
};
|
|
2348
|
-
const appendVisibleTextDelta = (text) => {
|
|
2349
|
-
if (!text) return;
|
|
2350
|
-
if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
|
|
2351
|
-
kind: "text",
|
|
2352
|
-
text
|
|
2353
|
-
});
|
|
2354
|
-
else appendTextDelta(text);
|
|
2355
|
-
};
|
|
2356
|
-
const appendRecoveredToolCall = (toolCall) => {
|
|
2357
|
-
if (currentBlock?.type === "toolCall") {
|
|
2358
|
-
currentBlock = null;
|
|
2359
|
-
flushPendingPostToolCallDeltas();
|
|
2360
|
-
}
|
|
2361
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
2362
|
-
const block = {
|
|
2363
|
-
type: "toolCall",
|
|
2364
|
-
id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
|
|
2365
|
-
name: toolCall.name,
|
|
2366
|
-
arguments: toolCall.arguments,
|
|
2367
|
-
partialArgs: toolCall.partialArgs
|
|
2368
|
-
};
|
|
2369
|
-
currentBlock = block;
|
|
2370
|
-
output.content.push(block);
|
|
2371
|
-
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2372
|
-
pushStreamEvent({
|
|
2373
|
-
type: "toolcall_start",
|
|
2374
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2375
|
-
partial: output
|
|
2376
|
-
});
|
|
2377
|
-
pushStreamEvent({
|
|
2378
|
-
type: "toolcall_delta",
|
|
2379
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2380
|
-
delta: toolCall.partialArgs,
|
|
2381
|
-
partial: output
|
|
2382
|
-
});
|
|
2383
|
-
};
|
|
2384
|
-
const appendFilteredVisibleTextDelta = (text) => {
|
|
2385
|
-
const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
|
|
2386
|
-
kind: "text",
|
|
2387
|
-
text
|
|
2388
|
-
}];
|
|
2389
|
-
for (const recoveredPart of recoveredParts) {
|
|
2390
|
-
if (recoveredPart.kind === "toolCall") {
|
|
2391
|
-
appendRecoveredToolCall(recoveredPart);
|
|
2392
|
-
continue;
|
|
2393
|
-
}
|
|
2394
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
2395
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2398
|
-
const flushDeepSeekToolCallRecovererAtEnd = () => {
|
|
2399
|
-
const recoveredParts = deepSeekToolCallRecoverer?.flush();
|
|
2400
|
-
if (!recoveredParts) return;
|
|
2401
|
-
for (const recoveredPart of recoveredParts) {
|
|
2402
|
-
if (recoveredPart.kind === "toolCall") {
|
|
2403
|
-
appendRecoveredToolCall(recoveredPart);
|
|
2404
|
-
continue;
|
|
2405
|
-
}
|
|
2406
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
2407
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
2408
|
-
}
|
|
2409
|
-
};
|
|
2410
|
-
const flushDeepSeekTextFilterAtEnd = () => {
|
|
2411
|
-
const parts = deepSeekTextFilter?.flush();
|
|
2412
|
-
if (!parts) return;
|
|
2413
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
2414
|
-
};
|
|
2415
|
-
const appendRoutedContentDelta = (delta) => {
|
|
2416
|
-
if (delta.kind === "text") {
|
|
2417
|
-
appendFilteredVisibleTextDelta(delta.text);
|
|
2418
|
-
return;
|
|
2419
|
-
}
|
|
2420
|
-
if (!emitReasoning) return;
|
|
2421
|
-
if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
|
|
2422
|
-
else appendThinkingDelta(delta);
|
|
2423
|
-
};
|
|
2424
|
-
const appendPartitionedVisibleDelta = (delta) => {
|
|
2425
|
-
if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
|
|
2426
|
-
};
|
|
2427
|
-
const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
|
|
2428
|
-
if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
|
|
2429
|
-
const latestBlock = output.content[output.content.length - 1];
|
|
2430
|
-
if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
|
|
2431
|
-
if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
|
|
2432
|
-
appendThinkingDelta({ text: "" });
|
|
2433
|
-
};
|
|
2434
|
-
const flushReasoningTagTextPartitionerAtEnd = () => {
|
|
2435
|
-
for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
|
|
2436
|
-
};
|
|
2437
|
-
const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
|
|
2438
|
-
const guardedStream = withFirstStreamEventTimeout(responseStream, {
|
|
2439
|
-
provider: model.provider,
|
|
2440
|
-
api: model.api,
|
|
2441
|
-
model: model.id,
|
|
2442
|
-
timeoutMs: options?.firstEventTimeoutMs ?? 0,
|
|
2443
|
-
stage: "completions",
|
|
2444
|
-
abort: options?.abortFirstEventStream,
|
|
2445
|
-
onTimeout: options?.onFirstEventTimeout,
|
|
2446
|
-
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."
|
|
2447
|
-
});
|
|
2448
|
-
for await (const rawChunk of guardedStream) {
|
|
2449
|
-
throwIfModelStreamAborted(options?.signal);
|
|
2450
|
-
chunkPushedEvent = false;
|
|
2451
|
-
if (!rawChunk || typeof rawChunk !== "object") {
|
|
2452
|
-
await cooperativeScheduler.afterEvent();
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
|
-
notifyLlmRequestActivity(options?.signal);
|
|
2456
|
-
const chunk = rawChunk;
|
|
2457
|
-
output.responseId ||= chunk.id;
|
|
2458
|
-
let hasReasoningUsageActivity = false;
|
|
2459
|
-
if (chunk.usage) {
|
|
2460
|
-
output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
|
|
2461
|
-
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
|
|
2462
|
-
}
|
|
2463
|
-
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
2464
|
-
if (!choice) {
|
|
2465
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2466
|
-
await cooperativeScheduler.afterEvent();
|
|
2467
|
-
continue;
|
|
2468
|
-
}
|
|
2469
|
-
const choiceUsage = choice.usage;
|
|
2470
|
-
if (!chunk.usage && choiceUsage) {
|
|
2471
|
-
output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
|
|
2472
|
-
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
|
|
2473
|
-
}
|
|
2474
|
-
if (choice.finish_reason) {
|
|
2475
|
-
const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
|
|
2476
|
-
output.stopReason = finishReasonResult.stopReason;
|
|
2477
|
-
if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
|
|
2478
|
-
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
2479
|
-
}
|
|
2480
|
-
const rawChoiceDelta = choice.delta ?? choice.message;
|
|
2481
|
-
if (!rawChoiceDelta) {
|
|
2482
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2483
|
-
await cooperativeScheduler.afterEvent();
|
|
2484
|
-
continue;
|
|
2485
|
-
}
|
|
2486
|
-
for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
|
|
2487
|
-
const choiceDelta = normalizedDelta.delta;
|
|
2488
|
-
const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
|
|
2489
|
-
const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
|
|
2490
|
-
if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
|
|
2491
|
-
const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningDeltas.filter((reasoningDelta) => reasoningDelta.kind === "thinking").map((reasoningDelta) => reasoningDelta.text));
|
|
2492
|
-
const appendReasoningDeltas = () => {
|
|
2493
|
-
for (const reasoningDelta of reasoningDeltas) {
|
|
2494
|
-
if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
|
|
2495
|
-
if (currentBlock?.type === "toolCall") {
|
|
2496
|
-
queuePostToolCallDelta({ ...reasoningDelta });
|
|
2497
|
-
continue;
|
|
2498
|
-
}
|
|
2499
|
-
if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
|
|
2500
|
-
else if (emitReasoning) appendThinkingDelta(reasoningDelta);
|
|
2501
|
-
}
|
|
2502
|
-
};
|
|
2503
|
-
if (hasMirroredReasoning) appendReasoningDeltas();
|
|
2504
|
-
for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
|
|
2505
|
-
const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
|
|
2506
|
-
for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
|
|
2507
|
-
} else {
|
|
2508
|
-
if (reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
|
|
2509
|
-
appendRoutedContentDelta(contentDelta);
|
|
2510
|
-
}
|
|
2511
|
-
if (!hasMirroredReasoning) appendReasoningDeltas();
|
|
2512
|
-
const toolCallDeltas = normalizedDelta.toolCalls;
|
|
2513
|
-
if (toolCallDeltas.length > 0) {
|
|
2514
|
-
sawNativeToolCallDelta = true;
|
|
2515
|
-
flushReasoningTagTextPartitionerAtEnd();
|
|
2516
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
2517
|
-
for (const toolCall of toolCallDeltas) {
|
|
2518
|
-
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
|
|
2519
|
-
let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
|
|
2520
|
-
if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
|
|
2521
|
-
if (!block) {
|
|
2522
|
-
if (currentBlock?.type === "toolCall") {
|
|
2523
|
-
currentBlock = null;
|
|
2524
|
-
flushPendingPostToolCallDeltas();
|
|
2525
|
-
}
|
|
2526
|
-
const initialSig = extractToolCallThoughtSignature(toolCall);
|
|
2527
|
-
block = {
|
|
2528
|
-
type: "toolCall",
|
|
2529
|
-
id: toolCall.id || "",
|
|
2530
|
-
name: toolCall.function?.name || "",
|
|
2531
|
-
arguments: {},
|
|
2532
|
-
partialArgs: "",
|
|
2533
|
-
...initialSig ? { thoughtSignature: initialSig } : {}
|
|
2534
|
-
};
|
|
2535
|
-
output.content.push(block);
|
|
2536
|
-
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2537
|
-
pushStreamEvent({
|
|
2538
|
-
type: "toolcall_start",
|
|
2539
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2540
|
-
partial: output
|
|
2541
|
-
});
|
|
2542
|
-
}
|
|
2543
|
-
if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
|
|
2544
|
-
if (toolCall.id) {
|
|
2545
|
-
block.id = toolCall.id;
|
|
2546
|
-
toolCallBlocksById.set(toolCall.id, block);
|
|
2547
|
-
}
|
|
2548
|
-
currentBlock = block;
|
|
2549
|
-
if (toolCall.function?.name) block.name = toolCall.function.name;
|
|
2550
|
-
const deltaSig = extractToolCallThoughtSignature(toolCall);
|
|
2551
|
-
if (deltaSig) block.thoughtSignature = deltaSig;
|
|
2552
|
-
if (toolCall.function?.arguments) {
|
|
2553
|
-
block.partialArgs += toolCall.function.arguments;
|
|
2554
|
-
block.arguments = parseStreamingJson(block.partialArgs);
|
|
2555
|
-
pushStreamEvent({
|
|
2556
|
-
type: "toolcall_delta",
|
|
2557
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2558
|
-
delta: toolCall.function.arguments,
|
|
2559
|
-
partial: output
|
|
2560
|
-
});
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
flushPendingPostToolCallDeltas();
|
|
2566
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2567
|
-
await cooperativeScheduler.afterEvent();
|
|
2568
|
-
}
|
|
2569
|
-
flushReasoningTagTextPartitionerAtEnd();
|
|
2570
|
-
flushDeepSeekToolCallRecovererAtEnd();
|
|
2571
|
-
flushDeepSeekTextFilterAtEnd();
|
|
2572
|
-
currentBlock = null;
|
|
2573
|
-
flushPendingPostToolCallDeltas();
|
|
2574
|
-
finalizeOpenAICompletionsToolCalls(output, {
|
|
2575
|
-
allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
|
|
2576
|
-
onConfirmedToolCall(block, contentIndex) {
|
|
2577
|
-
pushStreamEvent({
|
|
2578
|
-
type: "toolcall_end",
|
|
2579
|
-
contentIndex,
|
|
2580
|
-
toolCall: block,
|
|
2581
|
-
partial: output
|
|
2582
|
-
});
|
|
2583
|
-
}
|
|
2584
|
-
});
|
|
2585
|
-
if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
|
|
2586
|
-
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
2587
|
-
}
|
|
2588
|
-
function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
|
|
2589
|
-
const output = [];
|
|
2590
|
-
const pushDelta = (next) => {
|
|
2591
|
-
const previous = output[output.length - 1];
|
|
2592
|
-
if (!previous || previous.kind !== next.kind) {
|
|
2593
|
-
output.push(next);
|
|
2594
|
-
return;
|
|
2595
|
-
}
|
|
2596
|
-
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
2597
|
-
if (previous.signature !== next.signature) {
|
|
2598
|
-
output.push(next);
|
|
2599
|
-
return;
|
|
2600
|
-
}
|
|
2601
|
-
previous.text += next.text;
|
|
2602
|
-
return;
|
|
2603
|
-
}
|
|
2604
|
-
previous.text += next.text;
|
|
2605
|
-
};
|
|
2606
|
-
const reasoningDetails = delta.reasoning_details;
|
|
2607
|
-
let usedReasoningThinkingDetails = false;
|
|
2608
|
-
if (Array.isArray(reasoningDetails)) {
|
|
2609
|
-
const visibleTypes = new Set(visibleReasoningDetailTypes);
|
|
2610
|
-
for (const item of reasoningDetails) {
|
|
2611
|
-
const detail = item;
|
|
2612
|
-
if (typeof detail.text !== "string" || !detail.text) continue;
|
|
2613
|
-
if (detail.type === "reasoning.text") {
|
|
2614
|
-
usedReasoningThinkingDetails = true;
|
|
2615
|
-
pushDelta({
|
|
2616
|
-
kind: "thinking",
|
|
2617
|
-
signature: "reasoning_details",
|
|
2618
|
-
text: detail.text
|
|
2619
|
-
});
|
|
2620
|
-
continue;
|
|
2621
|
-
}
|
|
2622
|
-
if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
|
|
2623
|
-
kind: "text",
|
|
2624
|
-
text: detail.text
|
|
2625
|
-
});
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
if (!usedReasoningThinkingDetails) for (const field of [
|
|
2629
|
-
"reasoning_content",
|
|
2630
|
-
"reasoning",
|
|
2631
|
-
"reasoning_text"
|
|
2632
|
-
]) {
|
|
2633
|
-
const value = delta[field];
|
|
2634
|
-
if (typeof value === "string" && value.length > 0) {
|
|
2635
|
-
pushDelta({
|
|
2636
|
-
kind: "thinking",
|
|
2637
|
-
signature: field,
|
|
2638
|
-
text: value
|
|
2639
|
-
});
|
|
2640
|
-
break;
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2643
|
-
return output;
|
|
2644
|
-
}
|
|
2645
|
-
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
2646
|
-
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
2647
|
-
}
|
|
2648
|
-
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
2649
|
-
if (!model.reasoning) return false;
|
|
2650
|
-
const effort = resolveOpenAICompletionsReasoningEffort(options);
|
|
2651
|
-
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
2652
|
-
return true;
|
|
2653
|
-
}
|
|
2654
|
-
function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
2655
|
-
const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
|
|
2656
|
-
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
2657
|
-
}
|
|
2658
|
-
//#endregion
|
|
2659
1686
|
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
2660
1687
|
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
2661
1688
|
const messages = params.messages;
|
|
@@ -2731,15 +1758,14 @@ function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
|
|
|
2731
1758
|
}
|
|
2732
1759
|
}
|
|
2733
1760
|
return {
|
|
2734
|
-
baseURL,
|
|
1761
|
+
baseURL: resolveOpenAIClientBaseUrl(model, baseURL),
|
|
2735
1762
|
defaultHeaders: headers,
|
|
2736
1763
|
defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
|
|
2737
1764
|
};
|
|
2738
1765
|
}
|
|
2739
1766
|
function createOpenAICompletionsTransportStreamFn() {
|
|
2740
1767
|
return (model, context, options) => {
|
|
2741
|
-
const eventStream =
|
|
2742
|
-
const stream = eventStream;
|
|
1768
|
+
const { eventStream, stream } = createWritableTransportEventStream();
|
|
2743
1769
|
(async () => {
|
|
2744
1770
|
const output = {
|
|
2745
1771
|
role: "assistant",
|
|
@@ -2794,7 +1820,7 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
2794
1820
|
if (nextParams !== void 0) params = nextParams;
|
|
2795
1821
|
if (options?.openclawCodeModeToolSurface === true) {
|
|
2796
1822
|
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
2797
|
-
enforceCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
1823
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames, void 0, codeModeToolSurfaceObserver.get(options));
|
|
2798
1824
|
assertCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
2799
1825
|
}
|
|
2800
1826
|
if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
|
|
@@ -2808,7 +1834,7 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
2808
1834
|
stream: responseStream,
|
|
2809
1835
|
signal: firstEventAbort.signal,
|
|
2810
1836
|
abort: firstEventAbort.abort,
|
|
2811
|
-
hook:
|
|
1837
|
+
hook: createOpenAIProviderAcceptanceHook(options, response, model),
|
|
2812
1838
|
onReady: () => stream.push({
|
|
2813
1839
|
type: "start",
|
|
2814
1840
|
partial: output
|
|
@@ -2816,6 +1842,7 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
2816
1842
|
}), output, model, stream, {
|
|
2817
1843
|
signal: options?.signal,
|
|
2818
1844
|
emitReasoning,
|
|
1845
|
+
strictReasoningTags: reasoningTagTextPolicy.isStrict(options),
|
|
2819
1846
|
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
2820
1847
|
abortFirstEventStream: firstEventAbort.abort,
|
|
2821
1848
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
@@ -2835,6 +1862,7 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
2835
1862
|
cleanup: () => {
|
|
2836
1863
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
2837
1864
|
finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
|
|
1865
|
+
tagUnresolvedTextAsCommentary(output);
|
|
2838
1866
|
}
|
|
2839
1867
|
});
|
|
2840
1868
|
} finally {
|
|
@@ -2889,7 +1917,7 @@ function jsonValuesEqual(left, right) {
|
|
|
2889
1917
|
return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
|
|
2890
1918
|
}
|
|
2891
1919
|
function requestWithoutInput(request) {
|
|
2892
|
-
const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
|
|
1920
|
+
const { input: _input, previous_response_id: _previousResponseId, instructions: _instructions, ...rest } = request;
|
|
2893
1921
|
if (!isRecord(rest.metadata)) return rest;
|
|
2894
1922
|
const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
|
|
2895
1923
|
return {
|
|
@@ -3080,18 +2108,27 @@ function sanitizeOpenAICodexResponsesParams(model, params) {
|
|
|
3080
2108
|
stripOpenAICodexResponsesUnsupportedTextFields(params);
|
|
3081
2109
|
return params;
|
|
3082
2110
|
}
|
|
3083
|
-
function
|
|
2111
|
+
function buildOpenAIResponsesInstructionsText(context) {
|
|
3084
2112
|
if (!context.systemPrompt) return;
|
|
3085
2113
|
return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
|
|
3086
2114
|
}
|
|
3087
|
-
function
|
|
3088
|
-
|
|
2115
|
+
function resolveOpenAIResponsesInstructions(model, context, usesInstructionsField) {
|
|
2116
|
+
if (!usesInstructionsField) return;
|
|
2117
|
+
const instructions = buildOpenAIResponsesInstructionsText(context);
|
|
3089
2118
|
if (instructions && instructions.trim().length > 0) return instructions;
|
|
3090
2119
|
return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
|
|
3091
2120
|
}
|
|
3092
|
-
function
|
|
2121
|
+
function buildOpenAIResponsesCompactSystemMessage(model, instructions) {
|
|
2122
|
+
const compat = getCompat(model);
|
|
2123
|
+
const supportsDeveloperRole = typeof compat.supportsDeveloperRole === "boolean" ? compat.supportsDeveloperRole : void 0;
|
|
2124
|
+
return buildResponsesInputMessage(model.reasoning && supportsDeveloperRole !== false ? "developer" : "system", [{
|
|
2125
|
+
type: "input_text",
|
|
2126
|
+
text: instructions
|
|
2127
|
+
}]);
|
|
2128
|
+
}
|
|
2129
|
+
function ensureOpenAIResponsesNonEmptyInput(messages, context) {
|
|
3093
2130
|
if (messages.length > 0 || !context.systemPrompt) return;
|
|
3094
|
-
if (!
|
|
2131
|
+
if (!buildOpenAIResponsesInstructionsText(context)) throw new Error("OpenAI Responses requires non-empty input when only systemPrompt is provided.");
|
|
3095
2132
|
messages.push(buildResponsesInputMessage("user", [{
|
|
3096
2133
|
type: "input_text",
|
|
3097
2134
|
text: " "
|
|
@@ -3105,16 +2142,12 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
|
3105
2142
|
return responseFormat;
|
|
3106
2143
|
}
|
|
3107
2144
|
function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
|
|
3108
|
-
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3109
2145
|
const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
|
|
3110
|
-
const compat = getCompat(model);
|
|
3111
|
-
const supportsDeveloperRole = typeof compat.supportsDeveloperRole === "boolean" ? compat.supportsDeveloperRole : void 0;
|
|
3112
2146
|
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3113
2147
|
const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
|
|
3114
2148
|
const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
|
|
3115
2149
|
return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
|
|
3116
|
-
includeSystemPrompt: !
|
|
3117
|
-
supportsDeveloperRole,
|
|
2150
|
+
includeSystemPrompt: !payloadPolicy.usesInstructionsField,
|
|
3118
2151
|
replayReasoningItems: true,
|
|
3119
2152
|
replayResponsesItemIds,
|
|
3120
2153
|
authProfileId: options?.authProfileId,
|
|
@@ -3123,19 +2156,19 @@ function convertOpenAIResponsesMessagesForRequest(model, context, options, repla
|
|
|
3123
2156
|
});
|
|
3124
2157
|
}
|
|
3125
2158
|
function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
|
|
3126
|
-
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3127
2159
|
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3128
2160
|
const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
|
|
3129
|
-
|
|
2161
|
+
ensureOpenAIResponsesNonEmptyInput(messages, context);
|
|
3130
2162
|
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
3131
2163
|
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
2164
|
+
const instructions = resolveOpenAIResponsesInstructions(model, context, payloadPolicy.usesInstructionsField);
|
|
3132
2165
|
const params = {
|
|
3133
2166
|
model: model.id,
|
|
3134
2167
|
input: messages,
|
|
3135
2168
|
stream: true,
|
|
3136
2169
|
prompt_cache_key: promptCacheKey,
|
|
3137
2170
|
prompt_cache_retention: getPromptCacheRetention(model.baseUrl, cacheRetention),
|
|
3138
|
-
...
|
|
2171
|
+
...instructions ? { instructions } : {},
|
|
3139
2172
|
...metadata ? { metadata } : {}
|
|
3140
2173
|
};
|
|
3141
2174
|
const effectiveMaxTokens = options?.maxTokens || model.maxTokens;
|
|
@@ -3405,9 +2438,8 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
3405
2438
|
}
|
|
3406
2439
|
const event = readServerEvent(next.value);
|
|
3407
2440
|
if (!event) continue;
|
|
3408
|
-
if (event.type === "response.failed") throw new OpenAIResponsesWebSocketResponseFailedError(event.response.output.length > 0);
|
|
3409
2441
|
if (event.type === "response.completed") terminalResponse = event.response;
|
|
3410
|
-
terminalReceived = event.type === "response.completed" || event.type === "response.incomplete";
|
|
2442
|
+
terminalReceived = event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed";
|
|
3411
2443
|
yield event;
|
|
3412
2444
|
if (terminalReceived) {
|
|
3413
2445
|
degradedWebSocketConnections.delete(degradationKey);
|
|
@@ -3419,7 +2451,7 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
3419
2451
|
const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
|
|
3420
2452
|
if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
|
|
3421
2453
|
if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
3422
|
-
if (!requestDispatched || params.callerSignal?.aborted ||
|
|
2454
|
+
if (!requestDispatched || params.callerSignal?.aborted || safeRetry) throw error;
|
|
3423
2455
|
throw new OpenAIResponsesWebSocketPostDispatchError(error);
|
|
3424
2456
|
} finally {
|
|
3425
2457
|
await iterator.return?.().catch(() => void 0);
|
|
@@ -3441,149 +2473,6 @@ function closeOpenAIResponsesWebSocketSessions(sessionId) {
|
|
|
3441
2473
|
}
|
|
3442
2474
|
registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
|
|
3443
2475
|
//#endregion
|
|
3444
|
-
//#region packages/media-core/src/inline-image-data-url.ts
|
|
3445
|
-
/** Prefix used to distinguish inline data URLs from remote/local image references. */
|
|
3446
|
-
const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
|
|
3447
|
-
const IMAGE_SIGNATURES = [
|
|
3448
|
-
{
|
|
3449
|
-
mime: "image/png",
|
|
3450
|
-
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
|
|
3451
|
-
},
|
|
3452
|
-
{
|
|
3453
|
-
mime: "image/jpeg",
|
|
3454
|
-
matches: (buffer) => buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255
|
|
3455
|
-
},
|
|
3456
|
-
{
|
|
3457
|
-
mime: "image/webp",
|
|
3458
|
-
matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
3459
|
-
},
|
|
3460
|
-
{
|
|
3461
|
-
mime: "image/gif",
|
|
3462
|
-
matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
|
|
3463
|
-
},
|
|
3464
|
-
{
|
|
3465
|
-
mime: "image/bmp",
|
|
3466
|
-
matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
|
|
3467
|
-
}
|
|
3468
|
-
];
|
|
3469
|
-
const HEIC_BRANDS = /* @__PURE__ */ new Set([
|
|
3470
|
-
"heic",
|
|
3471
|
-
"heix",
|
|
3472
|
-
"hevc",
|
|
3473
|
-
"hevx",
|
|
3474
|
-
"heis",
|
|
3475
|
-
"heim",
|
|
3476
|
-
"hevm",
|
|
3477
|
-
"hevs"
|
|
3478
|
-
]);
|
|
3479
|
-
const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
|
|
3480
|
-
const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
|
|
3481
|
-
const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
|
|
3482
|
-
"image/png",
|
|
3483
|
-
"image/jpeg",
|
|
3484
|
-
"image/webp",
|
|
3485
|
-
"image/gif"
|
|
3486
|
-
]);
|
|
3487
|
-
function startsWithDataUrl(value) {
|
|
3488
|
-
return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
|
|
3489
|
-
}
|
|
3490
|
-
function sniffIsoBmffImageMime(buffer) {
|
|
3491
|
-
if (buffer.length < 12 || buffer.subarray(4, 8).toString("ascii") !== "ftyp") return;
|
|
3492
|
-
const brands = [buffer.subarray(8, 12).toString("ascii")];
|
|
3493
|
-
for (let offset = 16; offset + 4 <= buffer.length; offset += 4) brands.push(buffer.subarray(offset, offset + 4).toString("ascii"));
|
|
3494
|
-
if (brands.some((brand) => HEIC_BRANDS.has(brand))) return "image/heic";
|
|
3495
|
-
if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
|
|
3496
|
-
}
|
|
3497
|
-
/** Sniffs supported inline image formats from decoded bytes. */
|
|
3498
|
-
function sniffInlineImageMime(buffer) {
|
|
3499
|
-
return IMAGE_SIGNATURES.find((signature) => signature.matches(buffer))?.mime ?? sniffIsoBmffImageMime(buffer);
|
|
3500
|
-
}
|
|
3501
|
-
function isImageMimeType(value) {
|
|
3502
|
-
return value.trim().toLowerCase().startsWith("image/");
|
|
3503
|
-
}
|
|
3504
|
-
/** Canonicalizes trusted inline image base64 and rejects malformed or non-image payloads. */
|
|
3505
|
-
function sanitizeInlineImageBase64(params) {
|
|
3506
|
-
if (!isImageMimeType(params.mimeType)) return;
|
|
3507
|
-
const canonicalPayload = canonicalizeBase64(params.base64);
|
|
3508
|
-
if (!canonicalPayload) return;
|
|
3509
|
-
const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
|
|
3510
|
-
if (!sniffedMimeType) return;
|
|
3511
|
-
return {
|
|
3512
|
-
mimeType: sniffedMimeType,
|
|
3513
|
-
base64: canonicalPayload
|
|
3514
|
-
};
|
|
3515
|
-
}
|
|
3516
|
-
function parseInlineImageDataUrl(value) {
|
|
3517
|
-
if (!startsWithDataUrl(value)) return {
|
|
3518
|
-
metadata: [],
|
|
3519
|
-
payload: value
|
|
3520
|
-
};
|
|
3521
|
-
const commaIndex = value.indexOf(",");
|
|
3522
|
-
if (commaIndex < 0) return;
|
|
3523
|
-
return {
|
|
3524
|
-
metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
|
|
3525
|
-
payload: value.slice(commaIndex + 1)
|
|
3526
|
-
};
|
|
3527
|
-
}
|
|
3528
|
-
function metadataAllowsImageBase64(metadata) {
|
|
3529
|
-
const [mimeType, ...options] = metadata;
|
|
3530
|
-
return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
|
|
3531
|
-
}
|
|
3532
|
-
function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
|
|
3533
|
-
const parsed = parseInlineImageDataUrl(imageUrl);
|
|
3534
|
-
if (!parsed) return;
|
|
3535
|
-
if (parsed.metadata.length === 0) return imageUrl;
|
|
3536
|
-
if (!metadataAllowsImageBase64(parsed.metadata)) return;
|
|
3537
|
-
const [mimeType] = parsed.metadata;
|
|
3538
|
-
const sanitized = sanitizeInlineImageBase64({
|
|
3539
|
-
mimeType: mimeType ?? "",
|
|
3540
|
-
base64: parsed.payload
|
|
3541
|
-
});
|
|
3542
|
-
if (!sanitized) return;
|
|
3543
|
-
if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
|
|
3544
|
-
return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
|
|
3545
|
-
}
|
|
3546
|
-
/** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
|
|
3547
|
-
function sanitizeInlineImageDataUrl(imageUrl) {
|
|
3548
|
-
return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
|
|
3549
|
-
}
|
|
3550
|
-
//#endregion
|
|
3551
|
-
//#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
|
|
3552
|
-
/**
|
|
3553
|
-
* Sanitizes OpenAI Responses payloads before transport. Invalid inline images
|
|
3554
|
-
* are replaced with text placeholders so the request remains valid and
|
|
3555
|
-
* auditable.
|
|
3556
|
-
*/
|
|
3557
|
-
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
|
|
3558
|
-
function invalidSnakeImage() {
|
|
3559
|
-
return {
|
|
3560
|
-
type: "input_text",
|
|
3561
|
-
text: `[${IMAGE_OMITTED_TEXT}]`
|
|
3562
|
-
};
|
|
3563
|
-
}
|
|
3564
|
-
function sanitizeValue(value) {
|
|
3565
|
-
if (Array.isArray(value)) return value.map(sanitizeValue);
|
|
3566
|
-
if (!isRecord(value)) return value;
|
|
3567
|
-
if (value.type === "input_image" && typeof value.image_url === "string") {
|
|
3568
|
-
const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
|
|
3569
|
-
return imageUrl ? {
|
|
3570
|
-
...value,
|
|
3571
|
-
image_url: imageUrl
|
|
3572
|
-
} : invalidSnakeImage();
|
|
3573
|
-
}
|
|
3574
|
-
const next = {};
|
|
3575
|
-
for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
|
|
3576
|
-
return next;
|
|
3577
|
-
}
|
|
3578
|
-
/** Sanitize inline image fields inside a Responses API payload. */
|
|
3579
|
-
function sanitizeResponsesImagePayload(params) {
|
|
3580
|
-
if (!Array.isArray(params.input)) return params;
|
|
3581
|
-
return {
|
|
3582
|
-
...params,
|
|
3583
|
-
input: sanitizeValue(params.input)
|
|
3584
|
-
};
|
|
3585
|
-
}
|
|
3586
|
-
//#endregion
|
|
3587
2476
|
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
3588
2477
|
function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
|
|
3589
2478
|
if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
|
|
@@ -3617,17 +2506,18 @@ function resolveProviderTransportTurnState(model, params) {
|
|
|
3617
2506
|
}
|
|
3618
2507
|
});
|
|
3619
2508
|
}
|
|
3620
|
-
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
|
|
2509
|
+
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId, fetchOverride) {
|
|
3621
2510
|
return new OpenAI({
|
|
3622
2511
|
apiKey,
|
|
3623
|
-
baseURL: model
|
|
2512
|
+
baseURL: resolveOpenAIClientBaseUrl(model),
|
|
3624
2513
|
dangerouslyAllowBrowser: true,
|
|
3625
2514
|
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
|
|
3626
|
-
fetch: buildGuardedModelFetch(model),
|
|
2515
|
+
fetch: fetchOverride ?? buildGuardedModelFetch(model),
|
|
3627
2516
|
...buildOpenAISdkClientOptions(model)
|
|
3628
2517
|
});
|
|
3629
2518
|
}
|
|
3630
2519
|
async function postOpenAIResponsesCompaction(params) {
|
|
2520
|
+
const compactInput = typeof params.request.instructions === "string" && params.request.instructions.length > 0 ? [buildOpenAIResponsesCompactSystemMessage(params.model, params.request.instructions), ...params.request.input ?? []] : params.request.input;
|
|
3631
2521
|
const response = await params.client.post("/responses/compact", {
|
|
3632
2522
|
...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
|
|
3633
2523
|
timeoutMs: params.options?.timeoutMs,
|
|
@@ -3635,15 +2525,21 @@ async function postOpenAIResponsesCompaction(params) {
|
|
|
3635
2525
|
}),
|
|
3636
2526
|
body: {
|
|
3637
2527
|
model: params.request.model,
|
|
3638
|
-
input:
|
|
2528
|
+
input: compactInput
|
|
3639
2529
|
}
|
|
3640
2530
|
});
|
|
3641
2531
|
const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
|
|
3642
|
-
const item = output
|
|
2532
|
+
const item = output.at(-1);
|
|
2533
|
+
const retainedItems = output.slice(0, -1);
|
|
2534
|
+
const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
|
|
2535
|
+
const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
|
|
2536
|
+
const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
|
|
3643
2537
|
const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
|
|
3644
|
-
if (!isRecord(response) || response.object !== "response.compaction" || output.length !==
|
|
2538
|
+
if (!isRecord(response) || response.object !== "response.compaction" || !isOpenAIResponsesCompactionOutput(output, params.model) || retainedItems.length > 0 && (!retainedMessagePrefixSupported || retainedUserMessageCount !== inputUserMessageCount) || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return one trailing compaction item");
|
|
3645
2539
|
return {
|
|
2540
|
+
output,
|
|
3646
2541
|
item,
|
|
2542
|
+
historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
|
|
3647
2543
|
usage,
|
|
3648
2544
|
model: params.model,
|
|
3649
2545
|
replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
|
|
@@ -3656,8 +2552,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3656
2552
|
return (model, context, options) => {
|
|
3657
2553
|
const responsesOptions = options;
|
|
3658
2554
|
const compactRequest = claimResponsesCompactRequest(responsesOptions);
|
|
3659
|
-
const eventStream =
|
|
3660
|
-
const stream = eventStream;
|
|
2555
|
+
const { eventStream, stream } = createWritableTransportEventStream();
|
|
3661
2556
|
(async () => {
|
|
3662
2557
|
const output = createOpenAIResponsesAssistantOutput(model, config.outputApi);
|
|
3663
2558
|
let firstEventAbort;
|
|
@@ -3673,7 +2568,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3673
2568
|
});
|
|
3674
2569
|
const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
|
|
3675
2570
|
const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
|
|
3676
|
-
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
|
|
2571
|
+
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId, compactRequest ? createBoundedOpenAIResponsesCompactionFetch(buildGuardedModelFetch(model)) : void 0);
|
|
3677
2572
|
const buildRequest = async (replayMode) => {
|
|
3678
2573
|
let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
|
|
3679
2574
|
const nextParams = await options?.onPayload?.(params, model);
|
|
@@ -3684,7 +2579,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3684
2579
|
if (options?.openclawCodeModeToolSurface === true) {
|
|
3685
2580
|
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
3686
2581
|
const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
|
|
3687
|
-
enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
|
|
2582
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes, codeModeToolSurfaceObserver.get(options));
|
|
3688
2583
|
assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
|
|
3689
2584
|
}
|
|
3690
2585
|
return params;
|
|
@@ -3701,12 +2596,11 @@ function createResponsesTransportExecutor(config) {
|
|
|
3701
2596
|
output.usage.output = compacted.usage.output_tokens;
|
|
3702
2597
|
output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
|
|
3703
2598
|
compactRequest.resolve(compacted);
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
2599
|
+
finalizeTransportStream({
|
|
2600
|
+
stream,
|
|
2601
|
+
output,
|
|
2602
|
+
signal: options?.signal
|
|
3708
2603
|
});
|
|
3709
|
-
stream.end();
|
|
3710
2604
|
return;
|
|
3711
2605
|
}
|
|
3712
2606
|
const sessionId = options?.sessionId;
|
|
@@ -3744,7 +2638,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3744
2638
|
emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} requestIdHash=${redactIdentifier(options?.requestId, { len: 64 })} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
|
|
3745
2639
|
let continuationBaseline;
|
|
3746
2640
|
const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
|
|
3747
|
-
const { stream:
|
|
2641
|
+
const { stream: responseStream } = await config.createResponseStream({
|
|
3748
2642
|
client,
|
|
3749
2643
|
request: initialRequest,
|
|
3750
2644
|
requestOptions,
|
|
@@ -3753,19 +2647,23 @@ function createResponsesTransportExecutor(config) {
|
|
|
3753
2647
|
initialAttemptKind,
|
|
3754
2648
|
initialRejectedCompaction,
|
|
3755
2649
|
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
3756
|
-
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
2650
|
+
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint),
|
|
2651
|
+
canRetryStream: () => output.content.length === 0,
|
|
2652
|
+
wrapStream: ({ stream: rawResponseStream, response, attempt }) => {
|
|
2653
|
+
if (continuationClaim) continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
|
|
2654
|
+
return withProviderResponseHook({
|
|
2655
|
+
stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
|
|
2656
|
+
signal: firstEvent.signal,
|
|
2657
|
+
abort: firstEvent.abort,
|
|
2658
|
+
hook: createOpenAIProviderAcceptanceHook(options, response, model),
|
|
2659
|
+
onReady: () => {
|
|
2660
|
+
emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
|
|
2661
|
+
startStream();
|
|
2662
|
+
}
|
|
2663
|
+
});
|
|
3767
2664
|
}
|
|
3768
2665
|
});
|
|
2666
|
+
return responseStream;
|
|
3769
2667
|
};
|
|
3770
2668
|
let responseStream;
|
|
3771
2669
|
let finishWebSocket;
|
|
@@ -3796,8 +2694,16 @@ function createResponsesTransportExecutor(config) {
|
|
|
3796
2694
|
transport = "websocket";
|
|
3797
2695
|
emitModelTransportDebug(log, `[responses] websocket_selected provider=${model.provider} api=${model.api} model=${model.id} mode=${websocketMode} reused=${websocket.reusedConnection} continuation=${websocket.continuationStatus === "continued"} continuationStatus=${websocket.continuationStatus} sessionIdHash=${redactIdentifier(options?.sessionId)} headersHash=${redactIdentifier(JSON.stringify(Object.entries(websocketHeaders ?? {}).toSorted(([a], [b]) => a.localeCompare(b))))}`);
|
|
3798
2696
|
responseStream = { async *[Symbol.asyncIterator]() {
|
|
2697
|
+
let providerAccepted = false;
|
|
3799
2698
|
try {
|
|
3800
2699
|
for await (const event of websocket.stream) {
|
|
2700
|
+
if (!providerAccepted) {
|
|
2701
|
+
providerAccepted = true;
|
|
2702
|
+
await notifyProviderStreamOpened({
|
|
2703
|
+
options,
|
|
2704
|
+
cancelStream: () => websocket.finish({ keep: false })
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
3801
2707
|
startStream();
|
|
3802
2708
|
yield event;
|
|
3803
2709
|
}
|
|
@@ -3848,33 +2754,30 @@ function createResponsesTransportExecutor(config) {
|
|
|
3848
2754
|
throw error;
|
|
3849
2755
|
}
|
|
3850
2756
|
emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
2757
|
+
finalizeTransportStream({
|
|
2758
|
+
stream,
|
|
2759
|
+
output,
|
|
2760
|
+
signal: options?.signal
|
|
3855
2761
|
});
|
|
3856
|
-
stream.end();
|
|
3857
2762
|
} catch (error) {
|
|
3858
2763
|
if (compactRequest) {
|
|
3859
2764
|
compactRequest.reject(error);
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
error
|
|
2765
|
+
failTransportStream({
|
|
2766
|
+
stream,
|
|
2767
|
+
output,
|
|
2768
|
+
signal: options?.signal,
|
|
2769
|
+
error
|
|
3865
2770
|
});
|
|
3866
|
-
stream.end();
|
|
3867
2771
|
return;
|
|
3868
2772
|
}
|
|
3869
2773
|
if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
|
|
3870
2774
|
log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
error
|
|
2775
|
+
failTransportStream({
|
|
2776
|
+
stream,
|
|
2777
|
+
output,
|
|
2778
|
+
signal: options?.signal,
|
|
2779
|
+
error
|
|
3876
2780
|
});
|
|
3877
|
-
stream.end();
|
|
3878
2781
|
} finally {
|
|
3879
2782
|
continuationClaim?.release();
|
|
3880
2783
|
firstEventAbort?.dispose();
|
|
@@ -3905,23 +2808,20 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
3905
2808
|
createResponseStream: createResponsesStreamWithEncryptedContentRetry
|
|
3906
2809
|
});
|
|
3907
2810
|
}
|
|
3908
|
-
function normalizeAzureBaseUrl(baseUrl) {
|
|
3909
|
-
return baseUrl.replace(/\/+$/, "");
|
|
3910
|
-
}
|
|
3911
2811
|
function resolveAzureDeploymentName(model) {
|
|
3912
2812
|
return resolveAzureDeploymentNameFromMap({
|
|
3913
2813
|
modelId: model.id,
|
|
3914
2814
|
deploymentMap: process.env.AZURE_OPENAI_DEPLOYMENT_NAME_MAP
|
|
3915
2815
|
});
|
|
3916
2816
|
}
|
|
3917
|
-
function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHeaders) {
|
|
3918
|
-
const baseURL =
|
|
2817
|
+
function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHeaders, _sessionId, fetchOverride) {
|
|
2818
|
+
const baseURL = model.baseUrl.replace(/\/+$/, "");
|
|
3919
2819
|
const clientOptions = {
|
|
3920
2820
|
apiKey,
|
|
3921
2821
|
dangerouslyAllowBrowser: true,
|
|
3922
2822
|
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders),
|
|
3923
2823
|
baseURL,
|
|
3924
|
-
fetch: buildGuardedModelFetch(model),
|
|
2824
|
+
fetch: fetchOverride ?? buildGuardedModelFetch(model),
|
|
3925
2825
|
...buildOpenAISdkClientOptions(model)
|
|
3926
2826
|
};
|
|
3927
2827
|
if (isOpenAICompatibleAzureResponsesBaseUrl(baseURL)) return new OpenAI(clientOptions);
|
|
@@ -3967,10 +2867,116 @@ const responsesTesting = {
|
|
|
3967
2867
|
if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
|
|
3968
2868
|
//#endregion
|
|
3969
2869
|
//#region packages/ai/src/transports/provider-compaction-replay.ts
|
|
2870
|
+
function isAssistantReplayMessage(message) {
|
|
2871
|
+
return message.role === "assistant";
|
|
2872
|
+
}
|
|
2873
|
+
/** Resolve from prepared transport facts; never retain or return credential material. */
|
|
2874
|
+
function resolveCompactionReplayEligibility(model, options) {
|
|
2875
|
+
if (OPENAI_RESPONSES_APIS.has(model.api)) return true;
|
|
2876
|
+
return Boolean(options.apiKey?.trim()) && resolveAnthropicServerCompactionPlan(model, options.extraParams, options.apiKey).enabled;
|
|
2877
|
+
}
|
|
2878
|
+
function resolveCompactionSource(messages, model, identity) {
|
|
2879
|
+
if (identity.enabled === false) return;
|
|
2880
|
+
const assistants = messages.filter(isAssistantReplayMessage);
|
|
2881
|
+
const selected = (() => {
|
|
2882
|
+
if (OPENAI_RESPONSES_APIS.has(model.api)) {
|
|
2883
|
+
const responses = resolveNewestOpenAIResponsesCompactionReplay(assistants, model, identity);
|
|
2884
|
+
return responses ? {
|
|
2885
|
+
family: "responses",
|
|
2886
|
+
...responses
|
|
2887
|
+
} : void 0;
|
|
2888
|
+
}
|
|
2889
|
+
if (identity.enabled !== true) return;
|
|
2890
|
+
const anthropic = resolveNewestAnthropicCompaction(assistants, model, identity);
|
|
2891
|
+
return anthropic ? {
|
|
2892
|
+
family: "anthropic",
|
|
2893
|
+
...anthropic
|
|
2894
|
+
} : void 0;
|
|
2895
|
+
})();
|
|
2896
|
+
if (!selected) return;
|
|
2897
|
+
const owner = assistants.find((message) => message === selected.owner);
|
|
2898
|
+
return owner ? {
|
|
2899
|
+
...selected,
|
|
2900
|
+
owner
|
|
2901
|
+
} : void 0;
|
|
2902
|
+
}
|
|
2903
|
+
/** Manual recovery uses the durable client compactor, never a guessed retained-user prefix. */
|
|
2904
|
+
function requiresCompactionReplayRefresh(messages, model, identity) {
|
|
2905
|
+
const checkpoint = resolveCompactionSource(messages, model, identity);
|
|
2906
|
+
return checkpoint?.family === "responses" && checkpoint.mode === "refresh-required";
|
|
2907
|
+
}
|
|
2908
|
+
/** Carry a checkpoint immediately after reference-preserving history limiting, before repair. */
|
|
2909
|
+
function preserveCompactionReplayWindow(source, windowed, model, identity) {
|
|
2910
|
+
const checkpoint = resolveCompactionSource(source, model, identity);
|
|
2911
|
+
if (!checkpoint || windowed.some((message) => message === checkpoint.owner)) return windowed;
|
|
2912
|
+
const owner = checkpoint.owner;
|
|
2913
|
+
const replay = checkpoint.owner.providerReplay;
|
|
2914
|
+
const providerReplay = replay?.type === "openai-responses-retained-compaction" ? replay : {
|
|
2915
|
+
...replay,
|
|
2916
|
+
replayIndex: 0
|
|
2917
|
+
};
|
|
2918
|
+
const suffix = new Set(source.slice(source.indexOf(owner) + 1));
|
|
2919
|
+
return [{
|
|
2920
|
+
...owner,
|
|
2921
|
+
content: [],
|
|
2922
|
+
providerReplay
|
|
2923
|
+
}, ...windowed.filter((message) => suffix.has(message))];
|
|
2924
|
+
}
|
|
2925
|
+
function estimateResponsesWindow(checkpoint, estimate) {
|
|
2926
|
+
return checkpoint.output.reduce((tokens, entry) => {
|
|
2927
|
+
if (entry.type === "compaction") {
|
|
2928
|
+
const { encrypted_content, ...metadata } = entry;
|
|
2929
|
+
return tokens + estimate.text(encrypted_content) + estimate.json(metadata);
|
|
2930
|
+
}
|
|
2931
|
+
const { content, ...metadata } = entry;
|
|
2932
|
+
return tokens + estimate.json(metadata) + content.reduce((sum, block) => {
|
|
2933
|
+
if (block.type === "input_text") {
|
|
2934
|
+
const { text, ...fields } = block;
|
|
2935
|
+
return sum + estimate.text(text) + estimate.json(fields);
|
|
2936
|
+
}
|
|
2937
|
+
if (block.type === "input_image") {
|
|
2938
|
+
const { image_url: _imageUrl, ...fields } = block;
|
|
2939
|
+
return sum + estimate.image() + estimate.json(block.image_url?.startsWith("data:") ? fields : block);
|
|
2940
|
+
}
|
|
2941
|
+
return sum + estimate.json(block);
|
|
2942
|
+
}, 0);
|
|
2943
|
+
}, 0);
|
|
2944
|
+
}
|
|
2945
|
+
/** Estimate the canonical prefix and its tail once, independent of unbound usage snapshots. */
|
|
2946
|
+
function resolveCompactionReplayPressure(messages, model, identity, estimate) {
|
|
2947
|
+
const checkpoint = resolveCompactionSource(messages, model, identity);
|
|
2948
|
+
if (!checkpoint) return;
|
|
2949
|
+
if (checkpoint.family === "responses" && checkpoint.mode === "refresh-required") throw new CompactionReplayRefreshRequiredError();
|
|
2950
|
+
const ownerIndex = messages.findIndex((message) => message === checkpoint.owner);
|
|
2951
|
+
const owner = messages[ownerIndex];
|
|
2952
|
+
if (!owner) return;
|
|
2953
|
+
const prefixTokens = checkpoint.family === "anthropic" ? estimate.text(checkpoint.summary) : checkpoint.mode === "complete-window" ? estimateResponsesWindow(checkpoint, estimate) : estimate.text(checkpoint.item.encrypted_content);
|
|
2954
|
+
const { contextUsage: _staleContextUsage, ...usage } = checkpoint.owner.usage;
|
|
2955
|
+
const tail = [];
|
|
2956
|
+
for (const message of messages.slice(ownerIndex + 1)) {
|
|
2957
|
+
if (!isAssistantReplayMessage(message) || message.usage.contextUsage === void 0) {
|
|
2958
|
+
tail.push(message);
|
|
2959
|
+
continue;
|
|
2960
|
+
}
|
|
2961
|
+
const { contextUsage: _unboundContextUsage, ...billedUsage } = message.usage;
|
|
2962
|
+
tail.push({
|
|
2963
|
+
...message,
|
|
2964
|
+
usage: billedUsage
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
return {
|
|
2968
|
+
messages: [{
|
|
2969
|
+
...owner,
|
|
2970
|
+
content: checkpoint.owner.content.slice(checkpoint.replayIndex),
|
|
2971
|
+
usage
|
|
2972
|
+
}, ...tail],
|
|
2973
|
+
prefixTokens
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
3970
2976
|
/** Whether provider replay state is a prefix-bound server compaction checkpoint. */
|
|
3971
2977
|
function isCompactionReplayCheckpoint(replay) {
|
|
3972
2978
|
const type = replay && typeof replay === "object" ? replay.type : void 0;
|
|
3973
|
-
return type === "anthropic-compaction" || type === "openai-responses-compaction";
|
|
2979
|
+
return type === "anthropic-compaction" || type === "openai-responses-compaction" || type === "openai-responses-retained-compaction";
|
|
3974
2980
|
}
|
|
3975
2981
|
/** Strip prefix-bound checkpoints after local history rewrites. */
|
|
3976
2982
|
function stripCompactionReplayCheckpoint(message) {
|
|
@@ -3991,8 +2997,9 @@ function replaceCompactionReplayOwnerContent(message, content) {
|
|
|
3991
2997
|
};
|
|
3992
2998
|
const replay = message.providerReplay;
|
|
3993
2999
|
if (!isCompactionReplayCheckpoint(replay)) return next;
|
|
3000
|
+
if (replay.type === "openai-responses-retained-compaction") return next;
|
|
3994
3001
|
const replayIndex = replay.replayIndex ?? 0;
|
|
3995
|
-
if (content.length === 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
|
|
3002
|
+
if (content.length === 0 && message.content.length > 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
|
|
3996
3003
|
let sourceIndex = 0;
|
|
3997
3004
|
let nextReplayIndex = 0;
|
|
3998
3005
|
if (!content.every((block) => {
|
|
@@ -4261,4 +3268,4 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
4261
3268
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
4262
3269
|
}
|
|
4263
3270
|
//#endregion
|
|
4264
|
-
export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, replaceCompactionReplayOwnerContent, requestPreparedOpenAIResponsesCompaction, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveAnthropicServerCompactionPlan, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesCompactEndpointPlan, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesServerCompactionPlan, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompactionReplayCheckpoint, stripCompactionReplayCheckpointInPlace, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend, withProviderResponseHook };
|
|
3271
|
+
export { CompactionReplayRefreshRequiredError, GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, copyProviderAcceptanceObserver, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIProviderAcceptanceHook, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTerminalToolCallArguments, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, notifyProviderHttpMetadata, notifyProviderHttpResponse, notifyProviderStreamOpened, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, parseTerminalToolCallArguments, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, preserveCompactionReplayWindow, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, readOpenAICompletionsReasoningBatch, replaceCompactionReplayOwnerContent, requestPreparedOpenAIResponsesCompaction, requiresCompactionReplayRefresh, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveAnthropicServerCompactionPlan, resolveCodeModeResponsesVisibleToolNames, resolveCompactionReplayEligibility, resolveCompactionReplayPressure, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAIClientBaseUrl, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesCompactEndpointPlan, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesServerCompactionPlan, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompactionReplayCheckpoint, stripCompactionReplayCheckpointInPlace, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend, withProviderAcceptanceObserver, withProviderResponseHook };
|