@openclaw/ai 2026.9.2 → 2026.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/anthropic-C4Qu4H0Z.mjs +377 -0
- package/dist/{anthropic-payload-policy-d46X2pR0.d.mts → anthropic-payload-policy-BZ8umAbk.d.mts} +16 -6
- package/dist/{anthropic-compaction-replay-DB2FGLQc.mjs → anthropic-stream-reducer-CILWF7JD.mjs} +931 -184
- package/dist/{api-registry-DWtPjzyn.d.mts → api-registry-ByUwIR0e.d.mts} +2 -2
- package/dist/assistant-output-tLt4H-iQ.mjs +16 -0
- package/dist/assistant-text-phase-C20rxWwP.mjs +55 -0
- package/dist/{azure-openai-responses-C5bZRfAP.mjs → azure-openai-responses-BAlqlKKc.mjs} +5 -5
- package/dist/{base64-CEFBpSkN.mjs → base64-D-su8YVo.mjs} +1 -27
- package/dist/{diagnostics-DfFyKeX_.mjs → diagnostics-QuErwCIl.mjs} +96 -21
- package/dist/diagnostics.d.mts +3 -1
- package/dist/diagnostics.mjs +3 -3
- package/dist/{event-stream-BhT5T1Ay.d.mts → event-stream-Bt5Y4Pav.d.mts} +11 -3
- package/dist/event-stream-C3WGFsum.d.mts +1 -0
- package/dist/{event-stream-BgDvQeum.mjs → event-stream-D8PARQfL.mjs} +45 -1
- package/dist/event-stream.d.mts +2 -2
- package/dist/event-stream.mjs +1 -1
- package/dist/{github-copilot-headers-NCJtz9i0.mjs → github-copilot-headers-B37TB3rB.mjs} +3 -10
- package/dist/github-copilot-request-facts-BTEBMeOv.mjs +16 -0
- package/dist/{google-4qeuE8iX.mjs → google-DPBAOaOW.mjs} +10 -10
- package/dist/google-messages-6JkpHrhJ.mjs +448 -0
- package/dist/google-shared-BvBeW9aq.mjs +185 -0
- package/dist/{google-vertex-O9BcnB6X.mjs → google-vertex-k-TMCAYD.mjs} +7 -6
- package/dist/{host-DjzGmdZ2.d.mts → host-4atIX-2V.d.mts} +5 -3
- package/dist/{host-CEvLw30U.mjs → host-B8YfDGd4.mjs} +181 -41
- package/dist/host-policy-Zcg_cNz8.mjs +37 -0
- package/dist/{index-FnHM2FcI.d.mts → index-DdD3qerf.d.mts} +4 -4
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +7 -6
- package/dist/internal/anthropic.d.mts +7 -7
- package/dist/internal/anthropic.mjs +4 -4
- package/dist/internal/google-model-family.d.mts +5 -0
- package/dist/internal/google-model-family.mjs +15 -0
- package/dist/internal/openai-responses-payload-policy.d.mts +3 -3
- package/dist/internal/openai-responses-payload-policy.mjs +3 -2
- package/dist/internal/openai.d.mts +8 -103
- package/dist/internal/openai.mjs +9 -10
- package/dist/internal/retry-after.d.mts +2 -4
- package/dist/internal/retry-after.mjs +57 -8
- package/dist/internal/runtime.d.mts +43 -7
- package/dist/internal/runtime.mjs +6 -7
- package/dist/internal/shared.d.mts +43 -3
- package/dist/internal/shared.mjs +5 -4
- package/dist/internal/tool-schema.d.mts +63 -0
- package/dist/internal/tool-schema.mjs +3 -0
- package/dist/{mistral-Tb6oalqH.mjs → mistral-CxUZ1jUb.mjs} +10 -37
- package/dist/model-transport-url-DKocrEsb.d.mts +28 -0
- package/dist/{openai-chatgpt-responses-yUXjPNfu.mjs → openai-chatgpt-responses-CgO6kZfo.mjs} +50 -41
- package/dist/{openai-completions-compat-CpjYUArk.d.mts → openai-completions-compat-eHgh5UPE.d.mts} +26 -4
- package/dist/{openai-completions-stream-BsrBe3Gg.mjs → openai-completions-stream-Da2vvl-S.mjs} +654 -453
- package/dist/openai-completions-yJuk7eis.mjs +187 -0
- package/dist/openai-prompt-cache-B4eYo2-I.d.mts +16 -0
- package/dist/openai-prompt-cache-Bds-n_9Q.mjs +220 -0
- package/dist/openai-provider-client-S2gCrM2Z.mjs +24 -0
- package/dist/{openai-responses-CCL41ALM.mjs → openai-responses-DaYwH05E.mjs} +14 -29
- package/dist/{openai-responses-compaction-window-BMVHFOtq.mjs → openai-responses-compaction-window-CIhBAkkq.mjs} +6 -246
- package/dist/{openai-responses-contracts-rF5DDRNS.d.mts → openai-responses-contracts-BjBAqAg_.d.mts} +5 -38
- package/dist/openai-responses-contracts-DDOHA62Y.mjs +245 -0
- package/dist/{openai-responses-prompt-observer-internal-D0bhBfgL.mjs → openai-responses-prompt-observer-internal-f8J7wpsk.mjs} +3 -3
- package/dist/{openai-responses-shared-Bmma_3Qc.mjs → openai-responses-shared-B8RdBPCv.mjs} +47 -118
- package/dist/openai-tool-projection-793cE3QX.d.mts +37 -0
- package/dist/{openai-tool-schema-_pTAJqKF.mjs → openai-tool-schema-CzjyYXun.mjs} +38 -556
- package/dist/openai-tool-schema-ynZBgqW-.d.mts +38 -0
- package/dist/openai-transport-params-9aPuV5YY.mjs +675 -0
- package/dist/positive-integer-41zhOdcV.mjs +8 -0
- package/dist/{provider-error-9TraxGvt.mjs → provider-error-BA-v_tKd.mjs} +70 -11
- package/dist/{provider-options-Bcc_p-TU.d.mts → provider-options-Ceqv1OKk.d.mts} +4 -39
- package/dist/{provider-replay-context-BuSUaAk5.mjs → provider-replay-context-CJ_YvcEW.mjs} +1 -1
- package/dist/{provider-transcript-transform-BaMbI1hr.mjs → provider-transcript-transform-V5YzU9zh.mjs} +1 -1
- package/dist/provider-transport-turn-state-D5EXOFL2.mjs +42 -0
- package/dist/provider-types-CVjKjsuq.d.mts +29 -0
- package/dist/provider-types.d.mts +6 -31
- package/dist/providers.d.mts +2 -2
- package/dist/providers.mjs +11 -11
- package/dist/{reasoning-tag-text-partitioner-DLCNXki6.mjs → reasoning-tag-text-partitioner-BcR5pztD.mjs} +19 -19
- package/dist/record-coerce-DwRYMj3t.mjs +32 -0
- package/dist/retry-after-CdCURCVg.d.mts +15 -0
- package/dist/session-affinity-Bcunsn4I.mjs +23 -0
- package/dist/{simple-options-BjHCCh4v.mjs → simple-options-BQbb4yQL.mjs} +24 -5
- package/dist/src-DDmEryvj.mjs +2 -0
- package/dist/{stream-first-event-timeout-DcNjoFQE.mjs → stream-first-event-timeout-2hfrquuz.mjs} +1 -1
- package/dist/string-coerce-fsri9iCu.mjs +34 -0
- package/dist/{string-normalization--fwJ4S2q.mjs → string-normalization-CmLIasuf.mjs} +1 -1
- package/dist/{tool-schema-json-projection-mJhXDcyz.mjs → tool-schema-json-projection-ClptDdAO.mjs} +2 -38
- package/dist/{transport-stream-shared-xnaqxmbP.d.mts → transport-stream-shared-DNvmoWnv.d.mts} +20 -36
- package/dist/{transport-stream-shared-CZqMhfIw.mjs → transport-stream-shared-zHll9BxO.mjs} +225 -13
- package/dist/transport-utils-zrYjICLZ.mjs +118 -0
- package/dist/transports.d.mts +137 -193
- package/dist/transports.mjs +204 -1334
- package/dist/types-DlfwzH3T.d.mts +1 -0
- package/dist/types-LFWwv0cF.mjs +8 -0
- package/dist/{types-CJ1-Ht7A.d.mts → types-Ntv5z2g2.d.mts} +74 -57
- package/dist/types.d.mts +6 -6
- package/dist/types.mjs +6 -5
- package/dist/{src-2qBGKg8O.mjs → usage-cost-BNWbbXav.mjs} +0 -1
- package/dist/utf16-slice-CvGodqok.mjs +29 -0
- package/dist/{validation-AKZBDGQd.d.mts → validation-B0t_G2H6.d.mts} +1 -1
- package/dist/{validation-CJZtym2g.mjs → validation-BDzVDnTs.mjs} +9 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +13 -3
- package/dist/anthropic-BDdqdVLK.mjs +0 -886
- package/dist/event-stream-zctLx0yr.d.mts +0 -1
- package/dist/google-shared-CWeG8RIl.mjs +0 -636
- package/dist/google-thinking-level-C-V3tecN.mjs +0 -9
- package/dist/hash-CHgqbJmD.mjs +0 -16
- package/dist/json-parse-BuAJEbdW.mjs +0 -146
- package/dist/openai-completions-BIUV3RDT.mjs +0 -403
- package/dist/openai-prompt-cache-CGnVB74a.mjs +0 -21
- package/dist/openai-prompt-cache-CNoIfHYC.d.mts +0 -16
- package/dist/sanitize-unicode-D6xUvZaS.mjs +0 -83
- package/dist/transport-utils-7il795_9.mjs +0 -138
- package/dist/types-3Lnm-QSJ.d.mts +0 -1
- package/dist/types-BDdaOVi2.mjs +0 -6
- package/dist/utf16-slice-qz3nsy87.mjs +0 -84
package/dist/{openai-completions-stream-BsrBe3Gg.mjs → openai-completions-stream-Da2vvl-S.mjs}
RENAMED
|
@@ -1,72 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { r as appendAssistantThinking } from "./event-stream-D8PARQfL.mjs";
|
|
2
|
+
import { _ as isImageWithMediaPayload, d as describeToolResultMediaPlaceholder, m as extractToolResultText, v as sanitizeSurrogates } from "./host-B8YfDGd4.mjs";
|
|
3
|
+
import { o as isRecord } from "./record-coerce-DwRYMj3t.mjs";
|
|
4
|
+
import { t as truncateUtf16Safe } from "./utf16-slice-CvGodqok.mjs";
|
|
5
|
+
import { d as shouldOmitOllamaCompatResponseFormat, i as detectOpenAICompletionsCompat, r as resolveOpenAIPromptCacheParams, u as resolveOpenAICompletionsResponseFormat } from "./openai-prompt-cache-Bds-n_9Q.mjs";
|
|
6
|
+
import { r as emitModelTransportDebug } from "./diagnostics-QuErwCIl.mjs";
|
|
7
|
+
import { i as asNonNegativeFiniteNumber } from "./base64-D-su8YVo.mjs";
|
|
8
|
+
import { c as supportsModelTools, r as estimateStringChars } from "./transport-utils-zrYjICLZ.mjs";
|
|
9
|
+
import { n as uniqueStrings } from "./string-normalization-CmLIasuf.mjs";
|
|
10
|
+
import { i as resolveProviderEndpoint, r as resolveOpenAIStrictToolSetting } from "./host-policy-Zcg_cNz8.mjs";
|
|
11
|
+
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
12
|
+
import { f as splitSystemPromptCacheBoundary, h as stripSystemPromptRelocatableBoundary, m as stripSystemPromptCacheBoundary, p as splitSystemPromptRelocatableBoundary, v as sortPromptCacheToolsByName } from "./simple-options-BQbb4yQL.mjs";
|
|
13
|
+
import { C as readOpenAICompletionsContentDeltas, D as throwIfModelStreamAborted, E as resolvePromptCacheKey, S as parseOpenAICompletionsUsage, b as log, d as resolveOpenAIReasoningEffortMap, f as projectOpenAITools, g as createModelStreamCooperativeScheduler, h as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, p as reconcileOpenAICompletionsToolChoice, s as getCompat, u as resolveOpenAIStrictToolFlagWithDiagnostics, w as readOpenAICompletionsReasoningBatch, x as measureUtf8AppendBytes, y as isOpenAICompletionsThinkingEnabled } from "./openai-transport-params-9aPuV5YY.mjs";
|
|
14
|
+
import { E as parseStreamingJson, c as finalizeTerminalToolCallArguments, w as createToolArgumentPreviewSchedule } from "./transport-stream-shared-zHll9BxO.mjs";
|
|
15
|
+
import { a as tagUnresolvedTextAsCommentary, i as tagPendingCommentaryText, n as rememberPendingCommentaryTags, r as tagInterruptedTextPhases, t as clearPendingCommentaryText } from "./assistant-text-phase-C20rxWwP.mjs";
|
|
11
16
|
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
17
|
+
import { a as withFirstStreamEventTimeout } from "./stream-first-event-timeout-2hfrquuz.mjs";
|
|
18
|
+
import { t as transformProviderMessages } from "./provider-transcript-transform-V5YzU9zh.mjs";
|
|
19
|
+
import { _ as isOpenAIGpt56Model, b as resolveOpenAIReasoningEffortForModel, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel } from "./openai-responses-contracts-DDOHA62Y.mjs";
|
|
20
|
+
import { r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-CzjyYXun.mjs";
|
|
21
|
+
import { isGoogleGemini3FlashModel, isGoogleGemini3ProModel } from "./internal/google-model-family.mjs";
|
|
15
22
|
import { t as mapOpenAIStopReason } from "./openai-stop-reason-Drnn_6Qj.mjs";
|
|
23
|
+
import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-BcR5pztD.mjs";
|
|
16
24
|
import { randomUUID } from "node:crypto";
|
|
17
|
-
//#region packages/ai/src/utils/assistant-text-phase.ts
|
|
18
|
-
const EMPTY_ASSISTANT_TEXT_BLOCK_SET = /* @__PURE__ */ new Set();
|
|
19
|
-
function isAssistantTextPhaseBlock(block) {
|
|
20
|
-
if (!block || typeof block !== "object") return false;
|
|
21
|
-
const record = block;
|
|
22
|
-
return record.type === "text" && typeof record.text === "string";
|
|
23
|
-
}
|
|
24
|
-
function encodeAssistantTextSignatureV1(id, phase) {
|
|
25
|
-
return JSON.stringify({
|
|
26
|
-
v: 1,
|
|
27
|
-
id,
|
|
28
|
-
...phase ? { phase } : {}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
function tagUnphasedText(content, phase, idPrefix) {
|
|
32
|
-
const textBlocks = content.filter(isAssistantTextPhaseBlock);
|
|
33
|
-
let phaseIndex = textBlocks.filter((block) => block.textSignature !== void 0).length;
|
|
34
|
-
const tagged = /* @__PURE__ */ new Map();
|
|
35
|
-
for (const block of textBlocks) {
|
|
36
|
-
if (block.text.trim().length === 0 || block.textSignature !== void 0) continue;
|
|
37
|
-
const signature = encodeAssistantTextSignatureV1(`${idPrefix}-${phaseIndex}-${randomUUID().replaceAll("-", "").slice(0, 24)}`, phase);
|
|
38
|
-
block.textSignature = signature;
|
|
39
|
-
tagged.set(block, signature);
|
|
40
|
-
phaseIndex += 1;
|
|
41
|
-
}
|
|
42
|
-
return tagged;
|
|
43
|
-
}
|
|
44
|
-
/** Tags unphased narration before a tool-call event becomes consumer-visible. */
|
|
45
|
-
function tagPendingCommentaryText(content) {
|
|
46
|
-
return tagUnphasedText(content, "commentary", "commentary");
|
|
47
|
-
}
|
|
48
|
-
/** Records the confirmed final-answer boundary after reasoning resumes. */
|
|
49
|
-
function tagInterruptedTextPhases(content, interruptedText, preservedVisibleText = EMPTY_ASSISTANT_TEXT_BLOCK_SET) {
|
|
50
|
-
const interruptedTextIndex = content.indexOf(interruptedText);
|
|
51
|
-
if (interruptedTextIndex === -1) return;
|
|
52
|
-
const finalAnswerIndex = content.findIndex((block, index) => index > interruptedTextIndex && isAssistantTextPhaseBlock(block) && block.text.trim().length > 0);
|
|
53
|
-
if (finalAnswerIndex === -1) return;
|
|
54
|
-
tagUnphasedText(content.slice(0, finalAnswerIndex).filter((block) => !preservedVisibleText.has(block)), "commentary", "commentary");
|
|
55
|
-
tagUnphasedText(content.filter((block, index) => index >= finalAnswerIndex || preservedVisibleText.has(block)), "final_answer", "final-answer");
|
|
56
|
-
}
|
|
57
|
-
/** Prevents unresolved completion text from becoming a fallback answer after stream failure. */
|
|
58
|
-
function tagUnresolvedTextAsCommentary(message) {
|
|
59
|
-
if (message.openclawDelivery?.textPhaseRequiresTerminal) tagUnphasedText(message.content, "commentary", "commentary");
|
|
60
|
-
}
|
|
61
|
-
/** Rolls back only the exact provisional signatures created by this transport turn. */
|
|
62
|
-
function clearPendingCommentaryText(tags) {
|
|
63
|
-
for (const [block, signature] of tags) if (block.textSignature === signature) delete block.textSignature;
|
|
64
|
-
tags.clear();
|
|
65
|
-
}
|
|
66
|
-
function rememberPendingCommentaryTags(target, tagged) {
|
|
67
|
-
for (const [block, signature] of tagged) target.set(block, signature);
|
|
68
|
-
}
|
|
69
|
-
//#endregion
|
|
70
25
|
//#region packages/ai/src/transports/deepseek-dsml-grammar.ts
|
|
71
26
|
const DEEPSEEK_DSML_MARKERS = [
|
|
72
27
|
"|",
|
|
@@ -162,141 +117,132 @@ function longestDsmlOpenPrefixSuffixLength(text) {
|
|
|
162
117
|
return 0;
|
|
163
118
|
}
|
|
164
119
|
//#endregion
|
|
165
|
-
//#region packages/ai/src/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return new URL(params.baseUrl).origin === OLLAMA_CLOUD_ORIGIN;
|
|
183
|
-
} catch {
|
|
184
|
-
return false;
|
|
120
|
+
//#region packages/ai/src/transports/model-max-tokens-params.ts
|
|
121
|
+
/**
|
|
122
|
+
* Max-token parameter normalization across provider/native naming variants.
|
|
123
|
+
* Callers canonicalize aliases before dispatch so payloads cannot carry
|
|
124
|
+
* conflicting limits.
|
|
125
|
+
*/
|
|
126
|
+
const MAX_TOKENS_PARAM_KEYS = [
|
|
127
|
+
"maxTokens",
|
|
128
|
+
"max_completion_tokens",
|
|
129
|
+
"max_tokens"
|
|
130
|
+
];
|
|
131
|
+
/** Resolve the first supported max-token parameter present in a params object. */
|
|
132
|
+
function resolveMaxTokensParam(params) {
|
|
133
|
+
if (!params) return;
|
|
134
|
+
for (const key of MAX_TOKENS_PARAM_KEYS) {
|
|
135
|
+
const resolved = asNonNegativeFiniteNumber(params[key]);
|
|
136
|
+
if (resolved !== void 0) return resolved;
|
|
185
137
|
}
|
|
186
138
|
}
|
|
187
139
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
140
|
+
* Canonicalize merged params to `maxTokens`, preserving source precedence from
|
|
141
|
+
* left to right across the provided source objects.
|
|
190
142
|
*/
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
name: JSON_SCHEMA_RESPONSE_FORMAT_NAME,
|
|
201
|
-
schema: responseFormat
|
|
202
|
-
}
|
|
203
|
-
};
|
|
143
|
+
function canonicalizeMaxTokensParam(params) {
|
|
144
|
+
let resolved;
|
|
145
|
+
for (const source of params.sources) {
|
|
146
|
+
const sourceValue = resolveMaxTokensParam(source);
|
|
147
|
+
if (sourceValue !== void 0) resolved = sourceValue;
|
|
148
|
+
}
|
|
149
|
+
if (resolved === void 0) return;
|
|
150
|
+
for (const key of MAX_TOKENS_PARAM_KEYS) delete params.merged[key];
|
|
151
|
+
params.merged.maxTokens = resolved;
|
|
204
152
|
}
|
|
205
153
|
//#endregion
|
|
206
|
-
//#region packages/ai/src/transports/openai-completions-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
/** Resolves default request flags for an OpenAI-compatible completions endpoint. */
|
|
211
|
-
function resolveOpenAICompletionsCompatDefaults(input) {
|
|
212
|
-
const { provider, modelId, endpointClass, knownProviderFamily, supportsNativeStreamingUsageCompat = false, supportsOpenAICompletionsStreamingUsageCompat = false, usesExplicitProxyLikeEndpoint = false } = input;
|
|
213
|
-
const isDefaultRoute = endpointClass === "default";
|
|
214
|
-
const usesConfiguredNonOpenAIEndpoint = endpointClass !== "default" && endpointClass !== "openai-public";
|
|
215
|
-
const isMoonshot = knownProviderFamily === "moonshot" || endpointClass === "moonshot-native";
|
|
216
|
-
const isMoonshotLike = isMoonshot || knownProviderFamily === "modelstudio" || endpointClass === "modelstudio-native";
|
|
217
|
-
const isModelStudioLike = knownProviderFamily === "modelstudio" || endpointClass === "modelstudio-native" || isDefaultRoute && isDefaultRouteProvider(provider, "dashscope", "modelstudio", "qwen");
|
|
218
|
-
const isZai = endpointClass === "zai-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "zai");
|
|
219
|
-
const isDeepSeek = endpointClass === "deepseek-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "deepseek");
|
|
220
|
-
const isTogether = knownProviderFamily === "together" || input.baseUrl?.includes("api.together.ai") === true || input.baseUrl?.includes("api.together.xyz") === true || isDefaultRoute && isDefaultRouteProvider(input.provider, "together");
|
|
221
|
-
const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || input.baseUrl?.includes("gateway.ai.cloudflare.com") === true;
|
|
222
|
-
const isXiaomi = endpointClass === "xiaomi-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "xiaomi");
|
|
223
|
-
const isNonStandard = endpointClass === "cerebras-native" || endpointClass === "chutes-native" || endpointClass === "deepseek-native" || endpointClass === "mistral-public" || endpointClass === "opencode-native" || endpointClass === "opencode-go-native" || endpointClass === "xai-native" || isXiaomi || isZai || isDefaultRoute && isDefaultRouteProvider(input.provider, "cerebras", "chutes", "deepseek", "opencode", "xai");
|
|
224
|
-
const isOpenRouterLike = input.provider === "openrouter" || endpointClass === "openrouter";
|
|
225
|
-
const isLocalEndpoint = endpointClass === "local";
|
|
226
|
-
const usesMaxTokens = endpointClass === "chutes-native" || endpointClass === "mistral-public" || knownProviderFamily === "mistral" || isMoonshot || isCloudflareAiGateway || isZai || isTogether || isDefaultRoute && isDefaultRouteProvider(provider, "chutes");
|
|
154
|
+
//#region packages/ai/src/transports/openai-completions-cache-control.ts
|
|
155
|
+
const shapedPayloads = /* @__PURE__ */ new WeakSet();
|
|
156
|
+
function resolveCompletionsCacheControl(compat, retention, openRouterRoute) {
|
|
157
|
+
if (compat.cacheControlFormat !== "anthropic" || retention === "none") return;
|
|
227
158
|
return {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
supportsReasoningEffort: !isZai && !isTogether && knownProviderFamily !== "mistral" && endpointClass !== "xai-native" && !usesExplicitProxyLikeEndpoint,
|
|
231
|
-
supportsUsageInStreaming: supportsOpenAICompletionsStreamingUsageCompat || !isNonStandard && (isLocalEndpoint || !usesConfiguredNonOpenAIEndpoint || supportsNativeStreamingUsageCompat),
|
|
232
|
-
maxTokensField: usesMaxTokens ? "max_tokens" : "max_completion_tokens",
|
|
233
|
-
thinkingFormat: isDeepSeek || isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : isOpenRouterLike ? "openrouter" : "openai",
|
|
234
|
-
visibleReasoningDetailTypes: isOpenRouterLike ? ["response.output_text", "response.text"] : [],
|
|
235
|
-
supportsStrictMode: !isZai && !usesConfiguredNonOpenAIEndpoint,
|
|
236
|
-
supportsJsonSchemaResponseFormat: (endpointClass === "openai-public" || isDefaultRoute && isDefaultRouteProvider(provider, "openai")) && isKnownOpenAIJsonSchemaModelId(modelId),
|
|
237
|
-
requiresReasoningContentOnAssistantMessages: isDeepSeek || isXiaomi,
|
|
238
|
-
requiresNonEmptyUserOrAssistantMessage: isModelStudioLike,
|
|
239
|
-
cacheControlFormat: isModelStudioLike && endpointClass !== "custom" || provider === "openrouter" && modelId?.startsWith("anthropic/") === true ? "anthropic" : void 0,
|
|
240
|
-
sessionAffinityFormat: isOpenRouterLike ? "openrouter" : "openai",
|
|
241
|
-
supportsLongCacheRetention: !isModelStudioLike && provider !== "cloudflare-workers-ai" && provider !== "cloudflare-ai-gateway" && knownProviderFamily !== "together" && !input.baseUrl?.includes("api.cloudflare.com") && !input.baseUrl?.includes("gateway.ai.cloudflare.com") && !input.baseUrl?.includes("api.together.ai") && !input.baseUrl?.includes("api.together.xyz")
|
|
159
|
+
type: "ephemeral",
|
|
160
|
+
...retention === "long" && compat.supportsLongCacheRetention && (openRouterRoute || compat.configuredSupportsLongCacheRetention === true) ? { ttl: "1h" } : {}
|
|
242
161
|
};
|
|
243
162
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
163
|
+
/** Shared Chat Completions policy; repeated wrapper application preserves existing checkpoints. */
|
|
164
|
+
function applyCompletionsAnthropicCacheControl(payload, cacheControl = { type: "ephemeral" }, cacheOptOutIndexes = /* @__PURE__ */ new Set(), markTools = true, markMessages = true) {
|
|
165
|
+
if (shapedPayloads.has(payload)) return;
|
|
166
|
+
shapedPayloads.add(payload);
|
|
167
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
168
|
+
const tools = Array.isArray(payload.tools) ? payload.tools.filter(isRecord) : [];
|
|
169
|
+
const blocks = messages.filter(isRecord).flatMap((message) => Array.isArray(message.content) ? message.content.filter(isRecord) : []);
|
|
170
|
+
for (const block of [...tools, ...blocks]) delete block.cache_control;
|
|
171
|
+
if (!cacheControl) return;
|
|
172
|
+
const markText = (message, splitBoundary) => {
|
|
173
|
+
if (typeof message.content === "string" && message.content) message.content = [{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: message.content
|
|
176
|
+
}];
|
|
177
|
+
const content = message.content;
|
|
178
|
+
if (!Array.isArray(content)) return false;
|
|
179
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
180
|
+
const block = content[i];
|
|
181
|
+
if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string" || !block.text) continue;
|
|
182
|
+
const split = splitBoundary ? splitSystemPromptCacheBoundary(block.text) : void 0;
|
|
183
|
+
if (split) {
|
|
184
|
+
content.splice(i, 1, ...split.stablePrefix ? [{
|
|
185
|
+
type: "text",
|
|
186
|
+
text: split.stablePrefix,
|
|
187
|
+
cache_control: cacheControl
|
|
188
|
+
}] : [], ...split.dynamicSuffix ? [{
|
|
189
|
+
type: "text",
|
|
190
|
+
text: split.dynamicSuffix
|
|
191
|
+
}] : []);
|
|
192
|
+
if (!split.stablePrefix) return false;
|
|
193
|
+
} else block.cache_control = cacheControl;
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
266
197
|
};
|
|
198
|
+
const lastTool = tools.at(-1);
|
|
199
|
+
if (markTools && lastTool) lastTool.cache_control = cacheControl;
|
|
200
|
+
if (!markMessages) return;
|
|
201
|
+
const system = messages.find((message) => isRecord(message) && (message.role === "system" || message.role === "developer"));
|
|
202
|
+
if (isRecord(system)) markText(system, true);
|
|
203
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
204
|
+
const message = messages[i];
|
|
205
|
+
if (!cacheOptOutIndexes.has(i) && isRecord(message) && (message.role === "user" || message.role === "tool") && markText(message, false)) return;
|
|
206
|
+
}
|
|
267
207
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region packages/ai/src/transports/openai-completions-string-content.ts
|
|
210
|
+
/**
|
|
211
|
+
* OpenAI Chat Completions compatibility helpers. Some providers only accept
|
|
212
|
+
* role/content messages with plain string content instead of text block arrays.
|
|
213
|
+
*/
|
|
214
|
+
function flattenStringOnlyCompletionContent(content) {
|
|
215
|
+
if (!Array.isArray(content)) return content;
|
|
216
|
+
const textParts = [];
|
|
217
|
+
for (const item of content) {
|
|
218
|
+
if (!item || typeof item !== "object" || item.type !== "text" || typeof item.text !== "string") return content;
|
|
219
|
+
textParts.push(item.text);
|
|
220
|
+
}
|
|
221
|
+
return textParts.join("\n");
|
|
222
|
+
}
|
|
223
|
+
/** Flatten string-only text block content arrays into newline-joined strings. */
|
|
224
|
+
function flattenCompletionMessagesToStringContent(messages) {
|
|
225
|
+
return messages.map((message) => {
|
|
226
|
+
if (!message || typeof message !== "object") return message;
|
|
227
|
+
const content = message.content;
|
|
228
|
+
const flattenedContent = flattenStringOnlyCompletionContent(content);
|
|
229
|
+
if (flattenedContent === content) return message;
|
|
230
|
+
return {
|
|
231
|
+
...message,
|
|
232
|
+
content: flattenedContent
|
|
233
|
+
};
|
|
234
|
+
});
|
|
272
235
|
}
|
|
273
|
-
/**
|
|
274
|
-
function
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
requiresToolResultName: configured?.requiresToolResultName ?? false,
|
|
284
|
-
requiresAssistantAfterToolResult: configured?.requiresAssistantAfterToolResult ?? false,
|
|
285
|
-
requiresThinkingAsText: configured?.requiresThinkingAsText ?? false,
|
|
286
|
-
requiresReasoningContentOnAssistantMessages: configured?.requiresReasoningContentOnAssistantMessages ?? defaults.requiresReasoningContentOnAssistantMessages,
|
|
287
|
-
thinkingFormat: configured?.thinkingFormat ?? defaults.thinkingFormat,
|
|
288
|
-
openRouterRouting: configured?.openRouterRouting,
|
|
289
|
-
vercelGatewayRouting: configured?.vercelGatewayRouting ?? {},
|
|
290
|
-
zaiToolStream: configured?.zaiToolStream ?? false,
|
|
291
|
-
supportsStrictMode: configured?.supportsStrictMode ?? defaults.supportsStrictMode,
|
|
292
|
-
supportsJsonSchemaResponseFormat: configured?.supportsJsonSchemaResponseFormat ?? defaults.supportsJsonSchemaResponseFormat,
|
|
293
|
-
cacheControlFormat: configured?.cacheControlFormat ?? defaults.cacheControlFormat,
|
|
294
|
-
sessionAffinity: resolveSessionAffinity(model, defaults.sessionAffinityFormat),
|
|
295
|
-
supportsPromptCacheKey: configured?.supportsPromptCacheKey ?? false,
|
|
296
|
-
supportsLongCacheRetention: configured?.supportsLongCacheRetention ?? defaults.supportsLongCacheRetention,
|
|
297
|
-
visibleReasoningDetailTypes: configured && "visibleReasoningDetailTypes" in configured ? configured.visibleReasoningDetailTypes ?? defaults.visibleReasoningDetailTypes : defaults.visibleReasoningDetailTypes,
|
|
298
|
-
requiresNonEmptyUserOrAssistantMessage: defaults.requiresNonEmptyUserOrAssistantMessage
|
|
299
|
-
};
|
|
236
|
+
/** Strip completion messages to role/content fields for strict providers. */
|
|
237
|
+
function stripCompletionMessagesToRoleContent(messages) {
|
|
238
|
+
return messages.map((message) => {
|
|
239
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) return message;
|
|
240
|
+
const record = message;
|
|
241
|
+
const stripped = {};
|
|
242
|
+
if (Object.hasOwn(record, "role")) stripped.role = record.role;
|
|
243
|
+
if (Object.hasOwn(record, "content")) stripped.content = record.content;
|
|
244
|
+
return stripped;
|
|
245
|
+
});
|
|
300
246
|
}
|
|
301
247
|
//#endregion
|
|
302
248
|
//#region packages/ai/src/providers/openai-completions-tool-calls.ts
|
|
@@ -481,17 +427,18 @@ function finalizeOpenAICompletionsToolCalls(output, options = {}) {
|
|
|
481
427
|
}
|
|
482
428
|
}
|
|
483
429
|
//#endregion
|
|
430
|
+
//#region packages/ai/src/transports/openai-completions-host.ts
|
|
431
|
+
/**
|
|
432
|
+
* Chat Completions accepts Azure AI Foundry hosts in addition to traditional
|
|
433
|
+
* Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
|
|
434
|
+
* which intentionally excludes the .services.ai.azure.com Foundry suffix.
|
|
435
|
+
*/
|
|
436
|
+
function isAzureOpenAICompatibleHost(hostname) {
|
|
437
|
+
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
438
|
+
}
|
|
439
|
+
//#endregion
|
|
484
440
|
//#region packages/ai/src/openai-completions-messages.ts
|
|
485
441
|
const EMPTY_TOOL_RESULT_TEXT = "(no output)";
|
|
486
|
-
function isTextContentBlock(block) {
|
|
487
|
-
return block.type === "text";
|
|
488
|
-
}
|
|
489
|
-
function isThinkingContentBlock(block) {
|
|
490
|
-
return block.type === "thinking";
|
|
491
|
-
}
|
|
492
|
-
function isToolCallBlock(block) {
|
|
493
|
-
return block.type === "toolCall";
|
|
494
|
-
}
|
|
495
442
|
function sanitizeToolResultText(text, fallback) {
|
|
496
443
|
const sanitized = sanitizeSurrogates(text);
|
|
497
444
|
return sanitized.trim().length > 0 ? sanitized : fallback;
|
|
@@ -509,9 +456,18 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
509
456
|
return id;
|
|
510
457
|
};
|
|
511
458
|
const transformedMessages = transformProviderMessages(context.messages, model, (id) => normalizeToolCallId(id));
|
|
459
|
+
let relocatableSplit;
|
|
460
|
+
let systemParamIndex;
|
|
512
461
|
if (context.systemPrompt) {
|
|
513
462
|
const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
|
|
514
|
-
|
|
463
|
+
let systemPrompt;
|
|
464
|
+
if (options.preserveSystemPromptCacheBoundary) systemPrompt = stripSystemPromptRelocatableBoundary(context.systemPrompt);
|
|
465
|
+
else {
|
|
466
|
+
const split = splitSystemPromptRelocatableBoundary(context.systemPrompt);
|
|
467
|
+
if (split && split.relocatable.length > 0) relocatableSplit = split;
|
|
468
|
+
systemPrompt = stripSystemPromptCacheBoundary(context.systemPrompt);
|
|
469
|
+
}
|
|
470
|
+
systemParamIndex = params.length;
|
|
515
471
|
params.push({
|
|
516
472
|
role,
|
|
517
473
|
content: sanitizeSurrogates(systemPrompt)
|
|
@@ -562,25 +518,30 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
562
518
|
role: "assistant",
|
|
563
519
|
content: compat.requiresAssistantAfterToolResult ? "" : null
|
|
564
520
|
};
|
|
565
|
-
const assistantTexts =
|
|
566
|
-
const
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
if (
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
521
|
+
const assistantTexts = [];
|
|
522
|
+
const nonEmptyThinkingBlocks = [];
|
|
523
|
+
const toolCalls = [];
|
|
524
|
+
msg.content.forEach((block) => {
|
|
525
|
+
if (block.type === "text" && block.text.trim().length > 0) assistantTexts.push(sanitizeSurrogates(block.text));
|
|
526
|
+
else if (block.type === "thinking" && block.thinking.trim().length > 0) nonEmptyThinkingBlocks.push(block);
|
|
527
|
+
else if (block.type === "toolCall") toolCalls.push(block);
|
|
528
|
+
});
|
|
529
|
+
if (nonEmptyThinkingBlocks.length > 0 && compat.requiresThinkingAsText) assistantMsg.content = [{
|
|
530
|
+
type: "text",
|
|
531
|
+
text: nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n\n")
|
|
532
|
+
}, ...assistantTexts.map((text) => ({
|
|
533
|
+
type: "text",
|
|
534
|
+
text
|
|
535
|
+
}))];
|
|
536
|
+
else {
|
|
537
|
+
const assistantText = assistantTexts.join("\n");
|
|
538
|
+
if (assistantText.length > 0) assistantMsg.content = assistantText;
|
|
539
|
+
if (nonEmptyThinkingBlocks.length > 0) {
|
|
578
540
|
let signature = nonEmptyThinkingBlocks.at(0)?.thinkingSignature;
|
|
579
541
|
if (model.provider === "opencode-go" && signature === "reasoning") signature = "reasoning_content";
|
|
580
542
|
if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => block.thinking).join("\n");
|
|
581
543
|
}
|
|
582
|
-
}
|
|
583
|
-
const toolCalls = msg.content.filter(isToolCallBlock);
|
|
544
|
+
}
|
|
584
545
|
if (toolCalls.length > 0) {
|
|
585
546
|
assistantMsg.tool_calls = toolCalls.map((toolCall) => ({
|
|
586
547
|
id: toolCall.id,
|
|
@@ -604,17 +565,17 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
604
565
|
}
|
|
605
566
|
if (compat.requiresReasoningContentOnAssistantMessages && model.reasoning && assistantMsg.reasoning_content === void 0) assistantMsg.reasoning_content = "";
|
|
606
567
|
const content = assistantMsg.content;
|
|
607
|
-
if (!(content !== null && content !== void 0 &&
|
|
568
|
+
if (!(content !== null && content !== void 0 && content.length > 0) && !assistantMsg.tool_calls) continue;
|
|
608
569
|
params.push(assistantMsg);
|
|
609
570
|
} else if (msg.role === "toolResult") {
|
|
610
|
-
const
|
|
571
|
+
const imageContentParts = [];
|
|
611
572
|
let j = i;
|
|
612
573
|
while (j < transformedMessages.length) {
|
|
613
574
|
const toolMsg = transformedMessages.at(j);
|
|
614
575
|
if (toolMsg?.role !== "toolResult") break;
|
|
615
576
|
const textResult = extractToolResultText(toolMsg.content);
|
|
616
577
|
const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
|
|
617
|
-
const
|
|
578
|
+
const images = toolMsg.content.filter(isImageWithMediaPayload);
|
|
618
579
|
const toolResultMsg = {
|
|
619
580
|
role: "tool",
|
|
620
581
|
content: sanitizeToolResultText(textResult, mediaPlaceholder ?? EMPTY_TOOL_RESULT_TEXT),
|
|
@@ -622,8 +583,13 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
622
583
|
};
|
|
623
584
|
if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
|
|
624
585
|
params.push(toolResultMsg);
|
|
625
|
-
if (
|
|
626
|
-
|
|
586
|
+
if (images.length > 0 && model.input.includes("image")) {
|
|
587
|
+
const boundedToolName = sanitizeSurrogates(truncateUtf16Safe(toolMsg.toolName ?? "", 64));
|
|
588
|
+
imageContentParts.push({
|
|
589
|
+
type: "text",
|
|
590
|
+
text: `Image(s) from tool result #${j - i + 1}${boundedToolName ? ` (${boundedToolName})` : ""}:`
|
|
591
|
+
});
|
|
592
|
+
for (const block of images) imageContentParts.push({
|
|
627
593
|
type: "image_url",
|
|
628
594
|
image_url: { url: `data:${block.mimeType};base64,${block.data}` }
|
|
629
595
|
});
|
|
@@ -631,17 +597,14 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
631
597
|
j += 1;
|
|
632
598
|
}
|
|
633
599
|
i = j - 1;
|
|
634
|
-
if (
|
|
600
|
+
if (imageContentParts.length > 0) {
|
|
635
601
|
if (compat.requiresAssistantAfterToolResult) params.push({
|
|
636
602
|
role: "assistant",
|
|
637
603
|
content: "I have processed the tool results."
|
|
638
604
|
});
|
|
639
605
|
params.push({
|
|
640
606
|
role: "user",
|
|
641
|
-
content:
|
|
642
|
-
type: "text",
|
|
643
|
-
text: "Attached image(s) from tool result:"
|
|
644
|
-
}, ...imageBlocks]
|
|
607
|
+
content: imageContentParts
|
|
645
608
|
});
|
|
646
609
|
lastRole = "user";
|
|
647
610
|
} else lastRole = "toolResult";
|
|
@@ -649,261 +612,501 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
649
612
|
}
|
|
650
613
|
lastRole = msg.role;
|
|
651
614
|
}
|
|
615
|
+
if (relocatableSplit !== void 0 && systemParamIndex !== void 0) relocateNonBehavioralRegion({
|
|
616
|
+
params,
|
|
617
|
+
systemParamIndex,
|
|
618
|
+
split: relocatableSplit,
|
|
619
|
+
cacheOptOutIndexes: options.cacheOptOutIndexes
|
|
620
|
+
});
|
|
652
621
|
return params;
|
|
653
622
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
const provider = normalizeLowercaseStringOrEmpty(model.provider ?? "");
|
|
670
|
-
const id = normalizeLowercaseStringOrEmpty(model.id ?? "");
|
|
671
|
-
const builtinMap = provider === "openai" && OPENAI_MEDIUM_ONLY_REASONING_MODEL_IDS.has(id) ? {
|
|
672
|
-
minimal: "medium",
|
|
673
|
-
low: "medium"
|
|
674
|
-
} : {};
|
|
675
|
-
return {
|
|
676
|
-
...fallbackMap,
|
|
677
|
-
...builtinMap,
|
|
678
|
-
...readCompatReasoningEffortMap(model.compat)
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
//#endregion
|
|
682
|
-
//#region packages/ai/src/transports/openai-transport-params.ts
|
|
683
|
-
const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
|
|
684
|
-
const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
|
|
685
|
-
const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
686
|
-
function readToolPayloadField(record, field) {
|
|
687
|
-
try {
|
|
688
|
-
return Object.hasOwn(record, field) ? record[field] : void 0;
|
|
689
|
-
} catch {
|
|
623
|
+
/** Commit relocation only after finding an emitted carrier. */
|
|
624
|
+
function relocateNonBehavioralRegion(args) {
|
|
625
|
+
const text = sanitizeSurrogates(stripSystemPromptCacheBoundary(args.split.relocatable));
|
|
626
|
+
for (let index = args.systemParamIndex + 1; index < args.params.length; index++) {
|
|
627
|
+
const param = args.params[index];
|
|
628
|
+
if (!param || param.role !== "user") continue;
|
|
629
|
+
if (typeof param.content === "string") param.content = `${param.content}\n\n${text}`;
|
|
630
|
+
else if (Array.isArray(param.content)) param.content = [...param.content, {
|
|
631
|
+
type: "text",
|
|
632
|
+
text
|
|
633
|
+
}];
|
|
634
|
+
else continue;
|
|
635
|
+
const systemParam = args.params[args.systemParamIndex];
|
|
636
|
+
if (systemParam) systemParam.content = sanitizeSurrogates(stripSystemPromptCacheBoundary(args.split.remainingPrompt));
|
|
637
|
+
args.cacheOptOutIndexes?.add(index);
|
|
690
638
|
return;
|
|
691
639
|
}
|
|
692
640
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
if (
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
641
|
+
//#endregion
|
|
642
|
+
//#region packages/ai/src/transports/openai-completions-direct-policy.ts
|
|
643
|
+
function applyDirectCompletionsReasoningAndRouting(params, model, options, compat) {
|
|
644
|
+
const reasoningEffortMap = resolveOpenAIReasoningEffortMap(model);
|
|
645
|
+
const thinkingLevelMap = model.thinkingLevelMap;
|
|
646
|
+
const offReasoningEffort = reasoningEffortMap.off ?? model.thinkingLevelMap?.off;
|
|
647
|
+
const reasoningEffort = options?.reasoningEffort === void 0 ? offReasoningEffort ?? void 0 : reasoningEffortMap[options.reasoningEffort] ?? thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
|
|
648
|
+
const reasoningEnabled = reasoningEffort !== void 0 && reasoningEffort !== "none";
|
|
649
|
+
if (compat.thinkingFormat === "zai" && model.reasoning) params.thinking = reasoningEnabled ? {
|
|
650
|
+
type: "enabled",
|
|
651
|
+
clear_thinking: false
|
|
652
|
+
} : { type: "disabled" };
|
|
653
|
+
else if (compat.thinkingFormat === "qwen" && model.reasoning) params.enable_thinking = reasoningEnabled;
|
|
654
|
+
else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = {
|
|
655
|
+
enable_thinking: reasoningEnabled,
|
|
656
|
+
preserve_thinking: true
|
|
657
|
+
};
|
|
658
|
+
else if (compat.thinkingFormat === "deepseek" && model.reasoning) {
|
|
659
|
+
params.thinking = { type: reasoningEnabled ? "enabled" : "disabled" };
|
|
660
|
+
if (reasoningEnabled && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
661
|
+
} else if (compat.thinkingFormat === "openrouter" && model.reasoning) {
|
|
662
|
+
if (reasoningEnabled) params.reasoning = { effort: reasoningEffort };
|
|
663
|
+
else if (offReasoningEffort !== null) params.reasoning = { effort: offReasoningEffort ?? "none" };
|
|
664
|
+
} else if (compat.thinkingFormat === "together" && model.reasoning) {
|
|
665
|
+
params.reasoning = { enabled: reasoningEnabled };
|
|
666
|
+
if (reasoningEnabled && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
667
|
+
} else if (reasoningEnabled && model.reasoning && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
668
|
+
else if (model.reasoning && compat.supportsReasoningEffort) {
|
|
669
|
+
if (typeof offReasoningEffort === "string") params.reasoning_effort = offReasoningEffort;
|
|
670
|
+
}
|
|
671
|
+
if (compat.openRouterRouting) params.provider = compat.openRouterRouting;
|
|
672
|
+
if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
|
|
673
|
+
const routing = model.compat.vercelGatewayRouting;
|
|
674
|
+
if (routing.only || routing.order) {
|
|
675
|
+
const gatewayOptions = {};
|
|
676
|
+
if (routing.only) gatewayOptions.only = routing.only;
|
|
677
|
+
if (routing.order) gatewayOptions.order = routing.order;
|
|
678
|
+
params.providerOptions = { gateway: gatewayOptions };
|
|
710
679
|
}
|
|
711
|
-
return `hosted:${type}`;
|
|
712
680
|
}
|
|
713
|
-
const name = readCodeModePayloadToolName(tool);
|
|
714
|
-
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
|
|
715
681
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
return
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region packages/ai/src/transports/openai-completions-replay.ts
|
|
684
|
+
function isGoogleOpenAICompatModel(model) {
|
|
685
|
+
const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
|
|
686
|
+
return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
|
|
687
|
+
}
|
|
688
|
+
function requiresGoogleCompatToolCallThoughtSignature(model) {
|
|
689
|
+
return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
|
|
690
|
+
}
|
|
691
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
|
|
692
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
|
|
693
|
+
function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
|
|
694
|
+
return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
|
|
695
|
+
}
|
|
696
|
+
function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
|
|
697
|
+
if (!isGoogleOpenAICompatModel(model)) return;
|
|
698
|
+
const sigById = /* @__PURE__ */ new Map();
|
|
699
|
+
const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
|
|
700
|
+
for (const msg of context.messages ?? []) {
|
|
701
|
+
if (msg.role !== "assistant") continue;
|
|
702
|
+
const source = msg;
|
|
703
|
+
if (!Array.isArray(source.content)) continue;
|
|
704
|
+
for (const block of source.content) {
|
|
705
|
+
if (block.type !== "toolCall") continue;
|
|
706
|
+
const id = block.id;
|
|
707
|
+
const sig = block.thoughtSignature;
|
|
708
|
+
if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
|
|
709
|
+
const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
|
|
710
|
+
if (!isSameRoute && !fallbackSig) continue;
|
|
711
|
+
sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
|
|
731
712
|
}
|
|
732
713
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if (!Array.isArray(declarations)) continue;
|
|
752
|
-
const filtered = declarations.filter((declaration) => {
|
|
753
|
-
const declarationName = readCodeModePayloadToolName(declaration);
|
|
754
|
-
return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
|
|
755
|
-
});
|
|
756
|
-
if (filtered.length > 0) filteredGroups[key] = filtered;
|
|
714
|
+
}
|
|
715
|
+
if (sigById.size === 0 && !fallbackSig) return;
|
|
716
|
+
for (const message of outgoingMessages) {
|
|
717
|
+
const toolCalls = message.tool_calls;
|
|
718
|
+
if (!Array.isArray(toolCalls)) continue;
|
|
719
|
+
for (const toolCall of toolCalls) {
|
|
720
|
+
const id = toolCall.id;
|
|
721
|
+
if (typeof id !== "string") continue;
|
|
722
|
+
let sig = sigById.get(id) ?? fallbackSig;
|
|
723
|
+
if (typeof sig === "string" && sig.length > 0) {
|
|
724
|
+
if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
|
|
725
|
+
}
|
|
726
|
+
if (typeof sig !== "string" || sig.length === 0) continue;
|
|
727
|
+
const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
|
|
728
|
+
toolCall.extra_content = extra;
|
|
729
|
+
const google = extra.google && typeof extra.google === "object" ? extra.google : {};
|
|
730
|
+
extra.google = google;
|
|
731
|
+
google.thought_signature = sig;
|
|
757
732
|
}
|
|
758
|
-
|
|
759
|
-
});
|
|
760
|
-
if (beforeToolIdentities) observer?.({
|
|
761
|
-
beforeToolIdentities,
|
|
762
|
-
afterToolIdentities: readCodeModePayloadToolIdentities(payload)
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
function resolveCodeModeResponsesVisibleToolNames(context) {
|
|
766
|
-
return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
|
|
767
|
-
}
|
|
768
|
-
function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes, observer) {
|
|
769
|
-
if (!isRecord(payload)) return;
|
|
770
|
-
const tools = readToolPayloadField(payload, "tools");
|
|
771
|
-
if (!Array.isArray(tools)) return;
|
|
772
|
-
const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
|
|
773
|
-
payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
|
|
774
|
-
if (beforeToolIdentities) observer?.({
|
|
775
|
-
beforeToolIdentities,
|
|
776
|
-
afterToolIdentities: readCodeModePayloadToolIdentities(payload)
|
|
777
|
-
});
|
|
733
|
+
}
|
|
778
734
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
735
|
+
const COMPLETIONS_REASONING_REPLAY_FIELDS = [
|
|
736
|
+
"reasoning_details",
|
|
737
|
+
"reasoning_content",
|
|
738
|
+
"reasoning",
|
|
739
|
+
"reasoning_text"
|
|
740
|
+
];
|
|
741
|
+
function stripCompletionsReasoningReplayFields(record) {
|
|
742
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
|
|
743
|
+
}
|
|
744
|
+
function sanitizeOpenRouterReasoningReplayFields(record) {
|
|
745
|
+
const reasoningDetails = record.reasoning_details;
|
|
746
|
+
if (typeof reasoningDetails === "string") {
|
|
747
|
+
if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
|
|
748
|
+
delete record.reasoning_details;
|
|
749
|
+
} else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
|
|
750
|
+
if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
|
|
751
|
+
if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
|
|
752
|
+
const reasoningText = record.reasoning_text;
|
|
753
|
+
if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
|
|
754
|
+
if ("reasoning_text" in record) delete record.reasoning_text;
|
|
755
|
+
}
|
|
756
|
+
function sanitizeReasoningContentReplayFields(record) {
|
|
757
|
+
if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
|
|
758
|
+
delete record.reasoning_details;
|
|
759
|
+
delete record.reasoning;
|
|
760
|
+
delete record.reasoning_text;
|
|
761
|
+
}
|
|
762
|
+
const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
763
|
+
"deepseek-v4-flash",
|
|
764
|
+
"deepseek-v4-pro",
|
|
765
|
+
"kimi-for-coding",
|
|
766
|
+
"kimi-k2.5",
|
|
767
|
+
"kimi-k2.6",
|
|
768
|
+
"kimi-k2.7-code",
|
|
769
|
+
"kimi-k2.7-code-highspeed",
|
|
770
|
+
"kimi-k3",
|
|
771
|
+
"kimi-k2-thinking",
|
|
772
|
+
"kimi-k2-thinking-turbo",
|
|
773
|
+
"mimo-v2-pro",
|
|
774
|
+
"mimo-v2-omni",
|
|
775
|
+
"mimo-v2.5",
|
|
776
|
+
"mimo-v2.5-pro",
|
|
777
|
+
"mimo-v2.6-pro"
|
|
778
|
+
]);
|
|
779
|
+
const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
|
|
780
|
+
"-free",
|
|
781
|
+
"-paid",
|
|
782
|
+
"-trial"
|
|
783
|
+
];
|
|
784
|
+
function stripReasoningContentReplayTierSuffix(modelId) {
|
|
785
|
+
for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
|
|
786
|
+
return modelId;
|
|
787
|
+
}
|
|
788
|
+
function getReasoningContentReplayModelIdCandidates(modelId) {
|
|
789
|
+
if (typeof modelId !== "string") return [];
|
|
790
|
+
const normalized = modelId.trim().toLowerCase();
|
|
791
|
+
if (!normalized) return [];
|
|
792
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
793
|
+
const finalPart = parts[parts.length - 1] ?? normalized;
|
|
794
|
+
const candidates = [finalPart];
|
|
795
|
+
const colonParts = finalPart.split(":").filter(Boolean);
|
|
796
|
+
if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
|
|
797
|
+
const baseCount = candidates.length;
|
|
798
|
+
for (let index = 0; index < baseCount; index += 1) {
|
|
799
|
+
const candidate = candidates[index];
|
|
800
|
+
if (typeof candidate !== "string") continue;
|
|
801
|
+
const stripped = stripReasoningContentReplayTierSuffix(candidate);
|
|
802
|
+
if (stripped !== candidate) candidates.push(stripped);
|
|
803
|
+
}
|
|
804
|
+
return uniqueStrings(candidates.filter(Boolean));
|
|
786
805
|
}
|
|
787
|
-
function
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
provider: context.model.provider ?? null,
|
|
791
|
-
model: context.model.id ?? null,
|
|
792
|
-
diagnostics: diagnostics.map((entry) => ({
|
|
793
|
-
toolIndex: entry.toolIndex,
|
|
794
|
-
toolName: entry.toolName ?? null,
|
|
795
|
-
violations: entry.violations
|
|
796
|
-
}))
|
|
797
|
-
}));
|
|
806
|
+
function shouldPreserveReasoningContentReplay(model, compat) {
|
|
807
|
+
if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
|
|
808
|
+
return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
|
|
798
809
|
}
|
|
799
|
-
function
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
|
|
804
|
-
return true;
|
|
810
|
+
function shouldPreserveOpenRouterReasoningReplay(model) {
|
|
811
|
+
if (model.provider !== "openrouter") return true;
|
|
812
|
+
const normalizedModelId = model.id.trim().toLowerCase();
|
|
813
|
+
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
805
814
|
}
|
|
806
|
-
function
|
|
807
|
-
|
|
808
|
-
if (
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
provider: context.model.provider,
|
|
821
|
-
model: context.model.id,
|
|
822
|
-
incompatibleToolCount: diagnostics.length,
|
|
823
|
-
sample
|
|
824
|
-
}
|
|
825
|
-
};
|
|
826
|
-
});
|
|
815
|
+
function shouldTrustReasoningContentReplayMetadata(model) {
|
|
816
|
+
if (!model.reasoning) return false;
|
|
817
|
+
if (model.provider.trim().toLowerCase() === "openai") return false;
|
|
818
|
+
return shouldPreserveOpenRouterReasoningReplay(model);
|
|
819
|
+
}
|
|
820
|
+
function sanitizeCompletionsReasoningReplayFields(messages, options) {
|
|
821
|
+
if (!Array.isArray(messages)) return;
|
|
822
|
+
for (const msg of messages) {
|
|
823
|
+
if (!msg || typeof msg !== "object") continue;
|
|
824
|
+
const record = msg;
|
|
825
|
+
if (record.role !== "assistant") continue;
|
|
826
|
+
if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
|
|
827
|
+
else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
|
|
828
|
+
else stripCompletionsReasoningReplayFields(record);
|
|
827
829
|
}
|
|
828
|
-
return strict;
|
|
829
830
|
}
|
|
830
|
-
function
|
|
831
|
-
|
|
831
|
+
function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
832
|
+
injectToolCallThoughtSignatures(outgoingMessages, context, model);
|
|
833
|
+
sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
|
|
834
|
+
preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
|
|
835
|
+
preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
|
|
836
|
+
});
|
|
832
837
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
840
|
+
function isKnownOpenAICompletionsEndpoint(model) {
|
|
841
|
+
if (!model.baseUrl.trim()) return true;
|
|
842
|
+
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
843
|
+
if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
|
|
836
844
|
try {
|
|
837
|
-
|
|
838
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
839
|
-
if (url.hostname.toLowerCase() !== "chatgpt.com") return false;
|
|
840
|
-
const pathname = url.pathname.replace(/\/+$/u, "").toLowerCase();
|
|
841
|
-
return [
|
|
842
|
-
"/backend-api",
|
|
843
|
-
"/backend-api/v1",
|
|
844
|
-
"/backend-api/codex",
|
|
845
|
-
"/backend-api/codex/v1"
|
|
846
|
-
].includes(pathname);
|
|
845
|
+
return isAzureOpenAICompatibleHost(new URL(model.baseUrl).hostname.toLowerCase());
|
|
847
846
|
} catch {
|
|
848
847
|
return false;
|
|
849
848
|
}
|
|
850
849
|
}
|
|
851
|
-
function
|
|
852
|
-
return
|
|
850
|
+
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
851
|
+
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
853
852
|
}
|
|
854
|
-
function
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
853
|
+
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
854
|
+
if (options?.maxTokens) return {
|
|
855
|
+
maxTokens: options.maxTokens,
|
|
856
|
+
clampToModelMaxTokens: true
|
|
857
|
+
};
|
|
858
|
+
const paramsMaxTokens = resolveMaxTokensParam(model.params);
|
|
859
|
+
if (paramsMaxTokens) return {
|
|
860
|
+
maxTokens: paramsMaxTokens,
|
|
861
|
+
clampToModelMaxTokens: false
|
|
860
862
|
};
|
|
861
|
-
const resolvedHeaders = resolveProviderRequestPolicyConfig(model, {
|
|
862
|
-
provider: model.provider,
|
|
863
|
-
api: model.api,
|
|
864
|
-
baseUrl: model.baseUrl,
|
|
865
|
-
capability: "llm",
|
|
866
|
-
transport: "stream",
|
|
867
|
-
providerHeaders,
|
|
868
|
-
callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
|
|
869
|
-
precedence: "caller-wins"
|
|
870
|
-
}).headers ?? {};
|
|
871
|
-
if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
|
|
872
|
-
return resolvedHeaders;
|
|
873
|
-
}
|
|
874
|
-
function resolveOpenAISdkTimeoutMs(model, timeoutMs) {
|
|
875
|
-
return resolveModelRequestTimeoutMs(model, timeoutMs);
|
|
876
|
-
}
|
|
877
|
-
function buildOpenAISdkClientOptions(model) {
|
|
878
|
-
const timeout = resolveOpenAISdkTimeoutMs(model);
|
|
879
863
|
return {
|
|
880
|
-
|
|
881
|
-
|
|
864
|
+
maxTokens: model.maxTokens,
|
|
865
|
+
clampToModelMaxTokens: false
|
|
882
866
|
};
|
|
883
867
|
}
|
|
884
|
-
function
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
868
|
+
function resolveOpenAICompletionsModelMaxTokens(model) {
|
|
869
|
+
return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
|
|
870
|
+
}
|
|
871
|
+
const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
|
|
872
|
+
const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
|
|
873
|
+
function estimateOpenAICompletionsInputTokens(payload) {
|
|
874
|
+
let adjustedChars = 0;
|
|
875
|
+
adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
|
|
876
|
+
if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
|
|
877
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
|
|
878
|
+
} catch {
|
|
879
|
+
adjustedChars += 1024;
|
|
880
|
+
}
|
|
881
|
+
if (payload.response_format !== void 0) try {
|
|
882
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
|
|
883
|
+
} catch {
|
|
884
|
+
adjustedChars += 256;
|
|
885
|
+
}
|
|
886
|
+
return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
|
|
887
|
+
}
|
|
888
|
+
function estimateOpenAICompletionsMessagesChars(messages) {
|
|
889
|
+
if (!Array.isArray(messages)) return 0;
|
|
890
|
+
let adjustedChars = 0;
|
|
891
|
+
for (const message of messages) {
|
|
892
|
+
if (!message || typeof message !== "object") continue;
|
|
893
|
+
const record = message;
|
|
894
|
+
adjustedChars += estimateOpenAICompletionsContentChars(record.content);
|
|
895
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
|
|
896
|
+
if (record.tool_calls !== void 0) try {
|
|
897
|
+
adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
|
|
898
|
+
} catch {
|
|
899
|
+
adjustedChars += 256;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return adjustedChars;
|
|
903
|
+
}
|
|
904
|
+
function estimateOpenAICompletionsContentChars(value) {
|
|
905
|
+
if (typeof value === "string") return estimateStringChars(value);
|
|
906
|
+
if (!Array.isArray(value)) return 0;
|
|
907
|
+
let adjustedChars = 0;
|
|
908
|
+
for (const block of value) {
|
|
909
|
+
if (!block || typeof block !== "object") continue;
|
|
910
|
+
const record = block;
|
|
911
|
+
if (record.type === "image_url" || record.type === "input_image") {
|
|
912
|
+
adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
const text = record.text;
|
|
916
|
+
if (typeof text === "string") {
|
|
917
|
+
adjustedChars += estimateStringChars(text);
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
adjustedChars += estimateStringChars(JSON.stringify(block));
|
|
922
|
+
} catch {
|
|
923
|
+
adjustedChars += 256;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return adjustedChars;
|
|
927
|
+
}
|
|
928
|
+
function resolveOpenAICompletionsEffectiveContextTokens(model) {
|
|
929
|
+
const contextTokens = model.contextTokens;
|
|
930
|
+
if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
|
|
931
|
+
return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
|
|
932
|
+
}
|
|
933
|
+
function isQwenOpenAICompletionsThinkingFormat(format) {
|
|
934
|
+
return format === "qwen" || format === "qwen-chat-template";
|
|
935
|
+
}
|
|
936
|
+
function setQwenChatTemplateThinking(params, enabled) {
|
|
937
|
+
const existing = params.chat_template_kwargs;
|
|
938
|
+
params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
|
|
939
|
+
...existing,
|
|
940
|
+
enable_thinking: enabled
|
|
941
|
+
} : { enable_thinking: enabled };
|
|
942
|
+
}
|
|
943
|
+
function applyQwenOpenAICompletionsThinkingParams(params) {
|
|
944
|
+
if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
|
|
945
|
+
const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
|
|
946
|
+
if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
|
|
947
|
+
else params.payload.enable_thinking = enabled;
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
950
|
+
function applyTogetherOpenAICompletionsThinkingParams(params) {
|
|
951
|
+
if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
|
|
952
|
+
params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
|
|
953
|
+
}
|
|
954
|
+
function convertTools(tools, compat, model, mode) {
|
|
955
|
+
const projection = projectOpenAITools(tools);
|
|
956
|
+
const strict = mode === "direct" ? compat.supportsStrictMode ? false : void 0 : resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
|
|
957
|
+
transport: "stream",
|
|
958
|
+
supportsStrictMode: compat?.supportsStrictMode
|
|
959
|
+
}), {
|
|
960
|
+
transport: "completions",
|
|
961
|
+
model
|
|
962
|
+
});
|
|
888
963
|
return {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
964
|
+
projection,
|
|
965
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
966
|
+
const functionTool = {
|
|
967
|
+
name: tool.name,
|
|
968
|
+
description: tool.description,
|
|
969
|
+
parameters: mode === "direct" ? tool.parameters : normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
970
|
+
};
|
|
971
|
+
if (strict !== void 0) functionTool.strict = strict;
|
|
972
|
+
return {
|
|
973
|
+
type: "function",
|
|
974
|
+
function: functionTool
|
|
975
|
+
};
|
|
976
|
+
})
|
|
893
977
|
};
|
|
894
978
|
}
|
|
895
|
-
function
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
979
|
+
function buildOpenAICompletionsParams(model, context, options) {
|
|
980
|
+
return buildOpenAICompletionsRequest(model, context, options, { mode: "managed" });
|
|
981
|
+
}
|
|
982
|
+
function buildOpenAICompletionsRequest(model, context, options, policy) {
|
|
983
|
+
const resolvedPolicy = policy.mode === "direct" ? policy : {
|
|
984
|
+
...policy,
|
|
985
|
+
compat: getCompat(model)
|
|
986
|
+
};
|
|
987
|
+
const compat = resolvedPolicy.compat;
|
|
988
|
+
const managedCompat = resolvedPolicy.mode === "managed" ? resolvedPolicy.compat : void 0;
|
|
989
|
+
const endpointDetection = detectOpenAICompletionsCompat(model);
|
|
990
|
+
const compatDetection = policy.mode === "managed" ? endpointDetection : void 0;
|
|
991
|
+
const { endpointClass } = endpointDetection.capabilities;
|
|
992
|
+
const cacheRetention = policy.mode === "direct" ? policy.cacheRetention : resolveCacheRetention(options?.cacheRetention);
|
|
993
|
+
const cacheControl = resolveCompletionsCacheControl(compat, cacheRetention, endpointClass === "openrouter" || endpointClass === "default" && model.provider === "openrouter");
|
|
994
|
+
const markTools = endpointClass !== "modelstudio-native" && !(endpointClass === "default" && [
|
|
995
|
+
"modelstudio",
|
|
996
|
+
"dashscope",
|
|
997
|
+
"qwen"
|
|
998
|
+
].includes(model.provider));
|
|
999
|
+
const cacheOptOutIndexes = /* @__PURE__ */ new Set();
|
|
1000
|
+
let messages = convertMessages(model, context, compat, {
|
|
1001
|
+
cacheOptOutIndexes,
|
|
1002
|
+
preserveSystemPromptCacheBoundary: cacheControl !== void 0 && !managedCompat?.requiresStringContent
|
|
1003
|
+
});
|
|
1004
|
+
if (managedCompat) {
|
|
1005
|
+
applyCompletionsReplay(messages, context, model, managedCompat);
|
|
1006
|
+
if (managedCompat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
|
|
1007
|
+
if (managedCompat.requiresStringContent) messages = flattenCompletionMessagesToStringContent(messages);
|
|
1008
|
+
}
|
|
1009
|
+
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
1010
|
+
const params = {
|
|
1011
|
+
model: model.id,
|
|
1012
|
+
messages,
|
|
1013
|
+
stream: true,
|
|
1014
|
+
...resolveOpenAIPromptCacheParams(model, cacheRetention, compat)
|
|
906
1015
|
};
|
|
1016
|
+
if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
|
|
1017
|
+
if (compat.supportsStore) params.store = false;
|
|
1018
|
+
if (policy.mode === "direct" || compat.supportsPromptCacheKey && promptCacheKey) params.prompt_cache_key = compat.supportsPromptCacheKey ? promptCacheKey : void 0;
|
|
1019
|
+
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
1020
|
+
if (policy.mode === "managed" && options?.topP !== void 0) params.top_p = options.topP;
|
|
1021
|
+
const requestedResponseFormat = options?.responseFormat;
|
|
1022
|
+
const responseFormat = policy.mode === "direct" && requestedResponseFormat === void 0 ? void 0 : resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
1023
|
+
provider: model.provider,
|
|
1024
|
+
baseUrl: model.baseUrl,
|
|
1025
|
+
hasTools: () => Boolean(context.tools?.length)
|
|
1026
|
+
}) ? void 0 : requestedResponseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
1027
|
+
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
1028
|
+
if (policy.mode === "managed" && options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
1029
|
+
if (policy.mode === "managed" && options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
1030
|
+
if (policy.mode === "managed" && options?.seed !== void 0) params.seed = options.seed;
|
|
1031
|
+
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
1032
|
+
let directToolProjection;
|
|
1033
|
+
if (policy.mode === "direct" || supportsModelTools(model)) {
|
|
1034
|
+
if (context.tools) {
|
|
1035
|
+
const converted = convertTools(context.tools, compat, model, policy.mode);
|
|
1036
|
+
if (policy.mode === "direct") directToolProjection = converted.projection;
|
|
1037
|
+
if (converted.tools.length > 0 || policy.mode === "managed" && converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
|
|
1038
|
+
else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1039
|
+
if (policy.mode === "direct" && compat.zaiToolStream && converted.tools.length > 0) params.tool_stream = true;
|
|
1040
|
+
if (policy.mode === "managed" && options?.toolChoice) {
|
|
1041
|
+
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
|
|
1042
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1043
|
+
} else if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
|
|
1044
|
+
} else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1045
|
+
if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
|
|
1046
|
+
delete params.tools;
|
|
1047
|
+
delete params.tool_choice;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
if (policy.mode === "direct" && compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools);
|
|
1051
|
+
if (policy.mode === "direct" && options?.toolChoice) {
|
|
1052
|
+
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, directToolProjection ?? projectOpenAITools([]));
|
|
1053
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1054
|
+
}
|
|
1055
|
+
{
|
|
1056
|
+
const maxTokenBudget = policy.mode === "direct" ? {
|
|
1057
|
+
maxTokens: options?.maxTokens,
|
|
1058
|
+
clampToModelMaxTokens: true
|
|
1059
|
+
} : resolveOpenAICompletionsMaxTokens(model, options);
|
|
1060
|
+
const effectiveMaxTokens = maxTokenBudget.maxTokens;
|
|
1061
|
+
const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
|
|
1062
|
+
let clampedMaxTokens = effectiveMaxTokens;
|
|
1063
|
+
const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
|
|
1064
|
+
if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
|
|
1065
|
+
clampedMaxTokens = modelMaxTokens;
|
|
1066
|
+
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}`);
|
|
1067
|
+
}
|
|
1068
|
+
if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
|
|
1069
|
+
const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
|
|
1070
|
+
const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
|
|
1071
|
+
if (clampedMaxTokens > remainingBudget) {
|
|
1072
|
+
clampedMaxTokens = remainingBudget;
|
|
1073
|
+
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
if (policy.mode === "direct" ? options?.maxTokens : clampedMaxTokens) {
|
|
1077
|
+
if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1078
|
+
else params.max_completion_tokens = clampedMaxTokens;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (policy.mode === "direct") {
|
|
1082
|
+
applyDirectCompletionsReasoningAndRouting(params, model, options, compat);
|
|
1083
|
+
return params;
|
|
1084
|
+
}
|
|
1085
|
+
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
|
|
1086
|
+
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1087
|
+
model,
|
|
1088
|
+
effort: completionsReasoningEffort,
|
|
1089
|
+
fallbackMap: managedCompat?.reasoningEffortMap
|
|
1090
|
+
}) : void 0;
|
|
1091
|
+
const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
|
|
1092
|
+
const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
|
|
1093
|
+
const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
|
|
1094
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1095
|
+
modelReasoning: model.reasoning,
|
|
1096
|
+
payload: params,
|
|
1097
|
+
requestedEffort: completionsReasoningEffort
|
|
1098
|
+
});
|
|
1099
|
+
applyTogetherOpenAICompletionsThinkingParams({
|
|
1100
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1101
|
+
modelReasoning: model.reasoning,
|
|
1102
|
+
payload: params,
|
|
1103
|
+
requestedEffort: completionsReasoningEffort
|
|
1104
|
+
});
|
|
1105
|
+
if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
|
|
1106
|
+
else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
|
|
1107
|
+
else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
|
|
1108
|
+
if (compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools, !managedCompat?.requiresStringContent);
|
|
1109
|
+
return params;
|
|
907
1110
|
}
|
|
908
1111
|
//#endregion
|
|
909
1112
|
//#region packages/ai/src/transports/openai-completions-dsml.ts
|
|
@@ -1270,7 +1473,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
|
|
|
1270
1473
|
partial: output
|
|
1271
1474
|
});
|
|
1272
1475
|
}
|
|
1273
|
-
currentBlock
|
|
1476
|
+
appendAssistantThinking(currentBlock, reasoningDelta.text);
|
|
1274
1477
|
pushStreamEvent({
|
|
1275
1478
|
type: "thinking_delta",
|
|
1276
1479
|
contentIndex: blockIndex(),
|
|
@@ -1588,6 +1791,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
|
|
|
1588
1791
|
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
1589
1792
|
if (cooperativeScheduler) await cooperativeScheduler.afterEvent();
|
|
1590
1793
|
}
|
|
1794
|
+
throwIfModelStreamAborted(options?.signal);
|
|
1591
1795
|
if (!finishReason && (directMode || options?.sawStreamDONE?.() === false)) throw new Error("Stream ended without finish_reason");
|
|
1592
1796
|
flushReasoningTagTextPartitioner();
|
|
1593
1797
|
flushDeepSeekToolCallRecovererAtEnd();
|
|
@@ -1611,12 +1815,9 @@ async function processCompletionsStream(responseStream, output, model, stream, o
|
|
|
1611
1815
|
if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
|
|
1612
1816
|
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
1613
1817
|
}
|
|
1614
|
-
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
1615
|
-
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1616
|
-
}
|
|
1617
1818
|
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
1618
1819
|
if (!model.reasoning) return false;
|
|
1619
|
-
const effort =
|
|
1820
|
+
const effort = options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1620
1821
|
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
1621
1822
|
return true;
|
|
1622
1823
|
}
|
|
@@ -1625,4 +1826,4 @@ function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
|
1625
1826
|
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
1626
1827
|
}
|
|
1627
1828
|
//#endregion
|
|
1628
|
-
export {
|
|
1829
|
+
export { convertMessages as a, flattenCompletionMessagesToStringContent as c, canonicalizeMaxTokensParam as d, resolveMaxTokensParam as f, buildOpenAICompletionsRequest as i, stripCompletionMessagesToRoleContent as l, shouldEmitOpenAICompletionsReasoning as n, isAzureOpenAICompatibleHost as o, createDeepSeekTextFilter as p, buildOpenAICompletionsParams as r, finalizeOpenAICompletionsToolCalls as s, processCompletionsStream as t, applyCompletionsAnthropicCacheControl as u };
|