@openclaw/ai 2026.8.1-beta.1 → 2026.8.1-beta.3
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-CE-7SkF6.mjs → anthropic-B8KwTtlb.mjs} +62 -37
- package/dist/{anthropic-SrGtwsJu.d.mts → anthropic-BECQCNdF.d.mts} +0 -2
- package/dist/{src-QkygScBs.mjs → anthropic-BQXQudmA.mjs} +0 -2
- package/dist/{anthropic-usage-Ma4iX6uG.mjs → anthropic-compaction-replay-4Zx7-aCr.mjs} +225 -103
- package/dist/anthropic-payload-policy-CSx7MoKH.d.mts +50 -0
- package/dist/{api-registry-EpJoVwM1.d.mts → api-registry-DFxSnFqG.d.mts} +1 -1
- package/dist/{azure-openai-responses-Cc_kasye.mjs → azure-openai-responses-Aa4mVcYG.mjs} +23 -29
- package/dist/diagnostics.d.mts +24 -1
- package/dist/diagnostics.mjs +2 -1
- package/dist/event-stream-BVVgDSdq.d.mts +1 -0
- package/dist/{event-stream-DRmDMRHH.d.mts → event-stream-D91-ohD_.d.mts} +3 -1
- package/dist/{event-stream-CEV9t6da.mjs → event-stream-uSMZJ3FA.mjs} +16 -3
- package/dist/event-stream.d.mts +1 -1
- package/dist/event-stream.mjs +1 -1
- package/dist/{google-shared-Cq2UKx1l.mjs → google-shared-M1dl64nz.mjs} +17 -19
- package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
- package/dist/{google-vertex-CusxWzvE.mjs → google-vertex-DCJHM_a5.mjs} +5 -4
- package/dist/{google-CGQu21aw.mjs → google-yDRnipeq.mjs} +4 -4
- package/dist/host-DF_yxOsA.mjs +477 -0
- package/dist/{host-Dn4_SZI2.d.mts → host-DJq_FGcz.d.mts} +13 -8
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +7 -5
- package/dist/internal/anthropic.d.mts +21 -7
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/openai-responses-payload-policy.d.mts +3 -0
- package/dist/internal/openai-responses-payload-policy.mjs +3 -0
- package/dist/internal/openai.d.mts +6 -6
- package/dist/internal/openai.mjs +9 -7
- package/dist/internal/runtime.d.mts +19 -5
- package/dist/internal/runtime.mjs +85 -72
- package/dist/internal/shared.d.mts +17 -8
- package/dist/internal/shared.mjs +4 -5
- package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
- package/dist/{mistral-Dx6VzNjf.mjs → mistral-D6hRXI7A.mjs} +67 -72
- package/dist/{number-coercion-1Miyb5MO.mjs → number-coercion-H9qHik3g.mjs} +7 -2
- package/dist/openai-chatgpt-jwt-KWcgd0d_.mjs +19 -0
- package/dist/{openai-chatgpt-responses-SC9m0FSf.mjs → openai-chatgpt-responses-C339VIfT.mjs} +235 -146
- package/dist/{openai-completions-BaIpl93j.mjs → openai-completions-D88orSJ9.mjs} +88 -67
- package/dist/openai-completions-compat-B0KTleXp.d.mts +43 -0
- package/dist/{openai-responses-Bdx3V_Yu.mjs → openai-responses-BXEcr-Tp.mjs} +17 -24
- package/dist/openai-responses-contracts-CD75pQ0m.d.mts +68 -0
- package/dist/openai-responses-contracts-DvILoqBk.mjs +247 -0
- package/dist/openai-responses-payload-policy-BDo8CQsx.mjs +208 -0
- package/dist/openai-responses-payload-policy-CNGFT9zr.d.mts +41 -0
- package/dist/{openai-responses-prompt-observer-internal-DDZPxRfr.mjs → openai-responses-prompt-observer-internal-BG-1AsBI.mjs} +2 -3
- package/dist/{openai-responses-stream-internal-Bl8YyVtF.mjs → openai-responses-shared-Cvt5MgEm.mjs} +1287 -1039
- package/dist/{openai-reasoning-compat-DKebnIBL.mjs → openai-stop-reason-9OONKyAm.mjs} +222 -180
- package/dist/{openai-transport-shared-Cipt7egQ.mjs → openai-tool-projection-CHGLt03m.mjs} +239 -128
- package/dist/provider-error-C8mBRJDl.mjs +424 -0
- package/dist/provider-error-CzNw4BWX.d.mts +12 -0
- package/dist/provider-options-AvldWZt8.mjs +21 -0
- package/dist/{provider-options-C5kYML7i.d.mts → provider-options-Cc5ZQ6Aa.d.mts} +19 -3
- package/dist/provider-transcript-transform--gtf0ZbR.mjs +32 -0
- package/dist/provider-types.d.mts +31 -0
- package/dist/provider-types.mjs +8 -0
- package/dist/providers.d.mts +1 -1
- package/dist/providers.mjs +17 -19
- package/dist/{reasoning-tag-text-partitioner-CGDyLWUR.mjs → reasoning-tag-text-partitioner-5ygO2rZc.mjs} +17 -8
- package/dist/record-coerce-DdXsgUd_.mjs +23 -0
- package/dist/{model-utils-Dau5dlgm.mjs → sanitize-unicode-BP7HLLZn.mjs} +28 -2
- package/dist/session-resources-CkR4WWy1.mjs +21 -0
- package/dist/simple-options-oqsXB6U3.mjs +450 -0
- package/dist/src-D2H6yKkH.mjs +2 -0
- package/dist/{stream-first-event-timeout-BIBomOGq.mjs → stream-first-event-timeout-MK28puvq.mjs} +2 -2
- package/dist/string-coerce-fsri9iCu.mjs +34 -0
- package/dist/{tool-schema-json-projection-B1b-XCn5.mjs → tool-schema-json-projection-FEr-jOoZ.mjs} +20 -13
- package/dist/transport-utils-Bv0FHkd0.mjs +138 -0
- package/dist/transports.d.mts +199 -209
- package/dist/transports.mjs +1842 -1364
- package/dist/types-BDdaOVi2.mjs +6 -0
- package/dist/{types-CH7ReIcU.d.mts → types-DTAP49HF.d.mts} +35 -2
- package/dist/types.d.mts +5 -5
- package/dist/types.mjs +6 -4
- package/dist/utf16-slice-CvGodqok.mjs +29 -0
- package/dist/{validation-DAa_yFOM.mjs → validation-B61OhAio.mjs} +6 -6
- package/dist/{validation-CcPcuEbR.d.mts → validation-DYNNrU9u.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +15 -1
- package/dist/host-Bl7Kgddo.mjs +0 -407
- package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +0 -39
- package/dist/openai-responses-contracts-BBKBfAqR.d.mts +0 -116
- package/dist/openai-responses-shared-CCyMvg7M.mjs +0 -403
- package/dist/provider-error-DI0Ts28U.mjs +0 -47
- package/dist/sanitize-unicode-DT5o51ur.mjs +0 -26
- package/dist/tool-result-text-Dvkp2Dus.mjs +0 -274
- package/dist/transform-messages-DfjpNXNQ.mjs +0 -2
- package/dist/transport-stream-shared-CPNv7A3r.mjs +0 -297
package/dist/transports.mjs
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
|
|
2
|
-
import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
2
|
+
import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./anthropic-BQXQudmA.mjs";
|
|
3
|
+
import { n as normalizeLowercaseStringOrEmpty, t as hasNonEmptyString } from "./string-coerce-fsri9iCu.mjs";
|
|
4
|
+
import { r as calculateCost } from "./sanitize-unicode-BP7HLLZn.mjs";
|
|
5
|
+
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-DF_yxOsA.mjs";
|
|
6
|
+
import { a as isRecord } from "./record-coerce-DdXsgUd_.mjs";
|
|
7
|
+
import { i as canonicalizeBase64, o as stableStringify } from "./provider-error-C8mBRJDl.mjs";
|
|
8
|
+
import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-BG-1AsBI.mjs";
|
|
9
|
+
import { r as asNonNegativeFiniteNumber } from "./number-coercion-H9qHik3g.mjs";
|
|
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";
|
|
6
11
|
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
7
12
|
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
8
|
-
import { a as
|
|
9
|
-
import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-
|
|
10
|
-
import { A as
|
|
11
|
-
import { n as
|
|
12
|
-
import { a as convertMessages, c as resolveOpenAICompletionsResponseFormat, d as rememberPendingCommentaryTags, f as tagPendingCommentaryText, i as finalizeOpenAICompletionsToolCalls, l as shouldOmitOllamaCompatResponseFormat, n as mapOpenAIStopReason, o as detectOpenAICompletionsCompat, r as createOpenAICompletionsToolCallDeltaNormalizer, s as resolveOpenAICompletionsCompat, t as resolveOpenAIReasoningEffortMap, u as clearPendingCommentaryText } from "./openai-reasoning-compat-DKebnIBL.mjs";
|
|
13
|
+
import { C as withProviderResponseHook, E as quoteUnsafeIntegerLiterals, P as sortPromptCacheToolsByName, S as withProviderAcceptanceObserver, T as parseJsonPreservingUnsafeIntegers, _ as notifyProviderStreamOpened, a as assignTransportErrorDetails, b as sanitizeTransportPayloadText, c as createEmptyTransportUsage, d as finalizeTerminalToolCallArguments, f as finalizeTransportStream, g as notifyProviderHttpResponse, h as notifyProviderHttpMetadata, j as stripSystemPromptCacheBoundary, l as createWritableTransportEventStream, m as mergeTransportMetadata, o as coerceTransportToolCallArguments, p as mergeTransportHeaders, s as copyProviderAcceptanceObserver, t as adjustMaxTokensForThinking, u as failTransportStream, v as parseTerminalToolCallArguments, w as parseJsonObjectPreservingUnsafeIntegers, x as transportAbortError, y as sanitizeNonEmptyTransportPayloadText } from "./simple-options-oqsXB6U3.mjs";
|
|
14
|
+
import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-FEr-jOoZ.mjs";
|
|
15
|
+
import { A as resolveAnthropicImageMediaType, C as readAnthropicFallbackBoundary, D as usesFoundryBearerAuth, E as omitFoundryBearerCredentialHeaders, F as resolveAnthropicPayloadPolicy, I as resolveAnthropicServerCompactionPlan, M as applyAnthropicEphemeralCacheControlMarkers, N as applyAnthropicPayloadPolicyToParams, O as createAnthropicInlineImageBudget, P as resolveAnthropicEphemeralCacheControl, S as applyAnthropicFallbackBoundary, T as applyAnthropicRefusal, _ as ANTHROPIC_OMITTED_REASONING_TEXT, a as applyAnthropicMessageDeltaUsage, b as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, d as normalizeAnthropicToolCallId, f as normalizeAnthropicToolChoice, g as toClaudeCodeToolName, h as resolveOriginalAnthropicToolName, i as suppressAnthropicCompaction, j as applyAnthropicCacheControlToMessages, k as normalizeAnthropicInlineContent, m as reconcileAnthropicToolChoice, n as createCompactionCapture, o as applyAnthropicMessageStartUsage, p as projectAnthropicTools, r as isAnthropicReplayRejection, t as buildAnthropicReplayPlan, v as findActiveAnthropicToolTurnAssistantIndex, w as resolveAnthropicFallbackServingModelCost, y as ANTHROPIC_SERVER_SIDE_FALLBACKS } from "./anthropic-compaction-replay-4Zx7-aCr.mjs";
|
|
16
|
+
import { a as createOpenAICompletionsToolCallDeltaNormalizer, c as resolveOpenAICompletionsCompat, d as clearPendingCommentaryText, f as rememberPendingCommentaryTags, h as tagUnresolvedTextAsCommentary, i as hasToolCallHistory, l as resolveOpenAICompletionsResponseFormat, m as tagPendingCommentaryText, n as resolveOpenAIReasoningEffortMap, o as finalizeOpenAICompletionsToolCalls, p as tagInterruptedTextPhases, r as convertMessages, s as detectOpenAICompletionsCompat, t as mapOpenAIStopReason, u as shouldOmitOllamaCompatResponseFormat } from "./openai-stop-reason-9OONKyAm.mjs";
|
|
13
17
|
import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
|
|
14
|
-
import { n as parseStreamingJson } from "./json-parse-
|
|
18
|
+
import { n as parseStreamingJson } from "./json-parse-CDnesDM_.mjs";
|
|
15
19
|
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { C as
|
|
19
|
-
import {
|
|
20
|
-
import { t as
|
|
21
|
-
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-
|
|
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
|
+
import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
|
|
22
|
+
import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as suppressOpenAIResponsesCompaction, H as createOpenAIResponsesAssistantOutput, I as createResponsesStreamWithEncryptedContentRetry, K as resolveReplayableResponsesMessageId, L as isInvalidEncryptedContentError, R as resolveAzureOpenAIApiVersion, S as summarizeResponsesPayload, U as buildOpenAIResponsesReasoningReplayMetadata, V as convertResponsesMessages, W as captureOpenAIResponsesCompaction, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, _ as safeDebugValue, b as summarizeOpenAITransportError, c as processResponsesStream, dt as resolveModelPayloadDebugMode, f as observeResponsesStream, ft as resolveModelSseDebugMode, g as normalizeResponsesFailedEvent, h as logResponsesFailedNoDetails, m as buildResponsesFailedNoDetailsObservation, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, q as findOpenAIStrictToolProjectionDiagnostics, ut as emitModelTransportDebug, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-Cvt5MgEm.mjs";
|
|
23
|
+
import { t as codeModeToolSurfaceObserver } from "./provider-options-AvldWZt8.mjs";
|
|
24
|
+
import { a as createModelStreamCooperativeScheduler, c as isOpenAICompletionsThinkingEnabled, d as parseOpenAICompletionsUsage, f as readOpenAICompletionsContentDeltas, h as throwIfModelStreamAborted, i as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, l as log, m as resolvePromptCacheKey, n as reconcileOpenAICompletionsToolChoice, o as createOpenAIProviderAcceptanceHook, p as readOpenAICompletionsReasoningBatch, r as reconcileOpenAIResponsesToolChoice, s as createOpenAIResponseHook, t as projectOpenAITools, u as measureUtf8AppendBytes } from "./openai-tool-projection-CHGLt03m.mjs";
|
|
25
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-MK28puvq.mjs";
|
|
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";
|
|
22
28
|
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
23
|
-
import {
|
|
29
|
+
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
24
30
|
import { randomUUID } from "node:crypto";
|
|
25
31
|
import OpenAI, { AzureOpenAI } from "openai";
|
|
32
|
+
import { ResponsesWS } from "openai/resources/responses/ws.js";
|
|
26
33
|
//#region packages/ai/src/transports/anthropic-transport-stream.ts
|
|
27
34
|
/**
|
|
28
35
|
* Native Anthropic Messages streaming transport.
|
|
@@ -59,7 +66,7 @@ function isAnthropicOAuthToken(apiKey) {
|
|
|
59
66
|
}
|
|
60
67
|
function isDirectAnthropicModel(model) {
|
|
61
68
|
if (normalizeLowercaseStringOrEmpty(model.provider) !== "anthropic") return false;
|
|
62
|
-
const endpointClass = resolveProviderEndpoint(model
|
|
69
|
+
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
63
70
|
return endpointClass === "default" || endpointClass === "anthropic-public";
|
|
64
71
|
}
|
|
65
72
|
function isKimiAnthropicProvider(provider) {
|
|
@@ -74,7 +81,7 @@ function useAnthropicServerSideFallback(model) {
|
|
|
74
81
|
return (usesClaudeFable5MessagesContract(model) || resolveClaudeOpus5ModelIdentity(model) !== void 0) && isDirectAnthropicModel(model);
|
|
75
82
|
}
|
|
76
83
|
function supportsReasoningContentReplay(model) {
|
|
77
|
-
return resolveProviderEndpoint(model
|
|
84
|
+
return resolveProviderEndpoint(model).endpointClass === "xiaomi-native";
|
|
78
85
|
}
|
|
79
86
|
function buildAnthropicBetaHeader(model, betaFeatures, params) {
|
|
80
87
|
if (!isDirectAnthropicModel(model)) return;
|
|
@@ -169,7 +176,7 @@ async function convertAnthropicMessages(messages, model, isOAuthToken, options)
|
|
|
169
176
|
continue;
|
|
170
177
|
}
|
|
171
178
|
if (msg.role === "assistant") {
|
|
172
|
-
const blocks = [];
|
|
179
|
+
const blocks = i === 0 && options.compaction ? [options.compaction] : [];
|
|
173
180
|
const reasoningContent = [];
|
|
174
181
|
let omittedThinking = false;
|
|
175
182
|
for (const block of msg.content) {
|
|
@@ -296,7 +303,7 @@ function convertAnthropicTools(tools, isOAuthToken) {
|
|
|
296
303
|
};
|
|
297
304
|
}
|
|
298
305
|
function parseAnthropicToolCallArguments(inputJson) {
|
|
299
|
-
return parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson);
|
|
306
|
+
return coerceTransportToolCallArguments(parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson));
|
|
300
307
|
}
|
|
301
308
|
const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
302
309
|
/** Resolve the effective Anthropic API base URL from model or environment. */
|
|
@@ -399,7 +406,7 @@ async function* parseAnthropicSseBody(body, signal) {
|
|
|
399
406
|
}
|
|
400
407
|
function createAnthropicMessagesClient(params) {
|
|
401
408
|
const url = resolveAnthropicMessagesUrl(params.baseURL);
|
|
402
|
-
return { messages: { async
|
|
409
|
+
return { messages: { async stream(body, options) {
|
|
403
410
|
const headers = mergeTransportHeaders({
|
|
404
411
|
"content-type": "application/json",
|
|
405
412
|
"anthropic-version": "2023-06-01",
|
|
@@ -412,12 +419,10 @@ function createAnthropicMessagesClient(params) {
|
|
|
412
419
|
body: JSON.stringify(body),
|
|
413
420
|
signal: options?.signal
|
|
414
421
|
});
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
if (!response.body) return;
|
|
420
|
-
yield* parseAnthropicSseBody(response.body, options?.signal);
|
|
422
|
+
return {
|
|
423
|
+
response,
|
|
424
|
+
stream: response.body ? parseAnthropicSseBody(response.body, options?.signal) : []
|
|
425
|
+
};
|
|
421
426
|
} } };
|
|
422
427
|
}
|
|
423
428
|
function formatAnthropicMessagesHttpError(response, detail) {
|
|
@@ -528,11 +533,17 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
528
533
|
baseUrl: model.baseUrl,
|
|
529
534
|
cacheRetention: options?.cacheRetention,
|
|
530
535
|
enableCacheControl: true
|
|
531
|
-
});
|
|
536
|
+
}, model);
|
|
532
537
|
const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
|
|
533
|
-
const
|
|
538
|
+
const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
|
|
539
|
+
enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
|
|
540
|
+
authProfileId: options?.authProfileId,
|
|
541
|
+
sessionId: options?.sessionId
|
|
542
|
+
});
|
|
543
|
+
const messages = await convertAnthropicMessages(replayPlan.messages, model, isOAuthToken, {
|
|
534
544
|
allowReasoningContentReplay: supportsReasoningContentReplay(model),
|
|
535
545
|
cacheBreakpointOptOutMessageIndexes,
|
|
546
|
+
compaction: replayPlan.compaction,
|
|
536
547
|
replayThinkingEnabled
|
|
537
548
|
});
|
|
538
549
|
const params = {
|
|
@@ -591,7 +602,8 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
591
602
|
applyAnthropicPayloadPolicyToParams(params, payloadPolicy, cacheBreakpointOptOutMessageIndexes);
|
|
592
603
|
return {
|
|
593
604
|
params,
|
|
594
|
-
toolProjection
|
|
605
|
+
toolProjection,
|
|
606
|
+
usedCompactionReplay: replayPlan.compaction !== void 0
|
|
595
607
|
};
|
|
596
608
|
}
|
|
597
609
|
function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
@@ -605,7 +617,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
605
617
|
const reasoningModelMaxTokens = resolvePositiveAnthropicTokenLimit(model.maxTokens) ?? baseMaxTokens;
|
|
606
618
|
const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
|
|
607
619
|
const reasoning = options?.reasoning === "off" && mandatoryAdaptiveThinking ? "low" : options?.reasoning;
|
|
608
|
-
const resolved = {
|
|
620
|
+
const resolved = copyProviderAcceptanceObserver(options, {
|
|
609
621
|
temperature: options?.temperature,
|
|
610
622
|
stop: options?.stop,
|
|
611
623
|
maxTokens: baseMaxTokens,
|
|
@@ -615,13 +627,16 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
615
627
|
sessionId: options?.sessionId,
|
|
616
628
|
headers: options?.headers,
|
|
617
629
|
onPayload: options?.onPayload,
|
|
630
|
+
onResponse: options?.onResponse,
|
|
618
631
|
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
619
632
|
metadata: options?.metadata,
|
|
620
633
|
interleavedThinking: options?.interleavedThinking,
|
|
621
634
|
toolChoice: options?.toolChoice,
|
|
622
635
|
thinkingBudgets: options?.thinkingBudgets,
|
|
623
|
-
reasoning
|
|
624
|
-
|
|
636
|
+
reasoning,
|
|
637
|
+
...options?.anthropicServerCompaction === true ? { anthropicServerCompaction: true } : {},
|
|
638
|
+
...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
|
|
639
|
+
});
|
|
625
640
|
if (reasoning === "off") {
|
|
626
641
|
resolved.thinkingEnabled = false;
|
|
627
642
|
return resolved;
|
|
@@ -664,6 +679,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
664
679
|
const eventSink = refusalBuffer ?? stream;
|
|
665
680
|
let costModel = model;
|
|
666
681
|
let messageStartPromptUsage;
|
|
682
|
+
let usedCompactionReplay = false;
|
|
667
683
|
try {
|
|
668
684
|
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
|
|
669
685
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
@@ -676,17 +692,29 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
676
692
|
options: transportOptions
|
|
677
693
|
});
|
|
678
694
|
const builtParams = await buildAnthropicParams(model, requestContext, isOAuthToken, transportOptions);
|
|
695
|
+
usedCompactionReplay = builtParams.usedCompactionReplay;
|
|
679
696
|
let params = builtParams.params;
|
|
680
697
|
const toolProjection = builtParams.toolProjection;
|
|
681
698
|
const nextParams = await transportOptions.onPayload?.(params, model);
|
|
682
699
|
if (nextParams !== void 0) params = nextParams;
|
|
683
700
|
applyClaudeRequestContract(params, model);
|
|
684
|
-
const anthropicStream = client.messages.stream({
|
|
701
|
+
const { response, stream: anthropicStream } = await client.messages.stream({
|
|
685
702
|
...params,
|
|
686
703
|
stream: true
|
|
687
704
|
}, transportOptions.signal ? { signal: transportOptions.signal } : void 0);
|
|
705
|
+
await notifyProviderHttpResponse({
|
|
706
|
+
options: transportOptions,
|
|
707
|
+
response,
|
|
708
|
+
model
|
|
709
|
+
});
|
|
710
|
+
if (!response.ok) {
|
|
711
|
+
const detail = await readAnthropicMessagesErrorBodySnippet(response);
|
|
712
|
+
throw new Error(formatAnthropicMessagesHttpError(response, detail));
|
|
713
|
+
}
|
|
688
714
|
const blocks = output.content;
|
|
689
715
|
const blockIndexes = /* @__PURE__ */ new Map();
|
|
716
|
+
const sealedToolCalls = [];
|
|
717
|
+
const compactionCapture = createCompactionCapture(output, model, transportOptions);
|
|
690
718
|
const pendingThinkingSignatures = /* @__PURE__ */ new Map();
|
|
691
719
|
const allowReasoningContentReplay = supportsReasoningContentReplay(model);
|
|
692
720
|
const reasoningContentThinkingBlocks = /* @__PURE__ */ new Map();
|
|
@@ -720,6 +748,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
720
748
|
partial: output
|
|
721
749
|
});
|
|
722
750
|
}
|
|
751
|
+
if (contentIndex === void 0) return false;
|
|
723
752
|
block.thinking += text;
|
|
724
753
|
block.thinkingSignature = "reasoning_content";
|
|
725
754
|
eventSink.push({
|
|
@@ -751,6 +780,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
751
780
|
partial: output
|
|
752
781
|
});
|
|
753
782
|
}
|
|
783
|
+
if (contentIndex === void 0) return false;
|
|
754
784
|
block.text += text;
|
|
755
785
|
eventSink.push({
|
|
756
786
|
type: "text_delta",
|
|
@@ -809,9 +839,11 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
809
839
|
if (event.type === "content_block_start") {
|
|
810
840
|
const contentBlock = event.content_block;
|
|
811
841
|
const index = typeof event.index === "number" ? event.index : -1;
|
|
842
|
+
if (transportOptions.anthropicServerCompaction === true && compactionCapture.begin(index, contentBlock, output.content.length)) continue;
|
|
812
843
|
const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
|
|
813
844
|
if (fallbackBoundary) {
|
|
814
845
|
refusalBuffer?.discard();
|
|
846
|
+
sealedToolCalls.length = 0;
|
|
815
847
|
pendingTextEnds.length = 0;
|
|
816
848
|
blockIndexes.clear();
|
|
817
849
|
pendingThinkingSignatures.clear();
|
|
@@ -945,6 +977,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
945
977
|
if (event.type === "content_block_delta") {
|
|
946
978
|
const delta = event.delta;
|
|
947
979
|
const eventIndex = typeof event.index === "number" ? event.index : void 0;
|
|
980
|
+
if (eventIndex !== void 0 && compactionCapture.delta(eventIndex, delta)) continue;
|
|
948
981
|
let index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
|
|
949
982
|
let block = index === void 0 ? void 0 : blocks[index];
|
|
950
983
|
if (allowReasoningContentReplay) {
|
|
@@ -953,7 +986,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
953
986
|
let appendedContent = false;
|
|
954
987
|
if (!hasNativeAnthropicDelta && typeof delta?.content === "string" && delta.content.length > 0) {
|
|
955
988
|
const text = sanitizeTransportPayloadText(delta.content);
|
|
956
|
-
if (text.length > 0) if (block?.type === "text") {
|
|
989
|
+
if (text.length > 0) if (block?.type === "text" && index !== void 0) {
|
|
957
990
|
block.text += text;
|
|
958
991
|
eventSink.push({
|
|
959
992
|
type: "text_delta",
|
|
@@ -981,6 +1014,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
981
1014
|
partial: output
|
|
982
1015
|
});
|
|
983
1016
|
}
|
|
1017
|
+
if (index === void 0) continue;
|
|
984
1018
|
if (block?.type === "text" && delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
985
1019
|
block.text += delta.text;
|
|
986
1020
|
eventSink.push({
|
|
@@ -1025,6 +1059,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1025
1059
|
}
|
|
1026
1060
|
if (event.type === "content_block_stop") {
|
|
1027
1061
|
const eventIndex = typeof event.index === "number" ? event.index : void 0;
|
|
1062
|
+
if (eventIndex !== void 0 && compactionCapture.complete(eventIndex)) continue;
|
|
1028
1063
|
const pendingSignature = eventIndex === void 0 ? void 0 : pendingThinkingSignatures.get(eventIndex);
|
|
1029
1064
|
if (eventIndex !== void 0) pendingThinkingSignatures.delete(eventIndex);
|
|
1030
1065
|
const index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
|
|
@@ -1057,12 +1092,9 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1057
1092
|
continue;
|
|
1058
1093
|
}
|
|
1059
1094
|
if (block.type === "toolCall") {
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
contentIndex: index,
|
|
1064
|
-
toolCall: block,
|
|
1065
|
-
partial: output
|
|
1095
|
+
sealedToolCalls.push({
|
|
1096
|
+
block,
|
|
1097
|
+
contentIndex: index
|
|
1066
1098
|
});
|
|
1067
1099
|
finishReasoningContentSidecars(event.index);
|
|
1068
1100
|
}
|
|
@@ -1079,9 +1111,20 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1079
1111
|
flushPendingTextEnds();
|
|
1080
1112
|
}
|
|
1081
1113
|
}
|
|
1082
|
-
if (
|
|
1114
|
+
if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
|
|
1083
1115
|
if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
|
|
1084
1116
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1117
|
+
if ([...blockIndexes.values()].some((index) => blocks[index]?.type === "toolCall")) throw new Error("Provider completed stream with an incomplete tool call");
|
|
1118
|
+
finalizeTerminalToolCallArguments(sealedToolCalls.map(({ block }) => block), (block) => block.partialJson && block.partialJson.length > 0 ? block.partialJson : block.arguments);
|
|
1119
|
+
for (const sealed of sealedToolCalls) {
|
|
1120
|
+
delete sealed.block.partialJson;
|
|
1121
|
+
eventSink.push({
|
|
1122
|
+
type: "toolcall_end",
|
|
1123
|
+
contentIndex: sealed.contentIndex,
|
|
1124
|
+
toolCall: sealed.block,
|
|
1125
|
+
partial: output
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1085
1128
|
refusalBuffer?.flush();
|
|
1086
1129
|
if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
|
|
1087
1130
|
flushPendingTextEnds();
|
|
@@ -1093,7 +1136,8 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1093
1136
|
if (refusalBuffer) {
|
|
1094
1137
|
refusalBuffer.discard();
|
|
1095
1138
|
output.content = [];
|
|
1096
|
-
}
|
|
1139
|
+
} else output.content = output.content.filter((block) => block.type !== "toolCall");
|
|
1140
|
+
if (usedCompactionReplay && isAnthropicReplayRejection(error)) suppressAnthropicCompaction(output, model, options);
|
|
1097
1141
|
failTransportStream({
|
|
1098
1142
|
stream,
|
|
1099
1143
|
output,
|
|
@@ -1210,15 +1254,11 @@ const MAX_TOKENS_PARAM_KEYS = [
|
|
|
1210
1254
|
"max_completion_tokens",
|
|
1211
1255
|
"max_tokens"
|
|
1212
1256
|
];
|
|
1213
|
-
/** Return a finite non-negative max-token value, or undefined for invalid input. */
|
|
1214
|
-
function resolveNonNegativeMaxTokensParam(value) {
|
|
1215
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1216
|
-
}
|
|
1217
1257
|
/** Resolve the first supported max-token parameter present in a params object. */
|
|
1218
1258
|
function resolveMaxTokensParam(params) {
|
|
1219
1259
|
if (!params) return;
|
|
1220
1260
|
for (const key of MAX_TOKENS_PARAM_KEYS) {
|
|
1221
|
-
const resolved =
|
|
1261
|
+
const resolved = asNonNegativeFiniteNumber(params[key]);
|
|
1222
1262
|
if (resolved !== void 0) return resolved;
|
|
1223
1263
|
}
|
|
1224
1264
|
}
|
|
@@ -1335,6 +1375,172 @@ function stripCompletionMessagesToRoleContent(messages) {
|
|
|
1335
1375
|
});
|
|
1336
1376
|
}
|
|
1337
1377
|
//#endregion
|
|
1378
|
+
//#region packages/ai/src/transports/openai-completions-host.ts
|
|
1379
|
+
/**
|
|
1380
|
+
* Chat Completions accepts Azure AI Foundry hosts in addition to traditional
|
|
1381
|
+
* Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
|
|
1382
|
+
* which intentionally excludes the .services.ai.azure.com Foundry suffix.
|
|
1383
|
+
*/
|
|
1384
|
+
function isAzureOpenAICompatibleHost(hostname) {
|
|
1385
|
+
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
1386
|
+
}
|
|
1387
|
+
//#endregion
|
|
1388
|
+
//#region packages/ai/src/transports/openai-completions-replay.ts
|
|
1389
|
+
function isGoogleOpenAICompatModel(model) {
|
|
1390
|
+
const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
|
|
1391
|
+
return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
|
|
1392
|
+
}
|
|
1393
|
+
function requiresGoogleCompatToolCallThoughtSignature(model) {
|
|
1394
|
+
return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
|
|
1395
|
+
}
|
|
1396
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
|
|
1397
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
|
|
1398
|
+
function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
|
|
1399
|
+
return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
|
|
1400
|
+
}
|
|
1401
|
+
function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
|
|
1402
|
+
if (!isGoogleOpenAICompatModel(model)) return;
|
|
1403
|
+
const sigById = /* @__PURE__ */ new Map();
|
|
1404
|
+
const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
|
|
1405
|
+
for (const msg of context.messages ?? []) {
|
|
1406
|
+
if (msg.role !== "assistant") continue;
|
|
1407
|
+
const source = msg;
|
|
1408
|
+
if (!Array.isArray(source.content)) continue;
|
|
1409
|
+
for (const block of source.content) {
|
|
1410
|
+
if (block.type !== "toolCall") continue;
|
|
1411
|
+
const id = block.id;
|
|
1412
|
+
const sig = block.thoughtSignature;
|
|
1413
|
+
if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
|
|
1414
|
+
const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
|
|
1415
|
+
if (!isSameRoute && !fallbackSig) continue;
|
|
1416
|
+
sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (sigById.size === 0 && !fallbackSig) return;
|
|
1421
|
+
for (const message of outgoingMessages) {
|
|
1422
|
+
const toolCalls = message.tool_calls;
|
|
1423
|
+
if (!Array.isArray(toolCalls)) continue;
|
|
1424
|
+
for (const toolCall of toolCalls) {
|
|
1425
|
+
const id = toolCall.id;
|
|
1426
|
+
if (typeof id !== "string") continue;
|
|
1427
|
+
let sig = sigById.get(id) ?? fallbackSig;
|
|
1428
|
+
if (typeof sig === "string" && sig.length > 0) {
|
|
1429
|
+
if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
|
|
1430
|
+
}
|
|
1431
|
+
if (typeof sig !== "string" || sig.length === 0) continue;
|
|
1432
|
+
const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
|
|
1433
|
+
toolCall.extra_content = extra;
|
|
1434
|
+
const google = extra.google && typeof extra.google === "object" ? extra.google : {};
|
|
1435
|
+
extra.google = google;
|
|
1436
|
+
google.thought_signature = sig;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
const COMPLETIONS_REASONING_REPLAY_FIELDS = [
|
|
1441
|
+
"reasoning_details",
|
|
1442
|
+
"reasoning_content",
|
|
1443
|
+
"reasoning",
|
|
1444
|
+
"reasoning_text"
|
|
1445
|
+
];
|
|
1446
|
+
function stripCompletionsReasoningReplayFields(record) {
|
|
1447
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
|
|
1448
|
+
}
|
|
1449
|
+
function sanitizeOpenRouterReasoningReplayFields(record) {
|
|
1450
|
+
const reasoningDetails = record.reasoning_details;
|
|
1451
|
+
if (typeof reasoningDetails === "string") {
|
|
1452
|
+
if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
|
|
1453
|
+
delete record.reasoning_details;
|
|
1454
|
+
} else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
|
|
1455
|
+
if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
|
|
1456
|
+
if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
|
|
1457
|
+
const reasoningText = record.reasoning_text;
|
|
1458
|
+
if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
|
|
1459
|
+
if ("reasoning_text" in record) delete record.reasoning_text;
|
|
1460
|
+
}
|
|
1461
|
+
function sanitizeReasoningContentReplayFields(record) {
|
|
1462
|
+
if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
|
|
1463
|
+
delete record.reasoning_details;
|
|
1464
|
+
delete record.reasoning;
|
|
1465
|
+
delete record.reasoning_text;
|
|
1466
|
+
}
|
|
1467
|
+
const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
1468
|
+
"deepseek-v4-flash",
|
|
1469
|
+
"deepseek-v4-pro",
|
|
1470
|
+
"kimi-for-coding",
|
|
1471
|
+
"kimi-k2.5",
|
|
1472
|
+
"kimi-k2.6",
|
|
1473
|
+
"kimi-k2.7-code",
|
|
1474
|
+
"kimi-k2.7-code-highspeed",
|
|
1475
|
+
"kimi-k3",
|
|
1476
|
+
"kimi-k2-thinking",
|
|
1477
|
+
"kimi-k2-thinking-turbo",
|
|
1478
|
+
"mimo-v2-pro",
|
|
1479
|
+
"mimo-v2-omni",
|
|
1480
|
+
"mimo-v2.5",
|
|
1481
|
+
"mimo-v2.5-pro",
|
|
1482
|
+
"mimo-v2.6-pro"
|
|
1483
|
+
]);
|
|
1484
|
+
const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
|
|
1485
|
+
"-free",
|
|
1486
|
+
"-paid",
|
|
1487
|
+
"-trial"
|
|
1488
|
+
];
|
|
1489
|
+
function stripReasoningContentReplayTierSuffix(modelId) {
|
|
1490
|
+
for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
|
|
1491
|
+
return modelId;
|
|
1492
|
+
}
|
|
1493
|
+
function getReasoningContentReplayModelIdCandidates(modelId) {
|
|
1494
|
+
if (typeof modelId !== "string") return [];
|
|
1495
|
+
const normalized = modelId.trim().toLowerCase();
|
|
1496
|
+
if (!normalized) return [];
|
|
1497
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
1498
|
+
const finalPart = parts[parts.length - 1] ?? normalized;
|
|
1499
|
+
const candidates = [finalPart];
|
|
1500
|
+
const colonParts = finalPart.split(":").filter(Boolean);
|
|
1501
|
+
if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
|
|
1502
|
+
const baseCount = candidates.length;
|
|
1503
|
+
for (let index = 0; index < baseCount; index += 1) {
|
|
1504
|
+
const candidate = candidates[index];
|
|
1505
|
+
if (typeof candidate !== "string") continue;
|
|
1506
|
+
const stripped = stripReasoningContentReplayTierSuffix(candidate);
|
|
1507
|
+
if (stripped !== candidate) candidates.push(stripped);
|
|
1508
|
+
}
|
|
1509
|
+
return uniqueStrings(candidates.filter(Boolean));
|
|
1510
|
+
}
|
|
1511
|
+
function shouldPreserveReasoningContentReplay(model, compat) {
|
|
1512
|
+
if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
|
|
1513
|
+
return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
|
|
1514
|
+
}
|
|
1515
|
+
function shouldPreserveOpenRouterReasoningReplay(model) {
|
|
1516
|
+
if (model.provider !== "openrouter") return true;
|
|
1517
|
+
const normalizedModelId = model.id.trim().toLowerCase();
|
|
1518
|
+
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
1519
|
+
}
|
|
1520
|
+
function shouldTrustReasoningContentReplayMetadata(model) {
|
|
1521
|
+
if (!model.reasoning) return false;
|
|
1522
|
+
if (model.provider.trim().toLowerCase() === "openai") return false;
|
|
1523
|
+
return shouldPreserveOpenRouterReasoningReplay(model);
|
|
1524
|
+
}
|
|
1525
|
+
function sanitizeCompletionsReasoningReplayFields(messages, options) {
|
|
1526
|
+
if (!Array.isArray(messages)) return;
|
|
1527
|
+
for (const msg of messages) {
|
|
1528
|
+
if (!msg || typeof msg !== "object") continue;
|
|
1529
|
+
const record = msg;
|
|
1530
|
+
if (record.role !== "assistant") continue;
|
|
1531
|
+
if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
|
|
1532
|
+
else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
|
|
1533
|
+
else stripCompletionsReasoningReplayFields(record);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
1537
|
+
injectToolCallThoughtSignatures(outgoingMessages, context, model);
|
|
1538
|
+
sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
|
|
1539
|
+
preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
|
|
1540
|
+
preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
//#endregion
|
|
1338
1544
|
//#region packages/ai/src/transports/openai-transport-params.ts
|
|
1339
1545
|
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
1340
1546
|
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
@@ -1355,13 +1561,51 @@ function readCodeModePayloadToolName(tool) {
|
|
|
1355
1561
|
const fnName = readToolPayloadField(fn, "name");
|
|
1356
1562
|
return typeof fnName === "string" ? fnName : void 0;
|
|
1357
1563
|
}
|
|
1358
|
-
function
|
|
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) {
|
|
1359
1601
|
if (!isRecord(payload)) return;
|
|
1360
1602
|
const tools = readToolPayloadField(payload, "tools");
|
|
1361
1603
|
if (!Array.isArray(tools)) return;
|
|
1604
|
+
const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
|
|
1362
1605
|
payload.tools = tools.flatMap((tool) => {
|
|
1363
|
-
const
|
|
1364
|
-
if (
|
|
1606
|
+
const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
|
|
1607
|
+
if (identity) return [tool];
|
|
1608
|
+
if (identity === false) return [];
|
|
1365
1609
|
if (!isRecord(tool)) return [];
|
|
1366
1610
|
const filteredGroups = {};
|
|
1367
1611
|
for (const key of ["functionDeclarations", "function_declarations"]) {
|
|
@@ -1375,24 +1619,31 @@ function filterCodeModePayloadTools(payload, visibleToolNames) {
|
|
|
1375
1619
|
}
|
|
1376
1620
|
return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
|
|
1377
1621
|
});
|
|
1622
|
+
if (beforeToolIdentities) observer?.({
|
|
1623
|
+
beforeToolIdentities,
|
|
1624
|
+
afterToolIdentities: readCodeModePayloadToolIdentities(payload)
|
|
1625
|
+
});
|
|
1378
1626
|
}
|
|
1379
1627
|
function resolveCodeModeResponsesVisibleToolNames(context) {
|
|
1380
1628
|
return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
|
|
1381
1629
|
}
|
|
1382
|
-
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
|
|
1630
|
+
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes, observer) {
|
|
1383
1631
|
if (!isRecord(payload)) return;
|
|
1384
1632
|
const tools = readToolPayloadField(payload, "tools");
|
|
1385
1633
|
if (!Array.isArray(tools)) return;
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
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)
|
|
1389
1639
|
});
|
|
1390
1640
|
}
|
|
1391
|
-
function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
|
|
1641
|
+
function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1392
1642
|
const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
|
|
1393
1643
|
if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
|
|
1394
|
-
const
|
|
1395
|
-
|
|
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;
|
|
1396
1647
|
throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
|
|
1397
1648
|
}
|
|
1398
1649
|
function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
|
|
@@ -1469,7 +1720,7 @@ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, se
|
|
|
1469
1720
|
...optionHeaders,
|
|
1470
1721
|
...turnHeaders
|
|
1471
1722
|
};
|
|
1472
|
-
const resolvedHeaders = resolveProviderRequestPolicyConfig({
|
|
1723
|
+
const resolvedHeaders = resolveProviderRequestPolicyConfig(model, {
|
|
1473
1724
|
provider: model.provider,
|
|
1474
1725
|
api: model.api,
|
|
1475
1726
|
baseUrl: model.baseUrl,
|
|
@@ -1514,67 +1765,10 @@ function getCompat(model) {
|
|
|
1514
1765
|
};
|
|
1515
1766
|
}
|
|
1516
1767
|
//#endregion
|
|
1517
|
-
//#region packages/ai/src/transports/openai-completions-
|
|
1518
|
-
function hasToolHistory(messages) {
|
|
1519
|
-
return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
|
|
1520
|
-
}
|
|
1521
|
-
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
1522
|
-
const messages = params.messages;
|
|
1523
|
-
if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
|
|
1524
|
-
throw new Error(`OpenAI-compatible chat payload for ${model.provider}/${model.id} contains no non-empty user or assistant messages after compaction and transport transforms; refusing to send a system/tool-only request. Start a new user turn or repair the compacted session history.`);
|
|
1525
|
-
}
|
|
1526
|
-
const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
|
|
1527
|
-
const SSE_DONE_MAX_LINE_CHARS = 1024;
|
|
1528
|
-
function createSseDoneDetector() {
|
|
1529
|
-
const decoder = new TextDecoder();
|
|
1530
|
-
let line = "";
|
|
1531
|
-
let lineOverflowed = false;
|
|
1532
|
-
let sawDone = false;
|
|
1533
|
-
const finishLine = () => {
|
|
1534
|
-
if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
|
|
1535
|
-
line = "";
|
|
1536
|
-
lineOverflowed = false;
|
|
1537
|
-
};
|
|
1538
|
-
const observeText = (text) => {
|
|
1539
|
-
for (const char of text) {
|
|
1540
|
-
if (char === "\n" || char === "\r") {
|
|
1541
|
-
finishLine();
|
|
1542
|
-
continue;
|
|
1543
|
-
}
|
|
1544
|
-
if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
|
|
1545
|
-
else lineOverflowed = true;
|
|
1546
|
-
}
|
|
1547
|
-
};
|
|
1548
|
-
return {
|
|
1549
|
-
observe(chunk) {
|
|
1550
|
-
if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
|
|
1551
|
-
},
|
|
1552
|
-
finish() {
|
|
1553
|
-
if (sawDone) return;
|
|
1554
|
-
observeText(decoder.decode());
|
|
1555
|
-
if (line || lineOverflowed) finishLine();
|
|
1556
|
-
},
|
|
1557
|
-
sawDone: () => sawDone
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
|
|
1561
|
-
const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
|
|
1562
|
-
return new OpenAI({
|
|
1563
|
-
apiKey,
|
|
1564
|
-
baseURL: clientConfig.baseURL,
|
|
1565
|
-
dangerouslyAllowBrowser: true,
|
|
1566
|
-
defaultHeaders: clientConfig.defaultHeaders,
|
|
1567
|
-
defaultQuery: clientConfig.defaultQuery,
|
|
1568
|
-
fetch: opts?.fetch ?? buildGuardedModelFetch(model),
|
|
1569
|
-
...buildOpenAISdkClientOptions(model)
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
function isAzureOpenAICompatibleHost(hostname) {
|
|
1573
|
-
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
1574
|
-
}
|
|
1768
|
+
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
1575
1769
|
function isKnownOpenAICompletionsEndpoint(model) {
|
|
1576
1770
|
if (!model.baseUrl.trim()) return true;
|
|
1577
|
-
const endpointClass = resolveProviderEndpoint(model
|
|
1771
|
+
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
1578
1772
|
if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
|
|
1579
1773
|
try {
|
|
1580
1774
|
return isAzureOpenAICompatibleHost(new URL(model.baseUrl).hostname.toLowerCase());
|
|
@@ -1582,523 +1776,265 @@ function isKnownOpenAICompletionsEndpoint(model) {
|
|
|
1582
1776
|
return false;
|
|
1583
1777
|
}
|
|
1584
1778
|
}
|
|
1585
|
-
function
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
} catch {}
|
|
1599
|
-
if (isAzureHost) {
|
|
1600
|
-
const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
|
|
1601
|
-
if (apiVersionHeader) {
|
|
1602
|
-
const apiVersion = headers[apiVersionHeader]?.trim();
|
|
1603
|
-
delete headers[apiVersionHeader];
|
|
1604
|
-
if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1779
|
+
function resolveOpenAICompletionsReasoningEffort$1(options) {
|
|
1780
|
+
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1781
|
+
}
|
|
1782
|
+
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
1783
|
+
if (options?.maxTokens) return {
|
|
1784
|
+
maxTokens: options.maxTokens,
|
|
1785
|
+
clampToModelMaxTokens: true
|
|
1786
|
+
};
|
|
1787
|
+
const paramsMaxTokens = resolveMaxTokensParam(model.params);
|
|
1788
|
+
if (paramsMaxTokens) return {
|
|
1789
|
+
maxTokens: paramsMaxTokens,
|
|
1790
|
+
clampToModelMaxTokens: false
|
|
1791
|
+
};
|
|
1607
1792
|
return {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
|
|
1793
|
+
maxTokens: model.maxTokens,
|
|
1794
|
+
clampToModelMaxTokens: false
|
|
1611
1795
|
};
|
|
1612
1796
|
}
|
|
1613
|
-
function
|
|
1614
|
-
return (model
|
|
1615
|
-
const eventStream = createAssistantMessageEventStream();
|
|
1616
|
-
const stream = eventStream;
|
|
1617
|
-
(async () => {
|
|
1618
|
-
const output = {
|
|
1619
|
-
role: "assistant",
|
|
1620
|
-
content: [],
|
|
1621
|
-
api: model.api,
|
|
1622
|
-
provider: model.provider,
|
|
1623
|
-
model: model.id,
|
|
1624
|
-
usage: {
|
|
1625
|
-
input: 0,
|
|
1626
|
-
output: 0,
|
|
1627
|
-
cacheRead: 0,
|
|
1628
|
-
cacheWrite: 0,
|
|
1629
|
-
totalTokens: 0,
|
|
1630
|
-
cost: {
|
|
1631
|
-
input: 0,
|
|
1632
|
-
output: 0,
|
|
1633
|
-
cacheRead: 0,
|
|
1634
|
-
cacheWrite: 0,
|
|
1635
|
-
total: 0
|
|
1636
|
-
}
|
|
1637
|
-
},
|
|
1638
|
-
stopReason: "stop",
|
|
1639
|
-
timestamp: Date.now()
|
|
1640
|
-
};
|
|
1641
|
-
let firstEventAbort;
|
|
1642
|
-
try {
|
|
1643
|
-
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
1644
|
-
const doneDetector = createSseDoneDetector();
|
|
1645
|
-
const baseFetch = buildGuardedModelFetch(model);
|
|
1646
|
-
const doneDetectingFetch = async (url, init) => {
|
|
1647
|
-
const response = await baseFetch(url, init);
|
|
1648
|
-
if (!response.body || !response.ok) return response;
|
|
1649
|
-
if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
|
|
1650
|
-
const transformed = response.body.pipeThrough(new TransformStream({
|
|
1651
|
-
transform(chunk, controller) {
|
|
1652
|
-
doneDetector.observe(chunk);
|
|
1653
|
-
controller.enqueue(chunk);
|
|
1654
|
-
},
|
|
1655
|
-
flush() {
|
|
1656
|
-
doneDetector.finish();
|
|
1657
|
-
}
|
|
1658
|
-
}));
|
|
1659
|
-
return new Response(transformed, {
|
|
1660
|
-
headers: response.headers,
|
|
1661
|
-
status: response.status,
|
|
1662
|
-
statusText: response.statusText
|
|
1663
|
-
});
|
|
1664
|
-
};
|
|
1665
|
-
const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
|
|
1666
|
-
let params = buildOpenAICompletionsParams(model, context, options);
|
|
1667
|
-
const nextParams = await options?.onPayload?.(params, model);
|
|
1668
|
-
if (nextParams !== void 0) params = nextParams;
|
|
1669
|
-
if (options?.openclawCodeModeToolSurface === true) {
|
|
1670
|
-
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
1671
|
-
enforceCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
1672
|
-
assertCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
1673
|
-
}
|
|
1674
|
-
if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
|
|
1675
|
-
const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
|
|
1676
|
-
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
1677
|
-
const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
|
|
1678
|
-
timeoutMs: options?.timeoutMs,
|
|
1679
|
-
maxRetries: options?.maxRetries
|
|
1680
|
-
}));
|
|
1681
|
-
stream.push({
|
|
1682
|
-
type: "start",
|
|
1683
|
-
partial: output
|
|
1684
|
-
});
|
|
1685
|
-
await processOpenAICompletionsStream(responseStream, output, model, stream, {
|
|
1686
|
-
signal: options?.signal,
|
|
1687
|
-
emitReasoning,
|
|
1688
|
-
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
1689
|
-
abortFirstEventStream: firstEventAbort.abort,
|
|
1690
|
-
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
1691
|
-
sawStreamDONE: doneDetector.sawDone
|
|
1692
|
-
});
|
|
1693
|
-
finalizeTransportStream({
|
|
1694
|
-
stream,
|
|
1695
|
-
output,
|
|
1696
|
-
signal: options?.signal
|
|
1697
|
-
});
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
failTransportStream({
|
|
1700
|
-
stream,
|
|
1701
|
-
output,
|
|
1702
|
-
signal: options?.signal,
|
|
1703
|
-
error,
|
|
1704
|
-
cleanup: () => {
|
|
1705
|
-
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
1706
|
-
finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
|
|
1707
|
-
}
|
|
1708
|
-
});
|
|
1709
|
-
} finally {
|
|
1710
|
-
firstEventAbort?.dispose();
|
|
1711
|
-
}
|
|
1712
|
-
})();
|
|
1713
|
-
return eventStream;
|
|
1714
|
-
};
|
|
1797
|
+
function resolveOpenAICompletionsModelMaxTokens(model) {
|
|
1798
|
+
return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
|
|
1715
1799
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
let
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
|
|
1746
|
-
pendingPostToolCallBytes += nextBytes;
|
|
1747
|
-
const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
|
|
1748
|
-
if (!previous || previous.kind !== next.kind) {
|
|
1749
|
-
pendingPostToolCallDeltas.push(next);
|
|
1750
|
-
return;
|
|
1800
|
+
const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
|
|
1801
|
+
const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
|
|
1802
|
+
function estimateOpenAICompletionsInputTokens(payload) {
|
|
1803
|
+
let adjustedChars = 0;
|
|
1804
|
+
adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
|
|
1805
|
+
if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
|
|
1806
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
|
|
1807
|
+
} catch {
|
|
1808
|
+
adjustedChars += 1024;
|
|
1809
|
+
}
|
|
1810
|
+
if (payload.response_format !== void 0) try {
|
|
1811
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
|
|
1812
|
+
} catch {
|
|
1813
|
+
adjustedChars += 256;
|
|
1814
|
+
}
|
|
1815
|
+
return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
|
|
1816
|
+
}
|
|
1817
|
+
function estimateOpenAICompletionsMessagesChars(messages) {
|
|
1818
|
+
if (!Array.isArray(messages)) return 0;
|
|
1819
|
+
let adjustedChars = 0;
|
|
1820
|
+
for (const message of messages) {
|
|
1821
|
+
if (!message || typeof message !== "object") continue;
|
|
1822
|
+
const record = message;
|
|
1823
|
+
adjustedChars += estimateOpenAICompletionsContentChars(record.content);
|
|
1824
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
|
|
1825
|
+
if (record.tool_calls !== void 0) try {
|
|
1826
|
+
adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
|
|
1827
|
+
} catch {
|
|
1828
|
+
adjustedChars += 256;
|
|
1751
1829
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1830
|
+
}
|
|
1831
|
+
return adjustedChars;
|
|
1832
|
+
}
|
|
1833
|
+
function estimateOpenAICompletionsContentChars(value) {
|
|
1834
|
+
if (typeof value === "string") return estimateStringChars(value);
|
|
1835
|
+
if (!Array.isArray(value)) return 0;
|
|
1836
|
+
let adjustedChars = 0;
|
|
1837
|
+
for (const block of value) {
|
|
1838
|
+
if (!block || typeof block !== "object") continue;
|
|
1839
|
+
const record = block;
|
|
1840
|
+
if (record.type === "image_url" || record.type === "input_image") {
|
|
1841
|
+
adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
|
|
1842
|
+
continue;
|
|
1759
1843
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
currentBlock = {
|
|
1765
|
-
type: "thinking",
|
|
1766
|
-
thinking: "",
|
|
1767
|
-
...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
|
|
1768
|
-
};
|
|
1769
|
-
output.content.push(currentBlock);
|
|
1770
|
-
pushStreamEvent({
|
|
1771
|
-
type: "thinking_start",
|
|
1772
|
-
contentIndex: blockIndex(),
|
|
1773
|
-
partial: output
|
|
1774
|
-
});
|
|
1844
|
+
const text = record.text;
|
|
1845
|
+
if (typeof text === "string") {
|
|
1846
|
+
adjustedChars += estimateStringChars(text);
|
|
1847
|
+
continue;
|
|
1775
1848
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
delta: reasoningDelta.text,
|
|
1781
|
-
partial: output
|
|
1782
|
-
});
|
|
1783
|
-
};
|
|
1784
|
-
const appendTextDeltaInternal = (text) => {
|
|
1785
|
-
if (!currentBlock || currentBlock.type !== "text") {
|
|
1786
|
-
currentBlock = {
|
|
1787
|
-
type: "text",
|
|
1788
|
-
text: ""
|
|
1789
|
-
};
|
|
1790
|
-
output.content.push(currentBlock);
|
|
1791
|
-
pushStreamEvent({
|
|
1792
|
-
type: "text_start",
|
|
1793
|
-
contentIndex: blockIndex(),
|
|
1794
|
-
partial: output
|
|
1795
|
-
});
|
|
1849
|
+
try {
|
|
1850
|
+
adjustedChars += estimateStringChars(JSON.stringify(block));
|
|
1851
|
+
} catch {
|
|
1852
|
+
adjustedChars += 256;
|
|
1796
1853
|
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
};
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1854
|
+
}
|
|
1855
|
+
return adjustedChars;
|
|
1856
|
+
}
|
|
1857
|
+
function resolveOpenAICompletionsEffectiveContextTokens(model) {
|
|
1858
|
+
const contextTokens = model.contextTokens;
|
|
1859
|
+
if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
|
|
1860
|
+
return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
|
|
1861
|
+
}
|
|
1862
|
+
function isQwenOpenAICompletionsThinkingFormat(format) {
|
|
1863
|
+
return format === "qwen" || format === "qwen-chat-template";
|
|
1864
|
+
}
|
|
1865
|
+
function setQwenChatTemplateThinking(params, enabled) {
|
|
1866
|
+
const existing = params.chat_template_kwargs;
|
|
1867
|
+
params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
|
|
1868
|
+
...existing,
|
|
1869
|
+
enable_thinking: enabled
|
|
1870
|
+
} : { enable_thinking: enabled };
|
|
1871
|
+
}
|
|
1872
|
+
function applyQwenOpenAICompletionsThinkingParams(params) {
|
|
1873
|
+
if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
|
|
1874
|
+
const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
|
|
1875
|
+
if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
|
|
1876
|
+
else params.payload.enable_thinking = enabled;
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
function applyTogetherOpenAICompletionsThinkingParams(params) {
|
|
1880
|
+
if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
|
|
1881
|
+
params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
|
|
1882
|
+
}
|
|
1883
|
+
function convertTools(tools, compat, model) {
|
|
1884
|
+
const projection = projectOpenAITools(tools);
|
|
1885
|
+
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
|
|
1886
|
+
transport: "stream",
|
|
1887
|
+
supportsStrictMode: compat?.supportsStrictMode
|
|
1888
|
+
}), {
|
|
1889
|
+
transport: "completions",
|
|
1890
|
+
model
|
|
1891
|
+
});
|
|
1892
|
+
return {
|
|
1893
|
+
projection,
|
|
1894
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
1895
|
+
const functionTool = {
|
|
1896
|
+
name: tool.name,
|
|
1897
|
+
description: tool.description,
|
|
1898
|
+
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
1899
|
+
};
|
|
1900
|
+
if (strict !== void 0) functionTool.strict = strict;
|
|
1901
|
+
return {
|
|
1902
|
+
type: "function",
|
|
1903
|
+
function: functionTool
|
|
1904
|
+
};
|
|
1905
|
+
})
|
|
1821
1906
|
};
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1907
|
+
}
|
|
1908
|
+
function buildOpenAICompletionsParams(model, context, options) {
|
|
1909
|
+
const compat = getCompat(model);
|
|
1910
|
+
const compatDetection = detectOpenAICompletionsCompat(model);
|
|
1911
|
+
let messages = convertMessages(model, context.systemPrompt ? {
|
|
1912
|
+
...context,
|
|
1913
|
+
systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
|
|
1914
|
+
} : context, compat);
|
|
1915
|
+
applyCompletionsReplay(messages, context, model, compat);
|
|
1916
|
+
if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
|
|
1917
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
1918
|
+
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
1919
|
+
const params = {
|
|
1920
|
+
model: model.id,
|
|
1921
|
+
messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
|
|
1922
|
+
stream: true
|
|
1829
1923
|
};
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1924
|
+
if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
|
|
1925
|
+
if (compat.supportsStore) params.store = false;
|
|
1926
|
+
if (compat.supportsPromptCacheKey && promptCacheKey) {
|
|
1927
|
+
params.prompt_cache_key = promptCacheKey;
|
|
1928
|
+
if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
|
|
1929
|
+
}
|
|
1930
|
+
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
1931
|
+
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
1932
|
+
const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
1933
|
+
provider: model.provider,
|
|
1934
|
+
baseUrl: model.baseUrl,
|
|
1935
|
+
hasTools: () => Boolean(context.tools?.length)
|
|
1936
|
+
}) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
1937
|
+
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
1938
|
+
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
1939
|
+
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
1940
|
+
if (options?.seed !== void 0) params.seed = options.seed;
|
|
1941
|
+
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
1942
|
+
if (supportsModelTools(model)) {
|
|
1943
|
+
if (context.tools) {
|
|
1944
|
+
const converted = convertTools(context.tools, compat, model);
|
|
1945
|
+
if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
|
|
1946
|
+
else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1947
|
+
if (options?.toolChoice) {
|
|
1948
|
+
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
|
|
1949
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1950
|
+
} else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
|
|
1951
|
+
} else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1952
|
+
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
|
|
1953
|
+
delete params.tools;
|
|
1954
|
+
delete params.tool_choice;
|
|
1834
1955
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
1846
|
-
pushStreamEvent({
|
|
1847
|
-
type: "toolcall_start",
|
|
1848
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
1849
|
-
partial: output
|
|
1850
|
-
});
|
|
1851
|
-
pushStreamEvent({
|
|
1852
|
-
type: "toolcall_delta",
|
|
1853
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
1854
|
-
delta: toolCall.partialArgs,
|
|
1855
|
-
partial: output
|
|
1856
|
-
});
|
|
1857
|
-
};
|
|
1858
|
-
const appendFilteredVisibleTextDelta = (text) => {
|
|
1859
|
-
const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
|
|
1860
|
-
kind: "text",
|
|
1861
|
-
text
|
|
1862
|
-
}];
|
|
1863
|
-
for (const recoveredPart of recoveredParts) {
|
|
1864
|
-
if (recoveredPart.kind === "toolCall") {
|
|
1865
|
-
appendRecoveredToolCall(recoveredPart);
|
|
1866
|
-
continue;
|
|
1867
|
-
}
|
|
1868
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
1869
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
1956
|
+
}
|
|
1957
|
+
{
|
|
1958
|
+
const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
|
|
1959
|
+
const effectiveMaxTokens = maxTokenBudget.maxTokens;
|
|
1960
|
+
const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
|
|
1961
|
+
let clampedMaxTokens = effectiveMaxTokens;
|
|
1962
|
+
const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
|
|
1963
|
+
if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
|
|
1964
|
+
clampedMaxTokens = modelMaxTokens;
|
|
1965
|
+
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
|
|
1870
1966
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
appendRecoveredToolCall(recoveredPart);
|
|
1878
|
-
continue;
|
|
1967
|
+
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
|
|
1968
|
+
const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
|
|
1969
|
+
const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
|
|
1970
|
+
if (clampedMaxTokens > remainingBudget) {
|
|
1971
|
+
clampedMaxTokens = remainingBudget;
|
|
1972
|
+
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
|
|
1879
1973
|
}
|
|
1880
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
1881
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
1882
1974
|
}
|
|
1975
|
+
if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1976
|
+
else params.max_completion_tokens = clampedMaxTokens;
|
|
1977
|
+
}
|
|
1978
|
+
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort$1(options);
|
|
1979
|
+
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1980
|
+
model,
|
|
1981
|
+
effort: completionsReasoningEffort,
|
|
1982
|
+
fallbackMap: compat.reasoningEffortMap
|
|
1983
|
+
}) : void 0;
|
|
1984
|
+
const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
|
|
1985
|
+
const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
|
|
1986
|
+
const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
|
|
1987
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1988
|
+
modelReasoning: model.reasoning,
|
|
1989
|
+
payload: params,
|
|
1990
|
+
requestedEffort: completionsReasoningEffort
|
|
1991
|
+
});
|
|
1992
|
+
applyTogetherOpenAICompletionsThinkingParams({
|
|
1993
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1994
|
+
modelReasoning: model.reasoning,
|
|
1995
|
+
payload: params,
|
|
1996
|
+
requestedEffort: completionsReasoningEffort
|
|
1997
|
+
});
|
|
1998
|
+
if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
|
|
1999
|
+
else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
|
|
2000
|
+
else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
|
|
2001
|
+
return params;
|
|
2002
|
+
}
|
|
2003
|
+
//#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
|
|
1883
2029
|
};
|
|
1884
|
-
const
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
return;
|
|
1893
|
-
}
|
|
1894
|
-
if (!emitReasoning) return;
|
|
1895
|
-
if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
|
|
1896
|
-
else appendThinkingDelta(delta);
|
|
1897
|
-
};
|
|
1898
|
-
const appendPartitionedVisibleDelta = (delta) => {
|
|
1899
|
-
if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
|
|
1900
|
-
};
|
|
1901
|
-
const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
|
|
1902
|
-
if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
|
|
1903
|
-
const latestBlock = output.content[output.content.length - 1];
|
|
1904
|
-
if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
|
|
1905
|
-
if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
|
|
1906
|
-
appendThinkingDelta({ text: "" });
|
|
1907
|
-
};
|
|
1908
|
-
const flushReasoningTagTextPartitionerAtEnd = () => {
|
|
1909
|
-
for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
|
|
1910
|
-
};
|
|
1911
|
-
const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
|
|
1912
|
-
const guardedStream = withFirstStreamEventTimeout(responseStream, {
|
|
1913
|
-
provider: model.provider,
|
|
1914
|
-
api: model.api,
|
|
1915
|
-
model: model.id,
|
|
1916
|
-
timeoutMs: options?.firstEventTimeoutMs ?? 0,
|
|
1917
|
-
stage: "completions",
|
|
1918
|
-
abort: options?.abortFirstEventStream,
|
|
1919
|
-
onTimeout: options?.onFirstEventTimeout,
|
|
1920
|
-
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."
|
|
1921
|
-
});
|
|
1922
|
-
for await (const rawChunk of guardedStream) {
|
|
1923
|
-
throwIfModelStreamAborted(options?.signal);
|
|
1924
|
-
chunkPushedEvent = false;
|
|
1925
|
-
if (!rawChunk || typeof rawChunk !== "object") {
|
|
1926
|
-
await cooperativeScheduler.afterEvent();
|
|
1927
|
-
continue;
|
|
1928
|
-
}
|
|
1929
|
-
notifyLlmRequestActivity(options?.signal);
|
|
1930
|
-
const chunk = rawChunk;
|
|
1931
|
-
output.responseId ||= chunk.id;
|
|
1932
|
-
let hasReasoningUsageActivity = false;
|
|
1933
|
-
if (chunk.usage) {
|
|
1934
|
-
output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
|
|
1935
|
-
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
|
|
1936
|
-
}
|
|
1937
|
-
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
1938
|
-
if (!choice) {
|
|
1939
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
1940
|
-
await cooperativeScheduler.afterEvent();
|
|
1941
|
-
continue;
|
|
1942
|
-
}
|
|
1943
|
-
const choiceUsage = choice.usage;
|
|
1944
|
-
if (!chunk.usage && choiceUsage) {
|
|
1945
|
-
output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
|
|
1946
|
-
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
|
|
1947
|
-
}
|
|
1948
|
-
if (choice.finish_reason) {
|
|
1949
|
-
const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
|
|
1950
|
-
output.stopReason = finishReasonResult.stopReason;
|
|
1951
|
-
if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
|
|
1952
|
-
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
1953
|
-
}
|
|
1954
|
-
const rawChoiceDelta = choice.delta ?? choice.message;
|
|
1955
|
-
if (!rawChoiceDelta) {
|
|
1956
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
1957
|
-
await cooperativeScheduler.afterEvent();
|
|
1958
|
-
continue;
|
|
1959
|
-
}
|
|
1960
|
-
for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
|
|
1961
|
-
const choiceDelta = normalizedDelta.delta;
|
|
1962
|
-
const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
|
|
1963
|
-
const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
|
|
1964
|
-
if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
|
|
1965
|
-
const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningDeltas.filter((reasoningDelta) => reasoningDelta.kind === "thinking").map((reasoningDelta) => reasoningDelta.text));
|
|
1966
|
-
const appendReasoningDeltas = () => {
|
|
1967
|
-
for (const reasoningDelta of reasoningDeltas) {
|
|
1968
|
-
if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
|
|
1969
|
-
if (currentBlock?.type === "toolCall") {
|
|
1970
|
-
queuePostToolCallDelta({ ...reasoningDelta });
|
|
1971
|
-
continue;
|
|
1972
|
-
}
|
|
1973
|
-
if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
|
|
1974
|
-
else if (emitReasoning) appendThinkingDelta(reasoningDelta);
|
|
1975
|
-
}
|
|
1976
|
-
};
|
|
1977
|
-
if (hasMirroredReasoning) appendReasoningDeltas();
|
|
1978
|
-
for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
|
|
1979
|
-
const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
|
|
1980
|
-
for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
|
|
1981
|
-
} else {
|
|
1982
|
-
if (reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
|
|
1983
|
-
appendRoutedContentDelta(contentDelta);
|
|
1984
|
-
}
|
|
1985
|
-
if (!hasMirroredReasoning) appendReasoningDeltas();
|
|
1986
|
-
const toolCallDeltas = normalizedDelta.toolCalls;
|
|
1987
|
-
if (toolCallDeltas.length > 0) {
|
|
1988
|
-
sawNativeToolCallDelta = true;
|
|
1989
|
-
flushReasoningTagTextPartitionerAtEnd();
|
|
1990
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
1991
|
-
for (const toolCall of toolCallDeltas) {
|
|
1992
|
-
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
|
|
1993
|
-
let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
|
|
1994
|
-
if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
|
|
1995
|
-
if (!block) {
|
|
1996
|
-
if (currentBlock?.type === "toolCall") {
|
|
1997
|
-
currentBlock = null;
|
|
1998
|
-
flushPendingPostToolCallDeltas();
|
|
1999
|
-
}
|
|
2000
|
-
const initialSig = extractGoogleThoughtSignature(toolCall);
|
|
2001
|
-
block = {
|
|
2002
|
-
type: "toolCall",
|
|
2003
|
-
id: toolCall.id || "",
|
|
2004
|
-
name: toolCall.function?.name || "",
|
|
2005
|
-
arguments: {},
|
|
2006
|
-
partialArgs: "",
|
|
2007
|
-
...initialSig ? { thoughtSignature: initialSig } : {}
|
|
2008
|
-
};
|
|
2009
|
-
output.content.push(block);
|
|
2010
|
-
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2011
|
-
pushStreamEvent({
|
|
2012
|
-
type: "toolcall_start",
|
|
2013
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2014
|
-
partial: output
|
|
2015
|
-
});
|
|
2016
|
-
}
|
|
2017
|
-
if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
|
|
2018
|
-
if (toolCall.id) {
|
|
2019
|
-
block.id = toolCall.id;
|
|
2020
|
-
toolCallBlocksById.set(toolCall.id, block);
|
|
2021
|
-
}
|
|
2022
|
-
currentBlock = block;
|
|
2023
|
-
if (toolCall.function?.name) block.name = toolCall.function.name;
|
|
2024
|
-
const deltaSig = extractGoogleThoughtSignature(toolCall);
|
|
2025
|
-
if (deltaSig) block.thoughtSignature = deltaSig;
|
|
2026
|
-
if (toolCall.function?.arguments) {
|
|
2027
|
-
const nextArgumentBytes = measureUtf8Bytes(toolCall.function.arguments);
|
|
2028
|
-
const currentBlockArgBytes = toolCallBlockBytes.get(block) ?? 0;
|
|
2029
|
-
if (currentBlockArgBytes + nextArgumentBytes > MAX_TOOL_CALL_ARGUMENT_BUFFER_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
|
|
2030
|
-
toolCallBlockBytes.set(block, currentBlockArgBytes + nextArgumentBytes);
|
|
2031
|
-
block.partialArgs += toolCall.function.arguments;
|
|
2032
|
-
block.arguments = parseStreamingJson(block.partialArgs);
|
|
2033
|
-
pushStreamEvent({
|
|
2034
|
-
type: "toolcall_delta",
|
|
2035
|
-
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2036
|
-
delta: toolCall.function.arguments,
|
|
2037
|
-
partial: output
|
|
2038
|
-
});
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
flushPendingPostToolCallDeltas();
|
|
2044
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2045
|
-
await cooperativeScheduler.afterEvent();
|
|
2046
|
-
}
|
|
2047
|
-
flushReasoningTagTextPartitionerAtEnd();
|
|
2048
|
-
flushDeepSeekToolCallRecovererAtEnd();
|
|
2049
|
-
flushDeepSeekTextFilterAtEnd();
|
|
2050
|
-
currentBlock = null;
|
|
2051
|
-
flushPendingPostToolCallDeltas();
|
|
2052
|
-
finalizeOpenAICompletionsToolCalls(output, {
|
|
2053
|
-
allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
|
|
2054
|
-
onConfirmedToolCall(block, contentIndex) {
|
|
2055
|
-
pushStreamEvent({
|
|
2056
|
-
type: "toolcall_end",
|
|
2057
|
-
contentIndex,
|
|
2058
|
-
toolCall: block,
|
|
2059
|
-
partial: output
|
|
2060
|
-
});
|
|
2061
|
-
}
|
|
2062
|
-
});
|
|
2063
|
-
if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
|
|
2064
|
-
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
2065
|
-
}
|
|
2066
|
-
function shouldFilterDeepSeekDsmlText(compat) {
|
|
2067
|
-
return compat.thinkingFormat === "deepseek";
|
|
2068
|
-
}
|
|
2069
|
-
const DEEPSEEK_DSML_BARS = ["|", "|"];
|
|
2070
|
-
const DEEPSEEK_DSML_TOOL_KINDS = [
|
|
2071
|
-
"tool_calls",
|
|
2072
|
-
"tool_call",
|
|
2073
|
-
"function_calls"
|
|
2074
|
-
];
|
|
2075
|
-
const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
|
|
2076
|
-
const DEEPSEEK_DSML_TOOL_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
|
|
2077
|
-
const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_BARS.map((bar) => `<${bar}DSML${bar}invoke`);
|
|
2078
|
-
const DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.map((bar) => `</${bar}DSML${bar}invoke>`);
|
|
2079
|
-
const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
|
|
2080
|
-
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));
|
|
2081
|
-
const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
|
|
2082
|
-
const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
|
|
2083
|
-
function createDeepSeekDsmlToolCallRecoverer() {
|
|
2084
|
-
let buffer = "";
|
|
2085
|
-
let bufferBytes = 0;
|
|
2086
|
-
let bufferEndsWithHighSurrogate = false;
|
|
2087
|
-
let pendingScanChars = 0;
|
|
2088
|
-
let activeOpenToken = null;
|
|
2089
|
-
let blockScanState = {
|
|
2090
|
-
offset: 0,
|
|
2091
|
-
mode: "outer",
|
|
2092
|
-
invokeOpenStart: -1
|
|
2093
|
-
};
|
|
2094
|
-
const resetBlockScan = () => {
|
|
2095
|
-
activeOpenToken = null;
|
|
2096
|
-
pendingScanChars = 0;
|
|
2097
|
-
blockScanState = {
|
|
2098
|
-
offset: 0,
|
|
2099
|
-
mode: "outer",
|
|
2100
|
-
invokeOpenStart: -1
|
|
2101
|
-
};
|
|
2030
|
+
const resetBlockScan = () => {
|
|
2031
|
+
activeOpenToken = null;
|
|
2032
|
+
pendingScanChars = 0;
|
|
2033
|
+
blockScanState = {
|
|
2034
|
+
offset: 0,
|
|
2035
|
+
mode: "outer",
|
|
2036
|
+
invokeOpenStart: -1
|
|
2037
|
+
};
|
|
2102
2038
|
};
|
|
2103
2039
|
const consume = (final) => {
|
|
2104
2040
|
const output = [];
|
|
@@ -2180,7 +2116,7 @@ function createDeepSeekDsmlToolCallRecoverer() {
|
|
|
2180
2116
|
};
|
|
2181
2117
|
return {
|
|
2182
2118
|
push(chunk) {
|
|
2183
|
-
const append =
|
|
2119
|
+
const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
|
|
2184
2120
|
bufferBytes += append.bytes;
|
|
2185
2121
|
bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
|
|
2186
2122
|
buffer += chunk;
|
|
@@ -2336,20 +2272,6 @@ function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
|
|
|
2336
2272
|
}
|
|
2337
2273
|
return { kind: "incomplete" };
|
|
2338
2274
|
}
|
|
2339
|
-
function utf8ByteLengthForAppend(bufferEndsWithHighSurrogate, chunk) {
|
|
2340
|
-
let bytes = Buffer.byteLength(chunk, "utf8");
|
|
2341
|
-
if (!chunk) return {
|
|
2342
|
-
bytes,
|
|
2343
|
-
endsWithHighSurrogate: bufferEndsWithHighSurrogate
|
|
2344
|
-
};
|
|
2345
|
-
const nextCodeUnit = chunk.charCodeAt(0);
|
|
2346
|
-
if (bufferEndsWithHighSurrogate && nextCodeUnit >= 56320 && nextCodeUnit <= 57343) bytes -= 2;
|
|
2347
|
-
const finalCodeUnit = chunk.charCodeAt(chunk.length - 1);
|
|
2348
|
-
return {
|
|
2349
|
-
bytes,
|
|
2350
|
-
endsWithHighSurrogate: finalCodeUnit >= 55296 && finalCodeUnit <= 56319
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
2275
|
function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
|
|
2354
2276
|
const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
|
|
2355
2277
|
for (let length = maxLength; length > 0; length -= 1) {
|
|
@@ -2358,17 +2280,61 @@ function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
|
|
|
2358
2280
|
}
|
|
2359
2281
|
return 0;
|
|
2360
2282
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
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);
|
|
2367
2333
|
return;
|
|
2368
2334
|
}
|
|
2369
2335
|
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
2370
2336
|
if (previous.signature !== next.signature) {
|
|
2371
|
-
|
|
2337
|
+
pendingPostToolCallDeltas.push(next);
|
|
2372
2338
|
return;
|
|
2373
2339
|
}
|
|
2374
2340
|
previous.text += next.text;
|
|
@@ -2376,646 +2342,701 @@ function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
|
|
|
2376
2342
|
}
|
|
2377
2343
|
previous.text += next.text;
|
|
2378
2344
|
};
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
text: detail.text
|
|
2392
|
-
});
|
|
2393
|
-
continue;
|
|
2394
|
-
}
|
|
2395
|
-
if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
|
|
2396
|
-
kind: "text",
|
|
2397
|
-
text: detail.text
|
|
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
|
|
2398
2357
|
});
|
|
2399
2358
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
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
|
|
2412
2381
|
});
|
|
2413
|
-
break;
|
|
2414
2382
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
return true;
|
|
2426
|
-
}
|
|
2427
|
-
function shouldEmitOpenAICompletionsReasoningForModel(model, options) {
|
|
2428
|
-
return shouldEmitOpenAICompletionsReasoning(model, options);
|
|
2429
|
-
}
|
|
2430
|
-
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
2431
|
-
if (options?.maxTokens) return {
|
|
2432
|
-
maxTokens: options.maxTokens,
|
|
2433
|
-
clampToModelMaxTokens: true
|
|
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
|
+
});
|
|
2434
2393
|
};
|
|
2435
|
-
const
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
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;
|
|
2439
2403
|
};
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2404
|
+
const appendThinkingDelta = (reasoningDelta) => {
|
|
2405
|
+
flushPendingPostToolCallDeltas();
|
|
2406
|
+
appendThinkingDeltaInternal(reasoningDelta);
|
|
2443
2407
|
};
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
}
|
|
2448
|
-
const
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
function estimateOpenAICompletionsMessagesChars(messages) {
|
|
2466
|
-
if (!Array.isArray(messages)) return 0;
|
|
2467
|
-
let adjustedChars = 0;
|
|
2468
|
-
for (const message of messages) {
|
|
2469
|
-
if (!message || typeof message !== "object") continue;
|
|
2470
|
-
const record = message;
|
|
2471
|
-
adjustedChars += estimateOpenAICompletionsContentChars(record.content);
|
|
2472
|
-
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
|
|
2473
|
-
if (record.tool_calls !== void 0) try {
|
|
2474
|
-
adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
|
|
2475
|
-
} catch {
|
|
2476
|
-
adjustedChars += 256;
|
|
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);
|
|
2477
2429
|
}
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
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();
|
|
2490
2544
|
continue;
|
|
2491
2545
|
}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
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();
|
|
2495
2558
|
continue;
|
|
2496
2559
|
}
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2560
|
+
const choiceUsage = choice.usage;
|
|
2561
|
+
if (!chunk.usage && choiceUsage) {
|
|
2562
|
+
output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
|
|
2563
|
+
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
|
|
2501
2564
|
}
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
|
|
2508
|
-
return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
|
|
2509
|
-
}
|
|
2510
|
-
function isQwenOpenAICompletionsThinkingFormat(format) {
|
|
2511
|
-
return format === "qwen" || format === "qwen-chat-template";
|
|
2512
|
-
}
|
|
2513
|
-
function setQwenChatTemplateThinking(params, enabled) {
|
|
2514
|
-
const existing = params.chat_template_kwargs;
|
|
2515
|
-
params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
|
|
2516
|
-
...existing,
|
|
2517
|
-
enable_thinking: enabled
|
|
2518
|
-
} : { enable_thinking: enabled };
|
|
2519
|
-
}
|
|
2520
|
-
function applyQwenOpenAICompletionsThinkingParams(params) {
|
|
2521
|
-
if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
|
|
2522
|
-
const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
|
|
2523
|
-
if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
|
|
2524
|
-
else params.payload.enable_thinking = enabled;
|
|
2525
|
-
return true;
|
|
2526
|
-
}
|
|
2527
|
-
function applyTogetherOpenAICompletionsThinkingParams(params) {
|
|
2528
|
-
if (!params.modelReasoning || params.compatThinkingFormat !== "together") return false;
|
|
2529
|
-
params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
|
|
2530
|
-
return true;
|
|
2531
|
-
}
|
|
2532
|
-
function convertTools(tools, compat, model) {
|
|
2533
|
-
const projection = projectOpenAITools(tools);
|
|
2534
|
-
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
|
|
2535
|
-
transport: "stream",
|
|
2536
|
-
supportsStrictMode: compat?.supportsStrictMode
|
|
2537
|
-
}), {
|
|
2538
|
-
transport: "completions",
|
|
2539
|
-
model
|
|
2540
|
-
});
|
|
2541
|
-
return {
|
|
2542
|
-
projection,
|
|
2543
|
-
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
2544
|
-
const functionTool = {
|
|
2545
|
-
name: tool.name,
|
|
2546
|
-
description: tool.description,
|
|
2547
|
-
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
2548
|
-
};
|
|
2549
|
-
if (strict !== void 0) functionTool.strict = strict;
|
|
2550
|
-
return {
|
|
2551
|
-
type: "function",
|
|
2552
|
-
function: functionTool
|
|
2553
|
-
};
|
|
2554
|
-
})
|
|
2555
|
-
};
|
|
2556
|
-
}
|
|
2557
|
-
function extractGoogleThoughtSignature(toolCall) {
|
|
2558
|
-
const tc = toolCall;
|
|
2559
|
-
if (!tc) return;
|
|
2560
|
-
const fromExtra = (tc.extra_content?.google)?.thought_signature;
|
|
2561
|
-
if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
|
|
2562
|
-
const fromFunction = tc.function?.thought_signature;
|
|
2563
|
-
return typeof fromFunction === "string" && fromFunction.length > 0 ? fromFunction : void 0;
|
|
2564
|
-
}
|
|
2565
|
-
function isGoogleOpenAICompatModel(model) {
|
|
2566
|
-
const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
|
|
2567
|
-
return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
|
|
2568
|
-
}
|
|
2569
|
-
function requiresGoogleCompatToolCallThoughtSignature(model) {
|
|
2570
|
-
return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
|
|
2571
|
-
}
|
|
2572
|
-
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
|
|
2573
|
-
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
|
|
2574
|
-
function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
|
|
2575
|
-
return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
|
|
2576
|
-
}
|
|
2577
|
-
function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
|
|
2578
|
-
if (!isGoogleOpenAICompatModel(model)) return;
|
|
2579
|
-
const sigById = /* @__PURE__ */ new Map();
|
|
2580
|
-
const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
|
|
2581
|
-
for (const msg of context.messages ?? []) {
|
|
2582
|
-
if (msg.role !== "assistant") continue;
|
|
2583
|
-
const source = msg;
|
|
2584
|
-
if (!Array.isArray(source.content)) continue;
|
|
2585
|
-
for (const block of source.content) {
|
|
2586
|
-
if (block.type !== "toolCall") continue;
|
|
2587
|
-
const id = block.id;
|
|
2588
|
-
const sig = block.thoughtSignature;
|
|
2589
|
-
if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
|
|
2590
|
-
const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
|
|
2591
|
-
if (!isSameRoute && !fallbackSig) continue;
|
|
2592
|
-
sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
|
|
2593
|
-
}
|
|
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;
|
|
2594
2570
|
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
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
|
+
}
|
|
2606
2648
|
}
|
|
2607
|
-
if (typeof sig !== "string" || sig.length === 0) continue;
|
|
2608
|
-
const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
|
|
2609
|
-
toolCall.extra_content = extra;
|
|
2610
|
-
const google = extra.google && typeof extra.google === "object" ? extra.google : {};
|
|
2611
|
-
extra.google = google;
|
|
2612
|
-
google.thought_signature = sig;
|
|
2613
2649
|
}
|
|
2650
|
+
flushPendingPostToolCallDeltas();
|
|
2651
|
+
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2652
|
+
await cooperativeScheduler.afterEvent();
|
|
2614
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);
|
|
2615
2675
|
}
|
|
2616
|
-
|
|
2617
|
-
"
|
|
2618
|
-
"reasoning_content",
|
|
2619
|
-
"reasoning",
|
|
2620
|
-
"reasoning_text"
|
|
2621
|
-
];
|
|
2622
|
-
function stripCompletionsReasoningReplayFields(record) {
|
|
2623
|
-
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
|
|
2624
|
-
}
|
|
2625
|
-
function sanitizeOpenRouterReasoningReplayFields(record) {
|
|
2626
|
-
const reasoningDetails = record.reasoning_details;
|
|
2627
|
-
if (typeof reasoningDetails === "string") {
|
|
2628
|
-
if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
|
|
2629
|
-
delete record.reasoning_details;
|
|
2630
|
-
} else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
|
|
2631
|
-
if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
|
|
2632
|
-
if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
|
|
2633
|
-
const reasoningText = record.reasoning_text;
|
|
2634
|
-
if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
|
|
2635
|
-
if ("reasoning_text" in record) delete record.reasoning_text;
|
|
2636
|
-
}
|
|
2637
|
-
function sanitizeReasoningContentReplayFields(record) {
|
|
2638
|
-
if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
|
|
2639
|
-
delete record.reasoning_details;
|
|
2640
|
-
delete record.reasoning;
|
|
2641
|
-
delete record.reasoning_text;
|
|
2642
|
-
}
|
|
2643
|
-
const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
2644
|
-
"deepseek-v4-flash",
|
|
2645
|
-
"deepseek-v4-pro",
|
|
2646
|
-
"kimi-for-coding",
|
|
2647
|
-
"kimi-k2.5",
|
|
2648
|
-
"kimi-k2.6",
|
|
2649
|
-
"kimi-k2.7-code",
|
|
2650
|
-
"kimi-k2.7-code-highspeed",
|
|
2651
|
-
"kimi-k3",
|
|
2652
|
-
"kimi-k2-thinking",
|
|
2653
|
-
"kimi-k2-thinking-turbo",
|
|
2654
|
-
"mimo-v2-pro",
|
|
2655
|
-
"mimo-v2-omni",
|
|
2656
|
-
"mimo-v2.5",
|
|
2657
|
-
"mimo-v2.5-pro",
|
|
2658
|
-
"mimo-v2.6-pro"
|
|
2659
|
-
]);
|
|
2660
|
-
const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
|
|
2661
|
-
"-free",
|
|
2662
|
-
"-paid",
|
|
2663
|
-
"-trial"
|
|
2664
|
-
];
|
|
2665
|
-
function stripReasoningContentReplayTierSuffix(modelId) {
|
|
2666
|
-
for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
|
|
2667
|
-
return modelId;
|
|
2668
|
-
}
|
|
2669
|
-
function getReasoningContentReplayModelIdCandidates(modelId) {
|
|
2670
|
-
if (typeof modelId !== "string") return [];
|
|
2671
|
-
const normalized = modelId.trim().toLowerCase();
|
|
2672
|
-
if (!normalized) return [];
|
|
2673
|
-
const parts = normalized.split("/").filter(Boolean);
|
|
2674
|
-
const finalPart = parts[parts.length - 1] ?? normalized;
|
|
2675
|
-
const candidates = [finalPart];
|
|
2676
|
-
const colonParts = finalPart.split(":").filter(Boolean);
|
|
2677
|
-
if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
|
|
2678
|
-
const baseCount = candidates.length;
|
|
2679
|
-
for (let index = 0; index < baseCount; index += 1) {
|
|
2680
|
-
const candidate = candidates[index];
|
|
2681
|
-
if (typeof candidate !== "string") continue;
|
|
2682
|
-
const stripped = stripReasoningContentReplayTierSuffix(candidate);
|
|
2683
|
-
if (stripped !== candidate) candidates.push(stripped);
|
|
2684
|
-
}
|
|
2685
|
-
return uniqueStrings(candidates.filter(Boolean));
|
|
2686
|
-
}
|
|
2687
|
-
function shouldPreserveReasoningContentReplay(model, compat) {
|
|
2688
|
-
if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
|
|
2689
|
-
return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
|
|
2690
|
-
}
|
|
2691
|
-
function shouldPreserveOpenRouterReasoningReplay(model) {
|
|
2692
|
-
if (model.provider !== "openrouter") return true;
|
|
2693
|
-
const normalizedModelId = model.id.trim().toLowerCase();
|
|
2694
|
-
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
2676
|
+
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
2677
|
+
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
2695
2678
|
}
|
|
2696
|
-
function
|
|
2679
|
+
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
2697
2680
|
if (!model.reasoning) return false;
|
|
2698
|
-
|
|
2699
|
-
|
|
2681
|
+
const effort = resolveOpenAICompletionsReasoningEffort(options);
|
|
2682
|
+
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
2683
|
+
return true;
|
|
2700
2684
|
}
|
|
2701
|
-
function
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
if (!msg || typeof msg !== "object") continue;
|
|
2705
|
-
const record = msg;
|
|
2706
|
-
if (record.role !== "assistant") continue;
|
|
2707
|
-
if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
|
|
2708
|
-
else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
|
|
2709
|
-
else stripCompletionsReasoningReplayFields(record);
|
|
2710
|
-
}
|
|
2685
|
+
function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
2686
|
+
const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
|
|
2687
|
+
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
2711
2688
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
stream: true
|
|
2689
|
+
//#endregion
|
|
2690
|
+
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
2691
|
+
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
2692
|
+
const messages = params.messages;
|
|
2693
|
+
if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
|
|
2694
|
+
throw new Error(`OpenAI-compatible chat payload for ${model.provider}/${model.id} contains no non-empty user or assistant messages after compaction and transport transforms; refusing to send a system/tool-only request. Start a new user turn or repair the compacted session history.`);
|
|
2695
|
+
}
|
|
2696
|
+
const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
|
|
2697
|
+
const SSE_DONE_MAX_LINE_CHARS = 1024;
|
|
2698
|
+
function createSseDoneDetector() {
|
|
2699
|
+
const decoder = new TextDecoder();
|
|
2700
|
+
let line = "";
|
|
2701
|
+
let lineOverflowed = false;
|
|
2702
|
+
let sawDone = false;
|
|
2703
|
+
const finishLine = () => {
|
|
2704
|
+
if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
|
|
2705
|
+
line = "";
|
|
2706
|
+
lineOverflowed = false;
|
|
2731
2707
|
};
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
2741
|
-
provider: model.provider,
|
|
2742
|
-
baseUrl: model.baseUrl,
|
|
2743
|
-
hasTools: () => Boolean(context.tools?.length)
|
|
2744
|
-
}) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
2745
|
-
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
2746
|
-
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
2747
|
-
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
2748
|
-
if (options?.seed !== void 0) params.seed = options.seed;
|
|
2749
|
-
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
2750
|
-
if (supportsModelTools(model)) {
|
|
2751
|
-
if (context.tools) {
|
|
2752
|
-
const converted = convertTools(context.tools, compat, model);
|
|
2753
|
-
if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
|
|
2754
|
-
else if (hasToolHistory(context.messages)) params.tools = [];
|
|
2755
|
-
if (options?.toolChoice) {
|
|
2756
|
-
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
|
|
2757
|
-
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
2758
|
-
} else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
|
|
2759
|
-
} else if (hasToolHistory(context.messages)) params.tools = [];
|
|
2760
|
-
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
|
|
2761
|
-
delete params.tools;
|
|
2762
|
-
delete params.tool_choice;
|
|
2708
|
+
const observeText = (text) => {
|
|
2709
|
+
for (const char of text) {
|
|
2710
|
+
if (char === "\n" || char === "\r") {
|
|
2711
|
+
finishLine();
|
|
2712
|
+
continue;
|
|
2713
|
+
}
|
|
2714
|
+
if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
|
|
2715
|
+
else lineOverflowed = true;
|
|
2763
2716
|
}
|
|
2764
|
-
}
|
|
2765
|
-
{
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2717
|
+
};
|
|
2718
|
+
return {
|
|
2719
|
+
observe(chunk) {
|
|
2720
|
+
if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
|
|
2721
|
+
},
|
|
2722
|
+
finish() {
|
|
2723
|
+
if (sawDone) return;
|
|
2724
|
+
observeText(decoder.decode());
|
|
2725
|
+
if (line || lineOverflowed) finishLine();
|
|
2726
|
+
},
|
|
2727
|
+
sawDone: () => sawDone
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
|
|
2731
|
+
const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
|
|
2732
|
+
return new OpenAI({
|
|
2733
|
+
apiKey,
|
|
2734
|
+
baseURL: clientConfig.baseURL,
|
|
2735
|
+
dangerouslyAllowBrowser: true,
|
|
2736
|
+
defaultHeaders: clientConfig.defaultHeaders,
|
|
2737
|
+
defaultQuery: clientConfig.defaultQuery,
|
|
2738
|
+
fetch: opts?.fetch ?? buildGuardedModelFetch(model),
|
|
2739
|
+
...buildOpenAISdkClientOptions(model)
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
|
|
2743
|
+
const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
|
|
2744
|
+
const defaultQuery = {};
|
|
2745
|
+
let baseURL = model.baseUrl;
|
|
2746
|
+
let isAzureHost = false;
|
|
2747
|
+
try {
|
|
2748
|
+
const parsed = new URL(model.baseUrl);
|
|
2749
|
+
isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
|
|
2750
|
+
parsed.searchParams.forEach((value, key) => {
|
|
2751
|
+
if (value) defaultQuery[key] = value;
|
|
2752
|
+
});
|
|
2753
|
+
parsed.search = "";
|
|
2754
|
+
baseURL = parsed.toString().replace(/\/$/, "");
|
|
2755
|
+
} catch {}
|
|
2756
|
+
if (isAzureHost) {
|
|
2757
|
+
const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
|
|
2758
|
+
if (apiVersionHeader) {
|
|
2759
|
+
const apiVersion = headers[apiVersionHeader]?.trim();
|
|
2760
|
+
delete headers[apiVersionHeader];
|
|
2761
|
+
if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
|
|
2774
2762
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2763
|
+
}
|
|
2764
|
+
return {
|
|
2765
|
+
baseURL,
|
|
2766
|
+
defaultHeaders: headers,
|
|
2767
|
+
defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
function createOpenAICompletionsTransportStreamFn() {
|
|
2771
|
+
return (model, context, options) => {
|
|
2772
|
+
const { eventStream, stream } = createWritableTransportEventStream();
|
|
2773
|
+
(async () => {
|
|
2774
|
+
const output = {
|
|
2775
|
+
role: "assistant",
|
|
2776
|
+
content: [],
|
|
2777
|
+
api: model.api,
|
|
2778
|
+
provider: model.provider,
|
|
2779
|
+
model: model.id,
|
|
2780
|
+
usage: {
|
|
2781
|
+
input: 0,
|
|
2782
|
+
output: 0,
|
|
2783
|
+
cacheRead: 0,
|
|
2784
|
+
cacheWrite: 0,
|
|
2785
|
+
totalTokens: 0,
|
|
2786
|
+
cost: {
|
|
2787
|
+
input: 0,
|
|
2788
|
+
output: 0,
|
|
2789
|
+
cacheRead: 0,
|
|
2790
|
+
cacheWrite: 0,
|
|
2791
|
+
total: 0
|
|
2792
|
+
}
|
|
2793
|
+
},
|
|
2794
|
+
stopReason: "stop",
|
|
2795
|
+
timestamp: Date.now()
|
|
2796
|
+
};
|
|
2797
|
+
let firstEventAbort;
|
|
2798
|
+
try {
|
|
2799
|
+
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
2800
|
+
const doneDetector = createSseDoneDetector();
|
|
2801
|
+
const baseFetch = buildGuardedModelFetch(model);
|
|
2802
|
+
const doneDetectingFetch = async (url, init) => {
|
|
2803
|
+
const response = await baseFetch(url, init);
|
|
2804
|
+
if (!response.body || !response.ok) return response;
|
|
2805
|
+
if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
|
|
2806
|
+
const transformed = response.body.pipeThrough(new TransformStream({
|
|
2807
|
+
transform(chunk, controller) {
|
|
2808
|
+
doneDetector.observe(chunk);
|
|
2809
|
+
controller.enqueue(chunk);
|
|
2810
|
+
},
|
|
2811
|
+
flush() {
|
|
2812
|
+
doneDetector.finish();
|
|
2813
|
+
}
|
|
2814
|
+
}));
|
|
2815
|
+
return new Response(transformed, {
|
|
2816
|
+
headers: response.headers,
|
|
2817
|
+
status: response.status,
|
|
2818
|
+
statusText: response.statusText
|
|
2819
|
+
});
|
|
2820
|
+
};
|
|
2821
|
+
const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
|
|
2822
|
+
let params = buildOpenAICompletionsParams(model, context, options);
|
|
2823
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
2824
|
+
if (nextParams !== void 0) params = nextParams;
|
|
2825
|
+
if (options?.openclawCodeModeToolSurface === true) {
|
|
2826
|
+
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
2827
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames, void 0, codeModeToolSurfaceObserver.get(options));
|
|
2828
|
+
assertCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
2829
|
+
}
|
|
2830
|
+
if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
|
|
2831
|
+
const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
|
|
2832
|
+
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
2833
|
+
const { data: responseStream, response } = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
|
|
2834
|
+
timeoutMs: options?.timeoutMs,
|
|
2835
|
+
maxRetries: options?.maxRetries
|
|
2836
|
+
})).withResponse();
|
|
2837
|
+
await processCompletionsStream(withProviderResponseHook({
|
|
2838
|
+
stream: responseStream,
|
|
2839
|
+
signal: firstEventAbort.signal,
|
|
2840
|
+
abort: firstEventAbort.abort,
|
|
2841
|
+
hook: createOpenAIProviderAcceptanceHook(options, response, model),
|
|
2842
|
+
onReady: () => stream.push({
|
|
2843
|
+
type: "start",
|
|
2844
|
+
partial: output
|
|
2845
|
+
})
|
|
2846
|
+
}), output, model, stream, {
|
|
2847
|
+
signal: options?.signal,
|
|
2848
|
+
emitReasoning,
|
|
2849
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
2850
|
+
abortFirstEventStream: firstEventAbort.abort,
|
|
2851
|
+
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
2852
|
+
sawStreamDONE: doneDetector.sawDone
|
|
2853
|
+
});
|
|
2854
|
+
finalizeTransportStream({
|
|
2855
|
+
stream,
|
|
2856
|
+
output,
|
|
2857
|
+
signal: options?.signal
|
|
2858
|
+
});
|
|
2859
|
+
} catch (error) {
|
|
2860
|
+
failTransportStream({
|
|
2861
|
+
stream,
|
|
2862
|
+
output,
|
|
2863
|
+
signal: options?.signal,
|
|
2864
|
+
error,
|
|
2865
|
+
cleanup: () => {
|
|
2866
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
2867
|
+
finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
|
|
2868
|
+
tagUnresolvedTextAsCommentary(output);
|
|
2869
|
+
}
|
|
2870
|
+
});
|
|
2871
|
+
} finally {
|
|
2872
|
+
firstEventAbort?.dispose();
|
|
2781
2873
|
}
|
|
2782
|
-
}
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
}
|
|
2786
|
-
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
|
|
2787
|
-
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
2788
|
-
model,
|
|
2789
|
-
effort: completionsReasoningEffort,
|
|
2790
|
-
fallbackMap: compat.reasoningEffortMap
|
|
2791
|
-
}) : void 0;
|
|
2792
|
-
const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
|
|
2793
|
-
const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
|
|
2794
|
-
const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
|
|
2795
|
-
compatThinkingFormat: compat.thinkingFormat,
|
|
2796
|
-
modelReasoning: model.reasoning,
|
|
2797
|
-
payload: params,
|
|
2798
|
-
requestedEffort: completionsReasoningEffort
|
|
2799
|
-
});
|
|
2800
|
-
applyTogetherOpenAICompletionsThinkingParams({
|
|
2801
|
-
compatThinkingFormat: compat.thinkingFormat,
|
|
2802
|
-
modelReasoning: model.reasoning,
|
|
2803
|
-
payload: params,
|
|
2804
|
-
requestedEffort: completionsReasoningEffort
|
|
2805
|
-
});
|
|
2806
|
-
if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
|
|
2807
|
-
else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
|
|
2808
|
-
else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
|
|
2809
|
-
return params;
|
|
2810
|
-
}
|
|
2811
|
-
function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
2812
|
-
const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
|
|
2813
|
-
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
2874
|
+
})();
|
|
2875
|
+
return eventStream;
|
|
2876
|
+
};
|
|
2814
2877
|
}
|
|
2815
|
-
const completionsTesting = {
|
|
2816
|
-
getCompat,
|
|
2817
|
-
createSseDoneDetector,
|
|
2818
|
-
createOpenAICompletionsClient,
|
|
2819
|
-
buildOpenAICompletionsClientConfig,
|
|
2820
|
-
parseTransportChunkUsage: parseOpenAICompletionsUsage,
|
|
2821
|
-
processOpenAICompletionsStream,
|
|
2822
|
-
shouldEmitOpenAICompletionsReasoningForModel
|
|
2823
|
-
};
|
|
2824
|
-
if (process.env.VITEST || false) globalThis.openclawOpenAICompletionsTransportTestApi = completionsTesting;
|
|
2825
2878
|
//#endregion
|
|
2826
|
-
//#region packages/ai/src/transports/openai-responses-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
"openai-responses",
|
|
2834
|
-
"azure-openai-responses",
|
|
2835
|
-
"openai-chatgpt-responses",
|
|
2836
|
-
"openclaw-openai-responses-transport",
|
|
2837
|
-
"openclaw-openai-chatgpt-responses-transport"
|
|
2838
|
-
]);
|
|
2839
|
-
const OPENAI_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set([
|
|
2840
|
-
"openai",
|
|
2841
|
-
"azure-openai",
|
|
2842
|
-
"azure-openai-responses"
|
|
2843
|
-
]);
|
|
2844
|
-
const LOCAL_ENDPOINT_HOSTS = /* @__PURE__ */ new Set([
|
|
2845
|
-
"localhost",
|
|
2846
|
-
"127.0.0.1",
|
|
2847
|
-
"::1",
|
|
2848
|
-
"[::1]"
|
|
2849
|
-
]);
|
|
2850
|
-
const MODELSTUDIO_NATIVE_BASE_URLS = /* @__PURE__ */ new Set([
|
|
2851
|
-
"https://coding-intl.dashscope.aliyuncs.com/v1",
|
|
2852
|
-
"https://coding.dashscope.aliyuncs.com/v1",
|
|
2853
|
-
"https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
2854
|
-
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
2855
|
-
]);
|
|
2856
|
-
const MOONSHOT_NATIVE_BASE_URLS = /* @__PURE__ */ new Set(["https://api.moonshot.ai/v1", "https://api.moonshot.cn/v1"]);
|
|
2857
|
-
function normalizeLowercaseString(value) {
|
|
2858
|
-
const stringValue = readStringValue(value)?.trim().toLowerCase();
|
|
2859
|
-
return stringValue ? stringValue : void 0;
|
|
2860
|
-
}
|
|
2861
|
-
function normalizeComparableBaseUrl(value) {
|
|
2862
|
-
const trimmed = readStringValue(value)?.trim();
|
|
2863
|
-
if (!trimmed) return;
|
|
2864
|
-
const parsedValue = /^[a-z0-9.[\]-]+(?::\d+)?(?:[/?#].*)?$/i.test(trimmed) ? `https://${trimmed}` : trimmed;
|
|
2865
|
-
try {
|
|
2866
|
-
const url = new URL(parsedValue);
|
|
2867
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return;
|
|
2868
|
-
url.hash = "";
|
|
2869
|
-
url.search = "";
|
|
2870
|
-
return url.toString().replace(/\/+$/, "").toLowerCase();
|
|
2871
|
-
} catch {
|
|
2872
|
-
return;
|
|
2879
|
+
//#region packages/ai/src/transports/openai-responses-compact-request.ts
|
|
2880
|
+
const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
|
|
2881
|
+
function claimResponsesCompactRequest(options) {
|
|
2882
|
+
const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
|
|
2883
|
+
if (controller?.claimed === false) {
|
|
2884
|
+
controller.claimed = true;
|
|
2885
|
+
return controller;
|
|
2873
2886
|
}
|
|
2874
2887
|
}
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2888
|
+
/** Run a compact-endpoint request through the session's prepared stream stack. */
|
|
2889
|
+
async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
|
|
2890
|
+
const preparedOptions = { ...options };
|
|
2891
|
+
let resolveResult;
|
|
2892
|
+
let rejectResult;
|
|
2893
|
+
const result = new Promise((resolve, reject) => {
|
|
2894
|
+
resolveResult = resolve;
|
|
2895
|
+
rejectResult = reject;
|
|
2896
|
+
});
|
|
2897
|
+
const controller = {
|
|
2898
|
+
claimed: false,
|
|
2899
|
+
resolve: resolveResult,
|
|
2900
|
+
reject: rejectResult
|
|
2901
|
+
};
|
|
2902
|
+
Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
|
|
2903
|
+
const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
|
|
2904
|
+
if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
|
|
2878
2905
|
try {
|
|
2879
|
-
return
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
return new URL(`https://${trimmed}`).hostname.toLowerCase();
|
|
2883
|
-
} catch {
|
|
2884
|
-
return;
|
|
2885
|
-
}
|
|
2906
|
+
return await result;
|
|
2907
|
+
} finally {
|
|
2908
|
+
await stream.result().catch(() => void 0);
|
|
2886
2909
|
}
|
|
2887
2910
|
}
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
case "llm.chutes.ai": return "chutes-native";
|
|
2904
|
-
case "api.deepseek.com": return "deepseek-native";
|
|
2905
|
-
case "api.groq.com": return "groq-native";
|
|
2906
|
-
case "api.mistral.ai": return "mistral-public";
|
|
2907
|
-
case "api.openai.com": return "openai-public";
|
|
2908
|
-
case "chatgpt.com": return "openai";
|
|
2909
|
-
case "generativelanguage.googleapis.com": return "google-generative-ai";
|
|
2910
|
-
case "aiplatform.googleapis.com": return "google-vertex";
|
|
2911
|
-
case "api.x.ai": return "xai-native";
|
|
2912
|
-
case "api.z.ai": return "zai-native";
|
|
2913
|
-
}
|
|
2914
|
-
if (hostMatchesSuffix(host, ".githubcopilot.com")) return "github-copilot-native";
|
|
2915
|
-
if (hostMatchesSuffix(host, ".openai.azure.com")) return "azure-openai";
|
|
2916
|
-
if (hostMatchesSuffix(host, "openrouter.ai")) return "openrouter";
|
|
2917
|
-
if (hostMatchesSuffix(host, "opencode.ai")) return "opencode-native";
|
|
2918
|
-
if (hostMatchesSuffix(host, "-aiplatform.googleapis.com")) return "google-vertex";
|
|
2919
|
-
if (comparableBaseUrl && MOONSHOT_NATIVE_BASE_URLS.has(comparableBaseUrl)) return "moonshot-native";
|
|
2920
|
-
if (comparableBaseUrl && MODELSTUDIO_NATIVE_BASE_URLS.has(comparableBaseUrl)) return "modelstudio-native";
|
|
2921
|
-
if (isLocalEndpointHost(host)) return "local";
|
|
2922
|
-
return "custom";
|
|
2923
|
-
}
|
|
2924
|
-
function isOpenAIResponsesApi(api) {
|
|
2925
|
-
return api !== void 0 && OPENAI_RESPONSES_APIS.has(api);
|
|
2926
|
-
}
|
|
2927
|
-
function readCompatPayloadBoolean(compat, key) {
|
|
2928
|
-
if (!compat || typeof compat !== "object") return;
|
|
2929
|
-
const value = compat[key];
|
|
2930
|
-
return typeof value === "boolean" ? value : void 0;
|
|
2931
|
-
}
|
|
2932
|
-
function resolveOpenAIResponsesPayloadCapabilities(model) {
|
|
2933
|
-
const provider = normalizeLowercaseString(model.provider);
|
|
2934
|
-
const api = normalizeLowercaseString(model.api);
|
|
2935
|
-
const isOpenAIProvider = provider === "openai";
|
|
2936
|
-
const endpointClass = resolveBundledOpenAIResponsesEndpointClass(model.baseUrl);
|
|
2937
|
-
const isResponsesApi = isOpenAIResponsesApi(api);
|
|
2938
|
-
const usesConfiguredBaseUrl = endpointClass !== "default";
|
|
2939
|
-
const usesKnownNativeOpenAIEndpoint = endpointClass === "openai-public" || endpointClass === "openai" || endpointClass === "azure-openai";
|
|
2940
|
-
const usesKnownNativeOpenAIRoute = endpointClass === "default" ? provider === "openai" : usesKnownNativeOpenAIEndpoint;
|
|
2941
|
-
const usesExplicitProxyLikeEndpoint = usesConfiguredBaseUrl && !usesKnownNativeOpenAIEndpoint;
|
|
2942
|
-
const promptCacheKeySupport = readCompatPayloadBoolean(model.compat, "supportsPromptCacheKey");
|
|
2943
|
-
const shouldStripResponsesPromptCache = promptCacheKeySupport === true ? false : promptCacheKeySupport === false ? isResponsesApi : isResponsesApi && usesExplicitProxyLikeEndpoint;
|
|
2944
|
-
const supportsResponsesStoreField = readCompatPayloadBoolean(model.compat, "supportsStore") !== false && isResponsesApi;
|
|
2911
|
+
//#endregion
|
|
2912
|
+
//#region packages/ai/src/transports/openai-responses-continuation.ts
|
|
2913
|
+
const HTTP_CONTINUATION_IDLE_TTL_MS = 300 * 1e3;
|
|
2914
|
+
const TURN_HEADERS = /* @__PURE__ */ new Set([
|
|
2915
|
+
"traceparent",
|
|
2916
|
+
"x-openclaw-turn-id",
|
|
2917
|
+
"x-openclaw-turn-attempt"
|
|
2918
|
+
]);
|
|
2919
|
+
function jsonValuesEqual(left, right) {
|
|
2920
|
+
return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
|
|
2921
|
+
}
|
|
2922
|
+
function requestWithoutInput(request) {
|
|
2923
|
+
const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
|
|
2924
|
+
if (!isRecord(rest.metadata)) return rest;
|
|
2925
|
+
const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
|
|
2945
2926
|
return {
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
if (
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
}
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
if (
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
const isResponsesApi = isOpenAIResponsesApi(normalizeLowercaseString(model.api));
|
|
2989
|
-
const shouldStripDisabledReasoningPayload = isResponsesApi && (!capabilities.usesKnownNativeOpenAIRoute || !supportsOpenAIReasoningEffort(model, "none"));
|
|
2990
|
-
const shouldStripInputStatus = isResponsesApi && !capabilities.usesKnownNativeOpenAIRoute;
|
|
2927
|
+
...rest,
|
|
2928
|
+
metadata
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
function normalizeAssistantReplayInput(input) {
|
|
2932
|
+
return input.map((item) => {
|
|
2933
|
+
if (!isRecord(item)) return item;
|
|
2934
|
+
if (item.type === "reasoning") return { type: "reasoning" };
|
|
2935
|
+
if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
|
|
2936
|
+
const { id: _id, status: _status, ...stableItem } = item;
|
|
2937
|
+
if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
|
|
2938
|
+
if (!isRecord(part) || part.type !== "output_text") return part;
|
|
2939
|
+
const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
|
|
2940
|
+
return stablePart;
|
|
2941
|
+
});
|
|
2942
|
+
return stableItem;
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
function resolveResponsesContinuationRequest(continuation, request) {
|
|
2946
|
+
if (!continuation) return {
|
|
2947
|
+
request,
|
|
2948
|
+
continuationStatus: "no_previous_response"
|
|
2949
|
+
};
|
|
2950
|
+
if (request.previous_response_id) return {
|
|
2951
|
+
request,
|
|
2952
|
+
continuationStatus: "explicit_previous_response_id"
|
|
2953
|
+
};
|
|
2954
|
+
if (!jsonValuesEqual(requestWithoutInput(request), requestWithoutInput(continuation.lastRequest))) return {
|
|
2955
|
+
request,
|
|
2956
|
+
continuationStatus: "request_changed"
|
|
2957
|
+
};
|
|
2958
|
+
const currentInput = request.input ?? [];
|
|
2959
|
+
const previousInput = continuation.lastRequest.input ?? [];
|
|
2960
|
+
const baselineLength = previousInput.length + continuation.lastResponseItems.length;
|
|
2961
|
+
if (currentInput.length < baselineLength) return {
|
|
2962
|
+
request,
|
|
2963
|
+
continuationStatus: "history_shorter"
|
|
2964
|
+
};
|
|
2965
|
+
if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems))) return {
|
|
2966
|
+
request,
|
|
2967
|
+
continuationStatus: "history_changed"
|
|
2968
|
+
};
|
|
2991
2969
|
return {
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
if (
|
|
3015
|
-
|
|
2970
|
+
request: {
|
|
2971
|
+
...request,
|
|
2972
|
+
previous_response_id: continuation.lastResponseId,
|
|
2973
|
+
input: currentInput.slice(baselineLength)
|
|
2974
|
+
},
|
|
2975
|
+
continuationStatus: "continued"
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
const httpContinuationEntries = /* @__PURE__ */ new Map();
|
|
2979
|
+
let nextHttpContinuationGeneration = 1;
|
|
2980
|
+
function connectionIdentity(params) {
|
|
2981
|
+
const headers = Object.entries(resolveAiTransportHeaderSentinels(params.headers) ?? {}).map(([name, value]) => [name.toLowerCase(), value]).filter(([name]) => !TURN_HEADERS.has(name)).toSorted(([a], [b]) => a.localeCompare(b));
|
|
2982
|
+
return sha256Hex(JSON.stringify([
|
|
2983
|
+
getAiTransportHost().resolveSecretSentinel(params.apiKey),
|
|
2984
|
+
params.baseUrl,
|
|
2985
|
+
headers
|
|
2986
|
+
]));
|
|
2987
|
+
}
|
|
2988
|
+
function claimOpenAIResponsesHttpContinuation(params) {
|
|
2989
|
+
const key = `${params.sessionId}\0${connectionIdentity(params)}`;
|
|
2990
|
+
const previous = httpContinuationEntries.get(key);
|
|
2991
|
+
if (previous?.kind === "claimed") return;
|
|
2992
|
+
if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
|
|
2993
|
+
const generation = nextHttpContinuationGeneration++;
|
|
2994
|
+
const claimed = {
|
|
2995
|
+
kind: "claimed",
|
|
2996
|
+
sessionId: params.sessionId,
|
|
2997
|
+
generation
|
|
2998
|
+
};
|
|
2999
|
+
httpContinuationEntries.set(key, claimed);
|
|
3000
|
+
return {
|
|
3001
|
+
request: resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, params.request).request,
|
|
3002
|
+
commit: (effectiveRequest, response) => {
|
|
3003
|
+
if (httpContinuationEntries.get(key) !== claimed) return;
|
|
3004
|
+
const idleTimer = setTimeout(() => {
|
|
3005
|
+
const current = httpContinuationEntries.get(key);
|
|
3006
|
+
if (current?.kind === "ready" && current.generation === generation) httpContinuationEntries.delete(key);
|
|
3007
|
+
}, HTTP_CONTINUATION_IDLE_TTL_MS);
|
|
3008
|
+
idleTimer.unref?.();
|
|
3009
|
+
const ready = {
|
|
3010
|
+
...claimed,
|
|
3011
|
+
kind: "ready",
|
|
3012
|
+
state: {
|
|
3013
|
+
lastRequest: effectiveRequest,
|
|
3014
|
+
lastResponseId: response.id,
|
|
3015
|
+
lastResponseItems: response.output
|
|
3016
|
+
},
|
|
3017
|
+
idleTimer
|
|
3018
|
+
};
|
|
3019
|
+
httpContinuationEntries.set(key, ready);
|
|
3020
|
+
},
|
|
3021
|
+
release: () => {
|
|
3022
|
+
if (httpContinuationEntries.get(key) === claimed) httpContinuationEntries.delete(key);
|
|
3023
|
+
}
|
|
3024
|
+
};
|
|
3016
3025
|
}
|
|
3026
|
+
registerSessionResourceCleanup((sessionId) => {
|
|
3027
|
+
for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
|
|
3028
|
+
if (entry.kind === "ready") clearTimeout(entry.idleTimer);
|
|
3029
|
+
httpContinuationEntries.delete(key);
|
|
3030
|
+
}
|
|
3031
|
+
});
|
|
3017
3032
|
//#endregion
|
|
3018
3033
|
//#region packages/ai/src/transports/openai-responses-params-internal.ts
|
|
3034
|
+
const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
|
|
3035
|
+
"openai",
|
|
3036
|
+
"opencode",
|
|
3037
|
+
"azure-openai-responses",
|
|
3038
|
+
"github-copilot"
|
|
3039
|
+
]);
|
|
3019
3040
|
function convertResponsesTools(tools, model, options) {
|
|
3020
3041
|
const projection = projectOpenAITools(tools);
|
|
3021
3042
|
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
|
|
@@ -3114,7 +3135,7 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
|
3114
3135
|
};
|
|
3115
3136
|
return responseFormat;
|
|
3116
3137
|
}
|
|
3117
|
-
function
|
|
3138
|
+
function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
|
|
3118
3139
|
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3119
3140
|
const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
|
|
3120
3141
|
const compat = getCompat(model);
|
|
@@ -3122,19 +3143,20 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
|
|
|
3122
3143
|
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3123
3144
|
const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
|
|
3124
3145
|
const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
|
|
3125
|
-
|
|
3126
|
-
"openai",
|
|
3127
|
-
"opencode",
|
|
3128
|
-
"azure-openai-responses",
|
|
3129
|
-
"github-copilot"
|
|
3130
|
-
]), {
|
|
3146
|
+
return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
|
|
3131
3147
|
includeSystemPrompt: !isCodexResponses,
|
|
3132
3148
|
supportsDeveloperRole,
|
|
3133
3149
|
replayReasoningItems: true,
|
|
3134
3150
|
replayResponsesItemIds,
|
|
3135
3151
|
authProfileId: options?.authProfileId,
|
|
3136
|
-
sessionId: options?.sessionId
|
|
3152
|
+
sessionId: options?.sessionId,
|
|
3153
|
+
replayMode
|
|
3137
3154
|
});
|
|
3155
|
+
}
|
|
3156
|
+
function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
|
|
3157
|
+
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3158
|
+
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3159
|
+
const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
|
|
3138
3160
|
if (isCodexResponses) ensureOpenAICodexResponsesInput(messages, context);
|
|
3139
3161
|
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
3140
3162
|
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
@@ -3194,6 +3216,261 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
|
|
|
3194
3216
|
return sanitizeOpenAICodexResponsesParams(model, params);
|
|
3195
3217
|
}
|
|
3196
3218
|
//#endregion
|
|
3219
|
+
//#region packages/ai/src/transports/openai-responses-websocket.ts
|
|
3220
|
+
const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
|
|
3221
|
+
const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
|
|
3222
|
+
const WEBSOCKET_OPEN_STATE = 1;
|
|
3223
|
+
const websocketSessionCache = /* @__PURE__ */ new Map();
|
|
3224
|
+
const degradedWebSocketConnections = /* @__PURE__ */ new Map();
|
|
3225
|
+
function isOfficialOpenAIResponsesBaseUrl(baseUrl) {
|
|
3226
|
+
if (!baseUrl) return false;
|
|
3227
|
+
try {
|
|
3228
|
+
const url = new URL(baseUrl);
|
|
3229
|
+
return url.origin === "https://api.openai.com" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname.replace(/\/+$/, "") === "/v1";
|
|
3230
|
+
} catch {
|
|
3231
|
+
return false;
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
function supportsNativeOpenAIResponsesEndpoint(params) {
|
|
3235
|
+
return params.provider.trim().toLowerCase() === "openai" && params.api === "openai-responses" && isOfficialOpenAIResponsesBaseUrl(params.baseUrl);
|
|
3236
|
+
}
|
|
3237
|
+
function closeWebSocketSilently(socket, reason = "done") {
|
|
3238
|
+
try {
|
|
3239
|
+
socket.close({
|
|
3240
|
+
code: 1e3,
|
|
3241
|
+
reason
|
|
3242
|
+
});
|
|
3243
|
+
} catch {}
|
|
3244
|
+
}
|
|
3245
|
+
function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
|
|
3246
|
+
if (entry.idleTimer) {
|
|
3247
|
+
clearTimeout(entry.idleTimer);
|
|
3248
|
+
entry.idleTimer = void 0;
|
|
3249
|
+
}
|
|
3250
|
+
closeWebSocketSilently(entry.socket, reason);
|
|
3251
|
+
if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
|
|
3252
|
+
}
|
|
3253
|
+
function scheduleSessionWebSocketExpiry(cacheKey, entry) {
|
|
3254
|
+
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
3255
|
+
entry.idleTimer = setTimeout(() => {
|
|
3256
|
+
if (entry.busy) return;
|
|
3257
|
+
invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
|
|
3258
|
+
}, SESSION_WEBSOCKET_CACHE_TTL_MS);
|
|
3259
|
+
entry.idleTimer.unref?.();
|
|
3260
|
+
}
|
|
3261
|
+
function prepareWebSocketConnection(client, headers) {
|
|
3262
|
+
if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
|
|
3263
|
+
if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
|
|
3264
|
+
const resolvedApiKey = getAiTransportHost().resolveSecretSentinel(client.apiKey);
|
|
3265
|
+
const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
|
|
3266
|
+
for (const key of Object.keys(resolvedHeaders)) {
|
|
3267
|
+
const normalizedKey = key.toLowerCase();
|
|
3268
|
+
if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
|
|
3269
|
+
}
|
|
3270
|
+
if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
|
|
3271
|
+
return {
|
|
3272
|
+
client: client.withOptions({ apiKey: resolvedApiKey }),
|
|
3273
|
+
headers: resolvedHeaders,
|
|
3274
|
+
identity: sha256Hex(JSON.stringify([
|
|
3275
|
+
resolvedApiKey,
|
|
3276
|
+
client.baseURL,
|
|
3277
|
+
Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
|
|
3278
|
+
]))
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
function createWebSocket(connection, onError) {
|
|
3282
|
+
const socket = new ResponsesWS(connection.client, {
|
|
3283
|
+
headers: connection.headers,
|
|
3284
|
+
maxQueueSize: 1
|
|
3285
|
+
});
|
|
3286
|
+
socket.on("error", () => onError(socket));
|
|
3287
|
+
return socket;
|
|
3288
|
+
}
|
|
3289
|
+
function createTransientWebSocketLease(connection) {
|
|
3290
|
+
const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
|
|
3291
|
+
return {
|
|
3292
|
+
socket,
|
|
3293
|
+
iterator: socket.stream(),
|
|
3294
|
+
reusedConnection: false,
|
|
3295
|
+
release: () => closeWebSocketSilently(socket)
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
|
|
3299
|
+
entry.busy = true;
|
|
3300
|
+
return {
|
|
3301
|
+
socket: entry.socket,
|
|
3302
|
+
iterator: entry.socket.stream(),
|
|
3303
|
+
entry,
|
|
3304
|
+
reusedConnection,
|
|
3305
|
+
release: ({ keep } = {}) => {
|
|
3306
|
+
if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
|
|
3307
|
+
invalidateOwnedWebSocketSession(cacheKey, entry);
|
|
3308
|
+
return;
|
|
3309
|
+
}
|
|
3310
|
+
entry.busy = false;
|
|
3311
|
+
scheduleSessionWebSocketExpiry(cacheKey, entry);
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
function acquireWebSocket(params, connection) {
|
|
3316
|
+
if (!(params.mode !== "websocket" && Boolean(params.sessionId)) || !params.sessionId) return createTransientWebSocketLease(connection);
|
|
3317
|
+
const cacheKey = `${params.sessionId}\0${connection.identity}`;
|
|
3318
|
+
const cached = websocketSessionCache.get(cacheKey);
|
|
3319
|
+
if (cached) {
|
|
3320
|
+
if (cached.idleTimer) {
|
|
3321
|
+
clearTimeout(cached.idleTimer);
|
|
3322
|
+
cached.idleTimer = void 0;
|
|
3323
|
+
}
|
|
3324
|
+
if (cached.busy) return createTransientWebSocketLease(connection);
|
|
3325
|
+
const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
|
|
3326
|
+
if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
|
|
3327
|
+
invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
|
|
3328
|
+
}
|
|
3329
|
+
const entry = {
|
|
3330
|
+
socket: createWebSocket(connection, (failedSocket) => {
|
|
3331
|
+
const failedEntry = websocketSessionCache.get(cacheKey);
|
|
3332
|
+
if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
|
|
3333
|
+
else closeWebSocketSilently(failedSocket, "transport_error");
|
|
3334
|
+
}),
|
|
3335
|
+
sessionId: params.sessionId,
|
|
3336
|
+
busy: true,
|
|
3337
|
+
createdAt: Date.now()
|
|
3338
|
+
};
|
|
3339
|
+
websocketSessionCache.set(cacheKey, entry);
|
|
3340
|
+
return createCachedWebSocketLease(cacheKey, entry, false);
|
|
3341
|
+
}
|
|
3342
|
+
function sanitizeWebSocketRequest(request) {
|
|
3343
|
+
const { stream: _stream, background: _background, ...websocketRequest } = request;
|
|
3344
|
+
return websocketRequest;
|
|
3345
|
+
}
|
|
3346
|
+
async function nextWebSocketMessage(iterator, signal) {
|
|
3347
|
+
if (!signal) return iterator.next();
|
|
3348
|
+
if (signal.aborted) throw transportAbortError(signal);
|
|
3349
|
+
let onAbort;
|
|
3350
|
+
try {
|
|
3351
|
+
return await Promise.race([iterator.next(), new Promise((_resolve, reject) => {
|
|
3352
|
+
onAbort = () => reject(transportAbortError(signal));
|
|
3353
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3354
|
+
})]);
|
|
3355
|
+
} finally {
|
|
3356
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
function readServerEvent(message) {
|
|
3360
|
+
if (message.type === "message") return message.message;
|
|
3361
|
+
if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
|
|
3362
|
+
if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
|
|
3363
|
+
}
|
|
3364
|
+
function createOpenAIResponsesWebSocketStream(params) {
|
|
3365
|
+
const connection = prepareWebSocketConnection(params.client, params.headers);
|
|
3366
|
+
const fullRequest = sanitizeWebSocketRequest(params.request);
|
|
3367
|
+
const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
|
|
3368
|
+
const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
|
|
3369
|
+
const degraded = degradedWebSocketConnections.get(degradationKey);
|
|
3370
|
+
if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
|
|
3371
|
+
degradedWebSocketConnections.delete(degradationKey);
|
|
3372
|
+
const markDegraded = () => {
|
|
3373
|
+
const cooldownMs = params.degradeCooldownMs;
|
|
3374
|
+
if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
|
|
3375
|
+
degradedWebSocketConnections.set(degradationKey, {
|
|
3376
|
+
sessionId: params.sessionId,
|
|
3377
|
+
retryAt: Date.now() + cooldownMs
|
|
3378
|
+
});
|
|
3379
|
+
};
|
|
3380
|
+
let lease;
|
|
3381
|
+
try {
|
|
3382
|
+
lease = acquireWebSocket(params, connection);
|
|
3383
|
+
} catch (error) {
|
|
3384
|
+
markDegraded();
|
|
3385
|
+
throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
3386
|
+
}
|
|
3387
|
+
let prepared;
|
|
3388
|
+
try {
|
|
3389
|
+
const continuation = lease.entry?.continuation;
|
|
3390
|
+
if (continuation && lease.entry) {
|
|
3391
|
+
lease.entry.continuation = void 0;
|
|
3392
|
+
prepared = resolveResponsesContinuationRequest(continuation, fullRequest);
|
|
3393
|
+
} else prepared = {
|
|
3394
|
+
request: fullRequest,
|
|
3395
|
+
continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
|
|
3396
|
+
};
|
|
3397
|
+
} catch (error) {
|
|
3398
|
+
lease.iterator.return?.().catch(() => void 0);
|
|
3399
|
+
lease.release({ keep: false });
|
|
3400
|
+
throw error;
|
|
3401
|
+
}
|
|
3402
|
+
let streamStarted = false;
|
|
3403
|
+
let terminalResponse;
|
|
3404
|
+
let terminalReceived = false;
|
|
3405
|
+
let released = false;
|
|
3406
|
+
const finish = ({ keep = true } = {}) => {
|
|
3407
|
+
if (released) return;
|
|
3408
|
+
released = true;
|
|
3409
|
+
if (keep && lease.entry && terminalResponse) lease.entry.continuation = {
|
|
3410
|
+
lastRequest: fullRequest,
|
|
3411
|
+
lastResponseId: terminalResponse.id,
|
|
3412
|
+
lastResponseItems: terminalResponse.output
|
|
3413
|
+
};
|
|
3414
|
+
lease.release({ keep });
|
|
3415
|
+
};
|
|
3416
|
+
return {
|
|
3417
|
+
stream: { async *[Symbol.asyncIterator]() {
|
|
3418
|
+
if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
|
|
3419
|
+
streamStarted = true;
|
|
3420
|
+
const iterator = lease.iterator;
|
|
3421
|
+
let requestDispatched = false;
|
|
3422
|
+
try {
|
|
3423
|
+
if (params.signal?.aborted) throw transportAbortError(params.signal);
|
|
3424
|
+
for (;;) {
|
|
3425
|
+
const next = await nextWebSocketMessage(iterator, params.signal);
|
|
3426
|
+
if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
|
|
3427
|
+
if (next.value.type === "open") {
|
|
3428
|
+
if (!requestDispatched) {
|
|
3429
|
+
requestDispatched = true;
|
|
3430
|
+
lease.socket.send({
|
|
3431
|
+
...prepared.request,
|
|
3432
|
+
type: "response.create"
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
continue;
|
|
3436
|
+
}
|
|
3437
|
+
const event = readServerEvent(next.value);
|
|
3438
|
+
if (!event) continue;
|
|
3439
|
+
if (event.type === "response.completed") terminalResponse = event.response;
|
|
3440
|
+
terminalReceived = event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed";
|
|
3441
|
+
yield event;
|
|
3442
|
+
if (terminalReceived) {
|
|
3443
|
+
degradedWebSocketConnections.delete(degradationKey);
|
|
3444
|
+
return;
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
} catch (error) {
|
|
3448
|
+
if (lease.entry) lease.entry.continuation = void 0;
|
|
3449
|
+
const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
|
|
3450
|
+
if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
|
|
3451
|
+
if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
3452
|
+
if (!requestDispatched || params.callerSignal?.aborted || safeRetry) throw error;
|
|
3453
|
+
throw new OpenAIResponsesWebSocketPostDispatchError(error);
|
|
3454
|
+
} finally {
|
|
3455
|
+
await iterator.return?.().catch(() => void 0);
|
|
3456
|
+
if (!terminalReceived) finish({ keep: false });
|
|
3457
|
+
}
|
|
3458
|
+
} },
|
|
3459
|
+
request: prepared.request,
|
|
3460
|
+
reusedConnection: lease.reusedConnection,
|
|
3461
|
+
continuationStatus: prepared.continuationStatus,
|
|
3462
|
+
finish
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
function closeOpenAIResponsesWebSocketSessions(sessionId) {
|
|
3466
|
+
for (const [cacheKey, entry] of websocketSessionCache) {
|
|
3467
|
+
if (sessionId && entry.sessionId !== sessionId) continue;
|
|
3468
|
+
invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
|
|
3469
|
+
}
|
|
3470
|
+
for (const [key, entry] of degradedWebSocketConnections) if (!sessionId || entry.sessionId === sessionId) degradedWebSocketConnections.delete(key);
|
|
3471
|
+
}
|
|
3472
|
+
registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
|
|
3473
|
+
//#endregion
|
|
3197
3474
|
//#region packages/media-core/src/inline-image-data-url.ts
|
|
3198
3475
|
/** Prefix used to distinguish inline data URLs from remote/local image references. */
|
|
3199
3476
|
const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
|
|
@@ -3338,6 +3615,20 @@ function sanitizeResponsesImagePayload(params) {
|
|
|
3338
3615
|
}
|
|
3339
3616
|
//#endregion
|
|
3340
3617
|
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
3618
|
+
function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
|
|
3619
|
+
if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
|
|
3620
|
+
if (getAiTransportHost().requiresManagedTransport(model)) return;
|
|
3621
|
+
return supportsNativeOpenAIResponsesEndpoint({
|
|
3622
|
+
provider: model.provider,
|
|
3623
|
+
api: model.api,
|
|
3624
|
+
baseUrl: model.baseUrl
|
|
3625
|
+
}) ? transport : void 0;
|
|
3626
|
+
}
|
|
3627
|
+
function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
|
|
3628
|
+
const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
|
|
3629
|
+
if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
|
|
3630
|
+
return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
|
|
3631
|
+
}
|
|
3341
3632
|
function resolveProviderTransportTurnState(model, params) {
|
|
3342
3633
|
const normalizedProvider = model.provider.trim().toLowerCase();
|
|
3343
3634
|
const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
|
|
@@ -3366,111 +3657,265 @@ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turn
|
|
|
3366
3657
|
...buildOpenAISdkClientOptions(model)
|
|
3367
3658
|
});
|
|
3368
3659
|
}
|
|
3660
|
+
async function postOpenAIResponsesCompaction(params) {
|
|
3661
|
+
const response = await params.client.post("/responses/compact", {
|
|
3662
|
+
...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
|
|
3663
|
+
timeoutMs: params.options?.timeoutMs,
|
|
3664
|
+
maxRetries: params.options?.maxRetries
|
|
3665
|
+
}),
|
|
3666
|
+
body: {
|
|
3667
|
+
model: params.request.model,
|
|
3668
|
+
input: params.request.input
|
|
3669
|
+
}
|
|
3670
|
+
});
|
|
3671
|
+
const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
|
|
3672
|
+
const item = output.at(-1);
|
|
3673
|
+
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
|
+
const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
|
|
3676
|
+
const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
|
|
3677
|
+
const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
|
|
3678
|
+
const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
|
|
3679
|
+
if (!isRecord(response) || response.object !== "response.compaction" || !retainedMessagesAreValid || 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
|
+
return {
|
|
3681
|
+
item,
|
|
3682
|
+
historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
|
|
3683
|
+
usage,
|
|
3684
|
+
model: params.model,
|
|
3685
|
+
replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
|
|
3686
|
+
authProfileId: params.options?.authProfileId,
|
|
3687
|
+
sessionId: params.options?.sessionId
|
|
3688
|
+
})
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
3369
3691
|
function createResponsesTransportExecutor(config) {
|
|
3370
3692
|
return (model, context, options) => {
|
|
3371
3693
|
const responsesOptions = options;
|
|
3372
|
-
const
|
|
3373
|
-
const stream =
|
|
3694
|
+
const compactRequest = claimResponsesCompactRequest(responsesOptions);
|
|
3695
|
+
const { eventStream, stream } = createWritableTransportEventStream();
|
|
3374
3696
|
(async () => {
|
|
3375
|
-
const output =
|
|
3376
|
-
role: "assistant",
|
|
3377
|
-
content: [],
|
|
3378
|
-
api: config.outputApi ?? model.api,
|
|
3379
|
-
provider: model.provider,
|
|
3380
|
-
model: model.id,
|
|
3381
|
-
usage: {
|
|
3382
|
-
input: 0,
|
|
3383
|
-
output: 0,
|
|
3384
|
-
cacheRead: 0,
|
|
3385
|
-
cacheWrite: 0,
|
|
3386
|
-
totalTokens: 0,
|
|
3387
|
-
cost: {
|
|
3388
|
-
input: 0,
|
|
3389
|
-
output: 0,
|
|
3390
|
-
cacheRead: 0,
|
|
3391
|
-
cacheWrite: 0,
|
|
3392
|
-
total: 0
|
|
3393
|
-
}
|
|
3394
|
-
},
|
|
3395
|
-
stopReason: "stop",
|
|
3396
|
-
timestamp: Date.now()
|
|
3397
|
-
};
|
|
3697
|
+
const output = createOpenAIResponsesAssistantOutput(model, config.outputApi);
|
|
3398
3698
|
let firstEventAbort;
|
|
3699
|
+
let continuationClaim;
|
|
3399
3700
|
try {
|
|
3400
3701
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
3702
|
+
const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
|
|
3401
3703
|
const turnState = resolveProviderTransportTurnState(model, {
|
|
3402
3704
|
sessionId: options?.sessionId,
|
|
3403
3705
|
turnId: randomUUID(),
|
|
3404
3706
|
attempt: 1,
|
|
3405
|
-
transport: "stream"
|
|
3707
|
+
transport: websocketMode ? "websocket" : "stream"
|
|
3406
3708
|
});
|
|
3709
|
+
const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
|
|
3710
|
+
const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
|
|
3407
3711
|
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3712
|
+
const buildRequest = async (replayMode) => {
|
|
3713
|
+
let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
|
|
3714
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
3715
|
+
if (nextParams !== void 0) params = nextParams;
|
|
3716
|
+
if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
|
|
3717
|
+
params = sanitizeOpenAICodexResponsesParams(model, params);
|
|
3718
|
+
params = sanitizeResponsesImagePayload(params);
|
|
3719
|
+
if (options?.openclawCodeModeToolSurface === true) {
|
|
3720
|
+
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
3721
|
+
const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
|
|
3722
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes, codeModeToolSurfaceObserver.get(options));
|
|
3723
|
+
assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
|
|
3724
|
+
}
|
|
3725
|
+
return params;
|
|
3726
|
+
};
|
|
3727
|
+
const params = await buildRequest("checkpoint");
|
|
3728
|
+
if (compactRequest) {
|
|
3729
|
+
const compacted = await postOpenAIResponsesCompaction({
|
|
3730
|
+
client,
|
|
3731
|
+
model,
|
|
3732
|
+
request: params,
|
|
3733
|
+
options: responsesOptions
|
|
3734
|
+
});
|
|
3735
|
+
output.usage.input = compacted.usage.input_tokens;
|
|
3736
|
+
output.usage.output = compacted.usage.output_tokens;
|
|
3737
|
+
output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
|
|
3738
|
+
compactRequest.resolve(compacted);
|
|
3739
|
+
finalizeTransportStream({
|
|
3740
|
+
stream,
|
|
3741
|
+
output,
|
|
3742
|
+
signal: options?.signal
|
|
3743
|
+
});
|
|
3744
|
+
return;
|
|
3418
3745
|
}
|
|
3746
|
+
const sessionId = options?.sessionId;
|
|
3747
|
+
if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && supportsNativeOpenAIResponsesEndpoint({
|
|
3748
|
+
provider: model.provider,
|
|
3749
|
+
api: model.api,
|
|
3750
|
+
baseUrl: model.baseUrl
|
|
3751
|
+
}) && sessionId && params.store === true && !params.previous_response_id) continuationClaim = claimOpenAIResponsesHttpContinuation({
|
|
3752
|
+
sessionId,
|
|
3753
|
+
apiKey,
|
|
3754
|
+
baseUrl: model.baseUrl,
|
|
3755
|
+
headers: buildOpenAIClientHeaders(model, context, options?.headers, turnState?.headers, sessionId),
|
|
3756
|
+
request: params
|
|
3757
|
+
});
|
|
3419
3758
|
const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
|
|
3420
3759
|
const requestStartedAt = Date.now();
|
|
3421
|
-
|
|
3422
|
-
const
|
|
3760
|
+
let started = false;
|
|
3761
|
+
const startStream = () => {
|
|
3762
|
+
if (!started) {
|
|
3763
|
+
started = true;
|
|
3764
|
+
stream.push({
|
|
3765
|
+
type: "start",
|
|
3766
|
+
partial: output
|
|
3767
|
+
});
|
|
3768
|
+
}
|
|
3769
|
+
};
|
|
3770
|
+
const firstEvent = createFirstStreamEventAbortController(options?.signal);
|
|
3771
|
+
firstEventAbort = firstEvent;
|
|
3772
|
+
const requestOptions = buildOpenAISdkRequestOptions(model, firstEvent.signal, {
|
|
3423
3773
|
stream: config.streamRequest,
|
|
3424
3774
|
timeoutMs: options?.timeoutMs,
|
|
3425
3775
|
maxRetries: options?.maxRetries
|
|
3426
3776
|
});
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3777
|
+
const websocketSignal = combineWebSocketTimeoutSignal(firstEvent.signal, model, requestOptions?.timeout);
|
|
3778
|
+
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
|
+
let continuationBaseline;
|
|
3780
|
+
const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
|
|
3781
|
+
const { stream: rawResponseStream, response, attempt } = await config.createResponseStream({
|
|
3782
|
+
client,
|
|
3783
|
+
request: initialRequest,
|
|
3784
|
+
requestOptions,
|
|
3785
|
+
model,
|
|
3786
|
+
observePrompt,
|
|
3787
|
+
initialAttemptKind,
|
|
3788
|
+
initialRejectedCompaction,
|
|
3789
|
+
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
3790
|
+
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
|
|
3791
|
+
});
|
|
3792
|
+
if (continuationClaim) continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
|
|
3793
|
+
return withProviderResponseHook({
|
|
3794
|
+
stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
|
|
3795
|
+
signal: firstEvent.signal,
|
|
3796
|
+
abort: firstEvent.abort,
|
|
3797
|
+
hook: createOpenAIProviderAcceptanceHook(options, response, model),
|
|
3798
|
+
onReady: () => {
|
|
3799
|
+
emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
|
|
3800
|
+
startStream();
|
|
3801
|
+
}
|
|
3802
|
+
});
|
|
3803
|
+
};
|
|
3804
|
+
let responseStream;
|
|
3805
|
+
let finishWebSocket;
|
|
3806
|
+
let transport = "sse";
|
|
3807
|
+
const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
|
|
3808
|
+
const closeWebSocketForFallback = (reason) => {
|
|
3809
|
+
finishWebSocket?.({ keep: false });
|
|
3810
|
+
finishWebSocket = void 0;
|
|
3811
|
+
transport = "sse";
|
|
3812
|
+
logWebSocketFallback(reason);
|
|
3813
|
+
};
|
|
3814
|
+
if (websocketMode) try {
|
|
3815
|
+
const websocket = createOpenAIResponsesWebSocketStream({
|
|
3816
|
+
client,
|
|
3817
|
+
request: params,
|
|
3818
|
+
mode: websocketMode,
|
|
3819
|
+
sessionId: options?.sessionId,
|
|
3820
|
+
headers: websocketHeaders,
|
|
3821
|
+
signal: websocketSignal,
|
|
3822
|
+
callerSignal: options?.signal,
|
|
3823
|
+
degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs
|
|
3824
|
+
});
|
|
3825
|
+
finishWebSocket = websocket.finish;
|
|
3826
|
+
observePrompt?.(websocket.request, {
|
|
3827
|
+
egress: "responses-websocket",
|
|
3828
|
+
payloadVariant: "initial"
|
|
3829
|
+
});
|
|
3830
|
+
transport = "websocket";
|
|
3831
|
+
emitModelTransportDebug(log, `[responses] websocket_selected provider=${model.provider} api=${model.api} model=${model.id} mode=${websocketMode} reused=${websocket.reusedConnection} continuation=${websocket.continuationStatus === "continued"} continuationStatus=${websocket.continuationStatus} sessionIdHash=${redactIdentifier(options?.sessionId)} headersHash=${redactIdentifier(JSON.stringify(Object.entries(websocketHeaders ?? {}).toSorted(([a], [b]) => a.localeCompare(b))))}`);
|
|
3832
|
+
responseStream = { async *[Symbol.asyncIterator]() {
|
|
3833
|
+
let providerAccepted = false;
|
|
3834
|
+
try {
|
|
3835
|
+
for await (const event of websocket.stream) {
|
|
3836
|
+
if (!providerAccepted) {
|
|
3837
|
+
providerAccepted = true;
|
|
3838
|
+
await notifyProviderStreamOpened({
|
|
3839
|
+
options,
|
|
3840
|
+
cancelStream: () => websocket.finish({ keep: false })
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
3843
|
+
startStream();
|
|
3844
|
+
yield event;
|
|
3845
|
+
}
|
|
3846
|
+
} catch (error) {
|
|
3847
|
+
if (error instanceof OpenAIResponsesWebSocketSafeRetryError) {
|
|
3848
|
+
const encryptedContentRejected = isInvalidEncryptedContentError(error);
|
|
3849
|
+
const recovery = encryptedContentRejected ? await resolveNextResponsesEncryptedContentAttempt({
|
|
3850
|
+
kind: "initial",
|
|
3851
|
+
request: {
|
|
3852
|
+
...websocket.request,
|
|
3853
|
+
stream: true
|
|
3854
|
+
}
|
|
3855
|
+
}, error, { buildFullHistoryRequest: () => buildRequest("full-history") }) : void 0;
|
|
3856
|
+
if (encryptedContentRejected && !recovery) throw error;
|
|
3857
|
+
closeWebSocketForFallback(`safe_server_error code=${error.code} status=${safeDebugValue(error.status)} param=${safeDebugValue(error.param)}`);
|
|
3858
|
+
yield* await createSseStream(recovery?.request ?? await buildRequest("full-history"), recovery?.kind ?? "continuation-rejected", recovery?.rejectedCompaction);
|
|
3859
|
+
return;
|
|
3860
|
+
}
|
|
3861
|
+
if (websocketSignal.aborted || !(error instanceof OpenAIResponsesWebSocketPreDispatchError)) throw error;
|
|
3862
|
+
transport = "sse";
|
|
3863
|
+
logWebSocketFallback("before_first_event");
|
|
3864
|
+
yield* await createSseStream();
|
|
3865
|
+
}
|
|
3866
|
+
} };
|
|
3867
|
+
} catch {
|
|
3868
|
+
closeWebSocketForFallback("setup_failure");
|
|
3869
|
+
responseStream = await createSseStream();
|
|
3870
|
+
}
|
|
3871
|
+
else responseStream = await createSseStream();
|
|
3872
|
+
try {
|
|
3873
|
+
const terminal = await processResponsesStream(responseStream, output, stream, model, {
|
|
3874
|
+
...config.pricingOptions?.(responsesOptions, model),
|
|
3875
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
|
|
3876
|
+
abortFirstEventStream: firstEvent.abort,
|
|
3877
|
+
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
3878
|
+
signal: options?.signal,
|
|
3879
|
+
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
3880
|
+
authProfileId: responsesOptions?.authProfileId,
|
|
3881
|
+
sessionId: options?.sessionId
|
|
3882
|
+
})
|
|
3883
|
+
});
|
|
3884
|
+
finishWebSocket?.();
|
|
3885
|
+
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
3886
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
3887
|
+
if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal);
|
|
3888
|
+
} catch (error) {
|
|
3889
|
+
finishWebSocket?.({ keep: false });
|
|
3890
|
+
throw error;
|
|
3891
|
+
}
|
|
3892
|
+
emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
|
|
3893
|
+
finalizeTransportStream({
|
|
3894
|
+
stream,
|
|
3895
|
+
output,
|
|
3896
|
+
signal: options?.signal
|
|
3461
3897
|
});
|
|
3462
|
-
stream.end();
|
|
3463
3898
|
} catch (error) {
|
|
3899
|
+
if (compactRequest) {
|
|
3900
|
+
compactRequest.reject(error);
|
|
3901
|
+
failTransportStream({
|
|
3902
|
+
stream,
|
|
3903
|
+
output,
|
|
3904
|
+
signal: options?.signal,
|
|
3905
|
+
error
|
|
3906
|
+
});
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3464
3909
|
if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
|
|
3465
3910
|
log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
error
|
|
3911
|
+
failTransportStream({
|
|
3912
|
+
stream,
|
|
3913
|
+
output,
|
|
3914
|
+
signal: options?.signal,
|
|
3915
|
+
error
|
|
3471
3916
|
});
|
|
3472
|
-
stream.end();
|
|
3473
3917
|
} finally {
|
|
3918
|
+
continuationClaim?.release();
|
|
3474
3919
|
firstEventAbort?.dispose();
|
|
3475
3920
|
}
|
|
3476
3921
|
})();
|
|
@@ -3480,12 +3925,13 @@ function createResponsesTransportExecutor(config) {
|
|
|
3480
3925
|
function createOpenAIResponsesTransportStreamFn() {
|
|
3481
3926
|
return createResponsesTransportExecutor({
|
|
3482
3927
|
streamRequest: true,
|
|
3928
|
+
httpContinuation: true,
|
|
3483
3929
|
createClient: createOpenAIResponsesClient,
|
|
3484
3930
|
buildRequest: buildOpenAIResponsesParams,
|
|
3485
3931
|
createResponseStream: createResponsesStreamWithEncryptedContentRetry,
|
|
3486
|
-
pricingOptions: (options) => ({
|
|
3932
|
+
pricingOptions: (options, model) => ({
|
|
3487
3933
|
serviceTier: options?.serviceTier,
|
|
3488
|
-
applyServiceTierPricing
|
|
3934
|
+
applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
|
|
3489
3935
|
})
|
|
3490
3936
|
});
|
|
3491
3937
|
}
|
|
@@ -3494,18 +3940,8 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
3494
3940
|
outputApi: "azure-openai-responses",
|
|
3495
3941
|
firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
|
|
3496
3942
|
createClient: createAzureOpenAIClient,
|
|
3497
|
-
buildRequest: (model, context, options, metadata) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata),
|
|
3498
|
-
createResponseStream:
|
|
3499
|
-
observePrompt?.(request, {
|
|
3500
|
-
egress: "responses-sdk",
|
|
3501
|
-
payloadVariant: "initial"
|
|
3502
|
-
});
|
|
3503
|
-
const { data, response } = await client.responses.create(request, requestOptions).withResponse();
|
|
3504
|
-
return {
|
|
3505
|
-
stream: data,
|
|
3506
|
-
response
|
|
3507
|
-
};
|
|
3508
|
-
}
|
|
3943
|
+
buildRequest: (model, context, options, metadata, replayMode) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata, replayMode),
|
|
3944
|
+
createResponseStream: createResponsesStreamWithEncryptedContentRetry
|
|
3509
3945
|
});
|
|
3510
3946
|
}
|
|
3511
3947
|
function normalizeAzureBaseUrl(baseUrl) {
|
|
@@ -3533,8 +3969,8 @@ function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHead
|
|
|
3533
3969
|
apiVersion: resolveAzureOpenAIApiVersion()
|
|
3534
3970
|
});
|
|
3535
3971
|
}
|
|
3536
|
-
function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata) {
|
|
3537
|
-
const params = buildOpenAIResponsesParams(model, context, options, metadata);
|
|
3972
|
+
function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata, replayMode = "checkpoint") {
|
|
3973
|
+
const params = buildOpenAIResponsesParams(model, context, options, metadata, replayMode);
|
|
3538
3974
|
params.model = deploymentName;
|
|
3539
3975
|
delete params.store;
|
|
3540
3976
|
return params;
|
|
@@ -3559,11 +3995,8 @@ const responsesTesting = {
|
|
|
3559
3995
|
buildOpenAIResponsesReasoningReplayMetadata,
|
|
3560
3996
|
isInvalidEncryptedContentError,
|
|
3561
3997
|
normalizeResponsesFailedEvent,
|
|
3562
|
-
prepareOpenAIResponsesReasoningItemForReplay,
|
|
3563
3998
|
createResponsesStreamWithEncryptedContentRetry,
|
|
3564
3999
|
resolveAzureOpenAIApiVersion,
|
|
3565
|
-
stripResponsesRequestEncryptedContent,
|
|
3566
|
-
tagOpenAIResponsesReasoningReplayItem,
|
|
3567
4000
|
summarizeResponsesFailedNoDetailsObservation,
|
|
3568
4001
|
summarizeResponsesPayload,
|
|
3569
4002
|
summarizeResponsesTools,
|
|
@@ -3572,6 +4005,51 @@ const responsesTesting = {
|
|
|
3572
4005
|
};
|
|
3573
4006
|
if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
|
|
3574
4007
|
//#endregion
|
|
4008
|
+
//#region packages/ai/src/transports/provider-compaction-replay.ts
|
|
4009
|
+
/** Whether provider replay state is a prefix-bound server compaction checkpoint. */
|
|
4010
|
+
function isCompactionReplayCheckpoint(replay) {
|
|
4011
|
+
const type = replay && typeof replay === "object" ? replay.type : void 0;
|
|
4012
|
+
return type === "anthropic-compaction" || type === "openai-responses-compaction" || type === "openai-responses-retained-compaction";
|
|
4013
|
+
}
|
|
4014
|
+
/** Strip prefix-bound checkpoints after local history rewrites. */
|
|
4015
|
+
function stripCompactionReplayCheckpoint(message) {
|
|
4016
|
+
if (!isCompactionReplayCheckpoint(message.providerReplay)) return message;
|
|
4017
|
+
const replaySafeMessage = { ...message };
|
|
4018
|
+
delete replaySafeMessage.providerReplay;
|
|
4019
|
+
return replaySafeMessage;
|
|
4020
|
+
}
|
|
4021
|
+
/** Strip prefix-bound checkpoint state from an in-place message rewrite. */
|
|
4022
|
+
function stripCompactionReplayCheckpointInPlace(message) {
|
|
4023
|
+
if (isCompactionReplayCheckpoint(message.providerReplay)) delete message.providerReplay;
|
|
4024
|
+
}
|
|
4025
|
+
/** Reindex a prefix-bound checkpoint after known content removals. */
|
|
4026
|
+
function replaceCompactionReplayOwnerContent(message, content) {
|
|
4027
|
+
const next = {
|
|
4028
|
+
...message,
|
|
4029
|
+
content
|
|
4030
|
+
};
|
|
4031
|
+
const replay = message.providerReplay;
|
|
4032
|
+
if (!isCompactionReplayCheckpoint(replay)) return next;
|
|
4033
|
+
if (replay.type === "openai-responses-retained-compaction") return next;
|
|
4034
|
+
const replayIndex = replay.replayIndex ?? 0;
|
|
4035
|
+
if (content.length === 0 && message.content.length > 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
|
|
4036
|
+
let sourceIndex = 0;
|
|
4037
|
+
let nextReplayIndex = 0;
|
|
4038
|
+
if (!content.every((block) => {
|
|
4039
|
+
const index = message.content.indexOf(block, sourceIndex);
|
|
4040
|
+
sourceIndex = index + 1;
|
|
4041
|
+
nextReplayIndex += index >= 0 && index < replayIndex ? 1 : 0;
|
|
4042
|
+
return index >= 0;
|
|
4043
|
+
})) return stripCompactionReplayCheckpoint(next);
|
|
4044
|
+
return nextReplayIndex === replayIndex ? next : {
|
|
4045
|
+
...next,
|
|
4046
|
+
providerReplay: {
|
|
4047
|
+
...replay,
|
|
4048
|
+
replayIndex: nextReplayIndex
|
|
4049
|
+
}
|
|
4050
|
+
};
|
|
4051
|
+
}
|
|
4052
|
+
//#endregion
|
|
3575
4053
|
//#region packages/ai/src/transports/provider-transport-stream.ts
|
|
3576
4054
|
const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
|
|
3577
4055
|
"openai-responses",
|
|
@@ -3582,10 +4060,7 @@ const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
|
|
|
3582
4060
|
"google-generative-ai"
|
|
3583
4061
|
]);
|
|
3584
4062
|
const SIMPLE_TRANSPORT_API_ALIAS = {
|
|
3585
|
-
"openai-responses": "openclaw-openai-responses-transport",
|
|
3586
|
-
"openai-chatgpt-responses": "openclaw-openai-chatgpt-responses-transport",
|
|
3587
4063
|
"openai-completions": "openclaw-openai-completions-transport",
|
|
3588
|
-
"azure-openai-responses": "openclaw-azure-openai-responses-transport",
|
|
3589
4064
|
"anthropic-messages": "openclaw-anthropic-messages-transport",
|
|
3590
4065
|
"google-generative-ai": "openclaw-google-generative-ai-transport"
|
|
3591
4066
|
};
|
|
@@ -3638,6 +4113,10 @@ function isTransportAwareApiSupported(api) {
|
|
|
3638
4113
|
}
|
|
3639
4114
|
/** Maps public model APIs to the internal transport API id used by simple runtime dispatch. */
|
|
3640
4115
|
function resolveTransportAwareSimpleApi(api) {
|
|
4116
|
+
if (OPENAI_RESPONSES_APIS.has(api)) {
|
|
4117
|
+
const alias = `openclaw-${api}-transport`;
|
|
4118
|
+
return OPENAI_RESPONSES_APIS.has(alias) ? alias : void 0;
|
|
4119
|
+
}
|
|
3641
4120
|
return SIMPLE_TRANSPORT_API_ALIAS[api];
|
|
3642
4121
|
}
|
|
3643
4122
|
/** Creates a managed transport stream only when request overrides require it. */
|
|
@@ -3734,12 +4213,12 @@ function resolveProviderStreamApi(model) {
|
|
|
3734
4213
|
];
|
|
3735
4214
|
return `${PROVIDER_STREAM_API_PREFIX}${parts.map((part) => encodeURIComponent(part)).join(":")}`;
|
|
3736
4215
|
}
|
|
3737
|
-
function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
|
|
4216
|
+
function applyProviderSimpleCompletionWrapper(registry, model, cfg, hookSourceApi = model.api) {
|
|
3738
4217
|
if (model.api.startsWith(PROVIDER_SIMPLE_COMPLETION_API_PREFIX)) return model;
|
|
3739
4218
|
const sourceProvider = registry.getApiProvider(model.api);
|
|
3740
4219
|
if (!sourceProvider) return model;
|
|
3741
|
-
const
|
|
3742
|
-
const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api:
|
|
4220
|
+
const dispatchApi = model.api;
|
|
4221
|
+
const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: dispatchApi }), context, options);
|
|
3743
4222
|
const streamFn = getAiTransportHost().plugin.wrapSimpleCompletionStream({
|
|
3744
4223
|
provider: model.provider,
|
|
3745
4224
|
config: cfg,
|
|
@@ -3748,6 +4227,7 @@ function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
|
|
|
3748
4227
|
provider: model.provider,
|
|
3749
4228
|
modelId: model.id,
|
|
3750
4229
|
model,
|
|
4230
|
+
sourceApi: hookSourceApi,
|
|
3751
4231
|
streamFn: sourceStreamFn
|
|
3752
4232
|
}
|
|
3753
4233
|
});
|
|
@@ -3781,7 +4261,6 @@ function wrapPluginProviderStream(streamFn) {
|
|
|
3781
4261
|
};
|
|
3782
4262
|
}
|
|
3783
4263
|
function prepareProviderStreamModel(params) {
|
|
3784
|
-
if (params.model.api === "google-generative-ai") return;
|
|
3785
4264
|
const pluginModel = resolveModelHeaderSentinels(params.model);
|
|
3786
4265
|
const providerStreamFn = getAiTransportHost().plugin.resolveProviderStream({
|
|
3787
4266
|
provider: params.model.provider,
|
|
@@ -3807,20 +4286,19 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
3807
4286
|
cfg,
|
|
3808
4287
|
apiRegistry
|
|
3809
4288
|
});
|
|
3810
|
-
if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg);
|
|
4289
|
+
if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg, model.api);
|
|
3811
4290
|
const codexTransportModel = prepareCodexSimpleTransportModel(apiRegistry, model, cfg);
|
|
3812
|
-
if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg);
|
|
4291
|
+
if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg, model.api);
|
|
3813
4292
|
const transportAwareModel = prepareTransportAwareSimpleModel(model, { cfg });
|
|
3814
4293
|
if (transportAwareModel !== model) {
|
|
3815
4294
|
const streamFn = buildTransportAwareSimpleStreamFn(model, { cfg });
|
|
3816
|
-
if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg);
|
|
4295
|
+
if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg, model.api);
|
|
3817
4296
|
}
|
|
3818
|
-
if (model.api === "google-generative-ai") return applyProviderSimpleCompletionWrapper(apiRegistry, getAiTransportHost().prepareGoogleSimpleCompletionModel(apiRegistry, model), cfg);
|
|
3819
4297
|
if (model.provider === "anthropic-vertex") {
|
|
3820
4298
|
const api = resolveAnthropicVertexSimpleApi(model.baseUrl);
|
|
3821
|
-
if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg);
|
|
4299
|
+
if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg, model.api);
|
|
3822
4300
|
}
|
|
3823
4301
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
3824
4302
|
}
|
|
3825
4303
|
//#endregion
|
|
3826
|
-
export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend };
|
|
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 };
|