@openclaw/ai 2026.10.1-beta.2 → 2026.10.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +6 -0
  2. package/dist/{anthropic-CZv6XFER.mjs → anthropic-BAHKgWam.mjs} +30 -59
  3. package/dist/{anthropic-payload-policy-BhCcgNdm.d.mts → anthropic-payload-policy-DGblkHL5.d.mts} +19 -16
  4. package/dist/{anthropic-stream-reducer-BQ1tjd7x.mjs → anthropic-stream-reducer-g6w1YVii.mjs} +197 -285
  5. package/dist/{api-registry-DpU0Rj-I.d.mts → api-registry-BhXXYdRA.d.mts} +2 -2
  6. package/dist/assistant-message-events-CHMJgbhs.mjs +18 -0
  7. package/dist/{assistant-output-OLm-Oln0.mjs → assistant-output-CUEeSotf.mjs} +2 -2
  8. package/dist/{assistant-text-phase-Bl1aDCB7.mjs → assistant-text-phase-BrV8KdFO.mjs} +7 -6
  9. package/dist/{azure-openai-responses-DH7bd3QT.mjs → azure-openai-responses-C7qBuR23.mjs} +8 -9
  10. package/dist/{credential-redaction-BKv49aiv.d.mts → credential-redaction-BHxruEH6.d.mts} +3 -1
  11. package/dist/{diagnostics-Caj-4RBA.mjs → diagnostics-tQasYct1.mjs} +10 -3
  12. package/dist/diagnostics.d.mts +7 -7
  13. package/dist/diagnostics.mjs +3 -3
  14. package/dist/{env-api-keys-CyX4VKGO.mjs → env-api-keys-CrxwdylR.mjs} +24 -35
  15. package/dist/event-stream-6UNs-AhN.d.mts +1 -0
  16. package/dist/{event-stream-DrRM_90c.d.mts → event-stream-BcqWLo_u.d.mts} +1 -1
  17. package/dist/{event-stream-C5AbCAaZ.mjs → event-stream-X2gmsUWv.mjs} +5 -8
  18. package/dist/event-stream.d.mts +2 -2
  19. package/dist/event-stream.mjs +1 -1
  20. package/dist/google-Boz2QcpK.mjs +20 -0
  21. package/dist/{google-interactions-wrnE6MLw.mjs → google-interactions-tjAb1mc5.mjs} +52 -84
  22. package/dist/{google-messages-BwPPycw6.mjs → google-messages-C-RZzloH.mjs} +25 -35
  23. package/dist/google-provider-stream-CMImyxbX.mjs +58 -0
  24. package/dist/{google-shared-Eql1OMPU.mjs → google-shared-OyAtMTZv.mjs} +6 -15
  25. package/dist/google-vertex-hCxTJea7.mjs +40 -0
  26. package/dist/{host-BinBeGcQ.d.mts → host-CTDz7LXD.d.mts} +6 -3
  27. package/dist/{host-DCW52iHe.mjs → host-JWXZWa-c.mjs} +12 -8
  28. package/dist/{host-policy-B3jc4Rpn.mjs → host-policy-q4Azym9F.mjs} +1 -1
  29. package/dist/{index-a8e_oKnu.d.mts → index-BYDvopUB.d.mts} +27 -6
  30. package/dist/index.d.mts +7 -7
  31. package/dist/index.mjs +7 -6
  32. package/dist/internal/anthropic.d.mts +6 -6
  33. package/dist/internal/anthropic.mjs +5 -5
  34. package/dist/internal/openai-completions-compat.d.mts +1 -1
  35. package/dist/internal/openai-completions-compat.mjs +1 -1
  36. package/dist/internal/openai-responses-payload-policy.d.mts +8 -1
  37. package/dist/internal/openai-responses-payload-policy.mjs +3 -3
  38. package/dist/internal/openai.d.mts +9 -9
  39. package/dist/internal/openai.mjs +9 -11
  40. package/dist/internal/runtime.d.mts +14 -11
  41. package/dist/internal/runtime.mjs +10 -13
  42. package/dist/internal/shared.d.mts +5 -5
  43. package/dist/internal/shared.mjs +6 -7
  44. package/dist/internal/tool-schema.mjs +2 -2
  45. package/dist/{llm-request-activity-D8f_oxHj.mjs → llm-request-activity-CfMuh5cC.mjs} +2 -2
  46. package/dist/{mistral-DSt4vQAO.mjs → mistral-DUveP4hF.mjs} +71 -146
  47. package/dist/{model-transport-url-C5f59L3Y.d.mts → model-transport-url-BrsjjdjR.d.mts} +5 -1
  48. package/dist/{number-coercion-V8-BhME4.mjs → number-coercion-C3hztWoY.mjs} +5 -4
  49. package/dist/{openai-chatgpt-responses-BC2x0Oqx.mjs → openai-chatgpt-responses-OCwckGhY.mjs} +173 -242
  50. package/dist/{openai-completions-DNyHnXKg.mjs → openai-completions-DOltBUe9.mjs} +7 -6
  51. package/dist/{openai-completions-compat-BYdYpXhU.d.mts → openai-completions-compat-i-nLZxFy.d.mts} +10 -11
  52. package/dist/{openai-completions-compat-BdmlhD4u.mjs → openai-completions-compat-pg1q4uNG.mjs} +10 -6
  53. package/dist/openai-completions-transport-OMrn7gnZ.mjs +3319 -0
  54. package/dist/{openai-prompt-cache-I8YQTzf8.mjs → openai-prompt-cache-Cr2xKVrS.mjs} +1 -1
  55. package/dist/{openai-prompt-cache-45-AH_xv.d.mts → openai-prompt-cache-DIPMrMFy.d.mts} +2 -2
  56. package/dist/{openai-responses-uZ5oWSp7.mjs → openai-responses-BSBJUA19.mjs} +9 -10
  57. package/dist/{openai-responses-compaction-replay-BClApUIf.d.mts → openai-responses-compaction-replay-B2FE4Nw8.d.mts} +4 -4
  58. package/dist/{openai-responses-contracts-Br5IIw_h.d.mts → openai-responses-contracts-DKqMJ1Ct.d.mts} +9 -4
  59. package/dist/{openai-responses-contracts-BKu8O7l4.mjs → openai-responses-contracts-DlKMMTxK.mjs} +26 -6
  60. package/dist/{openai-responses-compaction-window-m878aVpU.mjs → openai-responses-endpoint-CinGh4VZ.mjs} +14 -4
  61. package/dist/{openai-responses-prompt-observer-internal-CCg7B6l4.mjs → openai-responses-request-lifecycle-D2IFiI0-.mjs} +39 -4
  62. package/dist/{openai-responses-shared-C7oVyjED.mjs → openai-responses-shared-DVGhq5xF.mjs} +1036 -327
  63. package/dist/{openai-responses-terminal-usage-DgImcyJu.d.mts → openai-responses-terminal-usage-BygjiAdi.d.mts} +2 -2
  64. package/dist/{openai-tool-schema-DYNePiF8.mjs → openai-tool-schema-DDPXsvJP.mjs} +236 -399
  65. package/dist/{positive-integer-CwLi8LUL.mjs → positive-integer-CqWjqSi9.mjs} +1 -1
  66. package/dist/{provider-error-DQBE1bUQ.mjs → provider-error-CF9GWCs2.mjs} +5 -3
  67. package/dist/{provider-options-D5ABCV_l.d.mts → provider-options-CVrXZBtA.d.mts} +3 -2
  68. package/dist/{provider-transport-turn-state-_M2kLdUE.mjs → provider-transport-turn-state-LuCaTsSO.mjs} +2 -2
  69. package/dist/{provider-types-F8Mqc9Ma.d.mts → provider-types-CHtRx0dI.d.mts} +3 -3
  70. package/dist/provider-types.d.mts +6 -6
  71. package/dist/providers.d.mts +2 -2
  72. package/dist/providers.mjs +12 -12
  73. package/dist/{reasoning-tag-text-partitioner-Ju9VpUGl.mjs → reasoning-tag-text-partitioner-0-Rsezog.mjs} +13 -12
  74. package/dist/{required-api-key-D4kn3252.mjs → required-api-key-CFJvcFy_.mjs} +1 -1
  75. package/dist/{simple-options-BkKTXxhp.mjs → simple-options-BARFAzk9.mjs} +9 -11
  76. package/dist/{src-B0NIt2UK.mjs → src-C_wfIUmV.mjs} +35 -10
  77. package/dist/{stream-DSdzKsDK.mjs → stream-DwrETW0_.mjs} +12 -21
  78. package/dist/{stream-first-event-timeout-D9ZmL_s9.mjs → stream-first-event-timeout-Dc42hKv6.mjs} +8 -21
  79. package/dist/{streaming-byte-guard-CC-HMn_u.mjs → streaming-byte-guard-DBXr1XYn.mjs} +16 -1
  80. package/dist/{string-normalization-PN10hf6d.mjs → string-normalization-I3ux50s9.mjs} +1 -1
  81. package/dist/{text-signature-MLwfi6fq.mjs → text-signature-B_MB6U_9.mjs} +32 -5
  82. package/dist/{tool-schema-json-projection-Ct8SeBeJ.mjs → tool-schema-json-projection-B8sG1I9v.mjs} +29 -23
  83. package/dist/{transport-stream-shared-BcAKh2XH.d.mts → transport-stream-shared-BmpzDtdQ.d.mts} +4 -4
  84. package/dist/{transport-stream-shared-CxXXz_Dd.mjs → transport-stream-shared-DH08b7Il.mjs} +31 -20
  85. package/dist/transports.d.mts +33 -22
  86. package/dist/transports.mjs +73 -1234
  87. package/dist/{types-DvzmWW8J.d.mts → types-Bd2T86VJ.d.mts} +5 -23
  88. package/dist/types-Kdfw_04V.d.mts +1 -0
  89. package/dist/types.d.mts +6 -6
  90. package/dist/types.mjs +5 -4
  91. package/dist/{validation-B3lQt_VE.d.mts → validation-DdGB1yy3.d.mts} +1 -1
  92. package/dist/{validation-DbqlIfDn.mjs → validation-opC2pjk9.mjs} +2 -3
  93. package/dist/validation.d.mts +1 -1
  94. package/dist/validation.mjs +1 -1
  95. package/package.json +4 -4
  96. package/dist/event-stream-DqDQWqiy.d.mts +0 -1
  97. package/dist/google-1WLOtb8w.mjs +0 -54
  98. package/dist/google-vertex-0LrlRoO3.mjs +0 -88
  99. package/dist/openai-completions-transport-DWM2mQDB.mjs +0 -2081
  100. package/dist/openai-responses-request-lifecycle-Fsag-IGZ.mjs +0 -36
  101. package/dist/openai-transport-params-JFsnIyto.mjs +0 -735
  102. package/dist/openai-web-search-tools-Cfu0uLyN.mjs +0 -14
  103. package/dist/provider-replay-context-C7Bmi84a.mjs +0 -25
  104. package/dist/types-BuiEBM3u.d.mts +0 -1
