@openclaw/ai 2026.8.1-beta.1 → 2026.8.1-beta.2
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-B6dLpq5L.mjs} +37 -22
- package/dist/{src-QkygScBs.mjs → anthropic-JsNA5KCu.mjs} +0 -1
- package/dist/{anthropic-usage-Ma4iX6uG.mjs → anthropic-compaction-replay-8lJNKXOE.mjs} +225 -102
- package/dist/anthropic-payload-policy-CiEuQS72.d.mts +49 -0
- package/dist/{api-registry-EpJoVwM1.d.mts → api-registry-k3zTz0cV.d.mts} +1 -1
- package/dist/{azure-openai-responses-Cc_kasye.mjs → azure-openai-responses-mxIOtUnn.mjs} +23 -29
- package/dist/diagnostics.d.mts +24 -1
- package/dist/diagnostics.mjs +2 -1
- package/dist/{event-stream-DRmDMRHH.d.mts → event-stream-BP6AWT8j.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-CGQu21aw.mjs → google-C7h2QzDX.mjs} +4 -4
- package/dist/{google-shared-Cq2UKx1l.mjs → google-shared-B0Qr9OR7.mjs} +9 -18
- package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
- package/dist/{google-vertex-CusxWzvE.mjs → google-vertex-Bhc3TjDl.mjs} +5 -4
- package/dist/{llm-request-activity-BjtkplhG.mjs → headers-DdOQtGuU.mjs} +9 -1
- package/dist/{host-Bl7Kgddo.mjs → host-DTqNc7ad.mjs} +227 -168
- package/dist/{host-Dn4_SZI2.d.mts → host-vWgMMhiJ.d.mts} +9 -4
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -5
- package/dist/internal/anthropic.d.mts +13 -5
- 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 +8 -7
- package/dist/internal/runtime.d.mts +17 -5
- package/dist/internal/runtime.mjs +85 -73
- package/dist/internal/shared.d.mts +1 -6
- package/dist/internal/shared.mjs +3 -4
- package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
- package/dist/{mistral-Dx6VzNjf.mjs → mistral-CEWoQI_g.mjs} +26 -45
- 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-CrPmqERt.mjs} +225 -144
- package/dist/{openai-completions-BaIpl93j.mjs → openai-completions-BPnt4Sml.mjs} +28 -43
- package/dist/openai-completions-compat-Dt3dcawL.d.mts +43 -0
- package/dist/{openai-responses-Bdx3V_Yu.mjs → openai-responses-DhIKtOup.mjs} +17 -24
- package/dist/openai-responses-contracts-CyfIkQi5.mjs +253 -0
- package/dist/openai-responses-contracts-XpZJxrRG.d.mts +68 -0
- package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +206 -0
- package/dist/openai-responses-payload-policy-BSs371VM.d.mts +40 -0
- package/dist/{openai-responses-prompt-observer-internal-DDZPxRfr.mjs → openai-responses-prompt-observer-internal-DgNTYnRY.mjs} +3 -3
- package/dist/{openai-responses-stream-internal-Bl8YyVtF.mjs → openai-responses-shared-DXIt3iY5.mjs} +1211 -908
- package/dist/{openai-reasoning-compat-DKebnIBL.mjs → openai-stop-reason-BkFkqqK0.mjs} +198 -172
- package/dist/{openai-transport-shared-Cipt7egQ.mjs → openai-tool-projection-CY04OcvQ.mjs} +151 -128
- package/dist/provider-error-BUwEnjXq.mjs +429 -0
- package/dist/provider-error-CzNw4BWX.d.mts +12 -0
- package/dist/{provider-options-C5kYML7i.d.mts → provider-options-B96RdNpH.d.mts} +9 -3
- package/dist/provider-transcript-transform-ePx-Bbfr.mjs +155 -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-rnPwX2pg.mjs} +14 -8
- package/dist/record-coerce-DdXsgUd_.mjs +23 -0
- package/dist/{model-utils-Dau5dlgm.mjs → sanitize-unicode-BYqrYtC_.mjs} +28 -2
- package/dist/session-resources-CkR4WWy1.mjs +21 -0
- package/dist/simple-options-D58D5Kvw.mjs +117 -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-q5d7QX5c.mjs} +5 -7
- package/dist/transport-utils-DJqkxbhC.mjs +138 -0
- package/dist/transports.d.mts +135 -178
- package/dist/transports.mjs +1732 -1294
- package/dist/types-BDdaOVi2.mjs +6 -0
- package/dist/{types-CH7ReIcU.d.mts → types-BHNrPS1l.d.mts} +19 -1
- package/dist/types.d.mts +4 -4
- 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-DT9SrFn3.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +15 -1
- package/dist/headers-B_e4-1J0.mjs +0 -9
- 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,36 @@
|
|
|
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 { r as createAssistantMessageEventStream } from "./event-stream-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./anthropic-JsNA5KCu.mjs";
|
|
3
|
+
import { r as createAssistantMessageEventStream } from "./event-stream-uSMZJ3FA.mjs";
|
|
4
|
+
import { n as normalizeLowercaseStringOrEmpty, t as hasNonEmptyString } from "./string-coerce-fsri9iCu.mjs";
|
|
5
|
+
import { r as calculateCost } from "./sanitize-unicode-BYqrYtC_.mjs";
|
|
6
|
+
import { _ as resolveAnthropicThinkingEffort, a as describeToolResultMediaPlaceholder, b as usesClaudeStreamingRefusalContract, d as ANTHROPIC_CLAUDE_CODE_VERSION, f as applyClaudeRequestContract, g as requiresClaudeAdaptiveThinking, h as prepareClaudeNoPrefillRequestContext, l as isImageWithMediaPayload, m as mapAnthropicStopReason, n as getAiTransportHost, o as extractToolResultBlockText, p as defaultsClaudeAdaptiveThinking, r as resolveAiTransportHeaderSentinels, s as extractToolResultText, u as ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK, y as usesClaudeFable5MessagesContract } from "./host-DTqNc7ad.mjs";
|
|
7
|
+
import { a as isRecord } from "./record-coerce-DdXsgUd_.mjs";
|
|
8
|
+
import { i as canonicalizeBase64, n as projectProviderError, o as stableStringify } from "./provider-error-BUwEnjXq.mjs";
|
|
9
|
+
import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-DgNTYnRY.mjs";
|
|
10
|
+
import { r as asNonNegativeFiniteNumber } from "./number-coercion-H9qHik3g.mjs";
|
|
11
|
+
import { _ as isOpenAIGpt56Model, c as OpenAIResponsesWebSocketPostDispatchError, d as OpenAIResponsesWebSocketSafeRetryError, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel, i as OPENAI_RESPONSES_APIS, l as OpenAIResponsesWebSocketPreDispatchError, p as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketResponseFailedError, v as normalizeOpenAIReasoningEffort, w as uniqueStrings, y as resolveOpenAIReasoningEffortForModel } from "./openai-responses-contracts-CyfIkQi5.mjs";
|
|
6
12
|
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
7
13
|
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
8
|
-
import {
|
|
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";
|
|
14
|
+
import { f as sortPromptCacheToolsByName, l as stripSystemPromptCacheBoundary, t as adjustMaxTokensForThinking } from "./simple-options-D58D5Kvw.mjs";
|
|
15
|
+
import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-q5d7QX5c.mjs";
|
|
16
|
+
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-8lJNKXOE.mjs";
|
|
17
|
+
import { a as createOpenAICompletionsToolCallDeltaNormalizer, c as resolveOpenAICompletionsCompat, d as clearPendingCommentaryText, f as rememberPendingCommentaryTags, i as hasToolCallHistory, l as resolveOpenAICompletionsResponseFormat, n as resolveOpenAIReasoningEffortMap, o as finalizeOpenAICompletionsToolCalls, p as tagPendingCommentaryText, r as convertMessages, s as detectOpenAICompletionsCompat, t as mapOpenAIStopReason, u as shouldOmitOllamaCompatResponseFormat } from "./openai-stop-reason-BkFkqqK0.mjs";
|
|
13
18
|
import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
|
|
14
|
-
import { n as parseStreamingJson } from "./json-parse-
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { a as
|
|
18
|
-
import {
|
|
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-
|
|
19
|
+
import { n as parseStreamingJson } from "./json-parse-CDnesDM_.mjs";
|
|
20
|
+
import { n as notifyLlmRequestActivity } from "./headers-DdOQtGuU.mjs";
|
|
21
|
+
import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as suppressOpenAIResponsesCompaction, H as createOpenAIResponsesAssistantOutput, I as createResponsesStreamWithEncryptedContentRetry, K as resolveReplayableResponsesMessageId, L as isInvalidEncryptedContentError, R as resolveAzureOpenAIApiVersion, S as summarizeResponsesPayload, U as buildOpenAIResponsesReasoningReplayMetadata, V as convertResponsesMessages, W as captureOpenAIResponsesCompaction, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, _ as safeDebugValue, b as summarizeOpenAITransportError, c as processResponsesStream, dt as resolveModelPayloadDebugMode, f as observeResponsesStream, ft as resolveModelSseDebugMode, g as normalizeResponsesFailedEvent, h as logResponsesFailedNoDetails, ht as quoteUnsafeIntegerLiterals, m as buildResponsesFailedNoDetailsObservation, mt as parseJsonPreservingUnsafeIntegers, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, pt as parseJsonObjectPreservingUnsafeIntegers, q as findOpenAIStrictToolProjectionDiagnostics, ut as emitModelTransportDebug, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-DXIt3iY5.mjs";
|
|
22
|
+
import { a as createWritableTransportEventStream, c as mergeTransportHeaders, d as sanitizeTransportPayloadText, f as transportAbortError, i as createEmptyTransportUsage, l as mergeTransportMetadata, n as assignTransportErrorDetails, o as failTransportStream, p as withProviderResponseHook, r as coerceTransportToolCallArguments, s as finalizeTransportStream, u as sanitizeNonEmptyTransportPayloadText } from "./provider-transcript-transform-ePx-Bbfr.mjs";
|
|
23
|
+
import { a as isGoogleGemini3FlashModel, c as readResponseTextSnippet, d as resolveModelHeaderSentinels$1, f as resolveSecretSentinel, i as isCodeModeModelVisibleToolName, l as redactIdentifier, m as supportsModelTools, n as createAbortError$1, o as isGoogleGemini3ProModel, p as sha256Hex, r as estimateStringChars, s as parseRetryAfterSeconds, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "./transport-utils-DJqkxbhC.mjs";
|
|
24
|
+
import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
|
|
25
|
+
import { a as createModelStreamCooperativeScheduler, c as log, d as readOpenAICompletionsContentDeltas, f as resolvePromptCacheKey, i as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, l as measureUtf8AppendBytes, n as reconcileOpenAICompletionsToolChoice, o as createOpenAIResponseHook, p as throwIfModelStreamAborted, r as reconcileOpenAIResponsesToolChoice, s as isOpenAICompletionsThinkingEnabled, t as projectOpenAITools, u as parseOpenAICompletionsUsage } from "./openai-tool-projection-CY04OcvQ.mjs";
|
|
26
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-MK28puvq.mjs";
|
|
27
|
+
import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-rnPwX2pg.mjs";
|
|
28
|
+
import { i as resolveOpenAIResponsesServerCompactionPlan, n as resolveOpenAIResponsesCompactEndpointPlan, r as resolveOpenAIResponsesPayloadPolicy, t as applyOpenAIResponsesPayloadPolicy } from "./openai-responses-payload-policy-BDxV-W0c.mjs";
|
|
22
29
|
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
23
|
-
import {
|
|
30
|
+
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
24
31
|
import { randomUUID } from "node:crypto";
|
|
25
32
|
import OpenAI, { AzureOpenAI } from "openai";
|
|
33
|
+
import { ResponsesWS } from "openai/resources/responses/ws.js";
|
|
26
34
|
//#region packages/ai/src/transports/anthropic-transport-stream.ts
|
|
27
35
|
/**
|
|
28
36
|
* Native Anthropic Messages streaming transport.
|
|
@@ -169,7 +177,7 @@ async function convertAnthropicMessages(messages, model, isOAuthToken, options)
|
|
|
169
177
|
continue;
|
|
170
178
|
}
|
|
171
179
|
if (msg.role === "assistant") {
|
|
172
|
-
const blocks = [];
|
|
180
|
+
const blocks = i === 0 && options.compaction ? [options.compaction] : [];
|
|
173
181
|
const reasoningContent = [];
|
|
174
182
|
let omittedThinking = false;
|
|
175
183
|
for (const block of msg.content) {
|
|
@@ -530,9 +538,15 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
530
538
|
enableCacheControl: true
|
|
531
539
|
});
|
|
532
540
|
const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
|
|
533
|
-
const
|
|
541
|
+
const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
|
|
542
|
+
enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
|
|
543
|
+
authProfileId: options?.authProfileId,
|
|
544
|
+
sessionId: options?.sessionId
|
|
545
|
+
});
|
|
546
|
+
const messages = await convertAnthropicMessages(replayPlan.messages, model, isOAuthToken, {
|
|
534
547
|
allowReasoningContentReplay: supportsReasoningContentReplay(model),
|
|
535
548
|
cacheBreakpointOptOutMessageIndexes,
|
|
549
|
+
compaction: replayPlan.compaction,
|
|
536
550
|
replayThinkingEnabled
|
|
537
551
|
});
|
|
538
552
|
const params = {
|
|
@@ -591,7 +605,8 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
591
605
|
applyAnthropicPayloadPolicyToParams(params, payloadPolicy, cacheBreakpointOptOutMessageIndexes);
|
|
592
606
|
return {
|
|
593
607
|
params,
|
|
594
|
-
toolProjection
|
|
608
|
+
toolProjection,
|
|
609
|
+
usedCompactionReplay: replayPlan.compaction !== void 0
|
|
595
610
|
};
|
|
596
611
|
}
|
|
597
612
|
function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
@@ -620,7 +635,9 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
620
635
|
interleavedThinking: options?.interleavedThinking,
|
|
621
636
|
toolChoice: options?.toolChoice,
|
|
622
637
|
thinkingBudgets: options?.thinkingBudgets,
|
|
623
|
-
reasoning
|
|
638
|
+
reasoning,
|
|
639
|
+
...options?.anthropicServerCompaction === true ? { anthropicServerCompaction: true } : {},
|
|
640
|
+
...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
|
|
624
641
|
};
|
|
625
642
|
if (reasoning === "off") {
|
|
626
643
|
resolved.thinkingEnabled = false;
|
|
@@ -664,6 +681,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
664
681
|
const eventSink = refusalBuffer ?? stream;
|
|
665
682
|
let costModel = model;
|
|
666
683
|
let messageStartPromptUsage;
|
|
684
|
+
let usedCompactionReplay = false;
|
|
667
685
|
try {
|
|
668
686
|
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
|
|
669
687
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
@@ -676,6 +694,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
676
694
|
options: transportOptions
|
|
677
695
|
});
|
|
678
696
|
const builtParams = await buildAnthropicParams(model, requestContext, isOAuthToken, transportOptions);
|
|
697
|
+
usedCompactionReplay = builtParams.usedCompactionReplay;
|
|
679
698
|
let params = builtParams.params;
|
|
680
699
|
const toolProjection = builtParams.toolProjection;
|
|
681
700
|
const nextParams = await transportOptions.onPayload?.(params, model);
|
|
@@ -687,6 +706,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
687
706
|
}, transportOptions.signal ? { signal: transportOptions.signal } : void 0);
|
|
688
707
|
const blocks = output.content;
|
|
689
708
|
const blockIndexes = /* @__PURE__ */ new Map();
|
|
709
|
+
const compactionCapture = createCompactionCapture(output, model, transportOptions);
|
|
690
710
|
const pendingThinkingSignatures = /* @__PURE__ */ new Map();
|
|
691
711
|
const allowReasoningContentReplay = supportsReasoningContentReplay(model);
|
|
692
712
|
const reasoningContentThinkingBlocks = /* @__PURE__ */ new Map();
|
|
@@ -809,6 +829,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
809
829
|
if (event.type === "content_block_start") {
|
|
810
830
|
const contentBlock = event.content_block;
|
|
811
831
|
const index = typeof event.index === "number" ? event.index : -1;
|
|
832
|
+
if (transportOptions.anthropicServerCompaction === true && compactionCapture.begin(index, contentBlock, output.content.length)) continue;
|
|
812
833
|
const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
|
|
813
834
|
if (fallbackBoundary) {
|
|
814
835
|
refusalBuffer?.discard();
|
|
@@ -945,6 +966,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
945
966
|
if (event.type === "content_block_delta") {
|
|
946
967
|
const delta = event.delta;
|
|
947
968
|
const eventIndex = typeof event.index === "number" ? event.index : void 0;
|
|
969
|
+
if (eventIndex !== void 0 && compactionCapture.delta(eventIndex, delta)) continue;
|
|
948
970
|
let index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
|
|
949
971
|
let block = index === void 0 ? void 0 : blocks[index];
|
|
950
972
|
if (allowReasoningContentReplay) {
|
|
@@ -1025,6 +1047,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1025
1047
|
}
|
|
1026
1048
|
if (event.type === "content_block_stop") {
|
|
1027
1049
|
const eventIndex = typeof event.index === "number" ? event.index : void 0;
|
|
1050
|
+
if (eventIndex !== void 0 && compactionCapture.complete(eventIndex)) continue;
|
|
1028
1051
|
const pendingSignature = eventIndex === void 0 ? void 0 : pendingThinkingSignatures.get(eventIndex);
|
|
1029
1052
|
if (eventIndex !== void 0) pendingThinkingSignatures.delete(eventIndex);
|
|
1030
1053
|
const index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
|
|
@@ -1079,7 +1102,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1079
1102
|
flushPendingTextEnds();
|
|
1080
1103
|
}
|
|
1081
1104
|
}
|
|
1082
|
-
if (
|
|
1105
|
+
if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
|
|
1083
1106
|
if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
|
|
1084
1107
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1085
1108
|
refusalBuffer?.flush();
|
|
@@ -1094,6 +1117,7 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1094
1117
|
refusalBuffer.discard();
|
|
1095
1118
|
output.content = [];
|
|
1096
1119
|
}
|
|
1120
|
+
if (usedCompactionReplay && isAnthropicReplayRejection(error)) suppressAnthropicCompaction(output, model, options);
|
|
1097
1121
|
failTransportStream({
|
|
1098
1122
|
stream,
|
|
1099
1123
|
output,
|
|
@@ -1210,15 +1234,11 @@ const MAX_TOKENS_PARAM_KEYS = [
|
|
|
1210
1234
|
"max_completion_tokens",
|
|
1211
1235
|
"max_tokens"
|
|
1212
1236
|
];
|
|
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
1237
|
/** Resolve the first supported max-token parameter present in a params object. */
|
|
1218
1238
|
function resolveMaxTokensParam(params) {
|
|
1219
1239
|
if (!params) return;
|
|
1220
1240
|
for (const key of MAX_TOKENS_PARAM_KEYS) {
|
|
1221
|
-
const resolved =
|
|
1241
|
+
const resolved = asNonNegativeFiniteNumber(params[key]);
|
|
1222
1242
|
if (resolved !== void 0) return resolved;
|
|
1223
1243
|
}
|
|
1224
1244
|
}
|
|
@@ -1335,6 +1355,172 @@ function stripCompletionMessagesToRoleContent(messages) {
|
|
|
1335
1355
|
});
|
|
1336
1356
|
}
|
|
1337
1357
|
//#endregion
|
|
1358
|
+
//#region packages/ai/src/transports/openai-completions-host.ts
|
|
1359
|
+
/**
|
|
1360
|
+
* Chat Completions accepts Azure AI Foundry hosts in addition to traditional
|
|
1361
|
+
* Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
|
|
1362
|
+
* which intentionally excludes the .services.ai.azure.com Foundry suffix.
|
|
1363
|
+
*/
|
|
1364
|
+
function isAzureOpenAICompatibleHost(hostname) {
|
|
1365
|
+
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
1366
|
+
}
|
|
1367
|
+
//#endregion
|
|
1368
|
+
//#region packages/ai/src/transports/openai-completions-replay.ts
|
|
1369
|
+
function isGoogleOpenAICompatModel(model) {
|
|
1370
|
+
const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
|
|
1371
|
+
return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
|
|
1372
|
+
}
|
|
1373
|
+
function requiresGoogleCompatToolCallThoughtSignature(model) {
|
|
1374
|
+
return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
|
|
1375
|
+
}
|
|
1376
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
|
|
1377
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
|
|
1378
|
+
function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
|
|
1379
|
+
return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
|
|
1380
|
+
}
|
|
1381
|
+
function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
|
|
1382
|
+
if (!isGoogleOpenAICompatModel(model)) return;
|
|
1383
|
+
const sigById = /* @__PURE__ */ new Map();
|
|
1384
|
+
const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
|
|
1385
|
+
for (const msg of context.messages ?? []) {
|
|
1386
|
+
if (msg.role !== "assistant") continue;
|
|
1387
|
+
const source = msg;
|
|
1388
|
+
if (!Array.isArray(source.content)) continue;
|
|
1389
|
+
for (const block of source.content) {
|
|
1390
|
+
if (block.type !== "toolCall") continue;
|
|
1391
|
+
const id = block.id;
|
|
1392
|
+
const sig = block.thoughtSignature;
|
|
1393
|
+
if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
|
|
1394
|
+
const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
|
|
1395
|
+
if (!isSameRoute && !fallbackSig) continue;
|
|
1396
|
+
sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (sigById.size === 0 && !fallbackSig) return;
|
|
1401
|
+
for (const message of outgoingMessages) {
|
|
1402
|
+
const toolCalls = message.tool_calls;
|
|
1403
|
+
if (!Array.isArray(toolCalls)) continue;
|
|
1404
|
+
for (const toolCall of toolCalls) {
|
|
1405
|
+
const id = toolCall.id;
|
|
1406
|
+
if (typeof id !== "string") continue;
|
|
1407
|
+
let sig = sigById.get(id) ?? fallbackSig;
|
|
1408
|
+
if (typeof sig === "string" && sig.length > 0) {
|
|
1409
|
+
if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
|
|
1410
|
+
}
|
|
1411
|
+
if (typeof sig !== "string" || sig.length === 0) continue;
|
|
1412
|
+
const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
|
|
1413
|
+
toolCall.extra_content = extra;
|
|
1414
|
+
const google = extra.google && typeof extra.google === "object" ? extra.google : {};
|
|
1415
|
+
extra.google = google;
|
|
1416
|
+
google.thought_signature = sig;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
const COMPLETIONS_REASONING_REPLAY_FIELDS = [
|
|
1421
|
+
"reasoning_details",
|
|
1422
|
+
"reasoning_content",
|
|
1423
|
+
"reasoning",
|
|
1424
|
+
"reasoning_text"
|
|
1425
|
+
];
|
|
1426
|
+
function stripCompletionsReasoningReplayFields(record) {
|
|
1427
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
|
|
1428
|
+
}
|
|
1429
|
+
function sanitizeOpenRouterReasoningReplayFields(record) {
|
|
1430
|
+
const reasoningDetails = record.reasoning_details;
|
|
1431
|
+
if (typeof reasoningDetails === "string") {
|
|
1432
|
+
if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
|
|
1433
|
+
delete record.reasoning_details;
|
|
1434
|
+
} else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
|
|
1435
|
+
if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
|
|
1436
|
+
if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
|
|
1437
|
+
const reasoningText = record.reasoning_text;
|
|
1438
|
+
if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
|
|
1439
|
+
if ("reasoning_text" in record) delete record.reasoning_text;
|
|
1440
|
+
}
|
|
1441
|
+
function sanitizeReasoningContentReplayFields(record) {
|
|
1442
|
+
if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
|
|
1443
|
+
delete record.reasoning_details;
|
|
1444
|
+
delete record.reasoning;
|
|
1445
|
+
delete record.reasoning_text;
|
|
1446
|
+
}
|
|
1447
|
+
const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
1448
|
+
"deepseek-v4-flash",
|
|
1449
|
+
"deepseek-v4-pro",
|
|
1450
|
+
"kimi-for-coding",
|
|
1451
|
+
"kimi-k2.5",
|
|
1452
|
+
"kimi-k2.6",
|
|
1453
|
+
"kimi-k2.7-code",
|
|
1454
|
+
"kimi-k2.7-code-highspeed",
|
|
1455
|
+
"kimi-k3",
|
|
1456
|
+
"kimi-k2-thinking",
|
|
1457
|
+
"kimi-k2-thinking-turbo",
|
|
1458
|
+
"mimo-v2-pro",
|
|
1459
|
+
"mimo-v2-omni",
|
|
1460
|
+
"mimo-v2.5",
|
|
1461
|
+
"mimo-v2.5-pro",
|
|
1462
|
+
"mimo-v2.6-pro"
|
|
1463
|
+
]);
|
|
1464
|
+
const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
|
|
1465
|
+
"-free",
|
|
1466
|
+
"-paid",
|
|
1467
|
+
"-trial"
|
|
1468
|
+
];
|
|
1469
|
+
function stripReasoningContentReplayTierSuffix(modelId) {
|
|
1470
|
+
for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
|
|
1471
|
+
return modelId;
|
|
1472
|
+
}
|
|
1473
|
+
function getReasoningContentReplayModelIdCandidates(modelId) {
|
|
1474
|
+
if (typeof modelId !== "string") return [];
|
|
1475
|
+
const normalized = modelId.trim().toLowerCase();
|
|
1476
|
+
if (!normalized) return [];
|
|
1477
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
1478
|
+
const finalPart = parts[parts.length - 1] ?? normalized;
|
|
1479
|
+
const candidates = [finalPart];
|
|
1480
|
+
const colonParts = finalPart.split(":").filter(Boolean);
|
|
1481
|
+
if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
|
|
1482
|
+
const baseCount = candidates.length;
|
|
1483
|
+
for (let index = 0; index < baseCount; index += 1) {
|
|
1484
|
+
const candidate = candidates[index];
|
|
1485
|
+
if (typeof candidate !== "string") continue;
|
|
1486
|
+
const stripped = stripReasoningContentReplayTierSuffix(candidate);
|
|
1487
|
+
if (stripped !== candidate) candidates.push(stripped);
|
|
1488
|
+
}
|
|
1489
|
+
return uniqueStrings(candidates.filter(Boolean));
|
|
1490
|
+
}
|
|
1491
|
+
function shouldPreserveReasoningContentReplay(model, compat) {
|
|
1492
|
+
if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
|
|
1493
|
+
return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
|
|
1494
|
+
}
|
|
1495
|
+
function shouldPreserveOpenRouterReasoningReplay(model) {
|
|
1496
|
+
if (model.provider !== "openrouter") return true;
|
|
1497
|
+
const normalizedModelId = model.id.trim().toLowerCase();
|
|
1498
|
+
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
1499
|
+
}
|
|
1500
|
+
function shouldTrustReasoningContentReplayMetadata(model) {
|
|
1501
|
+
if (!model.reasoning) return false;
|
|
1502
|
+
if (model.provider.trim().toLowerCase() === "openai") return false;
|
|
1503
|
+
return shouldPreserveOpenRouterReasoningReplay(model);
|
|
1504
|
+
}
|
|
1505
|
+
function sanitizeCompletionsReasoningReplayFields(messages, options) {
|
|
1506
|
+
if (!Array.isArray(messages)) return;
|
|
1507
|
+
for (const msg of messages) {
|
|
1508
|
+
if (!msg || typeof msg !== "object") continue;
|
|
1509
|
+
const record = msg;
|
|
1510
|
+
if (record.role !== "assistant") continue;
|
|
1511
|
+
if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
|
|
1512
|
+
else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
|
|
1513
|
+
else stripCompletionsReasoningReplayFields(record);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
1517
|
+
injectToolCallThoughtSignatures(outgoingMessages, context, model);
|
|
1518
|
+
sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
|
|
1519
|
+
preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
|
|
1520
|
+
preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
//#endregion
|
|
1338
1524
|
//#region packages/ai/src/transports/openai-transport-params.ts
|
|
1339
1525
|
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
1340
1526
|
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
@@ -1355,13 +1541,28 @@ function readCodeModePayloadToolName(tool) {
|
|
|
1355
1541
|
const fnName = readToolPayloadField(fn, "name");
|
|
1356
1542
|
return typeof fnName === "string" ? fnName : void 0;
|
|
1357
1543
|
}
|
|
1358
|
-
function
|
|
1544
|
+
function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
|
|
1545
|
+
if (!isRecord(tool)) return;
|
|
1546
|
+
const type = readToolPayloadField(tool, "type");
|
|
1547
|
+
if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
|
|
1548
|
+
try {
|
|
1549
|
+
if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
|
|
1550
|
+
} catch {
|
|
1551
|
+
return false;
|
|
1552
|
+
}
|
|
1553
|
+
return `hosted:${type}`;
|
|
1554
|
+
}
|
|
1555
|
+
const name = readCodeModePayloadToolName(tool);
|
|
1556
|
+
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
|
|
1557
|
+
}
|
|
1558
|
+
function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1359
1559
|
if (!isRecord(payload)) return;
|
|
1360
1560
|
const tools = readToolPayloadField(payload, "tools");
|
|
1361
1561
|
if (!Array.isArray(tools)) return;
|
|
1362
1562
|
payload.tools = tools.flatMap((tool) => {
|
|
1363
|
-
const
|
|
1364
|
-
if (
|
|
1563
|
+
const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
|
|
1564
|
+
if (identity) return [tool];
|
|
1565
|
+
if (identity === false) return [];
|
|
1365
1566
|
if (!isRecord(tool)) return [];
|
|
1366
1567
|
const filteredGroups = {};
|
|
1367
1568
|
for (const key of ["functionDeclarations", "function_declarations"]) {
|
|
@@ -1379,20 +1580,18 @@ function filterCodeModePayloadTools(payload, visibleToolNames) {
|
|
|
1379
1580
|
function resolveCodeModeResponsesVisibleToolNames(context) {
|
|
1380
1581
|
return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
|
|
1381
1582
|
}
|
|
1382
|
-
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
|
|
1583
|
+
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1383
1584
|
if (!isRecord(payload)) return;
|
|
1384
1585
|
const tools = readToolPayloadField(payload, "tools");
|
|
1385
1586
|
if (!Array.isArray(tools)) return;
|
|
1386
|
-
payload.tools = tools.filter((tool) =>
|
|
1387
|
-
const name = readCodeModePayloadToolName(tool);
|
|
1388
|
-
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames);
|
|
1389
|
-
});
|
|
1587
|
+
payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
|
|
1390
1588
|
}
|
|
1391
|
-
function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
|
|
1589
|
+
function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
|
|
1392
1590
|
const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
|
|
1393
1591
|
if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
|
|
1394
|
-
const
|
|
1395
|
-
|
|
1592
|
+
const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
|
|
1593
|
+
const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
|
|
1594
|
+
if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
|
|
1396
1595
|
throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
|
|
1397
1596
|
}
|
|
1398
1597
|
function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
|
|
@@ -1514,64 +1713,7 @@ function getCompat(model) {
|
|
|
1514
1713
|
};
|
|
1515
1714
|
}
|
|
1516
1715
|
//#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
|
-
}
|
|
1716
|
+
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
1575
1717
|
function isKnownOpenAICompletionsEndpoint(model) {
|
|
1576
1718
|
if (!model.baseUrl.trim()) return true;
|
|
1577
1719
|
const endpointClass = resolveProviderEndpoint(model.baseUrl).endpointClass;
|
|
@@ -1582,490 +1724,232 @@ function isKnownOpenAICompletionsEndpoint(model) {
|
|
|
1582
1724
|
return false;
|
|
1583
1725
|
}
|
|
1584
1726
|
}
|
|
1585
|
-
function
|
|
1586
|
-
|
|
1587
|
-
const defaultQuery = {};
|
|
1588
|
-
let baseURL = model.baseUrl;
|
|
1589
|
-
let isAzureHost = false;
|
|
1590
|
-
try {
|
|
1591
|
-
const parsed = new URL(model.baseUrl);
|
|
1592
|
-
isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
|
|
1593
|
-
parsed.searchParams.forEach((value, key) => {
|
|
1594
|
-
if (value) defaultQuery[key] = value;
|
|
1595
|
-
});
|
|
1596
|
-
parsed.search = "";
|
|
1597
|
-
baseURL = parsed.toString().replace(/\/$/, "");
|
|
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
|
-
}
|
|
1607
|
-
return {
|
|
1608
|
-
baseURL,
|
|
1609
|
-
defaultHeaders: headers,
|
|
1610
|
-
defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
|
|
1611
|
-
};
|
|
1727
|
+
function resolveOpenAICompletionsReasoningEffort$1(options) {
|
|
1728
|
+
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1612
1729
|
}
|
|
1613
|
-
function
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
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
|
-
};
|
|
1715
|
-
}
|
|
1716
|
-
async function processOpenAICompletionsStream(responseStream, output, model, stream, options) {
|
|
1717
|
-
const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
|
|
1718
|
-
const MAX_TOOL_CALL_ARGUMENT_BUFFER_BYTES = 256e3;
|
|
1719
|
-
const emitReasoning = options?.emitReasoning ?? true;
|
|
1720
|
-
const compat = getCompat(model);
|
|
1721
|
-
const deepSeekTextFilter = shouldFilterDeepSeekDsmlText(compat) ? createDeepSeekTextFilter() : null;
|
|
1722
|
-
const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText(compat) ? createDeepSeekDsmlToolCallRecoverer() : null;
|
|
1723
|
-
const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
|
|
1724
|
-
let currentBlock = null;
|
|
1725
|
-
let pendingPostToolCallDeltas = [];
|
|
1726
|
-
let pendingPostToolCallBytes = 0;
|
|
1727
|
-
let isFlushingPendingPostToolCallDeltas = false;
|
|
1728
|
-
const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
|
|
1729
|
-
const toolCallBlocksById = /* @__PURE__ */ new Map();
|
|
1730
|
-
const provisionalCommentaryTags = /* @__PURE__ */ new Map();
|
|
1731
|
-
const toolCallBlockBytes = /* @__PURE__ */ new WeakMap();
|
|
1732
|
-
const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
|
|
1733
|
-
const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
|
|
1734
|
-
let sawStopFinishReason = false;
|
|
1735
|
-
let sawNativeToolCallDelta = false;
|
|
1736
|
-
const blockIndex = () => output.content.length - 1;
|
|
1737
|
-
const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
|
|
1738
|
-
let chunkPushedEvent = false;
|
|
1739
|
-
const pushStreamEvent = (event) => {
|
|
1740
|
-
chunkPushedEvent = true;
|
|
1741
|
-
stream.push(event);
|
|
1742
|
-
};
|
|
1743
|
-
const queuePostToolCallDelta = (next) => {
|
|
1744
|
-
const nextBytes = measureUtf8Bytes(next.text);
|
|
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;
|
|
1751
|
-
}
|
|
1752
|
-
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
1753
|
-
if (previous.signature !== next.signature) {
|
|
1754
|
-
pendingPostToolCallDeltas.push(next);
|
|
1755
|
-
return;
|
|
1756
|
-
}
|
|
1757
|
-
previous.text += next.text;
|
|
1758
|
-
return;
|
|
1759
|
-
}
|
|
1760
|
-
previous.text += next.text;
|
|
1761
|
-
};
|
|
1762
|
-
const appendThinkingDeltaInternal = (reasoningDelta) => {
|
|
1763
|
-
if (!currentBlock || currentBlock.type !== "thinking") {
|
|
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
|
-
});
|
|
1775
|
-
}
|
|
1776
|
-
currentBlock.thinking += reasoningDelta.text;
|
|
1777
|
-
pushStreamEvent({
|
|
1778
|
-
type: "thinking_delta",
|
|
1779
|
-
contentIndex: blockIndex(),
|
|
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
|
-
});
|
|
1796
|
-
}
|
|
1797
|
-
currentBlock.text += text;
|
|
1798
|
-
pushStreamEvent({
|
|
1799
|
-
type: "text_delta",
|
|
1800
|
-
contentIndex: blockIndex(),
|
|
1801
|
-
delta: text
|
|
1802
|
-
});
|
|
1803
|
-
};
|
|
1804
|
-
const flushPendingPostToolCallDeltas = () => {
|
|
1805
|
-
if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
|
|
1806
|
-
isFlushingPendingPostToolCallDeltas = true;
|
|
1807
|
-
const bufferedDeltas = pendingPostToolCallDeltas;
|
|
1808
|
-
pendingPostToolCallDeltas = [];
|
|
1809
|
-
pendingPostToolCallBytes = 0;
|
|
1810
|
-
for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
|
|
1811
|
-
else if (emitReasoning) appendThinkingDeltaInternal(delta);
|
|
1812
|
-
isFlushingPendingPostToolCallDeltas = false;
|
|
1813
|
-
};
|
|
1814
|
-
const appendThinkingDelta = (reasoningDelta) => {
|
|
1815
|
-
flushPendingPostToolCallDeltas();
|
|
1816
|
-
appendThinkingDeltaInternal(reasoningDelta);
|
|
1817
|
-
};
|
|
1818
|
-
const appendTextDelta = (text) => {
|
|
1819
|
-
flushPendingPostToolCallDeltas();
|
|
1820
|
-
appendTextDeltaInternal(text);
|
|
1821
|
-
};
|
|
1822
|
-
const appendVisibleTextDelta = (text) => {
|
|
1823
|
-
if (!text) return;
|
|
1824
|
-
if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
|
|
1825
|
-
kind: "text",
|
|
1826
|
-
text
|
|
1827
|
-
});
|
|
1828
|
-
else appendTextDelta(text);
|
|
1829
|
-
};
|
|
1830
|
-
const appendRecoveredToolCall = (toolCall) => {
|
|
1831
|
-
if (currentBlock?.type === "toolCall") {
|
|
1832
|
-
currentBlock = null;
|
|
1833
|
-
flushPendingPostToolCallDeltas();
|
|
1834
|
-
}
|
|
1835
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
1836
|
-
const block = {
|
|
1837
|
-
type: "toolCall",
|
|
1838
|
-
id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
|
|
1839
|
-
name: toolCall.name,
|
|
1840
|
-
arguments: toolCall.arguments,
|
|
1841
|
-
partialArgs: toolCall.partialArgs
|
|
1842
|
-
};
|
|
1843
|
-
currentBlock = block;
|
|
1844
|
-
output.content.push(block);
|
|
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);
|
|
1870
|
-
}
|
|
1730
|
+
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
1731
|
+
if (options?.maxTokens) return {
|
|
1732
|
+
maxTokens: options.maxTokens,
|
|
1733
|
+
clampToModelMaxTokens: true
|
|
1871
1734
|
};
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
if (recoveredPart.kind === "toolCall") {
|
|
1877
|
-
appendRecoveredToolCall(recoveredPart);
|
|
1878
|
-
continue;
|
|
1879
|
-
}
|
|
1880
|
-
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
1881
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
1882
|
-
}
|
|
1735
|
+
const paramsMaxTokens = resolveMaxTokensParam(model.params);
|
|
1736
|
+
if (paramsMaxTokens) return {
|
|
1737
|
+
maxTokens: paramsMaxTokens,
|
|
1738
|
+
clampToModelMaxTokens: false
|
|
1883
1739
|
};
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
for (const part of parts) appendVisibleTextDelta(part);
|
|
1740
|
+
return {
|
|
1741
|
+
maxTokens: model.maxTokens,
|
|
1742
|
+
clampToModelMaxTokens: false
|
|
1888
1743
|
};
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1744
|
+
}
|
|
1745
|
+
function resolveOpenAICompletionsModelMaxTokens(model) {
|
|
1746
|
+
return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
|
|
1747
|
+
}
|
|
1748
|
+
const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
|
|
1749
|
+
const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
|
|
1750
|
+
function estimateOpenAICompletionsInputTokens(payload) {
|
|
1751
|
+
let adjustedChars = 0;
|
|
1752
|
+
adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
|
|
1753
|
+
if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
|
|
1754
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
|
|
1755
|
+
} catch {
|
|
1756
|
+
adjustedChars += 1024;
|
|
1757
|
+
}
|
|
1758
|
+
if (payload.response_format !== void 0) try {
|
|
1759
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
|
|
1760
|
+
} catch {
|
|
1761
|
+
adjustedChars += 256;
|
|
1762
|
+
}
|
|
1763
|
+
return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
|
|
1764
|
+
}
|
|
1765
|
+
function estimateOpenAICompletionsMessagesChars(messages) {
|
|
1766
|
+
if (!Array.isArray(messages)) return 0;
|
|
1767
|
+
let adjustedChars = 0;
|
|
1768
|
+
for (const message of messages) {
|
|
1769
|
+
if (!message || typeof message !== "object") continue;
|
|
1770
|
+
const record = message;
|
|
1771
|
+
adjustedChars += estimateOpenAICompletionsContentChars(record.content);
|
|
1772
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
|
|
1773
|
+
if (record.tool_calls !== void 0) try {
|
|
1774
|
+
adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
|
|
1775
|
+
} catch {
|
|
1776
|
+
adjustedChars += 256;
|
|
1893
1777
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
if (!
|
|
1903
|
-
const
|
|
1904
|
-
if (
|
|
1905
|
-
|
|
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();
|
|
1778
|
+
}
|
|
1779
|
+
return adjustedChars;
|
|
1780
|
+
}
|
|
1781
|
+
function estimateOpenAICompletionsContentChars(value) {
|
|
1782
|
+
if (typeof value === "string") return estimateStringChars(value);
|
|
1783
|
+
if (!Array.isArray(value)) return 0;
|
|
1784
|
+
let adjustedChars = 0;
|
|
1785
|
+
for (const block of value) {
|
|
1786
|
+
if (!block || typeof block !== "object") continue;
|
|
1787
|
+
const record = block;
|
|
1788
|
+
if (record.type === "image_url" || record.type === "input_image") {
|
|
1789
|
+
adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
|
|
1927
1790
|
continue;
|
|
1928
1791
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
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();
|
|
1792
|
+
const text = record.text;
|
|
1793
|
+
if (typeof text === "string") {
|
|
1794
|
+
adjustedChars += estimateStringChars(text);
|
|
1941
1795
|
continue;
|
|
1942
1796
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
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;
|
|
1797
|
+
try {
|
|
1798
|
+
adjustedChars += estimateStringChars(JSON.stringify(block));
|
|
1799
|
+
} catch {
|
|
1800
|
+
adjustedChars += 256;
|
|
1959
1801
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1802
|
+
}
|
|
1803
|
+
return adjustedChars;
|
|
1804
|
+
}
|
|
1805
|
+
function resolveOpenAICompletionsEffectiveContextTokens(model) {
|
|
1806
|
+
const contextTokens = model.contextTokens;
|
|
1807
|
+
if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
|
|
1808
|
+
return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
|
|
1809
|
+
}
|
|
1810
|
+
function isQwenOpenAICompletionsThinkingFormat(format) {
|
|
1811
|
+
return format === "qwen" || format === "qwen-chat-template";
|
|
1812
|
+
}
|
|
1813
|
+
function setQwenChatTemplateThinking(params, enabled) {
|
|
1814
|
+
const existing = params.chat_template_kwargs;
|
|
1815
|
+
params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
|
|
1816
|
+
...existing,
|
|
1817
|
+
enable_thinking: enabled
|
|
1818
|
+
} : { enable_thinking: enabled };
|
|
1819
|
+
}
|
|
1820
|
+
function applyQwenOpenAICompletionsThinkingParams(params) {
|
|
1821
|
+
if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
|
|
1822
|
+
const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
|
|
1823
|
+
if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
|
|
1824
|
+
else params.payload.enable_thinking = enabled;
|
|
1825
|
+
return true;
|
|
1826
|
+
}
|
|
1827
|
+
function applyTogetherOpenAICompletionsThinkingParams(params) {
|
|
1828
|
+
if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
|
|
1829
|
+
params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
|
|
1830
|
+
}
|
|
1831
|
+
function convertTools(tools, compat, model) {
|
|
1832
|
+
const projection = projectOpenAITools(tools);
|
|
1833
|
+
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
|
|
1834
|
+
transport: "stream",
|
|
1835
|
+
supportsStrictMode: compat?.supportsStrictMode
|
|
1836
|
+
}), {
|
|
1837
|
+
transport: "completions",
|
|
1838
|
+
model
|
|
1839
|
+
});
|
|
1840
|
+
return {
|
|
1841
|
+
projection,
|
|
1842
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
1843
|
+
const functionTool = {
|
|
1844
|
+
name: tool.name,
|
|
1845
|
+
description: tool.description,
|
|
1846
|
+
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
1976
1847
|
};
|
|
1977
|
-
if (
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
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
|
-
}
|
|
1848
|
+
if (strict !== void 0) functionTool.strict = strict;
|
|
1849
|
+
return {
|
|
1850
|
+
type: "function",
|
|
1851
|
+
function: functionTool
|
|
1852
|
+
};
|
|
1853
|
+
})
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
function buildOpenAICompletionsParams(model, context, options) {
|
|
1857
|
+
const compat = getCompat(model);
|
|
1858
|
+
const compatDetection = detectOpenAICompletionsCompat(model);
|
|
1859
|
+
let messages = convertMessages(model, context.systemPrompt ? {
|
|
1860
|
+
...context,
|
|
1861
|
+
systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
|
|
1862
|
+
} : context, compat);
|
|
1863
|
+
applyCompletionsReplay(messages, context, model, compat);
|
|
1864
|
+
if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
|
|
1865
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
1866
|
+
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
1867
|
+
const params = {
|
|
1868
|
+
model: model.id,
|
|
1869
|
+
messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
|
|
1870
|
+
stream: true
|
|
1871
|
+
};
|
|
1872
|
+
if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
|
|
1873
|
+
if (compat.supportsStore) params.store = false;
|
|
1874
|
+
if (compat.supportsPromptCacheKey && promptCacheKey) {
|
|
1875
|
+
params.prompt_cache_key = promptCacheKey;
|
|
1876
|
+
if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
|
|
1877
|
+
}
|
|
1878
|
+
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
1879
|
+
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
1880
|
+
const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
1881
|
+
provider: model.provider,
|
|
1882
|
+
baseUrl: model.baseUrl,
|
|
1883
|
+
hasTools: () => Boolean(context.tools?.length)
|
|
1884
|
+
}) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
1885
|
+
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
1886
|
+
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
1887
|
+
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
1888
|
+
if (options?.seed !== void 0) params.seed = options.seed;
|
|
1889
|
+
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
1890
|
+
if (supportsModelTools(model)) {
|
|
1891
|
+
if (context.tools) {
|
|
1892
|
+
const converted = convertTools(context.tools, compat, model);
|
|
1893
|
+
if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
|
|
1894
|
+
else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1895
|
+
if (options?.toolChoice) {
|
|
1896
|
+
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
|
|
1897
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1898
|
+
} else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
|
|
1899
|
+
} else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1900
|
+
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
|
|
1901
|
+
delete params.tools;
|
|
1902
|
+
delete params.tool_choice;
|
|
2042
1903
|
}
|
|
2043
|
-
flushPendingPostToolCallDeltas();
|
|
2044
|
-
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2045
|
-
await cooperativeScheduler.afterEvent();
|
|
2046
1904
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
1905
|
+
{
|
|
1906
|
+
const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
|
|
1907
|
+
const effectiveMaxTokens = maxTokenBudget.maxTokens;
|
|
1908
|
+
const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
|
|
1909
|
+
let clampedMaxTokens = effectiveMaxTokens;
|
|
1910
|
+
const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
|
|
1911
|
+
if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
|
|
1912
|
+
clampedMaxTokens = modelMaxTokens;
|
|
1913
|
+
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
|
|
1914
|
+
}
|
|
1915
|
+
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
|
|
1916
|
+
const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
|
|
1917
|
+
const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
|
|
1918
|
+
if (clampedMaxTokens > remainingBudget) {
|
|
1919
|
+
clampedMaxTokens = remainingBudget;
|
|
1920
|
+
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
|
|
1921
|
+
}
|
|
2061
1922
|
}
|
|
1923
|
+
if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1924
|
+
else params.max_completion_tokens = clampedMaxTokens;
|
|
1925
|
+
}
|
|
1926
|
+
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort$1(options);
|
|
1927
|
+
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1928
|
+
model,
|
|
1929
|
+
effort: completionsReasoningEffort,
|
|
1930
|
+
fallbackMap: compat.reasoningEffortMap
|
|
1931
|
+
}) : void 0;
|
|
1932
|
+
const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
|
|
1933
|
+
const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
|
|
1934
|
+
const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
|
|
1935
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1936
|
+
modelReasoning: model.reasoning,
|
|
1937
|
+
payload: params,
|
|
1938
|
+
requestedEffort: completionsReasoningEffort
|
|
2062
1939
|
});
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
1940
|
+
applyTogetherOpenAICompletionsThinkingParams({
|
|
1941
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1942
|
+
modelReasoning: model.reasoning,
|
|
1943
|
+
payload: params,
|
|
1944
|
+
requestedEffort: completionsReasoningEffort
|
|
1945
|
+
});
|
|
1946
|
+
if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
|
|
1947
|
+
else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
|
|
1948
|
+
else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
|
|
1949
|
+
return params;
|
|
2068
1950
|
}
|
|
1951
|
+
//#endregion
|
|
1952
|
+
//#region packages/ai/src/transports/openai-completions-dsml.ts
|
|
2069
1953
|
const DEEPSEEK_DSML_BARS = ["|", "|"];
|
|
2070
1954
|
const DEEPSEEK_DSML_TOOL_KINDS = [
|
|
2071
1955
|
"tool_calls",
|
|
@@ -2080,7 +1964,7 @@ const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPE
|
|
|
2080
1964
|
const DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_TOOL_CLOSE_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS.map((token) => token.length));
|
|
2081
1965
|
const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
|
|
2082
1966
|
const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
|
|
2083
|
-
function
|
|
1967
|
+
function createDsmlRecoverer() {
|
|
2084
1968
|
let buffer = "";
|
|
2085
1969
|
let bufferBytes = 0;
|
|
2086
1970
|
let bufferEndsWithHighSurrogate = false;
|
|
@@ -2180,7 +2064,7 @@ function createDeepSeekDsmlToolCallRecoverer() {
|
|
|
2180
2064
|
};
|
|
2181
2065
|
return {
|
|
2182
2066
|
push(chunk) {
|
|
2183
|
-
const append =
|
|
2067
|
+
const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
|
|
2184
2068
|
bufferBytes += append.bytes;
|
|
2185
2069
|
bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
|
|
2186
2070
|
buffer += chunk;
|
|
@@ -2336,20 +2220,6 @@ function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
|
|
|
2336
2220
|
}
|
|
2337
2221
|
return { kind: "incomplete" };
|
|
2338
2222
|
}
|
|
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
2223
|
function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
|
|
2354
2224
|
const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
|
|
2355
2225
|
for (let length = maxLength; length > 0; length -= 1) {
|
|
@@ -2358,664 +2228,784 @@ function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
|
|
|
2358
2228
|
}
|
|
2359
2229
|
return 0;
|
|
2360
2230
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2231
|
+
//#endregion
|
|
2232
|
+
//#region packages/ai/src/transports/openai-completions-stream.ts
|
|
2233
|
+
function extractToolCallThoughtSignature(toolCall) {
|
|
2234
|
+
const tc = toolCall;
|
|
2235
|
+
if (!tc) return;
|
|
2236
|
+
const fromExtra = (tc.extra_content?.google)?.thought_signature;
|
|
2237
|
+
if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
|
|
2238
|
+
const fromFunction = tc.function?.thought_signature;
|
|
2239
|
+
if (typeof fromFunction === "string" && fromFunction.length > 0) return fromFunction;
|
|
2240
|
+
const fromToolCall = tc.thought_signature;
|
|
2241
|
+
return typeof fromToolCall === "string" && fromToolCall.length > 0 ? fromToolCall : void 0;
|
|
2242
|
+
}
|
|
2243
|
+
async function processCompletionsStream(responseStream, output, model, stream, options) {
|
|
2244
|
+
const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
|
|
2245
|
+
const emitReasoning = options?.emitReasoning ?? true;
|
|
2246
|
+
const compat = getCompat(model);
|
|
2247
|
+
const shouldFilterDeepSeekDsmlText = compat.thinkingFormat === "deepseek";
|
|
2248
|
+
const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
|
|
2249
|
+
const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
|
|
2250
|
+
const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
|
|
2251
|
+
let currentBlock = null;
|
|
2252
|
+
let pendingPostToolCallDeltas = [];
|
|
2253
|
+
let pendingPostToolCallBytes = 0;
|
|
2254
|
+
let isFlushingPendingPostToolCallDeltas = false;
|
|
2255
|
+
const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
|
|
2256
|
+
const toolCallBlocksById = /* @__PURE__ */ new Map();
|
|
2257
|
+
const provisionalCommentaryTags = /* @__PURE__ */ new Map();
|
|
2258
|
+
const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
|
|
2259
|
+
const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
|
|
2260
|
+
let sawStopFinishReason = false;
|
|
2261
|
+
let sawNativeToolCallDelta = false;
|
|
2262
|
+
const blockIndex = () => output.content.length - 1;
|
|
2263
|
+
const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
|
|
2264
|
+
let chunkPushedEvent = false;
|
|
2265
|
+
const pushStreamEvent = (event) => {
|
|
2266
|
+
chunkPushedEvent = true;
|
|
2267
|
+
stream.push(event);
|
|
2268
|
+
};
|
|
2269
|
+
const queuePostToolCallDelta = (next) => {
|
|
2270
|
+
const nextBytes = measureUtf8Bytes(next.text);
|
|
2271
|
+
if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
|
|
2272
|
+
pendingPostToolCallBytes += nextBytes;
|
|
2273
|
+
const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
|
|
2365
2274
|
if (!previous || previous.kind !== next.kind) {
|
|
2366
|
-
|
|
2275
|
+
pendingPostToolCallDeltas.push(next);
|
|
2367
2276
|
return;
|
|
2368
2277
|
}
|
|
2369
2278
|
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
2370
2279
|
if (previous.signature !== next.signature) {
|
|
2371
|
-
|
|
2280
|
+
pendingPostToolCallDeltas.push(next);
|
|
2372
2281
|
return;
|
|
2373
2282
|
}
|
|
2374
|
-
previous.text += next.text;
|
|
2375
|
-
return;
|
|
2283
|
+
previous.text += next.text;
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
previous.text += next.text;
|
|
2287
|
+
};
|
|
2288
|
+
const appendThinkingDeltaInternal = (reasoningDelta) => {
|
|
2289
|
+
if (!currentBlock || currentBlock.type !== "thinking") {
|
|
2290
|
+
currentBlock = {
|
|
2291
|
+
type: "thinking",
|
|
2292
|
+
thinking: "",
|
|
2293
|
+
...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
|
|
2294
|
+
};
|
|
2295
|
+
output.content.push(currentBlock);
|
|
2296
|
+
pushStreamEvent({
|
|
2297
|
+
type: "thinking_start",
|
|
2298
|
+
contentIndex: blockIndex(),
|
|
2299
|
+
partial: output
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
currentBlock.thinking += reasoningDelta.text;
|
|
2303
|
+
pushStreamEvent({
|
|
2304
|
+
type: "thinking_delta",
|
|
2305
|
+
contentIndex: blockIndex(),
|
|
2306
|
+
delta: reasoningDelta.text,
|
|
2307
|
+
partial: output
|
|
2308
|
+
});
|
|
2309
|
+
};
|
|
2310
|
+
const appendTextDeltaInternal = (text) => {
|
|
2311
|
+
if (!currentBlock || currentBlock.type !== "text") {
|
|
2312
|
+
currentBlock = {
|
|
2313
|
+
type: "text",
|
|
2314
|
+
text: ""
|
|
2315
|
+
};
|
|
2316
|
+
output.content.push(currentBlock);
|
|
2317
|
+
pushStreamEvent({
|
|
2318
|
+
type: "text_start",
|
|
2319
|
+
contentIndex: blockIndex(),
|
|
2320
|
+
partial: output
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
currentBlock.text += text;
|
|
2324
|
+
pushStreamEvent({
|
|
2325
|
+
type: "text_delta",
|
|
2326
|
+
contentIndex: blockIndex(),
|
|
2327
|
+
delta: text
|
|
2328
|
+
});
|
|
2329
|
+
};
|
|
2330
|
+
const flushPendingPostToolCallDeltas = () => {
|
|
2331
|
+
if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
|
|
2332
|
+
isFlushingPendingPostToolCallDeltas = true;
|
|
2333
|
+
const bufferedDeltas = pendingPostToolCallDeltas;
|
|
2334
|
+
pendingPostToolCallDeltas = [];
|
|
2335
|
+
pendingPostToolCallBytes = 0;
|
|
2336
|
+
for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
|
|
2337
|
+
else if (emitReasoning) appendThinkingDeltaInternal(delta);
|
|
2338
|
+
isFlushingPendingPostToolCallDeltas = false;
|
|
2339
|
+
};
|
|
2340
|
+
const appendThinkingDelta = (reasoningDelta) => {
|
|
2341
|
+
flushPendingPostToolCallDeltas();
|
|
2342
|
+
appendThinkingDeltaInternal(reasoningDelta);
|
|
2343
|
+
};
|
|
2344
|
+
const appendTextDelta = (text) => {
|
|
2345
|
+
flushPendingPostToolCallDeltas();
|
|
2346
|
+
appendTextDeltaInternal(text);
|
|
2347
|
+
};
|
|
2348
|
+
const appendVisibleTextDelta = (text) => {
|
|
2349
|
+
if (!text) return;
|
|
2350
|
+
if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
|
|
2351
|
+
kind: "text",
|
|
2352
|
+
text
|
|
2353
|
+
});
|
|
2354
|
+
else appendTextDelta(text);
|
|
2355
|
+
};
|
|
2356
|
+
const appendRecoveredToolCall = (toolCall) => {
|
|
2357
|
+
if (currentBlock?.type === "toolCall") {
|
|
2358
|
+
currentBlock = null;
|
|
2359
|
+
flushPendingPostToolCallDeltas();
|
|
2360
|
+
}
|
|
2361
|
+
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
2362
|
+
const block = {
|
|
2363
|
+
type: "toolCall",
|
|
2364
|
+
id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
|
|
2365
|
+
name: toolCall.name,
|
|
2366
|
+
arguments: toolCall.arguments,
|
|
2367
|
+
partialArgs: toolCall.partialArgs
|
|
2368
|
+
};
|
|
2369
|
+
currentBlock = block;
|
|
2370
|
+
output.content.push(block);
|
|
2371
|
+
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2372
|
+
pushStreamEvent({
|
|
2373
|
+
type: "toolcall_start",
|
|
2374
|
+
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2375
|
+
partial: output
|
|
2376
|
+
});
|
|
2377
|
+
pushStreamEvent({
|
|
2378
|
+
type: "toolcall_delta",
|
|
2379
|
+
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2380
|
+
delta: toolCall.partialArgs,
|
|
2381
|
+
partial: output
|
|
2382
|
+
});
|
|
2383
|
+
};
|
|
2384
|
+
const appendFilteredVisibleTextDelta = (text) => {
|
|
2385
|
+
const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
|
|
2386
|
+
kind: "text",
|
|
2387
|
+
text
|
|
2388
|
+
}];
|
|
2389
|
+
for (const recoveredPart of recoveredParts) {
|
|
2390
|
+
if (recoveredPart.kind === "toolCall") {
|
|
2391
|
+
appendRecoveredToolCall(recoveredPart);
|
|
2392
|
+
continue;
|
|
2393
|
+
}
|
|
2394
|
+
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
2395
|
+
for (const part of parts) appendVisibleTextDelta(part);
|
|
2376
2396
|
}
|
|
2377
|
-
previous.text += next.text;
|
|
2378
2397
|
};
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
if (typeof detail.text !== "string" || !detail.text) continue;
|
|
2386
|
-
if (detail.type === "reasoning.text") {
|
|
2387
|
-
usedReasoningThinkingDetails = true;
|
|
2388
|
-
pushDelta({
|
|
2389
|
-
kind: "thinking",
|
|
2390
|
-
signature: "reasoning_details",
|
|
2391
|
-
text: detail.text
|
|
2392
|
-
});
|
|
2398
|
+
const flushDeepSeekToolCallRecovererAtEnd = () => {
|
|
2399
|
+
const recoveredParts = deepSeekToolCallRecoverer?.flush();
|
|
2400
|
+
if (!recoveredParts) return;
|
|
2401
|
+
for (const recoveredPart of recoveredParts) {
|
|
2402
|
+
if (recoveredPart.kind === "toolCall") {
|
|
2403
|
+
appendRecoveredToolCall(recoveredPart);
|
|
2393
2404
|
continue;
|
|
2394
2405
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
text: detail.text
|
|
2398
|
-
});
|
|
2406
|
+
const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
|
|
2407
|
+
for (const part of parts) appendVisibleTextDelta(part);
|
|
2399
2408
|
}
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
if (
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
signature: field,
|
|
2411
|
-
text: value
|
|
2412
|
-
});
|
|
2413
|
-
break;
|
|
2409
|
+
};
|
|
2410
|
+
const flushDeepSeekTextFilterAtEnd = () => {
|
|
2411
|
+
const parts = deepSeekTextFilter?.flush();
|
|
2412
|
+
if (!parts) return;
|
|
2413
|
+
for (const part of parts) appendVisibleTextDelta(part);
|
|
2414
|
+
};
|
|
2415
|
+
const appendRoutedContentDelta = (delta) => {
|
|
2416
|
+
if (delta.kind === "text") {
|
|
2417
|
+
appendFilteredVisibleTextDelta(delta.text);
|
|
2418
|
+
return;
|
|
2414
2419
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
2419
|
-
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
2420
|
-
}
|
|
2421
|
-
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
2422
|
-
if (!model.reasoning) return false;
|
|
2423
|
-
const effort = resolveOpenAICompletionsReasoningEffort(options);
|
|
2424
|
-
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
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
|
|
2420
|
+
if (!emitReasoning) return;
|
|
2421
|
+
if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
|
|
2422
|
+
else appendThinkingDelta(delta);
|
|
2434
2423
|
};
|
|
2435
|
-
const
|
|
2436
|
-
|
|
2437
|
-
maxTokens: paramsMaxTokens,
|
|
2438
|
-
clampToModelMaxTokens: false
|
|
2424
|
+
const appendPartitionedVisibleDelta = (delta) => {
|
|
2425
|
+
if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
|
|
2439
2426
|
};
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2427
|
+
const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
|
|
2428
|
+
if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
|
|
2429
|
+
const latestBlock = output.content[output.content.length - 1];
|
|
2430
|
+
if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
|
|
2431
|
+
if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
|
|
2432
|
+
appendThinkingDelta({ text: "" });
|
|
2443
2433
|
};
|
|
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;
|
|
2434
|
+
const flushReasoningTagTextPartitionerAtEnd = () => {
|
|
2435
|
+
for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
|
|
2436
|
+
};
|
|
2437
|
+
const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
|
|
2438
|
+
const guardedStream = withFirstStreamEventTimeout(responseStream, {
|
|
2439
|
+
provider: model.provider,
|
|
2440
|
+
api: model.api,
|
|
2441
|
+
model: model.id,
|
|
2442
|
+
timeoutMs: options?.firstEventTimeoutMs ?? 0,
|
|
2443
|
+
stage: "completions",
|
|
2444
|
+
abort: options?.abortFirstEventStream,
|
|
2445
|
+
onTimeout: options?.onFirstEventTimeout,
|
|
2446
|
+
hint: "The provider may be stalled while parsing the tool payload; retry with a smaller tool surface or enable OPENCLAW_DEBUG_MODEL_PAYLOAD=tools to inspect exposed tools."
|
|
2447
|
+
});
|
|
2448
|
+
for await (const rawChunk of guardedStream) {
|
|
2449
|
+
throwIfModelStreamAborted(options?.signal);
|
|
2450
|
+
chunkPushedEvent = false;
|
|
2451
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
2452
|
+
await cooperativeScheduler.afterEvent();
|
|
2453
|
+
continue;
|
|
2477
2454
|
}
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2455
|
+
notifyLlmRequestActivity(options?.signal);
|
|
2456
|
+
const chunk = rawChunk;
|
|
2457
|
+
output.responseId ||= chunk.id;
|
|
2458
|
+
let hasReasoningUsageActivity = false;
|
|
2459
|
+
if (chunk.usage) {
|
|
2460
|
+
output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
|
|
2461
|
+
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
|
|
2462
|
+
}
|
|
2463
|
+
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
2464
|
+
if (!choice) {
|
|
2465
|
+
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2466
|
+
await cooperativeScheduler.afterEvent();
|
|
2490
2467
|
continue;
|
|
2491
2468
|
}
|
|
2492
|
-
const
|
|
2493
|
-
if (
|
|
2494
|
-
|
|
2469
|
+
const choiceUsage = choice.usage;
|
|
2470
|
+
if (!chunk.usage && choiceUsage) {
|
|
2471
|
+
output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
|
|
2472
|
+
hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
|
|
2473
|
+
}
|
|
2474
|
+
if (choice.finish_reason) {
|
|
2475
|
+
const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
|
|
2476
|
+
output.stopReason = finishReasonResult.stopReason;
|
|
2477
|
+
if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
|
|
2478
|
+
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
2479
|
+
}
|
|
2480
|
+
const rawChoiceDelta = choice.delta ?? choice.message;
|
|
2481
|
+
if (!rawChoiceDelta) {
|
|
2482
|
+
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2483
|
+
await cooperativeScheduler.afterEvent();
|
|
2495
2484
|
continue;
|
|
2496
2485
|
}
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2486
|
+
for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
|
|
2487
|
+
const choiceDelta = normalizedDelta.delta;
|
|
2488
|
+
const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
|
|
2489
|
+
const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
|
|
2490
|
+
if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
|
|
2491
|
+
const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningDeltas.filter((reasoningDelta) => reasoningDelta.kind === "thinking").map((reasoningDelta) => reasoningDelta.text));
|
|
2492
|
+
const appendReasoningDeltas = () => {
|
|
2493
|
+
for (const reasoningDelta of reasoningDeltas) {
|
|
2494
|
+
if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
|
|
2495
|
+
if (currentBlock?.type === "toolCall") {
|
|
2496
|
+
queuePostToolCallDelta({ ...reasoningDelta });
|
|
2497
|
+
continue;
|
|
2498
|
+
}
|
|
2499
|
+
if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
|
|
2500
|
+
else if (emitReasoning) appendThinkingDelta(reasoningDelta);
|
|
2501
|
+
}
|
|
2502
|
+
};
|
|
2503
|
+
if (hasMirroredReasoning) appendReasoningDeltas();
|
|
2504
|
+
for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
|
|
2505
|
+
const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
|
|
2506
|
+
for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
|
|
2507
|
+
} else {
|
|
2508
|
+
if (reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
|
|
2509
|
+
appendRoutedContentDelta(contentDelta);
|
|
2510
|
+
}
|
|
2511
|
+
if (!hasMirroredReasoning) appendReasoningDeltas();
|
|
2512
|
+
const toolCallDeltas = normalizedDelta.toolCalls;
|
|
2513
|
+
if (toolCallDeltas.length > 0) {
|
|
2514
|
+
sawNativeToolCallDelta = true;
|
|
2515
|
+
flushReasoningTagTextPartitionerAtEnd();
|
|
2516
|
+
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
2517
|
+
for (const toolCall of toolCallDeltas) {
|
|
2518
|
+
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
|
|
2519
|
+
let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
|
|
2520
|
+
if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
|
|
2521
|
+
if (!block) {
|
|
2522
|
+
if (currentBlock?.type === "toolCall") {
|
|
2523
|
+
currentBlock = null;
|
|
2524
|
+
flushPendingPostToolCallDeltas();
|
|
2525
|
+
}
|
|
2526
|
+
const initialSig = extractToolCallThoughtSignature(toolCall);
|
|
2527
|
+
block = {
|
|
2528
|
+
type: "toolCall",
|
|
2529
|
+
id: toolCall.id || "",
|
|
2530
|
+
name: toolCall.function?.name || "",
|
|
2531
|
+
arguments: {},
|
|
2532
|
+
partialArgs: "",
|
|
2533
|
+
...initialSig ? { thoughtSignature: initialSig } : {}
|
|
2534
|
+
};
|
|
2535
|
+
output.content.push(block);
|
|
2536
|
+
toolCallBlockIndices.set(block, output.content.length - 1);
|
|
2537
|
+
pushStreamEvent({
|
|
2538
|
+
type: "toolcall_start",
|
|
2539
|
+
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2540
|
+
partial: output
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
|
|
2544
|
+
if (toolCall.id) {
|
|
2545
|
+
block.id = toolCall.id;
|
|
2546
|
+
toolCallBlocksById.set(toolCall.id, block);
|
|
2547
|
+
}
|
|
2548
|
+
currentBlock = block;
|
|
2549
|
+
if (toolCall.function?.name) block.name = toolCall.function.name;
|
|
2550
|
+
const deltaSig = extractToolCallThoughtSignature(toolCall);
|
|
2551
|
+
if (deltaSig) block.thoughtSignature = deltaSig;
|
|
2552
|
+
if (toolCall.function?.arguments) {
|
|
2553
|
+
block.partialArgs += toolCall.function.arguments;
|
|
2554
|
+
block.arguments = parseStreamingJson(block.partialArgs);
|
|
2555
|
+
pushStreamEvent({
|
|
2556
|
+
type: "toolcall_delta",
|
|
2557
|
+
contentIndex: toolCallBlockIndices.get(block) ?? -1,
|
|
2558
|
+
delta: toolCall.function.arguments,
|
|
2559
|
+
partial: output
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2501
2564
|
}
|
|
2565
|
+
flushPendingPostToolCallDeltas();
|
|
2566
|
+
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
2567
|
+
await cooperativeScheduler.afterEvent();
|
|
2502
2568
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
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
|
|
2569
|
+
flushReasoningTagTextPartitionerAtEnd();
|
|
2570
|
+
flushDeepSeekToolCallRecovererAtEnd();
|
|
2571
|
+
flushDeepSeekTextFilterAtEnd();
|
|
2572
|
+
currentBlock = null;
|
|
2573
|
+
flushPendingPostToolCallDeltas();
|
|
2574
|
+
finalizeOpenAICompletionsToolCalls(output, {
|
|
2575
|
+
allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
|
|
2576
|
+
onConfirmedToolCall(block, contentIndex) {
|
|
2577
|
+
pushStreamEvent({
|
|
2578
|
+
type: "toolcall_end",
|
|
2579
|
+
contentIndex,
|
|
2580
|
+
toolCall: block,
|
|
2581
|
+
partial: output
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2540
2584
|
});
|
|
2541
|
-
|
|
2542
|
-
|
|
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;
|
|
2585
|
+
if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
|
|
2586
|
+
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
2576
2587
|
}
|
|
2577
|
-
function
|
|
2578
|
-
|
|
2579
|
-
const
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
if (
|
|
2587
|
-
|
|
2588
|
-
|
|
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);
|
|
2588
|
+
function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
|
|
2589
|
+
const output = [];
|
|
2590
|
+
const pushDelta = (next) => {
|
|
2591
|
+
const previous = output[output.length - 1];
|
|
2592
|
+
if (!previous || previous.kind !== next.kind) {
|
|
2593
|
+
output.push(next);
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
if (next.kind === "thinking" && previous.kind === "thinking") {
|
|
2597
|
+
if (previous.signature !== next.signature) {
|
|
2598
|
+
output.push(next);
|
|
2599
|
+
return;
|
|
2593
2600
|
}
|
|
2601
|
+
previous.text += next.text;
|
|
2602
|
+
return;
|
|
2594
2603
|
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
if (
|
|
2605
|
-
|
|
2604
|
+
previous.text += next.text;
|
|
2605
|
+
};
|
|
2606
|
+
const reasoningDetails = delta.reasoning_details;
|
|
2607
|
+
let usedReasoningThinkingDetails = false;
|
|
2608
|
+
if (Array.isArray(reasoningDetails)) {
|
|
2609
|
+
const visibleTypes = new Set(visibleReasoningDetailTypes);
|
|
2610
|
+
for (const item of reasoningDetails) {
|
|
2611
|
+
const detail = item;
|
|
2612
|
+
if (typeof detail.text !== "string" || !detail.text) continue;
|
|
2613
|
+
if (detail.type === "reasoning.text") {
|
|
2614
|
+
usedReasoningThinkingDetails = true;
|
|
2615
|
+
pushDelta({
|
|
2616
|
+
kind: "thinking",
|
|
2617
|
+
signature: "reasoning_details",
|
|
2618
|
+
text: detail.text
|
|
2619
|
+
});
|
|
2620
|
+
continue;
|
|
2606
2621
|
}
|
|
2607
|
-
if (typeof
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
extra.google = google;
|
|
2612
|
-
google.thought_signature = sig;
|
|
2622
|
+
if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
|
|
2623
|
+
kind: "text",
|
|
2624
|
+
text: detail.text
|
|
2625
|
+
});
|
|
2613
2626
|
}
|
|
2614
2627
|
}
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
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);
|
|
2628
|
+
if (!usedReasoningThinkingDetails) for (const field of [
|
|
2629
|
+
"reasoning_content",
|
|
2630
|
+
"reasoning",
|
|
2631
|
+
"reasoning_text"
|
|
2632
|
+
]) {
|
|
2633
|
+
const value = delta[field];
|
|
2634
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2635
|
+
pushDelta({
|
|
2636
|
+
kind: "thinking",
|
|
2637
|
+
signature: field,
|
|
2638
|
+
text: value
|
|
2639
|
+
});
|
|
2640
|
+
break;
|
|
2641
|
+
}
|
|
2684
2642
|
}
|
|
2685
|
-
return
|
|
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));
|
|
2643
|
+
return output;
|
|
2690
2644
|
}
|
|
2691
|
-
function
|
|
2692
|
-
|
|
2693
|
-
const normalizedModelId = model.id.trim().toLowerCase();
|
|
2694
|
-
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
2645
|
+
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
2646
|
+
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
2695
2647
|
}
|
|
2696
|
-
function
|
|
2648
|
+
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
2697
2649
|
if (!model.reasoning) return false;
|
|
2698
|
-
|
|
2699
|
-
|
|
2650
|
+
const effort = resolveOpenAICompletionsReasoningEffort(options);
|
|
2651
|
+
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
2652
|
+
return true;
|
|
2700
2653
|
}
|
|
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
|
-
}
|
|
2654
|
+
function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
2655
|
+
const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
|
|
2656
|
+
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
2711
2657
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
stream: true
|
|
2658
|
+
//#endregion
|
|
2659
|
+
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
2660
|
+
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
2661
|
+
const messages = params.messages;
|
|
2662
|
+
if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
|
|
2663
|
+
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.`);
|
|
2664
|
+
}
|
|
2665
|
+
const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
|
|
2666
|
+
const SSE_DONE_MAX_LINE_CHARS = 1024;
|
|
2667
|
+
function createSseDoneDetector() {
|
|
2668
|
+
const decoder = new TextDecoder();
|
|
2669
|
+
let line = "";
|
|
2670
|
+
let lineOverflowed = false;
|
|
2671
|
+
let sawDone = false;
|
|
2672
|
+
const finishLine = () => {
|
|
2673
|
+
if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
|
|
2674
|
+
line = "";
|
|
2675
|
+
lineOverflowed = false;
|
|
2731
2676
|
};
|
|
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;
|
|
2677
|
+
const observeText = (text) => {
|
|
2678
|
+
for (const char of text) {
|
|
2679
|
+
if (char === "\n" || char === "\r") {
|
|
2680
|
+
finishLine();
|
|
2681
|
+
continue;
|
|
2682
|
+
}
|
|
2683
|
+
if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
|
|
2684
|
+
else lineOverflowed = true;
|
|
2763
2685
|
}
|
|
2764
|
-
}
|
|
2765
|
-
{
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2686
|
+
};
|
|
2687
|
+
return {
|
|
2688
|
+
observe(chunk) {
|
|
2689
|
+
if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
|
|
2690
|
+
},
|
|
2691
|
+
finish() {
|
|
2692
|
+
if (sawDone) return;
|
|
2693
|
+
observeText(decoder.decode());
|
|
2694
|
+
if (line || lineOverflowed) finishLine();
|
|
2695
|
+
},
|
|
2696
|
+
sawDone: () => sawDone
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
|
|
2700
|
+
const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
|
|
2701
|
+
return new OpenAI({
|
|
2702
|
+
apiKey,
|
|
2703
|
+
baseURL: clientConfig.baseURL,
|
|
2704
|
+
dangerouslyAllowBrowser: true,
|
|
2705
|
+
defaultHeaders: clientConfig.defaultHeaders,
|
|
2706
|
+
defaultQuery: clientConfig.defaultQuery,
|
|
2707
|
+
fetch: opts?.fetch ?? buildGuardedModelFetch(model),
|
|
2708
|
+
...buildOpenAISdkClientOptions(model)
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
|
|
2712
|
+
const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
|
|
2713
|
+
const defaultQuery = {};
|
|
2714
|
+
let baseURL = model.baseUrl;
|
|
2715
|
+
let isAzureHost = false;
|
|
2716
|
+
try {
|
|
2717
|
+
const parsed = new URL(model.baseUrl);
|
|
2718
|
+
isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
|
|
2719
|
+
parsed.searchParams.forEach((value, key) => {
|
|
2720
|
+
if (value) defaultQuery[key] = value;
|
|
2721
|
+
});
|
|
2722
|
+
parsed.search = "";
|
|
2723
|
+
baseURL = parsed.toString().replace(/\/$/, "");
|
|
2724
|
+
} catch {}
|
|
2725
|
+
if (isAzureHost) {
|
|
2726
|
+
const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
|
|
2727
|
+
if (apiVersionHeader) {
|
|
2728
|
+
const apiVersion = headers[apiVersionHeader]?.trim();
|
|
2729
|
+
delete headers[apiVersionHeader];
|
|
2730
|
+
if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
|
|
2774
2731
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2732
|
+
}
|
|
2733
|
+
return {
|
|
2734
|
+
baseURL,
|
|
2735
|
+
defaultHeaders: headers,
|
|
2736
|
+
defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
function createOpenAICompletionsTransportStreamFn() {
|
|
2740
|
+
return (model, context, options) => {
|
|
2741
|
+
const eventStream = createAssistantMessageEventStream();
|
|
2742
|
+
const stream = eventStream;
|
|
2743
|
+
(async () => {
|
|
2744
|
+
const output = {
|
|
2745
|
+
role: "assistant",
|
|
2746
|
+
content: [],
|
|
2747
|
+
api: model.api,
|
|
2748
|
+
provider: model.provider,
|
|
2749
|
+
model: model.id,
|
|
2750
|
+
usage: {
|
|
2751
|
+
input: 0,
|
|
2752
|
+
output: 0,
|
|
2753
|
+
cacheRead: 0,
|
|
2754
|
+
cacheWrite: 0,
|
|
2755
|
+
totalTokens: 0,
|
|
2756
|
+
cost: {
|
|
2757
|
+
input: 0,
|
|
2758
|
+
output: 0,
|
|
2759
|
+
cacheRead: 0,
|
|
2760
|
+
cacheWrite: 0,
|
|
2761
|
+
total: 0
|
|
2762
|
+
}
|
|
2763
|
+
},
|
|
2764
|
+
stopReason: "stop",
|
|
2765
|
+
timestamp: Date.now()
|
|
2766
|
+
};
|
|
2767
|
+
let firstEventAbort;
|
|
2768
|
+
try {
|
|
2769
|
+
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
2770
|
+
const doneDetector = createSseDoneDetector();
|
|
2771
|
+
const baseFetch = buildGuardedModelFetch(model);
|
|
2772
|
+
const doneDetectingFetch = async (url, init) => {
|
|
2773
|
+
const response = await baseFetch(url, init);
|
|
2774
|
+
if (!response.body || !response.ok) return response;
|
|
2775
|
+
if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
|
|
2776
|
+
const transformed = response.body.pipeThrough(new TransformStream({
|
|
2777
|
+
transform(chunk, controller) {
|
|
2778
|
+
doneDetector.observe(chunk);
|
|
2779
|
+
controller.enqueue(chunk);
|
|
2780
|
+
},
|
|
2781
|
+
flush() {
|
|
2782
|
+
doneDetector.finish();
|
|
2783
|
+
}
|
|
2784
|
+
}));
|
|
2785
|
+
return new Response(transformed, {
|
|
2786
|
+
headers: response.headers,
|
|
2787
|
+
status: response.status,
|
|
2788
|
+
statusText: response.statusText
|
|
2789
|
+
});
|
|
2790
|
+
};
|
|
2791
|
+
const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
|
|
2792
|
+
let params = buildOpenAICompletionsParams(model, context, options);
|
|
2793
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
2794
|
+
if (nextParams !== void 0) params = nextParams;
|
|
2795
|
+
if (options?.openclawCodeModeToolSurface === true) {
|
|
2796
|
+
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
2797
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
2798
|
+
assertCodeModeResponsesToolSurface(params, visibleToolNames);
|
|
2799
|
+
}
|
|
2800
|
+
if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
|
|
2801
|
+
const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
|
|
2802
|
+
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
2803
|
+
const { data: responseStream, response } = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
|
|
2804
|
+
timeoutMs: options?.timeoutMs,
|
|
2805
|
+
maxRetries: options?.maxRetries
|
|
2806
|
+
})).withResponse();
|
|
2807
|
+
await processCompletionsStream(withProviderResponseHook({
|
|
2808
|
+
stream: responseStream,
|
|
2809
|
+
signal: firstEventAbort.signal,
|
|
2810
|
+
abort: firstEventAbort.abort,
|
|
2811
|
+
hook: createOpenAIResponseHook(options?.onResponse, response, model),
|
|
2812
|
+
onReady: () => stream.push({
|
|
2813
|
+
type: "start",
|
|
2814
|
+
partial: output
|
|
2815
|
+
})
|
|
2816
|
+
}), output, model, stream, {
|
|
2817
|
+
signal: options?.signal,
|
|
2818
|
+
emitReasoning,
|
|
2819
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
2820
|
+
abortFirstEventStream: firstEventAbort.abort,
|
|
2821
|
+
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
2822
|
+
sawStreamDONE: doneDetector.sawDone
|
|
2823
|
+
});
|
|
2824
|
+
finalizeTransportStream({
|
|
2825
|
+
stream,
|
|
2826
|
+
output,
|
|
2827
|
+
signal: options?.signal
|
|
2828
|
+
});
|
|
2829
|
+
} catch (error) {
|
|
2830
|
+
failTransportStream({
|
|
2831
|
+
stream,
|
|
2832
|
+
output,
|
|
2833
|
+
signal: options?.signal,
|
|
2834
|
+
error,
|
|
2835
|
+
cleanup: () => {
|
|
2836
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
2837
|
+
finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
} finally {
|
|
2841
|
+
firstEventAbort?.dispose();
|
|
2781
2842
|
}
|
|
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;
|
|
2843
|
+
})();
|
|
2844
|
+
return eventStream;
|
|
2845
|
+
};
|
|
2814
2846
|
}
|
|
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
2847
|
//#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;
|
|
2848
|
+
//#region packages/ai/src/transports/openai-responses-compact-request.ts
|
|
2849
|
+
const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
|
|
2850
|
+
function claimResponsesCompactRequest(options) {
|
|
2851
|
+
const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
|
|
2852
|
+
if (controller?.claimed === false) {
|
|
2853
|
+
controller.claimed = true;
|
|
2854
|
+
return controller;
|
|
2873
2855
|
}
|
|
2874
2856
|
}
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2857
|
+
/** Run a compact-endpoint request through the session's prepared stream stack. */
|
|
2858
|
+
async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
|
|
2859
|
+
const preparedOptions = { ...options };
|
|
2860
|
+
let resolveResult;
|
|
2861
|
+
let rejectResult;
|
|
2862
|
+
const result = new Promise((resolve, reject) => {
|
|
2863
|
+
resolveResult = resolve;
|
|
2864
|
+
rejectResult = reject;
|
|
2865
|
+
});
|
|
2866
|
+
const controller = {
|
|
2867
|
+
claimed: false,
|
|
2868
|
+
resolve: resolveResult,
|
|
2869
|
+
reject: rejectResult
|
|
2870
|
+
};
|
|
2871
|
+
Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
|
|
2872
|
+
const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
|
|
2873
|
+
if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
|
|
2878
2874
|
try {
|
|
2879
|
-
return
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
return new URL(`https://${trimmed}`).hostname.toLowerCase();
|
|
2883
|
-
} catch {
|
|
2884
|
-
return;
|
|
2885
|
-
}
|
|
2875
|
+
return await result;
|
|
2876
|
+
} finally {
|
|
2877
|
+
await stream.result().catch(() => void 0);
|
|
2886
2878
|
}
|
|
2887
2879
|
}
|
|
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;
|
|
2880
|
+
//#endregion
|
|
2881
|
+
//#region packages/ai/src/transports/openai-responses-continuation.ts
|
|
2882
|
+
const HTTP_CONTINUATION_IDLE_TTL_MS = 300 * 1e3;
|
|
2883
|
+
const TURN_HEADERS = /* @__PURE__ */ new Set([
|
|
2884
|
+
"traceparent",
|
|
2885
|
+
"x-openclaw-turn-id",
|
|
2886
|
+
"x-openclaw-turn-attempt"
|
|
2887
|
+
]);
|
|
2888
|
+
function jsonValuesEqual(left, right) {
|
|
2889
|
+
return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
|
|
2890
|
+
}
|
|
2891
|
+
function requestWithoutInput(request) {
|
|
2892
|
+
const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
|
|
2893
|
+
if (!isRecord(rest.metadata)) return rest;
|
|
2894
|
+
const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
|
|
2945
2895
|
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;
|
|
2896
|
+
...rest,
|
|
2897
|
+
metadata
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
function normalizeAssistantReplayInput(input) {
|
|
2901
|
+
return input.map((item) => {
|
|
2902
|
+
if (!isRecord(item)) return item;
|
|
2903
|
+
if (item.type === "reasoning") return { type: "reasoning" };
|
|
2904
|
+
if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
|
|
2905
|
+
const { id: _id, status: _status, ...stableItem } = item;
|
|
2906
|
+
if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
|
|
2907
|
+
if (!isRecord(part) || part.type !== "output_text") return part;
|
|
2908
|
+
const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
|
|
2909
|
+
return stablePart;
|
|
2910
|
+
});
|
|
2911
|
+
return stableItem;
|
|
2912
|
+
});
|
|
2913
|
+
}
|
|
2914
|
+
function resolveResponsesContinuationRequest(continuation, request) {
|
|
2915
|
+
if (!continuation) return {
|
|
2916
|
+
request,
|
|
2917
|
+
continuationStatus: "no_previous_response"
|
|
2918
|
+
};
|
|
2919
|
+
if (request.previous_response_id) return {
|
|
2920
|
+
request,
|
|
2921
|
+
continuationStatus: "explicit_previous_response_id"
|
|
2922
|
+
};
|
|
2923
|
+
if (!jsonValuesEqual(requestWithoutInput(request), requestWithoutInput(continuation.lastRequest))) return {
|
|
2924
|
+
request,
|
|
2925
|
+
continuationStatus: "request_changed"
|
|
2926
|
+
};
|
|
2927
|
+
const currentInput = request.input ?? [];
|
|
2928
|
+
const previousInput = continuation.lastRequest.input ?? [];
|
|
2929
|
+
const baselineLength = previousInput.length + continuation.lastResponseItems.length;
|
|
2930
|
+
if (currentInput.length < baselineLength) return {
|
|
2931
|
+
request,
|
|
2932
|
+
continuationStatus: "history_shorter"
|
|
2933
|
+
};
|
|
2934
|
+
if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems))) return {
|
|
2935
|
+
request,
|
|
2936
|
+
continuationStatus: "history_changed"
|
|
2937
|
+
};
|
|
2991
2938
|
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
|
-
|
|
2939
|
+
request: {
|
|
2940
|
+
...request,
|
|
2941
|
+
previous_response_id: continuation.lastResponseId,
|
|
2942
|
+
input: currentInput.slice(baselineLength)
|
|
2943
|
+
},
|
|
2944
|
+
continuationStatus: "continued"
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
const httpContinuationEntries = /* @__PURE__ */ new Map();
|
|
2948
|
+
let nextHttpContinuationGeneration = 1;
|
|
2949
|
+
function connectionIdentity(params) {
|
|
2950
|
+
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));
|
|
2951
|
+
return sha256Hex(JSON.stringify([
|
|
2952
|
+
getAiTransportHost().resolveSecretSentinel(params.apiKey),
|
|
2953
|
+
params.baseUrl,
|
|
2954
|
+
headers
|
|
2955
|
+
]));
|
|
2956
|
+
}
|
|
2957
|
+
function claimOpenAIResponsesHttpContinuation(params) {
|
|
2958
|
+
const key = `${params.sessionId}\0${connectionIdentity(params)}`;
|
|
2959
|
+
const previous = httpContinuationEntries.get(key);
|
|
2960
|
+
if (previous?.kind === "claimed") return;
|
|
2961
|
+
if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
|
|
2962
|
+
const generation = nextHttpContinuationGeneration++;
|
|
2963
|
+
const claimed = {
|
|
2964
|
+
kind: "claimed",
|
|
2965
|
+
sessionId: params.sessionId,
|
|
2966
|
+
generation
|
|
2967
|
+
};
|
|
2968
|
+
httpContinuationEntries.set(key, claimed);
|
|
2969
|
+
return {
|
|
2970
|
+
request: resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, params.request).request,
|
|
2971
|
+
commit: (effectiveRequest, response) => {
|
|
2972
|
+
if (httpContinuationEntries.get(key) !== claimed) return;
|
|
2973
|
+
const idleTimer = setTimeout(() => {
|
|
2974
|
+
const current = httpContinuationEntries.get(key);
|
|
2975
|
+
if (current?.kind === "ready" && current.generation === generation) httpContinuationEntries.delete(key);
|
|
2976
|
+
}, HTTP_CONTINUATION_IDLE_TTL_MS);
|
|
2977
|
+
idleTimer.unref?.();
|
|
2978
|
+
const ready = {
|
|
2979
|
+
...claimed,
|
|
2980
|
+
kind: "ready",
|
|
2981
|
+
state: {
|
|
2982
|
+
lastRequest: effectiveRequest,
|
|
2983
|
+
lastResponseId: response.id,
|
|
2984
|
+
lastResponseItems: response.output
|
|
2985
|
+
},
|
|
2986
|
+
idleTimer
|
|
2987
|
+
};
|
|
2988
|
+
httpContinuationEntries.set(key, ready);
|
|
2989
|
+
},
|
|
2990
|
+
release: () => {
|
|
2991
|
+
if (httpContinuationEntries.get(key) === claimed) httpContinuationEntries.delete(key);
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
3016
2994
|
}
|
|
2995
|
+
registerSessionResourceCleanup((sessionId) => {
|
|
2996
|
+
for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
|
|
2997
|
+
if (entry.kind === "ready") clearTimeout(entry.idleTimer);
|
|
2998
|
+
httpContinuationEntries.delete(key);
|
|
2999
|
+
}
|
|
3000
|
+
});
|
|
3017
3001
|
//#endregion
|
|
3018
3002
|
//#region packages/ai/src/transports/openai-responses-params-internal.ts
|
|
3003
|
+
const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
|
|
3004
|
+
"openai",
|
|
3005
|
+
"opencode",
|
|
3006
|
+
"azure-openai-responses",
|
|
3007
|
+
"github-copilot"
|
|
3008
|
+
]);
|
|
3019
3009
|
function convertResponsesTools(tools, model, options) {
|
|
3020
3010
|
const projection = projectOpenAITools(tools);
|
|
3021
3011
|
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
|
|
@@ -3114,7 +3104,7 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
|
3114
3104
|
};
|
|
3115
3105
|
return responseFormat;
|
|
3116
3106
|
}
|
|
3117
|
-
function
|
|
3107
|
+
function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
|
|
3118
3108
|
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3119
3109
|
const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
|
|
3120
3110
|
const compat = getCompat(model);
|
|
@@ -3122,19 +3112,20 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
|
|
|
3122
3112
|
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3123
3113
|
const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
|
|
3124
3114
|
const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
|
|
3125
|
-
|
|
3126
|
-
"openai",
|
|
3127
|
-
"opencode",
|
|
3128
|
-
"azure-openai-responses",
|
|
3129
|
-
"github-copilot"
|
|
3130
|
-
]), {
|
|
3115
|
+
return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
|
|
3131
3116
|
includeSystemPrompt: !isCodexResponses,
|
|
3132
3117
|
supportsDeveloperRole,
|
|
3133
3118
|
replayReasoningItems: true,
|
|
3134
3119
|
replayResponsesItemIds,
|
|
3135
3120
|
authProfileId: options?.authProfileId,
|
|
3136
|
-
sessionId: options?.sessionId
|
|
3121
|
+
sessionId: options?.sessionId,
|
|
3122
|
+
replayMode
|
|
3137
3123
|
});
|
|
3124
|
+
}
|
|
3125
|
+
function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
|
|
3126
|
+
const isCodexResponses = isOpenAICodexResponsesModel(model);
|
|
3127
|
+
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
3128
|
+
const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
|
|
3138
3129
|
if (isCodexResponses) ensureOpenAICodexResponsesInput(messages, context);
|
|
3139
3130
|
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
3140
3131
|
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
@@ -3194,6 +3185,262 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
|
|
|
3194
3185
|
return sanitizeOpenAICodexResponsesParams(model, params);
|
|
3195
3186
|
}
|
|
3196
3187
|
//#endregion
|
|
3188
|
+
//#region packages/ai/src/transports/openai-responses-websocket.ts
|
|
3189
|
+
const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
|
|
3190
|
+
const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
|
|
3191
|
+
const WEBSOCKET_OPEN_STATE = 1;
|
|
3192
|
+
const websocketSessionCache = /* @__PURE__ */ new Map();
|
|
3193
|
+
const degradedWebSocketConnections = /* @__PURE__ */ new Map();
|
|
3194
|
+
function isOfficialOpenAIResponsesBaseUrl(baseUrl) {
|
|
3195
|
+
if (!baseUrl) return false;
|
|
3196
|
+
try {
|
|
3197
|
+
const url = new URL(baseUrl);
|
|
3198
|
+
return url.origin === "https://api.openai.com" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname.replace(/\/+$/, "") === "/v1";
|
|
3199
|
+
} catch {
|
|
3200
|
+
return false;
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
function supportsNativeOpenAIResponsesEndpoint(params) {
|
|
3204
|
+
return params.provider.trim().toLowerCase() === "openai" && params.api === "openai-responses" && isOfficialOpenAIResponsesBaseUrl(params.baseUrl);
|
|
3205
|
+
}
|
|
3206
|
+
function closeWebSocketSilently(socket, reason = "done") {
|
|
3207
|
+
try {
|
|
3208
|
+
socket.close({
|
|
3209
|
+
code: 1e3,
|
|
3210
|
+
reason
|
|
3211
|
+
});
|
|
3212
|
+
} catch {}
|
|
3213
|
+
}
|
|
3214
|
+
function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
|
|
3215
|
+
if (entry.idleTimer) {
|
|
3216
|
+
clearTimeout(entry.idleTimer);
|
|
3217
|
+
entry.idleTimer = void 0;
|
|
3218
|
+
}
|
|
3219
|
+
closeWebSocketSilently(entry.socket, reason);
|
|
3220
|
+
if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
|
|
3221
|
+
}
|
|
3222
|
+
function scheduleSessionWebSocketExpiry(cacheKey, entry) {
|
|
3223
|
+
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
3224
|
+
entry.idleTimer = setTimeout(() => {
|
|
3225
|
+
if (entry.busy) return;
|
|
3226
|
+
invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
|
|
3227
|
+
}, SESSION_WEBSOCKET_CACHE_TTL_MS);
|
|
3228
|
+
entry.idleTimer.unref?.();
|
|
3229
|
+
}
|
|
3230
|
+
function prepareWebSocketConnection(client, headers) {
|
|
3231
|
+
if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
|
|
3232
|
+
if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
|
|
3233
|
+
const resolvedApiKey = getAiTransportHost().resolveSecretSentinel(client.apiKey);
|
|
3234
|
+
const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
|
|
3235
|
+
for (const key of Object.keys(resolvedHeaders)) {
|
|
3236
|
+
const normalizedKey = key.toLowerCase();
|
|
3237
|
+
if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
|
|
3238
|
+
}
|
|
3239
|
+
if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
|
|
3240
|
+
return {
|
|
3241
|
+
client: client.withOptions({ apiKey: resolvedApiKey }),
|
|
3242
|
+
headers: resolvedHeaders,
|
|
3243
|
+
identity: sha256Hex(JSON.stringify([
|
|
3244
|
+
resolvedApiKey,
|
|
3245
|
+
client.baseURL,
|
|
3246
|
+
Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
|
|
3247
|
+
]))
|
|
3248
|
+
};
|
|
3249
|
+
}
|
|
3250
|
+
function createWebSocket(connection, onError) {
|
|
3251
|
+
const socket = new ResponsesWS(connection.client, {
|
|
3252
|
+
headers: connection.headers,
|
|
3253
|
+
maxQueueSize: 1
|
|
3254
|
+
});
|
|
3255
|
+
socket.on("error", () => onError(socket));
|
|
3256
|
+
return socket;
|
|
3257
|
+
}
|
|
3258
|
+
function createTransientWebSocketLease(connection) {
|
|
3259
|
+
const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
|
|
3260
|
+
return {
|
|
3261
|
+
socket,
|
|
3262
|
+
iterator: socket.stream(),
|
|
3263
|
+
reusedConnection: false,
|
|
3264
|
+
release: () => closeWebSocketSilently(socket)
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
|
|
3268
|
+
entry.busy = true;
|
|
3269
|
+
return {
|
|
3270
|
+
socket: entry.socket,
|
|
3271
|
+
iterator: entry.socket.stream(),
|
|
3272
|
+
entry,
|
|
3273
|
+
reusedConnection,
|
|
3274
|
+
release: ({ keep } = {}) => {
|
|
3275
|
+
if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
|
|
3276
|
+
invalidateOwnedWebSocketSession(cacheKey, entry);
|
|
3277
|
+
return;
|
|
3278
|
+
}
|
|
3279
|
+
entry.busy = false;
|
|
3280
|
+
scheduleSessionWebSocketExpiry(cacheKey, entry);
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
function acquireWebSocket(params, connection) {
|
|
3285
|
+
if (!(params.mode !== "websocket" && Boolean(params.sessionId)) || !params.sessionId) return createTransientWebSocketLease(connection);
|
|
3286
|
+
const cacheKey = `${params.sessionId}\0${connection.identity}`;
|
|
3287
|
+
const cached = websocketSessionCache.get(cacheKey);
|
|
3288
|
+
if (cached) {
|
|
3289
|
+
if (cached.idleTimer) {
|
|
3290
|
+
clearTimeout(cached.idleTimer);
|
|
3291
|
+
cached.idleTimer = void 0;
|
|
3292
|
+
}
|
|
3293
|
+
if (cached.busy) return createTransientWebSocketLease(connection);
|
|
3294
|
+
const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
|
|
3295
|
+
if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
|
|
3296
|
+
invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
|
|
3297
|
+
}
|
|
3298
|
+
const entry = {
|
|
3299
|
+
socket: createWebSocket(connection, (failedSocket) => {
|
|
3300
|
+
const failedEntry = websocketSessionCache.get(cacheKey);
|
|
3301
|
+
if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
|
|
3302
|
+
else closeWebSocketSilently(failedSocket, "transport_error");
|
|
3303
|
+
}),
|
|
3304
|
+
sessionId: params.sessionId,
|
|
3305
|
+
busy: true,
|
|
3306
|
+
createdAt: Date.now()
|
|
3307
|
+
};
|
|
3308
|
+
websocketSessionCache.set(cacheKey, entry);
|
|
3309
|
+
return createCachedWebSocketLease(cacheKey, entry, false);
|
|
3310
|
+
}
|
|
3311
|
+
function sanitizeWebSocketRequest(request) {
|
|
3312
|
+
const { stream: _stream, background: _background, ...websocketRequest } = request;
|
|
3313
|
+
return websocketRequest;
|
|
3314
|
+
}
|
|
3315
|
+
async function nextWebSocketMessage(iterator, signal) {
|
|
3316
|
+
if (!signal) return iterator.next();
|
|
3317
|
+
if (signal.aborted) throw transportAbortError(signal);
|
|
3318
|
+
let onAbort;
|
|
3319
|
+
try {
|
|
3320
|
+
return await Promise.race([iterator.next(), new Promise((_resolve, reject) => {
|
|
3321
|
+
onAbort = () => reject(transportAbortError(signal));
|
|
3322
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3323
|
+
})]);
|
|
3324
|
+
} finally {
|
|
3325
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
function readServerEvent(message) {
|
|
3329
|
+
if (message.type === "message") return message.message;
|
|
3330
|
+
if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
|
|
3331
|
+
if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
|
|
3332
|
+
}
|
|
3333
|
+
function createOpenAIResponsesWebSocketStream(params) {
|
|
3334
|
+
const connection = prepareWebSocketConnection(params.client, params.headers);
|
|
3335
|
+
const fullRequest = sanitizeWebSocketRequest(params.request);
|
|
3336
|
+
const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
|
|
3337
|
+
const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
|
|
3338
|
+
const degraded = degradedWebSocketConnections.get(degradationKey);
|
|
3339
|
+
if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
|
|
3340
|
+
degradedWebSocketConnections.delete(degradationKey);
|
|
3341
|
+
const markDegraded = () => {
|
|
3342
|
+
const cooldownMs = params.degradeCooldownMs;
|
|
3343
|
+
if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
|
|
3344
|
+
degradedWebSocketConnections.set(degradationKey, {
|
|
3345
|
+
sessionId: params.sessionId,
|
|
3346
|
+
retryAt: Date.now() + cooldownMs
|
|
3347
|
+
});
|
|
3348
|
+
};
|
|
3349
|
+
let lease;
|
|
3350
|
+
try {
|
|
3351
|
+
lease = acquireWebSocket(params, connection);
|
|
3352
|
+
} catch (error) {
|
|
3353
|
+
markDegraded();
|
|
3354
|
+
throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
3355
|
+
}
|
|
3356
|
+
let prepared;
|
|
3357
|
+
try {
|
|
3358
|
+
const continuation = lease.entry?.continuation;
|
|
3359
|
+
if (continuation && lease.entry) {
|
|
3360
|
+
lease.entry.continuation = void 0;
|
|
3361
|
+
prepared = resolveResponsesContinuationRequest(continuation, fullRequest);
|
|
3362
|
+
} else prepared = {
|
|
3363
|
+
request: fullRequest,
|
|
3364
|
+
continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
|
|
3365
|
+
};
|
|
3366
|
+
} catch (error) {
|
|
3367
|
+
lease.iterator.return?.().catch(() => void 0);
|
|
3368
|
+
lease.release({ keep: false });
|
|
3369
|
+
throw error;
|
|
3370
|
+
}
|
|
3371
|
+
let streamStarted = false;
|
|
3372
|
+
let terminalResponse;
|
|
3373
|
+
let terminalReceived = false;
|
|
3374
|
+
let released = false;
|
|
3375
|
+
const finish = ({ keep = true } = {}) => {
|
|
3376
|
+
if (released) return;
|
|
3377
|
+
released = true;
|
|
3378
|
+
if (keep && lease.entry && terminalResponse) lease.entry.continuation = {
|
|
3379
|
+
lastRequest: fullRequest,
|
|
3380
|
+
lastResponseId: terminalResponse.id,
|
|
3381
|
+
lastResponseItems: terminalResponse.output
|
|
3382
|
+
};
|
|
3383
|
+
lease.release({ keep });
|
|
3384
|
+
};
|
|
3385
|
+
return {
|
|
3386
|
+
stream: { async *[Symbol.asyncIterator]() {
|
|
3387
|
+
if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
|
|
3388
|
+
streamStarted = true;
|
|
3389
|
+
const iterator = lease.iterator;
|
|
3390
|
+
let requestDispatched = false;
|
|
3391
|
+
try {
|
|
3392
|
+
if (params.signal?.aborted) throw transportAbortError(params.signal);
|
|
3393
|
+
for (;;) {
|
|
3394
|
+
const next = await nextWebSocketMessage(iterator, params.signal);
|
|
3395
|
+
if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
|
|
3396
|
+
if (next.value.type === "open") {
|
|
3397
|
+
if (!requestDispatched) {
|
|
3398
|
+
requestDispatched = true;
|
|
3399
|
+
lease.socket.send({
|
|
3400
|
+
...prepared.request,
|
|
3401
|
+
type: "response.create"
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3406
|
+
const event = readServerEvent(next.value);
|
|
3407
|
+
if (!event) continue;
|
|
3408
|
+
if (event.type === "response.failed") throw new OpenAIResponsesWebSocketResponseFailedError(event.response.output.length > 0);
|
|
3409
|
+
if (event.type === "response.completed") terminalResponse = event.response;
|
|
3410
|
+
terminalReceived = event.type === "response.completed" || event.type === "response.incomplete";
|
|
3411
|
+
yield event;
|
|
3412
|
+
if (terminalReceived) {
|
|
3413
|
+
degradedWebSocketConnections.delete(degradationKey);
|
|
3414
|
+
return;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
} catch (error) {
|
|
3418
|
+
if (lease.entry) lease.entry.continuation = void 0;
|
|
3419
|
+
const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
|
|
3420
|
+
if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
|
|
3421
|
+
if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
3422
|
+
if (!requestDispatched || params.callerSignal?.aborted || error instanceof OpenAIResponsesWebSocketResponseFailedError || safeRetry) throw error;
|
|
3423
|
+
throw new OpenAIResponsesWebSocketPostDispatchError(error);
|
|
3424
|
+
} finally {
|
|
3425
|
+
await iterator.return?.().catch(() => void 0);
|
|
3426
|
+
if (!terminalReceived) finish({ keep: false });
|
|
3427
|
+
}
|
|
3428
|
+
} },
|
|
3429
|
+
request: prepared.request,
|
|
3430
|
+
reusedConnection: lease.reusedConnection,
|
|
3431
|
+
continuationStatus: prepared.continuationStatus,
|
|
3432
|
+
finish
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
function closeOpenAIResponsesWebSocketSessions(sessionId) {
|
|
3436
|
+
for (const [cacheKey, entry] of websocketSessionCache) {
|
|
3437
|
+
if (sessionId && entry.sessionId !== sessionId) continue;
|
|
3438
|
+
invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
|
|
3439
|
+
}
|
|
3440
|
+
for (const [key, entry] of degradedWebSocketConnections) if (!sessionId || entry.sessionId === sessionId) degradedWebSocketConnections.delete(key);
|
|
3441
|
+
}
|
|
3442
|
+
registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
|
|
3443
|
+
//#endregion
|
|
3197
3444
|
//#region packages/media-core/src/inline-image-data-url.ts
|
|
3198
3445
|
/** Prefix used to distinguish inline data URLs from remote/local image references. */
|
|
3199
3446
|
const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
|
|
@@ -3338,6 +3585,20 @@ function sanitizeResponsesImagePayload(params) {
|
|
|
3338
3585
|
}
|
|
3339
3586
|
//#endregion
|
|
3340
3587
|
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
3588
|
+
function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
|
|
3589
|
+
if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
|
|
3590
|
+
if (getAiTransportHost().requiresManagedTransport(model)) return;
|
|
3591
|
+
return supportsNativeOpenAIResponsesEndpoint({
|
|
3592
|
+
provider: model.provider,
|
|
3593
|
+
api: model.api,
|
|
3594
|
+
baseUrl: model.baseUrl
|
|
3595
|
+
}) ? transport : void 0;
|
|
3596
|
+
}
|
|
3597
|
+
function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
|
|
3598
|
+
const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
|
|
3599
|
+
if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
|
|
3600
|
+
return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
|
|
3601
|
+
}
|
|
3341
3602
|
function resolveProviderTransportTurnState(model, params) {
|
|
3342
3603
|
const normalizedProvider = model.provider.trim().toLowerCase();
|
|
3343
3604
|
const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
|
|
@@ -3366,94 +3627,227 @@ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turn
|
|
|
3366
3627
|
...buildOpenAISdkClientOptions(model)
|
|
3367
3628
|
});
|
|
3368
3629
|
}
|
|
3630
|
+
async function postOpenAIResponsesCompaction(params) {
|
|
3631
|
+
const response = await params.client.post("/responses/compact", {
|
|
3632
|
+
...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
|
|
3633
|
+
timeoutMs: params.options?.timeoutMs,
|
|
3634
|
+
maxRetries: params.options?.maxRetries
|
|
3635
|
+
}),
|
|
3636
|
+
body: {
|
|
3637
|
+
model: params.request.model,
|
|
3638
|
+
input: params.request.input
|
|
3639
|
+
}
|
|
3640
|
+
});
|
|
3641
|
+
const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
|
|
3642
|
+
const item = output[0];
|
|
3643
|
+
const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
|
|
3644
|
+
if (!isRecord(response) || response.object !== "response.compaction" || output.length !== 1 || !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 exactly one compaction item");
|
|
3645
|
+
return {
|
|
3646
|
+
item,
|
|
3647
|
+
usage,
|
|
3648
|
+
model: params.model,
|
|
3649
|
+
replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
|
|
3650
|
+
authProfileId: params.options?.authProfileId,
|
|
3651
|
+
sessionId: params.options?.sessionId
|
|
3652
|
+
})
|
|
3653
|
+
};
|
|
3654
|
+
}
|
|
3369
3655
|
function createResponsesTransportExecutor(config) {
|
|
3370
3656
|
return (model, context, options) => {
|
|
3371
3657
|
const responsesOptions = options;
|
|
3658
|
+
const compactRequest = claimResponsesCompactRequest(responsesOptions);
|
|
3372
3659
|
const eventStream = createAssistantMessageEventStream();
|
|
3373
3660
|
const stream = eventStream;
|
|
3374
3661
|
(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
|
-
};
|
|
3662
|
+
const output = createOpenAIResponsesAssistantOutput(model, config.outputApi);
|
|
3398
3663
|
let firstEventAbort;
|
|
3664
|
+
let continuationClaim;
|
|
3399
3665
|
try {
|
|
3400
3666
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
3667
|
+
const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
|
|
3401
3668
|
const turnState = resolveProviderTransportTurnState(model, {
|
|
3402
3669
|
sessionId: options?.sessionId,
|
|
3403
3670
|
turnId: randomUUID(),
|
|
3404
3671
|
attempt: 1,
|
|
3405
|
-
transport: "stream"
|
|
3672
|
+
transport: websocketMode ? "websocket" : "stream"
|
|
3406
3673
|
});
|
|
3674
|
+
const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
|
|
3675
|
+
const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
|
|
3407
3676
|
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3677
|
+
const buildRequest = async (replayMode) => {
|
|
3678
|
+
let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
|
|
3679
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
3680
|
+
if (nextParams !== void 0) params = nextParams;
|
|
3681
|
+
if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
|
|
3682
|
+
params = sanitizeOpenAICodexResponsesParams(model, params);
|
|
3683
|
+
params = sanitizeResponsesImagePayload(params);
|
|
3684
|
+
if (options?.openclawCodeModeToolSurface === true) {
|
|
3685
|
+
const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
|
|
3686
|
+
const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
|
|
3687
|
+
enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
|
|
3688
|
+
assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
|
|
3689
|
+
}
|
|
3690
|
+
return params;
|
|
3691
|
+
};
|
|
3692
|
+
const params = await buildRequest("checkpoint");
|
|
3693
|
+
if (compactRequest) {
|
|
3694
|
+
const compacted = await postOpenAIResponsesCompaction({
|
|
3695
|
+
client,
|
|
3696
|
+
model,
|
|
3697
|
+
request: params,
|
|
3698
|
+
options: responsesOptions
|
|
3699
|
+
});
|
|
3700
|
+
output.usage.input = compacted.usage.input_tokens;
|
|
3701
|
+
output.usage.output = compacted.usage.output_tokens;
|
|
3702
|
+
output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
|
|
3703
|
+
compactRequest.resolve(compacted);
|
|
3704
|
+
stream.push({
|
|
3705
|
+
type: "done",
|
|
3706
|
+
reason: output.stopReason,
|
|
3707
|
+
message: output
|
|
3708
|
+
});
|
|
3709
|
+
stream.end();
|
|
3710
|
+
return;
|
|
3418
3711
|
}
|
|
3712
|
+
const sessionId = options?.sessionId;
|
|
3713
|
+
if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && supportsNativeOpenAIResponsesEndpoint({
|
|
3714
|
+
provider: model.provider,
|
|
3715
|
+
api: model.api,
|
|
3716
|
+
baseUrl: model.baseUrl
|
|
3717
|
+
}) && sessionId && params.store === true && !params.previous_response_id) continuationClaim = claimOpenAIResponsesHttpContinuation({
|
|
3718
|
+
sessionId,
|
|
3719
|
+
apiKey,
|
|
3720
|
+
baseUrl: model.baseUrl,
|
|
3721
|
+
headers: buildOpenAIClientHeaders(model, context, options?.headers, turnState?.headers, sessionId),
|
|
3722
|
+
request: params
|
|
3723
|
+
});
|
|
3419
3724
|
const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
|
|
3420
3725
|
const requestStartedAt = Date.now();
|
|
3421
|
-
|
|
3422
|
-
const
|
|
3726
|
+
let started = false;
|
|
3727
|
+
const startStream = () => {
|
|
3728
|
+
if (!started) {
|
|
3729
|
+
started = true;
|
|
3730
|
+
stream.push({
|
|
3731
|
+
type: "start",
|
|
3732
|
+
partial: output
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3735
|
+
};
|
|
3736
|
+
const firstEvent = createFirstStreamEventAbortController(options?.signal);
|
|
3737
|
+
firstEventAbort = firstEvent;
|
|
3738
|
+
const requestOptions = buildOpenAISdkRequestOptions(model, firstEvent.signal, {
|
|
3423
3739
|
stream: config.streamRequest,
|
|
3424
3740
|
timeoutMs: options?.timeoutMs,
|
|
3425
3741
|
maxRetries: options?.maxRetries
|
|
3426
3742
|
});
|
|
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
|
-
|
|
3743
|
+
const websocketSignal = combineWebSocketTimeoutSignal(firstEvent.signal, model, requestOptions?.timeout);
|
|
3744
|
+
emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} requestIdHash=${redactIdentifier(options?.requestId, { len: 64 })} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
|
|
3745
|
+
let continuationBaseline;
|
|
3746
|
+
const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
|
|
3747
|
+
const { stream: rawResponseStream, response, attempt } = await config.createResponseStream({
|
|
3748
|
+
client,
|
|
3749
|
+
request: initialRequest,
|
|
3750
|
+
requestOptions,
|
|
3751
|
+
model,
|
|
3752
|
+
observePrompt,
|
|
3753
|
+
initialAttemptKind,
|
|
3754
|
+
initialRejectedCompaction,
|
|
3755
|
+
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
3756
|
+
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
|
|
3757
|
+
});
|
|
3758
|
+
if (continuationClaim) continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
|
|
3759
|
+
return withProviderResponseHook({
|
|
3760
|
+
stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
|
|
3761
|
+
signal: firstEvent.signal,
|
|
3762
|
+
abort: firstEvent.abort,
|
|
3763
|
+
hook: createOpenAIResponseHook(options?.onResponse, response, model),
|
|
3764
|
+
onReady: () => {
|
|
3765
|
+
emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
|
|
3766
|
+
startStream();
|
|
3767
|
+
}
|
|
3768
|
+
});
|
|
3769
|
+
};
|
|
3770
|
+
let responseStream;
|
|
3771
|
+
let finishWebSocket;
|
|
3772
|
+
let transport = "sse";
|
|
3773
|
+
const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
|
|
3774
|
+
const closeWebSocketForFallback = (reason) => {
|
|
3775
|
+
finishWebSocket?.({ keep: false });
|
|
3776
|
+
finishWebSocket = void 0;
|
|
3777
|
+
transport = "sse";
|
|
3778
|
+
logWebSocketFallback(reason);
|
|
3779
|
+
};
|
|
3780
|
+
if (websocketMode) try {
|
|
3781
|
+
const websocket = createOpenAIResponsesWebSocketStream({
|
|
3782
|
+
client,
|
|
3783
|
+
request: params,
|
|
3784
|
+
mode: websocketMode,
|
|
3785
|
+
sessionId: options?.sessionId,
|
|
3786
|
+
headers: websocketHeaders,
|
|
3787
|
+
signal: websocketSignal,
|
|
3788
|
+
callerSignal: options?.signal,
|
|
3789
|
+
degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs
|
|
3790
|
+
});
|
|
3791
|
+
finishWebSocket = websocket.finish;
|
|
3792
|
+
observePrompt?.(websocket.request, {
|
|
3793
|
+
egress: "responses-websocket",
|
|
3794
|
+
payloadVariant: "initial"
|
|
3795
|
+
});
|
|
3796
|
+
transport = "websocket";
|
|
3797
|
+
emitModelTransportDebug(log, `[responses] websocket_selected provider=${model.provider} api=${model.api} model=${model.id} mode=${websocketMode} reused=${websocket.reusedConnection} continuation=${websocket.continuationStatus === "continued"} continuationStatus=${websocket.continuationStatus} sessionIdHash=${redactIdentifier(options?.sessionId)} headersHash=${redactIdentifier(JSON.stringify(Object.entries(websocketHeaders ?? {}).toSorted(([a], [b]) => a.localeCompare(b))))}`);
|
|
3798
|
+
responseStream = { async *[Symbol.asyncIterator]() {
|
|
3799
|
+
try {
|
|
3800
|
+
for await (const event of websocket.stream) {
|
|
3801
|
+
startStream();
|
|
3802
|
+
yield event;
|
|
3803
|
+
}
|
|
3804
|
+
} catch (error) {
|
|
3805
|
+
if (error instanceof OpenAIResponsesWebSocketSafeRetryError) {
|
|
3806
|
+
const encryptedContentRejected = isInvalidEncryptedContentError(error);
|
|
3807
|
+
const recovery = encryptedContentRejected ? await resolveNextResponsesEncryptedContentAttempt({
|
|
3808
|
+
kind: "initial",
|
|
3809
|
+
request: {
|
|
3810
|
+
...websocket.request,
|
|
3811
|
+
stream: true
|
|
3812
|
+
}
|
|
3813
|
+
}, error, { buildFullHistoryRequest: () => buildRequest("full-history") }) : void 0;
|
|
3814
|
+
if (encryptedContentRejected && !recovery) throw error;
|
|
3815
|
+
closeWebSocketForFallback(`safe_server_error code=${error.code} status=${safeDebugValue(error.status)} param=${safeDebugValue(error.param)}`);
|
|
3816
|
+
yield* await createSseStream(recovery?.request ?? await buildRequest("full-history"), recovery?.kind ?? "continuation-rejected", recovery?.rejectedCompaction);
|
|
3817
|
+
return;
|
|
3818
|
+
}
|
|
3819
|
+
if (websocketSignal.aborted || !(error instanceof OpenAIResponsesWebSocketPreDispatchError)) throw error;
|
|
3820
|
+
transport = "sse";
|
|
3821
|
+
logWebSocketFallback("before_first_event");
|
|
3822
|
+
yield* await createSseStream();
|
|
3823
|
+
}
|
|
3824
|
+
} };
|
|
3825
|
+
} catch {
|
|
3826
|
+
closeWebSocketForFallback("setup_failure");
|
|
3827
|
+
responseStream = await createSseStream();
|
|
3828
|
+
}
|
|
3829
|
+
else responseStream = await createSseStream();
|
|
3830
|
+
try {
|
|
3831
|
+
const terminal = await processResponsesStream(responseStream, output, stream, model, {
|
|
3832
|
+
...config.pricingOptions?.(responsesOptions, model),
|
|
3833
|
+
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
|
|
3834
|
+
abortFirstEventStream: firstEvent.abort,
|
|
3835
|
+
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
3836
|
+
signal: options?.signal,
|
|
3837
|
+
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
3838
|
+
authProfileId: responsesOptions?.authProfileId,
|
|
3839
|
+
sessionId: options?.sessionId
|
|
3840
|
+
})
|
|
3841
|
+
});
|
|
3842
|
+
finishWebSocket?.();
|
|
3843
|
+
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
3844
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
3845
|
+
if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal);
|
|
3846
|
+
} catch (error) {
|
|
3847
|
+
finishWebSocket?.({ keep: false });
|
|
3848
|
+
throw error;
|
|
3849
|
+
}
|
|
3850
|
+
emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
|
|
3457
3851
|
stream.push({
|
|
3458
3852
|
type: "done",
|
|
3459
3853
|
reason: output.stopReason,
|
|
@@ -3461,9 +3855,20 @@ function createResponsesTransportExecutor(config) {
|
|
|
3461
3855
|
});
|
|
3462
3856
|
stream.end();
|
|
3463
3857
|
} catch (error) {
|
|
3858
|
+
if (compactRequest) {
|
|
3859
|
+
compactRequest.reject(error);
|
|
3860
|
+
Object.assign(output, projectProviderError(error, options?.signal));
|
|
3861
|
+
stream.push({
|
|
3862
|
+
type: "error",
|
|
3863
|
+
reason: output.stopReason,
|
|
3864
|
+
error: output
|
|
3865
|
+
});
|
|
3866
|
+
stream.end();
|
|
3867
|
+
return;
|
|
3868
|
+
}
|
|
3464
3869
|
if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
|
|
3465
3870
|
log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
|
|
3466
|
-
|
|
3871
|
+
Object.assign(output, projectProviderError(error, options?.signal));
|
|
3467
3872
|
stream.push({
|
|
3468
3873
|
type: "error",
|
|
3469
3874
|
reason: output.stopReason,
|
|
@@ -3471,6 +3876,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
3471
3876
|
});
|
|
3472
3877
|
stream.end();
|
|
3473
3878
|
} finally {
|
|
3879
|
+
continuationClaim?.release();
|
|
3474
3880
|
firstEventAbort?.dispose();
|
|
3475
3881
|
}
|
|
3476
3882
|
})();
|
|
@@ -3480,12 +3886,13 @@ function createResponsesTransportExecutor(config) {
|
|
|
3480
3886
|
function createOpenAIResponsesTransportStreamFn() {
|
|
3481
3887
|
return createResponsesTransportExecutor({
|
|
3482
3888
|
streamRequest: true,
|
|
3889
|
+
httpContinuation: true,
|
|
3483
3890
|
createClient: createOpenAIResponsesClient,
|
|
3484
3891
|
buildRequest: buildOpenAIResponsesParams,
|
|
3485
3892
|
createResponseStream: createResponsesStreamWithEncryptedContentRetry,
|
|
3486
|
-
pricingOptions: (options) => ({
|
|
3893
|
+
pricingOptions: (options, model) => ({
|
|
3487
3894
|
serviceTier: options?.serviceTier,
|
|
3488
|
-
applyServiceTierPricing
|
|
3895
|
+
applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
|
|
3489
3896
|
})
|
|
3490
3897
|
});
|
|
3491
3898
|
}
|
|
@@ -3494,18 +3901,8 @@ function createAzureOpenAIResponsesTransportStreamFn() {
|
|
|
3494
3901
|
outputApi: "azure-openai-responses",
|
|
3495
3902
|
firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
|
|
3496
3903
|
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
|
-
}
|
|
3904
|
+
buildRequest: (model, context, options, metadata, replayMode) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata, replayMode),
|
|
3905
|
+
createResponseStream: createResponsesStreamWithEncryptedContentRetry
|
|
3509
3906
|
});
|
|
3510
3907
|
}
|
|
3511
3908
|
function normalizeAzureBaseUrl(baseUrl) {
|
|
@@ -3533,8 +3930,8 @@ function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHead
|
|
|
3533
3930
|
apiVersion: resolveAzureOpenAIApiVersion()
|
|
3534
3931
|
});
|
|
3535
3932
|
}
|
|
3536
|
-
function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata) {
|
|
3537
|
-
const params = buildOpenAIResponsesParams(model, context, options, metadata);
|
|
3933
|
+
function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata, replayMode = "checkpoint") {
|
|
3934
|
+
const params = buildOpenAIResponsesParams(model, context, options, metadata, replayMode);
|
|
3538
3935
|
params.model = deploymentName;
|
|
3539
3936
|
delete params.store;
|
|
3540
3937
|
return params;
|
|
@@ -3559,11 +3956,8 @@ const responsesTesting = {
|
|
|
3559
3956
|
buildOpenAIResponsesReasoningReplayMetadata,
|
|
3560
3957
|
isInvalidEncryptedContentError,
|
|
3561
3958
|
normalizeResponsesFailedEvent,
|
|
3562
|
-
prepareOpenAIResponsesReasoningItemForReplay,
|
|
3563
3959
|
createResponsesStreamWithEncryptedContentRetry,
|
|
3564
3960
|
resolveAzureOpenAIApiVersion,
|
|
3565
|
-
stripResponsesRequestEncryptedContent,
|
|
3566
|
-
tagOpenAIResponsesReasoningReplayItem,
|
|
3567
3961
|
summarizeResponsesFailedNoDetailsObservation,
|
|
3568
3962
|
summarizeResponsesPayload,
|
|
3569
3963
|
summarizeResponsesTools,
|
|
@@ -3572,6 +3966,50 @@ const responsesTesting = {
|
|
|
3572
3966
|
};
|
|
3573
3967
|
if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
|
|
3574
3968
|
//#endregion
|
|
3969
|
+
//#region packages/ai/src/transports/provider-compaction-replay.ts
|
|
3970
|
+
/** Whether provider replay state is a prefix-bound server compaction checkpoint. */
|
|
3971
|
+
function isCompactionReplayCheckpoint(replay) {
|
|
3972
|
+
const type = replay && typeof replay === "object" ? replay.type : void 0;
|
|
3973
|
+
return type === "anthropic-compaction" || type === "openai-responses-compaction";
|
|
3974
|
+
}
|
|
3975
|
+
/** Strip prefix-bound checkpoints after local history rewrites. */
|
|
3976
|
+
function stripCompactionReplayCheckpoint(message) {
|
|
3977
|
+
if (!isCompactionReplayCheckpoint(message.providerReplay)) return message;
|
|
3978
|
+
const replaySafeMessage = { ...message };
|
|
3979
|
+
delete replaySafeMessage.providerReplay;
|
|
3980
|
+
return replaySafeMessage;
|
|
3981
|
+
}
|
|
3982
|
+
/** Strip prefix-bound checkpoint state from an in-place message rewrite. */
|
|
3983
|
+
function stripCompactionReplayCheckpointInPlace(message) {
|
|
3984
|
+
if (isCompactionReplayCheckpoint(message.providerReplay)) delete message.providerReplay;
|
|
3985
|
+
}
|
|
3986
|
+
/** Reindex a prefix-bound checkpoint after known content removals. */
|
|
3987
|
+
function replaceCompactionReplayOwnerContent(message, content) {
|
|
3988
|
+
const next = {
|
|
3989
|
+
...message,
|
|
3990
|
+
content
|
|
3991
|
+
};
|
|
3992
|
+
const replay = message.providerReplay;
|
|
3993
|
+
if (!isCompactionReplayCheckpoint(replay)) return next;
|
|
3994
|
+
const replayIndex = replay.replayIndex ?? 0;
|
|
3995
|
+
if (content.length === 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
|
|
3996
|
+
let sourceIndex = 0;
|
|
3997
|
+
let nextReplayIndex = 0;
|
|
3998
|
+
if (!content.every((block) => {
|
|
3999
|
+
const index = message.content.indexOf(block, sourceIndex);
|
|
4000
|
+
sourceIndex = index + 1;
|
|
4001
|
+
nextReplayIndex += index >= 0 && index < replayIndex ? 1 : 0;
|
|
4002
|
+
return index >= 0;
|
|
4003
|
+
})) return stripCompactionReplayCheckpoint(next);
|
|
4004
|
+
return nextReplayIndex === replayIndex ? next : {
|
|
4005
|
+
...next,
|
|
4006
|
+
providerReplay: {
|
|
4007
|
+
...replay,
|
|
4008
|
+
replayIndex: nextReplayIndex
|
|
4009
|
+
}
|
|
4010
|
+
};
|
|
4011
|
+
}
|
|
4012
|
+
//#endregion
|
|
3575
4013
|
//#region packages/ai/src/transports/provider-transport-stream.ts
|
|
3576
4014
|
const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
|
|
3577
4015
|
"openai-responses",
|
|
@@ -3582,10 +4020,7 @@ const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
|
|
|
3582
4020
|
"google-generative-ai"
|
|
3583
4021
|
]);
|
|
3584
4022
|
const SIMPLE_TRANSPORT_API_ALIAS = {
|
|
3585
|
-
"openai-responses": "openclaw-openai-responses-transport",
|
|
3586
|
-
"openai-chatgpt-responses": "openclaw-openai-chatgpt-responses-transport",
|
|
3587
4023
|
"openai-completions": "openclaw-openai-completions-transport",
|
|
3588
|
-
"azure-openai-responses": "openclaw-azure-openai-responses-transport",
|
|
3589
4024
|
"anthropic-messages": "openclaw-anthropic-messages-transport",
|
|
3590
4025
|
"google-generative-ai": "openclaw-google-generative-ai-transport"
|
|
3591
4026
|
};
|
|
@@ -3638,6 +4073,10 @@ function isTransportAwareApiSupported(api) {
|
|
|
3638
4073
|
}
|
|
3639
4074
|
/** Maps public model APIs to the internal transport API id used by simple runtime dispatch. */
|
|
3640
4075
|
function resolveTransportAwareSimpleApi(api) {
|
|
4076
|
+
if (OPENAI_RESPONSES_APIS.has(api)) {
|
|
4077
|
+
const alias = `openclaw-${api}-transport`;
|
|
4078
|
+
return OPENAI_RESPONSES_APIS.has(alias) ? alias : void 0;
|
|
4079
|
+
}
|
|
3641
4080
|
return SIMPLE_TRANSPORT_API_ALIAS[api];
|
|
3642
4081
|
}
|
|
3643
4082
|
/** Creates a managed transport stream only when request overrides require it. */
|
|
@@ -3734,12 +4173,12 @@ function resolveProviderStreamApi(model) {
|
|
|
3734
4173
|
];
|
|
3735
4174
|
return `${PROVIDER_STREAM_API_PREFIX}${parts.map((part) => encodeURIComponent(part)).join(":")}`;
|
|
3736
4175
|
}
|
|
3737
|
-
function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
|
|
4176
|
+
function applyProviderSimpleCompletionWrapper(registry, model, cfg, hookSourceApi = model.api) {
|
|
3738
4177
|
if (model.api.startsWith(PROVIDER_SIMPLE_COMPLETION_API_PREFIX)) return model;
|
|
3739
4178
|
const sourceProvider = registry.getApiProvider(model.api);
|
|
3740
4179
|
if (!sourceProvider) return model;
|
|
3741
|
-
const
|
|
3742
|
-
const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api:
|
|
4180
|
+
const dispatchApi = model.api;
|
|
4181
|
+
const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: dispatchApi }), context, options);
|
|
3743
4182
|
const streamFn = getAiTransportHost().plugin.wrapSimpleCompletionStream({
|
|
3744
4183
|
provider: model.provider,
|
|
3745
4184
|
config: cfg,
|
|
@@ -3748,6 +4187,7 @@ function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
|
|
|
3748
4187
|
provider: model.provider,
|
|
3749
4188
|
modelId: model.id,
|
|
3750
4189
|
model,
|
|
4190
|
+
sourceApi: hookSourceApi,
|
|
3751
4191
|
streamFn: sourceStreamFn
|
|
3752
4192
|
}
|
|
3753
4193
|
});
|
|
@@ -3781,7 +4221,6 @@ function wrapPluginProviderStream(streamFn) {
|
|
|
3781
4221
|
};
|
|
3782
4222
|
}
|
|
3783
4223
|
function prepareProviderStreamModel(params) {
|
|
3784
|
-
if (params.model.api === "google-generative-ai") return;
|
|
3785
4224
|
const pluginModel = resolveModelHeaderSentinels(params.model);
|
|
3786
4225
|
const providerStreamFn = getAiTransportHost().plugin.resolveProviderStream({
|
|
3787
4226
|
provider: params.model.provider,
|
|
@@ -3807,20 +4246,19 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
3807
4246
|
cfg,
|
|
3808
4247
|
apiRegistry
|
|
3809
4248
|
});
|
|
3810
|
-
if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg);
|
|
4249
|
+
if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg, model.api);
|
|
3811
4250
|
const codexTransportModel = prepareCodexSimpleTransportModel(apiRegistry, model, cfg);
|
|
3812
|
-
if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg);
|
|
4251
|
+
if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg, model.api);
|
|
3813
4252
|
const transportAwareModel = prepareTransportAwareSimpleModel(model, { cfg });
|
|
3814
4253
|
if (transportAwareModel !== model) {
|
|
3815
4254
|
const streamFn = buildTransportAwareSimpleStreamFn(model, { cfg });
|
|
3816
|
-
if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg);
|
|
4255
|
+
if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg, model.api);
|
|
3817
4256
|
}
|
|
3818
|
-
if (model.api === "google-generative-ai") return applyProviderSimpleCompletionWrapper(apiRegistry, getAiTransportHost().prepareGoogleSimpleCompletionModel(apiRegistry, model), cfg);
|
|
3819
4257
|
if (model.provider === "anthropic-vertex") {
|
|
3820
4258
|
const api = resolveAnthropicVertexSimpleApi(model.baseUrl);
|
|
3821
|
-
if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg);
|
|
4259
|
+
if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg, model.api);
|
|
3822
4260
|
}
|
|
3823
4261
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
3824
4262
|
}
|
|
3825
4263
|
//#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 };
|
|
4264
|
+
export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, replaceCompactionReplayOwnerContent, requestPreparedOpenAIResponsesCompaction, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveAnthropicServerCompactionPlan, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesCompactEndpointPlan, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesServerCompactionPlan, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompactionReplayCheckpoint, stripCompactionReplayCheckpointInPlace, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend, withProviderResponseHook };
|