@openclaw/ai 2026.9.1 → 2026.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/anthropic-CZy5U0NY.mjs +376 -0
- package/dist/anthropic-payload-policy-wuRCb6MH.d.mts +85 -0
- package/dist/anthropic-stream-reducer-B_yo_7pf.mjs +1669 -0
- package/dist/{api-registry-Cs6HGNqY.d.mts → api-registry-CB1-1oQ2.d.mts} +2 -2
- package/dist/assistant-output-iqnlJCV2.mjs +16 -0
- package/dist/assistant-text-phase-C20rxWwP.mjs +55 -0
- package/dist/{azure-openai-responses-CN4Fy5zV.mjs → azure-openai-responses-Crpa40QR.mjs} +5 -5
- package/dist/{base64-CEFBpSkN.mjs → base64-D-su8YVo.mjs} +1 -27
- package/dist/{diagnostics-KhXK-QJI.mjs → diagnostics-dV98PqIy.mjs} +96 -21
- package/dist/diagnostics.d.mts +3 -1
- package/dist/diagnostics.mjs +3 -3
- package/dist/{event-stream-vK_7r3bj.d.mts → event-stream-C1Z2piyk.d.mts} +11 -3
- package/dist/{event-stream-uSMZJ3FA.mjs → event-stream-D8PARQfL.mjs} +48 -10
- package/dist/event-stream-I_GlBsuZ.d.mts +1 -0
- package/dist/event-stream.d.mts +2 -2
- package/dist/event-stream.mjs +1 -1
- package/dist/{github-copilot-headers-NCJtz9i0.mjs → github-copilot-headers-B37TB3rB.mjs} +3 -10
- package/dist/github-copilot-request-facts-BTEBMeOv.mjs +16 -0
- package/dist/{google-CC-nrwXg.mjs → google-BDPriaVe.mjs} +10 -10
- package/dist/google-messages-CVn9eFpF.mjs +449 -0
- package/dist/google-shared-BedY23XS.mjs +185 -0
- package/dist/{google-vertex-DCr0pyzQ.mjs → google-vertex-DMz8XQCn.mjs} +7 -6
- package/dist/{host-BIaiBURL.mjs → host-CWuF-sS3.mjs} +84 -34
- package/dist/{host-BztR4tQj.d.mts → host-DK3wmS3e.d.mts} +3 -3
- package/dist/host-policy-CAopLRKA.mjs +37 -0
- package/dist/{index-AfaxKT8w.d.mts → index-CQ6LTHw8.d.mts} +11 -5
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +5 -5
- package/dist/internal/anthropic.d.mts +14 -11
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/google-model-family.d.mts +5 -0
- package/dist/internal/google-model-family.mjs +15 -0
- package/dist/internal/openai-responses-payload-policy.d.mts +1 -1
- package/dist/internal/openai-responses-payload-policy.mjs +1 -1
- package/dist/internal/openai.d.mts +8 -103
- package/dist/internal/openai.mjs +10 -10
- package/dist/internal/retry-after.d.mts +2 -4
- package/dist/internal/retry-after.mjs +57 -8
- package/dist/internal/runtime.d.mts +7 -6
- package/dist/internal/runtime.mjs +8 -7
- package/dist/internal/shared.d.mts +14 -3
- package/dist/internal/shared.mjs +6 -4
- package/dist/internal/tool-schema.d.mts +63 -0
- package/dist/internal/tool-schema.mjs +3 -0
- package/dist/{json-parse-BuAJEbdW.mjs → json-parse-Dw_hnxsA.mjs} +1 -1
- package/dist/{mistral-B7etBd_H.mjs → mistral--m-Jm6VZ.mjs} +16 -38
- package/dist/model-transport-url-DKocrEsb.d.mts +28 -0
- package/dist/{openai-chatgpt-responses-BAJ4gq3i.mjs → openai-chatgpt-responses-nDZccFW-.mjs} +62 -54
- package/dist/openai-completions-KuoZyx0d.mjs +187 -0
- package/dist/{openai-completions-compat-4IjSBpr6.d.mts → openai-completions-compat-CXn3T1we.d.mts} +26 -4
- package/dist/{openai-completions-stream-DZjwK8vp.mjs → openai-completions-stream-BQk3SkLD.mjs} +621 -451
- package/dist/openai-prompt-cache-BI0rkM-5.mjs +220 -0
- package/dist/openai-prompt-cache-rrutvqxq.d.mts +16 -0
- package/dist/openai-provider-client-ha_WxTyj.mjs +24 -0
- package/dist/openai-reasoning-effort-BK7FbcLT.mjs +167 -0
- package/dist/{openai-responses-BEUlxfDu.mjs → openai-responses-D99dOzKI.mjs} +14 -32
- package/dist/{openai-responses-compaction-window-DO7yV7az.mjs → openai-responses-compaction-window-D5jbzCi3.mjs} +7 -159
- package/dist/{openai-responses-contracts-bPzSN_Ba.d.mts → openai-responses-contracts-B55afwRo.d.mts} +12 -4
- package/dist/{openai-responses-prompt-observer-internal-C7x4IK7r.mjs → openai-responses-prompt-observer-internal-tApyTLVu.mjs} +3 -3
- package/dist/{openai-responses-shared-DiAdpNAM.mjs → openai-responses-shared-ZyQEzS5i.mjs} +217 -212
- package/dist/openai-tool-projection-793cE3QX.d.mts +37 -0
- package/dist/{openai-tool-schema-BMiHFH36.mjs → openai-tool-schema-CzjyYXun.mjs} +53 -583
- package/dist/openai-tool-schema-ynZBgqW-.d.mts +38 -0
- package/dist/openai-transport-params-DNasp2fU.mjs +674 -0
- package/dist/{provider-error-B6EGq6gg.mjs → provider-error-C6TbKiey.mjs} +29 -13
- package/dist/{provider-options-CxFPtvh7.d.mts → provider-options-BXr9Ec83.d.mts} +19 -42
- package/dist/provider-replay-context-BuSUaAk5.mjs +21 -0
- package/dist/{provider-transcript-transform-WvJmFUAf.mjs → provider-transcript-transform-pPmUIwKt.mjs} +1 -1
- package/dist/provider-types-CAV0Og3m.d.mts +29 -0
- package/dist/provider-types.d.mts +6 -31
- package/dist/providers.d.mts +2 -2
- package/dist/providers.mjs +11 -11
- package/dist/{reasoning-tag-text-partitioner-C-4uedDb.mjs → reasoning-tag-text-partitioner-BQBi4B9d.mjs} +90 -35
- package/dist/record-coerce-DwRYMj3t.mjs +32 -0
- package/dist/retry-after-CdCURCVg.d.mts +15 -0
- package/dist/{sanitize-unicode-S6binQG-.mjs → sanitize-unicode-Bb-v9meu.mjs} +1 -1
- package/dist/session-affinity-CCH7eYdB.mjs +20 -0
- package/dist/{simple-options-0PLDyJ-d.mjs → simple-options-tcKOqnpF.mjs} +3 -3
- package/dist/{src-C8U7lkoa.mjs → src-B2Q_6G8V.mjs} +10 -2
- package/dist/{stream-first-event-timeout-DcNjoFQE.mjs → stream-first-event-timeout-2hfrquuz.mjs} +1 -1
- package/dist/string-coerce-fsri9iCu.mjs +34 -0
- package/dist/{string-normalization--fwJ4S2q.mjs → string-normalization-CmLIasuf.mjs} +1 -1
- package/dist/{tool-schema-json-projection-BtZiml7r.mjs → tool-schema-json-projection-ClptDdAO.mjs} +2 -38
- package/dist/{transport-stream-shared-CytPVLIg.mjs → transport-stream-shared-Cu3ZPhNW.mjs} +5 -5
- package/dist/{transport-stream-shared-BrvFTkoO.d.mts → transport-stream-shared-DOxpGJ-H.d.mts} +4 -4
- package/dist/transport-utils-CCooe-cr.mjs +121 -0
- package/dist/transports.d.mts +114 -41
- package/dist/transports.mjs +796 -1596
- package/dist/types-BADKjDBI.d.mts +1 -0
- package/dist/{types-DbrhszyQ.d.mts → types-Dy1q0CSu.d.mts} +101 -64
- package/dist/types.d.mts +6 -6
- package/dist/types.mjs +4 -4
- package/dist/utf16-slice-CvGodqok.mjs +29 -0
- package/dist/{validation-CJZtym2g.mjs → validation-BDzVDnTs.mjs} +9 -1
- package/dist/{validation-BOwtcl9X.d.mts → validation-CaFUZN9B.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +14 -4
- package/dist/anthropic-compaction-replay-OMJZ0uyo.mjs +0 -838
- package/dist/anthropic-hk7F7ptG.mjs +0 -883
- package/dist/anthropic-payload-policy-DBT1itQ-.d.mts +0 -51
- package/dist/event-stream-DeDhbCc5.d.mts +0 -1
- package/dist/google-shared-CjPY0hZM.mjs +0 -634
- package/dist/google-thinking-level-C-V3tecN.mjs +0 -9
- package/dist/openai-completions-D0QZ0AyB.mjs +0 -403
- package/dist/openai-prompt-cache-2uo_1OR1.d.mts +0 -7
- package/dist/openai-prompt-cache-mZTCdRPo.mjs +0 -12
- package/dist/transport-utils-CtuS1Upe.mjs +0 -138
- package/dist/types-B5EFUmXs.d.mts +0 -1
- package/dist/utf16-slice-qz3nsy87.mjs +0 -84
package/dist/transports.mjs
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { r as
|
|
4
|
-
import {
|
|
5
|
-
import { i as stableStringify } from "./provider-error-
|
|
6
|
-
import { r as
|
|
7
|
-
import { a as
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
1
|
+
import { _ as supportsClaudeAdaptiveThinking, h as resolveClaudeSonnet5ModelIdentity, m as resolveClaudeOpus5ModelIdentity } from "./src-B2Q_6G8V.mjs";
|
|
2
|
+
import { n as normalizeLowercaseStringOrEmpty, t as hasNonEmptyString } from "./string-coerce-fsri9iCu.mjs";
|
|
3
|
+
import { S as usesClaudeStreamingRefusalContract, _ as prepareClaudeNoPrefillRequestContext, h as defaultsClaudeAdaptiveThinking, m as applyClaudeRequestContract, n as getAiTransportHost, p as ANTHROPIC_CLAUDE_CODE_VERSION, r as resolveAiTransportHeaderSentinels, v as requiresClaudeAdaptiveThinking, x as usesClaudeFable5MessagesContract, y as resolveAnthropicThinkingEffort } from "./host-CWuF-sS3.mjs";
|
|
4
|
+
import { o as isRecord } from "./record-coerce-DwRYMj3t.mjs";
|
|
5
|
+
import { i as stableStringify } from "./provider-error-C6TbKiey.mjs";
|
|
6
|
+
import { a as isNativeOpenAIEndpoint, c as resolveOpenAIPromptCacheKeySupport, i as detectOpenAICompletionsCompat, l as usesNativeOpenAICodexResponsesBackend, o as isOpenAICodexResponsesModel, r as resolveOpenAIPromptCacheParams, s as resolveOpenAICompletionsCompat } from "./openai-prompt-cache-BI0rkM-5.mjs";
|
|
7
|
+
import { a as resolveModelSseDebugMode, i as resolveModelPayloadDebugMode, n as formatModelTransportDebugUrl, r as emitModelTransportDebug, t as formatModelTransportDebugBaseUrl, u as toErrorObject } from "./diagnostics-dV98PqIy.mjs";
|
|
8
|
+
import "./base64-D-su8YVo.mjs";
|
|
9
|
+
import { c as resolveModelHeaderSentinels$1, i as isCodeModeModelVisibleToolName, n as createAbortError$1, o as readResponseTextSnippet, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "./transport-utils-CCooe-cr.mjs";
|
|
10
|
+
import { parseRetryAfterHeadersSeconds } from "./internal/retry-after.mjs";
|
|
11
|
+
import { i as resolveProviderEndpoint, r as resolveOpenAIStrictToolSetting, s as transformTransportMessages, t as buildGuardedModelFetch } from "./host-policy-CAopLRKA.mjs";
|
|
12
|
+
import { B as logAnthropicContextEdits, C as applyAnthropicThinkingBindingControls, D as ANTHROPIC_SERVER_SIDE_FALLBACKS, F as applyAnthropicPayloadPolicyToParams, G as resolveAnthropicServerCompactionPlan, H as resolveAnthropicContextManagementBetaHeader, I as applyAnthropicRequestCacheControl, J as usesFoundryBearerAuth, K as isAnthropicOAuthApiKey, L as buildAnthropicSystemBlocks, N as applyAnthropicContextManagementToRequest, O as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, P as applyAnthropicEphemeralCacheControlMarkers, R as isAnthropicServerToolClearingEnabled, U as resolveAnthropicEphemeralCacheControl, V as resolveAnthropicCacheOptions, W as resolveAnthropicPayloadPolicy, d as convertAnthropicTools, f as buildAnthropicReplayPlan, g as normalizeAnthropicToolCallId, h as suppressAnthropicCompaction, l as buildAnthropicGenerationParams, m as resolveNewestAnthropicCompaction, p as isAnthropicReplayRejection, q as omitFoundryBearerCredentialHeaders, t as consumeAnthropicStream, u as convertAnthropicMessages, z as isDirectAnthropicModel } from "./anthropic-stream-reducer-B_yo_7pf.mjs";
|
|
10
13
|
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
11
|
-
import { a as codeModeToolSurfaceObserver, d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, o as reasoningTagTextPolicy, t as adjustMaxTokensForThinking } from "./simple-options-
|
|
12
|
-
import { a as
|
|
13
|
-
import { a as isGoogleGemini3FlashModel, c as parseRetryAfterSeconds, f as resolveModelHeaderSentinels$1, i as isCodeModeModelVisibleToolName, l as readResponseTextSnippet, m as supportsModelTools, n as createAbortError$1, o as isGoogleGemini3ProModel, p as sha256Hex, r as estimateStringChars, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, u as redactIdentifier } from "./transport-utils-CtuS1Upe.mjs";
|
|
14
|
+
import { a as codeModeToolSurfaceObserver, d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, o as reasoningTagTextPolicy, t as adjustMaxTokensForThinking } from "./simple-options-tcKOqnpF.mjs";
|
|
15
|
+
import { C as readOpenAICompletionsContentDeltas, D as throwIfModelStreamAborted, E as resolvePromptCacheKey, O as redactIdentifier, S as parseOpenAICompletionsUsage, T as resolveOpenAIClientBaseUrl, _ as createOpenAIProviderAcceptanceHook, a as enforceCodeModeResponsesToolSurface, b as log, c as readCodeModePayloadToolName, d as resolveOpenAIReasoningEffortMap, f as projectOpenAITools, g as createModelStreamCooperativeScheduler, h as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, i as buildOpenAISdkRequestOptions, k as sha256Hex, l as resolveCodeModeResponsesVisibleToolNames, m as reconcileOpenAIResponsesToolChoice, n as buildOpenAIClientHeaders, o as filterCodeModePayloadTools, r as buildOpenAISdkClientOptions, s as getCompat, t as assertCodeModeResponsesToolSurface, u as resolveOpenAIStrictToolFlagWithDiagnostics, v as createOpenAIResponseHook, w as readOpenAICompletionsReasoningBatch, x as measureUtf8AppendBytes, y as isOpenAICompletionsThinkingEnabled } from "./openai-transport-params-DNasp2fU.mjs";
|
|
14
16
|
import { n as getEnvApiKey } from "./env-api-keys-bktO00EJ.mjs";
|
|
15
|
-
import { S as quoteUnsafeIntegerLiterals, _ as transportAbortError, a as createWritableTransportEventStream, b as parseJsonObjectPreservingUnsafeIntegers, c as finalizeTransportStream, d as notifyProviderHttpMetadata, f as notifyProviderHttpResponse, g as sanitizeTransportPayloadText, h as sanitizeNonEmptyTransportPayloadText, i as createEmptyTransportUsage, l as mergeTransportHeaders, m as parseTerminalToolCallArguments, n as coerceTransportToolCallArguments, o as failTransportStream, p as notifyProviderStreamOpened, r as copyProviderAcceptanceObserver, s as finalizeTerminalToolCallArguments, t as assignTransportErrorDetails, u as mergeTransportMetadata, v as withProviderAcceptanceObserver, x as parseJsonPreservingUnsafeIntegers, y as withProviderResponseHook } from "./transport-stream-shared-
|
|
16
|
-
import { C as createDeepSeekTextFilter, E as tagUnresolvedTextAsCommentary, S as shouldOmitOllamaCompatResponseFormat, T as tagPendingCommentaryText, _ as hasToolCallHistory, a as buildOpenAISdkClientOptions, b as resolveOpenAICompletionsCompat, c as filterCodeModePayloadTools, d as readCodeModePayloadToolName, f as resolveCodeModeResponsesVisibleToolNames, g as convertMessages, h as resolveOpenAIReasoningEffortMap, i as buildOpenAIClientHeaders, l as getCompat, m as usesNativeOpenAICodexResponsesBackend, n as shouldEmitOpenAICompletionsReasoning, o as buildOpenAISdkRequestOptions, p as resolveOpenAIStrictToolFlagWithDiagnostics, r as assertCodeModeResponsesToolSurface, s as enforceCodeModeResponsesToolSurface, t as processCompletionsStream, u as isOpenAICodexResponsesModel, v as finalizeOpenAICompletionsToolCalls, x as resolveOpenAICompletionsResponseFormat, y as detectOpenAICompletionsCompat } from "./openai-completions-stream-DZjwK8vp.mjs";
|
|
17
|
+
import { S as quoteUnsafeIntegerLiterals, _ as transportAbortError, a as createWritableTransportEventStream, b as parseJsonObjectPreservingUnsafeIntegers, c as finalizeTransportStream, d as notifyProviderHttpMetadata, f as notifyProviderHttpResponse, g as sanitizeTransportPayloadText, h as sanitizeNonEmptyTransportPayloadText, i as createEmptyTransportUsage, l as mergeTransportHeaders, m as parseTerminalToolCallArguments, n as coerceTransportToolCallArguments, o as failTransportStream, p as notifyProviderStreamOpened, r as copyProviderAcceptanceObserver, s as finalizeTerminalToolCallArguments, t as assignTransportErrorDetails, u as mergeTransportMetadata, v as withProviderAcceptanceObserver, x as parseJsonPreservingUnsafeIntegers, y as withProviderResponseHook } from "./transport-stream-shared-Cu3ZPhNW.mjs";
|
|
17
18
|
import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { t as
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { i as
|
|
24
|
-
import {
|
|
19
|
+
import { n as providerReplayContextMatches, t as buildProviderReplayContext } from "./provider-replay-context-BuSUaAk5.mjs";
|
|
20
|
+
import { a as tagUnresolvedTextAsCommentary } from "./assistant-text-phase-C20rxWwP.mjs";
|
|
21
|
+
import { t as createAssistantOutput } from "./assistant-output-iqnlJCV2.mjs";
|
|
22
|
+
import { t as resolveOpencodeSessionHeaders } from "./session-affinity-CCH7eYdB.mjs";
|
|
23
|
+
import { c as flattenCompletionMessagesToStringContent, d as canonicalizeMaxTokensParam, f as resolveMaxTokensParam, l as stripCompletionMessagesToRoleContent, n as shouldEmitOpenAICompletionsReasoning, o as isAzureOpenAICompatibleHost, p as createDeepSeekTextFilter, r as buildOpenAICompletionsParams, s as finalizeOpenAICompletionsToolCalls, t as processCompletionsStream, u as applyCompletionsAnthropicCacheControl } from "./openai-completions-stream-BQk3SkLD.mjs";
|
|
24
|
+
import { a as googleFlashSupportsMinimalThinking, i as consumeGoogleGenerateContentStream, n as projectGoogleMessages, r as requiresGoogleToolCallId, t as convertGoogleTools } from "./google-messages-CVn9eFpF.mjs";
|
|
25
|
+
import { i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-2hfrquuz.mjs";
|
|
26
|
+
import { i as normalizeOpenAIReasoningEffort, l as supportsOpenAITemperature, o as resolveOpenAIReasoningEffortForModel } from "./openai-reasoning-effort-BK7FbcLT.mjs";
|
|
27
|
+
import { _ as OpenAIResponsesWebSocketPostDispatchError, a as applyOpenAIResponsesPayloadPolicy, c as resolveOpenAIResponsesServerCompactionPlan, d as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, f as OPENAI_RESPONSES_APIS, i as sanitizeResponsesImagePayload, l as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, n as isOpenAIResponsesCompactionOutput, o as resolveOpenAIResponsesCompactEndpointPlan, s as resolveOpenAIResponsesPayloadPolicy, t as createBoundedOpenAIResponsesCompactionFetch, v as OpenAIResponsesWebSocketPreDispatchError, x as parseOpenAIResponsesWebSocketServerError, y as OpenAIResponsesWebSocketSafeRetryError } from "./openai-responses-compaction-window-D5jbzCi3.mjs";
|
|
28
|
+
import { $ as resolveReplayableResponsesMessageId, B as resolveAzureOpenAIApiVersion, C as summarizeResponsesFailedNoDetailsObservation, G as recordResponsesInputReplay, H as buildResponsesInputMessage, J as buildOpenAIResponsesReasoningReplayMetadata, K as responsesInputFingerprint, Q as suppressOpenAIResponsesCompaction, R as createResponsesStreamWithEncryptedContentRetry, S as summarizeOpenAITransportError, T as summarizeResponsesTools, U as convertResponsesMessages, V as resolveNextResponsesEncryptedContentAttempt, W as createOpenAIResponsesAssistantOutput, X as isOpenAIResponsesReplayContext, Y as captureOpenAIResponsesCompaction, Z as resolveNewestOpenAIResponsesCompactionReplay, _ as logResponsesFailedNoDetails, b as stringifyRedactedEvent, c as convertProjectedResponsesTools, g as buildResponsesFailedNoDetailsObservation, h as ResponsesStreamFailure, m as observeResponsesStream, n as applyResponsesServiceTierPricing, q as CompactionReplayRefreshRequiredError, u as processResponsesStream, v as normalizeResponsesFailedEvent, w as summarizeResponsesPayload, x as stringifyRedactedPayload, y as safeDebugValue, z as isInvalidEncryptedContentError } from "./openai-responses-shared-ZyQEzS5i.mjs";
|
|
25
29
|
import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
|
|
26
30
|
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
27
|
-
import { t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-
|
|
31
|
+
import { t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-tApyTLVu.mjs";
|
|
28
32
|
import { randomUUID } from "node:crypto";
|
|
29
33
|
import OpenAI, { AzureOpenAI } from "openai";
|
|
30
34
|
import { ResponsesWS } from "openai/resources/responses/ws.js";
|
|
@@ -59,11 +63,6 @@ function resolveAnthropicMessagesMaxTokens(params) {
|
|
|
59
63
|
const contextWindow = resolvePositiveAnthropicTokenLimit(params.modelContextWindow);
|
|
60
64
|
return contextWindow === void 0 ? ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS : Math.max(1, Math.min(ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS, Math.floor(contextWindow / ANTHROPIC_MESSAGES_FALLBACK_CONTEXT_DIVISOR)));
|
|
61
65
|
}
|
|
62
|
-
function isDirectAnthropicModel(model) {
|
|
63
|
-
if (normalizeLowercaseStringOrEmpty(model.provider) !== "anthropic") return false;
|
|
64
|
-
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
65
|
-
return endpointClass === "default" || endpointClass === "anthropic-public";
|
|
66
|
-
}
|
|
67
66
|
function isKimiAnthropicProvider(provider) {
|
|
68
67
|
return /^kimi(?:-|$)/.test(normalizeLowercaseStringOrEmpty(provider ?? ""));
|
|
69
68
|
}
|
|
@@ -82,224 +81,12 @@ function buildAnthropicBetaHeader(model, betaFeatures, params) {
|
|
|
82
81
|
if (!isDirectAnthropicModel(model)) return;
|
|
83
82
|
return params.oauth ? `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}` : betaFeatures.join(",");
|
|
84
83
|
}
|
|
85
|
-
const NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
|
|
86
|
-
async function convertContentBlocks(content, model, imageBudget) {
|
|
87
|
-
const text = extractToolResultText(content);
|
|
88
|
-
const mediaPlaceholder = describeToolResultMediaPlaceholder(content);
|
|
89
|
-
if (!(model.input.includes("image") && content.some(isImageWithMediaPayload))) return sanitizeNonEmptyTransportPayloadText(text, mediaPlaceholder ?? "(no output)");
|
|
90
|
-
const blocks = [];
|
|
91
|
-
let hasTextBlock = false;
|
|
92
|
-
for (const block of content) {
|
|
93
|
-
if (!block || typeof block !== "object") continue;
|
|
94
|
-
const record = block;
|
|
95
|
-
const blockText = extractToolResultBlockText(block);
|
|
96
|
-
if (blockText) {
|
|
97
|
-
blocks.push({
|
|
98
|
-
type: "text",
|
|
99
|
-
text: sanitizeTransportPayloadText(blockText)
|
|
100
|
-
});
|
|
101
|
-
hasTextBlock = true;
|
|
102
|
-
}
|
|
103
|
-
if (!isImageWithMediaPayload(record)) continue;
|
|
104
|
-
const [normalizedImage] = await normalizeAnthropicInlineContent([{
|
|
105
|
-
type: "image",
|
|
106
|
-
data: typeof record.data === "string" ? record.data : "",
|
|
107
|
-
mimeType: typeof record.mimeType === "string" ? record.mimeType : "image/png"
|
|
108
|
-
}], imageBudget);
|
|
109
|
-
if (normalizedImage?.type !== "image") continue;
|
|
110
|
-
blocks.push({
|
|
111
|
-
type: "image",
|
|
112
|
-
source: {
|
|
113
|
-
type: "base64",
|
|
114
|
-
media_type: resolveAnthropicImageMediaType(normalizedImage.mimeType),
|
|
115
|
-
data: normalizedImage.data
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
if (!hasTextBlock) blocks.unshift({
|
|
120
|
-
type: "text",
|
|
121
|
-
text: mediaPlaceholder ?? "(see attached image)"
|
|
122
|
-
});
|
|
123
|
-
return blocks;
|
|
124
|
-
}
|
|
125
|
-
async function convertAnthropicMessages(messages, model, isOAuthToken, options) {
|
|
126
|
-
const params = [];
|
|
127
|
-
const imageBudget = createAnthropicInlineImageBudget();
|
|
128
|
-
const allowReasoningContentReplay = options.allowReasoningContentReplay === true;
|
|
129
|
-
const replayThinkingEnabled = options.replayThinkingEnabled !== false;
|
|
130
|
-
const transformedMessages = transformTransportMessages(messages, model, normalizeAnthropicToolCallId);
|
|
131
|
-
const activeToolTurnAssistantIndex = replayThinkingEnabled ? -1 : findActiveAnthropicToolTurnAssistantIndex(transformedMessages);
|
|
132
|
-
for (let i = 0; i < transformedMessages.length; i += 1) {
|
|
133
|
-
const msg = transformedMessages[i];
|
|
134
|
-
if (!msg) continue;
|
|
135
|
-
if (msg.role === "user") {
|
|
136
|
-
const isRuntimeContextCarrier = msg.runtimeContextCarrier === true;
|
|
137
|
-
if (typeof msg.content === "string") {
|
|
138
|
-
if (msg.content.trim().length > 0) {
|
|
139
|
-
const userParam = {
|
|
140
|
-
role: "user",
|
|
141
|
-
content: sanitizeTransportPayloadText(msg.content)
|
|
142
|
-
};
|
|
143
|
-
if (isRuntimeContextCarrier) options.cacheBreakpointOptOutMessageIndexes.add(params.length);
|
|
144
|
-
params.push(userParam);
|
|
145
|
-
}
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const blocks = (model.input.includes("image") ? await normalizeAnthropicInlineContent(msg.content, imageBudget) : msg.content.map((item) => item.type === "image" ? {
|
|
149
|
-
type: "text",
|
|
150
|
-
text: NON_VISION_USER_IMAGE_PLACEHOLDER
|
|
151
|
-
} : item)).map((item) => item.type === "text" ? {
|
|
152
|
-
type: "text",
|
|
153
|
-
text: sanitizeTransportPayloadText(item.text)
|
|
154
|
-
} : {
|
|
155
|
-
type: "image",
|
|
156
|
-
source: {
|
|
157
|
-
type: "base64",
|
|
158
|
-
media_type: resolveAnthropicImageMediaType(item.mimeType),
|
|
159
|
-
data: item.data
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
let filteredBlocks = model.input.includes("image") ? blocks : blocks.filter((block) => block.type !== "image");
|
|
163
|
-
filteredBlocks = filteredBlocks.filter((block) => block.type !== "text" || block.text.trim().length > 0);
|
|
164
|
-
if (filteredBlocks.length === 0) continue;
|
|
165
|
-
const userParam = {
|
|
166
|
-
role: "user",
|
|
167
|
-
content: filteredBlocks
|
|
168
|
-
};
|
|
169
|
-
if (isRuntimeContextCarrier) options.cacheBreakpointOptOutMessageIndexes.add(params.length);
|
|
170
|
-
params.push(userParam);
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (msg.role === "assistant") {
|
|
174
|
-
const blocks = i === 0 && options.compaction ? [options.compaction] : [];
|
|
175
|
-
const reasoningContent = [];
|
|
176
|
-
let omittedThinking = false;
|
|
177
|
-
for (const block of msg.content) {
|
|
178
|
-
if (block.type === "text") {
|
|
179
|
-
if (block.text.trim().length > 0) blocks.push({
|
|
180
|
-
type: "text",
|
|
181
|
-
text: sanitizeTransportPayloadText(block.text)
|
|
182
|
-
});
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (block.type === "thinking") {
|
|
186
|
-
const thinkingSignature = block.thinkingSignature?.trim();
|
|
187
|
-
const isReasoningContent = thinkingSignature === "reasoning_content";
|
|
188
|
-
if (!replayThinkingEnabled && i !== activeToolTurnAssistantIndex && !isReasoningContent) {
|
|
189
|
-
omittedThinking = true;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (block.redacted) {
|
|
193
|
-
blocks.push({
|
|
194
|
-
type: "redacted_thinking",
|
|
195
|
-
data: block.thinkingSignature
|
|
196
|
-
});
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
const hasNativeThinkingSignature = Boolean(thinkingSignature) && !isReasoningContent;
|
|
200
|
-
if (block.thinking.trim().length === 0 && !hasNativeThinkingSignature) continue;
|
|
201
|
-
if (!thinkingSignature) blocks.push({
|
|
202
|
-
type: "text",
|
|
203
|
-
text: sanitizeTransportPayloadText(block.thinking)
|
|
204
|
-
});
|
|
205
|
-
else {
|
|
206
|
-
const thinking = thinkingSignature === "reasoning_content" ? sanitizeTransportPayloadText(block.thinking) : block.thinking;
|
|
207
|
-
if (thinkingSignature === "reasoning_content") {
|
|
208
|
-
if (allowReasoningContentReplay) {
|
|
209
|
-
blocks.push({
|
|
210
|
-
type: "thinking",
|
|
211
|
-
thinking,
|
|
212
|
-
signature: thinkingSignature
|
|
213
|
-
});
|
|
214
|
-
reasoningContent.push(thinking);
|
|
215
|
-
}
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
blocks.push({
|
|
219
|
-
type: "thinking",
|
|
220
|
-
thinking,
|
|
221
|
-
signature: thinkingSignature
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if (block.type === "toolCall") blocks.push({
|
|
227
|
-
type: "tool_use",
|
|
228
|
-
id: block.id,
|
|
229
|
-
name: isOAuthToken ? toClaudeCodeToolName(block.name) : block.name,
|
|
230
|
-
input: coerceTransportToolCallArguments(block.arguments)
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
if (blocks.length === 0 && omittedThinking) blocks.push({
|
|
234
|
-
type: "text",
|
|
235
|
-
text: ANTHROPIC_OMITTED_REASONING_TEXT
|
|
236
|
-
});
|
|
237
|
-
if (blocks.length > 0) {
|
|
238
|
-
const assistantMsg = {
|
|
239
|
-
role: "assistant",
|
|
240
|
-
content: blocks
|
|
241
|
-
};
|
|
242
|
-
if (reasoningContent.length > 0) assistantMsg.reasoning_content = reasoningContent.join("\n");
|
|
243
|
-
else if (allowReasoningContentReplay) blocks.unshift({
|
|
244
|
-
type: "thinking",
|
|
245
|
-
thinking: "",
|
|
246
|
-
signature: "reasoning_content"
|
|
247
|
-
});
|
|
248
|
-
params.push(assistantMsg);
|
|
249
|
-
}
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
if (msg.role === "toolResult") {
|
|
253
|
-
const toolResult = msg;
|
|
254
|
-
const toolResults = [{
|
|
255
|
-
type: "tool_result",
|
|
256
|
-
tool_use_id: toolResult.toolCallId,
|
|
257
|
-
content: await convertContentBlocks(toolResult.content, model, imageBudget),
|
|
258
|
-
is_error: toolResult.isError
|
|
259
|
-
}];
|
|
260
|
-
let j = i + 1;
|
|
261
|
-
while (j < transformedMessages.length) {
|
|
262
|
-
const nextMsg = transformedMessages.at(j);
|
|
263
|
-
if (nextMsg?.role !== "toolResult") break;
|
|
264
|
-
toolResults.push({
|
|
265
|
-
type: "tool_result",
|
|
266
|
-
tool_use_id: nextMsg.toolCallId,
|
|
267
|
-
content: await convertContentBlocks(nextMsg.content, model, imageBudget),
|
|
268
|
-
is_error: nextMsg.isError
|
|
269
|
-
});
|
|
270
|
-
j += 1;
|
|
271
|
-
}
|
|
272
|
-
i = j - 1;
|
|
273
|
-
params.push({
|
|
274
|
-
role: "user",
|
|
275
|
-
content: toolResults
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return params;
|
|
280
|
-
}
|
|
281
84
|
function ensureNonEmptyAnthropicMessages(messages) {
|
|
282
85
|
return messages.length > 0 ? messages : [{
|
|
283
86
|
role: "user",
|
|
284
87
|
content: EMPTY_ANTHROPIC_MESSAGES_FALLBACK_TEXT
|
|
285
88
|
}];
|
|
286
89
|
}
|
|
287
|
-
function convertAnthropicTools(tools, isOAuthToken) {
|
|
288
|
-
const projection = projectAnthropicTools(tools ?? [], (name) => isOAuthToken ? toClaudeCodeToolName(name) : name);
|
|
289
|
-
const converted = [];
|
|
290
|
-
for (const tool of projection.tools) converted.push({
|
|
291
|
-
name: tool.wireName,
|
|
292
|
-
description: tool.description,
|
|
293
|
-
input_schema: tool.inputSchema
|
|
294
|
-
});
|
|
295
|
-
return {
|
|
296
|
-
projection,
|
|
297
|
-
tools: converted
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
function parseAnthropicToolCallArguments(inputJson) {
|
|
301
|
-
return coerceTransportToolCallArguments(parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson));
|
|
302
|
-
}
|
|
303
90
|
const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
304
91
|
/** Resolve the effective Anthropic API base URL from model or environment. */
|
|
305
92
|
function resolveAnthropicBaseUrl(baseUrl) {
|
|
@@ -402,12 +189,13 @@ async function* parseAnthropicSseBody(body, signal) {
|
|
|
402
189
|
function createAnthropicMessagesClient(params) {
|
|
403
190
|
const url = resolveAnthropicMessagesUrl(params.baseURL);
|
|
404
191
|
return { messages: { async stream(body, options) {
|
|
405
|
-
const headers = mergeTransportHeaders({
|
|
192
|
+
const headers = new Headers(mergeTransportHeaders({
|
|
406
193
|
"content-type": "application/json",
|
|
407
194
|
"anthropic-version": "2023-06-01",
|
|
408
195
|
...params.apiKey ? { "x-api-key": params.apiKey } : {},
|
|
409
196
|
...params.authToken ? { authorization: `Bearer ${params.authToken}` } : {}
|
|
410
|
-
}, params.defaultHeaders);
|
|
197
|
+
}, params.defaultHeaders));
|
|
198
|
+
for (const [name, value] of Object.entries(options?.headers ?? {})) headers.set(name, value);
|
|
411
199
|
const response = await params.fetch(url, {
|
|
412
200
|
method: "POST",
|
|
413
201
|
headers,
|
|
@@ -421,7 +209,7 @@ function createAnthropicMessagesClient(params) {
|
|
|
421
209
|
} } };
|
|
422
210
|
}
|
|
423
211
|
function formatAnthropicMessagesHttpError(response, detail) {
|
|
424
|
-
const retryAfterSeconds =
|
|
212
|
+
const retryAfterSeconds = parseRetryAfterHeadersSeconds(response.headers);
|
|
425
213
|
const retryAfterSuffix = Number.isFinite(retryAfterSeconds) ? `; Retry-After: ${Math.ceil(retryAfterSeconds ?? 0)} seconds` : "";
|
|
426
214
|
return `HTTP ${response.status}: ${detail || "Anthropic Messages request failed"}${retryAfterSuffix}`;
|
|
427
215
|
}
|
|
@@ -440,6 +228,7 @@ async function readAnthropicMessagesErrorBodySnippet(response) {
|
|
|
440
228
|
}
|
|
441
229
|
function createAnthropicTransportClient(params) {
|
|
442
230
|
const { model, context, apiKey, options } = params;
|
|
231
|
+
const optionHeaders = resolveOpencodeSessionHeaders(model, options);
|
|
443
232
|
const needsInterleavedBeta = (options?.interleavedThinking ?? true) && !supportsClaudeAdaptiveThinking(model);
|
|
444
233
|
const fetch = isKimiAnthropicProvider(model.provider) && options?.thinkingEnabled === true ? buildGuardedModelFetch(model, void 0, { sanitizeSse: false }) : buildGuardedModelFetch(model);
|
|
445
234
|
if (model.provider === "github-copilot") {
|
|
@@ -453,7 +242,7 @@ function createAnthropicTransportClient(params) {
|
|
|
453
242
|
accept: "application/json",
|
|
454
243
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
455
244
|
...betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}
|
|
456
|
-
}, model.headers, getAiTransportHost().buildCopilotDynamicHeaders(context.messages),
|
|
245
|
+
}, model.headers, getAiTransportHost().buildCopilotDynamicHeaders(context.messages), optionHeaders),
|
|
457
246
|
fetch
|
|
458
247
|
}),
|
|
459
248
|
isOAuthToken: false
|
|
@@ -470,7 +259,7 @@ function createAnthropicTransportClient(params) {
|
|
|
470
259
|
accept: "application/json",
|
|
471
260
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
472
261
|
...betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}
|
|
473
|
-
}, omitFoundryBearerCredentialHeaders(model.headers),
|
|
262
|
+
}, omitFoundryBearerCredentialHeaders(model.headers), optionHeaders),
|
|
474
263
|
fetch
|
|
475
264
|
}),
|
|
476
265
|
isOAuthToken: false
|
|
@@ -491,7 +280,7 @@ function createAnthropicTransportClient(params) {
|
|
|
491
280
|
...betaHeader ? { "anthropic-beta": betaHeader } : {},
|
|
492
281
|
"user-agent": `claude-cli/${ANTHROPIC_CLAUDE_CODE_VERSION}`,
|
|
493
282
|
"x-app": "cli"
|
|
494
|
-
}, model.headers,
|
|
283
|
+
}, model.headers, optionHeaders),
|
|
495
284
|
fetch
|
|
496
285
|
}),
|
|
497
286
|
isOAuthToken: true
|
|
@@ -499,45 +288,39 @@ function createAnthropicTransportClient(params) {
|
|
|
499
288
|
}
|
|
500
289
|
if (useAnthropicServerSideFallback(model)) betaFeatures.push(ANTHROPIC_SERVER_SIDE_FALLBACK_BETA);
|
|
501
290
|
const betaHeader = buildAnthropicBetaHeader(model, betaFeatures, { oauth: false });
|
|
291
|
+
const defaultHeaders = mergeTransportHeaders({
|
|
292
|
+
accept: "application/json",
|
|
293
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
294
|
+
...betaHeader ? { "anthropic-beta": betaHeader } : {}
|
|
295
|
+
}, model.headers, optionHeaders);
|
|
502
296
|
return {
|
|
503
297
|
client: createAnthropicMessagesClient({
|
|
504
298
|
apiKey,
|
|
505
299
|
baseURL: model.baseUrl,
|
|
506
|
-
defaultHeaders
|
|
507
|
-
accept: "application/json",
|
|
508
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
509
|
-
...betaHeader ? { "anthropic-beta": betaHeader } : {}
|
|
510
|
-
}, model.headers, options?.headers),
|
|
300
|
+
defaultHeaders,
|
|
511
301
|
fetch
|
|
512
302
|
}),
|
|
513
|
-
isOAuthToken: false
|
|
303
|
+
isOAuthToken: false,
|
|
304
|
+
directApiKeyBetaHeader: isDirectAnthropicModel(model) ? new Headers(defaultHeaders).get("anthropic-beta") ?? "" : void 0
|
|
514
305
|
};
|
|
515
306
|
}
|
|
516
307
|
async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
517
|
-
const
|
|
518
|
-
const replayThinkingEnabled = mandatoryAdaptiveThinking || options?.thinkingEnabled === true;
|
|
308
|
+
const replayThinkingEnabled = requiresClaudeAdaptiveThinking(model) || options?.thinkingEnabled === true;
|
|
519
309
|
const maxTokens = resolveAnthropicMessagesMaxTokens({
|
|
520
310
|
modelContextWindow: model.contextWindow,
|
|
521
311
|
modelMaxTokens: model.maxTokens,
|
|
522
312
|
requestedMaxTokens: options?.maxTokens
|
|
523
313
|
});
|
|
524
314
|
if (maxTokens === void 0) throw new Error(`Anthropic Messages transport requires a positive maxTokens value for ${model.provider}/${model.id}`);
|
|
525
|
-
const
|
|
526
|
-
provider: model.provider,
|
|
527
|
-
api: model.api,
|
|
528
|
-
baseUrl: model.baseUrl,
|
|
529
|
-
cacheRetention: options?.cacheRetention,
|
|
530
|
-
enableCacheControl: true
|
|
531
|
-
}, model);
|
|
532
|
-
const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
|
|
315
|
+
const { cacheControl, supportsCacheControlOnTools } = resolveAnthropicCacheOptions(model, options?.cacheRetention);
|
|
533
316
|
const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
|
|
534
317
|
enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
|
|
535
318
|
authProfileId: options?.authProfileId,
|
|
536
319
|
sessionId: options?.sessionId
|
|
537
320
|
});
|
|
538
|
-
const messages = await convertAnthropicMessages(replayPlan.messages, model, isOAuthToken, {
|
|
321
|
+
const messages = await convertAnthropicMessages(transformTransportMessages(replayPlan.messages, model, normalizeAnthropicToolCallId), model, isOAuthToken, {
|
|
322
|
+
profile: "transport",
|
|
539
323
|
allowReasoningContentReplay: supportsReasoningContentReplay(model),
|
|
540
|
-
cacheBreakpointOptOutMessageIndexes,
|
|
541
324
|
compaction: replayPlan.compaction,
|
|
542
325
|
replayThinkingEnabled
|
|
543
326
|
});
|
|
@@ -548,54 +331,18 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
|
|
|
548
331
|
stream: true
|
|
549
332
|
};
|
|
550
333
|
if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks = ANTHROPIC_SERVER_SIDE_FALLBACKS;
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}] : []
|
|
564
|
-
];
|
|
565
|
-
else if (context.systemPrompt) params.system = [{
|
|
566
|
-
type: "text",
|
|
567
|
-
text: sanitizeTransportPayloadText(context.systemPrompt)
|
|
568
|
-
}];
|
|
569
|
-
if (options?.temperature !== void 0 && !options.thinkingEnabled && !supportsClaudeNativeXhighEffort(model)) params.temperature = options.temperature;
|
|
570
|
-
if (options?.stop !== void 0 && options.stop.length > 0) params.stop_sequences = options.stop;
|
|
571
|
-
let toolProjection;
|
|
572
|
-
if (context.tools) {
|
|
573
|
-
const convertedTools = convertAnthropicTools(context.tools, isOAuthToken);
|
|
574
|
-
toolProjection = convertedTools.projection;
|
|
575
|
-
if (convertedTools.tools.length > 0) params.tools = convertedTools.tools;
|
|
576
|
-
}
|
|
577
|
-
if (mandatoryAdaptiveThinking || model.reasoning || supportsClaudeAdaptiveThinking(model)) {
|
|
578
|
-
if (mandatoryAdaptiveThinking || options?.thinkingEnabled) {
|
|
579
|
-
if (supportsClaudeAdaptiveThinking(model)) {
|
|
580
|
-
params.thinking = {
|
|
581
|
-
type: "adaptive",
|
|
582
|
-
display: options?.thinkingDisplay ?? "summarized"
|
|
583
|
-
};
|
|
584
|
-
const effort = options?.effort ?? (mandatoryAdaptiveThinking ? "high" : void 0);
|
|
585
|
-
if (effort) params.output_config = { effort };
|
|
586
|
-
} else params.thinking = {
|
|
587
|
-
type: "enabled",
|
|
588
|
-
budget_tokens: options?.thinkingBudgetTokens ?? 1024
|
|
589
|
-
};
|
|
590
|
-
} else if (options?.thinkingEnabled === false) params.thinking = { type: "disabled" };
|
|
591
|
-
}
|
|
592
|
-
if (options?.metadata && typeof options.metadata.user_id === "string") params.metadata = { user_id: options.metadata.user_id };
|
|
593
|
-
if (options?.toolChoice) {
|
|
594
|
-
const normalizedToolChoice = normalizeAnthropicToolChoice(replayThinkingEnabled, options.toolChoice);
|
|
595
|
-
const projectedToolChoice = toolProjection ? reconcileAnthropicToolChoice(normalizedToolChoice, toolProjection) : normalizedToolChoice;
|
|
596
|
-
if (projectedToolChoice) params.tool_choice = projectedToolChoice;
|
|
597
|
-
}
|
|
598
|
-
applyAnthropicPayloadPolicyToParams(params, payloadPolicy, cacheBreakpointOptOutMessageIndexes);
|
|
334
|
+
const system = buildAnthropicSystemBlocks(context.systemPrompt, isOAuthToken, cacheControl);
|
|
335
|
+
if (system) params.system = system;
|
|
336
|
+
const convertedTools = context.tools ? convertAnthropicTools(context.tools, isOAuthToken) : void 0;
|
|
337
|
+
const toolProjection = convertedTools?.projection;
|
|
338
|
+
Object.assign(params, buildAnthropicGenerationParams({
|
|
339
|
+
model,
|
|
340
|
+
options,
|
|
341
|
+
tools: convertedTools?.tools,
|
|
342
|
+
toolProjection,
|
|
343
|
+
profile: "transport"
|
|
344
|
+
}));
|
|
345
|
+
applyAnthropicRequestCacheControl(params, cacheControl, supportsCacheControlOnTools);
|
|
599
346
|
return {
|
|
600
347
|
params,
|
|
601
348
|
toolProjection,
|
|
@@ -630,7 +377,9 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
|
|
|
630
377
|
toolChoice: options?.toolChoice,
|
|
631
378
|
thinkingBudgets: options?.thinkingBudgets,
|
|
632
379
|
reasoning,
|
|
633
|
-
|
|
380
|
+
anthropicServerCompaction: options?.anthropicServerCompaction,
|
|
381
|
+
anthropicCompactThreshold: options?.anthropicCompactThreshold,
|
|
382
|
+
cacheTtlPruning: options?.cacheTtlPruning,
|
|
634
383
|
...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
|
|
635
384
|
});
|
|
636
385
|
if (reasoning === "off") {
|
|
@@ -661,27 +410,15 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
661
410
|
const options = rawOptions;
|
|
662
411
|
const { eventStream, stream } = createWritableTransportEventStream();
|
|
663
412
|
(async () => {
|
|
664
|
-
const output =
|
|
665
|
-
|
|
666
|
-
content: [],
|
|
667
|
-
api: "anthropic-messages",
|
|
668
|
-
provider: model.provider,
|
|
669
|
-
model: model.id,
|
|
670
|
-
usage: createEmptyTransportUsage(),
|
|
671
|
-
stopReason: "stop",
|
|
672
|
-
timestamp: Date.now()
|
|
673
|
-
};
|
|
674
|
-
const refusalBuffer = usesClaudeStreamingRefusalContract(model) ? createDeferredEventBuffer(stream, () => notifyLlmRequestActivity(options?.signal)) : void 0;
|
|
675
|
-
const eventSink = refusalBuffer ?? stream;
|
|
676
|
-
let costModel = model;
|
|
677
|
-
let messageStartPromptUsage;
|
|
413
|
+
const output = createAssistantOutput(model, "anthropic-messages");
|
|
414
|
+
const refusalBuffer = usesClaudeStreamingRefusalContract(model) ? createDeferredEventBuffer(stream) : void 0;
|
|
678
415
|
let usedCompactionReplay = false;
|
|
679
416
|
try {
|
|
680
417
|
const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
|
|
681
418
|
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
682
419
|
const transportOptions = resolveAnthropicTransportOptions(model, options, apiKey);
|
|
683
420
|
const requestContext = prepareClaudeNoPrefillRequestContext(model, context);
|
|
684
|
-
const { client, isOAuthToken } = createAnthropicTransportClient({
|
|
421
|
+
const { client, isOAuthToken, directApiKeyBetaHeader } = createAnthropicTransportClient({
|
|
685
422
|
model,
|
|
686
423
|
context: requestContext,
|
|
687
424
|
apiKey,
|
|
@@ -691,13 +428,19 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
691
428
|
usedCompactionReplay = builtParams.usedCompactionReplay;
|
|
692
429
|
let params = builtParams.params;
|
|
693
430
|
const toolProjection = builtParams.toolProjection;
|
|
431
|
+
applyAnthropicContextManagementToRequest(params, model, transportOptions, directApiKeyBetaHeader);
|
|
694
432
|
const nextParams = await transportOptions.onPayload?.(params, model);
|
|
695
433
|
if (nextParams !== void 0) params = nextParams;
|
|
696
434
|
applyClaudeRequestContract(params, model);
|
|
435
|
+
const betaHeader = resolveAnthropicContextManagementBetaHeader(params, directApiKeyBetaHeader);
|
|
436
|
+
const bindingHeaders = applyAnthropicThinkingBindingControls(params, betaHeader) ?? (betaHeader !== void 0 ? { "anthropic-beta": betaHeader } : void 0);
|
|
697
437
|
const { response, stream: anthropicStream } = await client.messages.stream({
|
|
698
438
|
...params,
|
|
699
439
|
stream: true
|
|
700
|
-
},
|
|
440
|
+
}, {
|
|
441
|
+
signal: transportOptions.signal,
|
|
442
|
+
headers: bindingHeaders
|
|
443
|
+
});
|
|
701
444
|
await notifyProviderHttpResponse({
|
|
702
445
|
options: transportOptions,
|
|
703
446
|
response,
|
|
@@ -707,429 +450,17 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
707
450
|
const detail = await readAnthropicMessagesErrorBodySnippet(response);
|
|
708
451
|
throw new Error(formatAnthropicMessagesHttpError(response, detail));
|
|
709
452
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
const flushPendingTextEnds = () => {
|
|
722
|
-
for (const event of pendingTextEnds) eventSink.push(event);
|
|
723
|
-
pendingTextEnds.length = 0;
|
|
724
|
-
};
|
|
725
|
-
const eventIndexKey = (eventIndex) => typeof eventIndex === "number" ? eventIndex : -1;
|
|
726
|
-
const appendReasoningContentThinkingDelta = (eventIndex, rawText) => {
|
|
727
|
-
if (typeof rawText !== "string") return false;
|
|
728
|
-
const text = sanitizeTransportPayloadText(rawText);
|
|
729
|
-
if (text.length === 0) return false;
|
|
730
|
-
const key = eventIndexKey(eventIndex);
|
|
731
|
-
let contentIndex = reasoningContentThinkingBlocks.get(key);
|
|
732
|
-
let block = contentIndex === void 0 ? void 0 : output.content[contentIndex];
|
|
733
|
-
if (!block || block.type !== "thinking") {
|
|
734
|
-
block = {
|
|
735
|
-
type: "thinking",
|
|
736
|
-
thinking: "",
|
|
737
|
-
thinkingSignature: "reasoning_content"
|
|
738
|
-
};
|
|
739
|
-
output.content.push(block);
|
|
740
|
-
contentIndex = output.content.length - 1;
|
|
741
|
-
reasoningContentThinkingBlocks.set(key, contentIndex);
|
|
742
|
-
eventSink.push({
|
|
743
|
-
type: "thinking_start",
|
|
744
|
-
contentIndex,
|
|
745
|
-
partial: output
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
if (contentIndex === void 0) return false;
|
|
749
|
-
block.thinking += text;
|
|
750
|
-
block.thinkingSignature = "reasoning_content";
|
|
751
|
-
eventSink.push({
|
|
752
|
-
type: "thinking_delta",
|
|
753
|
-
contentIndex,
|
|
754
|
-
delta: text,
|
|
755
|
-
partial: output
|
|
756
|
-
});
|
|
757
|
-
return true;
|
|
758
|
-
};
|
|
759
|
-
const appendReasoningContentTextDelta = (eventIndex, rawText) => {
|
|
760
|
-
if (typeof rawText !== "string") return false;
|
|
761
|
-
const text = sanitizeTransportPayloadText(rawText);
|
|
762
|
-
if (text.length === 0) return false;
|
|
763
|
-
const key = eventIndexKey(eventIndex);
|
|
764
|
-
let contentIndex = reasoningContentTextBlocks.get(key);
|
|
765
|
-
let block = contentIndex === void 0 ? void 0 : output.content[contentIndex];
|
|
766
|
-
if (!block || block.type !== "text") {
|
|
767
|
-
block = {
|
|
768
|
-
type: "text",
|
|
769
|
-
text: ""
|
|
770
|
-
};
|
|
771
|
-
output.content.push(block);
|
|
772
|
-
contentIndex = output.content.length - 1;
|
|
773
|
-
reasoningContentTextBlocks.set(key, contentIndex);
|
|
774
|
-
eventSink.push({
|
|
775
|
-
type: "text_start",
|
|
776
|
-
contentIndex,
|
|
777
|
-
partial: output
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
if (contentIndex === void 0) return false;
|
|
781
|
-
block.text += text;
|
|
782
|
-
eventSink.push({
|
|
783
|
-
type: "text_delta",
|
|
784
|
-
contentIndex,
|
|
785
|
-
delta: text,
|
|
786
|
-
partial: output
|
|
787
|
-
});
|
|
788
|
-
return true;
|
|
789
|
-
};
|
|
790
|
-
const finishReasoningContentSidecars = (eventIndex) => {
|
|
791
|
-
const key = eventIndexKey(eventIndex);
|
|
792
|
-
const thinkingContentIndex = reasoningContentThinkingBlocks.get(key);
|
|
793
|
-
if (thinkingContentIndex !== void 0) {
|
|
794
|
-
reasoningContentThinkingBlocks.delete(key);
|
|
795
|
-
const block = output.content[thinkingContentIndex];
|
|
796
|
-
if (block?.type === "thinking") eventSink.push({
|
|
797
|
-
type: "thinking_end",
|
|
798
|
-
contentIndex: thinkingContentIndex,
|
|
799
|
-
content: block.thinking,
|
|
800
|
-
partial: output
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
const textContentIndex = reasoningContentTextBlocks.get(key);
|
|
804
|
-
if (textContentIndex === void 0) return;
|
|
805
|
-
reasoningContentTextBlocks.delete(key);
|
|
806
|
-
const block = output.content[textContentIndex];
|
|
807
|
-
if (block?.type === "text") eventSink.push({
|
|
808
|
-
type: "text_end",
|
|
809
|
-
contentIndex: textContentIndex,
|
|
810
|
-
content: block.text,
|
|
811
|
-
partial: output
|
|
812
|
-
});
|
|
813
|
-
};
|
|
814
|
-
for await (const event of anthropicStream) {
|
|
815
|
-
if (event.type === "error") {
|
|
816
|
-
const error = event.error;
|
|
817
|
-
throw new Error(error?.message || "Anthropic Messages stream failed");
|
|
818
|
-
}
|
|
819
|
-
if (event.type === "message_start") {
|
|
820
|
-
const message = event.message;
|
|
821
|
-
const usage = message?.usage ?? {};
|
|
822
|
-
output.responseId = typeof message?.id === "string" ? message.id : void 0;
|
|
823
|
-
output.responseModel = typeof message?.model === "string" ? message.model : void 0;
|
|
824
|
-
messageStartPromptUsage = applyAnthropicMessageStartUsage(output.usage, usage);
|
|
825
|
-
calculateCost(costModel, output.usage);
|
|
826
|
-
eventSink.push({
|
|
827
|
-
type: "start",
|
|
828
|
-
partial: output
|
|
829
|
-
});
|
|
830
|
-
continue;
|
|
831
|
-
}
|
|
832
|
-
if (event.type === "message_stop") {
|
|
833
|
-
sawMessageStop = true;
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
if (event.type === "content_block_start") {
|
|
837
|
-
const contentBlock = event.content_block;
|
|
838
|
-
const index = typeof event.index === "number" ? event.index : -1;
|
|
839
|
-
if (transportOptions.anthropicServerCompaction === true && compactionCapture.begin(index, contentBlock, output.content.length)) continue;
|
|
840
|
-
const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
|
|
841
|
-
if (fallbackBoundary) {
|
|
842
|
-
refusalBuffer?.discard();
|
|
843
|
-
sealedToolCalls.length = 0;
|
|
844
|
-
pendingTextEnds.length = 0;
|
|
845
|
-
blockIndexes.clear();
|
|
846
|
-
pendingThinkingSignatures.clear();
|
|
847
|
-
applyAnthropicFallbackBoundary({
|
|
848
|
-
output,
|
|
849
|
-
boundary: fallbackBoundary,
|
|
850
|
-
provider: model.provider
|
|
851
|
-
});
|
|
852
|
-
costModel = {
|
|
853
|
-
...model,
|
|
854
|
-
cost: resolveAnthropicFallbackServingModelCost({
|
|
855
|
-
requestedModelId: model.id,
|
|
856
|
-
servingModelId: fallbackBoundary.toModel,
|
|
857
|
-
requestedCost: model.cost
|
|
858
|
-
})
|
|
859
|
-
};
|
|
860
|
-
calculateCost(costModel, output.usage);
|
|
861
|
-
eventSink.push({
|
|
862
|
-
type: "start",
|
|
863
|
-
partial: output
|
|
864
|
-
});
|
|
865
|
-
for (const [i, block] of output.content.entries()) {
|
|
866
|
-
if (block.type !== "text") continue;
|
|
867
|
-
delete block.index;
|
|
868
|
-
eventSink.push({
|
|
869
|
-
type: "text_start",
|
|
870
|
-
contentIndex: i,
|
|
871
|
-
partial: output
|
|
872
|
-
});
|
|
873
|
-
if (block.text) eventSink.push({
|
|
874
|
-
type: "text_delta",
|
|
875
|
-
contentIndex: i,
|
|
876
|
-
delta: block.text,
|
|
877
|
-
partial: output
|
|
878
|
-
});
|
|
879
|
-
pendingTextEnds.push({
|
|
880
|
-
type: "text_end",
|
|
881
|
-
contentIndex: i,
|
|
882
|
-
content: block.text,
|
|
883
|
-
partial: output
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
pendingThinkingSignatures.delete(index);
|
|
889
|
-
if (contentBlock?.type === "text") {
|
|
890
|
-
const text = typeof contentBlock.text === "string" ? sanitizeTransportPayloadText(contentBlock.text) : "";
|
|
891
|
-
const block = {
|
|
892
|
-
type: "text",
|
|
893
|
-
text,
|
|
894
|
-
index
|
|
895
|
-
};
|
|
896
|
-
output.content.push(block);
|
|
897
|
-
const contentIndex = output.content.length - 1;
|
|
898
|
-
blockIndexes.set(index, contentIndex);
|
|
899
|
-
eventSink.push({
|
|
900
|
-
type: "text_start",
|
|
901
|
-
contentIndex,
|
|
902
|
-
partial: output
|
|
903
|
-
});
|
|
904
|
-
if (text.length > 0) eventSink.push({
|
|
905
|
-
type: "text_delta",
|
|
906
|
-
contentIndex,
|
|
907
|
-
delta: text,
|
|
908
|
-
partial: output
|
|
909
|
-
});
|
|
910
|
-
continue;
|
|
911
|
-
}
|
|
912
|
-
if (contentBlock?.type === "thinking") {
|
|
913
|
-
const thinking = typeof contentBlock.thinking === "string" ? contentBlock.thinking : "";
|
|
914
|
-
const block = {
|
|
915
|
-
type: "thinking",
|
|
916
|
-
thinking,
|
|
917
|
-
thinkingSignature: typeof contentBlock.signature === "string" ? contentBlock.signature : "",
|
|
918
|
-
index
|
|
919
|
-
};
|
|
920
|
-
output.content.push(block);
|
|
921
|
-
const contentIndex = output.content.length - 1;
|
|
922
|
-
blockIndexes.set(index, contentIndex);
|
|
923
|
-
eventSink.push({
|
|
924
|
-
type: "thinking_start",
|
|
925
|
-
contentIndex,
|
|
926
|
-
partial: output
|
|
927
|
-
});
|
|
928
|
-
if (thinking.length > 0) eventSink.push({
|
|
929
|
-
type: "thinking_delta",
|
|
930
|
-
contentIndex,
|
|
931
|
-
delta: thinking,
|
|
932
|
-
partial: output
|
|
933
|
-
});
|
|
934
|
-
continue;
|
|
935
|
-
}
|
|
936
|
-
if (contentBlock?.type === "redacted_thinking") {
|
|
937
|
-
const block = {
|
|
938
|
-
type: "thinking",
|
|
939
|
-
thinking: "[Reasoning redacted]",
|
|
940
|
-
thinkingSignature: typeof contentBlock.data === "string" ? contentBlock.data : "",
|
|
941
|
-
redacted: true,
|
|
942
|
-
index
|
|
943
|
-
};
|
|
944
|
-
output.content.push(block);
|
|
945
|
-
blockIndexes.set(index, output.content.length - 1);
|
|
946
|
-
eventSink.push({
|
|
947
|
-
type: "thinking_start",
|
|
948
|
-
contentIndex: output.content.length - 1,
|
|
949
|
-
partial: output
|
|
950
|
-
});
|
|
951
|
-
continue;
|
|
952
|
-
}
|
|
953
|
-
if (contentBlock?.type === "tool_use") {
|
|
954
|
-
tagPendingCommentaryText(output.content);
|
|
955
|
-
flushPendingTextEnds();
|
|
956
|
-
const block = {
|
|
957
|
-
type: "toolCall",
|
|
958
|
-
id: typeof contentBlock.id === "string" ? contentBlock.id : "",
|
|
959
|
-
name: typeof contentBlock.name === "string" ? isOAuthToken ? resolveOriginalAnthropicToolName(contentBlock.name, toolProjection) : contentBlock.name : "",
|
|
960
|
-
arguments: contentBlock.input && typeof contentBlock.input === "object" ? contentBlock.input : {},
|
|
961
|
-
partialJson: "",
|
|
962
|
-
index
|
|
963
|
-
};
|
|
964
|
-
output.content.push(block);
|
|
965
|
-
blockIndexes.set(index, output.content.length - 1);
|
|
966
|
-
toolArgumentPreviewSchedules.set(block, createToolArgumentPreviewSchedule());
|
|
967
|
-
eventSink.push({
|
|
968
|
-
type: "toolcall_start",
|
|
969
|
-
contentIndex: output.content.length - 1,
|
|
970
|
-
partial: output
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
|
-
continue;
|
|
974
|
-
}
|
|
975
|
-
if (event.type === "content_block_delta") {
|
|
976
|
-
const delta = event.delta;
|
|
977
|
-
const eventIndex = typeof event.index === "number" ? event.index : void 0;
|
|
978
|
-
if (eventIndex !== void 0 && compactionCapture.delta(eventIndex, delta)) continue;
|
|
979
|
-
let index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
|
|
980
|
-
let block = index === void 0 ? void 0 : blocks[index];
|
|
981
|
-
if (allowReasoningContentReplay) {
|
|
982
|
-
const appendedThinking = appendReasoningContentThinkingDelta(event.index, delta?.reasoning_content);
|
|
983
|
-
const hasNativeAnthropicDelta = delta?.type === "text_delta" && typeof delta.text === "string" || delta?.type === "thinking_delta" && typeof delta.thinking === "string" || delta?.type === "input_json_delta" && typeof delta.partial_json === "string" || delta?.type === "signature_delta" && typeof delta.signature === "string";
|
|
984
|
-
let appendedContent = false;
|
|
985
|
-
if (!hasNativeAnthropicDelta && typeof delta?.content === "string" && delta.content.length > 0) {
|
|
986
|
-
const text = sanitizeTransportPayloadText(delta.content);
|
|
987
|
-
if (text.length > 0) {
|
|
988
|
-
if (block?.type === "text" && index !== void 0) {
|
|
989
|
-
block.text += text;
|
|
990
|
-
eventSink.push({
|
|
991
|
-
type: "text_delta",
|
|
992
|
-
contentIndex: index,
|
|
993
|
-
delta: text,
|
|
994
|
-
partial: output
|
|
995
|
-
});
|
|
996
|
-
appendedContent = true;
|
|
997
|
-
} else appendedContent = appendReasoningContentTextDelta(event.index, text);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
if ((appendedThinking || appendedContent) && !hasNativeAnthropicDelta) continue;
|
|
1001
|
-
}
|
|
1002
|
-
if (!block && delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1003
|
-
block = {
|
|
1004
|
-
type: "text",
|
|
1005
|
-
text: "",
|
|
1006
|
-
index: typeof event.index === "number" ? event.index : blocks.length
|
|
1007
|
-
};
|
|
1008
|
-
output.content.push(block);
|
|
1009
|
-
index = output.content.length - 1;
|
|
1010
|
-
if (typeof event.index === "number") blockIndexes.set(event.index, index);
|
|
1011
|
-
eventSink.push({
|
|
1012
|
-
type: "text_start",
|
|
1013
|
-
contentIndex: index,
|
|
1014
|
-
partial: output
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
if (index === void 0) continue;
|
|
1018
|
-
if (block?.type === "text" && delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1019
|
-
block.text += delta.text;
|
|
1020
|
-
eventSink.push({
|
|
1021
|
-
type: "text_delta",
|
|
1022
|
-
contentIndex: index,
|
|
1023
|
-
delta: delta.text,
|
|
1024
|
-
partial: output
|
|
1025
|
-
});
|
|
1026
|
-
continue;
|
|
1027
|
-
}
|
|
1028
|
-
if (block?.type === "thinking" && delta?.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
1029
|
-
block.thinking += delta.thinking;
|
|
1030
|
-
eventSink.push({
|
|
1031
|
-
type: "thinking_delta",
|
|
1032
|
-
contentIndex: index,
|
|
1033
|
-
delta: delta.thinking,
|
|
1034
|
-
partial: output
|
|
1035
|
-
});
|
|
1036
|
-
continue;
|
|
1037
|
-
}
|
|
1038
|
-
if (block?.type === "toolCall" && delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
1039
|
-
const partialJson = `${block.partialJson ?? ""}${delta.partial_json}`;
|
|
1040
|
-
block.partialJson = partialJson;
|
|
1041
|
-
if (toolArgumentPreviewSchedules.get(block)?.(partialJson.length)) block.arguments = parseAnthropicToolCallArguments(partialJson);
|
|
1042
|
-
eventSink.push({
|
|
1043
|
-
type: "toolcall_delta",
|
|
1044
|
-
contentIndex: index,
|
|
1045
|
-
delta: delta.partial_json,
|
|
1046
|
-
partial: output
|
|
1047
|
-
});
|
|
1048
|
-
continue;
|
|
1049
|
-
}
|
|
1050
|
-
if (block?.type === "thinking" && delta?.type === "signature_delta" && typeof delta.signature === "string") {
|
|
1051
|
-
const signatureIndex = eventIndexKey(event.index);
|
|
1052
|
-
const pendingSignature = pendingThinkingSignatures.get(signatureIndex);
|
|
1053
|
-
if (pendingSignature === void 0) {
|
|
1054
|
-
block.thinkingSignature = "";
|
|
1055
|
-
pendingThinkingSignatures.set(signatureIndex, delta.signature);
|
|
1056
|
-
} else pendingThinkingSignatures.set(signatureIndex, pendingSignature + delta.signature);
|
|
1057
|
-
}
|
|
1058
|
-
continue;
|
|
1059
|
-
}
|
|
1060
|
-
if (event.type === "content_block_stop") {
|
|
1061
|
-
const eventIndex = typeof event.index === "number" ? event.index : void 0;
|
|
1062
|
-
if (eventIndex !== void 0 && compactionCapture.complete(eventIndex)) continue;
|
|
1063
|
-
const pendingSignature = eventIndex === void 0 ? void 0 : pendingThinkingSignatures.get(eventIndex);
|
|
1064
|
-
if (eventIndex !== void 0) pendingThinkingSignatures.delete(eventIndex);
|
|
1065
|
-
const index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
|
|
1066
|
-
const block = index === void 0 ? void 0 : blocks[index];
|
|
1067
|
-
if (eventIndex === void 0 || index === void 0 || !block) {
|
|
1068
|
-
finishReasoningContentSidecars(event.index);
|
|
1069
|
-
continue;
|
|
1070
|
-
}
|
|
1071
|
-
blockIndexes.delete(eventIndex);
|
|
1072
|
-
delete block.index;
|
|
1073
|
-
if (block.type === "text") {
|
|
1074
|
-
pendingTextEnds.push({
|
|
1075
|
-
type: "text_end",
|
|
1076
|
-
contentIndex: index,
|
|
1077
|
-
content: block.text,
|
|
1078
|
-
partial: output
|
|
1079
|
-
});
|
|
1080
|
-
finishReasoningContentSidecars(event.index);
|
|
1081
|
-
continue;
|
|
1082
|
-
}
|
|
1083
|
-
if (block.type === "thinking") {
|
|
1084
|
-
if (pendingSignature !== void 0) block.thinkingSignature = pendingSignature;
|
|
1085
|
-
eventSink.push({
|
|
1086
|
-
type: "thinking_end",
|
|
1087
|
-
contentIndex: index,
|
|
1088
|
-
content: block.thinking,
|
|
1089
|
-
partial: output
|
|
1090
|
-
});
|
|
1091
|
-
finishReasoningContentSidecars(event.index);
|
|
1092
|
-
continue;
|
|
1093
|
-
}
|
|
1094
|
-
if (block.type === "toolCall") {
|
|
1095
|
-
sealedToolCalls.push({
|
|
1096
|
-
block,
|
|
1097
|
-
contentIndex: index
|
|
1098
|
-
});
|
|
1099
|
-
finishReasoningContentSidecars(event.index);
|
|
1100
|
-
}
|
|
1101
|
-
continue;
|
|
1102
|
-
}
|
|
1103
|
-
if (event.type === "message_delta") {
|
|
1104
|
-
const delta = event.delta;
|
|
1105
|
-
const usage = event.usage;
|
|
1106
|
-
if (delta?.stop_reason) {
|
|
1107
|
-
if (delta.stop_reason === "refusal") applyAnthropicRefusal(output, delta.stop_details, model.provider);
|
|
1108
|
-
else output.stopReason = mapAnthropicStopReason(delta.stop_reason);
|
|
1109
|
-
}
|
|
1110
|
-
applyAnthropicMessageDeltaUsage(output.usage, usage, messageStartPromptUsage);
|
|
1111
|
-
calculateCost(costModel, output.usage);
|
|
1112
|
-
if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
|
|
1113
|
-
flushPendingTextEnds();
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
|
|
1117
|
-
if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
|
|
1118
|
-
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1119
|
-
if ([...blockIndexes.values()].some((index) => blocks[index]?.type === "toolCall")) throw new Error("Provider completed stream with an incomplete tool call");
|
|
1120
|
-
finalizeTerminalToolCallArguments(sealedToolCalls.map(({ block }) => block), (block) => block.partialJson && block.partialJson.length > 0 ? block.partialJson : block.arguments);
|
|
1121
|
-
for (const sealed of sealedToolCalls) {
|
|
1122
|
-
delete sealed.block.partialJson;
|
|
1123
|
-
eventSink.push({
|
|
1124
|
-
type: "toolcall_end",
|
|
1125
|
-
contentIndex: sealed.contentIndex,
|
|
1126
|
-
toolCall: sealed.block,
|
|
1127
|
-
partial: output
|
|
1128
|
-
});
|
|
1129
|
-
}
|
|
1130
|
-
refusalBuffer?.flush();
|
|
1131
|
-
if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
|
|
1132
|
-
flushPendingTextEnds();
|
|
453
|
+
await consumeAnthropicStream({
|
|
454
|
+
events: anthropicStream,
|
|
455
|
+
model,
|
|
456
|
+
options: transportOptions,
|
|
457
|
+
output,
|
|
458
|
+
stream,
|
|
459
|
+
refusalBuffer,
|
|
460
|
+
isOAuthToken,
|
|
461
|
+
toolProjection,
|
|
462
|
+
profile: "transport"
|
|
463
|
+
});
|
|
1133
464
|
finalizeTransportStream({
|
|
1134
465
|
stream,
|
|
1135
466
|
output
|
|
@@ -1155,63 +486,6 @@ function createAnthropicMessagesTransportStreamFn() {
|
|
|
1155
486
|
};
|
|
1156
487
|
}
|
|
1157
488
|
//#endregion
|
|
1158
|
-
//#region packages/ai/src/transports/model-max-tokens-params.ts
|
|
1159
|
-
/**
|
|
1160
|
-
* Max-token parameter normalization across provider/native naming variants.
|
|
1161
|
-
* Callers canonicalize aliases before dispatch so payloads cannot carry
|
|
1162
|
-
* conflicting limits.
|
|
1163
|
-
*/
|
|
1164
|
-
const MAX_TOKENS_PARAM_KEYS = [
|
|
1165
|
-
"maxTokens",
|
|
1166
|
-
"max_completion_tokens",
|
|
1167
|
-
"max_tokens"
|
|
1168
|
-
];
|
|
1169
|
-
/** Resolve the first supported max-token parameter present in a params object. */
|
|
1170
|
-
function resolveMaxTokensParam(params) {
|
|
1171
|
-
if (!params) return;
|
|
1172
|
-
for (const key of MAX_TOKENS_PARAM_KEYS) {
|
|
1173
|
-
const resolved = asNonNegativeFiniteNumber(params[key]);
|
|
1174
|
-
if (resolved !== void 0) return resolved;
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Canonicalize merged params to `maxTokens`, preserving source precedence from
|
|
1179
|
-
* left to right across the provided source objects.
|
|
1180
|
-
*/
|
|
1181
|
-
function canonicalizeMaxTokensParam(params) {
|
|
1182
|
-
let resolved;
|
|
1183
|
-
for (const source of params.sources) {
|
|
1184
|
-
const sourceValue = resolveMaxTokensParam(source);
|
|
1185
|
-
if (sourceValue !== void 0) resolved = sourceValue;
|
|
1186
|
-
}
|
|
1187
|
-
if (resolved === void 0) return;
|
|
1188
|
-
for (const key of MAX_TOKENS_PARAM_KEYS) delete params.merged[key];
|
|
1189
|
-
params.merged.maxTokens = resolved;
|
|
1190
|
-
}
|
|
1191
|
-
//#endregion
|
|
1192
|
-
//#region packages/ai/src/transports/model-transport-url.ts
|
|
1193
|
-
/**
|
|
1194
|
-
* Debug formatting helpers for model transport endpoints.
|
|
1195
|
-
* Keeps logs useful without exposing credentials, request params, or fragments.
|
|
1196
|
-
*/
|
|
1197
|
-
/** Return a sanitized URL suitable for logs and diagnostics. */
|
|
1198
|
-
function formatModelTransportDebugUrl(rawUrl) {
|
|
1199
|
-
try {
|
|
1200
|
-
const parsed = new URL(rawUrl);
|
|
1201
|
-
parsed.username = "";
|
|
1202
|
-
parsed.password = "";
|
|
1203
|
-
parsed.search = "";
|
|
1204
|
-
parsed.hash = "";
|
|
1205
|
-
return parsed.toString();
|
|
1206
|
-
} catch {
|
|
1207
|
-
return "<invalid-url>";
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
/** Format a configured base URL for debug output, or the implicit default. */
|
|
1211
|
-
function formatModelTransportDebugBaseUrl(rawUrl) {
|
|
1212
|
-
return rawUrl ? formatModelTransportDebugUrl(rawUrl) : "default";
|
|
1213
|
-
}
|
|
1214
|
-
//#endregion
|
|
1215
489
|
//#region packages/ai/src/transports/openai-compatible-conversation-turn.ts
|
|
1216
490
|
/**
|
|
1217
491
|
* OpenAI-compatible conversation turn detector.
|
|
@@ -1248,488 +522,44 @@ function hasOpenAICompatibleConversationTurn(messages) {
|
|
|
1248
522
|
});
|
|
1249
523
|
}
|
|
1250
524
|
//#endregion
|
|
1251
|
-
//#region packages/ai/src/transports/openai-completions-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
function flattenStringOnlyCompletionContent(content) {
|
|
1257
|
-
if (!Array.isArray(content)) return content;
|
|
1258
|
-
const textParts = [];
|
|
1259
|
-
for (const item of content) {
|
|
1260
|
-
if (!item || typeof item !== "object" || item.type !== "text" || typeof item.text !== "string") return content;
|
|
1261
|
-
textParts.push(item.text);
|
|
1262
|
-
}
|
|
1263
|
-
return textParts.join("\n");
|
|
1264
|
-
}
|
|
1265
|
-
/** Flatten string-only text block content arrays into newline-joined strings. */
|
|
1266
|
-
function flattenCompletionMessagesToStringContent(messages) {
|
|
1267
|
-
return messages.map((message) => {
|
|
1268
|
-
if (!message || typeof message !== "object") return message;
|
|
1269
|
-
const content = message.content;
|
|
1270
|
-
const flattenedContent = flattenStringOnlyCompletionContent(content);
|
|
1271
|
-
if (flattenedContent === content) return message;
|
|
1272
|
-
return {
|
|
1273
|
-
...message,
|
|
1274
|
-
content: flattenedContent
|
|
1275
|
-
};
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
/** Strip completion messages to role/content fields for strict providers. */
|
|
1279
|
-
function stripCompletionMessagesToRoleContent(messages) {
|
|
1280
|
-
return messages.map((message) => {
|
|
1281
|
-
if (!message || typeof message !== "object" || Array.isArray(message)) return message;
|
|
1282
|
-
const record = message;
|
|
1283
|
-
const stripped = {};
|
|
1284
|
-
if (Object.hasOwn(record, "role")) stripped.role = record.role;
|
|
1285
|
-
if (Object.hasOwn(record, "content")) stripped.content = record.content;
|
|
1286
|
-
return stripped;
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
//#endregion
|
|
1290
|
-
//#region packages/ai/src/transports/openai-completions-host.ts
|
|
1291
|
-
/**
|
|
1292
|
-
* Chat Completions accepts Azure AI Foundry hosts in addition to traditional
|
|
1293
|
-
* Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
|
|
1294
|
-
* which intentionally excludes the .services.ai.azure.com Foundry suffix.
|
|
1295
|
-
*/
|
|
1296
|
-
function isAzureOpenAICompatibleHost(hostname) {
|
|
1297
|
-
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
525
|
+
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
526
|
+
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
527
|
+
const messages = params.messages;
|
|
528
|
+
if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
|
|
529
|
+
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.`);
|
|
1298
530
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
function
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
|
|
1317
|
-
for (const msg of context.messages ?? []) {
|
|
1318
|
-
if (msg.role !== "assistant") continue;
|
|
1319
|
-
const source = msg;
|
|
1320
|
-
if (!Array.isArray(source.content)) continue;
|
|
1321
|
-
for (const block of source.content) {
|
|
1322
|
-
if (block.type !== "toolCall") continue;
|
|
1323
|
-
const id = block.id;
|
|
1324
|
-
const sig = block.thoughtSignature;
|
|
1325
|
-
if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
|
|
1326
|
-
const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
|
|
1327
|
-
if (!isSameRoute && !fallbackSig) continue;
|
|
1328
|
-
sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
if (sigById.size === 0 && !fallbackSig) return;
|
|
1333
|
-
for (const message of outgoingMessages) {
|
|
1334
|
-
const toolCalls = message.tool_calls;
|
|
1335
|
-
if (!Array.isArray(toolCalls)) continue;
|
|
1336
|
-
for (const toolCall of toolCalls) {
|
|
1337
|
-
const id = toolCall.id;
|
|
1338
|
-
if (typeof id !== "string") continue;
|
|
1339
|
-
let sig = sigById.get(id) ?? fallbackSig;
|
|
1340
|
-
if (typeof sig === "string" && sig.length > 0) {
|
|
1341
|
-
if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
|
|
531
|
+
const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
|
|
532
|
+
const SSE_DONE_MAX_LINE_CHARS = 1024;
|
|
533
|
+
function createSseDoneDetector() {
|
|
534
|
+
const decoder = new TextDecoder();
|
|
535
|
+
let line = "";
|
|
536
|
+
let lineOverflowed = false;
|
|
537
|
+
let sawDone = false;
|
|
538
|
+
const finishLine = () => {
|
|
539
|
+
if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
|
|
540
|
+
line = "";
|
|
541
|
+
lineOverflowed = false;
|
|
542
|
+
};
|
|
543
|
+
const observeText = (text) => {
|
|
544
|
+
for (const char of text) {
|
|
545
|
+
if (char === "\n" || char === "\r") {
|
|
546
|
+
finishLine();
|
|
547
|
+
continue;
|
|
1342
548
|
}
|
|
1343
|
-
if (
|
|
1344
|
-
|
|
1345
|
-
toolCall.extra_content = extra;
|
|
1346
|
-
const google = extra.google && typeof extra.google === "object" ? extra.google : {};
|
|
1347
|
-
extra.google = google;
|
|
1348
|
-
google.thought_signature = sig;
|
|
549
|
+
if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
|
|
550
|
+
else lineOverflowed = true;
|
|
1349
551
|
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
const COMPLETIONS_REASONING_REPLAY_FIELDS = [
|
|
1353
|
-
"reasoning_details",
|
|
1354
|
-
"reasoning_content",
|
|
1355
|
-
"reasoning",
|
|
1356
|
-
"reasoning_text"
|
|
1357
|
-
];
|
|
1358
|
-
function stripCompletionsReasoningReplayFields(record) {
|
|
1359
|
-
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
|
|
1360
|
-
}
|
|
1361
|
-
function sanitizeOpenRouterReasoningReplayFields(record) {
|
|
1362
|
-
const reasoningDetails = record.reasoning_details;
|
|
1363
|
-
if (typeof reasoningDetails === "string") {
|
|
1364
|
-
if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
|
|
1365
|
-
delete record.reasoning_details;
|
|
1366
|
-
} else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
|
|
1367
|
-
if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
|
|
1368
|
-
if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
|
|
1369
|
-
const reasoningText = record.reasoning_text;
|
|
1370
|
-
if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
|
|
1371
|
-
if ("reasoning_text" in record) delete record.reasoning_text;
|
|
1372
|
-
}
|
|
1373
|
-
function sanitizeReasoningContentReplayFields(record) {
|
|
1374
|
-
if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
|
|
1375
|
-
delete record.reasoning_details;
|
|
1376
|
-
delete record.reasoning;
|
|
1377
|
-
delete record.reasoning_text;
|
|
1378
|
-
}
|
|
1379
|
-
const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
1380
|
-
"deepseek-v4-flash",
|
|
1381
|
-
"deepseek-v4-pro",
|
|
1382
|
-
"kimi-for-coding",
|
|
1383
|
-
"kimi-k2.5",
|
|
1384
|
-
"kimi-k2.6",
|
|
1385
|
-
"kimi-k2.7-code",
|
|
1386
|
-
"kimi-k2.7-code-highspeed",
|
|
1387
|
-
"kimi-k3",
|
|
1388
|
-
"kimi-k2-thinking",
|
|
1389
|
-
"kimi-k2-thinking-turbo",
|
|
1390
|
-
"mimo-v2-pro",
|
|
1391
|
-
"mimo-v2-omni",
|
|
1392
|
-
"mimo-v2.5",
|
|
1393
|
-
"mimo-v2.5-pro",
|
|
1394
|
-
"mimo-v2.6-pro"
|
|
1395
|
-
]);
|
|
1396
|
-
const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
|
|
1397
|
-
"-free",
|
|
1398
|
-
"-paid",
|
|
1399
|
-
"-trial"
|
|
1400
|
-
];
|
|
1401
|
-
function stripReasoningContentReplayTierSuffix(modelId) {
|
|
1402
|
-
for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
|
|
1403
|
-
return modelId;
|
|
1404
|
-
}
|
|
1405
|
-
function getReasoningContentReplayModelIdCandidates(modelId) {
|
|
1406
|
-
if (typeof modelId !== "string") return [];
|
|
1407
|
-
const normalized = modelId.trim().toLowerCase();
|
|
1408
|
-
if (!normalized) return [];
|
|
1409
|
-
const parts = normalized.split("/").filter(Boolean);
|
|
1410
|
-
const finalPart = parts[parts.length - 1] ?? normalized;
|
|
1411
|
-
const candidates = [finalPart];
|
|
1412
|
-
const colonParts = finalPart.split(":").filter(Boolean);
|
|
1413
|
-
if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
|
|
1414
|
-
const baseCount = candidates.length;
|
|
1415
|
-
for (let index = 0; index < baseCount; index += 1) {
|
|
1416
|
-
const candidate = candidates[index];
|
|
1417
|
-
if (typeof candidate !== "string") continue;
|
|
1418
|
-
const stripped = stripReasoningContentReplayTierSuffix(candidate);
|
|
1419
|
-
if (stripped !== candidate) candidates.push(stripped);
|
|
1420
|
-
}
|
|
1421
|
-
return uniqueStrings(candidates.filter(Boolean));
|
|
1422
|
-
}
|
|
1423
|
-
function shouldPreserveReasoningContentReplay(model, compat) {
|
|
1424
|
-
if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
|
|
1425
|
-
return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
|
|
1426
|
-
}
|
|
1427
|
-
function shouldPreserveOpenRouterReasoningReplay(model) {
|
|
1428
|
-
if (model.provider !== "openrouter") return true;
|
|
1429
|
-
const normalizedModelId = model.id.trim().toLowerCase();
|
|
1430
|
-
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
1431
|
-
}
|
|
1432
|
-
function shouldTrustReasoningContentReplayMetadata(model) {
|
|
1433
|
-
if (!model.reasoning) return false;
|
|
1434
|
-
if (model.provider.trim().toLowerCase() === "openai") return false;
|
|
1435
|
-
return shouldPreserveOpenRouterReasoningReplay(model);
|
|
1436
|
-
}
|
|
1437
|
-
function sanitizeCompletionsReasoningReplayFields(messages, options) {
|
|
1438
|
-
if (!Array.isArray(messages)) return;
|
|
1439
|
-
for (const msg of messages) {
|
|
1440
|
-
if (!msg || typeof msg !== "object") continue;
|
|
1441
|
-
const record = msg;
|
|
1442
|
-
if (record.role !== "assistant") continue;
|
|
1443
|
-
if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
|
|
1444
|
-
else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
|
|
1445
|
-
else stripCompletionsReasoningReplayFields(record);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
1449
|
-
injectToolCallThoughtSignatures(outgoingMessages, context, model);
|
|
1450
|
-
sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
|
|
1451
|
-
preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
|
|
1452
|
-
preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
//#endregion
|
|
1456
|
-
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
1457
|
-
function isKnownOpenAICompletionsEndpoint(model) {
|
|
1458
|
-
if (!model.baseUrl.trim()) return true;
|
|
1459
|
-
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
1460
|
-
if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
|
|
1461
|
-
try {
|
|
1462
|
-
return isAzureOpenAICompatibleHost(new URL(model.baseUrl).hostname.toLowerCase());
|
|
1463
|
-
} catch {
|
|
1464
|
-
return false;
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
1468
|
-
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1469
|
-
}
|
|
1470
|
-
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
1471
|
-
if (options?.maxTokens) return {
|
|
1472
|
-
maxTokens: options.maxTokens,
|
|
1473
|
-
clampToModelMaxTokens: true
|
|
1474
|
-
};
|
|
1475
|
-
const paramsMaxTokens = resolveMaxTokensParam(model.params);
|
|
1476
|
-
if (paramsMaxTokens) return {
|
|
1477
|
-
maxTokens: paramsMaxTokens,
|
|
1478
|
-
clampToModelMaxTokens: false
|
|
1479
552
|
};
|
|
1480
553
|
return {
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
function estimateOpenAICompletionsInputTokens(payload) {
|
|
1491
|
-
let adjustedChars = 0;
|
|
1492
|
-
adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
|
|
1493
|
-
if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
|
|
1494
|
-
adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
|
|
1495
|
-
} catch {
|
|
1496
|
-
adjustedChars += 1024;
|
|
1497
|
-
}
|
|
1498
|
-
if (payload.response_format !== void 0) try {
|
|
1499
|
-
adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
|
|
1500
|
-
} catch {
|
|
1501
|
-
adjustedChars += 256;
|
|
1502
|
-
}
|
|
1503
|
-
return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
|
|
1504
|
-
}
|
|
1505
|
-
function estimateOpenAICompletionsMessagesChars(messages) {
|
|
1506
|
-
if (!Array.isArray(messages)) return 0;
|
|
1507
|
-
let adjustedChars = 0;
|
|
1508
|
-
for (const message of messages) {
|
|
1509
|
-
if (!message || typeof message !== "object") continue;
|
|
1510
|
-
const record = message;
|
|
1511
|
-
adjustedChars += estimateOpenAICompletionsContentChars(record.content);
|
|
1512
|
-
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
|
|
1513
|
-
if (record.tool_calls !== void 0) try {
|
|
1514
|
-
adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
|
|
1515
|
-
} catch {
|
|
1516
|
-
adjustedChars += 256;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
return adjustedChars;
|
|
1520
|
-
}
|
|
1521
|
-
function estimateOpenAICompletionsContentChars(value) {
|
|
1522
|
-
if (typeof value === "string") return estimateStringChars(value);
|
|
1523
|
-
if (!Array.isArray(value)) return 0;
|
|
1524
|
-
let adjustedChars = 0;
|
|
1525
|
-
for (const block of value) {
|
|
1526
|
-
if (!block || typeof block !== "object") continue;
|
|
1527
|
-
const record = block;
|
|
1528
|
-
if (record.type === "image_url" || record.type === "input_image") {
|
|
1529
|
-
adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
|
|
1530
|
-
continue;
|
|
1531
|
-
}
|
|
1532
|
-
const text = record.text;
|
|
1533
|
-
if (typeof text === "string") {
|
|
1534
|
-
adjustedChars += estimateStringChars(text);
|
|
1535
|
-
continue;
|
|
1536
|
-
}
|
|
1537
|
-
try {
|
|
1538
|
-
adjustedChars += estimateStringChars(JSON.stringify(block));
|
|
1539
|
-
} catch {
|
|
1540
|
-
adjustedChars += 256;
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
return adjustedChars;
|
|
1544
|
-
}
|
|
1545
|
-
function resolveOpenAICompletionsEffectiveContextTokens(model) {
|
|
1546
|
-
const contextTokens = model.contextTokens;
|
|
1547
|
-
if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
|
|
1548
|
-
return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
|
|
1549
|
-
}
|
|
1550
|
-
function isQwenOpenAICompletionsThinkingFormat(format) {
|
|
1551
|
-
return format === "qwen" || format === "qwen-chat-template";
|
|
1552
|
-
}
|
|
1553
|
-
function setQwenChatTemplateThinking(params, enabled) {
|
|
1554
|
-
const existing = params.chat_template_kwargs;
|
|
1555
|
-
params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
|
|
1556
|
-
...existing,
|
|
1557
|
-
enable_thinking: enabled
|
|
1558
|
-
} : { enable_thinking: enabled };
|
|
1559
|
-
}
|
|
1560
|
-
function applyQwenOpenAICompletionsThinkingParams(params) {
|
|
1561
|
-
if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
|
|
1562
|
-
const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
|
|
1563
|
-
if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
|
|
1564
|
-
else params.payload.enable_thinking = enabled;
|
|
1565
|
-
return true;
|
|
1566
|
-
}
|
|
1567
|
-
function applyTogetherOpenAICompletionsThinkingParams(params) {
|
|
1568
|
-
if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
|
|
1569
|
-
params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
|
|
1570
|
-
}
|
|
1571
|
-
function convertTools(tools, compat, model) {
|
|
1572
|
-
const projection = projectOpenAITools(tools);
|
|
1573
|
-
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
|
|
1574
|
-
transport: "stream",
|
|
1575
|
-
supportsStrictMode: compat?.supportsStrictMode
|
|
1576
|
-
}), {
|
|
1577
|
-
transport: "completions",
|
|
1578
|
-
model
|
|
1579
|
-
});
|
|
1580
|
-
return {
|
|
1581
|
-
projection,
|
|
1582
|
-
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
1583
|
-
const functionTool = {
|
|
1584
|
-
name: tool.name,
|
|
1585
|
-
description: tool.description,
|
|
1586
|
-
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
1587
|
-
};
|
|
1588
|
-
if (strict !== void 0) functionTool.strict = strict;
|
|
1589
|
-
return {
|
|
1590
|
-
type: "function",
|
|
1591
|
-
function: functionTool
|
|
1592
|
-
};
|
|
1593
|
-
})
|
|
1594
|
-
};
|
|
1595
|
-
}
|
|
1596
|
-
function buildOpenAICompletionsParams(model, context, options) {
|
|
1597
|
-
const compat = getCompat(model);
|
|
1598
|
-
const compatDetection = detectOpenAICompletionsCompat(model);
|
|
1599
|
-
const completionsContext = context.systemPrompt ? {
|
|
1600
|
-
...context,
|
|
1601
|
-
systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
|
|
1602
|
-
} : context;
|
|
1603
|
-
let messages = convertMessages(model, completionsContext, compat);
|
|
1604
|
-
applyCompletionsReplay(messages, context, model, compat);
|
|
1605
|
-
if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
|
|
1606
|
-
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
1607
|
-
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
1608
|
-
const params = {
|
|
1609
|
-
model: model.id,
|
|
1610
|
-
messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
|
|
1611
|
-
stream: true
|
|
1612
|
-
};
|
|
1613
|
-
if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
|
|
1614
|
-
if (compat.supportsStore) params.store = false;
|
|
1615
|
-
if (compat.supportsPromptCacheKey && promptCacheKey) {
|
|
1616
|
-
params.prompt_cache_key = promptCacheKey;
|
|
1617
|
-
if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
|
|
1618
|
-
}
|
|
1619
|
-
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
1620
|
-
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
1621
|
-
const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
1622
|
-
provider: model.provider,
|
|
1623
|
-
baseUrl: model.baseUrl,
|
|
1624
|
-
hasTools: () => Boolean(context.tools?.length)
|
|
1625
|
-
}) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
1626
|
-
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
1627
|
-
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
1628
|
-
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
1629
|
-
if (options?.seed !== void 0) params.seed = options.seed;
|
|
1630
|
-
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
1631
|
-
if (supportsModelTools(model)) {
|
|
1632
|
-
if (context.tools) {
|
|
1633
|
-
const converted = convertTools(context.tools, compat, model);
|
|
1634
|
-
if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
|
|
1635
|
-
else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1636
|
-
if (options?.toolChoice) {
|
|
1637
|
-
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
|
|
1638
|
-
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1639
|
-
} else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
|
|
1640
|
-
} else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1641
|
-
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
|
|
1642
|
-
delete params.tools;
|
|
1643
|
-
delete params.tool_choice;
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
{
|
|
1647
|
-
const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
|
|
1648
|
-
const effectiveMaxTokens = maxTokenBudget.maxTokens;
|
|
1649
|
-
const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
|
|
1650
|
-
let clampedMaxTokens = effectiveMaxTokens;
|
|
1651
|
-
const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
|
|
1652
|
-
if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
|
|
1653
|
-
clampedMaxTokens = modelMaxTokens;
|
|
1654
|
-
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
|
|
1655
|
-
}
|
|
1656
|
-
if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
|
|
1657
|
-
const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
|
|
1658
|
-
const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
|
|
1659
|
-
if (clampedMaxTokens > remainingBudget) {
|
|
1660
|
-
clampedMaxTokens = remainingBudget;
|
|
1661
|
-
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
if (clampedMaxTokens) {
|
|
1665
|
-
if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1666
|
-
else params.max_completion_tokens = clampedMaxTokens;
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
|
|
1670
|
-
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1671
|
-
model,
|
|
1672
|
-
effort: completionsReasoningEffort,
|
|
1673
|
-
fallbackMap: compat.reasoningEffortMap
|
|
1674
|
-
}) : void 0;
|
|
1675
|
-
const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
|
|
1676
|
-
const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
|
|
1677
|
-
const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
|
|
1678
|
-
compatThinkingFormat: compat.thinkingFormat,
|
|
1679
|
-
modelReasoning: model.reasoning,
|
|
1680
|
-
payload: params,
|
|
1681
|
-
requestedEffort: completionsReasoningEffort
|
|
1682
|
-
});
|
|
1683
|
-
applyTogetherOpenAICompletionsThinkingParams({
|
|
1684
|
-
compatThinkingFormat: compat.thinkingFormat,
|
|
1685
|
-
modelReasoning: model.reasoning,
|
|
1686
|
-
payload: params,
|
|
1687
|
-
requestedEffort: completionsReasoningEffort
|
|
1688
|
-
});
|
|
1689
|
-
if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
|
|
1690
|
-
else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
|
|
1691
|
-
else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
|
|
1692
|
-
return params;
|
|
1693
|
-
}
|
|
1694
|
-
//#endregion
|
|
1695
|
-
//#region packages/ai/src/transports/openai-completions-transport.ts
|
|
1696
|
-
function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
|
|
1697
|
-
const messages = params.messages;
|
|
1698
|
-
if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
|
|
1699
|
-
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.`);
|
|
1700
|
-
}
|
|
1701
|
-
const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
|
|
1702
|
-
const SSE_DONE_MAX_LINE_CHARS = 1024;
|
|
1703
|
-
function createSseDoneDetector() {
|
|
1704
|
-
const decoder = new TextDecoder();
|
|
1705
|
-
let line = "";
|
|
1706
|
-
let lineOverflowed = false;
|
|
1707
|
-
let sawDone = false;
|
|
1708
|
-
const finishLine = () => {
|
|
1709
|
-
if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
|
|
1710
|
-
line = "";
|
|
1711
|
-
lineOverflowed = false;
|
|
1712
|
-
};
|
|
1713
|
-
const observeText = (text) => {
|
|
1714
|
-
for (const char of text) {
|
|
1715
|
-
if (char === "\n" || char === "\r") {
|
|
1716
|
-
finishLine();
|
|
1717
|
-
continue;
|
|
1718
|
-
}
|
|
1719
|
-
if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
|
|
1720
|
-
else lineOverflowed = true;
|
|
1721
|
-
}
|
|
1722
|
-
};
|
|
1723
|
-
return {
|
|
1724
|
-
observe(chunk) {
|
|
1725
|
-
if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
|
|
1726
|
-
},
|
|
1727
|
-
finish() {
|
|
1728
|
-
if (sawDone) return;
|
|
1729
|
-
observeText(decoder.decode());
|
|
1730
|
-
if (line || lineOverflowed) finishLine();
|
|
1731
|
-
},
|
|
1732
|
-
sawDone: () => sawDone
|
|
554
|
+
observe(chunk) {
|
|
555
|
+
if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
|
|
556
|
+
},
|
|
557
|
+
finish() {
|
|
558
|
+
if (sawDone) return;
|
|
559
|
+
observeText(decoder.decode());
|
|
560
|
+
if (line || lineOverflowed) finishLine();
|
|
561
|
+
},
|
|
562
|
+
sawDone: () => sawDone
|
|
1733
563
|
};
|
|
1734
564
|
}
|
|
1735
565
|
function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
|
|
@@ -1823,7 +653,7 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
1823
653
|
statusText: response.statusText
|
|
1824
654
|
});
|
|
1825
655
|
};
|
|
1826
|
-
const client = createOpenAICompletionsClient(model, context, apiKey, options
|
|
656
|
+
const client = createOpenAICompletionsClient(model, context, apiKey, resolveOpencodeSessionHeaders(model, options), { fetch: doneDetectingFetch });
|
|
1827
657
|
let params = buildOpenAICompletionsParams(model, context, options);
|
|
1828
658
|
const nextParams = await options?.onPayload?.(params, model);
|
|
1829
659
|
if (nextParams !== void 0) params = nextParams;
|
|
@@ -1880,164 +710,6 @@ function createOpenAICompletionsTransportStreamFn() {
|
|
|
1880
710
|
};
|
|
1881
711
|
}
|
|
1882
712
|
//#endregion
|
|
1883
|
-
//#region packages/ai/src/transports/openai-responses-compact-request.ts
|
|
1884
|
-
const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
|
|
1885
|
-
function claimResponsesCompactRequest(options) {
|
|
1886
|
-
const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
|
|
1887
|
-
if (controller?.claimed === false) {
|
|
1888
|
-
controller.claimed = true;
|
|
1889
|
-
return controller;
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
/** Run a compact-endpoint request through the session's prepared stream stack. */
|
|
1893
|
-
async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
|
|
1894
|
-
const preparedOptions = { ...options };
|
|
1895
|
-
let resolveResult;
|
|
1896
|
-
let rejectResult;
|
|
1897
|
-
const result = new Promise((resolve, reject) => {
|
|
1898
|
-
resolveResult = resolve;
|
|
1899
|
-
rejectResult = reject;
|
|
1900
|
-
});
|
|
1901
|
-
const controller = {
|
|
1902
|
-
claimed: false,
|
|
1903
|
-
resolve: resolveResult,
|
|
1904
|
-
reject: rejectResult
|
|
1905
|
-
};
|
|
1906
|
-
Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
|
|
1907
|
-
const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
|
|
1908
|
-
if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
|
|
1909
|
-
try {
|
|
1910
|
-
return await result;
|
|
1911
|
-
} finally {
|
|
1912
|
-
await stream.result().catch(() => void 0);
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
//#endregion
|
|
1916
|
-
//#region packages/ai/src/transports/openai-responses-continuation.ts
|
|
1917
|
-
const HTTP_CONTINUATION_IDLE_TTL_MS = 3e5;
|
|
1918
|
-
const TURN_HEADERS = /* @__PURE__ */ new Set([
|
|
1919
|
-
"traceparent",
|
|
1920
|
-
"x-openclaw-turn-id",
|
|
1921
|
-
"x-openclaw-turn-attempt"
|
|
1922
|
-
]);
|
|
1923
|
-
function jsonValuesEqual(left, right) {
|
|
1924
|
-
return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
|
|
1925
|
-
}
|
|
1926
|
-
function requestWithoutInput(request) {
|
|
1927
|
-
const { input: _input, previous_response_id: _previousResponseId, instructions: _instructions, tools: _tools, ...rest } = request;
|
|
1928
|
-
if (!isRecord(rest.metadata)) return rest;
|
|
1929
|
-
const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
|
|
1930
|
-
return {
|
|
1931
|
-
...rest,
|
|
1932
|
-
metadata
|
|
1933
|
-
};
|
|
1934
|
-
}
|
|
1935
|
-
function normalizeAssistantReplayInput(input, fromResponse = false) {
|
|
1936
|
-
return input.map((item) => {
|
|
1937
|
-
if (!isRecord(item)) return item;
|
|
1938
|
-
if (item.type === "reasoning") return { type: "reasoning" };
|
|
1939
|
-
if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
|
|
1940
|
-
const { id: _id, status: _status, ...stableItem } = item;
|
|
1941
|
-
if (fromResponse && item.type === "function_call") {
|
|
1942
|
-
const args = parseJsonObjectPreservingUnsafeIntegers(stableItem.arguments);
|
|
1943
|
-
stableItem.arguments = args ? JSON.stringify(args) : stableItem.arguments;
|
|
1944
|
-
}
|
|
1945
|
-
if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
|
|
1946
|
-
if (!isRecord(part) || part.type !== "output_text") return part;
|
|
1947
|
-
const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
|
|
1948
|
-
return stablePart;
|
|
1949
|
-
});
|
|
1950
|
-
return stableItem;
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
function resolveResponsesContinuationRequest(continuation, request) {
|
|
1954
|
-
if (!continuation) return {
|
|
1955
|
-
request,
|
|
1956
|
-
continuationStatus: "no_previous_response"
|
|
1957
|
-
};
|
|
1958
|
-
if (request.previous_response_id) return {
|
|
1959
|
-
request,
|
|
1960
|
-
continuationStatus: "explicit_previous_response_id"
|
|
1961
|
-
};
|
|
1962
|
-
if (!jsonValuesEqual(requestWithoutInput(request), requestWithoutInput(continuation.lastRequest))) return {
|
|
1963
|
-
request,
|
|
1964
|
-
continuationStatus: "request_changed"
|
|
1965
|
-
};
|
|
1966
|
-
const currentInput = request.input ?? [];
|
|
1967
|
-
const previousInput = continuation.lastRequest.input ?? [];
|
|
1968
|
-
const baselineLength = previousInput.length + continuation.lastResponseItems.length;
|
|
1969
|
-
if (currentInput.length < baselineLength) return {
|
|
1970
|
-
request,
|
|
1971
|
-
continuationStatus: "history_shorter"
|
|
1972
|
-
};
|
|
1973
|
-
if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems, true))) return {
|
|
1974
|
-
request,
|
|
1975
|
-
continuationStatus: "history_changed"
|
|
1976
|
-
};
|
|
1977
|
-
return {
|
|
1978
|
-
request: {
|
|
1979
|
-
...request,
|
|
1980
|
-
previous_response_id: continuation.lastResponseId,
|
|
1981
|
-
input: currentInput.slice(baselineLength)
|
|
1982
|
-
},
|
|
1983
|
-
continuationStatus: "continued"
|
|
1984
|
-
};
|
|
1985
|
-
}
|
|
1986
|
-
const httpContinuationEntries = /* @__PURE__ */ new Map();
|
|
1987
|
-
let nextHttpContinuationGeneration = 1;
|
|
1988
|
-
function connectionIdentity(params) {
|
|
1989
|
-
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));
|
|
1990
|
-
return sha256Hex(JSON.stringify([
|
|
1991
|
-
getAiTransportHost().resolveSecretSentinel(params.apiKey),
|
|
1992
|
-
params.baseUrl,
|
|
1993
|
-
headers
|
|
1994
|
-
]));
|
|
1995
|
-
}
|
|
1996
|
-
function claimOpenAIResponsesHttpContinuation(params) {
|
|
1997
|
-
const key = `${params.sessionId}\0${connectionIdentity(params)}`;
|
|
1998
|
-
const previous = httpContinuationEntries.get(key);
|
|
1999
|
-
if (previous?.kind === "claimed") return;
|
|
2000
|
-
if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
|
|
2001
|
-
const generation = nextHttpContinuationGeneration++;
|
|
2002
|
-
const claimed = {
|
|
2003
|
-
kind: "claimed",
|
|
2004
|
-
sessionId: params.sessionId,
|
|
2005
|
-
generation
|
|
2006
|
-
};
|
|
2007
|
-
httpContinuationEntries.set(key, claimed);
|
|
2008
|
-
return {
|
|
2009
|
-
request: resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, params.request).request,
|
|
2010
|
-
commit: (effectiveRequest, response) => {
|
|
2011
|
-
if (httpContinuationEntries.get(key) !== claimed) return;
|
|
2012
|
-
const idleTimer = setTimeout(() => {
|
|
2013
|
-
const current = httpContinuationEntries.get(key);
|
|
2014
|
-
if (current?.kind === "ready" && current.generation === generation) httpContinuationEntries.delete(key);
|
|
2015
|
-
}, HTTP_CONTINUATION_IDLE_TTL_MS);
|
|
2016
|
-
idleTimer.unref?.();
|
|
2017
|
-
const ready = {
|
|
2018
|
-
...claimed,
|
|
2019
|
-
kind: "ready",
|
|
2020
|
-
state: {
|
|
2021
|
-
lastRequest: effectiveRequest,
|
|
2022
|
-
lastResponseId: response.id,
|
|
2023
|
-
lastResponseItems: response.output
|
|
2024
|
-
},
|
|
2025
|
-
idleTimer
|
|
2026
|
-
};
|
|
2027
|
-
httpContinuationEntries.set(key, ready);
|
|
2028
|
-
},
|
|
2029
|
-
release: () => {
|
|
2030
|
-
if (httpContinuationEntries.get(key) === claimed) httpContinuationEntries.delete(key);
|
|
2031
|
-
}
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
registerSessionResourceCleanup((sessionId) => {
|
|
2035
|
-
for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
|
|
2036
|
-
if (entry.kind === "ready") clearTimeout(entry.idleTimer);
|
|
2037
|
-
httpContinuationEntries.delete(key);
|
|
2038
|
-
}
|
|
2039
|
-
});
|
|
2040
|
-
//#endregion
|
|
2041
713
|
//#region packages/ai/src/transports/openai-responses-params-internal.ts
|
|
2042
714
|
const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
|
|
2043
715
|
"openai",
|
|
@@ -2045,30 +717,6 @@ const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
|
|
|
2045
717
|
"azure-openai-responses",
|
|
2046
718
|
"github-copilot"
|
|
2047
719
|
]);
|
|
2048
|
-
function convertResponsesTools(tools, model, options) {
|
|
2049
|
-
const projection = projectOpenAITools(tools);
|
|
2050
|
-
const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
|
|
2051
|
-
transport: "responses",
|
|
2052
|
-
model
|
|
2053
|
-
});
|
|
2054
|
-
return {
|
|
2055
|
-
projection,
|
|
2056
|
-
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
2057
|
-
const result = {
|
|
2058
|
-
type: "function",
|
|
2059
|
-
name: tool.name,
|
|
2060
|
-
description: tool.description,
|
|
2061
|
-
parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
2062
|
-
};
|
|
2063
|
-
if (strict !== void 0) result.strict = strict;
|
|
2064
|
-
return result;
|
|
2065
|
-
})
|
|
2066
|
-
};
|
|
2067
|
-
}
|
|
2068
|
-
function getPromptCacheRetention(baseUrl, cacheRetention) {
|
|
2069
|
-
if (cacheRetention !== "long") return;
|
|
2070
|
-
return baseUrl?.includes("api.openai.com") ? "24h" : void 0;
|
|
2071
|
-
}
|
|
2072
720
|
function resolveOpenAIReasoningEffort(options) {
|
|
2073
721
|
return normalizeOpenAIReasoningEffort(options?.reasoningEffort ?? options?.reasoning ?? "high");
|
|
2074
722
|
}
|
|
@@ -2101,6 +749,7 @@ const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
|
|
|
2101
749
|
"max_output_tokens",
|
|
2102
750
|
"metadata",
|
|
2103
751
|
"prompt_cache_retention",
|
|
752
|
+
"prompt_cache_options",
|
|
2104
753
|
"service_tier",
|
|
2105
754
|
"temperature",
|
|
2106
755
|
"top_p"
|
|
@@ -2116,6 +765,7 @@ function stripOpenAICodexResponsesUnsupportedTextFields(params) {
|
|
|
2116
765
|
function sanitizeOpenAICodexResponsesParams(model, params) {
|
|
2117
766
|
if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
|
|
2118
767
|
for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
|
|
768
|
+
Object.assign(params, { store: false });
|
|
2119
769
|
stripOpenAICodexResponsesUnsupportedTextFields(params);
|
|
2120
770
|
return params;
|
|
2121
771
|
}
|
|
@@ -2151,84 +801,399 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
|
|
|
2151
801
|
...responseFormat.json_schema,
|
|
2152
802
|
type: "json_schema"
|
|
2153
803
|
};
|
|
2154
|
-
return responseFormat;
|
|
804
|
+
return responseFormat;
|
|
805
|
+
}
|
|
806
|
+
function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
|
|
807
|
+
const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
|
|
808
|
+
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
809
|
+
const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
|
|
810
|
+
const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
|
|
811
|
+
return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
|
|
812
|
+
includeSystemPrompt: !payloadPolicy.usesInstructionsField,
|
|
813
|
+
replayReasoningItems: true,
|
|
814
|
+
replayResponsesItemIds,
|
|
815
|
+
authProfileId: options?.authProfileId,
|
|
816
|
+
sessionId: options?.sessionId,
|
|
817
|
+
replayMode
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
|
|
821
|
+
const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
|
|
822
|
+
const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
|
|
823
|
+
ensureOpenAIResponsesNonEmptyInput(messages, context);
|
|
824
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
825
|
+
const compat = getCompat(model);
|
|
826
|
+
const promptCacheKey = compat.supportsPromptCacheKey ? resolvePromptCacheKey(options, cacheRetention) : void 0;
|
|
827
|
+
const instructions = resolveOpenAIResponsesInstructions(model, context, payloadPolicy.usesInstructionsField);
|
|
828
|
+
const params = {
|
|
829
|
+
model: model.id,
|
|
830
|
+
input: messages,
|
|
831
|
+
stream: true,
|
|
832
|
+
prompt_cache_key: promptCacheKey,
|
|
833
|
+
...resolveOpenAIPromptCacheParams(model, cacheRetention, compat),
|
|
834
|
+
...instructions ? { instructions } : {},
|
|
835
|
+
...metadata ? { metadata } : {}
|
|
836
|
+
};
|
|
837
|
+
const effectiveMaxTokens = options?.maxTokens || model.maxTokens;
|
|
838
|
+
if (effectiveMaxTokens) params.max_output_tokens = Math.max(effectiveMaxTokens, 16);
|
|
839
|
+
if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) params.temperature = options.temperature;
|
|
840
|
+
if (options?.topP !== void 0 && model.id !== "gpt-6-astra") params.top_p = options.topP;
|
|
841
|
+
if (options?.responseFormat !== void 0) params.text = {
|
|
842
|
+
...params.text,
|
|
843
|
+
format: resolveOpenAIResponsesTextFormat(options.responseFormat)
|
|
844
|
+
};
|
|
845
|
+
if (options?.serviceTier !== void 0 && payloadPolicy.allowsServiceTier) params.service_tier = options.serviceTier;
|
|
846
|
+
if (context.tools) {
|
|
847
|
+
const tools = context.tools;
|
|
848
|
+
const strict = resolveOpenAIStrictToolSetting(model, { transport: "stream" });
|
|
849
|
+
const projection = projectOpenAITools(tools);
|
|
850
|
+
const converted = convertProjectedResponsesTools(projection, strict, model);
|
|
851
|
+
if (converted.length > 0 || projection.inputToolCount === 0 && projection.diagnostics.length === 0) params.tools = converted;
|
|
852
|
+
if (options?.toolChoice) {
|
|
853
|
+
const toolChoice = reconcileOpenAIResponsesToolChoice(options.toolChoice, projection);
|
|
854
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (model.reasoning) {
|
|
858
|
+
if (options?.reasoningEffort || options?.reasoning || options?.reasoningSummary) {
|
|
859
|
+
const requestedReasoningEffort = resolveOpenAIReasoningEffort(options);
|
|
860
|
+
const resolvedReasoningEffort = resolveOpenAIReasoningEffortForModel({
|
|
861
|
+
model,
|
|
862
|
+
effort: requestedReasoningEffort
|
|
863
|
+
});
|
|
864
|
+
const reasoningEffort = resolvedReasoningEffort ? raiseMinimalReasoningForResponsesWebSearch({
|
|
865
|
+
model,
|
|
866
|
+
effort: resolvedReasoningEffort,
|
|
867
|
+
tools: params.tools
|
|
868
|
+
}) : void 0;
|
|
869
|
+
if (reasoningEffort) {
|
|
870
|
+
params.reasoning = {
|
|
871
|
+
effort: reasoningEffort,
|
|
872
|
+
...reasoningEffort === "none" ? {} : { summary: options?.reasoningSummary || "auto" }
|
|
873
|
+
};
|
|
874
|
+
if (reasoningEffort !== "none") params.include = ["reasoning.encrypted_content"];
|
|
875
|
+
}
|
|
876
|
+
} else if (model.provider !== "github-copilot") {
|
|
877
|
+
const reasoningEffort = resolveOpenAIReasoningEffortForModel({
|
|
878
|
+
model,
|
|
879
|
+
effort: "none"
|
|
880
|
+
});
|
|
881
|
+
if (reasoningEffort) params.reasoning = { effort: reasoningEffort };
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
applyOpenAIResponsesPayloadPolicy(params, payloadPolicy);
|
|
885
|
+
return sanitizeOpenAICodexResponsesParams(model, params);
|
|
886
|
+
}
|
|
887
|
+
//#endregion
|
|
888
|
+
//#region packages/ai/src/transports/openai-responses-reasoning-update.ts
|
|
889
|
+
function isConfigurationUpdate(value) {
|
|
890
|
+
return isRecord(value) && value.type === "configuration_update" && isRecord(value.reasoning) && typeof value.reasoning.effort === "string";
|
|
891
|
+
}
|
|
892
|
+
function isResponsesReasoningUpdateCompatible(request) {
|
|
893
|
+
const mode = isRecord(request.reasoning) ? request.reasoning.mode : void 0;
|
|
894
|
+
return request.model === "gpt-6-astra" && (mode === void 0 || mode === "standard") && (!isRecord(request.multi_agent) || request.multi_agent.enabled !== true) && request.truncation !== "auto" && (!Array.isArray(request.context_management) || !request.context_management.some((item) => isRecord(item) && item.type === "compaction"));
|
|
895
|
+
}
|
|
896
|
+
function supportsResponsesReasoningUpdate(request) {
|
|
897
|
+
return isResponsesReasoningUpdateCompatible(request) && isRecord(request.reasoning) && typeof request.reasoning.effort === "string";
|
|
898
|
+
}
|
|
899
|
+
function canReferenceResponsesReasoningHistory(previous, request) {
|
|
900
|
+
return !previous.input?.some(isConfigurationUpdate) || isResponsesReasoningUpdateCompatible(request);
|
|
901
|
+
}
|
|
902
|
+
/** Rehydrate input controls only provisionally; continuation must validate the full prefix. */
|
|
903
|
+
function replayResponsesReasoningUpdates(previous, request, previousOutputLength, steering) {
|
|
904
|
+
if (steering !== "required-input" && (!supportsResponsesReasoningUpdate(previous) || !supportsResponsesReasoningUpdate(request)) || !Array.isArray(previous.input) || !Array.isArray(request.input) || request.input.some(isConfigurationUpdate)) return request;
|
|
905
|
+
const input = [...request.input];
|
|
906
|
+
let activeEffort = isRecord(previous.reasoning) ? previous.reasoning.effort : void 0;
|
|
907
|
+
for (const [index, item] of previous.input.entries()) if (isConfigurationUpdate(item)) {
|
|
908
|
+
input.splice(index, 0, item);
|
|
909
|
+
activeEffort = item.reasoning.effort;
|
|
910
|
+
}
|
|
911
|
+
if (steering === "required-input") return input.length === request.input.length ? request : {
|
|
912
|
+
...request,
|
|
913
|
+
input
|
|
914
|
+
};
|
|
915
|
+
if (!isRecord(previous.reasoning) || !isRecord(request.reasoning) || typeof request.reasoning.effort !== "string") return request;
|
|
916
|
+
if (activeEffort !== request.reasoning.effort && steering !== "automatic") {
|
|
917
|
+
const baselineLength = previous.input.length + previousOutputLength;
|
|
918
|
+
const nextUser = input.findIndex((item, index) => index >= baselineLength && "role" in item && item.role === "user");
|
|
919
|
+
if (nextUser === -1) return request;
|
|
920
|
+
input.splice(nextUser, 0, {
|
|
921
|
+
type: "configuration_update",
|
|
922
|
+
reasoning: { effort: request.reasoning.effort }
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
if (input.length === request.input.length && activeEffort === request.reasoning.effort) return request;
|
|
926
|
+
return {
|
|
927
|
+
...request,
|
|
928
|
+
reasoning: {
|
|
929
|
+
...request.reasoning,
|
|
930
|
+
effort: previous.reasoning.effort
|
|
931
|
+
},
|
|
932
|
+
input
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
//#endregion
|
|
936
|
+
//#region packages/ai/src/transports/openai-responses-continuation.ts
|
|
937
|
+
const HTTP_CONTINUATION_IDLE_TTL_MS = 3e5;
|
|
938
|
+
const TURN_HEADERS = /* @__PURE__ */ new Set([
|
|
939
|
+
"traceparent",
|
|
940
|
+
"x-openclaw-turn-id",
|
|
941
|
+
"x-openclaw-turn-attempt"
|
|
942
|
+
]);
|
|
943
|
+
function jsonValuesEqual(left, right) {
|
|
944
|
+
const leftJson = JSON.stringify(left);
|
|
945
|
+
const normalizedLeft = stableStringify(JSON.parse(leftJson));
|
|
946
|
+
const rightJson = JSON.stringify(right);
|
|
947
|
+
return leftJson === rightJson || normalizedLeft === stableStringify(JSON.parse(rightJson));
|
|
948
|
+
}
|
|
949
|
+
function requestWithoutInput(request) {
|
|
950
|
+
const { input: _input, previous_response_id: _previousResponseId, instructions: _instructions, tools: _tools, ...rest } = request;
|
|
951
|
+
if (!isRecord(rest.metadata)) return rest;
|
|
952
|
+
const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
|
|
953
|
+
return {
|
|
954
|
+
...rest,
|
|
955
|
+
metadata
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
function normalizeAssistantReplayInput(input, fromResponse = false) {
|
|
959
|
+
return input.map((item) => {
|
|
960
|
+
if (!isRecord(item)) return item;
|
|
961
|
+
if (item.type === "reasoning") return { type: "reasoning" };
|
|
962
|
+
if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
|
|
963
|
+
const { id: _id, status: _status, ...stableItem } = item;
|
|
964
|
+
if (fromResponse && item.type === "function_call") {
|
|
965
|
+
const args = parseJsonObjectPreservingUnsafeIntegers(stableItem.arguments);
|
|
966
|
+
stableItem.arguments = args ? JSON.stringify(args) : stableItem.arguments;
|
|
967
|
+
}
|
|
968
|
+
if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
|
|
969
|
+
if (!isRecord(part) || part.type !== "output_text") return part;
|
|
970
|
+
const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
|
|
971
|
+
return stablePart;
|
|
972
|
+
});
|
|
973
|
+
return stableItem;
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
function responsesContinuationRequestFingerprint(request) {
|
|
977
|
+
const serialized = JSON.stringify(requestWithoutInput(request));
|
|
978
|
+
return sha256Hex(stableStringify(JSON.parse(serialized)));
|
|
979
|
+
}
|
|
980
|
+
function responsesContinuationPrefixFingerprint(input, output = []) {
|
|
981
|
+
const serialized = JSON.stringify([...normalizeAssistantReplayInput(input), ...normalizeAssistantReplayInput(output, true)]);
|
|
982
|
+
return sha256Hex(stableStringify(JSON.parse(serialized)));
|
|
983
|
+
}
|
|
984
|
+
function resolveResponsesContinuationRequest(continuation, request, steering) {
|
|
985
|
+
if (!continuation) return {
|
|
986
|
+
request,
|
|
987
|
+
continuationStatus: "no_previous_response"
|
|
988
|
+
};
|
|
989
|
+
if (request.previous_response_id) return {
|
|
990
|
+
request,
|
|
991
|
+
continuationStatus: "explicit_previous_response_id"
|
|
992
|
+
};
|
|
993
|
+
if (!canReferenceResponsesReasoningHistory(continuation.lastRequest, request)) return {
|
|
994
|
+
request,
|
|
995
|
+
continuationStatus: "request_changed"
|
|
996
|
+
};
|
|
997
|
+
const prepared = replayResponsesReasoningUpdates(continuation.lastRequest, request, continuation.lastResponseItems.length, steering);
|
|
998
|
+
if (steering !== "required-input" && !jsonValuesEqual(requestWithoutInput(prepared), requestWithoutInput(continuation.lastRequest))) return {
|
|
999
|
+
request,
|
|
1000
|
+
continuationStatus: "request_changed"
|
|
1001
|
+
};
|
|
1002
|
+
const currentInput = prepared.input ?? [];
|
|
1003
|
+
const previousInput = continuation.lastRequest.input ?? [];
|
|
1004
|
+
const baselineLength = previousInput.length + continuation.lastResponseItems.length;
|
|
1005
|
+
if (currentInput.length < baselineLength) return {
|
|
1006
|
+
request,
|
|
1007
|
+
continuationStatus: "history_shorter"
|
|
1008
|
+
};
|
|
1009
|
+
if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems, true))) return {
|
|
1010
|
+
request,
|
|
1011
|
+
continuationStatus: "history_changed"
|
|
1012
|
+
};
|
|
1013
|
+
return {
|
|
1014
|
+
request: {
|
|
1015
|
+
...prepared,
|
|
1016
|
+
previous_response_id: continuation.lastResponseId,
|
|
1017
|
+
input: currentInput.slice(baselineLength)
|
|
1018
|
+
},
|
|
1019
|
+
...prepared !== request ? { fullRequest: prepared } : {},
|
|
1020
|
+
continuationStatus: "continued"
|
|
1021
|
+
};
|
|
2155
1022
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
|
|
2160
|
-
const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
|
|
2161
|
-
return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
|
|
2162
|
-
includeSystemPrompt: !payloadPolicy.usesInstructionsField,
|
|
2163
|
-
replayReasoningItems: true,
|
|
2164
|
-
replayResponsesItemIds,
|
|
2165
|
-
authProfileId: options?.authProfileId,
|
|
2166
|
-
sessionId: options?.sessionId,
|
|
2167
|
-
replayMode
|
|
2168
|
-
});
|
|
1023
|
+
const httpContinuationEntries = /* @__PURE__ */ new Map();
|
|
1024
|
+
function deleteHttpContinuationIfOwned(key, entry) {
|
|
1025
|
+
if (httpContinuationEntries.get(key) === entry) httpContinuationEntries.delete(key);
|
|
2169
1026
|
}
|
|
2170
|
-
function
|
|
2171
|
-
const
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
const effectiveMaxTokens = options?.maxTokens || model.maxTokens;
|
|
2187
|
-
if (effectiveMaxTokens) params.max_output_tokens = effectiveMaxTokens;
|
|
2188
|
-
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
2189
|
-
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
2190
|
-
if (options?.responseFormat !== void 0) params.text = {
|
|
2191
|
-
...params.text,
|
|
2192
|
-
format: resolveOpenAIResponsesTextFormat(options.responseFormat)
|
|
1027
|
+
function connectionIdentity(params) {
|
|
1028
|
+
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));
|
|
1029
|
+
return sha256Hex(JSON.stringify([
|
|
1030
|
+
getAiTransportHost().resolveSecretSentinel(params.apiKey),
|
|
1031
|
+
params.baseUrl,
|
|
1032
|
+
headers
|
|
1033
|
+
]));
|
|
1034
|
+
}
|
|
1035
|
+
function claimOpenAIResponsesHttpContinuation(params) {
|
|
1036
|
+
const key = `${params.sessionId}\0${connectionIdentity(params)}`;
|
|
1037
|
+
const previous = httpContinuationEntries.get(key);
|
|
1038
|
+
if (previous?.kind === "claimed") return;
|
|
1039
|
+
if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
|
|
1040
|
+
const claimed = {
|
|
1041
|
+
kind: "claimed",
|
|
1042
|
+
sessionId: params.sessionId
|
|
2193
1043
|
};
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
tools: params.tools
|
|
2214
|
-
}) : void 0;
|
|
2215
|
-
if (reasoningEffort) {
|
|
2216
|
-
params.reasoning = {
|
|
2217
|
-
effort: reasoningEffort,
|
|
2218
|
-
...reasoningEffort === "none" ? {} : { summary: options?.reasoningSummary || "auto" }
|
|
1044
|
+
httpContinuationEntries.set(key, claimed);
|
|
1045
|
+
try {
|
|
1046
|
+
const request = previous?.kind === "ready" ? params.request : params.restoreRequest?.() ?? params.request;
|
|
1047
|
+
const resolved = resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, request);
|
|
1048
|
+
const fullRequest = resolved.fullRequest ?? request;
|
|
1049
|
+
return {
|
|
1050
|
+
request: params.request.store === false ? fullRequest : resolved.request,
|
|
1051
|
+
fullRequest,
|
|
1052
|
+
commit: (effectiveRequest, response) => {
|
|
1053
|
+
if (httpContinuationEntries.get(key) !== claimed) return;
|
|
1054
|
+
const ready = {
|
|
1055
|
+
...claimed,
|
|
1056
|
+
kind: "ready",
|
|
1057
|
+
state: {
|
|
1058
|
+
lastRequest: effectiveRequest,
|
|
1059
|
+
lastResponseId: response.id,
|
|
1060
|
+
lastResponseItems: response.output
|
|
1061
|
+
},
|
|
1062
|
+
idleTimer: setTimeout(() => deleteHttpContinuationIfOwned(key, ready), HTTP_CONTINUATION_IDLE_TTL_MS)
|
|
2219
1063
|
};
|
|
2220
|
-
|
|
1064
|
+
ready.idleTimer.unref?.();
|
|
1065
|
+
httpContinuationEntries.set(key, ready);
|
|
1066
|
+
},
|
|
1067
|
+
release: () => deleteHttpContinuationIfOwned(key, claimed)
|
|
1068
|
+
};
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
deleteHttpContinuationIfOwned(key, claimed);
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
registerSessionResourceCleanup((sessionId) => {
|
|
1075
|
+
for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
|
|
1076
|
+
if (entry.kind === "ready") clearTimeout(entry.idleTimer);
|
|
1077
|
+
httpContinuationEntries.delete(key);
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
//#endregion
|
|
1081
|
+
//#region packages/ai/src/transports/openai-responses-steering.ts
|
|
1082
|
+
function cloneWireRequest(request) {
|
|
1083
|
+
const serialized = JSON.stringify(request);
|
|
1084
|
+
return JSON.parse(serialized);
|
|
1085
|
+
}
|
|
1086
|
+
async function projectResponsesSteeringInput(request, project) {
|
|
1087
|
+
const { input: activeInput, ...activeSettings } = cloneWireRequest(request);
|
|
1088
|
+
if (!Array.isArray(activeInput)) return [];
|
|
1089
|
+
const { input, ...settings } = cloneWireRequest(await project());
|
|
1090
|
+
if (!Array.isArray(input) || stableStringify(settings) !== stableStringify(activeSettings) || stableStringify(input.slice(0, activeInput.length)) !== stableStringify(activeInput)) return [];
|
|
1091
|
+
const projected = input.slice(activeInput.length);
|
|
1092
|
+
return projected.every((item) => isRecord(item) && item.role === "user") ? projected : [];
|
|
1093
|
+
}
|
|
1094
|
+
/** One response owns admission; accepted input stays on this connection until continuation. */
|
|
1095
|
+
function createResponsesSteering(params) {
|
|
1096
|
+
let responseId;
|
|
1097
|
+
let sealed = false;
|
|
1098
|
+
let unsubscribe;
|
|
1099
|
+
const pending = [];
|
|
1100
|
+
const accepted = /* @__PURE__ */ new Map();
|
|
1101
|
+
const acknowledged = /* @__PURE__ */ new Set();
|
|
1102
|
+
const seal = () => {
|
|
1103
|
+
sealed = true;
|
|
1104
|
+
const cleanup = unsubscribe;
|
|
1105
|
+
unsubscribe = void 0;
|
|
1106
|
+
cleanup?.();
|
|
1107
|
+
};
|
|
1108
|
+
return {
|
|
1109
|
+
get responseId() {
|
|
1110
|
+
return responseId;
|
|
1111
|
+
},
|
|
1112
|
+
get pending() {
|
|
1113
|
+
return pending.length > 0;
|
|
1114
|
+
},
|
|
1115
|
+
get acceptedInput() {
|
|
1116
|
+
return [...accepted.values()].flat();
|
|
1117
|
+
},
|
|
1118
|
+
seal,
|
|
1119
|
+
close(error) {
|
|
1120
|
+
seal();
|
|
1121
|
+
for (const submission of pending.splice(0)) submission.reject(error);
|
|
1122
|
+
},
|
|
1123
|
+
handle(event) {
|
|
1124
|
+
if (!isRecord(event)) return false;
|
|
1125
|
+
if (event.type === "response.created" && !responseId && !sealed) {
|
|
1126
|
+
const response = isRecord(event.response) ? event.response : void 0;
|
|
1127
|
+
if (typeof response?.id !== "string" || !response.id.trim()) throw new Error("Responses steering requires a response identity");
|
|
1128
|
+
const activeResponseId = response.id;
|
|
1129
|
+
responseId = activeResponseId;
|
|
1130
|
+
const cleanup = params.onActiveResponse({
|
|
1131
|
+
needsContinuation: params.needsContinuation,
|
|
1132
|
+
steer(messages) {
|
|
1133
|
+
if (sealed || messages.length === 0) return Promise.resolve(false);
|
|
1134
|
+
params.assertActive();
|
|
1135
|
+
const converted = params.toInput(messages);
|
|
1136
|
+
const submit = (input) => {
|
|
1137
|
+
if (sealed || input.length === 0) return Promise.resolve(false);
|
|
1138
|
+
params.assertActive();
|
|
1139
|
+
return new Promise((resolve, reject) => {
|
|
1140
|
+
pending.push({
|
|
1141
|
+
input,
|
|
1142
|
+
resolve,
|
|
1143
|
+
reject
|
|
1144
|
+
});
|
|
1145
|
+
params.send({
|
|
1146
|
+
type: "response.steer",
|
|
1147
|
+
previous_response_id: activeResponseId,
|
|
1148
|
+
input
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
};
|
|
1152
|
+
return Array.isArray(converted) ? submit(converted) : converted.then(submit);
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
if (sealed) cleanup?.();
|
|
1156
|
+
else unsubscribe = cleanup;
|
|
1157
|
+
return false;
|
|
2221
1158
|
}
|
|
2222
|
-
|
|
2223
|
-
const
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
1159
|
+
if (event.type !== "response.steer.accepted" && event.type !== "response.steer.failed" && event.type !== "response.steer.pending") return false;
|
|
1160
|
+
const steer = isRecord(event.steer) ? event.steer : void 0;
|
|
1161
|
+
if (!responseId || steer?.previous_response_id !== responseId) throw new Error("Responses steering acknowledgement has an unexpected identity");
|
|
1162
|
+
if (event.type === "response.steer.failed" && steer.id === void 0) {
|
|
1163
|
+
const index = pending.findIndex((submission) => stableStringify(submission.input) === stableStringify(steer.input));
|
|
1164
|
+
const submission = pending[index];
|
|
1165
|
+
if (!submission) throw new Error("Responses steering acknowledgement has no pending submission");
|
|
1166
|
+
pending.splice(index, 1);
|
|
1167
|
+
submission.resolve(false);
|
|
1168
|
+
return true;
|
|
1169
|
+
}
|
|
1170
|
+
if (typeof steer.id !== "string" || !steer.id.trim()) throw new Error("Responses steering acknowledgement has an unexpected identity");
|
|
1171
|
+
if (event.type === "response.steer.pending") {
|
|
1172
|
+
if (!accepted.has(steer.id)) throw new Error("Responses steering pending event has no accepted submission");
|
|
1173
|
+
return true;
|
|
1174
|
+
}
|
|
1175
|
+
if (event.type === "response.steer.failed" && accepted.has(steer.id)) throw new Error("OpenAI could not apply accepted steering; the queued message remains in the conversation");
|
|
1176
|
+
if (acknowledged.has(steer.id)) throw new Error("Responses steering acknowledgement was already applied");
|
|
1177
|
+
const submission = pending.shift();
|
|
1178
|
+
if (!submission) throw new Error("Responses steering acknowledgement has no pending submission");
|
|
1179
|
+
acknowledged.add(steer.id);
|
|
1180
|
+
if (event.type === "response.steer.accepted") {
|
|
1181
|
+
accepted.set(steer.id, submission.input);
|
|
1182
|
+
submission.resolve(true);
|
|
1183
|
+
} else submission.resolve(false);
|
|
1184
|
+
return true;
|
|
2228
1185
|
}
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
/** Accepted steering is prepended by the server, so never repeat it in response.create. */
|
|
1189
|
+
function omitAcceptedSteering(input, accepted) {
|
|
1190
|
+
const remaining = [...input];
|
|
1191
|
+
for (const message of accepted) {
|
|
1192
|
+
const index = remaining.findIndex((item) => stableStringify(item) === stableStringify(message));
|
|
1193
|
+
if (index < 0) throw new Error("Responses steering continuation no longer contains the accepted user input");
|
|
1194
|
+
remaining.splice(index, 1);
|
|
2229
1195
|
}
|
|
2230
|
-
|
|
2231
|
-
return sanitizeOpenAICodexResponsesParams(model, params);
|
|
1196
|
+
return remaining;
|
|
2232
1197
|
}
|
|
2233
1198
|
//#endregion
|
|
2234
1199
|
//#region packages/ai/src/transports/openai-responses-websocket.ts
|
|
@@ -2263,6 +1228,9 @@ function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
|
|
|
2263
1228
|
entry.idleTimer = void 0;
|
|
2264
1229
|
}
|
|
2265
1230
|
closeWebSocketSilently(entry.socket, reason);
|
|
1231
|
+
entry.steeringContinuation?.steering.close(/* @__PURE__ */ new Error("Responses steering connection closed"));
|
|
1232
|
+
entry.steeringContinuation?.iterator.return?.().catch(() => void 0);
|
|
1233
|
+
entry.steeringContinuation = void 0;
|
|
2266
1234
|
if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
|
|
2267
1235
|
}
|
|
2268
1236
|
function scheduleSessionWebSocketExpiry(cacheKey, entry) {
|
|
@@ -2312,11 +1280,14 @@ function createTransientWebSocketLease(connection) {
|
|
|
2312
1280
|
}
|
|
2313
1281
|
function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
|
|
2314
1282
|
entry.busy = true;
|
|
1283
|
+
const steeringContinuation = entry.steeringContinuation;
|
|
1284
|
+
entry.steeringContinuation = void 0;
|
|
2315
1285
|
return {
|
|
2316
1286
|
socket: entry.socket,
|
|
2317
|
-
iterator: entry.socket.stream(),
|
|
1287
|
+
iterator: steeringContinuation?.iterator ?? entry.socket.stream(),
|
|
2318
1288
|
entry,
|
|
2319
1289
|
reusedConnection,
|
|
1290
|
+
steeringContinuation,
|
|
2320
1291
|
release: ({ keep } = {}) => {
|
|
2321
1292
|
if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
|
|
2322
1293
|
invalidateOwnedWebSocketSession(cacheKey, entry);
|
|
@@ -2378,7 +1349,7 @@ function readServerEvent(message) {
|
|
|
2378
1349
|
}
|
|
2379
1350
|
function createOpenAIResponsesWebSocketStream(params) {
|
|
2380
1351
|
const connection = prepareWebSocketConnection(params.client, params.headers);
|
|
2381
|
-
|
|
1352
|
+
let fullRequest = sanitizeWebSocketRequest(params.request);
|
|
2382
1353
|
const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
|
|
2383
1354
|
const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
|
|
2384
1355
|
const degraded = degradedWebSocketConnections.get(degradationKey);
|
|
@@ -2400,15 +1371,20 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
2400
1371
|
throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
2401
1372
|
}
|
|
2402
1373
|
let prepared;
|
|
1374
|
+
const resumedSteering = lease.steeringContinuation;
|
|
1375
|
+
const steeringMode = resumedSteering ? resumedSteering.requiresInput ? "required-input" : "automatic" : void 0;
|
|
2403
1376
|
try {
|
|
2404
1377
|
const continuation = lease.entry?.continuation;
|
|
2405
1378
|
if (continuation && lease.entry) {
|
|
2406
1379
|
lease.entry.continuation = void 0;
|
|
2407
|
-
prepared = resolveResponsesContinuationRequest(continuation, fullRequest);
|
|
2408
|
-
} else
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
1380
|
+
prepared = resolveResponsesContinuationRequest(continuation, fullRequest, steeringMode);
|
|
1381
|
+
} else {
|
|
1382
|
+
fullRequest = params.restoreRequest?.(fullRequest) ?? fullRequest;
|
|
1383
|
+
prepared = {
|
|
1384
|
+
request: fullRequest,
|
|
1385
|
+
continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
2412
1388
|
} catch (error) {
|
|
2413
1389
|
lease.iterator.return?.().catch(() => void 0);
|
|
2414
1390
|
lease.release({ keep: false });
|
|
@@ -2418,11 +1394,54 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
2418
1394
|
let terminalResponse;
|
|
2419
1395
|
let terminalReceived = false;
|
|
2420
1396
|
let released = false;
|
|
1397
|
+
let retainIterator = false;
|
|
1398
|
+
let deferredInput = false;
|
|
1399
|
+
let inputReplay;
|
|
1400
|
+
if (resumedSteering) try {
|
|
1401
|
+
if (prepared.continuationStatus !== "continued" || !prepared.request.previous_response_id) throw new Error("Responses steering continuation changed its request or history");
|
|
1402
|
+
const input = omitAcceptedSteering(prepared.request.input ?? [], resumedSteering.acceptedInput);
|
|
1403
|
+
if (!resumedSteering.requiresInput && (stableStringify(fullRequest.instructions) !== stableStringify(resumedSteering.instructions) || stableStringify(fullRequest.tools) !== stableStringify(resumedSteering.tools))) throw new Error("Responses automatic steering continuation cannot change instructions or tools");
|
|
1404
|
+
const baseline = prepared.fullRequest ?? fullRequest;
|
|
1405
|
+
const priorLength = (baseline.input?.length ?? 0) - (prepared.request.input?.length ?? 0);
|
|
1406
|
+
const fingerprints = input.map(responsesInputFingerprint);
|
|
1407
|
+
if (fingerprints.length > 0) inputReplay = {
|
|
1408
|
+
afterResponseId: prepared.request.previous_response_id,
|
|
1409
|
+
before: resumedSteering.requiresInput ? fingerprints : [],
|
|
1410
|
+
after: resumedSteering.requiresInput ? [] : fingerprints
|
|
1411
|
+
};
|
|
1412
|
+
const delivered = resumedSteering.requiresInput ? input : [];
|
|
1413
|
+
prepared.fullRequest = {
|
|
1414
|
+
...baseline,
|
|
1415
|
+
input: [
|
|
1416
|
+
...(baseline.input ?? []).slice(0, priorLength),
|
|
1417
|
+
...resumedSteering.acceptedInput,
|
|
1418
|
+
...delivered
|
|
1419
|
+
]
|
|
1420
|
+
};
|
|
1421
|
+
prepared.request = {
|
|
1422
|
+
...prepared.request,
|
|
1423
|
+
input: delivered
|
|
1424
|
+
};
|
|
1425
|
+
deferredInput = !resumedSteering.requiresInput && input.length > 0;
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
lease.iterator.return?.().catch(() => void 0);
|
|
1428
|
+
lease.release({ keep: false });
|
|
1429
|
+
throw new OpenAIResponsesWebSocketPostDispatchError(error);
|
|
1430
|
+
}
|
|
1431
|
+
const steering = lease.entry && params.onActiveResponse && params.steeringInput ? createResponsesSteering({
|
|
1432
|
+
onActiveResponse: params.onActiveResponse,
|
|
1433
|
+
toInput: params.steeringInput,
|
|
1434
|
+
send: (event) => lease.socket.sendRaw(JSON.stringify(event)),
|
|
1435
|
+
needsContinuation: () => deferredInput,
|
|
1436
|
+
assertActive: () => {
|
|
1437
|
+
if (released || terminalReceived || params.signal?.aborted) throw new Error("Responses steering is no longer active");
|
|
1438
|
+
}
|
|
1439
|
+
}) : void 0;
|
|
2421
1440
|
const finish = ({ keep = true } = {}) => {
|
|
2422
1441
|
if (released) return;
|
|
2423
1442
|
released = true;
|
|
2424
1443
|
if (keep && lease.entry && terminalResponse) lease.entry.continuation = {
|
|
2425
|
-
lastRequest: fullRequest,
|
|
1444
|
+
lastRequest: prepared.fullRequest ?? fullRequest,
|
|
2426
1445
|
lastResponseId: terminalResponse.id,
|
|
2427
1446
|
lastResponseItems: terminalResponse.output
|
|
2428
1447
|
};
|
|
@@ -2436,8 +1455,19 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
2436
1455
|
let requestDispatched = false;
|
|
2437
1456
|
try {
|
|
2438
1457
|
if (params.signal?.aborted) throw transportAbortError(params.signal);
|
|
1458
|
+
if (resumedSteering) {
|
|
1459
|
+
requestDispatched = true;
|
|
1460
|
+
if (resumedSteering.requiresInput) lease.socket.send({
|
|
1461
|
+
...prepared.request,
|
|
1462
|
+
type: "response.create"
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
2439
1465
|
for (;;) {
|
|
2440
|
-
const
|
|
1466
|
+
const buffered = resumedSteering?.buffered.shift();
|
|
1467
|
+
const next = buffered ? {
|
|
1468
|
+
value: buffered,
|
|
1469
|
+
done: false
|
|
1470
|
+
} : await nextWebSocketMessage(iterator, params.signal);
|
|
2441
1471
|
if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
|
|
2442
1472
|
if (next.value.type === "open") {
|
|
2443
1473
|
if (!requestDispatched) {
|
|
@@ -2451,8 +1481,51 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
2451
1481
|
}
|
|
2452
1482
|
const event = readServerEvent(next.value);
|
|
2453
1483
|
if (!event) continue;
|
|
1484
|
+
if (isRecord(event) && typeof event.type === "string" && event.type.startsWith("response.steer.")) {
|
|
1485
|
+
if ((isRecord(event.steer) && event.steer.previous_response_id === steering?.responseId ? steering : resumedSteering?.steering ?? steering)?.handle(event)) continue;
|
|
1486
|
+
}
|
|
1487
|
+
steering?.handle(event);
|
|
2454
1488
|
if (event.type === "response.completed") terminalResponse = event.response;
|
|
2455
1489
|
terminalReceived = event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed";
|
|
1490
|
+
if (terminalReceived) {
|
|
1491
|
+
steering?.seal();
|
|
1492
|
+
if (event.type === "response.failed") steering?.close(/* @__PURE__ */ new Error("Responses failed before steering could be applied"));
|
|
1493
|
+
const continuationBuffer = [];
|
|
1494
|
+
for (;;) {
|
|
1495
|
+
if (!steering?.pending) break;
|
|
1496
|
+
const acknowledgement = await nextWebSocketMessage(iterator, params.signal);
|
|
1497
|
+
if (acknowledgement.done) throw new Error("Responses closed before acknowledging steering");
|
|
1498
|
+
const acknowledgedEvent = readServerEvent(acknowledgement.value);
|
|
1499
|
+
if (!steering.handle(acknowledgedEvent)) continuationBuffer.push(acknowledgement.value);
|
|
1500
|
+
}
|
|
1501
|
+
const acceptedInput = steering?.acceptedInput ?? [];
|
|
1502
|
+
const isSteered = event.type === "response.incomplete" && isRecord(event.response.incomplete_details) && event.response.incomplete_details.reason === "steered";
|
|
1503
|
+
if (lease.entry && steering && acceptedInput.length > 0 && (event.type === "response.completed" || isSteered)) {
|
|
1504
|
+
if (event.type === "response.incomplete") terminalResponse = event.response;
|
|
1505
|
+
lease.entry.steeringContinuation = {
|
|
1506
|
+
iterator,
|
|
1507
|
+
buffered: continuationBuffer,
|
|
1508
|
+
acceptedInput,
|
|
1509
|
+
requiresInput: (terminalResponse?.output ?? []).some((item) => (item.type === "function_call" || item.type === "custom_tool_call") && !(isRecord(item) && item.async === true) || item.type === "mcp_approval_request"),
|
|
1510
|
+
steering,
|
|
1511
|
+
instructions: fullRequest.instructions,
|
|
1512
|
+
tools: fullRequest.tools
|
|
1513
|
+
};
|
|
1514
|
+
retainIterator = true;
|
|
1515
|
+
if (isSteered) {
|
|
1516
|
+
yield {
|
|
1517
|
+
...event,
|
|
1518
|
+
type: "response.completed",
|
|
1519
|
+
response: {
|
|
1520
|
+
...event.response,
|
|
1521
|
+
status: "completed",
|
|
1522
|
+
incomplete_details: null
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
2456
1529
|
yield event;
|
|
2457
1530
|
if (terminalReceived) {
|
|
2458
1531
|
degradedWebSocketConnections.delete(degradationKey);
|
|
@@ -2460,20 +1533,28 @@ function createOpenAIResponsesWebSocketStream(params) {
|
|
|
2460
1533
|
}
|
|
2461
1534
|
}
|
|
2462
1535
|
} catch (error) {
|
|
1536
|
+
const steeringDispatched = Boolean(steering?.pending || steering?.acceptedInput.length || resumedSteering);
|
|
1537
|
+
steering?.close(error instanceof Error ? error : /* @__PURE__ */ new Error("Responses steering failed"));
|
|
2463
1538
|
if (lease.entry) lease.entry.continuation = void 0;
|
|
2464
|
-
const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
|
|
1539
|
+
const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError && !steeringDispatched;
|
|
2465
1540
|
if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
|
|
2466
1541
|
if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
|
|
2467
1542
|
if (!requestDispatched || params.callerSignal?.aborted || safeRetry) throw error;
|
|
2468
1543
|
throw new OpenAIResponsesWebSocketPostDispatchError(error);
|
|
2469
1544
|
} finally {
|
|
2470
|
-
|
|
1545
|
+
steering?.seal();
|
|
1546
|
+
if (!retainIterator) {
|
|
1547
|
+
steering?.close(/* @__PURE__ */ new Error("Responses stream ended before steering was confirmed"));
|
|
1548
|
+
await iterator.return?.().catch(() => void 0);
|
|
1549
|
+
}
|
|
2471
1550
|
if (!terminalReceived) finish({ keep: false });
|
|
2472
1551
|
}
|
|
2473
1552
|
} },
|
|
2474
1553
|
request: prepared.request,
|
|
1554
|
+
fullRequest: prepared.fullRequest ?? fullRequest,
|
|
2475
1555
|
reusedConnection: lease.reusedConnection,
|
|
2476
1556
|
continuationStatus: prepared.continuationStatus,
|
|
1557
|
+
inputReplay,
|
|
2477
1558
|
finish
|
|
2478
1559
|
};
|
|
2479
1560
|
}
|
|
@@ -2486,6 +1567,123 @@ function closeOpenAIResponsesWebSocketSessions(sessionId) {
|
|
|
2486
1567
|
}
|
|
2487
1568
|
registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
|
|
2488
1569
|
//#endregion
|
|
1570
|
+
//#region packages/ai/src/transports/openai-responses-compact-client.ts
|
|
1571
|
+
async function postOpenAIResponsesCompaction(params) {
|
|
1572
|
+
const compactInput = typeof params.request.instructions === "string" && params.request.instructions.length > 0 ? [buildOpenAIResponsesCompactSystemMessage(params.model, params.request.instructions), ...params.request.input ?? []] : params.request.input;
|
|
1573
|
+
const response = await params.client.post("/responses/compact", {
|
|
1574
|
+
...buildOpenAISdkRequestOptions(params.model, params.options?.signal, { timeoutMs: params.options?.timeoutMs }),
|
|
1575
|
+
body: {
|
|
1576
|
+
model: params.request.model,
|
|
1577
|
+
input: compactInput
|
|
1578
|
+
}
|
|
1579
|
+
});
|
|
1580
|
+
const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
|
|
1581
|
+
const item = output.at(-1);
|
|
1582
|
+
const retainedItems = output.slice(0, -1);
|
|
1583
|
+
const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
|
|
1584
|
+
const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
|
|
1585
|
+
const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
|
|
1586
|
+
const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
|
|
1587
|
+
if (!isRecord(response) || response.object !== "response.compaction" || !isOpenAIResponsesCompactionOutput(output, params.model) || retainedItems.length > 0 && (!retainedMessagePrefixSupported || retainedUserMessageCount !== inputUserMessageCount) || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return one trailing compaction item");
|
|
1588
|
+
return {
|
|
1589
|
+
output,
|
|
1590
|
+
item,
|
|
1591
|
+
historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
|
|
1592
|
+
usage,
|
|
1593
|
+
model: params.model,
|
|
1594
|
+
replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
|
|
1595
|
+
authProfileId: params.options?.authProfileId,
|
|
1596
|
+
sessionId: params.options?.sessionId
|
|
1597
|
+
})
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
//#endregion
|
|
1601
|
+
//#region packages/ai/src/transports/openai-responses-compact-request.ts
|
|
1602
|
+
const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
|
|
1603
|
+
function claimResponsesCompactRequest(options) {
|
|
1604
|
+
const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
|
|
1605
|
+
if (controller?.claimed === false) {
|
|
1606
|
+
controller.claimed = true;
|
|
1607
|
+
return controller;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
/** Run a compact-endpoint request through the session's prepared stream stack. */
|
|
1611
|
+
async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
|
|
1612
|
+
const preparedOptions = { ...options };
|
|
1613
|
+
let resolveResult;
|
|
1614
|
+
let rejectResult;
|
|
1615
|
+
const result = new Promise((resolve, reject) => {
|
|
1616
|
+
resolveResult = resolve;
|
|
1617
|
+
rejectResult = reject;
|
|
1618
|
+
});
|
|
1619
|
+
const controller = {
|
|
1620
|
+
claimed: false,
|
|
1621
|
+
resolve: resolveResult,
|
|
1622
|
+
reject: rejectResult
|
|
1623
|
+
};
|
|
1624
|
+
Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
|
|
1625
|
+
const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
|
|
1626
|
+
if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
|
|
1627
|
+
try {
|
|
1628
|
+
return await result;
|
|
1629
|
+
} finally {
|
|
1630
|
+
await stream.result().catch(() => void 0);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
//#endregion
|
|
1634
|
+
//#region packages/ai/src/transports/openai-responses-reasoning-state.ts
|
|
1635
|
+
function inputReplay(message) {
|
|
1636
|
+
const value = "openclawResponsesInputReplay" in message ? message.openclawResponsesInputReplay : void 0;
|
|
1637
|
+
return isRecord(value) ? value : void 0;
|
|
1638
|
+
}
|
|
1639
|
+
/** Save only admitted settings and hashes, never another copy of the conversation. */
|
|
1640
|
+
function recordResponsesReasoningState(message, model, identity, request, output) {
|
|
1641
|
+
if (!supportsResponsesReasoningUpdate(request) || !isRecord(request.reasoning) || !request.input || request.previous_response_id || message.providerReplay || output.some((item) => isRecord(item) && item.type === "compaction")) return;
|
|
1642
|
+
const reasoning = {
|
|
1643
|
+
...buildProviderReplayContext(model, identity),
|
|
1644
|
+
effort: request.reasoning.effort,
|
|
1645
|
+
controls: request.input.flatMap((item, index) => isConfigurationUpdate(item) ? [{
|
|
1646
|
+
index,
|
|
1647
|
+
item
|
|
1648
|
+
}] : []),
|
|
1649
|
+
inputLength: request.input.length,
|
|
1650
|
+
outputLength: output.length,
|
|
1651
|
+
prefixHash: responsesContinuationPrefixFingerprint(request.input, output),
|
|
1652
|
+
requestHash: responsesContinuationRequestFingerprint(request)
|
|
1653
|
+
};
|
|
1654
|
+
Object.assign(message, { openclawResponsesInputReplay: {
|
|
1655
|
+
...inputReplay(message),
|
|
1656
|
+
reasoning
|
|
1657
|
+
} });
|
|
1658
|
+
}
|
|
1659
|
+
/** A cold transport can replay controls, but cannot resurrect a server response handle. */
|
|
1660
|
+
function restoreResponsesReasoningState(context, model, identity, request) {
|
|
1661
|
+
const latest = context.messages.findLast((message) => message.role === "assistant");
|
|
1662
|
+
const state = latest ? inputReplay(latest)?.reasoning : void 0;
|
|
1663
|
+
if (!isRecord(state)) return request;
|
|
1664
|
+
const { effort, inputLength, outputLength, controls, prefixHash, requestHash } = state;
|
|
1665
|
+
if (!isOpenAIResponsesReplayContext(state) || !providerReplayContextMatches(state, buildProviderReplayContext(model, identity)) || latest?.providerReplay || !supportsResponsesReasoningUpdate(request) || !isRecord(request.reasoning) || !request.input || request.previous_response_id || request.input.some(isConfigurationUpdate) || typeof effort !== "string" || typeof inputLength !== "number" || !Number.isSafeInteger(inputLength) || inputLength < 0 || typeof outputLength !== "number" || !Number.isSafeInteger(outputLength) || outputLength < 0 || !Array.isArray(controls) || controls.length > inputLength || inputLength + outputLength > request.input.length + controls.length) return request;
|
|
1666
|
+
const previousInput = request.input.slice(0, inputLength - controls.length);
|
|
1667
|
+
let lastIndex = -1;
|
|
1668
|
+
for (const control of controls) {
|
|
1669
|
+
if (!isRecord(control) || typeof control.index !== "number" || !Number.isSafeInteger(control.index) || control.index <= lastIndex || control.index > previousInput.length || !isConfigurationUpdate(control.item)) return request;
|
|
1670
|
+
previousInput.splice(control.index, 0, control.item);
|
|
1671
|
+
lastIndex = control.index;
|
|
1672
|
+
}
|
|
1673
|
+
const previous = {
|
|
1674
|
+
...request,
|
|
1675
|
+
reasoning: {
|
|
1676
|
+
...request.reasoning,
|
|
1677
|
+
effort
|
|
1678
|
+
},
|
|
1679
|
+
input: previousInput
|
|
1680
|
+
};
|
|
1681
|
+
if (responsesContinuationRequestFingerprint(previous) !== requestHash) return request;
|
|
1682
|
+
const prepared = replayResponsesReasoningUpdates(previous, request, outputLength);
|
|
1683
|
+
if (responsesContinuationPrefixFingerprint((prepared.input ?? []).slice(0, inputLength + outputLength)) !== prefixHash) return request;
|
|
1684
|
+
return prepared;
|
|
1685
|
+
}
|
|
1686
|
+
//#endregion
|
|
2489
1687
|
//#region packages/ai/src/transports/openai-responses-client.ts
|
|
2490
1688
|
function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
|
|
2491
1689
|
if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
|
|
@@ -2529,35 +1727,6 @@ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turn
|
|
|
2529
1727
|
...buildOpenAISdkClientOptions(model)
|
|
2530
1728
|
});
|
|
2531
1729
|
}
|
|
2532
|
-
async function postOpenAIResponsesCompaction(params) {
|
|
2533
|
-
const compactInput = typeof params.request.instructions === "string" && params.request.instructions.length > 0 ? [buildOpenAIResponsesCompactSystemMessage(params.model, params.request.instructions), ...params.request.input ?? []] : params.request.input;
|
|
2534
|
-
const response = await params.client.post("/responses/compact", {
|
|
2535
|
-
...buildOpenAISdkRequestOptions(params.model, params.options?.signal, { timeoutMs: params.options?.timeoutMs }),
|
|
2536
|
-
body: {
|
|
2537
|
-
model: params.request.model,
|
|
2538
|
-
input: compactInput
|
|
2539
|
-
}
|
|
2540
|
-
});
|
|
2541
|
-
const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
|
|
2542
|
-
const item = output.at(-1);
|
|
2543
|
-
const retainedItems = output.slice(0, -1);
|
|
2544
|
-
const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
|
|
2545
|
-
const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
|
|
2546
|
-
const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
|
|
2547
|
-
const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
|
|
2548
|
-
if (!isRecord(response) || response.object !== "response.compaction" || !isOpenAIResponsesCompactionOutput(output, params.model) || retainedItems.length > 0 && (!retainedMessagePrefixSupported || retainedUserMessageCount !== inputUserMessageCount) || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return one trailing compaction item");
|
|
2549
|
-
return {
|
|
2550
|
-
output,
|
|
2551
|
-
item,
|
|
2552
|
-
historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
|
|
2553
|
-
usage,
|
|
2554
|
-
model: params.model,
|
|
2555
|
-
replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
|
|
2556
|
-
authProfileId: params.options?.authProfileId,
|
|
2557
|
-
sessionId: params.options?.sessionId
|
|
2558
|
-
})
|
|
2559
|
-
};
|
|
2560
|
-
}
|
|
2561
1730
|
function createResponsesTransportExecutor(config) {
|
|
2562
1731
|
return (model, context, options) => {
|
|
2563
1732
|
const responsesOptions = options;
|
|
@@ -2579,8 +1748,10 @@ function createResponsesTransportExecutor(config) {
|
|
|
2579
1748
|
const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
|
|
2580
1749
|
const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
|
|
2581
1750
|
const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId, compactRequest ? createBoundedOpenAIResponsesCompactionFetch(buildGuardedModelFetch(model)) : void 0);
|
|
2582
|
-
const
|
|
2583
|
-
|
|
1751
|
+
const nativeAstra = model.id === "gpt-6-astra" && supportsNativeOpenAIResponsesEndpoint(model);
|
|
1752
|
+
const asyncToolExecutionEligible = nativeAstra && options?.asyncToolExecution === true && !responsesOptions?.openclawCodeModeToolSurface;
|
|
1753
|
+
const prepareRequest = async (request) => {
|
|
1754
|
+
let params = request;
|
|
2584
1755
|
const nextParams = await options?.onPayload?.(params, model);
|
|
2585
1756
|
if (nextParams !== void 0) params = nextParams;
|
|
2586
1757
|
if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
|
|
@@ -2592,9 +1763,15 @@ function createResponsesTransportExecutor(config) {
|
|
|
2592
1763
|
enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes, codeModeToolSurfaceObserver.get(options));
|
|
2593
1764
|
assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
|
|
2594
1765
|
}
|
|
1766
|
+
if (asyncToolExecutionEligible && params.model === "gpt-6-astra" && params.multi_agent?.enabled !== true && params.tools) params.tools = params.tools.map((tool) => tool.type === "function" ? {
|
|
1767
|
+
...tool,
|
|
1768
|
+
async: true
|
|
1769
|
+
} : tool);
|
|
2595
1770
|
return params;
|
|
2596
1771
|
};
|
|
2597
|
-
const
|
|
1772
|
+
const buildRequest = (replayMode, requestContext = context) => prepareRequest(config.buildRequest(model, requestContext, responsesOptions, turnState?.metadata, replayMode));
|
|
1773
|
+
let params = await buildRequest("checkpoint");
|
|
1774
|
+
const asyncTools = asyncToolExecutionEligible && params.model === "gpt-6-astra" && params.multi_agent?.enabled !== true;
|
|
2598
1775
|
if (compactRequest) {
|
|
2599
1776
|
const compacted = await postOpenAIResponsesCompaction({
|
|
2600
1777
|
client,
|
|
@@ -2618,13 +1795,17 @@ function createResponsesTransportExecutor(config) {
|
|
|
2618
1795
|
provider: model.provider,
|
|
2619
1796
|
api: model.api,
|
|
2620
1797
|
baseUrl: model.baseUrl
|
|
2621
|
-
}) && sessionId && params.store === true && !params.previous_response_id)
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
1798
|
+
}) && sessionId && (params.store === true || supportsResponsesReasoningUpdate(params)) && !params.previous_response_id) {
|
|
1799
|
+
continuationClaim = claimOpenAIResponsesHttpContinuation({
|
|
1800
|
+
sessionId,
|
|
1801
|
+
apiKey,
|
|
1802
|
+
baseUrl: model.baseUrl,
|
|
1803
|
+
headers: buildOpenAIClientHeaders(model, context, options?.headers, turnState?.headers, sessionId),
|
|
1804
|
+
request: params,
|
|
1805
|
+
restoreRequest: () => restoreResponsesReasoningState(context, model, responsesOptions, params)
|
|
1806
|
+
});
|
|
1807
|
+
if (continuationClaim) params = continuationClaim.fullRequest;
|
|
1808
|
+
}
|
|
2628
1809
|
const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
|
|
2629
1810
|
const requestStartedAt = Date.now();
|
|
2630
1811
|
let started = false;
|
|
@@ -2659,7 +1840,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
2659
1840
|
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint),
|
|
2660
1841
|
canRetryStream: () => output.content.length === 0,
|
|
2661
1842
|
wrapStream: ({ stream: rawResponseStream, response, attempt }) => {
|
|
2662
|
-
|
|
1843
|
+
continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
|
|
2663
1844
|
return withProviderResponseHook({
|
|
2664
1845
|
stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
|
|
2665
1846
|
signal: firstEvent.signal,
|
|
@@ -2675,6 +1856,7 @@ function createResponsesTransportExecutor(config) {
|
|
|
2675
1856
|
return responseStream;
|
|
2676
1857
|
};
|
|
2677
1858
|
let responseStream;
|
|
1859
|
+
let websocketBaseline;
|
|
2678
1860
|
let finishWebSocket;
|
|
2679
1861
|
let transport = "sse";
|
|
2680
1862
|
const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
|
|
@@ -2688,14 +1870,22 @@ function createResponsesTransportExecutor(config) {
|
|
|
2688
1870
|
const websocket = createOpenAIResponsesWebSocketStream({
|
|
2689
1871
|
client,
|
|
2690
1872
|
request: params,
|
|
1873
|
+
restoreRequest: (request) => restoreResponsesReasoningState(context, model, responsesOptions, request),
|
|
2691
1874
|
mode: websocketMode,
|
|
2692
1875
|
sessionId: options?.sessionId,
|
|
2693
1876
|
headers: websocketHeaders,
|
|
2694
1877
|
signal: websocketSignal,
|
|
2695
1878
|
callerSignal: options?.signal,
|
|
2696
|
-
degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs
|
|
1879
|
+
degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs,
|
|
1880
|
+
onActiveResponse: nativeAstra && params.model === "gpt-6-astra" ? options?.onActiveResponse : void 0,
|
|
1881
|
+
steeringInput: (messages) => projectResponsesSteeringInput(params, () => buildRequest("checkpoint", {
|
|
1882
|
+
...context,
|
|
1883
|
+
messages: [...context.messages, ...messages]
|
|
1884
|
+
}))
|
|
2697
1885
|
});
|
|
2698
1886
|
finishWebSocket = websocket.finish;
|
|
1887
|
+
websocketBaseline = websocket.fullRequest;
|
|
1888
|
+
recordResponsesInputReplay(output, websocket.inputReplay);
|
|
2699
1889
|
observePrompt?.(websocket.request, {
|
|
2700
1890
|
egress: "responses-websocket",
|
|
2701
1891
|
payloadVariant: "initial"
|
|
@@ -2737,7 +1927,8 @@ function createResponsesTransportExecutor(config) {
|
|
|
2737
1927
|
yield* await createSseStream();
|
|
2738
1928
|
}
|
|
2739
1929
|
} };
|
|
2740
|
-
} catch {
|
|
1930
|
+
} catch (error) {
|
|
1931
|
+
if (error instanceof OpenAIResponsesWebSocketPostDispatchError) throw error;
|
|
2741
1932
|
closeWebSocketForFallback("setup_failure");
|
|
2742
1933
|
responseStream = await createSseStream();
|
|
2743
1934
|
}
|
|
@@ -2752,11 +1943,14 @@ function createResponsesTransportExecutor(config) {
|
|
|
2752
1943
|
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
2753
1944
|
authProfileId: responsesOptions?.authProfileId,
|
|
2754
1945
|
sessionId: options?.sessionId
|
|
2755
|
-
})
|
|
1946
|
+
}),
|
|
1947
|
+
asyncToolExecution: asyncTools
|
|
2756
1948
|
});
|
|
2757
1949
|
finishWebSocket?.();
|
|
2758
1950
|
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
2759
1951
|
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
|
|
1952
|
+
const admitted = transport === "websocket" ? websocketBaseline : continuationBaseline;
|
|
1953
|
+
if (terminal && admitted && supportsNativeOpenAIResponsesEndpoint(model)) recordResponsesReasoningState(output, model, responsesOptions, admitted, terminal.output);
|
|
2760
1954
|
if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal);
|
|
2761
1955
|
} catch (error) {
|
|
2762
1956
|
finishWebSocket?.({ keep: false });
|
|
@@ -3039,7 +2233,7 @@ const SIMPLE_TRANSPORT_API_ALIAS = {
|
|
|
3039
2233
|
"google-generative-ai": "openclaw-google-generative-ai-transport"
|
|
3040
2234
|
};
|
|
3041
2235
|
function createProviderOwnedGoogleTransportStreamFn(model, ctx) {
|
|
3042
|
-
|
|
2236
|
+
const streamFn = getAiTransportHost().plugin.resolveProviderStream({
|
|
3043
2237
|
provider: model.provider,
|
|
3044
2238
|
config: ctx?.cfg,
|
|
3045
2239
|
workspaceDir: ctx?.workspaceDir,
|
|
@@ -3066,6 +2260,10 @@ function createProviderOwnedGoogleTransportStreamFn(model, ctx) {
|
|
|
3066
2260
|
model
|
|
3067
2261
|
}
|
|
3068
2262
|
}) ?? void 0;
|
|
2263
|
+
return streamFn ? (requestModel, context, options) => streamFn(requestModel, context, {
|
|
2264
|
+
...options,
|
|
2265
|
+
headers: resolveOpencodeSessionHeaders(requestModel, options)
|
|
2266
|
+
}) : void 0;
|
|
3069
2267
|
}
|
|
3070
2268
|
function createSupportedTransportStreamFn(model, ctx) {
|
|
3071
2269
|
switch (model.api) {
|
|
@@ -3250,7 +2448,9 @@ function prepareProviderStreamModel(params) {
|
|
|
3250
2448
|
const streamFn = providerStreamFn ? wrapPluginProviderStream(providerStreamFn) : transportFallback && params.model.api === "google-generative-ai" ? wrapPluginProviderStream(transportFallback) : transportFallback;
|
|
3251
2449
|
if (!streamFn) return;
|
|
3252
2450
|
const api = params.apiRegistry.getApiProvider(params.model.api) ? resolveProviderStreamApi(params.model) : params.model.api;
|
|
3253
|
-
|
|
2451
|
+
const sourceApi = params.model.api;
|
|
2452
|
+
const sourceStreamFn = (runtimeModel, context, options) => streamFn(projectModel(runtimeModel, { api: sourceApi }), context, options);
|
|
2453
|
+
if (!registerCustomApi(params.apiRegistry, api, sourceStreamFn)) return;
|
|
3254
2454
|
return api === params.model.api ? params.model : projectModel(params.model, { api });
|
|
3255
2455
|
}
|
|
3256
2456
|
function prepareModelForSimpleCompletion(params) {
|
|
@@ -3275,4 +2475,4 @@ function prepareModelForSimpleCompletion(params) {
|
|
|
3275
2475
|
return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
|
|
3276
2476
|
}
|
|
3277
2477
|
//#endregion
|
|
3278
|
-
export { CompactionReplayRefreshRequiredError, GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE,
|
|
2478
|
+
export { CompactionReplayRefreshRequiredError, GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, applyAnthropicContextManagementToRequest, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyAnthropicRequestCacheControl, applyCompletionsAnthropicCacheControl, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildAnthropicSystemBlocks, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, consumeGoogleGenerateContentStream, convertGoogleTools, copyProviderAcceptanceObserver, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIProviderAcceptanceHook, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTerminalToolCallArguments, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isAnthropicServerToolClearingEnabled, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isDirectAnthropicModel, isNativeOpenAIEndpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, logAnthropicContextEdits, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, notifyProviderHttpMetadata, notifyProviderHttpResponse, notifyProviderStreamOpened, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, parseTerminalToolCallArguments, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, preserveCompactionReplayWindow, projectGoogleMessages, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, readOpenAICompletionsReasoningBatch, replaceCompactionReplayOwnerContent, requestPreparedOpenAIResponsesCompaction, requiresCompactionReplayRefresh, requiresGoogleToolCallId, resolveAnthropicCacheOptions, resolveAnthropicContextManagementBetaHeader, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveAnthropicServerCompactionPlan, resolveCodeModeResponsesVisibleToolNames, resolveCompactionReplayEligibility, resolveCompactionReplayPressure, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAIClientBaseUrl, resolveOpenAICompletionsCompat, resolveOpenAIPromptCacheKeySupport, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesCompactEndpointPlan, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesServerCompactionPlan, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompactionReplayCheckpoint, stripCompactionReplayCheckpointInPlace, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend, withProviderAcceptanceObserver, withProviderResponseHook };
|