@@ -1,2081 +0,0 @@
1
- import { _ as runtimeContextContentToText, f as hasRuntimeContextMarker, p as isRuntimeContextMessage } from "./types-CQc6JNG4.mjs";
2
- import { r as appendAssistantThinking } from "./event-stream-C5AbCAaZ.mjs";
3
- import { o as isRecord } from "./record-coerce-CNDovvft.mjs";
4
- import { s as readNonEmptyStringPreservingWhitespace, t as hasNonEmptyString } from "./string-coerce-CtgOVOSv.mjs";
5
- import { n as asNonNegativeFiniteNumber } from "./number-coercion-V8-BhME4.mjs";
6
- import { N as isImageWithMediaPayload, r as getAiTransportHost } from "./host-DCW52iHe.mjs";
7
- import { _ as isOpenAIGpt54MiniModel, v as isOpenAIGpt55Model, y as isOpenAIGpt56Model } from "./openai-responses-contracts-BKu8O7l4.mjs";
8
- import { r as uniqueStrings } from "./string-normalization-PN10hf6d.mjs";
9
- import { o as truncateUtf16Safe } from "./provider-error-DQBE1bUQ.mjs";
10
- import { r as resolveOpenAIPromptCacheParams } from "./openai-prompt-cache-I8YQTzf8.mjs";
11
- import { r as emitModelTransportDebug } from "./diagnostics-Caj-4RBA.mjs";
12
- import { c as supportsModelTools, r as estimateStringChars } from "./text-signature-MLwfi6fq.mjs";
13
- import { i as resolveOpenAICompletionsCompat, l as shouldOmitOllamaCompatResponseFormat, n as isNativeOpenAIEndpoint, s as resolveOpenAICompletionsResponseFormat, t as detectOpenAICompletionsCompat } from "./openai-completions-compat-BdmlhD4u.mjs";
14
- import { i as resolveProviderEndpoint, t as buildGuardedModelFetch } from "./host-policy-B3jc4Rpn.mjs";
15
- import { i as resolveCacheRetention, r as buildCopilotDynamicHeaders } from "./cloudflare-5qSGvIUH.mjs";
16
- import { E as createToolArgumentPreviewSchedule, O as parseStreamingJson, P as sanitizeSurrogates, S as withProviderResponseHook, _ as sanitizeNonEmptyTransportPayloadText, b as transportAbortError, c as failTransportStream, d as iterateModelStream, l as finalizeTerminalToolCallArguments, s as createWritableTransportEventStream, u as finalizeTransportStream, y as throwIfModelStreamAborted } from "./transport-stream-shared-CxXXz_Dd.mjs";
17
- import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, S as splitSystemPromptRelocatableBoundary, d as describeToolResultMediaPlaceholder, l as codeModeToolSurfaceObserver, m as extractToolResultText, s as prepareModelRequestBody, u as reasoningTagTextPolicy, w as stripSystemPromptRelocatableBoundary, x as splitSystemPromptCacheBoundary } from "./simple-options-BkKTXxhp.mjs";
18
- import { C as log, D as readOpenAICompletionsReasoningBatch, E as readOpenAICompletionsContentDeltas, O as resolveOpenAIClientBaseUrl, S as isOpenAICompletionsThinkingEnabled, T as parseOpenAICompletionsUsage, a as enforceCodeModeResponsesToolSurface, d as prepareOpenAITools, f as projectOpenAITools, g as resolveOpenAISimpleReasoningEffort, h as resolveOpenAIRequestReasoning, i as buildOpenAISdkRequestOptions, k as resolvePromptCacheKey, l as resolveCodeModeResponsesVisibleToolNames, n as buildOpenAIClientHeaders, p as reconcileOpenAICompletionsToolChoice, r as buildOpenAISdkClientOptions, s as getCompat, t as assertCodeModeResponsesToolSurface, u as resolveOpenAIStrictToolFlagWithDiagnostics, v as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, w as measureUtf8AppendBytes, y as createOpenAIProviderAcceptanceHook } from "./openai-transport-params-JFsnIyto.mjs";
19
- import { n as getEnvApiKey } from "./env-api-keys-CyX4VKGO.mjs";
20
- import { n as transformProviderMessages, t as createAssistantOutput } from "./assistant-output-OLm-Oln0.mjs";
21
- import { a as tagUnresolvedTextAsCommentary, i as tagPendingCommentaryText, n as rememberPendingCommentaryTags, r as tagInterruptedTextPhases, t as clearPendingCommentaryText } from "./assistant-text-phase-Bl1aDCB7.mjs";
22
- import { t as notifyLlmRequestActivity } from "./llm-request-activity-D8f_oxHj.mjs";
23
- import { n as resolveOpencodeSessionHeaders } from "./session-affinity-DA4WHv1h.mjs";
24
- import "./types.mjs";
25
- import { i as createOpenAIProviderClient, n as resolveProviderSimpleCompletionHeaders, r as resolveProviderTransportTurnState, t as filterProviderTurnHeadersForExplicitOpencodeSession } from "./provider-transport-turn-state-_M2kLdUE.mjs";
26
- import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-D9ZmL_s9.mjs";
27
- import { f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-DYNePiF8.mjs";
28
- import { isGoogleGemini3ThinkingLevelModel } from "./internal/google-model-family.mjs";
29
- import { t as mapOpenAIStopReason } from "./openai-stop-reason-8s70vDXj.mjs";
30
- import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-Ju9VpUGl.mjs";
31
- import { randomUUID } from "node:crypto";
32
- import OpenAI from "openai";
33
- //#region packages/ai/src/transports/deepseek-dsml-grammar.ts
34
- const DEEPSEEK_DSML_MARKERS = [
35
- "|",
36
- "|",
37
- "||"
38
- ].map((bar) => `${bar}DSML${bar}`);
39
- const DEEPSEEK_DSML_MARKER_PATTERN = `(${DEEPSEEK_DSML_MARKERS.map((marker) => marker.replaceAll("|", "\\|")).join("|")})`;
40
- function findEarliestDsmlToken(text, tokens, fromIndex = 0) {
41
- let best = null;
42
- for (const token of tokens) {
43
- const index = text.indexOf(token, fromIndex);
44
- if (index !== -1 && (!best || index < best.index)) best = {
45
- index,
46
- token
47
- };
48
- }
49
- return best;
50
- }
51
- function longestDsmlTokenPrefixSuffixLength(text, tokens, maxTokenLength) {
52
- const maxLength = Math.min(text.length, maxTokenLength - 1);
53
- for (let length = maxLength; length > 0; length--) {
54
- const suffix = text.slice(text.length - length);
55
- if (tokens.some((token) => token.startsWith(suffix))) return length;
56
- }
57
- return 0;
58
- }
59
- //#endregion
60
- //#region packages/ai/src/transports/deepseek-text-filter.ts
61
- /**
62
- * DeepSeek DSML streaming text filter.
63
- * Removes provider-emitted DSML tool markup while buffering split tag prefixes
64
- * across streamed chunks.
65
- */
66
- const DSML_KINDS = [
67
- "tool_use_error",
68
- "tool_calls",
69
- "tool_call",
70
- "function_calls"
71
- ];
72
- const DSML_OPEN_TOKENS = DEEPSEEK_DSML_MARKERS.flatMap((marker) => DSML_KINDS.map((kind) => `<${marker}${kind}>`));
73
- const MAX_OPEN_TOKEN_LEN = Math.max(...DSML_OPEN_TOKENS.map((token) => token.length));
74
- /** Create an incremental text filter that strips DeepSeek DSML tool blocks. */
75
- function createDeepSeekTextFilter() {
76
- let buffer = "";
77
- let closeToken;
78
- const consume = (final) => {
79
- const output = [];
80
- const emit = (text) => {
81
- if (text) output.push(text);
82
- };
83
- while (buffer) {
84
- if (closeToken) {
85
- const closeIndex = buffer.indexOf(closeToken);
86
- if (closeIndex !== -1) {
87
- buffer = buffer.slice(closeIndex + closeToken.length);
88
- closeToken = void 0;
89
- continue;
90
- }
91
- const keep = final ? 0 : Math.min(buffer.length, closeToken.length - 1);
92
- buffer = buffer.slice(buffer.length - keep);
93
- if (final) closeToken = void 0;
94
- return output;
95
- }
96
- const open = findEarliestDsmlToken(buffer, DSML_OPEN_TOKENS);
97
- if (open) {
98
- emit(buffer.slice(0, open.index));
99
- buffer = buffer.slice(open.index + open.token.length);
100
- closeToken = open.token.replace("<", "</");
101
- continue;
102
- }
103
- if (final) {
104
- emit(buffer);
105
- buffer = "";
106
- return output;
107
- }
108
- const keep = longestDsmlTokenPrefixSuffixLength(buffer, DSML_OPEN_TOKENS, MAX_OPEN_TOKEN_LEN);
109
- const emitLength = buffer.length - keep;
110
- if (emitLength <= 0) return output;
111
- emit(buffer.slice(0, emitLength));
112
- buffer = buffer.slice(emitLength);
113
- return output;
114
- }
115
- return output;
116
- };
117
- return {
118
- push(chunk) {
119
- buffer += chunk;
120
- return consume(false);
121
- },
122
- flush() {
123
- return consume(true);
124
- }
125
- };
126
- }
127
- //#endregion
128
- //#region packages/ai/src/transports/model-max-tokens-params.ts
129
- /**
130
- * Max-token parameter normalization across provider/native naming variants.
131
- * Callers canonicalize aliases before dispatch so payloads cannot carry
132
- * conflicting limits.
133
- */
134
- const MAX_TOKENS_PARAM_KEYS = [
135
- "maxTokens",
136
- "max_completion_tokens",
137
- "max_tokens"
138
- ];
139
- /** Resolve the first supported max-token parameter present in a params object. */
140
- function resolveMaxTokensParam(params) {
141
- if (!params) return;
142
- for (const key of MAX_TOKENS_PARAM_KEYS) {
143
- const resolved = asNonNegativeFiniteNumber(params[key]);
144
- if (resolved !== void 0) return resolved;
145
- }
146
- }
147
- /**
148
- * Canonicalize merged params to `maxTokens`, preserving source precedence from
149
- * left to right across the provided source objects.
150
- */
151
- function canonicalizeMaxTokensParam(params) {
152
- let resolved;
153
- for (const source of params.sources) {
154
- const sourceValue = resolveMaxTokensParam(source);
155
- if (sourceValue !== void 0) resolved = sourceValue;
156
- }
157
- if (resolved === void 0) return;
158
- for (const key of MAX_TOKENS_PARAM_KEYS) delete params.merged[key];
159
- params.merged.maxTokens = resolved;
160
- }
161
- //#endregion
162
- //#region packages/ai/src/transports/openai-compatible-conversation-turn.ts
163
- function hasNonEmptyContentPart(part) {
164
- if (!part || typeof part !== "object") return false;
165
- const record = part;
166
- if (record.type === "text") return hasNonEmptyString(record.text);
167
- return true;
168
- }
169
- function hasNonEmptyMessageContent(content) {
170
- if (hasNonEmptyString(content)) return true;
171
- if (!Array.isArray(content)) return false;
172
- return content.some(hasNonEmptyContentPart);
173
- }
174
- /** Returns whether an OpenAI-compatible messages payload contains a usable turn. */
175
- function hasOpenAICompatibleConversationTurn(messages) {
176
- if (!Array.isArray(messages)) return false;
177
- return messages.some((message) => {
178
- if (!message || typeof message !== "object") return false;
179
- const record = message;
180
- if (record.role === "user") return hasNonEmptyMessageContent(record.content);
181
- if (record.role === "assistant") {
182
- if (hasNonEmptyMessageContent(record.content)) return true;
183
- const toolCalls = record.tool_calls;
184
- return Array.isArray(toolCalls) && toolCalls.some((toolCall) => toolCall && typeof toolCall === "object");
185
- }
186
- return false;
187
- });
188
- }
189
- //#endregion
190
- //#region packages/ai/src/transports/openai-completions-cache-control.ts
191
- const shapedPayloads = /* @__PURE__ */ new WeakSet();
192
- function resolveCompletionsCacheControl(compat, retention, openRouterRoute) {
193
- if (compat.cacheControlFormat !== "anthropic" || retention === "none") return;
194
- return {
195
- type: "ephemeral",
196
- ...retention === "long" && compat.supportsLongCacheRetention && (openRouterRoute || compat.configuredSupportsLongCacheRetention === true) ? { ttl: "1h" } : {}
197
- };
198
- }
199
- /** Shared Chat Completions policy; repeated wrapper application preserves existing checkpoints. */
200
- function applyCompletionsAnthropicCacheControl(payload, cacheControl = { type: "ephemeral" }, cacheOptOutIndexes = /* @__PURE__ */ new Set(), markTools = true, markMessages = true) {
201
- if (shapedPayloads.has(payload)) return;
202
- shapedPayloads.add(payload);
203
- const messages = Array.isArray(payload.messages) ? payload.messages : [];
204
- const tools = Array.isArray(payload.tools) ? payload.tools.filter(isRecord) : [];
205
- const blocks = messages.filter(isRecord).flatMap((message) => Array.isArray(message.content) ? message.content.filter(isRecord) : []);
206
- for (const block of [...tools, ...blocks]) delete block.cache_control;
207
- if (!cacheControl) return;
208
- const markText = (message, splitBoundary) => {
209
- if (typeof message.content === "string" && message.content) message.content = [{
210
- type: "text",
211
- text: message.content
212
- }];
213
- const content = message.content;
214
- if (!Array.isArray(content)) return false;
215
- for (let i = content.length - 1; i >= 0; i--) {
216
- const block = content[i];
217
- if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string" || !block.text) continue;
218
- const split = splitBoundary ? splitSystemPromptCacheBoundary(block.text) : void 0;
219
- if (split) {
220
- content.splice(i, 1, ...split.stablePrefix ? [{
221
- type: "text",
222
- text: split.stablePrefix,
223
- cache_control: cacheControl
224
- }] : [], ...split.dynamicSuffix ? [{
225
- type: "text",
226
- text: split.dynamicSuffix
227
- }] : []);
228
- if (!split.stablePrefix) return false;
229
- } else block.cache_control = cacheControl;
230
- return true;
231
- }
232
- return false;
233
- };
234
- const lastTool = tools.at(-1);
235
- if (markTools && lastTool) lastTool.cache_control = cacheControl;
236
- if (!markMessages) return;
237
- const system = messages.find((message) => isRecord(message) && (message.role === "system" || message.role === "developer"));
238
- if (isRecord(system)) markText(system, true);
239
- for (let i = messages.length - 1; i >= 0; i--) {
240
- const message = messages[i];
241
- if (!cacheOptOutIndexes.has(i) && isRecord(message) && (message.role === "user" || message.role === "tool") && markText(message, false)) return;
242
- }
243
- }
244
- //#endregion
245
- //#region packages/ai/src/transports/openai-completions-string-content.ts
246
- /**
247
- * OpenAI Chat Completions compatibility helpers. Some providers only accept
248
- * role/content messages with plain string content instead of text block arrays.
249
- */
250
- function flattenStringOnlyCompletionContent(content) {
251
- if (!Array.isArray(content)) return content;
252
- const textParts = [];
253
- for (const item of content) {
254
- if (!item || typeof item !== "object" || item.type !== "text" || typeof item.text !== "string") return content;
255
- textParts.push(item.text);
256
- }
257
- return textParts.join("\n");
258
- }
259
- /** Flatten string-only text block content arrays into newline-joined strings. */
260
- function flattenCompletionMessagesToStringContent(messages) {
261
- return messages.map((message) => {
262
- if (!message || typeof message !== "object") return message;
263
- const content = message.content;
264
- const flattenedContent = flattenStringOnlyCompletionContent(content);
265
- if (flattenedContent === content) return message;
266
- return {
267
- ...message,
268
- content: flattenedContent
269
- };
270
- });
271
- }
272
- /** Strip completion messages to role/content fields for strict providers. */
273
- function stripCompletionMessagesToRoleContent(messages) {
274
- return messages.map((message) => {
275
- if (!message || typeof message !== "object" || Array.isArray(message)) return message;
276
- const record = message;
277
- const stripped = {};
278
- if (Object.hasOwn(record, "role")) stripped.role = record.role;
279
- if (Object.hasOwn(record, "content")) stripped.content = record.content;
280
- return stripped;
281
- });
282
- }
283
- //#endregion
284
- //#region packages/ai/src/providers/openai-completions-tool-calls.ts
285
- const MAX_BUFFERED_TOOL_CALL_ARGUMENT_BYTES = 256e3;
286
- const MAX_BUFFERED_LEGACY_FOLLOWING_DELTA_BYTES = 256e3;
287
- const MAX_BUFFERED_LEGACY_FOLLOWING_DELTAS = 1024;
288
- /** Keep encrypted provider reasoning attached to the first matching tool call. */
289
- function createOpenAIEncryptedToolCallReasoningTracker() {
290
- const firstBlocks = /* @__PURE__ */ new Map();
291
- const pendingDetails = /* @__PURE__ */ new Map();
292
- return {
293
- rememberToolCall(id, block, previousId = id) {
294
- const previous = firstBlocks.get(previousId);
295
- if (previousId !== id && previous?.block === block) {
296
- firstBlocks.delete(previousId);
297
- if (previous.signature && block.thoughtSignature === previous.signature) delete block.thoughtSignature;
298
- }
299
- if (!id || firstBlocks.has(id)) return;
300
- const pendingDetail = pendingDetails.get(id);
301
- firstBlocks.set(id, {
302
- block,
303
- signature: pendingDetail
304
- });
305
- if (pendingDetail) {
306
- block.thoughtSignature = pendingDetail;
307
- pendingDetails.delete(id);
308
- }
309
- },
310
- consumeDetails(details) {
311
- if (!Array.isArray(details)) return;
312
- for (const detail of details) {
313
- if (!isRecord(detail) || detail.type !== "reasoning.encrypted" || typeof detail.id !== "string" || detail.id.length === 0 || typeof detail.data !== "string" || detail.data.length === 0) continue;
314
- const serializedDetail = JSON.stringify(detail);
315
- const matchingBlock = firstBlocks.get(detail.id);
316
- if (matchingBlock) {
317
- matchingBlock.signature = serializedDetail;
318
- matchingBlock.block.thoughtSignature = serializedDetail;
319
- } else pendingDetails.set(detail.id, serializedDetail);
320
- }
321
- }
322
- };
323
- }
324
- /** Normalize the SDK's legacy single-function lane into its modern tool-call shape. */
325
- function createOpenAICompletionsToolCallDeltaNormalizer() {
326
- let sawModernToolCall = false;
327
- let pendingLegacyArgumentBytes = 0;
328
- let pendingFollowingDeltaBytes = 0;
329
- let pendingLegacyToolCall;
330
- const modernArguments = /* @__PURE__ */ new Map();
331
- const pendingFollowingDeltas = [];
332
- const takePendingFollowingDeltas = () => {
333
- pendingFollowingDeltaBytes = 0;
334
- return pendingFollowingDeltas.splice(0).map((delta) => ({
335
- delta,
336
- toolCalls: []
337
- }));
338
- };
339
- const bufferFollowingDelta = (delta) => {
340
- const nextDeltaBytes = Buffer.byteLength(JSON.stringify(delta), "utf8");
341
- if (pendingFollowingDeltaBytes + nextDeltaBytes > MAX_BUFFERED_LEGACY_FOLLOWING_DELTA_BYTES || pendingFollowingDeltas.length >= MAX_BUFFERED_LEGACY_FOLLOWING_DELTAS) throw new Error("Exceeded legacy tool-call content buffer limit");
342
- pendingFollowingDeltaBytes += nextDeltaBytes;
343
- pendingFollowingDeltas.push(delta);
344
- };
345
- const hasObservableContent = (value) => {
346
- if (typeof value === "string") return value.length > 0;
347
- if (Array.isArray(value)) return value.some(hasObservableContent);
348
- if (isRecord(value)) return Object.entries(value).some(([field, nestedValue]) => field !== "type" && field !== "id" && field !== "index" && hasObservableContent(nestedValue));
349
- return false;
350
- };
351
- return (delta, finishReason) => {
352
- const ordinaryDelta = { ...delta };
353
- delete ordinaryDelta.function_call;
354
- delete ordinaryDelta.tool_calls;
355
- if (delta.tool_calls && delta.tool_calls.length > 0) {
356
- for (const toolCall of delta.tool_calls) {
357
- const index = typeof toolCall.index === "number" ? toolCall.index : void 0;
358
- const state = (index === void 0 ? void 0 : modernArguments.get(index)) ?? (toolCall.id ? modernArguments.get(toolCall.id) : void 0) ?? {
359
- bytes: 0,
360
- pendingHighSurrogate: false
361
- };
362
- if (index !== void 0) modernArguments.set(index, state);
363
- if (toolCall.id) modernArguments.set(toolCall.id, state);
364
- const argumentDelta = toolCall.function?.arguments ?? "";
365
- const append = measureUtf8AppendBytes(state.pendingHighSurrogate, argumentDelta);
366
- state.bytes += append.bytes;
367
- if (state.bytes > MAX_BUFFERED_TOOL_CALL_ARGUMENT_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
368
- state.pendingHighSurrogate = append.endsWithHighSurrogate;
369
- }
370
- const precedingDeltas = takePendingFollowingDeltas();
371
- sawModernToolCall = true;
372
- pendingLegacyArgumentBytes = 0;
373
- pendingLegacyToolCall = void 0;
374
- return [...precedingDeltas, {
375
- delta: ordinaryDelta,
376
- toolCalls: delta.tool_calls
377
- }];
378
- }
379
- const functionCall = delta.function_call;
380
- if (sawModernToolCall || !functionCall && !pendingLegacyToolCall) return [{
381
- delta: ordinaryDelta,
382
- toolCalls: []
383
- }];
384
- const hadPendingLegacyCall = pendingLegacyToolCall !== void 0;
385
- const leadingDeltas = [];
386
- if (Object.entries(ordinaryDelta).some(([field, value]) => field !== "role" && hasObservableContent(value))) {
387
- if (hadPendingLegacyCall) bufferFollowingDelta(ordinaryDelta);
388
- else leadingDeltas.push({
389
- delta: ordinaryDelta,
390
- toolCalls: []
391
- });
392
- }
393
- if (functionCall && (functionCall.name || functionCall.arguments)) {
394
- const nextFunctionName = pendingLegacyToolCall?.function?.name ? void 0 : functionCall.name;
395
- const nextArgumentBytes = Buffer.byteLength(functionCall.arguments ?? "", "utf8") + Buffer.byteLength(nextFunctionName ?? "", "utf8");
396
- if (pendingLegacyArgumentBytes + nextArgumentBytes > MAX_BUFFERED_TOOL_CALL_ARGUMENT_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
397
- pendingLegacyArgumentBytes += nextArgumentBytes;
398
- pendingLegacyToolCall ??= {
399
- index: 0,
400
- id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
401
- type: "function",
402
- function: {}
403
- };
404
- const pendingFunction = pendingLegacyToolCall.function ??= {};
405
- if (nextFunctionName) pendingFunction.name = nextFunctionName;
406
- if (functionCall.arguments) pendingFunction.arguments = (pendingFunction.arguments ?? "") + functionCall.arguments;
407
- }
408
- if (!pendingLegacyToolCall) return leadingDeltas.length > 0 ? leadingDeltas : [{
409
- delta: ordinaryDelta,
410
- toolCalls: []
411
- }];
412
- if (finishReason === "function_call") {
413
- const confirmedLegacyToolCall = pendingLegacyToolCall;
414
- pendingLegacyToolCall = void 0;
415
- pendingLegacyArgumentBytes = 0;
416
- return [
417
- ...leadingDeltas,
418
- {
419
- delta: {},
420
- toolCalls: [confirmedLegacyToolCall]
421
- },
422
- ...takePendingFollowingDeltas()
423
- ];
424
- }
425
- if (finishReason) {
426
- pendingLegacyArgumentBytes = 0;
427
- pendingLegacyToolCall = void 0;
428
- return [...leadingDeltas, ...takePendingFollowingDeltas()];
429
- }
430
- return leadingDeltas;
431
- };
432
- }
433
- /** Publish only executable calls; streaming scratch state never belongs in replay. */
434
- function finalizeOpenAICompletionsToolCalls(output, options = {}) {
435
- const isToolCall = (block) => block.type === "toolCall";
436
- const hasToolCalls = output.content.some(isToolCall);
437
- if (output.stopReason === "toolUse" && !hasToolCalls) output.stopReason = "stop";
438
- if (output.stopReason === "stop" && hasToolCalls && options.allowSilentToolCallPromotion !== false && !output.content.some((block) => {
439
- const candidate = block;
440
- return candidate.type === "text" && typeof candidate.text === "string" && candidate.text.trim().length > 0;
441
- })) output.stopReason = "toolUse";
442
- if (output.stopReason !== "toolUse") {
443
- if (hasToolCalls) output.content = output.content.filter((block) => !isToolCall(block));
444
- return;
445
- }
446
- const toolCalls = output.content.filter(isToolCall);
447
- const rejectToolCalls = () => {
448
- output.stopReason = "error";
449
- output.errorMessage = "Provider returned an incomplete or malformed tool call";
450
- output.content = output.content.filter((candidate) => !isToolCall(candidate));
451
- };
452
- if (toolCalls.some((call) => typeof call.name !== "string" || call.name.trim().length === 0 || typeof call.partialArgs !== "string" || call.partialArgs.trim().length === 0)) {
453
- rejectToolCalls();
454
- return;
455
- }
456
- try {
457
- finalizeTerminalToolCallArguments(toolCalls, (call) => call.partialArgs);
458
- } catch {
459
- rejectToolCalls();
460
- return;
461
- }
462
- for (let contentIndex = 0; contentIndex < output.content.length; contentIndex += 1) {
463
- const block = output.content[contentIndex];
464
- if (!block || !isToolCall(block)) continue;
465
- delete block.partialArgs;
466
- delete block.streamIndex;
467
- options.onConfirmedToolCall?.(block, contentIndex);
468
- }
469
- }
470
- //#endregion
471
- //#region packages/ai/src/transports/openai-completions-host.ts
472
- /**
473
- * Chat Completions accepts Azure AI Foundry hosts in addition to traditional
474
- * Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
475
- * which intentionally excludes the .services.ai.azure.com Foundry suffix.
476
- */
477
- function isAzureOpenAICompatibleHost(hostname) {
478
- return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
479
- }
480
- //#endregion
481
- //#region packages/ai/src/openai-completions-messages.ts
482
- /** Whether replayed messages require a tools marker for proxy compatibility. */
483
- function hasToolCallHistory(messages) {
484
- return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
485
- }
486
- /** Convert a normalized transcript to OpenAI Chat Completions messages. */
487
- function convertMessages(model, context, compat, options = {}) {
488
- const params = [];
489
- const normalizeToolCallId = (id) => {
490
- if (id.includes("|")) return id.slice(0, id.indexOf("|")).replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
491
- if (model.provider === "openai") return id.length > 40 ? truncateUtf16Safe(id, 40) : id;
492
- return id;
493
- };
494
- const transformedMessages = transformProviderMessages(context.messages, model, normalizeToolCallId);
495
- let relocatableSplit;
496
- let systemParamIndex;
497
- if (context.systemPrompt) {
498
- const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
499
- let systemPrompt;
500
- if (options.preserveSystemPromptCacheBoundary) systemPrompt = stripSystemPromptRelocatableBoundary(context.systemPrompt);
501
- else {
502
- const split = splitSystemPromptRelocatableBoundary(context.systemPrompt);
503
- if (split && split.relocatable.length > 0) relocatableSplit = split;
504
- systemPrompt = stripSystemPromptCacheBoundary(context.systemPrompt);
505
- }
506
- systemParamIndex = params.length;
507
- params.push({
508
- role,
509
- content: sanitizeSurrogates(systemPrompt)
510
- });
511
- }
512
- let lastRole = null;
513
- for (let i = 0; i < transformedMessages.length; i++) {
514
- const msg = transformedMessages[i];
515
- if (!msg) continue;
516
- if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && msg.role === "user") params.push({
517
- role: "assistant",
518
- content: "I have processed the tool results."
519
- });
520
- if (isRuntimeContextMessage(msg)) {
521
- params.push({
522
- role: model.reasoning && compat.supportsDeveloperRole ? "developer" : "system",
523
- content: sanitizeSurrogates(runtimeContextContentToText(msg.content))
524
- });
525
- options.cacheOptOutIndexes?.add(params.length - 1);
526
- } else if (msg.role === "user") {
527
- let userParam;
528
- if (typeof msg.content === "string") userParam = {
529
- role: "user",
530
- content: sanitizeSurrogates(msg.content)
531
- };
532
- else {
533
- const content = msg.content.map((item) => {
534
- if (item.type === "text") return {
535
- type: "text",
536
- text: sanitizeSurrogates(item.text)
537
- };
538
- if (item.type === "video") return {
539
- type: "video_url",
540
- video_url: { url: `data:${item.mimeType};base64,${item.data}` }
541
- };
542
- return {
543
- type: "image_url",
544
- image_url: { url: `data:${item.mimeType};base64,${item.data}` }
545
- };
546
- });
547
- if (content.length === 0) continue;
548
- userParam = {
549
- role: "user",
550
- content
551
- };
552
- }
553
- if (hasRuntimeContextMarker(msg)) options.cacheOptOutIndexes?.add(params.length);
554
- params.push(userParam);
555
- } else if (msg.role === "assistant") {
556
- const assistantMsg = {
557
- role: "assistant",
558
- content: compat.requiresAssistantAfterToolResult ? "" : null
559
- };
560
- const assistantTexts = [];
561
- const nonEmptyThinkingBlocks = [];
562
- const toolCalls = [];
563
- msg.content.forEach((block) => {
564
- if (block.type === "text" && block.text.trim().length > 0) assistantTexts.push(sanitizeSurrogates(block.text));
565
- else if (block.type === "thinking" && block.thinking.trim().length > 0) nonEmptyThinkingBlocks.push(block);
566
- else if (block.type === "toolCall") toolCalls.push(block);
567
- });
568
- if (nonEmptyThinkingBlocks.length > 0 && compat.requiresThinkingAsText) assistantMsg.content = [{
569
- type: "text",
570
- text: nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n\n")
571
- }, ...assistantTexts.map((text) => ({
572
- type: "text",
573
- text
574
- }))];
575
- else {
576
- const assistantText = assistantTexts.join("\n");
577
- if (assistantText.length > 0) assistantMsg.content = assistantText;
578
- if (nonEmptyThinkingBlocks.length > 0) {
579
- let signature = nonEmptyThinkingBlocks.at(0)?.thinkingSignature;
580
- if (model.provider === "opencode-go" && signature === "reasoning") signature = "reasoning_content";
581
- if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => block.thinking).join("\n");
582
- }
583
- }
584
- if (toolCalls.length > 0) {
585
- assistantMsg.tool_calls = toolCalls.map((toolCall) => ({
586
- id: toolCall.id,
587
- type: "function",
588
- function: {
589
- name: toolCall.name,
590
- arguments: JSON.stringify(toolCall.arguments)
591
- }
592
- }));
593
- const reasoningDetails = toolCalls.flatMap((toolCall) => {
594
- const signature = toolCall.thoughtSignature;
595
- if (!signature) return [];
596
- try {
597
- const parsed = JSON.parse(signature);
598
- return parsed ? [parsed] : [];
599
- } catch {
600
- return [];
601
- }
602
- });
603
- if (reasoningDetails.length > 0) assistantMsg.reasoning_details = reasoningDetails;
604
- }
605
- if (compat.requiresReasoningContentOnAssistantMessages && model.reasoning && assistantMsg.reasoning_content === void 0) assistantMsg.reasoning_content = "";
606
- const content = assistantMsg.content;
607
- if (!(content !== null && content !== void 0 && content.length > 0) && !assistantMsg.tool_calls) continue;
608
- params.push(assistantMsg);
609
- } else if (msg.role === "toolResult") {
610
- const imageContentParts = [];
611
- let j = i;
612
- while (j < transformedMessages.length) {
613
- const toolMsg = transformedMessages.at(j);
614
- if (toolMsg?.role !== "toolResult") break;
615
- const textResult = extractToolResultText(toolMsg.content);
616
- const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
617
- const images = toolMsg.content.filter(isImageWithMediaPayload);
618
- const toolResultMsg = {
619
- role: "tool",
620
- content: sanitizeNonEmptyTransportPayloadText(textResult, mediaPlaceholder),
621
- tool_call_id: toolMsg.toolCallId
622
- };
623
- if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
624
- params.push(toolResultMsg);
625
- if (images.length > 0 && model.input.includes("image")) {
626
- const boundedToolName = sanitizeSurrogates(truncateUtf16Safe(toolMsg.toolName ?? "", 64));
627
- imageContentParts.push({
628
- type: "text",
629
- text: `Image(s) from tool result #${j - i + 1}${boundedToolName ? ` (${boundedToolName})` : ""}:`
630
- });
631
- for (const block of images) imageContentParts.push({
632
- type: "image_url",
633
- image_url: { url: `data:${block.mimeType};base64,${block.data}` }
634
- });
635
- }
636
- j += 1;
637
- }
638
- i = j - 1;
639
- if (imageContentParts.length > 0) {
640
- if (compat.requiresAssistantAfterToolResult) params.push({
641
- role: "assistant",
642
- content: "I have processed the tool results."
643
- });
644
- params.push({
645
- role: "user",
646
- content: imageContentParts
647
- });
648
- lastRole = "user";
649
- } else lastRole = "toolResult";
650
- continue;
651
- }
652
- lastRole = msg.role;
653
- }
654
- if (relocatableSplit !== void 0 && systemParamIndex !== void 0) relocateNonBehavioralRegion({
655
- params,
656
- systemParamIndex,
657
- split: relocatableSplit,
658
- cacheOptOutIndexes: options.cacheOptOutIndexes
659
- });
660
- return params;
661
- }
662
- /** Commit relocation only after finding an emitted carrier. */
663
- function relocateNonBehavioralRegion(args) {
664
- const text = sanitizeSurrogates(stripSystemPromptCacheBoundary(args.split.relocatable));
665
- for (let index = args.systemParamIndex + 1; index < args.params.length; index++) {
666
- const param = args.params[index];
667
- if (!param || param.role !== "user") continue;
668
- if (typeof param.content === "string") param.content = `${param.content}\n\n${text}`;
669
- else if (Array.isArray(param.content)) param.content = [...param.content, {
670
- type: "text",
671
- text
672
- }];
673
- else continue;
674
- const systemParam = args.params[args.systemParamIndex];
675
- if (systemParam) systemParam.content = sanitizeSurrogates(stripSystemPromptCacheBoundary(args.split.remainingPrompt));
676
- args.cacheOptOutIndexes?.add(index);
677
- return;
678
- }
679
- }
680
- //#endregion
681
- //#region packages/ai/src/transports/openai-completions-direct-policy.ts
682
- function applyDirectCompletionsReasoningAndRouting(params, model, reasoning, compat) {
683
- const nativeEffort = compat.thinkingFormat === "openrouter" ? void 0 : reasoning.effort;
684
- const reasoningEnabled = reasoning.thinkingEnabled ?? false;
685
- if (compat.thinkingFormat === "zai" && model.reasoning) params.thinking = reasoningEnabled ? {
686
- type: "enabled",
687
- clear_thinking: false
688
- } : { type: "disabled" };
689
- else if (compat.thinkingFormat === "qwen" && model.reasoning) params.enable_thinking = reasoningEnabled;
690
- else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = {
691
- enable_thinking: reasoningEnabled,
692
- preserve_thinking: true
693
- };
694
- else if ((compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "together") && model.reasoning) {
695
- if (compat.thinkingFormat === "deepseek") params.thinking = { type: reasoningEnabled ? "enabled" : "disabled" };
696
- else params.reasoning = { enabled: reasoningEnabled };
697
- if (reasoningEnabled && compat.supportsReasoningEffort && nativeEffort !== void 0) params.reasoning_effort = nativeEffort;
698
- } else if (model.reasoning && compat.supportsReasoningEffort && nativeEffort !== void 0) params.reasoning_effort = nativeEffort;
699
- if (compat.openRouterRouting) params.provider = compat.openRouterRouting;
700
- if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
701
- const routing = model.compat.vercelGatewayRouting;
702
- if (routing.only || routing.order) {
703
- const gatewayOptions = {};
704
- if (routing.only) gatewayOptions.only = routing.only;
705
- if (routing.order) gatewayOptions.order = routing.order;
706
- params.providerOptions = { gateway: gatewayOptions };
707
- }
708
- }
709
- }
710
- //#endregion
711
- //#region packages/ai/src/transports/openai-completions-replay.ts
712
- function isGoogleOpenAICompatModel(model) {
713
- const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
714
- return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
715
- }
716
- const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
717
- const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
718
- function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
719
- return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
720
- }
721
- function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
722
- if (!isGoogleOpenAICompatModel(model)) return;
723
- const sigById = /* @__PURE__ */ new Map();
724
- const fallbackSig = isGoogleGemini3ThinkingLevelModel(model.id) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
725
- for (const msg of context.messages ?? []) {
726
- if (msg.role !== "assistant") continue;
727
- if (!Array.isArray(msg.content)) continue;
728
- for (const block of msg.content) {
729
- if (block.type !== "toolCall") continue;
730
- const id = block.id;
731
- const sig = block.thoughtSignature;
732
- if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
733
- const isSameRoute = msg.api === model.api && msg.provider === model.provider && msg.model === model.id;
734
- if (!isSameRoute && !fallbackSig) continue;
735
- sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
736
- }
737
- }
738
- }
739
- if (sigById.size === 0 && !fallbackSig) return;
740
- for (const message of outgoingMessages) {
741
- const toolCalls = message.tool_calls;
742
- if (!Array.isArray(toolCalls)) continue;
743
- for (const toolCall of toolCalls) {
744
- const id = toolCall.id;
745
- if (typeof id !== "string") continue;
746
- let sig = sigById.get(id) ?? fallbackSig;
747
- if (typeof sig === "string" && sig.length > 0) {
748
- if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
749
- }
750
- if (typeof sig !== "string" || sig.length === 0) continue;
751
- const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
752
- toolCall.extra_content = extra;
753
- const google = extra.google && typeof extra.google === "object" ? extra.google : {};
754
- extra.google = google;
755
- google.thought_signature = sig;
756
- }
757
- }
758
- }
759
- const COMPLETIONS_REASONING_REPLAY_FIELDS = [
760
- "reasoning_details",
761
- "reasoning_content",
762
- "reasoning",
763
- "reasoning_text"
764
- ];
765
- function stripCompletionsReasoningReplayFields(record) {
766
- for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) delete record[field];
767
- }
768
- function sanitizeOpenRouterReasoningReplayFields(record) {
769
- const reasoningDetails = record.reasoning_details;
770
- if (typeof reasoningDetails === "string") {
771
- if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
772
- delete record.reasoning_details;
773
- } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
774
- for (const field of ["reasoning", "reasoning_content"]) if (field in record && (typeof record[field] !== "string" || record[field] === "")) delete record[field];
775
- const reasoningText = record.reasoning_text;
776
- if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
777
- delete record.reasoning_text;
778
- }
779
- function sanitizeReasoningContentReplayFields(record) {
780
- if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
781
- delete record.reasoning_details;
782
- delete record.reasoning;
783
- delete record.reasoning_text;
784
- }
785
- const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
786
- "deepseek-v4-flash",
787
- "deepseek-v4-pro",
788
- "kimi-for-coding",
789
- "kimi-k2.5",
790
- "kimi-k2.6",
791
- "kimi-k2.7-code",
792
- "kimi-k2.7-code-highspeed",
793
- "kimi-k3",
794
- "kimi-k2-thinking",
795
- "kimi-k2-thinking-turbo",
796
- "mimo-v2-pro",
797
- "mimo-v2-omni",
798
- "mimo-v2.5",
799
- "mimo-v2.5-pro",
800
- "mimo-v2.6-flash",
801
- "mimo-v2.6-pro",
802
- "mimo-v2.6-pro-ultraspeed"
803
- ]);
804
- const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
805
- "-free",
806
- "-paid",
807
- "-trial"
808
- ];
809
- function stripReasoningContentReplayTierSuffix(modelId) {
810
- for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
811
- return modelId;
812
- }
813
- function getReasoningContentReplayModelIdCandidates(modelId) {
814
- if (typeof modelId !== "string") return [];
815
- const normalized = modelId.trim().toLowerCase();
816
- if (!normalized) return [];
817
- const parts = normalized.split("/").filter(Boolean);
818
- const finalPart = parts[parts.length - 1] ?? normalized;
819
- const candidates = [finalPart];
820
- const colonParts = finalPart.split(":").filter(Boolean);
821
- if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
822
- for (const candidate of candidates.slice()) {
823
- const stripped = stripReasoningContentReplayTierSuffix(candidate);
824
- if (stripped !== candidate) candidates.push(stripped);
825
- }
826
- return uniqueStrings(candidates.filter(Boolean));
827
- }
828
- function shouldPreserveReasoningContentReplay(model, compat) {
829
- if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
830
- return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
831
- }
832
- function shouldPreserveOpenRouterReasoningReplay(model) {
833
- if (model.provider !== "openrouter") return true;
834
- const normalizedModelId = model.id.trim().toLowerCase();
835
- return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
836
- }
837
- function shouldTrustReasoningContentReplayMetadata(model) {
838
- if (!model.reasoning) return false;
839
- if (model.provider.trim().toLowerCase() === "openai") return false;
840
- return shouldPreserveOpenRouterReasoningReplay(model);
841
- }
842
- function sanitizeCompletionsReasoningReplayFields(messages, options) {
843
- if (!Array.isArray(messages)) return;
844
- for (const msg of messages) {
845
- if (!msg || typeof msg !== "object") continue;
846
- const record = msg;
847
- if (record.role !== "assistant") continue;
848
- if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
849
- else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
850
- else stripCompletionsReasoningReplayFields(record);
851
- }
852
- }
853
- function applyCompletionsReplay(outgoingMessages, context, model, compat) {
854
- injectToolCallThoughtSignatures(outgoingMessages, context, model);
855
- sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
856
- preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
857
- preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
858
- });
859
- }
860
- //#endregion
861
- //#region packages/ai/src/transports/openai-completions-params.ts
862
- function isKnownOpenAICompletionsEndpoint(model) {
863
- if (!model.baseUrl.trim() || isNativeOpenAIEndpoint(model)) return true;
864
- const endpointClass = resolveProviderEndpoint(model).endpointClass;
865
- if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
866
- const endpoint = URL.parse(model.baseUrl);
867
- return endpoint !== null && isAzureOpenAICompatibleHost(endpoint.hostname.toLowerCase());
868
- }
869
- function resolveOpenAICompletionsMaxTokens(model, options) {
870
- if (options?.maxTokens) return {
871
- maxTokens: options.maxTokens,
872
- clampToModelMaxTokens: true
873
- };
874
- const paramsMaxTokens = resolveMaxTokensParam(model.params);
875
- if (paramsMaxTokens) return {
876
- maxTokens: paramsMaxTokens,
877
- clampToModelMaxTokens: false
878
- };
879
- return {
880
- maxTokens: model.maxTokens,
881
- clampToModelMaxTokens: false
882
- };
883
- }
884
- function resolveOpenAICompletionsModelMaxTokens(model) {
885
- return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
886
- }
887
- const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
888
- const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
889
- const MIN_USEFUL_OUTPUT_TOKENS = 16;
890
- function estimateJsonChars(value, fallback) {
891
- try {
892
- return estimateStringChars(JSON.stringify(value));
893
- } catch {
894
- return fallback;
895
- }
896
- }
897
- function estimateOpenAICompletionsInputTokens(payload) {
898
- let adjustedChars = 0;
899
- adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
900
- if (Array.isArray(payload.tools) && payload.tools.length > 0) adjustedChars += estimateJsonChars(payload.tools, 1024);
901
- if (payload.response_format !== void 0) adjustedChars += estimateJsonChars(payload.response_format, 256);
902
- return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
903
- }
904
- function estimateOpenAICompletionsMessagesChars(messages) {
905
- if (!Array.isArray(messages)) return 0;
906
- let adjustedChars = 0;
907
- for (const message of messages) {
908
- if (!message || typeof message !== "object") continue;
909
- const record = message;
910
- adjustedChars += estimateOpenAICompletionsContentChars(record.content);
911
- for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
912
- if (record.tool_calls !== void 0) adjustedChars += estimateJsonChars(record.tool_calls, 256);
913
- }
914
- return adjustedChars;
915
- }
916
- function estimateOpenAICompletionsContentChars(value) {
917
- if (typeof value === "string") return estimateStringChars(value);
918
- if (!Array.isArray(value)) return 0;
919
- let adjustedChars = 0;
920
- for (const block of value) {
921
- if (!block || typeof block !== "object") continue;
922
- const record = block;
923
- if (record.type === "image_url" || record.type === "input_image") {
924
- adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
925
- continue;
926
- }
927
- const text = record.text;
928
- if (typeof text === "string") {
929
- adjustedChars += estimateStringChars(text);
930
- continue;
931
- }
932
- adjustedChars += estimateJsonChars(block, 256);
933
- }
934
- return adjustedChars;
935
- }
936
- function resolveOpenAICompletionsEffectiveContextTokens(model) {
937
- const contextTokens = model.contextTokens;
938
- if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
939
- return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
940
- }
941
- function setQwenChatTemplateThinking(params, enabled) {
942
- const existing = params.chat_template_kwargs;
943
- params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
944
- ...existing,
945
- enable_thinking: enabled
946
- } : { enable_thinking: enabled };
947
- }
948
- /** Return whether the binary control replaces scalar reasoning effort. */
949
- function applyBinaryCompletionsThinkingParams(params) {
950
- if (!params.modelReasoning) return false;
951
- const enabled = params.thinkingEnabled;
952
- switch (params.compatThinkingFormat) {
953
- case "qwen-chat-template":
954
- setQwenChatTemplateThinking(params.payload, enabled);
955
- return true;
956
- case "qwen":
957
- params.payload.enable_thinking = enabled;
958
- return true;
959
- case "together":
960
- params.payload.reasoning = { enabled };
961
- return !enabled;
962
- default: return false;
963
- }
964
- }
965
- function convertTools(tools, compat, model, mode) {
966
- const prepared = mode === "managed" ? prepareOpenAITools(tools) : void 0;
967
- const projection = prepared?.projection ?? projectOpenAITools(tools);
968
- const convert = () => {
969
- const strict = mode === "direct" ? compat.supportsStrictMode ? false : void 0 : resolveOpenAIStrictToolFlagWithDiagnostics(projection, getAiTransportHost().resolveOpenAIStrictToolSetting(model, {
970
- transport: "stream",
971
- supportsStrictMode: compat?.supportsStrictMode
972
- }), {
973
- transport: "completions",
974
- model
975
- });
976
- return {
977
- projection,
978
- tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
979
- const functionTool = {
980
- name: tool.name,
981
- description: tool.description,
982
- parameters: mode === "direct" ? tool.parameters : normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
983
- };
984
- if (strict !== void 0) functionTool.strict = strict;
985
- return {
986
- type: "function",
987
- function: functionTool
988
- };
989
- })
990
- };
991
- };
992
- return prepared ? withPreparedToolSchemaNormalization(prepared.schemas, convert) : convert();
993
- }
994
- function buildOpenAICompletionsParams(model, context, options) {
995
- return buildOpenAICompletionsRequest(model, context, options, { mode: "managed" });
996
- }
997
- function buildOpenAICompletionsRequest(model, context, options, policy) {
998
- const resolvedPolicy = policy.mode === "direct" ? policy : {
999
- ...policy,
1000
- compat: getCompat(model)
1001
- };
1002
- const compat = resolvedPolicy.compat;
1003
- const managedCompat = resolvedPolicy.mode === "managed" ? resolvedPolicy.compat : void 0;
1004
- const endpointDetection = detectOpenAICompletionsCompat(model);
1005
- const compatDetection = policy.mode === "managed" ? endpointDetection : void 0;
1006
- const { endpointClass } = endpointDetection.capabilities;
1007
- const cacheRetention = policy.mode === "direct" ? policy.cacheRetention : resolveCacheRetention(options?.cacheRetention);
1008
- const cacheControl = resolveCompletionsCacheControl(compat, cacheRetention, endpointClass === "openrouter" || endpointClass === "default" && model.provider === "openrouter");
1009
- const markTools = endpointClass !== "modelstudio-native" && !(endpointClass === "default" && [
1010
- "modelstudio",
1011
- "dashscope",
1012
- "qwen"
1013
- ].includes(model.provider));
1014
- const cacheOptOutIndexes = /* @__PURE__ */ new Set();
1015
- let messages = convertMessages(model, context, compat, {
1016
- cacheOptOutIndexes,
1017
- preserveSystemPromptCacheBoundary: cacheControl !== void 0 && !managedCompat?.requiresStringContent
1018
- });
1019
- if (managedCompat) {
1020
- applyCompletionsReplay(messages, context, model, managedCompat);
1021
- if (managedCompat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
1022
- if (managedCompat.requiresStringContent) messages = flattenCompletionMessagesToStringContent(messages);
1023
- }
1024
- const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
1025
- const params = {
1026
- model: model.id,
1027
- messages,
1028
- stream: true,
1029
- ...resolveOpenAIPromptCacheParams(model, cacheRetention, compat)
1030
- };
1031
- if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
1032
- if (compat.supportsStore) params.store = false;
1033
- if (policy.mode === "direct" || compat.supportsPromptCacheKey && promptCacheKey) params.prompt_cache_key = compat.supportsPromptCacheKey ? promptCacheKey : void 0;
1034
- if (options?.temperature !== void 0) params.temperature = options.temperature;
1035
- if (policy.mode === "managed" && options?.topP !== void 0) params.top_p = options.topP;
1036
- const requestedResponseFormat = options?.responseFormat;
1037
- const responseFormat = policy.mode === "direct" && requestedResponseFormat === void 0 ? void 0 : resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
1038
- provider: model.provider,
1039
- baseUrl: model.baseUrl,
1040
- hasTools: () => Boolean(context.tools?.length)
1041
- }) ? void 0 : requestedResponseFormat, compat.supportsJsonSchemaResponseFormat);
1042
- if (responseFormat !== void 0) params.response_format = responseFormat;
1043
- if (policy.mode === "managed" && options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
1044
- if (policy.mode === "managed" && options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
1045
- if (policy.mode === "managed" && options?.seed !== void 0) params.seed = options.seed;
1046
- if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
1047
- let directToolProjection;
1048
- if (policy.mode === "direct" || supportsModelTools(model)) {
1049
- if (context.tools) {
1050
- const converted = convertTools(context.tools, compat, model, policy.mode);
1051
- if (policy.mode === "direct") directToolProjection = converted.projection;
1052
- if (converted.tools.length > 0 || policy.mode === "managed" && converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
1053
- else if (hasToolCallHistory(context.messages)) params.tools = [];
1054
- if (policy.mode === "direct" && compat.zaiToolStream && converted.tools.length > 0) params.tool_stream = true;
1055
- if (policy.mode === "managed" && options?.toolChoice) {
1056
- const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
1057
- if (toolChoice !== void 0) params.tool_choice = toolChoice;
1058
- } else if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
1059
- } else if (hasToolCallHistory(context.messages)) params.tools = [];
1060
- if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
1061
- delete params.tools;
1062
- delete params.tool_choice;
1063
- }
1064
- }
1065
- if (policy.mode === "direct" && compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools);
1066
- if (policy.mode === "direct" && options?.toolChoice) {
1067
- const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, directToolProjection ?? projectOpenAITools([]));
1068
- if (toolChoice !== void 0) params.tool_choice = toolChoice;
1069
- }
1070
- const isOpenRouter = compat.thinkingFormat === "openrouter";
1071
- const usesBinaryOpenRouterThinking = isOpenRouter && (model.compat?.supportsReasoningEffort === false || model.compat?.supportedReasoningEfforts?.length === 0);
1072
- const simpleReasoning = options?.reasoning;
1073
- const requestedEffort = policy.mode === "direct" ? options?.reasoningEffort : options?.reasoningEffort ?? (simpleReasoning === "none" ? "none" : resolveOpenAISimpleReasoningEffort({
1074
- ...model,
1075
- compat: model.compat ?? void 0
1076
- }, simpleReasoning)) ?? (usesBinaryOpenRouterThinking ? void 0 : "high");
1077
- const reasoning = resolveOpenAIRequestReasoning(model, requestedEffort);
1078
- const { effort, thinkingEnabled } = reasoning;
1079
- {
1080
- const maxTokenBudget = policy.mode === "direct" ? {
1081
- maxTokens: options?.maxTokens,
1082
- clampToModelMaxTokens: true
1083
- } : resolveOpenAICompletionsMaxTokens(model, options);
1084
- const effectiveMaxTokens = maxTokenBudget.maxTokens;
1085
- const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
1086
- let clampedMaxTokens = effectiveMaxTokens;
1087
- const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
1088
- if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
1089
- clampedMaxTokens = modelMaxTokens;
1090
- if (policy.mode === "managed") emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
1091
- }
1092
- if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
1093
- const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
1094
- const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
1095
- if (clampedMaxTokens > remainingBudget) {
1096
- clampedMaxTokens = remainingBudget;
1097
- emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
1098
- if (remainingBudget < MIN_USEFUL_OUTPUT_TOKENS) {
1099
- if (model.reasoning && thinkingEnabled !== false) throw Object.assign(/* @__PURE__ */ new Error(`Context window exceeded: estimated input ${estimatedInputTokens} leaves only ${remainingBudget} output tokens within the ${effectiveContextTokens}-token context.`), { code: "context_length_exceeded" });
1100
- log.warn(`[completions] insufficient_output_budget provider=${model.provider} api=${model.api} model=${model.id} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
1101
- }
1102
- }
1103
- }
1104
- if (policy.mode === "direct" ? options?.maxTokens : clampedMaxTokens) {
1105
- if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
1106
- else params.max_completion_tokens = clampedMaxTokens;
1107
- }
1108
- }
1109
- if (isOpenRouter && model.reasoning) {
1110
- if (usesBinaryOpenRouterThinking && thinkingEnabled !== void 0) params.reasoning = { enabled: thinkingEnabled };
1111
- else if (effort !== void 0) params.reasoning = { effort };
1112
- }
1113
- if (policy.mode === "direct") applyDirectCompletionsReasoningAndRouting(params, model, reasoning, compat);
1114
- else {
1115
- const suppressScalarEffort = applyBinaryCompletionsThinkingParams({
1116
- compatThinkingFormat: compat.thinkingFormat,
1117
- modelReasoning: model.reasoning,
1118
- payload: params,
1119
- thinkingEnabled: thinkingEnabled ?? false
1120
- });
1121
- if (!isOpenRouter && effort && model.reasoning && compat.supportsReasoningEffort && !suppressScalarEffort) params.reasoning_effort = effort;
1122
- if (compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools, !managedCompat?.requiresStringContent);
1123
- }
1124
- if (params.tools?.length && isKnownOpenAICompletionsEndpoint(model)) {
1125
- if (isOpenAIGpt56Model(model)) params.reasoning_effort = "none";
1126
- else if (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model)) delete params.reasoning_effort;
1127
- }
1128
- return params;
1129
- }
1130
- //#endregion
1131
- //#region packages/ai/src/transports/openai-completions-dsml.ts
1132
- const DEEPSEEK_DSML_TOOL_KINDS = [
1133
- "tool_calls",
1134
- "tool_call",
1135
- "function_calls"
1136
- ];
1137
- const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_MARKERS.flatMap((marker) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${marker}${kind}>`));
1138
- const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_MARKERS.map((marker) => `<${marker}invoke`);
1139
- const DEEPSEEK_DSML_INVOKE_OPEN_TAG = new RegExp(`^<${DEEPSEEK_DSML_MARKER_PATTERN}invoke\\b[^<>]*>$`);
1140
- const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
1141
- const DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length + 1), ...DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES.map((token) => token.length + 2));
1142
- const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
1143
- const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 65536;
1144
- const DSML_NAME_ATTRIBUTE_RE = /\bname=("([^"]*)"|'([^']*)'|([^\s>]+))/;
1145
- function createDsmlRecoverer() {
1146
- let buffer = "";
1147
- let bufferBytes = 0;
1148
- let bufferEndsWithHighSurrogate = false;
1149
- let pendingScanChars = 0;
1150
- let activeOpenToken = null;
1151
- let blockScanState = { offset: 0 };
1152
- const resetBlockScan = () => {
1153
- activeOpenToken = null;
1154
- pendingScanChars = 0;
1155
- blockScanState = { offset: 0 };
1156
- };
1157
- const consume = (final) => {
1158
- const output = [];
1159
- while (buffer) {
1160
- const open = activeOpenToken ? {
1161
- index: 0,
1162
- token: activeOpenToken
1163
- } : findEarliestDsmlToken(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS);
1164
- if (!open) {
1165
- resetBlockScan();
1166
- if (final) {
1167
- output.push({
1168
- kind: "text",
1169
- text: buffer
1170
- });
1171
- buffer = "";
1172
- bufferBytes = 0;
1173
- bufferEndsWithHighSurrogate = false;
1174
- return output;
1175
- }
1176
- const keep = longestDsmlTokenPrefixSuffixLength(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN);
1177
- const emitLength = buffer.length - keep;
1178
- if (emitLength > 0) {
1179
- const emitted = buffer.slice(0, emitLength);
1180
- output.push({
1181
- kind: "text",
1182
- text: emitted
1183
- });
1184
- bufferBytes -= Buffer.byteLength(emitted, "utf8");
1185
- buffer = buffer.slice(emitted.length);
1186
- if (!buffer) bufferEndsWithHighSurrogate = false;
1187
- }
1188
- return output;
1189
- }
1190
- if (open.index > 0) {
1191
- const prefix = buffer.slice(0, open.index);
1192
- output.push({
1193
- kind: "text",
1194
- text: prefix
1195
- });
1196
- bufferBytes -= Buffer.byteLength(prefix, "utf8");
1197
- buffer = buffer.slice(prefix.length);
1198
- resetBlockScan();
1199
- }
1200
- activeOpenToken = open.token;
1201
- if (blockScanState.offset === 0) blockScanState.offset = open.token.length;
1202
- const blockScan = scanDeepSeekDsmlToolBlock(buffer, open.token.replace("<", "</"), open.token.length, blockScanState);
1203
- if (blockScan.kind === "nested-open") throw new Error("Nested DeepSeek DSML recovery wrappers are not supported");
1204
- const close = blockScan.kind === "close" ? blockScan : null;
1205
- if (!close) {
1206
- if (final) {
1207
- output.push({
1208
- kind: "text",
1209
- text: buffer
1210
- });
1211
- buffer = "";
1212
- bufferBytes = 0;
1213
- bufferEndsWithHighSurrogate = false;
1214
- return output;
1215
- }
1216
- if (bufferBytes > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
1217
- return output;
1218
- }
1219
- resetBlockScan();
1220
- const body = buffer.slice(open.token.length, close.index);
1221
- const blockText = buffer.slice(0, close.index + close.token.length);
1222
- if (Buffer.byteLength(blockText, "utf8") > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
1223
- const recoveredToolCalls = parseDeepSeekDsmlToolCallBlock(body);
1224
- if (recoveredToolCalls.length > 0) output.push(...recoveredToolCalls);
1225
- else output.push({
1226
- kind: "text",
1227
- text: blockText
1228
- });
1229
- bufferBytes -= Buffer.byteLength(blockText, "utf8");
1230
- buffer = buffer.slice(blockText.length);
1231
- if (!buffer) bufferEndsWithHighSurrogate = false;
1232
- }
1233
- return output;
1234
- };
1235
- return {
1236
- push(chunk) {
1237
- const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
1238
- bufferBytes += append.bytes;
1239
- bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
1240
- buffer += chunk;
1241
- pendingScanChars += chunk.length;
1242
- if (activeOpenToken && pendingScanChars < DEEPSEEK_DSML_SCAN_BATCH_CHARS && !chunk.includes("<") && !chunk.includes(">") && bufferBytes <= MAX_DSML_RECOVERY_BUFFER_BYTES) return [];
1243
- pendingScanChars = 0;
1244
- return consume(false);
1245
- },
1246
- flush() {
1247
- return consume(true);
1248
- }
1249
- };
1250
- }
1251
- function parseDeepSeekDsmlToolCallBlock(body) {
1252
- const toolCalls = [];
1253
- const invokeOpenRegex = new RegExp(`<${DEEPSEEK_DSML_MARKER_PATTERN}invoke\\b([^<>]*)>`, "g");
1254
- let openMatch;
1255
- while ((openMatch = invokeOpenRegex.exec(body)) !== null) {
1256
- const invokeBodyStart = openMatch.index + openMatch[0].length;
1257
- const invokeCloseToken = `</${openMatch[1]}invoke>`;
1258
- const invokeCloseIndex = body.indexOf(invokeCloseToken, invokeBodyStart);
1259
- if (invokeCloseIndex === -1) break;
1260
- const invokeBody = body.slice(invokeBodyStart, invokeCloseIndex);
1261
- invokeOpenRegex.lastIndex = invokeCloseIndex + invokeCloseToken.length;
1262
- const invokeName = parseDsmlNameAttribute(openMatch[2] ?? "");
1263
- if (!invokeName) continue;
1264
- const parsedArguments = parseDeepSeekDsmlInvokeArguments(invokeBody);
1265
- if (!parsedArguments) continue;
1266
- toolCalls.push({
1267
- kind: "toolCall",
1268
- name: invokeName,
1269
- arguments: parsedArguments,
1270
- partialArgs: JSON.stringify(parsedArguments)
1271
- });
1272
- }
1273
- return toolCalls;
1274
- }
1275
- function parseDeepSeekDsmlInvokeArguments(body) {
1276
- const args = {};
1277
- const parameterRegex = new RegExp(`<${DEEPSEEK_DSML_MARKER_PATTERN}parameter\\b([^>]*)>([\\s\\S]*?)</\\1parameter>`, "g");
1278
- let parameterMatch;
1279
- while ((parameterMatch = parameterRegex.exec(body)) !== null) {
1280
- const name = parseDsmlNameAttribute(parameterMatch[2] ?? "");
1281
- if (!name) continue;
1282
- const rawValue = parameterMatch[3] ?? "";
1283
- if (rawValue.length === 0) {
1284
- const stringAttribute = Array.from((parameterMatch[2] ?? "").matchAll(/(?:^|\s)([^\s=]+)=(?:"([^"]*)"|'([^']*)'|([^\s"']+))(?=\s|$)/g)).find((attribute) => attribute[1] === "string");
1285
- if ((stringAttribute?.[2] ?? stringAttribute?.[3] ?? stringAttribute?.[4]) !== "true") continue;
1286
- }
1287
- Object.defineProperty(args, name, {
1288
- value: decodeDeepSeekDsmlText(rawValue),
1289
- enumerable: true,
1290
- configurable: true,
1291
- writable: true
1292
- });
1293
- }
1294
- if (Object.keys(args).length > 0) return args;
1295
- const trimmed = body.trim();
1296
- if (!trimmed.startsWith("{")) return null;
1297
- try {
1298
- const parsed = JSON.parse(trimmed);
1299
- if (isRecord(parsed) && Object.keys(parsed).length > 0) return parsed;
1300
- } catch {
1301
- return null;
1302
- }
1303
- return null;
1304
- }
1305
- function parseDsmlNameAttribute(attributes) {
1306
- const match = DSML_NAME_ATTRIBUTE_RE.exec(attributes);
1307
- const value = match?.[2] ?? match?.[3] ?? match?.[4];
1308
- return value ? decodeDeepSeekDsmlText(value) : null;
1309
- }
1310
- function decodeDeepSeekDsmlText(value) {
1311
- return value.replaceAll("&quot;", "\"").replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
1312
- }
1313
- function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
1314
- while (state.offset < text.length) {
1315
- if (state.invoke?.kind === "open") {
1316
- const nextOpen = text.indexOf("<", state.offset);
1317
- const nextClose = text.indexOf(">", state.offset);
1318
- if (nextClose === -1 && nextOpen === -1) {
1319
- state.offset = text.length;
1320
- return { kind: "incomplete" };
1321
- }
1322
- if (nextOpen !== -1 && (nextClose === -1 || nextOpen < nextClose)) {
1323
- state.invoke = void 0;
1324
- state.offset = nextOpen;
1325
- continue;
1326
- }
1327
- const invokeOpenTag = text.slice(state.invoke.start, nextClose + 1);
1328
- const open = DEEPSEEK_DSML_INVOKE_OPEN_TAG.exec(invokeOpenTag);
1329
- if (!open) {
1330
- state.offset = state.invoke.start + 1;
1331
- state.invoke = void 0;
1332
- continue;
1333
- }
1334
- state.invoke = {
1335
- kind: "body",
1336
- closeToken: `</${open[1]}invoke>`
1337
- };
1338
- state.offset = nextClose + 1;
1339
- continue;
1340
- }
1341
- if (state.invoke?.kind === "body") {
1342
- const invokeCloseIndex = text.indexOf(state.invoke.closeToken, state.offset);
1343
- if (invokeCloseIndex === -1) {
1344
- state.offset = Math.max(0, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
1345
- return { kind: "incomplete" };
1346
- }
1347
- state.offset = invokeCloseIndex + state.invoke.closeToken.length;
1348
- state.invoke = void 0;
1349
- continue;
1350
- }
1351
- const toolOpen = findEarliestDsmlToken(text, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, state.offset);
1352
- const toolCloseIndex = text.indexOf(closeToken, state.offset);
1353
- const invokeOpen = findEarliestDsmlToken(text, DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES, state.offset);
1354
- const next = [
1355
- toolOpen ? {
1356
- kind: "nested-open",
1357
- ...toolOpen
1358
- } : null,
1359
- toolCloseIndex === -1 ? null : {
1360
- kind: "close",
1361
- index: toolCloseIndex,
1362
- token: closeToken
1363
- },
1364
- invokeOpen ? {
1365
- kind: "invoke-open",
1366
- ...invokeOpen
1367
- } : null
1368
- ].filter((candidate) => candidate !== null).toSorted((left, right) => left.index - right.index)[0];
1369
- if (!next) {
1370
- state.offset = Math.max(contentStartIndex, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
1371
- return { kind: "incomplete" };
1372
- }
1373
- if (next.kind === "invoke-open") {
1374
- state.invoke = {
1375
- kind: "open",
1376
- start: next.index
1377
- };
1378
- state.offset = next.index + next.token.length;
1379
- continue;
1380
- }
1381
- return next;
1382
- }
1383
- return { kind: "incomplete" };
1384
- }
1385
- //#endregion
1386
- //#region packages/ai/src/transports/openai-completions-stream.ts
1387
- function extractToolCallThoughtSignature(toolCall) {
1388
- const tc = toolCall;
1389
- if (!tc) return;
1390
- const extra = tc.extra_content?.google;
1391
- return readNonEmptyStringPreservingWhitespace(extra?.thought_signature) ?? readNonEmptyStringPreservingWhitespace(tc.function?.thought_signature) ?? readNonEmptyStringPreservingWhitespace(tc.thought_signature);
1392
- }
1393
- async function processCompletionsStream(responseStream, output, model, stream, options) {
1394
- const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
1395
- const directMode = options?.mode === "direct";
1396
- const emitReasoning = options?.emitReasoning ?? true;
1397
- const compat = getCompat(model);
1398
- const visibleReasoningDetailTypes = new Set(compat.visibleReasoningDetailTypes);
1399
- const shouldFilterDeepSeekDsmlText = !directMode && compat.thinkingFormat === "deepseek";
1400
- const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
1401
- const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
1402
- const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
1403
- if (options?.strictReasoningTags) reasoningTagTextPartitioner.markStrict();
1404
- let currentBlock = null;
1405
- let directTextBlock = null;
1406
- let directThinkingBlock = null;
1407
- let currentTextSource;
1408
- let pendingInterruptedTextBlock = null;
1409
- let confirmedInterruptedTextBlock = null;
1410
- let pendingPostToolCallDeltas = [];
1411
- let pendingPostToolCallBytes = 0;
1412
- let isFlushingPendingPostToolCallDeltas = false;
1413
- const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
1414
- const toolCallBlocksById = /* @__PURE__ */ new Map();
1415
- const encryptedReasoning = createOpenAIEncryptedToolCallReasoningTracker();
1416
- const toolArgumentPreviewSchedules = /* @__PURE__ */ new WeakMap();
1417
- const provisionalCommentaryTags = directMode ? options.provisionalCommentaryTags : /* @__PURE__ */ new Map();
1418
- const contentBlockIndices = /* @__PURE__ */ new WeakMap();
1419
- const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
1420
- let explicitVisibleTextBlocks;
1421
- const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
1422
- let finishReason;
1423
- let sawNativeToolCallDelta = false;
1424
- const blockIndex = () => directMode && currentBlock && currentBlock.type !== "toolCall" ? contentBlockIndices.get(currentBlock) ?? output.content.length - 1 : output.content.length - 1;
1425
- let chunkPushedEvent = false;
1426
- const pushStreamEvent = (event) => {
1427
- chunkPushedEvent = true;
1428
- stream.push(event);
1429
- };
1430
- const queuePostToolCallDelta = (next) => {
1431
- const nextBytes = Buffer.byteLength(next.text, "utf8");
1432
- if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
1433
- pendingPostToolCallBytes += nextBytes;
1434
- const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
1435
- if (!previous || previous.kind !== next.kind || previous.kind === "text" && next.kind === "text" && previous.source !== next.source || previous.kind === "thinking" && next.kind === "thinking" && previous.signature !== next.signature) {
1436
- pendingPostToolCallDeltas.push(next);
1437
- return;
1438
- }
1439
- previous.text += next.text;
1440
- };
1441
- const appendThinkingDeltaInternal = (reasoningDelta) => {
1442
- if (directMode && directThinkingBlock) currentBlock = directThinkingBlock;
1443
- if (!currentBlock || currentBlock.type !== "thinking") {
1444
- options?.beforeContentBlock?.("thinking");
1445
- const thinkingSignature = reasoningDelta.signature;
1446
- currentBlock = {
1447
- type: "thinking",
1448
- thinking: "",
1449
- ...thinkingSignature ? { thinkingSignature } : {}
1450
- };
1451
- if (directMode) {
1452
- directTextBlock = null;
1453
- directThinkingBlock = currentBlock;
1454
- }
1455
- output.content.push(currentBlock);
1456
- contentBlockIndices.set(currentBlock, output.content.length - 1);
1457
- pushStreamEvent({
1458
- type: "thinking_start",
1459
- contentIndex: blockIndex(),
1460
- partial: output
1461
- });
1462
- }
1463
- appendAssistantThinking(currentBlock, reasoningDelta.text);
1464
- pushStreamEvent({
1465
- type: "thinking_delta",
1466
- contentIndex: blockIndex(),
1467
- delta: reasoningDelta.text,
1468
- partial: output
1469
- });
1470
- };
1471
- const appendTextDeltaInternal = (text, source) => {
1472
- if (directMode && directTextBlock) currentBlock = directTextBlock;
1473
- if (currentBlock?.type === "text" && currentTextSource !== source) currentBlock = null;
1474
- if (!currentBlock || currentBlock.type !== "text") {
1475
- options?.beforeContentBlock?.("text");
1476
- currentBlock = {
1477
- type: "text",
1478
- text: ""
1479
- };
1480
- currentTextSource = source;
1481
- if (directMode) {
1482
- directTextBlock = currentBlock;
1483
- directThinkingBlock = null;
1484
- }
1485
- if (source === "reasoning_detail") (explicitVisibleTextBlocks ??= /* @__PURE__ */ new Set()).add(currentBlock);
1486
- output.content.push(currentBlock);
1487
- contentBlockIndices.set(currentBlock, output.content.length - 1);
1488
- pushStreamEvent({
1489
- type: "text_start",
1490
- contentIndex: blockIndex(),
1491
- partial: output
1492
- });
1493
- }
1494
- currentBlock.text += text;
1495
- if (pendingInterruptedTextBlock && text.trim()) {
1496
- confirmedInterruptedTextBlock = pendingInterruptedTextBlock;
1497
- pendingInterruptedTextBlock = null;
1498
- }
1499
- pushStreamEvent({
1500
- type: "text_delta",
1501
- contentIndex: blockIndex(),
1502
- delta: text,
1503
- ...directMode ? { partial: output } : {}
1504
- });
1505
- };
1506
- const flushPendingPostToolCallDeltas = () => {
1507
- if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
1508
- isFlushingPendingPostToolCallDeltas = true;
1509
- const bufferedDeltas = pendingPostToolCallDeltas;
1510
- pendingPostToolCallDeltas = [];
1511
- pendingPostToolCallBytes = 0;
1512
- for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text, delta.source);
1513
- else if (emitReasoning) appendThinkingDeltaInternal(delta);
1514
- isFlushingPendingPostToolCallDeltas = false;
1515
- };
1516
- const appendThinkingDelta = (reasoningDelta) => {
1517
- flushPendingPostToolCallDeltas();
1518
- appendThinkingDeltaInternal(reasoningDelta);
1519
- };
1520
- const appendTextDelta = (text, source) => {
1521
- flushPendingPostToolCallDeltas();
1522
- appendTextDeltaInternal(text, source);
1523
- };
1524
- const appendVisibleTextDelta = (text) => {
1525
- if (!text) return;
1526
- if (currentBlock?.type === "toolCall" && !directMode) queuePostToolCallDelta({
1527
- kind: "text",
1528
- text
1529
- });
1530
- else appendTextDelta(text);
1531
- };
1532
- const appendReasoningDeltas = (reasoningDeltas) => {
1533
- for (const reasoningDelta of reasoningDeltas) {
1534
- if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
1535
- if (currentBlock?.type === "toolCall" && !directMode) {
1536
- queuePostToolCallDelta({ ...reasoningDelta });
1537
- continue;
1538
- }
1539
- if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text, reasoningDelta.source);
1540
- else if (emitReasoning) appendThinkingDelta(directMode && model.provider === "opencode-go" && reasoningDelta.signature === "reasoning" ? {
1541
- ...reasoningDelta,
1542
- signature: "reasoning_content"
1543
- } : reasoningDelta);
1544
- }
1545
- };
1546
- const appendRecoveredToolCall = (toolCall) => {
1547
- if (currentBlock?.type === "toolCall") {
1548
- currentBlock = null;
1549
- flushPendingPostToolCallDeltas();
1550
- }
1551
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1552
- const block = {
1553
- type: "toolCall",
1554
- id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
1555
- name: toolCall.name,
1556
- arguments: toolCall.arguments,
1557
- partialArgs: toolCall.partialArgs
1558
- };
1559
- toolArgumentPreviewSchedules.set(block, createToolArgumentPreviewSchedule());
1560
- currentBlock = block;
1561
- output.content.push(block);
1562
- toolCallBlockIndices.set(block, output.content.length - 1);
1563
- pushStreamEvent({
1564
- type: "toolcall_start",
1565
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
1566
- partial: output
1567
- });
1568
- pushStreamEvent({
1569
- type: "toolcall_delta",
1570
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
1571
- delta: toolCall.partialArgs,
1572
- partial: output
1573
- });
1574
- };
1575
- const appendRecoveredParts = (recoveredParts) => {
1576
- for (const recoveredPart of recoveredParts) {
1577
- if (recoveredPart.kind === "toolCall") {
1578
- appendRecoveredToolCall(recoveredPart);
1579
- continue;
1580
- }
1581
- const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
1582
- for (const part of parts) appendVisibleTextDelta(part);
1583
- }
1584
- };
1585
- const appendFilteredVisibleTextDelta = (text) => {
1586
- appendRecoveredParts(deepSeekToolCallRecoverer?.push(text) ?? [{
1587
- kind: "text",
1588
- text
1589
- }]);
1590
- };
1591
- const appendPartitionedVisibleDelta = (delta) => {
1592
- if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
1593
- };
1594
- const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
1595
- if (directMode || !hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
1596
- const latestBlock = output.content[output.content.length - 1];
1597
- if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
1598
- if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
1599
- appendThinkingDelta({ text: "" });
1600
- };
1601
- const flushReasoningTagTextPartitioner = () => {
1602
- for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
1603
- };
1604
- const sealTextBeforeReasoning = () => {
1605
- if (currentBlock?.type !== "text" && !reasoningTagTextPartitioner.hasPending()) return;
1606
- flushReasoningTagTextPartitioner();
1607
- if (currentBlock?.type !== "text") return;
1608
- if (currentTextSource !== "reasoning_detail" && currentBlock.text.trim()) pendingInterruptedTextBlock = currentBlock;
1609
- currentBlock = null;
1610
- if (directMode) directTextBlock = null;
1611
- currentTextSource = void 0;
1612
- };
1613
- const beginReasoning = (hasFollowingVisibleText, forceStrict = false) => {
1614
- if (!output.openclawDelivery?.textPhaseRequiresTerminal) output.openclawDelivery = {
1615
- ...output.openclawDelivery,
1616
- textPhaseRequiresTerminal: true
1617
- };
1618
- if (forceStrict || reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
1619
- if (!hasFollowingVisibleText || !reasoningTagTextPartitioner.hasPendingSyntax()) sealTextBeforeReasoning();
1620
- };
1621
- const guardedStream = withFirstStreamEventTimeout(responseStream, {
1622
- provider: model.provider,
1623
- api: model.api,
1624
- model: model.id,
1625
- timeoutMs: options?.firstEventTimeoutMs ?? 0,
1626
- stage: "completions",
1627
- abort: options?.abortFirstEventStream,
1628
- onTimeout: options?.onFirstEventTimeout,
1629
- hint: "The provider may be stalled while parsing the tool payload; retry with a smaller tool surface or enable OPENCLAW_DEBUG_MODEL_PAYLOAD=tools to inspect exposed tools."
1630
- });
1631
- const events = directMode ? guardedStream : iterateModelStream(guardedStream, options?.signal);
1632
- for await (const rawChunk of events) {
1633
- throwIfModelStreamAborted(options?.signal);
1634
- chunkPushedEvent = false;
1635
- if (!rawChunk || typeof rawChunk !== "object") continue;
1636
- notifyLlmRequestActivity(options?.signal);
1637
- const chunk = rawChunk;
1638
- output.responseId ||= chunk.id;
1639
- if (typeof chunk.model === "string" && chunk.model.length > 0 && chunk.model !== model.id) output.responseModel ||= chunk.model;
1640
- let hasReasoningUsageActivity = false;
1641
- if (chunk.usage) {
1642
- output.usage = parseOpenAICompletionsUsage(chunk.usage, model, { includeReasoningTokens: !directMode });
1643
- hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
1644
- }
1645
- const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
1646
- if (!choice) {
1647
- emitReasoningUsageActivity(hasReasoningUsageActivity);
1648
- continue;
1649
- }
1650
- const choiceUsage = choice.usage;
1651
- if (!chunk.usage && choiceUsage) {
1652
- output.usage = parseOpenAICompletionsUsage(choiceUsage, model, { includeReasoningTokens: !directMode });
1653
- hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
1654
- }
1655
- if (choice.finish_reason) {
1656
- const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
1657
- output.stopReason = finishReasonResult.stopReason;
1658
- finishReason = finishReasonResult.stopReason;
1659
- if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
1660
- }
1661
- const rawChoiceDelta = choice.delta ?? choice.message;
1662
- if (!rawChoiceDelta) {
1663
- emitReasoningUsageActivity(hasReasoningUsageActivity);
1664
- continue;
1665
- }
1666
- for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
1667
- const choiceDelta = normalizedDelta.delta;
1668
- const deltaFields = choiceDelta;
1669
- const reasoningBatch = readOpenAICompletionsReasoningBatch(deltaFields, visibleReasoningDetailTypes);
1670
- const reasoningDeltas = reasoningBatch.deltas;
1671
- const hasReasoningThinking = reasoningBatch.hasThinking;
1672
- const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningBatch.mirroredThinking);
1673
- const lastVisibleTextIndex = contentDeltas.findLastIndex((delta) => delta.kind === "text");
1674
- const hasSameChunkVisibleText = reasoningBatch.hasVisibleText || lastVisibleTextIndex !== -1;
1675
- if (hasReasoningThinking) {
1676
- beginReasoning(hasSameChunkVisibleText, true);
1677
- appendReasoningDeltas(reasoningDeltas);
1678
- }
1679
- for (const [contentDeltaIndex, contentDelta] of contentDeltas.entries()) if (contentDelta.kind === "text") {
1680
- const routedDeltas = hasReasoningThinking ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
1681
- for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
1682
- } else {
1683
- beginReasoning(contentDeltaIndex < lastVisibleTextIndex);
1684
- if (emitReasoning) {
1685
- if (currentBlock?.type === "toolCall" && !directMode) queuePostToolCallDelta(contentDelta);
1686
- else appendThinkingDelta(contentDelta);
1687
- }
1688
- }
1689
- if (!hasReasoningThinking) appendReasoningDeltas(reasoningDeltas);
1690
- const toolCallDeltas = normalizedDelta.toolCalls;
1691
- if (toolCallDeltas.length > 0) {
1692
- sawNativeToolCallDelta = true;
1693
- flushReasoningTagTextPartitioner();
1694
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1695
- for (const toolCall of toolCallDeltas) {
1696
- const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
1697
- let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
1698
- if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
1699
- if (!block) {
1700
- if (currentBlock?.type === "toolCall") {
1701
- currentBlock = null;
1702
- flushPendingPostToolCallDeltas();
1703
- }
1704
- const initialSig = directMode ? void 0 : extractToolCallThoughtSignature(toolCall);
1705
- options?.beforeContentBlock?.("toolCall");
1706
- if (directMode) directThinkingBlock = null;
1707
- block = {
1708
- type: "toolCall",
1709
- id: toolCall.id || "",
1710
- name: toolCall.function?.name || "",
1711
- arguments: {},
1712
- partialArgs: "",
1713
- ...initialSig ? { thoughtSignature: initialSig } : {}
1714
- };
1715
- encryptedReasoning.rememberToolCall(block.id, block);
1716
- toolArgumentPreviewSchedules.set(block, createToolArgumentPreviewSchedule());
1717
- output.content.push(block);
1718
- toolCallBlockIndices.set(block, output.content.length - 1);
1719
- pushStreamEvent({
1720
- type: "toolcall_start",
1721
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
1722
- partial: output
1723
- });
1724
- }
1725
- if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
1726
- if (toolCall.id) {
1727
- const previousId = block.id;
1728
- if (!directMode || !block.id) block.id = toolCall.id;
1729
- toolCallBlocksById.set(toolCall.id, block);
1730
- if (block.id === toolCall.id) encryptedReasoning.rememberToolCall(toolCall.id, block, previousId);
1731
- }
1732
- currentBlock = block;
1733
- const conflictingId = directMode && block.id && toolCall.id && block.id !== toolCall.id;
1734
- if (toolCall.function?.name && !conflictingId) block.name = toolCall.function.name;
1735
- const deltaSig = directMode ? void 0 : extractToolCallThoughtSignature(toolCall);
1736
- if (deltaSig) block.thoughtSignature = deltaSig;
1737
- const toolArgumentsDelta = toolCall.function?.arguments;
1738
- if (toolArgumentsDelta) {
1739
- block.partialArgs += toolArgumentsDelta;
1740
- if (toolArgumentPreviewSchedules.get(block)?.(block.partialArgs.length)) block.arguments = parseStreamingJson(block.partialArgs);
1741
- }
1742
- if (toolArgumentsDelta || directMode) pushStreamEvent({
1743
- type: "toolcall_delta",
1744
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
1745
- delta: toolArgumentsDelta ?? "",
1746
- partial: output
1747
- });
1748
- }
1749
- }
1750
- encryptedReasoning.consumeDetails(deltaFields.reasoning_details);
1751
- }
1752
- flushPendingPostToolCallDeltas();
1753
- emitReasoningUsageActivity(hasReasoningUsageActivity);
1754
- }
1755
- throwIfModelStreamAborted(options?.signal);
1756
- if (!finishReason && (directMode || options?.sawStreamDONE?.() === false)) throw new Error("Stream ended without finish_reason");
1757
- flushReasoningTagTextPartitioner();
1758
- appendRecoveredParts(deepSeekToolCallRecoverer?.flush() ?? []);
1759
- for (const part of deepSeekTextFilter?.flush() ?? []) appendVisibleTextDelta(part);
1760
- currentBlock = null;
1761
- flushPendingPostToolCallDeltas();
1762
- finalizeOpenAICompletionsToolCalls(output, {
1763
- allowSilentToolCallPromotion: finishReason === "stop" || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
1764
- onConfirmedToolCall(block, contentIndex) {
1765
- if (directMode || block.type !== "toolCall") return;
1766
- pushStreamEvent({
1767
- type: "toolcall_end",
1768
- contentIndex,
1769
- toolCall: block,
1770
- partial: output
1771
- });
1772
- }
1773
- });
1774
- if (confirmedInterruptedTextBlock && output.stopReason !== "toolUse" && output.stopReason !== "error" && output.stopReason !== "aborted") tagInterruptedTextPhases(output.content, confirmedInterruptedTextBlock, explicitVisibleTextBlocks);
1775
- if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
1776
- if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
1777
- if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
1778
- }
1779
- function shouldEmitOpenAICompletionsReasoning(model, options) {
1780
- if (!model.reasoning) return false;
1781
- const effort = options?.reasoningEffort ?? options?.reasoning ?? "high";
1782
- return Boolean(effort) && isOpenAICompletionsThinkingEnabled(effort);
1783
- }
1784
- function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
1785
- const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
1786
- return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
1787
- }
1788
- //#endregion
1789
- //#region packages/ai/src/transports/openai-completions-transport.ts
1790
- function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
1791
- const messages = params.messages;
1792
- if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
1793
- 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.`);
1794
- }
1795
- const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
1796
- const SSE_DONE_MAX_LINE_CHARS = 1024;
1797
- function createSseDoneDetector() {
1798
- const decoder = new TextDecoder();
1799
- let line = "";
1800
- let lineOverflowed = false;
1801
- let sawDone = false;
1802
- const finishLine = () => {
1803
- if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
1804
- line = "";
1805
- lineOverflowed = false;
1806
- };
1807
- const observeText = (text) => {
1808
- for (const char of text) {
1809
- if (char === "\n" || char === "\r") {
1810
- finishLine();
1811
- continue;
1812
- }
1813
- if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
1814
- else lineOverflowed = true;
1815
- }
1816
- };
1817
- return {
1818
- observe(chunk) {
1819
- if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
1820
- },
1821
- finish() {
1822
- if (sawDone) return;
1823
- observeText(decoder.decode());
1824
- if (line || lineOverflowed) finishLine();
1825
- },
1826
- sawDone: () => sawDone
1827
- };
1828
- }
1829
- function buildOpenAICompletionsClientConfig(model, headers) {
1830
- const defaultQuery = {};
1831
- let baseURL = model.baseUrl;
1832
- let isAzureHost = false;
1833
- const parsed = URL.parse(model.baseUrl);
1834
- if (parsed) {
1835
- isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
1836
- parsed.searchParams.forEach((value, key) => {
1837
- if (value) defaultQuery[key] = value;
1838
- });
1839
- parsed.search = "";
1840
- baseURL = parsed.toString().replace(/\/$/, "");
1841
- }
1842
- if (isAzureHost) {
1843
- const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
1844
- if (apiVersionHeader) {
1845
- const apiVersion = headers[apiVersionHeader]?.trim();
1846
- delete headers[apiVersionHeader];
1847
- if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
1848
- }
1849
- }
1850
- return {
1851
- baseURL: resolveOpenAIClientBaseUrl(model, baseURL),
1852
- defaultHeaders: headers,
1853
- defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
1854
- };
1855
- }
1856
- function createDirectCompletionsEventStream(output, stream) {
1857
- const finishedBlocks = /* @__PURE__ */ new Set();
1858
- const contentIndices = /* @__PURE__ */ new WeakMap();
1859
- let openTextBlock;
1860
- let openThinkingBlock;
1861
- const finishBlock = (block) => {
1862
- const contentIndex = contentIndices.get(block);
1863
- if (contentIndex === void 0 || finishedBlocks.has(block)) return;
1864
- finishedBlocks.add(block);
1865
- if (block.type === "text") {
1866
- openTextBlock = void 0;
1867
- stream.push({
1868
- type: "text_end",
1869
- contentIndex,
1870
- content: block.text,
1871
- partial: output
1872
- });
1873
- } else if (block.type === "thinking") {
1874
- openThinkingBlock = void 0;
1875
- stream.push({
1876
- type: "thinking_end",
1877
- contentIndex,
1878
- content: block.thinking,
1879
- partial: output
1880
- });
1881
- } else if (block.type === "toolCall") stream.push({
1882
- type: "toolcall_end",
1883
- contentIndex,
1884
- toolCall: block,
1885
- partial: output
1886
- });
1887
- };
1888
- return {
1889
- stream: { push(event) {
1890
- if (event.type === "text_start" || event.type === "thinking_start" || event.type === "toolcall_start") {
1891
- const block = output.content[event.contentIndex];
1892
- if (block) {
1893
- contentIndices.set(block, event.contentIndex);
1894
- if (block.type === "text") openTextBlock = block;
1895
- else if (block.type === "thinking") openThinkingBlock = block;
1896
- }
1897
- }
1898
- stream.push(event);
1899
- } },
1900
- beforeContentBlock: (nextType) => {
1901
- if (openThinkingBlock) finishBlock(openThinkingBlock);
1902
- if (openTextBlock && nextType !== "toolCall") finishBlock(openTextBlock);
1903
- },
1904
- finish(includeToolCalls) {
1905
- for (const block of output.content) if (block.type !== "toolCall" || includeToolCalls) finishBlock(block);
1906
- }
1907
- };
1908
- }
1909
- function createOpenAICompletionsTransportStreamFn() {
1910
- return (model, context, options) => streamOpenAICompletionsRequest(model, context, options, "managed");
1911
- }
1912
- function streamOpenAICompletionsRequest(model, context, options, mode) {
1913
- const { eventStream, stream } = createWritableTransportEventStream();
1914
- (async () => {
1915
- const output = createAssistantOutput(model);
1916
- const provisionalCommentaryTags = /* @__PURE__ */ new Map();
1917
- let firstEventAbort;
1918
- try {
1919
- const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
1920
- const directEmitReasoning = Boolean(mode === "direct" && model.reasoning && options?.reasoningEffort && isOpenAICompletionsThinkingEnabled(options.reasoningEffort));
1921
- const cacheRetention = resolveCacheRetention(options?.cacheRetention);
1922
- const policy = mode === "direct" ? {
1923
- mode,
1924
- compat: resolveOpenAICompletionsCompat(model),
1925
- cacheRetention
1926
- } : { mode };
1927
- const { client, sawStreamDONE } = policy.mode === "direct" ? {
1928
- client: createDirectCompletionsClient(model, context, apiKey, resolveProviderSimpleCompletionHeaders(model, options), cacheRetention === "none" ? void 0 : options?.sessionId, policy.compat),
1929
- sawStreamDONE: void 0
1930
- } : createManagedCompletionsClient(model, context, options, apiKey, cacheRetention);
1931
- let params = buildOpenAICompletionsRequest(model, context, options, policy);
1932
- const encodeBody = prepareModelRequestBody(options);
1933
- const nextParams = await options?.onPayload?.(params, model);
1934
- if (nextParams !== void 0) params = nextParams;
1935
- if (mode === "managed") {
1936
- if (options?.openclawCodeModeToolSurface === true) {
1937
- const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
1938
- enforceCodeModeResponsesToolSurface(params, visibleToolNames, void 0, codeModeToolSurfaceObserver.get(options));
1939
- assertCodeModeResponsesToolSurface(params, visibleToolNames);
1940
- }
1941
- if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
1942
- }
1943
- const emitReasoning = mode === "direct" ? directEmitReasoning : shouldEmitOpenAICompletionsReasoning(model, options);
1944
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
1945
- const requestOptions = mode === "direct" ? {
1946
- ...await encodeBody(params),
1947
- signal: firstEventAbort.signal,
1948
- ...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
1949
- maxRetries: 0
1950
- } : {
1951
- ...buildOpenAISdkRequestOptions(model, firstEventAbort.signal, { timeoutMs: options?.timeoutMs }),
1952
- ...await encodeBody(params)
1953
- };
1954
- const { data: responseStream, response } = await client.chat.completions.create(params, requestOptions).withResponse();
1955
- const hookedResponseStream = withProviderResponseHook({
1956
- stream: responseStream,
1957
- signal: firstEventAbort.signal,
1958
- abort: firstEventAbort.abort,
1959
- hook: createOpenAIProviderAcceptanceHook(options, response, model),
1960
- onReady: () => stream.push({
1961
- type: "start",
1962
- partial: output
1963
- })
1964
- });
1965
- const directEvents = mode === "direct" ? createDirectCompletionsEventStream(output, stream) : void 0;
1966
- try {
1967
- await processCompletionsStream(hookedResponseStream, output, model, directEvents?.stream ?? stream, {
1968
- ...directEvents ? {
1969
- mode: "direct",
1970
- beforeContentBlock: directEvents.beforeContentBlock,
1971
- provisionalCommentaryTags
1972
- } : { mode: "managed" },
1973
- signal: options?.signal,
1974
- emitReasoning,
1975
- strictReasoningTags: reasoningTagTextPolicy.isStrict(options),
1976
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
1977
- abortFirstEventStream: firstEventAbort.abort,
1978
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
1979
- sawStreamDONE
1980
- });
1981
- if (directEvents) {
1982
- if (options?.signal?.aborted) throw transportAbortError(options.signal);
1983
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage || (output.stopReason === "aborted" ? "Request was aborted" : "Provider returned an invalid tool call"));
1984
- }
1985
- } catch (error) {
1986
- directEvents?.finish(false);
1987
- throw error;
1988
- }
1989
- directEvents?.finish(output.stopReason === "toolUse");
1990
- finalizeTransportStream({
1991
- stream,
1992
- output,
1993
- signal: options?.signal
1994
- });
1995
- } catch (error) {
1996
- failTransportStream({
1997
- stream,
1998
- output,
1999
- signal: options?.signal,
2000
- error,
2001
- cleanup: () => {
2002
- if (mode === "managed") output.stopReason = options?.signal?.aborted ? "aborted" : "error";
2003
- finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
2004
- clearPendingCommentaryText(provisionalCommentaryTags);
2005
- tagUnresolvedTextAsCommentary(output);
2006
- if (mode === "direct") for (const block of output.content) {
2007
- delete block.index;
2008
- delete block.partialArgs;
2009
- delete block.streamIndex;
2010
- }
2011
- }
2012
- });
2013
- } finally {
2014
- firstEventAbort?.dispose();
2015
- }
2016
- })();
2017
- return eventStream;
2018
- }
2019
- function createDirectCompletionsClient(model, context, apiKey, optionsHeaders, sessionId, compat) {
2020
- if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
2021
- const headers = { ...model.headers };
2022
- if (model.provider === "github-copilot") Object.assign(headers, buildCopilotDynamicHeaders(context.messages));
2023
- if (sessionId && compat.sessionAffinity !== "none") {
2024
- if (compat.sessionAffinity === "openrouter") headers["x-session-id"] = sessionId;
2025
- else {
2026
- headers.session_id = sessionId;
2027
- headers["x-client-request-id"] = sessionId;
2028
- headers["x-session-affinity"] = sessionId;
2029
- }
2030
- }
2031
- return createOpenAIProviderClient(model, apiKey, headers, optionsHeaders);
2032
- }
2033
- function createManagedCompletionsClient(model, context, options, apiKey, cacheRetention) {
2034
- const turnState = resolveProviderTransportTurnState(model, {
2035
- sessionId: options?.sessionId,
2036
- turnId: randomUUID(),
2037
- attempt: 1,
2038
- transport: "stream"
2039
- });
2040
- const optionHeaders = resolveOpencodeSessionHeaders(model, options);
2041
- const turnHeaders = filterProviderTurnHeadersForExplicitOpencodeSession(model, options, turnState?.headers);
2042
- const doneDetector = createSseDoneDetector();
2043
- const baseFetch = buildGuardedModelFetch(model);
2044
- const doneDetectingFetch = async (url, init) => {
2045
- const response = await baseFetch(url, init);
2046
- if (!response.body || !response.ok) return response;
2047
- if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
2048
- const transformed = response.body.pipeThrough(new TransformStream({
2049
- transform(chunk, controller) {
2050
- doneDetector.observe(chunk);
2051
- controller.enqueue(chunk);
2052
- },
2053
- flush() {
2054
- doneDetector.finish();
2055
- }
2056
- }));
2057
- return new Response(transformed, {
2058
- headers: response.headers,
2059
- status: response.status,
2060
- statusText: response.statusText
2061
- });
2062
- };
2063
- const clientConfig = buildOpenAICompletionsClientConfig(model, buildOpenAIClientHeaders(model, context, {
2064
- ...turnHeaders,
2065
- ...optionHeaders
2066
- }, void 0, resolvePromptCacheKey(options, cacheRetention), cacheRetention));
2067
- return {
2068
- client: new OpenAI({
2069
- apiKey,
2070
- baseURL: clientConfig.baseURL,
2071
- dangerouslyAllowBrowser: true,
2072
- defaultHeaders: clientConfig.defaultHeaders,
2073
- defaultQuery: clientConfig.defaultQuery,
2074
- fetch: doneDetectingFetch,
2075
- ...buildOpenAISdkClientOptions(model)
2076
- }),
2077
- sawStreamDONE: doneDetector.sawDone
2078
- };
2079
- }
2080
- //#endregion
2081
- export { flattenCompletionMessagesToStringContent as a, hasOpenAICompatibleConversationTurn as c, createDeepSeekTextFilter as d, convertMessages as i, canonicalizeMaxTokensParam as l, streamOpenAICompletionsRequest as n, stripCompletionMessagesToRoleContent as o, buildOpenAICompletionsParams as r, applyCompletionsAnthropicCacheControl as s, createOpenAICompletionsTransportStreamFn as t, resolveMaxTokensParam as u };