@openclaw/ai 2026.9.1 → 2026.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/anthropic-CZy5U0NY.mjs +376 -0
- package/dist/anthropic-payload-policy-wuRCb6MH.d.mts +85 -0
- package/dist/anthropic-stream-reducer-B_yo_7pf.mjs +1669 -0
- package/dist/{api-registry-Cs6HGNqY.d.mts → api-registry-CB1-1oQ2.d.mts} +2 -2
- package/dist/assistant-output-iqnlJCV2.mjs +16 -0
- package/dist/assistant-text-phase-C20rxWwP.mjs +55 -0
- package/dist/{azure-openai-responses-CN4Fy5zV.mjs → azure-openai-responses-Crpa40QR.mjs} +5 -5
- package/dist/{base64-CEFBpSkN.mjs → base64-D-su8YVo.mjs} +1 -27
- package/dist/{diagnostics-KhXK-QJI.mjs → diagnostics-dV98PqIy.mjs} +96 -21
- package/dist/diagnostics.d.mts +3 -1
- package/dist/diagnostics.mjs +3 -3
- package/dist/{event-stream-vK_7r3bj.d.mts → event-stream-C1Z2piyk.d.mts} +11 -3
- package/dist/{event-stream-uSMZJ3FA.mjs → event-stream-D8PARQfL.mjs} +48 -10
- package/dist/event-stream-I_GlBsuZ.d.mts +1 -0
- package/dist/event-stream.d.mts +2 -2
- package/dist/event-stream.mjs +1 -1
- package/dist/{github-copilot-headers-NCJtz9i0.mjs → github-copilot-headers-B37TB3rB.mjs} +3 -10
- package/dist/github-copilot-request-facts-BTEBMeOv.mjs +16 -0
- package/dist/{google-CC-nrwXg.mjs → google-BDPriaVe.mjs} +10 -10
- package/dist/google-messages-CVn9eFpF.mjs +449 -0
- package/dist/google-shared-BedY23XS.mjs +185 -0
- package/dist/{google-vertex-DCr0pyzQ.mjs → google-vertex-DMz8XQCn.mjs} +7 -6
- package/dist/{host-BIaiBURL.mjs → host-CWuF-sS3.mjs} +84 -34
- package/dist/{host-BztR4tQj.d.mts → host-DK3wmS3e.d.mts} +3 -3
- package/dist/host-policy-CAopLRKA.mjs +37 -0
- package/dist/{index-AfaxKT8w.d.mts → index-CQ6LTHw8.d.mts} +11 -5
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +5 -5
- package/dist/internal/anthropic.d.mts +14 -11
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/google-model-family.d.mts +5 -0
- package/dist/internal/google-model-family.mjs +15 -0
- package/dist/internal/openai-responses-payload-policy.d.mts +1 -1
- package/dist/internal/openai-responses-payload-policy.mjs +1 -1
- package/dist/internal/openai.d.mts +8 -103
- package/dist/internal/openai.mjs +10 -10
- package/dist/internal/retry-after.d.mts +2 -4
- package/dist/internal/retry-after.mjs +57 -8
- package/dist/internal/runtime.d.mts +7 -6
- package/dist/internal/runtime.mjs +8 -7
- package/dist/internal/shared.d.mts +14 -3
- package/dist/internal/shared.mjs +6 -4
- package/dist/internal/tool-schema.d.mts +63 -0
- package/dist/internal/tool-schema.mjs +3 -0
- package/dist/{json-parse-BuAJEbdW.mjs → json-parse-Dw_hnxsA.mjs} +1 -1
- package/dist/{mistral-B7etBd_H.mjs → mistral--m-Jm6VZ.mjs} +16 -38
- package/dist/model-transport-url-DKocrEsb.d.mts +28 -0
- package/dist/{openai-chatgpt-responses-BAJ4gq3i.mjs → openai-chatgpt-responses-nDZccFW-.mjs} +62 -54
- package/dist/openai-completions-KuoZyx0d.mjs +187 -0
- package/dist/{openai-completions-compat-4IjSBpr6.d.mts → openai-completions-compat-CXn3T1we.d.mts} +26 -4
- package/dist/{openai-completions-stream-DZjwK8vp.mjs → openai-completions-stream-BQk3SkLD.mjs} +621 -451
- package/dist/openai-prompt-cache-BI0rkM-5.mjs +220 -0
- package/dist/openai-prompt-cache-rrutvqxq.d.mts +16 -0
- package/dist/openai-provider-client-ha_WxTyj.mjs +24 -0
- package/dist/openai-reasoning-effort-BK7FbcLT.mjs +167 -0
- package/dist/{openai-responses-BEUlxfDu.mjs → openai-responses-D99dOzKI.mjs} +14 -32
- package/dist/{openai-responses-compaction-window-DO7yV7az.mjs → openai-responses-compaction-window-D5jbzCi3.mjs} +7 -159
- package/dist/{openai-responses-contracts-bPzSN_Ba.d.mts → openai-responses-contracts-B55afwRo.d.mts} +12 -4
- package/dist/{openai-responses-prompt-observer-internal-C7x4IK7r.mjs → openai-responses-prompt-observer-internal-tApyTLVu.mjs} +3 -3
- package/dist/{openai-responses-shared-DiAdpNAM.mjs → openai-responses-shared-ZyQEzS5i.mjs} +217 -212
- package/dist/openai-tool-projection-793cE3QX.d.mts +37 -0
- package/dist/{openai-tool-schema-BMiHFH36.mjs → openai-tool-schema-CzjyYXun.mjs} +53 -583
- package/dist/openai-tool-schema-ynZBgqW-.d.mts +38 -0
- package/dist/openai-transport-params-DNasp2fU.mjs +674 -0
- package/dist/{provider-error-B6EGq6gg.mjs → provider-error-C6TbKiey.mjs} +29 -13
- package/dist/{provider-options-CxFPtvh7.d.mts → provider-options-BXr9Ec83.d.mts} +19 -42
- package/dist/provider-replay-context-BuSUaAk5.mjs +21 -0
- package/dist/{provider-transcript-transform-WvJmFUAf.mjs → provider-transcript-transform-pPmUIwKt.mjs} +1 -1
- package/dist/provider-types-CAV0Og3m.d.mts +29 -0
- package/dist/provider-types.d.mts +6 -31
- package/dist/providers.d.mts +2 -2
- package/dist/providers.mjs +11 -11
- package/dist/{reasoning-tag-text-partitioner-C-4uedDb.mjs → reasoning-tag-text-partitioner-BQBi4B9d.mjs} +90 -35
- package/dist/record-coerce-DwRYMj3t.mjs +32 -0
- package/dist/retry-after-CdCURCVg.d.mts +15 -0
- package/dist/{sanitize-unicode-S6binQG-.mjs → sanitize-unicode-Bb-v9meu.mjs} +1 -1
- package/dist/session-affinity-CCH7eYdB.mjs +20 -0
- package/dist/{simple-options-0PLDyJ-d.mjs → simple-options-tcKOqnpF.mjs} +3 -3
- package/dist/{src-C8U7lkoa.mjs → src-B2Q_6G8V.mjs} +10 -2
- package/dist/{stream-first-event-timeout-DcNjoFQE.mjs → stream-first-event-timeout-2hfrquuz.mjs} +1 -1
- package/dist/string-coerce-fsri9iCu.mjs +34 -0
- package/dist/{string-normalization--fwJ4S2q.mjs → string-normalization-CmLIasuf.mjs} +1 -1
- package/dist/{tool-schema-json-projection-BtZiml7r.mjs → tool-schema-json-projection-ClptDdAO.mjs} +2 -38
- package/dist/{transport-stream-shared-CytPVLIg.mjs → transport-stream-shared-Cu3ZPhNW.mjs} +5 -5
- package/dist/{transport-stream-shared-BrvFTkoO.d.mts → transport-stream-shared-DOxpGJ-H.d.mts} +4 -4
- package/dist/transport-utils-CCooe-cr.mjs +121 -0
- package/dist/transports.d.mts +114 -41
- package/dist/transports.mjs +796 -1596
- package/dist/types-BADKjDBI.d.mts +1 -0
- package/dist/{types-DbrhszyQ.d.mts → types-Dy1q0CSu.d.mts} +101 -64
- package/dist/types.d.mts +6 -6
- package/dist/types.mjs +4 -4
- package/dist/utf16-slice-CvGodqok.mjs +29 -0
- package/dist/{validation-CJZtym2g.mjs → validation-BDzVDnTs.mjs} +9 -1
- package/dist/{validation-BOwtcl9X.d.mts → validation-CaFUZN9B.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +14 -4
- package/dist/anthropic-compaction-replay-OMJZ0uyo.mjs +0 -838
- package/dist/anthropic-hk7F7ptG.mjs +0 -883
- package/dist/anthropic-payload-policy-DBT1itQ-.d.mts +0 -51
- package/dist/event-stream-DeDhbCc5.d.mts +0 -1
- package/dist/google-shared-CjPY0hZM.mjs +0 -634
- package/dist/google-thinking-level-C-V3tecN.mjs +0 -9
- package/dist/openai-completions-D0QZ0AyB.mjs +0 -403
- package/dist/openai-prompt-cache-2uo_1OR1.d.mts +0 -7
- package/dist/openai-prompt-cache-mZTCdRPo.mjs +0 -12
- package/dist/transport-utils-CtuS1Upe.mjs +0 -138
- package/dist/types-B5EFUmXs.d.mts +0 -1
- package/dist/utf16-slice-qz3nsy87.mjs +0 -84
package/dist/{openai-completions-stream-DZjwK8vp.mjs → openai-completions-stream-BQk3SkLD.mjs}
RENAMED
|
@@ -1,72 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as sanitizeSurrogates } from "./sanitize-unicode-
|
|
3
|
-
import { a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { r as appendAssistantThinking } from "./event-stream-D8PARQfL.mjs";
|
|
2
|
+
import { t as sanitizeSurrogates } from "./sanitize-unicode-Bb-v9meu.mjs";
|
|
3
|
+
import { a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload } from "./host-CWuF-sS3.mjs";
|
|
4
|
+
import { o as isRecord } from "./record-coerce-DwRYMj3t.mjs";
|
|
5
|
+
import { t as truncateUtf16Safe } from "./utf16-slice-CvGodqok.mjs";
|
|
6
|
+
import { d as shouldOmitOllamaCompatResponseFormat, i as detectOpenAICompletionsCompat, r as resolveOpenAIPromptCacheParams, u as resolveOpenAICompletionsResponseFormat } from "./openai-prompt-cache-BI0rkM-5.mjs";
|
|
7
|
+
import { r as emitModelTransportDebug } from "./diagnostics-dV98PqIy.mjs";
|
|
8
|
+
import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-BQBi4B9d.mjs";
|
|
9
|
+
import { i as asNonNegativeFiniteNumber } from "./base64-D-su8YVo.mjs";
|
|
10
|
+
import { l as supportsModelTools, r as estimateStringChars } from "./transport-utils-CCooe-cr.mjs";
|
|
11
|
+
import { n as uniqueStrings } from "./string-normalization-CmLIasuf.mjs";
|
|
12
|
+
import { i as resolveProviderEndpoint, r as resolveOpenAIStrictToolSetting } from "./host-policy-CAopLRKA.mjs";
|
|
13
|
+
import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
|
|
14
|
+
import { d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, u as splitSystemPromptCacheBoundary } from "./simple-options-tcKOqnpF.mjs";
|
|
15
|
+
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-DNasp2fU.mjs";
|
|
16
|
+
import { s as finalizeTerminalToolCallArguments } from "./transport-stream-shared-Cu3ZPhNW.mjs";
|
|
17
|
+
import { a as tagUnresolvedTextAsCommentary, i as tagPendingCommentaryText, n as rememberPendingCommentaryTags, r as tagInterruptedTextPhases, t as clearPendingCommentaryText } from "./assistant-text-phase-C20rxWwP.mjs";
|
|
18
|
+
import { r as parseStreamingJson, t as createToolArgumentPreviewSchedule } from "./json-parse-Dw_hnxsA.mjs";
|
|
10
19
|
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { t as
|
|
20
|
+
import { a as withFirstStreamEventTimeout } from "./stream-first-event-timeout-2hfrquuz.mjs";
|
|
21
|
+
import { t as transformProviderMessages } from "./provider-transcript-transform-pPmUIwKt.mjs";
|
|
22
|
+
import { n as isOpenAIGpt55Model, o as resolveOpenAIReasoningEffortForModel, r as isOpenAIGpt56Model, t as isOpenAIGpt54MiniModel } from "./openai-reasoning-effort-BK7FbcLT.mjs";
|
|
23
|
+
import { r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-CzjyYXun.mjs";
|
|
24
|
+
import { isGoogleGemini3FlashModel, isGoogleGemini3ProModel } from "./internal/google-model-family.mjs";
|
|
14
25
|
import { t as mapOpenAIStopReason } from "./openai-stop-reason-Drnn_6Qj.mjs";
|
|
15
|
-
import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-C-4uedDb.mjs";
|
|
16
26
|
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
27
|
//#region packages/ai/src/transports/deepseek-dsml-grammar.ts
|
|
71
28
|
const DEEPSEEK_DSML_MARKERS = [
|
|
72
29
|
"|",
|
|
@@ -162,141 +119,132 @@ function longestDsmlOpenPrefixSuffixLength(text) {
|
|
|
162
119
|
return 0;
|
|
163
120
|
}
|
|
164
121
|
//#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;
|
|
122
|
+
//#region packages/ai/src/transports/model-max-tokens-params.ts
|
|
123
|
+
/**
|
|
124
|
+
* Max-token parameter normalization across provider/native naming variants.
|
|
125
|
+
* Callers canonicalize aliases before dispatch so payloads cannot carry
|
|
126
|
+
* conflicting limits.
|
|
127
|
+
*/
|
|
128
|
+
const MAX_TOKENS_PARAM_KEYS = [
|
|
129
|
+
"maxTokens",
|
|
130
|
+
"max_completion_tokens",
|
|
131
|
+
"max_tokens"
|
|
132
|
+
];
|
|
133
|
+
/** Resolve the first supported max-token parameter present in a params object. */
|
|
134
|
+
function resolveMaxTokensParam(params) {
|
|
135
|
+
if (!params) return;
|
|
136
|
+
for (const key of MAX_TOKENS_PARAM_KEYS) {
|
|
137
|
+
const resolved = asNonNegativeFiniteNumber(params[key]);
|
|
138
|
+
if (resolved !== void 0) return resolved;
|
|
185
139
|
}
|
|
186
140
|
}
|
|
187
141
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
142
|
+
* Canonicalize merged params to `maxTokens`, preserving source precedence from
|
|
143
|
+
* left to right across the provided source objects.
|
|
190
144
|
*/
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
name: JSON_SCHEMA_RESPONSE_FORMAT_NAME,
|
|
201
|
-
schema: responseFormat
|
|
202
|
-
}
|
|
203
|
-
};
|
|
145
|
+
function canonicalizeMaxTokensParam(params) {
|
|
146
|
+
let resolved;
|
|
147
|
+
for (const source of params.sources) {
|
|
148
|
+
const sourceValue = resolveMaxTokensParam(source);
|
|
149
|
+
if (sourceValue !== void 0) resolved = sourceValue;
|
|
150
|
+
}
|
|
151
|
+
if (resolved === void 0) return;
|
|
152
|
+
for (const key of MAX_TOKENS_PARAM_KEYS) delete params.merged[key];
|
|
153
|
+
params.merged.maxTokens = resolved;
|
|
204
154
|
}
|
|
205
155
|
//#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");
|
|
156
|
+
//#region packages/ai/src/transports/openai-completions-cache-control.ts
|
|
157
|
+
const shapedPayloads = /* @__PURE__ */ new WeakSet();
|
|
158
|
+
function resolveCompletionsCacheControl(compat, retention, openRouterRoute) {
|
|
159
|
+
if (compat.cacheControlFormat !== "anthropic" || retention === "none") return;
|
|
227
160
|
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")
|
|
161
|
+
type: "ephemeral",
|
|
162
|
+
...retention === "long" && compat.supportsLongCacheRetention && (openRouterRoute || compat.configuredSupportsLongCacheRetention === true) ? { ttl: "1h" } : {}
|
|
242
163
|
};
|
|
243
164
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
165
|
+
/** Shared Chat Completions policy; repeated wrapper application preserves existing checkpoints. */
|
|
166
|
+
function applyCompletionsAnthropicCacheControl(payload, cacheControl = { type: "ephemeral" }, cacheOptOutIndexes = /* @__PURE__ */ new Set(), markTools = true, markMessages = true) {
|
|
167
|
+
if (shapedPayloads.has(payload)) return;
|
|
168
|
+
shapedPayloads.add(payload);
|
|
169
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
170
|
+
const tools = Array.isArray(payload.tools) ? payload.tools.filter(isRecord) : [];
|
|
171
|
+
const blocks = messages.filter(isRecord).flatMap((message) => Array.isArray(message.content) ? message.content.filter(isRecord) : []);
|
|
172
|
+
for (const block of [...tools, ...blocks]) delete block.cache_control;
|
|
173
|
+
if (!cacheControl) return;
|
|
174
|
+
const markText = (message, splitBoundary) => {
|
|
175
|
+
if (typeof message.content === "string" && message.content) message.content = [{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: message.content
|
|
178
|
+
}];
|
|
179
|
+
const content = message.content;
|
|
180
|
+
if (!Array.isArray(content)) return false;
|
|
181
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
182
|
+
const block = content[i];
|
|
183
|
+
if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string" || !block.text) continue;
|
|
184
|
+
const split = splitBoundary ? splitSystemPromptCacheBoundary(block.text) : void 0;
|
|
185
|
+
if (split) {
|
|
186
|
+
content.splice(i, 1, ...split.stablePrefix ? [{
|
|
187
|
+
type: "text",
|
|
188
|
+
text: split.stablePrefix,
|
|
189
|
+
cache_control: cacheControl
|
|
190
|
+
}] : [], ...split.dynamicSuffix ? [{
|
|
191
|
+
type: "text",
|
|
192
|
+
text: split.dynamicSuffix
|
|
193
|
+
}] : []);
|
|
194
|
+
if (!split.stablePrefix) return false;
|
|
195
|
+
} else block.cache_control = cacheControl;
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
266
199
|
};
|
|
200
|
+
const lastTool = tools.at(-1);
|
|
201
|
+
if (markTools && lastTool) lastTool.cache_control = cacheControl;
|
|
202
|
+
if (!markMessages) return;
|
|
203
|
+
const system = messages.find((message) => isRecord(message) && (message.role === "system" || message.role === "developer"));
|
|
204
|
+
if (isRecord(system)) markText(system, true);
|
|
205
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
206
|
+
const message = messages[i];
|
|
207
|
+
if (!cacheOptOutIndexes.has(i) && isRecord(message) && (message.role === "user" || message.role === "tool") && markText(message, false)) return;
|
|
208
|
+
}
|
|
267
209
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region packages/ai/src/transports/openai-completions-string-content.ts
|
|
212
|
+
/**
|
|
213
|
+
* OpenAI Chat Completions compatibility helpers. Some providers only accept
|
|
214
|
+
* role/content messages with plain string content instead of text block arrays.
|
|
215
|
+
*/
|
|
216
|
+
function flattenStringOnlyCompletionContent(content) {
|
|
217
|
+
if (!Array.isArray(content)) return content;
|
|
218
|
+
const textParts = [];
|
|
219
|
+
for (const item of content) {
|
|
220
|
+
if (!item || typeof item !== "object" || item.type !== "text" || typeof item.text !== "string") return content;
|
|
221
|
+
textParts.push(item.text);
|
|
222
|
+
}
|
|
223
|
+
return textParts.join("\n");
|
|
224
|
+
}
|
|
225
|
+
/** Flatten string-only text block content arrays into newline-joined strings. */
|
|
226
|
+
function flattenCompletionMessagesToStringContent(messages) {
|
|
227
|
+
return messages.map((message) => {
|
|
228
|
+
if (!message || typeof message !== "object") return message;
|
|
229
|
+
const content = message.content;
|
|
230
|
+
const flattenedContent = flattenStringOnlyCompletionContent(content);
|
|
231
|
+
if (flattenedContent === content) return message;
|
|
232
|
+
return {
|
|
233
|
+
...message,
|
|
234
|
+
content: flattenedContent
|
|
235
|
+
};
|
|
236
|
+
});
|
|
272
237
|
}
|
|
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
|
-
};
|
|
238
|
+
/** Strip completion messages to role/content fields for strict providers. */
|
|
239
|
+
function stripCompletionMessagesToRoleContent(messages) {
|
|
240
|
+
return messages.map((message) => {
|
|
241
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) return message;
|
|
242
|
+
const record = message;
|
|
243
|
+
const stripped = {};
|
|
244
|
+
if (Object.hasOwn(record, "role")) stripped.role = record.role;
|
|
245
|
+
if (Object.hasOwn(record, "content")) stripped.content = record.content;
|
|
246
|
+
return stripped;
|
|
247
|
+
});
|
|
300
248
|
}
|
|
301
249
|
//#endregion
|
|
302
250
|
//#region packages/ai/src/providers/openai-completions-tool-calls.ts
|
|
@@ -481,17 +429,18 @@ function finalizeOpenAICompletionsToolCalls(output, options = {}) {
|
|
|
481
429
|
}
|
|
482
430
|
}
|
|
483
431
|
//#endregion
|
|
432
|
+
//#region packages/ai/src/transports/openai-completions-host.ts
|
|
433
|
+
/**
|
|
434
|
+
* Chat Completions accepts Azure AI Foundry hosts in addition to traditional
|
|
435
|
+
* Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
|
|
436
|
+
* which intentionally excludes the .services.ai.azure.com Foundry suffix.
|
|
437
|
+
*/
|
|
438
|
+
function isAzureOpenAICompatibleHost(hostname) {
|
|
439
|
+
return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
484
442
|
//#region packages/ai/src/openai-completions-messages.ts
|
|
485
443
|
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
444
|
function sanitizeToolResultText(text, fallback) {
|
|
496
445
|
const sanitized = sanitizeSurrogates(text);
|
|
497
446
|
return sanitized.trim().length > 0 ? sanitized : fallback;
|
|
@@ -562,25 +511,30 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
562
511
|
role: "assistant",
|
|
563
512
|
content: compat.requiresAssistantAfterToolResult ? "" : null
|
|
564
513
|
};
|
|
565
|
-
const
|
|
514
|
+
const assistantTexts = [];
|
|
515
|
+
const nonEmptyThinkingBlocks = [];
|
|
516
|
+
const toolCalls = [];
|
|
517
|
+
msg.content.forEach((block) => {
|
|
518
|
+
if (block.type === "text" && block.text.trim().length > 0) assistantTexts.push(sanitizeSurrogates(block.text));
|
|
519
|
+
else if (block.type === "thinking" && block.thinking.trim().length > 0) nonEmptyThinkingBlocks.push(block);
|
|
520
|
+
else if (block.type === "toolCall") toolCalls.push(block);
|
|
521
|
+
});
|
|
522
|
+
if (nonEmptyThinkingBlocks.length > 0 && compat.requiresThinkingAsText) assistantMsg.content = [{
|
|
566
523
|
type: "text",
|
|
567
|
-
text: sanitizeSurrogates(block.
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
else {
|
|
577
|
-
if (assistantText.length > 0) assistantMsg.content = assistantText;
|
|
524
|
+
text: nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n\n")
|
|
525
|
+
}, ...assistantTexts.map((text) => ({
|
|
526
|
+
type: "text",
|
|
527
|
+
text
|
|
528
|
+
}))];
|
|
529
|
+
else {
|
|
530
|
+
const assistantText = assistantTexts.join("\n");
|
|
531
|
+
if (assistantText.length > 0) assistantMsg.content = assistantText;
|
|
532
|
+
if (nonEmptyThinkingBlocks.length > 0) {
|
|
578
533
|
let signature = nonEmptyThinkingBlocks.at(0)?.thinkingSignature;
|
|
579
534
|
if (model.provider === "opencode-go" && signature === "reasoning") signature = "reasoning_content";
|
|
580
535
|
if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => block.thinking).join("\n");
|
|
581
536
|
}
|
|
582
|
-
}
|
|
583
|
-
const toolCalls = msg.content.filter(isToolCallBlock);
|
|
537
|
+
}
|
|
584
538
|
if (toolCalls.length > 0) {
|
|
585
539
|
assistantMsg.tool_calls = toolCalls.map((toolCall) => ({
|
|
586
540
|
id: toolCall.id,
|
|
@@ -604,17 +558,17 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
604
558
|
}
|
|
605
559
|
if (compat.requiresReasoningContentOnAssistantMessages && model.reasoning && assistantMsg.reasoning_content === void 0) assistantMsg.reasoning_content = "";
|
|
606
560
|
const content = assistantMsg.content;
|
|
607
|
-
if (!(content !== null && content !== void 0 &&
|
|
561
|
+
if (!(content !== null && content !== void 0 && content.length > 0) && !assistantMsg.tool_calls) continue;
|
|
608
562
|
params.push(assistantMsg);
|
|
609
563
|
} else if (msg.role === "toolResult") {
|
|
610
|
-
const
|
|
564
|
+
const imageContentParts = [];
|
|
611
565
|
let j = i;
|
|
612
566
|
while (j < transformedMessages.length) {
|
|
613
567
|
const toolMsg = transformedMessages.at(j);
|
|
614
568
|
if (toolMsg?.role !== "toolResult") break;
|
|
615
569
|
const textResult = extractToolResultText(toolMsg.content);
|
|
616
570
|
const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
|
|
617
|
-
const
|
|
571
|
+
const images = toolMsg.content.filter(isImageWithMediaPayload);
|
|
618
572
|
const toolResultMsg = {
|
|
619
573
|
role: "tool",
|
|
620
574
|
content: sanitizeToolResultText(textResult, mediaPlaceholder ?? EMPTY_TOOL_RESULT_TEXT),
|
|
@@ -622,8 +576,13 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
622
576
|
};
|
|
623
577
|
if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
|
|
624
578
|
params.push(toolResultMsg);
|
|
625
|
-
if (
|
|
626
|
-
|
|
579
|
+
if (images.length > 0 && model.input.includes("image")) {
|
|
580
|
+
const boundedToolName = sanitizeSurrogates(truncateUtf16Safe(toolMsg.toolName ?? "", 64));
|
|
581
|
+
imageContentParts.push({
|
|
582
|
+
type: "text",
|
|
583
|
+
text: `Image(s) from tool result #${j - i + 1}${boundedToolName ? ` (${boundedToolName})` : ""}:`
|
|
584
|
+
});
|
|
585
|
+
for (const block of images) imageContentParts.push({
|
|
627
586
|
type: "image_url",
|
|
628
587
|
image_url: { url: `data:${block.mimeType};base64,${block.data}` }
|
|
629
588
|
});
|
|
@@ -631,17 +590,14 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
631
590
|
j += 1;
|
|
632
591
|
}
|
|
633
592
|
i = j - 1;
|
|
634
|
-
if (
|
|
593
|
+
if (imageContentParts.length > 0) {
|
|
635
594
|
if (compat.requiresAssistantAfterToolResult) params.push({
|
|
636
595
|
role: "assistant",
|
|
637
596
|
content: "I have processed the tool results."
|
|
638
597
|
});
|
|
639
598
|
params.push({
|
|
640
599
|
role: "user",
|
|
641
|
-
content:
|
|
642
|
-
type: "text",
|
|
643
|
-
text: "Attached image(s) from tool result:"
|
|
644
|
-
}, ...imageBlocks]
|
|
600
|
+
content: imageContentParts
|
|
645
601
|
});
|
|
646
602
|
lastRole = "user";
|
|
647
603
|
} else lastRole = "toolResult";
|
|
@@ -652,258 +608,474 @@ function convertMessages(model, context, compat, options = {}) {
|
|
|
652
608
|
return params;
|
|
653
609
|
}
|
|
654
610
|
//#endregion
|
|
655
|
-
//#region packages/ai/src/transports/openai-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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)
|
|
611
|
+
//#region packages/ai/src/transports/openai-completions-direct-policy.ts
|
|
612
|
+
function applyDirectCompletionsReasoningAndRouting(params, model, options, compat) {
|
|
613
|
+
const reasoningEffortMap = resolveOpenAIReasoningEffortMap(model);
|
|
614
|
+
const thinkingLevelMap = model.thinkingLevelMap;
|
|
615
|
+
const offReasoningEffort = reasoningEffortMap.off ?? model.thinkingLevelMap?.off;
|
|
616
|
+
const reasoningEffort = options?.reasoningEffort === void 0 ? offReasoningEffort ?? void 0 : reasoningEffortMap[options.reasoningEffort] ?? thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
|
|
617
|
+
const reasoningEnabled = reasoningEffort !== void 0 && reasoningEffort !== "none";
|
|
618
|
+
if (compat.thinkingFormat === "zai" && model.reasoning) params.thinking = reasoningEnabled ? {
|
|
619
|
+
type: "enabled",
|
|
620
|
+
clear_thinking: false
|
|
621
|
+
} : { type: "disabled" };
|
|
622
|
+
else if (compat.thinkingFormat === "qwen" && model.reasoning) params.enable_thinking = reasoningEnabled;
|
|
623
|
+
else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = {
|
|
624
|
+
enable_thinking: reasoningEnabled,
|
|
625
|
+
preserve_thinking: true
|
|
679
626
|
};
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
|
|
627
|
+
else if (compat.thinkingFormat === "deepseek" && model.reasoning) {
|
|
628
|
+
params.thinking = { type: reasoningEnabled ? "enabled" : "disabled" };
|
|
629
|
+
if (reasoningEnabled && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
630
|
+
} else if (compat.thinkingFormat === "openrouter" && model.reasoning) {
|
|
631
|
+
if (reasoningEnabled) params.reasoning = { effort: reasoningEffort };
|
|
632
|
+
else if (offReasoningEffort !== null) params.reasoning = { effort: offReasoningEffort ?? "none" };
|
|
633
|
+
} else if (compat.thinkingFormat === "together" && model.reasoning) {
|
|
634
|
+
params.reasoning = { enabled: reasoningEnabled };
|
|
635
|
+
if (reasoningEnabled && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
636
|
+
} else if (reasoningEnabled && model.reasoning && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
637
|
+
else if (model.reasoning && compat.supportsReasoningEffort) {
|
|
638
|
+
if (typeof offReasoningEffort === "string") params.reasoning_effort = offReasoningEffort;
|
|
691
639
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
return typeof fnName === "string" ? fnName : void 0;
|
|
701
|
-
}
|
|
702
|
-
function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
|
|
703
|
-
if (!isRecord(tool)) return;
|
|
704
|
-
const type = readToolPayloadField(tool, "type");
|
|
705
|
-
if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
|
|
706
|
-
try {
|
|
707
|
-
if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
|
|
708
|
-
} catch {
|
|
709
|
-
return false;
|
|
640
|
+
if (compat.openRouterRouting) params.provider = compat.openRouterRouting;
|
|
641
|
+
if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
|
|
642
|
+
const routing = model.compat.vercelGatewayRouting;
|
|
643
|
+
if (routing.only || routing.order) {
|
|
644
|
+
const gatewayOptions = {};
|
|
645
|
+
if (routing.only) gatewayOptions.only = routing.only;
|
|
646
|
+
if (routing.order) gatewayOptions.order = routing.order;
|
|
647
|
+
params.providerOptions = { gateway: gatewayOptions };
|
|
710
648
|
}
|
|
711
|
-
return `hosted:${type}`;
|
|
712
649
|
}
|
|
713
|
-
const name = readCodeModePayloadToolName(tool);
|
|
714
|
-
return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
|
|
715
650
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
return
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region packages/ai/src/transports/openai-completions-replay.ts
|
|
653
|
+
function isGoogleOpenAICompatModel(model) {
|
|
654
|
+
const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
|
|
655
|
+
return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
|
|
656
|
+
}
|
|
657
|
+
function requiresGoogleCompatToolCallThoughtSignature(model) {
|
|
658
|
+
return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
|
|
659
|
+
}
|
|
660
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
|
|
661
|
+
const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
|
|
662
|
+
function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
|
|
663
|
+
return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
|
|
664
|
+
}
|
|
665
|
+
function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
|
|
666
|
+
if (!isGoogleOpenAICompatModel(model)) return;
|
|
667
|
+
const sigById = /* @__PURE__ */ new Map();
|
|
668
|
+
const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
|
|
669
|
+
for (const msg of context.messages ?? []) {
|
|
670
|
+
if (msg.role !== "assistant") continue;
|
|
671
|
+
const source = msg;
|
|
672
|
+
if (!Array.isArray(source.content)) continue;
|
|
673
|
+
for (const block of source.content) {
|
|
674
|
+
if (block.type !== "toolCall") continue;
|
|
675
|
+
const id = block.id;
|
|
676
|
+
const sig = block.thoughtSignature;
|
|
677
|
+
if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
|
|
678
|
+
const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
|
|
679
|
+
if (!isSameRoute && !fallbackSig) continue;
|
|
680
|
+
sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
|
|
731
681
|
}
|
|
732
682
|
}
|
|
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;
|
|
683
|
+
}
|
|
684
|
+
if (sigById.size === 0 && !fallbackSig) return;
|
|
685
|
+
for (const message of outgoingMessages) {
|
|
686
|
+
const toolCalls = message.tool_calls;
|
|
687
|
+
if (!Array.isArray(toolCalls)) continue;
|
|
688
|
+
for (const toolCall of toolCalls) {
|
|
689
|
+
const id = toolCall.id;
|
|
690
|
+
if (typeof id !== "string") continue;
|
|
691
|
+
let sig = sigById.get(id) ?? fallbackSig;
|
|
692
|
+
if (typeof sig === "string" && sig.length > 0) {
|
|
693
|
+
if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
|
|
694
|
+
}
|
|
695
|
+
if (typeof sig !== "string" || sig.length === 0) continue;
|
|
696
|
+
const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
|
|
697
|
+
toolCall.extra_content = extra;
|
|
698
|
+
const google = extra.google && typeof extra.google === "object" ? extra.google : {};
|
|
699
|
+
extra.google = google;
|
|
700
|
+
google.thought_signature = sig;
|
|
757
701
|
}
|
|
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
|
-
});
|
|
702
|
+
}
|
|
778
703
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
704
|
+
const COMPLETIONS_REASONING_REPLAY_FIELDS = [
|
|
705
|
+
"reasoning_details",
|
|
706
|
+
"reasoning_content",
|
|
707
|
+
"reasoning",
|
|
708
|
+
"reasoning_text"
|
|
709
|
+
];
|
|
710
|
+
function stripCompletionsReasoningReplayFields(record) {
|
|
711
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
|
|
712
|
+
}
|
|
713
|
+
function sanitizeOpenRouterReasoningReplayFields(record) {
|
|
714
|
+
const reasoningDetails = record.reasoning_details;
|
|
715
|
+
if (typeof reasoningDetails === "string") {
|
|
716
|
+
if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
|
|
717
|
+
delete record.reasoning_details;
|
|
718
|
+
} else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
|
|
719
|
+
if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
|
|
720
|
+
if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
|
|
721
|
+
const reasoningText = record.reasoning_text;
|
|
722
|
+
if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
|
|
723
|
+
if ("reasoning_text" in record) delete record.reasoning_text;
|
|
724
|
+
}
|
|
725
|
+
function sanitizeReasoningContentReplayFields(record) {
|
|
726
|
+
if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
|
|
727
|
+
delete record.reasoning_details;
|
|
728
|
+
delete record.reasoning;
|
|
729
|
+
delete record.reasoning_text;
|
|
730
|
+
}
|
|
731
|
+
const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
732
|
+
"deepseek-v4-flash",
|
|
733
|
+
"deepseek-v4-pro",
|
|
734
|
+
"kimi-for-coding",
|
|
735
|
+
"kimi-k2.5",
|
|
736
|
+
"kimi-k2.6",
|
|
737
|
+
"kimi-k2.7-code",
|
|
738
|
+
"kimi-k2.7-code-highspeed",
|
|
739
|
+
"kimi-k3",
|
|
740
|
+
"kimi-k2-thinking",
|
|
741
|
+
"kimi-k2-thinking-turbo",
|
|
742
|
+
"mimo-v2-pro",
|
|
743
|
+
"mimo-v2-omni",
|
|
744
|
+
"mimo-v2.5",
|
|
745
|
+
"mimo-v2.5-pro",
|
|
746
|
+
"mimo-v2.6-pro"
|
|
747
|
+
]);
|
|
748
|
+
const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
|
|
749
|
+
"-free",
|
|
750
|
+
"-paid",
|
|
751
|
+
"-trial"
|
|
752
|
+
];
|
|
753
|
+
function stripReasoningContentReplayTierSuffix(modelId) {
|
|
754
|
+
for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
|
|
755
|
+
return modelId;
|
|
756
|
+
}
|
|
757
|
+
function getReasoningContentReplayModelIdCandidates(modelId) {
|
|
758
|
+
if (typeof modelId !== "string") return [];
|
|
759
|
+
const normalized = modelId.trim().toLowerCase();
|
|
760
|
+
if (!normalized) return [];
|
|
761
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
762
|
+
const finalPart = parts[parts.length - 1] ?? normalized;
|
|
763
|
+
const candidates = [finalPart];
|
|
764
|
+
const colonParts = finalPart.split(":").filter(Boolean);
|
|
765
|
+
if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
|
|
766
|
+
const baseCount = candidates.length;
|
|
767
|
+
for (let index = 0; index < baseCount; index += 1) {
|
|
768
|
+
const candidate = candidates[index];
|
|
769
|
+
if (typeof candidate !== "string") continue;
|
|
770
|
+
const stripped = stripReasoningContentReplayTierSuffix(candidate);
|
|
771
|
+
if (stripped !== candidate) candidates.push(stripped);
|
|
772
|
+
}
|
|
773
|
+
return uniqueStrings(candidates.filter(Boolean));
|
|
786
774
|
}
|
|
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
|
-
}));
|
|
775
|
+
function shouldPreserveReasoningContentReplay(model, compat) {
|
|
776
|
+
if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
|
|
777
|
+
return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
|
|
798
778
|
}
|
|
799
|
-
function
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
|
|
804
|
-
return true;
|
|
779
|
+
function shouldPreserveOpenRouterReasoningReplay(model) {
|
|
780
|
+
if (model.provider !== "openrouter") return true;
|
|
781
|
+
const normalizedModelId = model.id.trim().toLowerCase();
|
|
782
|
+
return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
|
|
805
783
|
}
|
|
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
|
-
});
|
|
784
|
+
function shouldTrustReasoningContentReplayMetadata(model) {
|
|
785
|
+
if (!model.reasoning) return false;
|
|
786
|
+
if (model.provider.trim().toLowerCase() === "openai") return false;
|
|
787
|
+
return shouldPreserveOpenRouterReasoningReplay(model);
|
|
788
|
+
}
|
|
789
|
+
function sanitizeCompletionsReasoningReplayFields(messages, options) {
|
|
790
|
+
if (!Array.isArray(messages)) return;
|
|
791
|
+
for (const msg of messages) {
|
|
792
|
+
if (!msg || typeof msg !== "object") continue;
|
|
793
|
+
const record = msg;
|
|
794
|
+
if (record.role !== "assistant") continue;
|
|
795
|
+
if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
|
|
796
|
+
else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
|
|
797
|
+
else stripCompletionsReasoningReplayFields(record);
|
|
827
798
|
}
|
|
828
|
-
return strict;
|
|
829
799
|
}
|
|
830
|
-
function
|
|
831
|
-
|
|
800
|
+
function applyCompletionsReplay(outgoingMessages, context, model, compat) {
|
|
801
|
+
injectToolCallThoughtSignatures(outgoingMessages, context, model);
|
|
802
|
+
sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
|
|
803
|
+
preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
|
|
804
|
+
preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
|
|
805
|
+
});
|
|
832
806
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
807
|
+
//#endregion
|
|
808
|
+
//#region packages/ai/src/transports/openai-completions-params.ts
|
|
809
|
+
function isKnownOpenAICompletionsEndpoint(model) {
|
|
810
|
+
if (!model.baseUrl.trim()) return true;
|
|
811
|
+
const endpointClass = resolveProviderEndpoint(model).endpointClass;
|
|
812
|
+
if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
|
|
836
813
|
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);
|
|
814
|
+
return isAzureOpenAICompatibleHost(new URL(model.baseUrl).hostname.toLowerCase());
|
|
847
815
|
} catch {
|
|
848
816
|
return false;
|
|
849
817
|
}
|
|
850
818
|
}
|
|
851
|
-
function
|
|
852
|
-
return
|
|
819
|
+
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
820
|
+
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
853
821
|
}
|
|
854
|
-
function
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
822
|
+
function resolveOpenAICompletionsMaxTokens(model, options) {
|
|
823
|
+
if (options?.maxTokens) return {
|
|
824
|
+
maxTokens: options.maxTokens,
|
|
825
|
+
clampToModelMaxTokens: true
|
|
826
|
+
};
|
|
827
|
+
const paramsMaxTokens = resolveMaxTokensParam(model.params);
|
|
828
|
+
if (paramsMaxTokens) return {
|
|
829
|
+
maxTokens: paramsMaxTokens,
|
|
830
|
+
clampToModelMaxTokens: false
|
|
860
831
|
};
|
|
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
832
|
return {
|
|
880
|
-
|
|
881
|
-
|
|
833
|
+
maxTokens: model.maxTokens,
|
|
834
|
+
clampToModelMaxTokens: false
|
|
882
835
|
};
|
|
883
836
|
}
|
|
884
|
-
function
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
837
|
+
function resolveOpenAICompletionsModelMaxTokens(model) {
|
|
838
|
+
return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
|
|
839
|
+
}
|
|
840
|
+
const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
|
|
841
|
+
const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
|
|
842
|
+
function estimateOpenAICompletionsInputTokens(payload) {
|
|
843
|
+
let adjustedChars = 0;
|
|
844
|
+
adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
|
|
845
|
+
if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
|
|
846
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
|
|
847
|
+
} catch {
|
|
848
|
+
adjustedChars += 1024;
|
|
849
|
+
}
|
|
850
|
+
if (payload.response_format !== void 0) try {
|
|
851
|
+
adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
|
|
852
|
+
} catch {
|
|
853
|
+
adjustedChars += 256;
|
|
854
|
+
}
|
|
855
|
+
return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
|
|
856
|
+
}
|
|
857
|
+
function estimateOpenAICompletionsMessagesChars(messages) {
|
|
858
|
+
if (!Array.isArray(messages)) return 0;
|
|
859
|
+
let adjustedChars = 0;
|
|
860
|
+
for (const message of messages) {
|
|
861
|
+
if (!message || typeof message !== "object") continue;
|
|
862
|
+
const record = message;
|
|
863
|
+
adjustedChars += estimateOpenAICompletionsContentChars(record.content);
|
|
864
|
+
for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
|
|
865
|
+
if (record.tool_calls !== void 0) try {
|
|
866
|
+
adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
|
|
867
|
+
} catch {
|
|
868
|
+
adjustedChars += 256;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return adjustedChars;
|
|
872
|
+
}
|
|
873
|
+
function estimateOpenAICompletionsContentChars(value) {
|
|
874
|
+
if (typeof value === "string") return estimateStringChars(value);
|
|
875
|
+
if (!Array.isArray(value)) return 0;
|
|
876
|
+
let adjustedChars = 0;
|
|
877
|
+
for (const block of value) {
|
|
878
|
+
if (!block || typeof block !== "object") continue;
|
|
879
|
+
const record = block;
|
|
880
|
+
if (record.type === "image_url" || record.type === "input_image") {
|
|
881
|
+
adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
const text = record.text;
|
|
885
|
+
if (typeof text === "string") {
|
|
886
|
+
adjustedChars += estimateStringChars(text);
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
try {
|
|
890
|
+
adjustedChars += estimateStringChars(JSON.stringify(block));
|
|
891
|
+
} catch {
|
|
892
|
+
adjustedChars += 256;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return adjustedChars;
|
|
896
|
+
}
|
|
897
|
+
function resolveOpenAICompletionsEffectiveContextTokens(model) {
|
|
898
|
+
const contextTokens = model.contextTokens;
|
|
899
|
+
if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
|
|
900
|
+
return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
|
|
901
|
+
}
|
|
902
|
+
function isQwenOpenAICompletionsThinkingFormat(format) {
|
|
903
|
+
return format === "qwen" || format === "qwen-chat-template";
|
|
904
|
+
}
|
|
905
|
+
function setQwenChatTemplateThinking(params, enabled) {
|
|
906
|
+
const existing = params.chat_template_kwargs;
|
|
907
|
+
params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
|
|
908
|
+
...existing,
|
|
909
|
+
enable_thinking: enabled
|
|
910
|
+
} : { enable_thinking: enabled };
|
|
911
|
+
}
|
|
912
|
+
function applyQwenOpenAICompletionsThinkingParams(params) {
|
|
913
|
+
if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
|
|
914
|
+
const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
|
|
915
|
+
if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
|
|
916
|
+
else params.payload.enable_thinking = enabled;
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
function applyTogetherOpenAICompletionsThinkingParams(params) {
|
|
920
|
+
if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
|
|
921
|
+
params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
|
|
922
|
+
}
|
|
923
|
+
function convertTools(tools, compat, model, mode) {
|
|
924
|
+
const projection = projectOpenAITools(tools);
|
|
925
|
+
const strict = mode === "direct" ? compat.supportsStrictMode ? false : void 0 : resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
|
|
926
|
+
transport: "stream",
|
|
927
|
+
supportsStrictMode: compat?.supportsStrictMode
|
|
928
|
+
}), {
|
|
929
|
+
transport: "completions",
|
|
930
|
+
model
|
|
931
|
+
});
|
|
888
932
|
return {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
933
|
+
projection,
|
|
934
|
+
tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
|
|
935
|
+
const functionTool = {
|
|
936
|
+
name: tool.name,
|
|
937
|
+
description: tool.description,
|
|
938
|
+
parameters: mode === "direct" ? tool.parameters : normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
|
|
939
|
+
};
|
|
940
|
+
if (strict !== void 0) functionTool.strict = strict;
|
|
941
|
+
return {
|
|
942
|
+
type: "function",
|
|
943
|
+
function: functionTool
|
|
944
|
+
};
|
|
945
|
+
})
|
|
893
946
|
};
|
|
894
947
|
}
|
|
895
|
-
function
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
948
|
+
function buildOpenAICompletionsParams(model, context, options) {
|
|
949
|
+
return buildOpenAICompletionsRequest(model, context, options, { mode: "managed" });
|
|
950
|
+
}
|
|
951
|
+
function buildOpenAICompletionsRequest(model, context, options, policy) {
|
|
952
|
+
const resolvedPolicy = policy.mode === "direct" ? policy : {
|
|
953
|
+
...policy,
|
|
954
|
+
compat: getCompat(model)
|
|
955
|
+
};
|
|
956
|
+
const compat = resolvedPolicy.compat;
|
|
957
|
+
const managedCompat = resolvedPolicy.mode === "managed" ? resolvedPolicy.compat : void 0;
|
|
958
|
+
const endpointDetection = detectOpenAICompletionsCompat(model);
|
|
959
|
+
const compatDetection = policy.mode === "managed" ? endpointDetection : void 0;
|
|
960
|
+
const { endpointClass } = endpointDetection.capabilities;
|
|
961
|
+
const cacheRetention = policy.mode === "direct" ? policy.cacheRetention : resolveCacheRetention(options?.cacheRetention);
|
|
962
|
+
const cacheControl = resolveCompletionsCacheControl(compat, cacheRetention, endpointClass === "openrouter" || endpointClass === "default" && model.provider === "openrouter");
|
|
963
|
+
const markTools = endpointClass !== "modelstudio-native" && !(endpointClass === "default" && [
|
|
964
|
+
"modelstudio",
|
|
965
|
+
"dashscope",
|
|
966
|
+
"qwen"
|
|
967
|
+
].includes(model.provider));
|
|
968
|
+
const cacheOptOutIndexes = /* @__PURE__ */ new Set();
|
|
969
|
+
let messages = convertMessages(model, context, compat, {
|
|
970
|
+
cacheOptOutIndexes,
|
|
971
|
+
preserveSystemPromptCacheBoundary: cacheControl !== void 0 && !managedCompat?.requiresStringContent
|
|
972
|
+
});
|
|
973
|
+
if (managedCompat) {
|
|
974
|
+
applyCompletionsReplay(messages, context, model, managedCompat);
|
|
975
|
+
if (managedCompat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
|
|
976
|
+
if (managedCompat.requiresStringContent) messages = flattenCompletionMessagesToStringContent(messages);
|
|
977
|
+
}
|
|
978
|
+
const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
|
|
979
|
+
const params = {
|
|
980
|
+
model: model.id,
|
|
981
|
+
messages,
|
|
982
|
+
stream: true,
|
|
983
|
+
...resolveOpenAIPromptCacheParams(model, cacheRetention, compat)
|
|
906
984
|
};
|
|
985
|
+
if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
|
|
986
|
+
if (compat.supportsStore) params.store = false;
|
|
987
|
+
if (policy.mode === "direct" || compat.supportsPromptCacheKey && promptCacheKey) params.prompt_cache_key = compat.supportsPromptCacheKey ? promptCacheKey : void 0;
|
|
988
|
+
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
989
|
+
if (policy.mode === "managed" && options?.topP !== void 0) params.top_p = options.topP;
|
|
990
|
+
const requestedResponseFormat = options?.responseFormat;
|
|
991
|
+
const responseFormat = policy.mode === "direct" && requestedResponseFormat === void 0 ? void 0 : resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
992
|
+
provider: model.provider,
|
|
993
|
+
baseUrl: model.baseUrl,
|
|
994
|
+
hasTools: () => Boolean(context.tools?.length)
|
|
995
|
+
}) ? void 0 : requestedResponseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
996
|
+
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
997
|
+
if (policy.mode === "managed" && options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
998
|
+
if (policy.mode === "managed" && options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
999
|
+
if (policy.mode === "managed" && options?.seed !== void 0) params.seed = options.seed;
|
|
1000
|
+
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
1001
|
+
let directToolProjection;
|
|
1002
|
+
if (policy.mode === "direct" || supportsModelTools(model)) {
|
|
1003
|
+
if (context.tools) {
|
|
1004
|
+
const converted = convertTools(context.tools, compat, model, policy.mode);
|
|
1005
|
+
if (policy.mode === "direct") directToolProjection = converted.projection;
|
|
1006
|
+
if (converted.tools.length > 0 || policy.mode === "managed" && converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
|
|
1007
|
+
else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1008
|
+
if (policy.mode === "direct" && compat.zaiToolStream && converted.tools.length > 0) params.tool_stream = true;
|
|
1009
|
+
if (policy.mode === "managed" && options?.toolChoice) {
|
|
1010
|
+
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
|
|
1011
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1012
|
+
} else if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
|
|
1013
|
+
} else if (hasToolCallHistory(context.messages)) params.tools = [];
|
|
1014
|
+
if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
|
|
1015
|
+
delete params.tools;
|
|
1016
|
+
delete params.tool_choice;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (policy.mode === "direct" && compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools);
|
|
1020
|
+
if (policy.mode === "direct" && options?.toolChoice) {
|
|
1021
|
+
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, directToolProjection ?? projectOpenAITools([]));
|
|
1022
|
+
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
1023
|
+
}
|
|
1024
|
+
{
|
|
1025
|
+
const maxTokenBudget = policy.mode === "direct" ? {
|
|
1026
|
+
maxTokens: options?.maxTokens,
|
|
1027
|
+
clampToModelMaxTokens: true
|
|
1028
|
+
} : resolveOpenAICompletionsMaxTokens(model, options);
|
|
1029
|
+
const effectiveMaxTokens = maxTokenBudget.maxTokens;
|
|
1030
|
+
const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
|
|
1031
|
+
let clampedMaxTokens = effectiveMaxTokens;
|
|
1032
|
+
const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
|
|
1033
|
+
if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
|
|
1034
|
+
clampedMaxTokens = modelMaxTokens;
|
|
1035
|
+
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}`);
|
|
1036
|
+
}
|
|
1037
|
+
if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
|
|
1038
|
+
const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
|
|
1039
|
+
const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
|
|
1040
|
+
if (clampedMaxTokens > remainingBudget) {
|
|
1041
|
+
clampedMaxTokens = remainingBudget;
|
|
1042
|
+
emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
if (policy.mode === "direct" ? options?.maxTokens : clampedMaxTokens) {
|
|
1046
|
+
if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
|
|
1047
|
+
else params.max_completion_tokens = clampedMaxTokens;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
if (policy.mode === "direct") {
|
|
1051
|
+
applyDirectCompletionsReasoningAndRouting(params, model, options, compat);
|
|
1052
|
+
return params;
|
|
1053
|
+
}
|
|
1054
|
+
const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
|
|
1055
|
+
const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
|
|
1056
|
+
model,
|
|
1057
|
+
effort: completionsReasoningEffort,
|
|
1058
|
+
fallbackMap: managedCompat?.reasoningEffortMap
|
|
1059
|
+
}) : void 0;
|
|
1060
|
+
const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
|
|
1061
|
+
const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
|
|
1062
|
+
const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
|
|
1063
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1064
|
+
modelReasoning: model.reasoning,
|
|
1065
|
+
payload: params,
|
|
1066
|
+
requestedEffort: completionsReasoningEffort
|
|
1067
|
+
});
|
|
1068
|
+
applyTogetherOpenAICompletionsThinkingParams({
|
|
1069
|
+
compatThinkingFormat: compat.thinkingFormat,
|
|
1070
|
+
modelReasoning: model.reasoning,
|
|
1071
|
+
payload: params,
|
|
1072
|
+
requestedEffort: completionsReasoningEffort
|
|
1073
|
+
});
|
|
1074
|
+
if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
|
|
1075
|
+
else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
|
|
1076
|
+
else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
|
|
1077
|
+
if (compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools, !managedCompat?.requiresStringContent);
|
|
1078
|
+
return params;
|
|
907
1079
|
}
|
|
908
1080
|
//#endregion
|
|
909
1081
|
//#region packages/ai/src/transports/openai-completions-dsml.ts
|
|
@@ -1270,7 +1442,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
|
|
|
1270
1442
|
partial: output
|
|
1271
1443
|
});
|
|
1272
1444
|
}
|
|
1273
|
-
currentBlock
|
|
1445
|
+
appendAssistantThinking(currentBlock, reasoningDelta.text);
|
|
1274
1446
|
pushStreamEvent({
|
|
1275
1447
|
type: "thinking_delta",
|
|
1276
1448
|
contentIndex: blockIndex(),
|
|
@@ -1588,6 +1760,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
|
|
|
1588
1760
|
emitReasoningUsageActivity(hasReasoningUsageActivity);
|
|
1589
1761
|
if (cooperativeScheduler) await cooperativeScheduler.afterEvent();
|
|
1590
1762
|
}
|
|
1763
|
+
throwIfModelStreamAborted(options?.signal);
|
|
1591
1764
|
if (!finishReason && (directMode || options?.sawStreamDONE?.() === false)) throw new Error("Stream ended without finish_reason");
|
|
1592
1765
|
flushReasoningTagTextPartitioner();
|
|
1593
1766
|
flushDeepSeekToolCallRecovererAtEnd();
|
|
@@ -1611,12 +1784,9 @@ async function processCompletionsStream(responseStream, output, model, stream, o
|
|
|
1611
1784
|
if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
|
|
1612
1785
|
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
1613
1786
|
}
|
|
1614
|
-
function resolveOpenAICompletionsReasoningEffort(options) {
|
|
1615
|
-
return options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1616
|
-
}
|
|
1617
1787
|
function shouldEmitOpenAICompletionsReasoning(model, options) {
|
|
1618
1788
|
if (!model.reasoning) return false;
|
|
1619
|
-
const effort =
|
|
1789
|
+
const effort = options?.reasoningEffort ?? options?.reasoning ?? "high";
|
|
1620
1790
|
if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
|
|
1621
1791
|
return true;
|
|
1622
1792
|
}
|
|
@@ -1625,4 +1795,4 @@ function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
|
|
|
1625
1795
|
return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
|
|
1626
1796
|
}
|
|
1627
1797
|
//#endregion
|
|
1628
|
-
export {
|
|
1798
|
+
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 };
|