@openclaw/ai 2026.8.1-beta.3 → 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-B8KwTtlb.mjs → anthropic-Djj7L1rx.mjs} +21 -21
- package/dist/{anthropic-compaction-replay-4Zx7-aCr.mjs → anthropic-compaction-replay-CY4tkD4V.mjs} +33 -34
- package/dist/{anthropic-payload-policy-CSx7MoKH.d.mts → anthropic-payload-policy-i_5_inWe.d.mts} +3 -2
- package/dist/{api-registry-DFxSnFqG.d.mts → api-registry-C9-wZd_W.d.mts} +2 -1
- package/dist/{azure-openai-responses-Aa4mVcYG.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-D91-ohD_.d.mts → event-stream-UhnxHuqt.d.mts} +1 -2
- package/dist/event-stream.d.mts +2 -1
- package/dist/{google-yDRnipeq.mjs → google-0Ca6TYbw.mjs} +4 -4
- package/dist/{google-shared-M1dl64nz.mjs → google-shared-DVfL9COU.mjs} +14 -7
- package/dist/{google-vertex-DCJHM_a5.mjs → google-vertex-5dAMxprN.mjs} +4 -4
- package/dist/{host-DF_yxOsA.mjs → host-D51fmkH6.mjs} +4 -8
- package/dist/{host-DJq_FGcz.d.mts → host-DoPJJ1KD.d.mts} +3 -3
- 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 +51 -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 -9
- package/dist/internal/retry-after.d.mts +2 -3
- package/dist/internal/runtime.d.mts +51 -40
- package/dist/internal/runtime.mjs +9 -9
- package/dist/internal/shared.d.mts +25 -25
- package/dist/internal/shared.mjs +3 -3
- package/dist/{json-parse-CDnesDM_.mjs → json-parse-BuAJEbdW.mjs} +18 -2
- package/dist/{mistral-D6hRXI7A.mjs → mistral-BCnrI-kV.mjs} +22 -25
- package/dist/{openai-chatgpt-responses-C339VIfT.mjs → openai-chatgpt-responses-CGBGWSqr.mjs} +31 -14
- package/dist/openai-completions-BaTuwID7.mjs +401 -0
- package/dist/{openai-completions-compat-B0KTleXp.d.mts → openai-completions-compat-DB7FFq7k.d.mts} +3 -3
- package/dist/openai-completions-stream-Bpns6uNc.mjs +1622 -0
- package/dist/{openai-responses-BXEcr-Tp.mjs → openai-responses-1GBcQujk.mjs} +8 -6
- package/dist/openai-responses-compaction-window-DwydGZI8.mjs +686 -0
- package/dist/{openai-responses-contracts-CD75pQ0m.d.mts → openai-responses-contracts-DgaR3t5T.d.mts} +14 -5
- package/dist/{openai-responses-payload-policy-CNGFT9zr.d.mts → openai-responses-payload-policy-rLRPsSmB.d.mts} +1 -0
- package/dist/{openai-responses-prompt-observer-internal-BG-1AsBI.mjs → openai-responses-prompt-observer-internal-_nhGTn2b.mjs} +3 -2
- package/dist/{openai-responses-shared-Cvt5MgEm.mjs → openai-responses-shared-zPmKnhF5.mjs} +315 -1468
- package/dist/openai-stop-reason-Drnn_6Qj.mjs +28 -0
- package/dist/openai-tool-schema-Dgl4H2MU.mjs +1695 -0
- package/dist/{provider-error-C8mBRJDl.mjs → provider-error-jZP5NMTZ.mjs} +3 -77
- package/dist/{provider-options-Cc5ZQ6Aa.d.mts → provider-options-HvA_slFa.d.mts} +13 -4
- package/dist/{provider-transcript-transform--gtf0ZbR.mjs → provider-transcript-transform-C01bQ5a3.mjs} +1 -1
- 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-5ygO2rZc.mjs → reasoning-tag-text-partitioner-CwzKb0bA.mjs} +1 -1
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{sanitize-unicode-BP7HLLZn.mjs → sanitize-unicode-DCkltN94.mjs} +1 -2
- package/dist/simple-options-DKx86yX9.mjs +155 -0
- package/dist/{anthropic-BQXQudmA.mjs → src-Bu3FiFAG.mjs} +1 -0
- 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-FEr-jOoZ.mjs → tool-schema-json-projection-qvTEs5Am.mjs} +2 -2
- package/dist/{simple-options-oqsXB6U3.mjs → transport-stream-shared-CT5ycdzn.mjs} +10 -118
- package/dist/transport-stream-shared-NjwWNaRY.d.mts +138 -0
- package/dist/{transport-utils-Bv0FHkd0.mjs → transport-utils-BHx9z8Cl.mjs} +9 -9
- package/dist/transports.d.mts +127 -189
- package/dist/transports.mjs +181 -1214
- package/dist/types-DtGu2d3C.d.mts +1 -0
- package/dist/{types-DTAP49HF.d.mts → types-h38Azne1.d.mts} +38 -17
- package/dist/types.d.mts +5 -4
- package/dist/types.mjs +1 -2
- package/dist/utf16-slice-qz3nsy87.mjs +84 -0
- package/dist/{validation-DYNNrU9u.d.mts → validation-C68lP2uy.d.mts} +1 -2
- package/dist/validation.d.mts +1 -1
- package/package.json +6 -6
- package/dist/anthropic-BECQCNdF.d.mts +0 -98
- package/dist/event-stream-BVVgDSdq.d.mts +0 -1
- package/dist/headers-B_e4-1J0.mjs +0 -9
- package/dist/index-BVVgDSdq.d.mts +0 -1
- package/dist/openai-completions-D88orSJ9.mjs +0 -632
- package/dist/openai-responses-contracts-DvILoqBk.mjs +0 -247
- package/dist/openai-responses-payload-policy-BDo8CQsx.mjs +0 -208
- package/dist/openai-stop-reason-9OONKyAm.mjs +0 -579
- package/dist/openai-tool-projection-CHGLt03m.mjs +0 -426
- package/dist/provider-options-AvldWZt8.mjs +0 -21
- 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,30 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { r as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { n as
|
|
9
|
-
import { r as
|
|
10
|
-
import { _ as isOpenAIGpt56Model, d as OpenAIResponsesWebSocketSafeRetryError, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel, i as OPENAI_RESPONSES_APIS, l as OpenAIResponsesWebSocketPostDispatchError, p as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketPreDispatchError, v as normalizeOpenAIReasoningEffort, w as uniqueStrings, y as resolveOpenAIReasoningEffortForModel } from "./openai-responses-contracts-DvILoqBk.mjs";
|
|
11
|
-
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";
|
|
12
10
|
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
13
|
-
import {
|
|
14
|
-
import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch
|
|
15
|
-
import {
|
|
16
|
-
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";
|
|
17
17
|
import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
|
|
18
|
-
import {
|
|
18
|
+
import { r as parseStreamingJson, t as createToolArgumentPreviewSchedule } from "./json-parse-BuAJEbdW.mjs";
|
|
19
19
|
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
20
|
-
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-Bv0FHkd0.mjs";
|
|
21
20
|
import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
|
|
22
|
-
import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { a as
|
|
26
|
-
import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-5ygO2rZc.mjs";
|
|
27
|
-
import { i as resolveOpenAIResponsesServerCompactionPlan, n as resolveOpenAIResponsesCompactEndpointPlan, r as resolveOpenAIResponsesPayloadPolicy, t as applyOpenAIResponsesPayloadPolicy } from "./openai-responses-payload-policy-BDo8CQsx.mjs";
|
|
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";
|
|
28
25
|
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
29
26
|
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
30
27
|
import { randomUUID } from "node:crypto";
|
|
@@ -61,9 +58,6 @@ function resolveAnthropicMessagesMaxTokens(params) {
|
|
|
61
58
|
const contextWindow = resolvePositiveAnthropicTokenLimit(params.modelContextWindow);
|
|
62
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)));
|
|
63
60
|
}
|
|
64
|
-
function isAnthropicOAuthToken(apiKey) {
|
|
65
|
-
return (resolveSecretSentinel(apiKey) ?? apiKey).includes("sk-ant-oat");
|
|
66
|
-
}
|
|
67
61
|
function isDirectAnthropicModel(model) {
|
|
68
62
|
if (normalizeLowercaseStringOrEmpty(model.provider) !== "anthropic") return false;
|
|
69
63
|
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
@@ -483,7 +477,7 @@ function createAnthropicTransportClient(params) {
|
|
|
483
477
|
}
|
|
484
478
|
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
|
|
485
479
|
if (needsInterleavedBeta) betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
486
|
-
if (
|
|
480
|
+
if (isAnthropicOAuthApiKey(apiKey)) {
|
|
487
481
|
const betaHeader = buildAnthropicBetaHeader(model, betaFeatures, { oauth: true });
|
|
488
482
|
return {
|
|
489
483
|
client: createAnthropicMessagesClient({
|
|
@@ -713,6 +707,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
713
707
|
}
|
|
714
708
|
const blocks = output.content;
|
|
715
709
|
const blockIndexes = /* @__PURE__ */ new Map();
|
|
710
|
+
const toolArgumentPreviewSchedules = /* @__PURE__ */ new WeakMap();
|
|
716
711
|
const sealedToolCalls = [];
|
|
717
712
|
const compactionCapture = createCompactionCapture(output, model, transportOptions);
|
|
718
713
|
const pendingThinkingSignatures = /* @__PURE__ */ new Map();
|
|
@@ -966,6 +961,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
966
961
|
};
|
|
967
962
|
output.content.push(block);
|
|
968
963
|
blockIndexes.set(index, output.content.length - 1);
|
|
964
|
+
toolArgumentPreviewSchedules.set(block, createToolArgumentPreviewSchedule());
|
|
969
965
|
eventSink.push({
|
|
970
966
|
type: "toolcall_start",
|
|
971
967
|
contentIndex: output.content.length - 1,
|
|
@@ -1038,7 +1034,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1038
1034
|
if (block?.type === "toolCall" && delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
1039
1035
|
const partialJson = `${block.partialJson ?? ""}${delta.partial_json}`;
|
|
1040
1036
|
block.partialJson = partialJson;
|
|
1041
|
-
block.arguments = parseAnthropicToolCallArguments(partialJson);
|
|
1037
|
+
if (toolArgumentPreviewSchedules.get(block)?.(partialJson.length)) block.arguments = parseAnthropicToolCallArguments(partialJson);
|
|
1042
1038
|
eventSink.push({
|
|
1043
1039
|
type: "toolcall_delta",
|
|
1044
1040
|
contentIndex: index,
|
|
@@ -1153,96 +1149,6 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1153
1149
|
};
|
|
1154
1150
|
}
|
|
1155
1151
|
//#endregion
|
|
1156
|
-
//#region packages/ai/src/transports/deepseek-text-filter.ts
|
|
1157
|
-
/**
|
|
1158
|
-
* DeepSeek DSML streaming text filter.
|
|
1159
|
-
* Removes provider-emitted DSML tool markup while buffering split tag prefixes
|
|
1160
|
-
* across streamed chunks.
|
|
1161
|
-
*/
|
|
1162
|
-
const DSML_KINDS = [
|
|
1163
|
-
"tool_use_error",
|
|
1164
|
-
"tool_calls",
|
|
1165
|
-
"tool_call",
|
|
1166
|
-
"function_calls"
|
|
1167
|
-
];
|
|
1168
|
-
const DSML_BARS = ["|", "|"];
|
|
1169
|
-
const DSML_OPEN_TOKENS = DSML_BARS.flatMap((bar) => DSML_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
|
|
1170
|
-
const DSML_CLOSE_TOKENS = DSML_BARS.flatMap((bar) => DSML_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
|
|
1171
|
-
const MAX_OPEN_TOKEN_LEN = Math.max(...DSML_OPEN_TOKENS.map((token) => token.length));
|
|
1172
|
-
const MAX_CLOSE_TOKEN_LEN = Math.max(...DSML_CLOSE_TOKENS.map((token) => token.length));
|
|
1173
|
-
/** Create an incremental text filter that strips DeepSeek DSML tool blocks. */
|
|
1174
|
-
function createDeepSeekTextFilter() {
|
|
1175
|
-
let buffer = "";
|
|
1176
|
-
let insideDsml = false;
|
|
1177
|
-
const consume = (final) => {
|
|
1178
|
-
const output = [];
|
|
1179
|
-
const emit = (text) => {
|
|
1180
|
-
if (text) output.push(text);
|
|
1181
|
-
};
|
|
1182
|
-
while (buffer) {
|
|
1183
|
-
if (insideDsml) {
|
|
1184
|
-
const close = findEarliestToken(buffer, DSML_CLOSE_TOKENS);
|
|
1185
|
-
if (close) {
|
|
1186
|
-
buffer = buffer.slice(close.index + close.token.length);
|
|
1187
|
-
insideDsml = false;
|
|
1188
|
-
continue;
|
|
1189
|
-
}
|
|
1190
|
-
const keep = final ? 0 : Math.min(buffer.length, MAX_CLOSE_TOKEN_LEN - 1);
|
|
1191
|
-
buffer = buffer.slice(buffer.length - keep);
|
|
1192
|
-
if (final) insideDsml = false;
|
|
1193
|
-
return output;
|
|
1194
|
-
}
|
|
1195
|
-
const open = findEarliestToken(buffer, DSML_OPEN_TOKENS);
|
|
1196
|
-
if (open) {
|
|
1197
|
-
emit(buffer.slice(0, open.index));
|
|
1198
|
-
buffer = buffer.slice(open.index + open.token.length);
|
|
1199
|
-
insideDsml = true;
|
|
1200
|
-
continue;
|
|
1201
|
-
}
|
|
1202
|
-
if (final) {
|
|
1203
|
-
emit(buffer);
|
|
1204
|
-
buffer = "";
|
|
1205
|
-
return output;
|
|
1206
|
-
}
|
|
1207
|
-
const keep = longestDsmlOpenPrefixSuffixLength(buffer);
|
|
1208
|
-
const emitLength = buffer.length - keep;
|
|
1209
|
-
if (emitLength <= 0) return output;
|
|
1210
|
-
emit(buffer.slice(0, emitLength));
|
|
1211
|
-
buffer = buffer.slice(emitLength);
|
|
1212
|
-
return output;
|
|
1213
|
-
}
|
|
1214
|
-
return output;
|
|
1215
|
-
};
|
|
1216
|
-
return {
|
|
1217
|
-
push(chunk) {
|
|
1218
|
-
buffer += chunk;
|
|
1219
|
-
return consume(false);
|
|
1220
|
-
},
|
|
1221
|
-
flush() {
|
|
1222
|
-
return consume(true);
|
|
1223
|
-
}
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
function findEarliestToken(text, tokens) {
|
|
1227
|
-
let best = null;
|
|
1228
|
-
for (const token of tokens) {
|
|
1229
|
-
const index = text.indexOf(token);
|
|
1230
|
-
if (index !== -1 && (!best || index < best.index)) best = {
|
|
1231
|
-
index,
|
|
1232
|
-
token
|
|
1233
|
-
};
|
|
1234
|
-
}
|
|
1235
|
-
return best;
|
|
1236
|
-
}
|
|
1237
|
-
function longestDsmlOpenPrefixSuffixLength(text) {
|
|
1238
|
-
const maxLength = Math.min(text.length, MAX_OPEN_TOKEN_LEN - 1);
|
|
1239
|
-
for (let length = maxLength; length > 0; length--) {
|
|
1240
|
-
const suffix = text.slice(text.length - length);
|
|
1241
|
-
if (DSML_OPEN_TOKENS.some((token) => token.startsWith(suffix))) return length;
|
|
1242
|
-
}
|
|
1243
|
-
return 0;
|
|
1244
|
-
}
|
|
1245
|
-
//#endregion
|
|
1246
1152
|
//#region packages/ai/src/transports/model-max-tokens-params.ts
|
|
1247
1153
|
/**
|
|
1248
1154
|
* Max-token parameter normalization across provider/native naming variants.
|
|
@@ -1541,230 +1447,6 @@ function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
|
1541
1447
|
});
|
|
1542
1448
|
}
|
|
1543
1449
|
//#endregion
|
|
1544
|
-
//#region packages/ai/src/transports/openai-transport-params.ts
|
|
1545
|
-
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
1546
|
-
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
1547
|
-
const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
1548
|
-
function readToolPayloadField(record, field) {
|
|
1549
|
-
try {
|
|
1550
|
-
return Object.hasOwn(record, field) ? record[field] : void 0;
|
|
1551
|
-
} catch {
|
|
1552
|
-
return;
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
function readCodeModePayloadToolName(tool) {
|
|
1556
|
-
if (!isRecord(tool)) return;
|
|
1557
|
-
const name = readToolPayloadField(tool, "name");
|
|
1558
|
-
if (typeof name === "string") return name;
|
|
1559
|
-
const fn = readToolPayloadField(tool, "function");
|
|
1560
|
-
if (!isRecord(fn)) return;
|
|
1561
|
-
const fnName = readToolPayloadField(fn, "name");
|
|
1562
|
-
return typeof fnName === "string" ? fnName : void 0;
|
|
1563
|
-
}
|
|
1564
|
-
function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
|
|
1565
|
-
if (!isRecord(tool)) return;
|
|
1566
|
-
const type = readToolPayloadField(tool, "type");
|
|
1567
|
-
if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
|
|
1568
|
-
try {
|
|
1569
|
-
if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
|
|
1570
|
-
} catch {
|
|
1571
|
-
return false;
|
|
1572
|
-
}
|
|
1573
|
-
return `hosted:${type}`;
|
|
1574
|
-
}
|
|
1575
|
-
const name = readCodeModePayloadToolName(tool);
|
|
1576
|
-
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
|
|
1577
|
-
}
|
|
1578
|
-
function readCodeModePayloadToolIdentities(payload) {
|
|
1579
|
-
if (!isRecord(payload)) return [];
|
|
1580
|
-
const tools = readToolPayloadField(payload, "tools");
|
|
1581
|
-
if (!Array.isArray(tools)) return [];
|
|
1582
|
-
return tools.flatMap((tool) => {
|
|
1583
|
-
if (!isRecord(tool)) return [];
|
|
1584
|
-
const identities = [];
|
|
1585
|
-
const name = readCodeModePayloadToolName(tool);
|
|
1586
|
-
if (name) identities.push(`client:${name}`);
|
|
1587
|
-
for (const key of ["functionDeclarations", "function_declarations"]) {
|
|
1588
|
-
const declarations = readToolPayloadField(tool, key);
|
|
1589
|
-
if (!Array.isArray(declarations)) continue;
|
|
1590
|
-
for (const declaration of declarations) {
|
|
1591
|
-
const declarationName = readCodeModePayloadToolName(declaration);
|
|
1592
|
-
if (declarationName) identities.push(`client:${declarationName}`);
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
const type = readToolPayloadField(tool, "type");
|
|
1596
|
-
if (typeof type === "string" && type !== "function") identities.push(`hosted:${type}`);
|
|
1597
|
-
return identities;
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes, observer) {
|
|
1601
|
-
if (!isRecord(payload)) return;
|
|
1602
|
-
const tools = readToolPayloadField(payload, "tools");
|
|
1603
|
-
if (!Array.isArray(tools)) return;
|
|
1604
|
-
const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
|
|
1605
|
-
payload.tools = tools.flatMap((tool) => {
|
|
1606
|
-
const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
|
|
1607
|
-
if (identity) return [tool];
|
|
1608
|
-
if (identity === false) return [];
|
|
1609
|
-
if (!isRecord(tool)) return [];
|
|
1610
|
-
const filteredGroups = {};
|
|
1611
|
-
for (const key of ["functionDeclarations", "function_declarations"]) {
|
|
1612
|
-
const declarations = readToolPayloadField(tool, key);
|
|
1613
|
-
if (!Array.isArray(declarations)) continue;
|
|
1614
|
-
const filtered = declarations.filter((declaration) => {
|
|
1615
|
-
const declarationName = readCodeModePayloadToolName(declaration);
|
|
1616
|
-
return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
|
|
1617
|
-
});
|
|
1618
|
-
if (filtered.length > 0) filteredGroups[key] = filtered;
|
|
1619
|
-
}
|
|
1620
|
-
return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
|
|
1621
|
-
});
|
|
1622
|
-
if (beforeToolIdentities) observer?.({
|
|
1623
|
-
beforeToolIdentities,
|
|
1624
|
-
afterToolIdentities: readCodeModePayloadToolIdentities(payload)
|
|
1625
|
-
});
|
|
1626
|
-
}
|
|
1627
|
-
function resolveCodeModeResponsesVisibleToolNames(context) {
|
|
1628
|
-
return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
|
|
1629
|
-
}
|
|
1630
|
-
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes, observer) {
|
|
1631
|
-
if (!isRecord(payload)) return;
|
|
1632
|
-
const tools = readToolPayloadField(payload, "tools");
|
|
1633
|
-
if (!Array.isArray(tools)) return;
|
|
1634
|
-
const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
|
|
1635
|
-
payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
|
|
1636
|
-
if (beforeToolIdentities) observer?.({
|
|
1637
|
-
beforeToolIdentities,
|
|
1638
|
-
afterToolIdentities: readCodeModePayloadToolIdentities(payload)
|
|
1639
|
-
});
|
|
1640
|
-
}
|
|
1641
|
-
function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1642
|
-
const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
|
|
1643
|
-
if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
|
|
1644
|
-
const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
|
|
1645
|
-
const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
|
|
1646
|
-
if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
|
|
1647
|
-
throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
|
|
1648
|
-
}
|
|
1649
|
-
function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
|
|
1650
|
-
return sha256Hex(JSON.stringify({
|
|
1651
|
-
transport: context.transport,
|
|
1652
|
-
provider: context.model.provider ?? null,
|
|
1653
|
-
model: context.model.id ?? null,
|
|
1654
|
-
diagnostics: diagnostics.map((entry) => ({
|
|
1655
|
-
toolIndex: entry.toolIndex,
|
|
1656
|
-
toolName: entry.toolName ?? null,
|
|
1657
|
-
violations: entry.violations
|
|
1658
|
-
}))
|
|
1659
|
-
}));
|
|
1660
|
-
}
|
|
1661
|
-
function shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context) {
|
|
1662
|
-
const key = buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context);
|
|
1663
|
-
if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.has(key)) return false;
|
|
1664
|
-
if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.size >= MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS) loggedOpenAIStrictToolDowngradeDiagnosticKeys.clear();
|
|
1665
|
-
loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
|
|
1666
|
-
return true;
|
|
1667
|
-
}
|
|
1668
|
-
function resolveOpenAIStrictToolFlagWithDiagnostics(projection, strictSetting, context) {
|
|
1669
|
-
const strict = resolveOpenAIProjectedToolsStrictToolFlag(projection, strictSetting);
|
|
1670
|
-
if (strictSetting === true && strict === false) {
|
|
1671
|
-
const diagnostics = findOpenAIStrictToolProjectionDiagnostics(projection);
|
|
1672
|
-
getAiTransportHost().logDebug("openai-transport", () => {
|
|
1673
|
-
if (!shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context)) return null;
|
|
1674
|
-
const sample = diagnostics.slice(0, 5).map((entry) => ({
|
|
1675
|
-
tool: entry.toolName ?? `tool[${entry.toolIndex}]`,
|
|
1676
|
-
violations: entry.violations.slice(0, 8)
|
|
1677
|
-
}));
|
|
1678
|
-
return {
|
|
1679
|
-
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`,
|
|
1680
|
-
data: {
|
|
1681
|
-
transport: context.transport,
|
|
1682
|
-
provider: context.model.provider,
|
|
1683
|
-
model: context.model.id,
|
|
1684
|
-
incompatibleToolCount: diagnostics.length,
|
|
1685
|
-
sample
|
|
1686
|
-
}
|
|
1687
|
-
};
|
|
1688
|
-
});
|
|
1689
|
-
}
|
|
1690
|
-
return strict;
|
|
1691
|
-
}
|
|
1692
|
-
function isOpenAICodexResponsesModel(model) {
|
|
1693
|
-
return OPENAI_CODEX_RESPONSES_PROVIDERS.has(model.provider) && (model.api === "openai-chatgpt-responses" || model.api === "openclaw-openai-chatgpt-responses-transport");
|
|
1694
|
-
}
|
|
1695
|
-
function isNativeOpenAICodexResponsesBaseUrl(baseUrl) {
|
|
1696
|
-
const trimmed = typeof baseUrl === "string" ? baseUrl.trim() : "";
|
|
1697
|
-
if (!trimmed) return false;
|
|
1698
|
-
try {
|
|
1699
|
-
const url = new URL(trimmed);
|
|
1700
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
1701
|
-
if (url.hostname.toLowerCase() !== "chatgpt.com") return false;
|
|
1702
|
-
const pathname = url.pathname.replace(/\/+$/u, "").toLowerCase();
|
|
1703
|
-
return [
|
|
1704
|
-
"/backend-api",
|
|
1705
|
-
"/backend-api/v1",
|
|
1706
|
-
"/backend-api/codex",
|
|
1707
|
-
"/backend-api/codex/v1"
|
|
1708
|
-
].includes(pathname);
|
|
1709
|
-
} catch {
|
|
1710
|
-
return false;
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
function usesNativeOpenAICodexResponsesBackend(model) {
|
|
1714
|
-
return isOpenAICodexResponsesModel(model) && isNativeOpenAICodexResponsesBaseUrl(model.baseUrl);
|
|
1715
|
-
}
|
|
1716
|
-
function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId) {
|
|
1717
|
-
const providerHeaders = { ...model.headers };
|
|
1718
|
-
if (model.provider === "github-copilot") Object.assign(providerHeaders, getAiTransportHost().buildCopilotDynamicHeaders(context.messages));
|
|
1719
|
-
const callerHeaders = {
|
|
1720
|
-
...optionHeaders,
|
|
1721
|
-
...turnHeaders
|
|
1722
|
-
};
|
|
1723
|
-
const resolvedHeaders = resolveProviderRequestPolicyConfig(model, {
|
|
1724
|
-
provider: model.provider,
|
|
1725
|
-
api: model.api,
|
|
1726
|
-
baseUrl: model.baseUrl,
|
|
1727
|
-
capability: "llm",
|
|
1728
|
-
transport: "stream",
|
|
1729
|
-
providerHeaders,
|
|
1730
|
-
callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
|
|
1731
|
-
precedence: "caller-wins"
|
|
1732
|
-
}).headers ?? {};
|
|
1733
|
-
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
|
|
1734
|
-
return resolvedHeaders;
|
|
1735
|
-
}
|
|
1736
|
-
function resolveOpenAISdkTimeoutMs(model, timeoutMs) {
|
|
1737
|
-
return resolveModelRequestTimeoutMs(model, timeoutMs);
|
|
1738
|
-
}
|
|
1739
|
-
function buildOpenAISdkClientOptions(model) {
|
|
1740
|
-
const timeout = resolveOpenAISdkTimeoutMs(model);
|
|
1741
|
-
return timeout === void 0 ? {} : { timeout };
|
|
1742
|
-
}
|
|
1743
|
-
function buildOpenAISdkRequestOptions(model, signal, options) {
|
|
1744
|
-
const timeout = resolveOpenAISdkTimeoutMs(model, options?.timeoutMs);
|
|
1745
|
-
const headers = options?.stream === true && usesNativeOpenAICodexResponsesBackend(model) ? { Accept: "text/event-stream" } : void 0;
|
|
1746
|
-
if (timeout === void 0 && options?.maxRetries === void 0 && !signal && !headers) return;
|
|
1747
|
-
return {
|
|
1748
|
-
...headers ? { headers } : {},
|
|
1749
|
-
...signal ? { signal } : {},
|
|
1750
|
-
...timeout !== void 0 ? { timeout } : {},
|
|
1751
|
-
...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
|
|
1752
|
-
};
|
|
1753
|
-
}
|
|
1754
|
-
function getCompat(model) {
|
|
1755
|
-
const resolved = resolveOpenAICompletionsCompat(model);
|
|
1756
|
-
const compat = model.compat ?? {};
|
|
1757
|
-
return {
|
|
1758
|
-
...resolved,
|
|
1759
|
-
cacheControlFormat: resolved.cacheControlFormat,
|
|
1760
|
-
reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, {}),
|
|
1761
|
-
openRouterRouting: resolved.openRouterRouting ?? {},
|
|
1762
|
-
vercelGatewayRouting: resolved.vercelGatewayRouting,
|
|
1763
|
-
requiresStringContent: compat.requiresStringContent ?? false,
|
|
1764
|
-
strictMessageKeys: compat.strictMessageKeys === true
|
|
1765
|
-
};
|
|
1766
|
-
}
|
|
1767
|
-
//#endregion
|
|
1768
1450
|
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
1769
1451
|
function isKnownOpenAICompletionsEndpoint(model) {
|
|
1770
1452
|
if (!model.baseUrl.trim()) return true;
|
|
@@ -1776,7 +1458,7 @@ function isKnownOpenAICompletionsEndpoint(model) {
|
|
|
1776
1458
|
return false;
|
|
1777
1459
|
}
|
|
1778
1460
|
}
|
|
1779
|
-
function resolveOpenAICompletionsReasoningEffort
|
|
1461
|
+
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
1780
1462
|
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1781
1463
|
}
|
|
1782
1464
|
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
@@ -1975,7 +1657,7 @@ function buildOpenAICompletionsParams(model, context, options) {
|
|
|
1975
1657
|
if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1976
1658
|
else params.max_completion_tokens = clampedMaxTokens;
|
|
1977
1659
|
}
|
|
1978
|
-
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort
|
|
1660
|
+
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
|
|
1979
1661
|
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1980
1662
|
model,
|
|
1981
1663
|
effort: completionsReasoningEffort,
|
|
@@ -2001,692 +1683,6 @@ function buildOpenAICompletionsParams(model, context, options) {
|
|
|
2001
1683
|
return params;
|
|
2002
1684
|
}
|
|
2003
1685
|
//#endregion
|
|
2004
|
-
//#region packages/ai/src/transports/openai-completions-dsml.ts
|
|
2005
|
-
const DEEPSEEK_DSML_BARS = ["|", "|"];
|
|
2006
|
-
const DEEPSEEK_DSML_TOOL_KINDS = [
|
|
2007
|
-
"tool_calls",
|
|
2008
|
-
"tool_call",
|
|
2009
|
-
"function_calls"
|
|
2010
|
-
];
|
|
2011
|
-
const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
|
|
2012
|
-
const DEEPSEEK_DSML_TOOL_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
|
|
2013
|
-
const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_BARS.map((bar) => `<${bar}DSML${bar}invoke`);
|
|
2014
|
-
const DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.map((bar) => `</${bar}DSML${bar}invoke>`);
|
|
2015
|
-
const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
|
|
2016
|
-
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));
|
|
2017
|
-
const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
|
|
2018
|
-
const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
|
|
2019
|
-
function createDsmlRecoverer() {
|
|
2020
|
-
let buffer = "";
|
|
2021
|
-
let bufferBytes = 0;
|
|
2022
|
-
let bufferEndsWithHighSurrogate = false;
|
|
2023
|
-
let pendingScanChars = 0;
|
|
2024
|
-
let activeOpenToken = null;
|
|
2025
|
-
let blockScanState = {
|
|
2026
|
-
offset: 0,
|
|
2027
|
-
mode: "outer",
|
|
2028
|
-
invokeOpenStart: -1
|
|
2029
|
-
};
|
|
2030
|
-
const resetBlockScan = () => {
|
|
2031
|
-
activeOpenToken = null;
|
|
2032
|
-
pendingScanChars = 0;
|
|
2033
|
-
blockScanState = {
|
|
2034
|
-
offset: 0,
|
|
2035
|
-
mode: "outer",
|
|
2036
|
-
invokeOpenStart: -1
|
|
2037
|
-
};
|
|
2038
|
-
};
|
|
2039
|
-
const consume = (final) => {
|
|
2040
|
-
const output = [];
|
|
2041
|
-
while (buffer) {
|
|
2042
|
-
const open = activeOpenToken ? {
|
|
2043
|
-
index: 0,
|
|
2044
|
-
token: activeOpenToken
|
|
2045
|
-
} : findEarliestStringToken(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS);
|
|
2046
|
-
if (!open) {
|
|
2047
|
-
resetBlockScan();
|
|
2048
|
-
if (final) {
|
|
2049
|
-
output.push({
|
|
2050
|
-
kind: "text",
|
|
2051
|
-
text: buffer
|
|
2052
|
-
});
|
|
2053
|
-
buffer = "";
|
|
2054
|
-
bufferBytes = 0;
|
|
2055
|
-
bufferEndsWithHighSurrogate = false;
|
|
2056
|
-
return output;
|
|
2057
|
-
}
|
|
2058
|
-
const keep = longestDeepSeekDsmlToolOpenPrefixSuffixLength(buffer);
|
|
2059
|
-
const emitLength = buffer.length - keep;
|
|
2060
|
-
if (emitLength > 0) {
|
|
2061
|
-
const emitted = buffer.slice(0, emitLength);
|
|
2062
|
-
output.push({
|
|
2063
|
-
kind: "text",
|
|
2064
|
-
text: emitted
|
|
2065
|
-
});
|
|
2066
|
-
bufferBytes -= Buffer.byteLength(emitted, "utf8");
|
|
2067
|
-
buffer = buffer.slice(emitted.length);
|
|
2068
|
-
if (!buffer) bufferEndsWithHighSurrogate = false;
|
|
2069
|
-
}
|
|
2070
|
-
return output;
|
|
2071
|
-
}
|
|
2072
|
-
if (open.index > 0) {
|
|
2073
|
-
const prefix = buffer.slice(0, open.index);
|
|
2074
|
-
output.push({
|
|
2075
|
-
kind: "text",
|
|
2076
|
-
text: prefix
|
|
2077
|
-
});
|
|
2078
|
-
bufferBytes -= Buffer.byteLength(prefix, "utf8");
|
|
2079
|
-
buffer = buffer.slice(prefix.length);
|
|
2080
|
-
resetBlockScan();
|
|
2081
|
-
}
|
|
2082
|
-
activeOpenToken = open.token;
|
|
2083
|
-
if (blockScanState.offset === 0) blockScanState.offset = open.token.length;
|
|
2084
|
-
const blockScan = scanDeepSeekDsmlToolBlock(buffer, open.token.replace("<", "</"), open.token.length, blockScanState);
|
|
2085
|
-
if (blockScan.kind === "nested-open") throw new Error("Nested DeepSeek DSML recovery wrappers are not supported");
|
|
2086
|
-
const close = blockScan.kind === "close" ? blockScan : null;
|
|
2087
|
-
if (!close) {
|
|
2088
|
-
if (final) {
|
|
2089
|
-
output.push({
|
|
2090
|
-
kind: "text",
|
|
2091
|
-
text: buffer
|
|
2092
|
-
});
|
|
2093
|
-
buffer = "";
|
|
2094
|
-
bufferBytes = 0;
|
|
2095
|
-
bufferEndsWithHighSurrogate = false;
|
|
2096
|
-
return output;
|
|
2097
|
-
}
|
|
2098
|
-
if (bufferBytes > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
|
|
2099
|
-
return output;
|
|
2100
|
-
}
|
|
2101
|
-
resetBlockScan();
|
|
2102
|
-
const body = buffer.slice(open.token.length, close.index);
|
|
2103
|
-
const blockText = buffer.slice(0, close.index + close.token.length);
|
|
2104
|
-
if (Buffer.byteLength(blockText, "utf8") > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
|
|
2105
|
-
const recoveredToolCalls = parseDeepSeekDsmlToolCallBlock(body);
|
|
2106
|
-
if (recoveredToolCalls.length > 0) output.push(...recoveredToolCalls);
|
|
2107
|
-
else output.push({
|
|
2108
|
-
kind: "text",
|
|
2109
|
-
text: blockText
|
|
2110
|
-
});
|
|
2111
|
-
bufferBytes -= Buffer.byteLength(blockText, "utf8");
|
|
2112
|
-
buffer = buffer.slice(blockText.length);
|
|
2113
|
-
if (!buffer) bufferEndsWithHighSurrogate = false;
|
|
2114
|
-
}
|
|
2115
|
-
return output;
|
|
2116
|
-
};
|
|
2117
|
-
return {
|
|
2118
|
-
push(chunk) {
|
|
2119
|
-
const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
|
|
2120
|
-
bufferBytes += append.bytes;
|
|
2121
|
-
bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
|
|
2122
|
-
buffer += chunk;
|
|
2123
|
-
pendingScanChars += chunk.length;
|
|
2124
|
-
if (activeOpenToken && pendingScanChars < DEEPSEEK_DSML_SCAN_BATCH_CHARS && !chunk.includes("<") && !chunk.includes(">") && bufferBytes <= MAX_DSML_RECOVERY_BUFFER_BYTES) return [];
|
|
2125
|
-
pendingScanChars = 0;
|
|
2126
|
-
return consume(false);
|
|
2127
|
-
},
|
|
2128
|
-
flush() {
|
|
2129
|
-
return consume(true);
|
|
2130
|
-
}
|
|
2131
|
-
};
|
|
2132
|
-
}
|
|
2133
|
-
function parseDeepSeekDsmlToolCallBlock(body) {
|
|
2134
|
-
const toolCalls = [];
|
|
2135
|
-
const invokeOpenRegex = /<[||]DSML[||]invoke\b([^<>]*)>/g;
|
|
2136
|
-
let openMatch;
|
|
2137
|
-
while ((openMatch = invokeOpenRegex.exec(body)) !== null) {
|
|
2138
|
-
const invokeBodyStart = openMatch.index + openMatch[0].length;
|
|
2139
|
-
const invokeClose = findEarliestStringToken(body.slice(invokeBodyStart), ["</|DSML|invoke>", "</|DSML|invoke>"]);
|
|
2140
|
-
if (!invokeClose) break;
|
|
2141
|
-
const invokeBody = body.slice(invokeBodyStart, invokeBodyStart + invokeClose.index);
|
|
2142
|
-
invokeOpenRegex.lastIndex = invokeBodyStart + invokeClose.index + invokeClose.token.length;
|
|
2143
|
-
const invokeName = parseXmlAttribute(openMatch[1] ?? "", "name");
|
|
2144
|
-
if (!invokeName) continue;
|
|
2145
|
-
const parsedArguments = parseDeepSeekDsmlInvokeArguments(invokeBody);
|
|
2146
|
-
if (!parsedArguments) continue;
|
|
2147
|
-
toolCalls.push({
|
|
2148
|
-
kind: "toolCall",
|
|
2149
|
-
name: invokeName,
|
|
2150
|
-
arguments: parsedArguments,
|
|
2151
|
-
partialArgs: JSON.stringify(parsedArguments)
|
|
2152
|
-
});
|
|
2153
|
-
}
|
|
2154
|
-
return toolCalls;
|
|
2155
|
-
}
|
|
2156
|
-
function parseDeepSeekDsmlInvokeArguments(body) {
|
|
2157
|
-
const args = {};
|
|
2158
|
-
const parameterRegex = /<[||]DSML[||]parameter\b([^>]*)>([\s\S]*?)<\/[||]DSML[||]parameter>/g;
|
|
2159
|
-
let parameterMatch;
|
|
2160
|
-
while ((parameterMatch = parameterRegex.exec(body)) !== null) {
|
|
2161
|
-
const name = parseXmlAttribute(parameterMatch[1] ?? "", "name");
|
|
2162
|
-
if (!name) continue;
|
|
2163
|
-
const rawValue = parameterMatch[2] ?? "";
|
|
2164
|
-
if (rawValue.length === 0) continue;
|
|
2165
|
-
args[name] = decodeDeepSeekDsmlText(rawValue);
|
|
2166
|
-
}
|
|
2167
|
-
if (Object.keys(args).length > 0) return args;
|
|
2168
|
-
const trimmed = body.trim();
|
|
2169
|
-
if (!trimmed.startsWith("{")) return null;
|
|
2170
|
-
try {
|
|
2171
|
-
const parsed = JSON.parse(trimmed);
|
|
2172
|
-
if (isRecord(parsed) && Object.keys(parsed).length > 0) return parsed;
|
|
2173
|
-
} catch {
|
|
2174
|
-
return null;
|
|
2175
|
-
}
|
|
2176
|
-
return null;
|
|
2177
|
-
}
|
|
2178
|
-
const xmlAttributeRegexCache = /* @__PURE__ */ new Map();
|
|
2179
|
-
function xmlAttributeRegex(name) {
|
|
2180
|
-
const cached = xmlAttributeRegexCache.get(name);
|
|
2181
|
-
if (cached) return cached;
|
|
2182
|
-
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2183
|
-
const pattern = new RegExp(`\\b${escaped}=("([^"]*)"|'([^']*)'|([^\\s>]+))`);
|
|
2184
|
-
xmlAttributeRegexCache.set(name, pattern);
|
|
2185
|
-
return pattern;
|
|
2186
|
-
}
|
|
2187
|
-
function parseXmlAttribute(attributes, name) {
|
|
2188
|
-
const match = xmlAttributeRegex(name).exec(attributes);
|
|
2189
|
-
const value = match?.[2] ?? match?.[3] ?? match?.[4];
|
|
2190
|
-
return value ? decodeDeepSeekDsmlText(value) : null;
|
|
2191
|
-
}
|
|
2192
|
-
function decodeDeepSeekDsmlText(value) {
|
|
2193
|
-
return value.replaceAll(""", "\"").replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
2194
|
-
}
|
|
2195
|
-
function findEarliestStringToken(text, tokens, fromIndex = 0) {
|
|
2196
|
-
let best = null;
|
|
2197
|
-
for (const token of tokens) {
|
|
2198
|
-
const index = text.indexOf(token, fromIndex);
|
|
2199
|
-
if (index !== -1 && (!best || index < best.index)) best = {
|
|
2200
|
-
index,
|
|
2201
|
-
token
|
|
2202
|
-
};
|
|
2203
|
-
}
|
|
2204
|
-
return best;
|
|
2205
|
-
}
|
|
2206
|
-
function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
|
|
2207
|
-
while (state.offset < text.length) {
|
|
2208
|
-
if (state.mode === "invoke-open") {
|
|
2209
|
-
const nextOpen = text.indexOf("<", state.offset);
|
|
2210
|
-
const nextClose = text.indexOf(">", state.offset);
|
|
2211
|
-
if (nextClose === -1 && nextOpen === -1) {
|
|
2212
|
-
state.offset = text.length;
|
|
2213
|
-
return { kind: "incomplete" };
|
|
2214
|
-
}
|
|
2215
|
-
if (nextOpen !== -1 && (nextClose === -1 || nextOpen < nextClose)) {
|
|
2216
|
-
state.mode = "outer";
|
|
2217
|
-
state.offset = nextOpen;
|
|
2218
|
-
state.invokeOpenStart = -1;
|
|
2219
|
-
continue;
|
|
2220
|
-
}
|
|
2221
|
-
const invokeOpenTag = text.slice(state.invokeOpenStart, nextClose + 1);
|
|
2222
|
-
if (!/^<[||]DSML[||]invoke\b[^<>]*>$/.test(invokeOpenTag)) {
|
|
2223
|
-
state.mode = "outer";
|
|
2224
|
-
state.offset = state.invokeOpenStart + 1;
|
|
2225
|
-
state.invokeOpenStart = -1;
|
|
2226
|
-
continue;
|
|
2227
|
-
}
|
|
2228
|
-
state.mode = "invoke-body";
|
|
2229
|
-
state.offset = nextClose + 1;
|
|
2230
|
-
state.invokeOpenStart = -1;
|
|
2231
|
-
continue;
|
|
2232
|
-
}
|
|
2233
|
-
if (state.mode === "invoke-body") {
|
|
2234
|
-
const invokeClose = findEarliestStringToken(text, DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS, state.offset);
|
|
2235
|
-
if (!invokeClose) {
|
|
2236
|
-
state.offset = Math.max(0, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
|
|
2237
|
-
return { kind: "incomplete" };
|
|
2238
|
-
}
|
|
2239
|
-
state.mode = "outer";
|
|
2240
|
-
state.offset = invokeClose.index + invokeClose.token.length;
|
|
2241
|
-
continue;
|
|
2242
|
-
}
|
|
2243
|
-
const toolOpen = findEarliestStringToken(text, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, state.offset);
|
|
2244
|
-
const toolCloseIndex = text.indexOf(closeToken, state.offset);
|
|
2245
|
-
const invokeOpen = findEarliestStringToken(text, DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES, state.offset);
|
|
2246
|
-
const next = [
|
|
2247
|
-
toolOpen ? {
|
|
2248
|
-
kind: "nested-open",
|
|
2249
|
-
...toolOpen
|
|
2250
|
-
} : null,
|
|
2251
|
-
toolCloseIndex === -1 ? null : {
|
|
2252
|
-
kind: "close",
|
|
2253
|
-
index: toolCloseIndex,
|
|
2254
|
-
token: closeToken
|
|
2255
|
-
},
|
|
2256
|
-
invokeOpen ? {
|
|
2257
|
-
kind: "invoke-open",
|
|
2258
|
-
...invokeOpen
|
|
2259
|
-
} : null
|
|
2260
|
-
].filter((candidate) => candidate !== null).toSorted((left, right) => left.index - right.index)[0];
|
|
2261
|
-
if (!next) {
|
|
2262
|
-
state.offset = Math.max(contentStartIndex, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
|
|
2263
|
-
return { kind: "incomplete" };
|
|
2264
|
-
}
|
|
2265
|
-
if (next.kind === "invoke-open") {
|
|
2266
|
-
state.mode = "invoke-open";
|
|
2267
|
-
state.invokeOpenStart = next.index;
|
|
2268
|
-
state.offset = next.index + next.token.length;
|
|
2269
|
-
continue;
|
|
2270
|
-
}
|
|
2271
|
-
return next;
|
|
2272
|
-
}
|
|
2273
|
-
return { kind: "incomplete" };
|
|
2274
|
-
}
|
|
2275
|
-
function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
|
|
2276
|
-
const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
|
|
2277
|
-
for (let length = maxLength; length > 0; length -= 1) {
|
|
2278
|
-
const suffix = text.slice(text.length - length);
|
|
2279
|
-
if (DEEPSEEK_DSML_TOOL_OPEN_TOKENS.some((token) => token.startsWith(suffix))) return length;
|
|
2280
|
-
}
|
|
2281
|
-
return 0;
|
|
2282
|
-
}
|
|
2283
|
-
//#endregion
|
|
2284
|
-
//#region packages/ai/src/transports/openai-completions-stream.ts
|
|
2285
|
-
function extractToolCallThoughtSignature(toolCall) {
|
|
2286
|
-
const tc = toolCall;
|
|
2287
|
-
if (!tc) return;
|
|
2288
|
-
const fromExtra = (tc.extra_content?.google)?.thought_signature;
|
|
2289
|
-
if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
|
|
2290
|
-
const fromFunction = tc.function?.thought_signature;
|
|
2291
|
-
if (typeof fromFunction === "string" && fromFunction.length > 0) return fromFunction;
|
|
2292
|
-
const fromToolCall = tc.thought_signature;
|
|
2293
|
-
return typeof fromToolCall === "string" && fromToolCall.length > 0 ? fromToolCall : void 0;
|
|
2294
|
-
}
|
|
2295
|
-
async function processCompletionsStream(responseStream, output, model, stream, options) {
|
|
2296
|
-
const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
|
|
2297
|
-
const emitReasoning = options?.emitReasoning ?? true;
|
|
2298
|
-
const compat = getCompat(model);
|
|
2299
|
-
const visibleReasoningDetailTypes = new Set(compat.visibleReasoningDetailTypes);
|
|
2300
|
-
const shouldFilterDeepSeekDsmlText = compat.thinkingFormat === "deepseek";
|
|
2301
|
-
const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
|
|
2302
|
-
const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
|
|
2303
|
-
const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
|
|
2304
|
-
let currentBlock = null;
|
|
2305
|
-
let currentTextSource;
|
|
2306
|
-
let pendingInterruptedTextBlock = null;
|
|
2307
|
-
let confirmedInterruptedTextBlock = null;
|
|
2308
|
-
let pendingPostToolCallDeltas = [];
|
|
2309
|
-
let pendingPostToolCallBytes = 0;
|
|
2310
|
-
let isFlushingPendingPostToolCallDeltas = false;
|
|
2311
|
-
const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
|
|
2312
|
-
const toolCallBlocksById = /* @__PURE__ */ new Map();
|
|
2313
|
-
const provisionalCommentaryTags = /* @__PURE__ */ new Map();
|
|
2314
|
-
const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
|
|
2315
|
-
let explicitVisibleTextBlocks;
|
|
2316
|
-
const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
|
|
2317
|
-
let sawStopFinishReason = false;
|
|
2318
|
-
let sawNativeToolCallDelta = false;
|
|
2319
|
-
const blockIndex = () => output.content.length - 1;
|
|
2320
|
-
const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
|
|
2321
|
-
let chunkPushedEvent = false;
|
|
2322
|
-
const pushStreamEvent = (event) => {
|
|
2323
|
-
chunkPushedEvent = true;
|
|
2324
|
-
stream.push(event);
|
|
2325
|
-
};
|
|
2326
|
-
const queuePostToolCallDelta = (next) => {
|
|
2327
|
-
const nextBytes = measureUtf8Bytes(next.text);
|
|
2328
|
-
if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
|
|
2329
|
-
pendingPostToolCallBytes += nextBytes;
|
|
2330
|
-
const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
|
|
2331
|
-
if (!previous || previous.kind !== next.kind || previous.kind === "text" && next.kind === "text" && previous.source !== next.source) {
|
|
2332
|
-
pendingPostToolCallDeltas.push(next);
|
|
2333
|
-
return;
|
|
2334
|
-
}
|
|
2335
|
-
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
2336
|
-
if (previous.signature !== next.signature) {
|
|
2337
|
-
pendingPostToolCallDeltas.push(next);
|
|
2338
|
-
return;
|
|
2339
|
-
}
|
|
2340
|
-
previous.text += next.text;
|
|
2341
|
-
return;
|
|
2342
|
-
}
|
|
2343
|
-
previous.text += next.text;
|
|
2344
|
-
};
|
|
2345
|
-
const appendThinkingDeltaInternal = (reasoningDelta) => {
|
|
2346
|
-
if (!currentBlock || currentBlock.type !== "thinking") {
|
|
2347
|
-
currentBlock = {
|
|
2348
|
-
type: "thinking",
|
|
2349
|
-
thinking: "",
|
|
2350
|
-
...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
|
|
2351
|
-
};
|
|
2352
|
-
output.content.push(currentBlock);
|
|
2353
|
-
pushStreamEvent({
|
|
2354
|
-
type: "thinking_start",
|
|
2355
|
-
contentIndex: blockIndex(),
|
|
2356
|
-
partial: output
|
|
2357
|
-
});
|
|
2358
|
-
}
|
|
2359
|
-
currentBlock.thinking += reasoningDelta.text;
|
|
2360
|
-
pushStreamEvent({
|
|
2361
|
-
type: "thinking_delta",
|
|
2362
|
-
contentIndex: blockIndex(),
|
|
2363
|
-
delta: reasoningDelta.text,
|
|
2364
|
-
partial: output
|
|
2365
|
-
});
|
|
2366
|
-
};
|
|
2367
|
-
const appendTextDeltaInternal = (text, source) => {
|
|
2368
|
-
if (currentBlock?.type === "text" && currentTextSource !== source) currentBlock = null;
|
|
2369
|
-
if (!currentBlock || currentBlock.type !== "text") {
|
|
2370
|
-
currentBlock = {
|
|
2371
|
-
type: "text",
|
|
2372
|
-
text: ""
|
|
2373
|
-
};
|
|
2374
|
-
currentTextSource = source;
|
|
2375
|
-
if (source === "reasoning_detail") (explicitVisibleTextBlocks ??= /* @__PURE__ */ new Set()).add(currentBlock);
|
|
2376
|
-
output.content.push(currentBlock);
|
|
2377
|
-
pushStreamEvent({
|
|
2378
|
-
type: "text_start",
|
|
2379
|
-
contentIndex: blockIndex(),
|
|
2380
|
-
partial: output
|
|
2381
|
-
});
|
|
2382
|
-
}
|
|
2383
|
-
currentBlock.text += text;
|
|
2384
|
-
if (pendingInterruptedTextBlock && text.trim()) {
|
|
2385
|
-
confirmedInterruptedTextBlock = pendingInterruptedTextBlock;
|
|
2386
|
-
pendingInterruptedTextBlock = null;
|
|
2387
|
-
}
|
|
2388
|
-
pushStreamEvent({
|
|
2389
|
-
type: "text_delta",
|
|
2390
|
-
contentIndex: blockIndex(),
|
|
2391
|
-
delta: text
|
|
2392
|
-
});
|
|
2393
|
-
};
|
|
2394
|
-
const flushPendingPostToolCallDeltas = () => {
|
|
2395
|
-
if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
|
|
2396
|
-
isFlushingPendingPostToolCallDeltas = true;
|
|
2397
|
-
const bufferedDeltas = pendingPostToolCallDeltas;
|
|
2398
|
-
pendingPostToolCallDeltas = [];
|
|
2399
|
-
pendingPostToolCallBytes = 0;
|
|
2400
|
-
for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text, delta.source);
|
|
2401
|
-
else if (emitReasoning) appendThinkingDeltaInternal(delta);
|
|
2402
|
-
isFlushingPendingPostToolCallDeltas = false;
|
|
2403
|
-
};
|
|
2404
|
-
const appendThinkingDelta = (reasoningDelta) => {
|
|
2405
|
-
flushPendingPostToolCallDeltas();
|
|
2406
|
-
appendThinkingDeltaInternal(reasoningDelta);
|
|
2407
|
-
};
|
|
2408
|
-
const appendTextDelta = (text, source) => {
|
|
2409
|
-
flushPendingPostToolCallDeltas();
|
|
2410
|
-
appendTextDeltaInternal(text, source);
|
|
2411
|
-
};
|
|
2412
|
-
const appendVisibleTextDelta = (text) => {
|
|
2413
|
-
if (!text) return;
|
|
2414
|
-
if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
|
|
2415
|
-
kind: "text",
|
|
2416
|
-
text
|
|
2417
|
-
});
|
|
2418
|
-
else appendTextDelta(text);
|
|
2419
|
-
};
|
|
2420
|
-
const appendReasoningDeltas = (reasoningDeltas) => {
|
|
2421
|
-
for (const reasoningDelta of reasoningDeltas) {
|
|
2422
|
-
if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
|
|
2423
|
-
if (currentBlock?.type === "toolCall") {
|
|
2424
|
-
queuePostToolCallDelta({ ...reasoningDelta });
|
|
2425
|
-
continue;
|
|
2426
|
-
}
|
|
2427
|
-
if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text, reasoningDelta.source);
|
|
2428
|
-
else if (emitReasoning) appendThinkingDelta(reasoningDelta);
|
|
2429
|
-
}
|
|
2430
|
-
};
|
|
2431
|
-
const appendRecoveredToolCall = (toolCall) => {
|
|
2432
|
-
if (currentBlock?.type === "toolCall") {
|
|
2433
|
-
currentBlock = null;
|
|
2434
|
-
flushPendingPostToolCallDeltas();
|
|
2435
|
-
}
|
|
2436
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
2437
|
-
const block = {
|
|
2438
|
-
type: "toolCall",
|
|
2439
|
-
id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
|
|
2440
|
-
name: toolCall.name,
|
|
2441
|
-
arguments: toolCall.arguments,
|
|
2442
|
-
partialArgs: toolCall.partialArgs
|
|
2443
|
-
};
|
|
2444
|
-
currentBlock = block;
|
|
2445
|
-
output.content.push(block);
|
|
2446
|
-
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2447
|
-
pushStreamEvent({
|
|
2448
|
-
type: "toolcall_start",
|
|
2449
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2450
|
-
partial: output
|
|
2451
|
-
});
|
|
2452
|
-
pushStreamEvent({
|
|
2453
|
-
type: "toolcall_delta",
|
|
2454
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2455
|
-
delta: toolCall.partialArgs,
|
|
2456
|
-
partial: output
|
|
2457
|
-
});
|
|
2458
|
-
};
|
|
2459
|
-
const appendFilteredVisibleTextDelta = (text) => {
|
|
2460
|
-
const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
|
|
2461
|
-
kind: "text",
|
|
2462
|
-
text
|
|
2463
|
-
}];
|
|
2464
|
-
for (const recoveredPart of recoveredParts) {
|
|
2465
|
-
if (recoveredPart.kind === "toolCall") {
|
|
2466
|
-
appendRecoveredToolCall(recoveredPart);
|
|
2467
|
-
continue;
|
|
2468
|
-
}
|
|
2469
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
2470
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
2471
|
-
}
|
|
2472
|
-
};
|
|
2473
|
-
const flushDeepSeekToolCallRecovererAtEnd = () => {
|
|
2474
|
-
const recoveredParts = deepSeekToolCallRecoverer?.flush();
|
|
2475
|
-
if (!recoveredParts) return;
|
|
2476
|
-
for (const recoveredPart of recoveredParts) {
|
|
2477
|
-
if (recoveredPart.kind === "toolCall") {
|
|
2478
|
-
appendRecoveredToolCall(recoveredPart);
|
|
2479
|
-
continue;
|
|
2480
|
-
}
|
|
2481
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
2482
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
2483
|
-
}
|
|
2484
|
-
};
|
|
2485
|
-
const flushDeepSeekTextFilterAtEnd = () => {
|
|
2486
|
-
const parts = deepSeekTextFilter?.flush();
|
|
2487
|
-
if (!parts) return;
|
|
2488
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
2489
|
-
};
|
|
2490
|
-
const appendRoutedContentDelta = (delta) => {
|
|
2491
|
-
if (delta.kind === "text") {
|
|
2492
|
-
appendFilteredVisibleTextDelta(delta.text);
|
|
2493
|
-
return;
|
|
2494
|
-
}
|
|
2495
|
-
if (!emitReasoning) return;
|
|
2496
|
-
if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
|
|
2497
|
-
else appendThinkingDelta(delta);
|
|
2498
|
-
};
|
|
2499
|
-
const appendPartitionedVisibleDelta = (delta) => {
|
|
2500
|
-
if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
|
|
2501
|
-
};
|
|
2502
|
-
const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
|
|
2503
|
-
if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
|
|
2504
|
-
const latestBlock = output.content[output.content.length - 1];
|
|
2505
|
-
if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
|
|
2506
|
-
if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
|
|
2507
|
-
appendThinkingDelta({ text: "" });
|
|
2508
|
-
};
|
|
2509
|
-
const flushReasoningTagTextPartitioner = () => {
|
|
2510
|
-
for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
|
|
2511
|
-
};
|
|
2512
|
-
const sealTextBeforeReasoning = () => {
|
|
2513
|
-
if (currentBlock?.type !== "text" && !reasoningTagTextPartitioner.hasPending()) return;
|
|
2514
|
-
flushReasoningTagTextPartitioner();
|
|
2515
|
-
if (currentBlock?.type !== "text") return;
|
|
2516
|
-
if (currentTextSource !== "reasoning_detail" && currentBlock.text.trim()) pendingInterruptedTextBlock = currentBlock;
|
|
2517
|
-
currentBlock = null;
|
|
2518
|
-
currentTextSource = void 0;
|
|
2519
|
-
};
|
|
2520
|
-
const beginReasoning = (hasFollowingVisibleText, forceStrict = false) => {
|
|
2521
|
-
if (!output.openclawDelivery?.textPhaseRequiresTerminal) output.openclawDelivery = {
|
|
2522
|
-
...output.openclawDelivery,
|
|
2523
|
-
textPhaseRequiresTerminal: true
|
|
2524
|
-
};
|
|
2525
|
-
if (forceStrict || reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
|
|
2526
|
-
if (!hasFollowingVisibleText || !reasoningTagTextPartitioner.hasPendingSyntax()) sealTextBeforeReasoning();
|
|
2527
|
-
};
|
|
2528
|
-
const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
|
|
2529
|
-
const guardedStream = withFirstStreamEventTimeout(responseStream, {
|
|
2530
|
-
provider: model.provider,
|
|
2531
|
-
api: model.api,
|
|
2532
|
-
model: model.id,
|
|
2533
|
-
timeoutMs: options?.firstEventTimeoutMs ?? 0,
|
|
2534
|
-
stage: "completions",
|
|
2535
|
-
abort: options?.abortFirstEventStream,
|
|
2536
|
-
onTimeout: options?.onFirstEventTimeout,
|
|
2537
|
-
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."
|
|
2538
|
-
});
|
|
2539
|
-
for await (const rawChunk of guardedStream) {
|
|
2540
|
-
throwIfModelStreamAborted(options?.signal);
|
|
2541
|
-
chunkPushedEvent = false;
|
|
2542
|
-
if (!rawChunk || typeof rawChunk !== "object") {
|
|
2543
|
-
await cooperativeScheduler.afterEvent();
|
|
2544
|
-
continue;
|
|
2545
|
-
}
|
|
2546
|
-
notifyLlmRequestActivity(options?.signal);
|
|
2547
|
-
const chunk = rawChunk;
|
|
2548
|
-
output.responseId ||= chunk.id;
|
|
2549
|
-
let hasReasoningUsageActivity = false;
|
|
2550
|
-
if (chunk.usage) {
|
|
2551
|
-
output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
|
|
2552
|
-
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
|
|
2553
|
-
}
|
|
2554
|
-
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
2555
|
-
if (!choice) {
|
|
2556
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2557
|
-
await cooperativeScheduler.afterEvent();
|
|
2558
|
-
continue;
|
|
2559
|
-
}
|
|
2560
|
-
const choiceUsage = choice.usage;
|
|
2561
|
-
if (!chunk.usage && choiceUsage) {
|
|
2562
|
-
output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
|
|
2563
|
-
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
|
|
2564
|
-
}
|
|
2565
|
-
if (choice.finish_reason) {
|
|
2566
|
-
const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
|
|
2567
|
-
output.stopReason = finishReasonResult.stopReason;
|
|
2568
|
-
if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
|
|
2569
|
-
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
2570
|
-
}
|
|
2571
|
-
const rawChoiceDelta = choice.delta ?? choice.message;
|
|
2572
|
-
if (!rawChoiceDelta) {
|
|
2573
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2574
|
-
await cooperativeScheduler.afterEvent();
|
|
2575
|
-
continue;
|
|
2576
|
-
}
|
|
2577
|
-
for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
|
|
2578
|
-
const choiceDelta = normalizedDelta.delta;
|
|
2579
|
-
const reasoningBatch = readOpenAICompletionsReasoningBatch(choiceDelta, visibleReasoningDetailTypes);
|
|
2580
|
-
const reasoningDeltas = reasoningBatch.deltas;
|
|
2581
|
-
const hasReasoningThinking = reasoningBatch.hasThinking;
|
|
2582
|
-
const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningBatch.mirroredThinking);
|
|
2583
|
-
const lastVisibleTextIndex = contentDeltas.findLastIndex((delta) => delta.kind === "text");
|
|
2584
|
-
const hasSameChunkVisibleText = reasoningBatch.hasVisibleText || lastVisibleTextIndex !== -1;
|
|
2585
|
-
if (hasReasoningThinking) {
|
|
2586
|
-
beginReasoning(hasSameChunkVisibleText, true);
|
|
2587
|
-
appendReasoningDeltas(reasoningDeltas);
|
|
2588
|
-
}
|
|
2589
|
-
for (const [contentDeltaIndex, contentDelta] of contentDeltas.entries()) if (contentDelta.kind === "text") {
|
|
2590
|
-
const routedDeltas = hasReasoningThinking ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
|
|
2591
|
-
for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
|
|
2592
|
-
} else {
|
|
2593
|
-
beginReasoning(contentDeltaIndex < lastVisibleTextIndex);
|
|
2594
|
-
appendRoutedContentDelta(contentDelta);
|
|
2595
|
-
}
|
|
2596
|
-
if (!hasReasoningThinking) appendReasoningDeltas(reasoningDeltas);
|
|
2597
|
-
const toolCallDeltas = normalizedDelta.toolCalls;
|
|
2598
|
-
if (toolCallDeltas.length > 0) {
|
|
2599
|
-
sawNativeToolCallDelta = true;
|
|
2600
|
-
flushReasoningTagTextPartitioner();
|
|
2601
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
2602
|
-
for (const toolCall of toolCallDeltas) {
|
|
2603
|
-
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
|
|
2604
|
-
let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
|
|
2605
|
-
if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
|
|
2606
|
-
if (!block) {
|
|
2607
|
-
if (currentBlock?.type === "toolCall") {
|
|
2608
|
-
currentBlock = null;
|
|
2609
|
-
flushPendingPostToolCallDeltas();
|
|
2610
|
-
}
|
|
2611
|
-
const initialSig = extractToolCallThoughtSignature(toolCall);
|
|
2612
|
-
block = {
|
|
2613
|
-
type: "toolCall",
|
|
2614
|
-
id: toolCall.id || "",
|
|
2615
|
-
name: toolCall.function?.name || "",
|
|
2616
|
-
arguments: {},
|
|
2617
|
-
partialArgs: "",
|
|
2618
|
-
...initialSig ? { thoughtSignature: initialSig } : {}
|
|
2619
|
-
};
|
|
2620
|
-
output.content.push(block);
|
|
2621
|
-
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2622
|
-
pushStreamEvent({
|
|
2623
|
-
type: "toolcall_start",
|
|
2624
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2625
|
-
partial: output
|
|
2626
|
-
});
|
|
2627
|
-
}
|
|
2628
|
-
if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
|
|
2629
|
-
if (toolCall.id) {
|
|
2630
|
-
block.id = toolCall.id;
|
|
2631
|
-
toolCallBlocksById.set(toolCall.id, block);
|
|
2632
|
-
}
|
|
2633
|
-
currentBlock = block;
|
|
2634
|
-
if (toolCall.function?.name) block.name = toolCall.function.name;
|
|
2635
|
-
const deltaSig = extractToolCallThoughtSignature(toolCall);
|
|
2636
|
-
if (deltaSig) block.thoughtSignature = deltaSig;
|
|
2637
|
-
if (toolCall.function?.arguments) {
|
|
2638
|
-
block.partialArgs += toolCall.function.arguments;
|
|
2639
|
-
block.arguments = parseStreamingJson(block.partialArgs);
|
|
2640
|
-
pushStreamEvent({
|
|
2641
|
-
type: "toolcall_delta",
|
|
2642
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2643
|
-
delta: toolCall.function.arguments,
|
|
2644
|
-
partial: output
|
|
2645
|
-
});
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
flushPendingPostToolCallDeltas();
|
|
2651
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2652
|
-
await cooperativeScheduler.afterEvent();
|
|
2653
|
-
}
|
|
2654
|
-
flushReasoningTagTextPartitioner();
|
|
2655
|
-
flushDeepSeekToolCallRecovererAtEnd();
|
|
2656
|
-
flushDeepSeekTextFilterAtEnd();
|
|
2657
|
-
currentBlock = null;
|
|
2658
|
-
flushPendingPostToolCallDeltas();
|
|
2659
|
-
finalizeOpenAICompletionsToolCalls(output, {
|
|
2660
|
-
allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
|
|
2661
|
-
onConfirmedToolCall(block, contentIndex) {
|
|
2662
|
-
if (block.type !== "toolCall") return;
|
|
2663
|
-
pushStreamEvent({
|
|
2664
|
-
type: "toolcall_end",
|
|
2665
|
-
contentIndex,
|
|
2666
|
-
toolCall: block,
|
|
2667
|
-
partial: output
|
|
2668
|
-
});
|
|
2669
|
-
}
|
|
2670
|
-
});
|
|
2671
|
-
if (confirmedInterruptedTextBlock && output.stopReason !== "toolUse" && output.stopReason !== "error" && output.stopReason !== "aborted") tagInterruptedTextPhases(output.content, confirmedInterruptedTextBlock, explicitVisibleTextBlocks);
|
|
2672
|
-
if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
|
|
2673
|
-
if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
|
|
2674
|
-
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
2675
|
-
}
|
|
2676
|
-
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
2677
|
-
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
2678
|
-
}
|
|
2679
|
-
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
2680
|
-
if (!model.reasoning) return false;
|
|
2681
|
-
const effort = resolveOpenAICompletionsReasoningEffort(options);
|
|
2682
|
-
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
2683
|
-
return true;
|
|
2684
|
-
}
|
|
2685
|
-
function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
2686
|
-
const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
|
|
2687
|
-
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
2688
|
-
}
|
|
2689
|
-
//#endregion
|
|
2690
1686
|
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
2691
1687
|
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
2692
1688
|
const messages = params.messages;
|
|
@@ -2762,7 +1758,7 @@ function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
|
|
|
2762
1758
|
}
|
|
2763
1759
|
}
|
|
2764
1760
|
return {
|
|
2765
|
-
baseURL,
|
|
1761
|
+
baseURL: resolveOpenAIClientBaseUrl(model, baseURL),
|
|
2766
1762
|
defaultHeaders: headers,
|
|
2767
1763
|
defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
|
|
2768
1764
|
};
|
|
@@ -2846,6 +1842,7 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
2846
1842
|
}), output, model, stream, {
|
|
2847
1843
|
signal: options?.signal,
|
|
2848
1844
|
emitReasoning,
|
|
1845
|
+
strictReasoningTags: reasoningTagTextPolicy.isStrict(options),
|
|
2849
1846
|
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
2850
1847
|
abortFirstEventStream: firstEventAbort.abort,
|
|
2851
1848
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
@@ -2920,7 +1917,7 @@ function jsonValuesEqual(left, right) {
|
|
|
2920
1917
|
return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
|
|
2921
1918
|
}
|
|
2922
1919
|
function requestWithoutInput(request) {
|
|
2923
|
-
const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
|
|
1920
|
+
const { input: _input, previous_response_id: _previousResponseId, instructions: _instructions, ...rest } = request;
|
|
2924
1921
|
if (!isRecord(rest.metadata)) return rest;
|
|
2925
1922
|
const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
|
|
2926
1923
|
return {
|
|
@@ -3111,18 +2108,27 @@ function sanitizeOpenAICodexResponsesParams(model, params) {
|
|
|
3111
2108
|
stripOpenAICodexResponsesUnsupportedTextFields(params);
|
|
3112
2109
|
return params;
|
|
3113
2110
|
}
|
|
3114
|
-
function
|
|
2111
|
+
function buildOpenAIResponsesInstructionsText(context) {
|
|
3115
2112
|
if (!context.systemPrompt) return;
|
|
3116
2113
|
return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
|
|
3117
2114
|
}
|
|
3118
|
-
function
|
|
3119
|
-
|
|
2115
|
+
function resolveOpenAIResponsesInstructions(model, context, usesInstructionsField) {
|
|
2116
|
+
if (!usesInstructionsField) return;
|
|
2117
|
+
const instructions = buildOpenAIResponsesInstructionsText(context);
|
|
3120
2118
|
if (instructions && instructions.trim().length > 0) return instructions;
|
|
3121
2119
|
return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
|
|
3122
2120
|
}
|
|
3123
|
-
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) {
|
|
3124
2130
|
if (messages.length > 0 || !context.systemPrompt) return;
|
|
3125
|
-
if (!
|
|
2131
|
+
if (!buildOpenAIResponsesInstructionsText(context)) throw new Error("OpenAI Responses requires non-empty input when only systemPrompt is provided.");
|
|
3126
2132
|
messages.push(buildResponsesInputMessage("user", [{
|
|
3127
2133
|
type: "input_text",
|
|
3128
2134
|
text: " "
|
|
@@ -3136,16 +2142,12 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
|
3136
2142
|
return responseFormat;
|
|
3137
2143
|
}
|
|
3138
2144
|
function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
|
|
3139
|
-
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3140
2145
|
const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
|
|
3141
|
-
const compat = getCompat(model);
|
|
3142
|
-
const supportsDeveloperRole = typeof compat.supportsDeveloperRole === "boolean" ? compat.supportsDeveloperRole : void 0;
|
|
3143
2146
|
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3144
2147
|
const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
|
|
3145
2148
|
const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
|
|
3146
2149
|
return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
|
|
3147
|
-
includeSystemPrompt: !
|
|
3148
|
-
supportsDeveloperRole,
|
|
2150
|
+
includeSystemPrompt: !payloadPolicy.usesInstructionsField,
|
|
3149
2151
|
replayReasoningItems: true,
|
|
3150
2152
|
replayResponsesItemIds,
|
|
3151
2153
|
authProfileId: options?.authProfileId,
|
|
@@ -3154,19 +2156,19 @@ function convertOpenAIResponsesMessagesForRequest(model, context, options, repla
|
|
|
3154
2156
|
});
|
|
3155
2157
|
}
|
|
3156
2158
|
function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
|
|
3157
|
-
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3158
2159
|
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3159
2160
|
const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
|
|
3160
|
-
|
|
2161
|
+
ensureOpenAIResponsesNonEmptyInput(messages, context);
|
|
3161
2162
|
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
3162
2163
|
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
2164
|
+
const instructions = resolveOpenAIResponsesInstructions(model, context, payloadPolicy.usesInstructionsField);
|
|
3163
2165
|
const params = {
|
|
3164
2166
|
model: model.id,
|
|
3165
2167
|
input: messages,
|
|
3166
2168
|
stream: true,
|
|
3167
2169
|
prompt_cache_key: promptCacheKey,
|
|
3168
2170
|
prompt_cache_retention: getPromptCacheRetention(model.baseUrl, cacheRetention),
|
|
3169
|
-
...
|
|
2171
|
+
...instructions ? { instructions } : {},
|
|
3170
2172
|
...metadata ? { metadata } : {}
|
|
3171
2173
|
};
|
|
3172
2174
|
const effectiveMaxTokens = options?.maxTokens || model.maxTokens;
|
|
@@ -3471,149 +2473,6 @@ function closeOpenAIResponsesWebSocketSessions(sessionId) {
|
|
|
3471
2473
|
}
|
|
3472
2474
|
registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
|
|
3473
2475
|
//#endregion
|
|
3474
|
-
//#region packages/media-core/src/inline-image-data-url.ts
|
|
3475
|
-
/** Prefix used to distinguish inline data URLs from remote/local image references. */
|
|
3476
|
-
const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
|
|
3477
|
-
const IMAGE_SIGNATURES = [
|
|
3478
|
-
{
|
|
3479
|
-
mime: "image/png",
|
|
3480
|
-
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
|
|
3481
|
-
},
|
|
3482
|
-
{
|
|
3483
|
-
mime: "image/jpeg",
|
|
3484
|
-
matches: (buffer) => buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255
|
|
3485
|
-
},
|
|
3486
|
-
{
|
|
3487
|
-
mime: "image/webp",
|
|
3488
|
-
matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
3489
|
-
},
|
|
3490
|
-
{
|
|
3491
|
-
mime: "image/gif",
|
|
3492
|
-
matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
|
|
3493
|
-
},
|
|
3494
|
-
{
|
|
3495
|
-
mime: "image/bmp",
|
|
3496
|
-
matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
|
|
3497
|
-
}
|
|
3498
|
-
];
|
|
3499
|
-
const HEIC_BRANDS = /* @__PURE__ */ new Set([
|
|
3500
|
-
"heic",
|
|
3501
|
-
"heix",
|
|
3502
|
-
"hevc",
|
|
3503
|
-
"hevx",
|
|
3504
|
-
"heis",
|
|
3505
|
-
"heim",
|
|
3506
|
-
"hevm",
|
|
3507
|
-
"hevs"
|
|
3508
|
-
]);
|
|
3509
|
-
const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
|
|
3510
|
-
const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
|
|
3511
|
-
const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
|
|
3512
|
-
"image/png",
|
|
3513
|
-
"image/jpeg",
|
|
3514
|
-
"image/webp",
|
|
3515
|
-
"image/gif"
|
|
3516
|
-
]);
|
|
3517
|
-
function startsWithDataUrl(value) {
|
|
3518
|
-
return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
|
|
3519
|
-
}
|
|
3520
|
-
function sniffIsoBmffImageMime(buffer) {
|
|
3521
|
-
if (buffer.length < 12 || buffer.subarray(4, 8).toString("ascii") !== "ftyp") return;
|
|
3522
|
-
const brands = [buffer.subarray(8, 12).toString("ascii")];
|
|
3523
|
-
for (let offset = 16; offset + 4 <= buffer.length; offset += 4) brands.push(buffer.subarray(offset, offset + 4).toString("ascii"));
|
|
3524
|
-
if (brands.some((brand) => HEIC_BRANDS.has(brand))) return "image/heic";
|
|
3525
|
-
if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
|
|
3526
|
-
}
|
|
3527
|
-
/** Sniffs supported inline image formats from decoded bytes. */
|
|
3528
|
-
function sniffInlineImageMime(buffer) {
|
|
3529
|
-
return IMAGE_SIGNATURES.find((signature) => signature.matches(buffer))?.mime ?? sniffIsoBmffImageMime(buffer);
|
|
3530
|
-
}
|
|
3531
|
-
function isImageMimeType(value) {
|
|
3532
|
-
return value.trim().toLowerCase().startsWith("image/");
|
|
3533
|
-
}
|
|
3534
|
-
/** Canonicalizes trusted inline image base64 and rejects malformed or non-image payloads. */
|
|
3535
|
-
function sanitizeInlineImageBase64(params) {
|
|
3536
|
-
if (!isImageMimeType(params.mimeType)) return;
|
|
3537
|
-
const canonicalPayload = canonicalizeBase64(params.base64);
|
|
3538
|
-
if (!canonicalPayload) return;
|
|
3539
|
-
const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
|
|
3540
|
-
if (!sniffedMimeType) return;
|
|
3541
|
-
return {
|
|
3542
|
-
mimeType: sniffedMimeType,
|
|
3543
|
-
base64: canonicalPayload
|
|
3544
|
-
};
|
|
3545
|
-
}
|
|
3546
|
-
function parseInlineImageDataUrl(value) {
|
|
3547
|
-
if (!startsWithDataUrl(value)) return {
|
|
3548
|
-
metadata: [],
|
|
3549
|
-
payload: value
|
|
3550
|
-
};
|
|
3551
|
-
const commaIndex = value.indexOf(",");
|
|
3552
|
-
if (commaIndex < 0) return;
|
|
3553
|
-
return {
|
|
3554
|
-
metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
|
|
3555
|
-
payload: value.slice(commaIndex + 1)
|
|
3556
|
-
};
|
|
3557
|
-
}
|
|
3558
|
-
function metadataAllowsImageBase64(metadata) {
|
|
3559
|
-
const [mimeType, ...options] = metadata;
|
|
3560
|
-
return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
|
|
3561
|
-
}
|
|
3562
|
-
function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
|
|
3563
|
-
const parsed = parseInlineImageDataUrl(imageUrl);
|
|
3564
|
-
if (!parsed) return;
|
|
3565
|
-
if (parsed.metadata.length === 0) return imageUrl;
|
|
3566
|
-
if (!metadataAllowsImageBase64(parsed.metadata)) return;
|
|
3567
|
-
const [mimeType] = parsed.metadata;
|
|
3568
|
-
const sanitized = sanitizeInlineImageBase64({
|
|
3569
|
-
mimeType: mimeType ?? "",
|
|
3570
|
-
base64: parsed.payload
|
|
3571
|
-
});
|
|
3572
|
-
if (!sanitized) return;
|
|
3573
|
-
if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
|
|
3574
|
-
return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
|
|
3575
|
-
}
|
|
3576
|
-
/** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
|
|
3577
|
-
function sanitizeInlineImageDataUrl(imageUrl) {
|
|
3578
|
-
return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
|
|
3579
|
-
}
|
|
3580
|
-
//#endregion
|
|
3581
|
-
//#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
|
|
3582
|
-
/**
|
|
3583
|
-
* Sanitizes OpenAI Responses payloads before transport. Invalid inline images
|
|
3584
|
-
* are replaced with text placeholders so the request remains valid and
|
|
3585
|
-
* auditable.
|
|
3586
|
-
*/
|
|
3587
|
-
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
|
|
3588
|
-
function invalidSnakeImage() {
|
|
3589
|
-
return {
|
|
3590
|
-
type: "input_text",
|
|
3591
|
-
text: `[${IMAGE_OMITTED_TEXT}]`
|
|
3592
|
-
};
|
|
3593
|
-
}
|
|
3594
|
-
function sanitizeValue(value) {
|
|
3595
|
-
if (Array.isArray(value)) return value.map(sanitizeValue);
|
|
3596
|
-
if (!isRecord(value)) return value;
|
|
3597
|
-
if (value.type === "input_image" && typeof value.image_url === "string") {
|
|
3598
|
-
const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
|
|
3599
|
-
return imageUrl ? {
|
|
3600
|
-
...value,
|
|
3601
|
-
image_url: imageUrl
|
|
3602
|
-
} : invalidSnakeImage();
|
|
3603
|
-
}
|
|
3604
|
-
const next = {};
|
|
3605
|
-
for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
|
|
3606
|
-
return next;
|
|
3607
|
-
}
|
|
3608
|
-
/** Sanitize inline image fields inside a Responses API payload. */
|
|
3609
|
-
function sanitizeResponsesImagePayload(params) {
|
|
3610
|
-
if (!Array.isArray(params.input)) return params;
|
|
3611
|
-
return {
|
|
3612
|
-
...params,
|
|
3613
|
-
input: sanitizeValue(params.input)
|
|
3614
|
-
};
|
|
3615
|
-
}
|
|
3616
|
-
//#endregion
|
|
3617
2476
|
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
3618
2477
|
function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
|
|
3619
2478
|
if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
|
|
@@ -3647,17 +2506,18 @@ function resolveProviderTransportTurnState(model, params) {
|
|
|
3647
2506
|
}
|
|
3648
2507
|
});
|
|
3649
2508
|
}
|
|
3650
|
-
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
|
|
2509
|
+
function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId, fetchOverride) {
|
|
3651
2510
|
return new OpenAI({
|
|
3652
2511
|
apiKey,
|
|
3653
|
-
baseURL: model
|
|
2512
|
+
baseURL: resolveOpenAIClientBaseUrl(model),
|
|
3654
2513
|
dangerouslyAllowBrowser: true,
|
|
3655
2514
|
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
|
|
3656
|
-
fetch: buildGuardedModelFetch(model),
|
|
2515
|
+
fetch: fetchOverride ?? buildGuardedModelFetch(model),
|
|
3657
2516
|
...buildOpenAISdkClientOptions(model)
|
|
3658
2517
|
});
|
|
3659
2518
|
}
|
|
3660
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;
|
|
3661
2521
|
const response = await params.client.post("/responses/compact", {
|
|
3662
2522
|
...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
|
|
3663
2523
|
timeoutMs: params.options?.timeoutMs,
|
|
@@ -3665,19 +2525,19 @@ async function postOpenAIResponsesCompaction(params) {
|
|
|
3665
2525
|
}),
|
|
3666
2526
|
body: {
|
|
3667
2527
|
model: params.request.model,
|
|
3668
|
-
input:
|
|
2528
|
+
input: compactInput
|
|
3669
2529
|
}
|
|
3670
2530
|
});
|
|
3671
2531
|
const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
|
|
3672
2532
|
const item = output.at(-1);
|
|
3673
2533
|
const retainedItems = output.slice(0, -1);
|
|
3674
|
-
const retainedMessagesAreValid = retainedItems.every((candidate) => isRecord(candidate) && candidate.type === "message" && (candidate.role === "user" || candidate.role === "developer" || candidate.role === "system") && Array.isArray(candidate.content));
|
|
3675
2534
|
const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
|
|
3676
2535
|
const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
|
|
3677
2536
|
const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
|
|
3678
2537
|
const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
|
|
3679
|
-
if (!isRecord(response) || response.object !== "response.compaction" || !
|
|
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");
|
|
3680
2539
|
return {
|
|
2540
|
+
output,
|
|
3681
2541
|
item,
|
|
3682
2542
|
historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
|
|
3683
2543
|
usage,
|
|
@@ -3708,7 +2568,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3708
2568
|
});
|
|
3709
2569
|
const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
|
|
3710
2570
|
const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
|
|
3711
|
-
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);
|
|
3712
2572
|
const buildRequest = async (replayMode) => {
|
|
3713
2573
|
let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
|
|
3714
2574
|
const nextParams = await options?.onPayload?.(params, model);
|
|
@@ -3778,7 +2638,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3778
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)}`);
|
|
3779
2639
|
let continuationBaseline;
|
|
3780
2640
|
const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
|
|
3781
|
-
const { stream:
|
|
2641
|
+
const { stream: responseStream } = await config.createResponseStream({
|
|
3782
2642
|
client,
|
|
3783
2643
|
request: initialRequest,
|
|
3784
2644
|
requestOptions,
|
|
@@ -3787,19 +2647,23 @@ function createResponsesTransportExecutor(config) {
|
|
|
3787
2647
|
initialAttemptKind,
|
|
3788
2648
|
initialRejectedCompaction,
|
|
3789
2649
|
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
3790
|
-
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
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
|
+
});
|
|
3801
2664
|
}
|
|
3802
2665
|
});
|
|
2666
|
+
return responseStream;
|
|
3803
2667
|
};
|
|
3804
2668
|
let responseStream;
|
|
3805
2669
|
let finishWebSocket;
|
|
@@ -3944,23 +2808,20 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
3944
2808
|
createResponseStream: createResponsesStreamWithEncryptedContentRetry
|
|
3945
2809
|
});
|
|
3946
2810
|
}
|
|
3947
|
-
function normalizeAzureBaseUrl(baseUrl) {
|
|
3948
|
-
return baseUrl.replace(/\/+$/, "");
|
|
3949
|
-
}
|
|
3950
2811
|
function resolveAzureDeploymentName(model) {
|
|
3951
2812
|
return resolveAzureDeploymentNameFromMap({
|
|
3952
2813
|
modelId: model.id,
|
|
3953
2814
|
deploymentMap: process.env.AZURE_OPENAI_DEPLOYMENT_NAME_MAP
|
|
3954
2815
|
});
|
|
3955
2816
|
}
|
|
3956
|
-
function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHeaders) {
|
|
3957
|
-
const baseURL =
|
|
2817
|
+
function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHeaders, _sessionId, fetchOverride) {
|
|
2818
|
+
const baseURL = model.baseUrl.replace(/\/+$/, "");
|
|
3958
2819
|
const clientOptions = {
|
|
3959
2820
|
apiKey,
|
|
3960
2821
|
dangerouslyAllowBrowser: true,
|
|
3961
2822
|
defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders),
|
|
3962
2823
|
baseURL,
|
|
3963
|
-
fetch: buildGuardedModelFetch(model),
|
|
2824
|
+
fetch: fetchOverride ?? buildGuardedModelFetch(model),
|
|
3964
2825
|
...buildOpenAISdkClientOptions(model)
|
|
3965
2826
|
};
|
|
3966
2827
|
if (isOpenAICompatibleAzureResponsesBaseUrl(baseURL)) return new OpenAI(clientOptions);
|
|
@@ -4006,6 +2867,112 @@ const responsesTesting = {
|
|
|
4006
2867
|
if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
|
|
4007
2868
|
//#endregion
|
|
4008
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
|
+
}
|
|
4009
2976
|
/** Whether provider replay state is a prefix-bound server compaction checkpoint. */
|
|
4010
2977
|
function isCompactionReplayCheckpoint(replay) {
|
|
4011
2978
|
const type = replay && typeof replay === "object" ? replay.type : void 0;
|
|
@@ -4301,4 +3268,4 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
4301
3268
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
4302
3269
|
}
|
|
4303
3270
|
//#endregion
|
|
4304
|
-
export { 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, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, readOpenAICompletionsReasoningBatch, 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, withProviderAcceptanceObserver, 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 };
|