@xiaoso/dsh-tool-plus 0.1.16 → 0.1.17-beta.0
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/lib/{abort-DZ54CaKU.mjs → abort-BTblp0Rt.mjs} +2 -2
- package/lib/{alibaba-coding-plan-DODpkcqi.mjs → alibaba-coding-plan-t3R4YKCB.mjs} +3 -3
- package/lib/{alibaba-token-plan-C6lpKCda.mjs → alibaba-token-plan-CO4djG1y.mjs} +4 -4
- package/lib/{alibaba-token-plan-CHGyWOfX.mjs → alibaba-token-plan-CUjYJX0-.mjs} +1 -1
- package/lib/{anthropic-Dv_GGX6g.mjs → anthropic-nf7w6so1.mjs} +48 -21
- package/lib/{api-key-validation-MnMf_ihX.mjs → api-key-validation-C_tZ7Y9a.mjs} +2 -2
- package/lib/behavior-BwvTUtag.mjs +111 -0
- package/lib/{browser-render-BZXt548F.mjs → browser-render-BQaEVK_n.mjs} +4 -4
- package/lib/{reference-CtvM4zLU.mjs → bundled-references-CrQ4dQcT.mjs} +57 -4
- package/lib/cascade-BWjRTD1q.mjs +30265 -0
- package/lib/{cline-pass-BT9CBEOj.mjs → cline-pass-Bjm-O8GG.mjs} +2 -2
- package/lib/cloudflare-CKXRJ0l9.mjs +37 -0
- package/lib/{cloudflare-ai-gateway-DReN6ohJ.mjs → cloudflare-ai-gateway-BpnBSjwV.mjs} +8 -4
- package/lib/cloudflare-workers-ai-odj9XswL.mjs +32 -0
- package/lib/collapse-_nkb2dn7.mjs +1945 -0
- package/lib/{coreweave-C82HrLpb.mjs → coreweave-CGDZOD8-.mjs} +1 -1
- package/lib/{coreweave-C68UgRQO.mjs → coreweave-DwffcgW5.mjs} +4 -4
- package/lib/cursor-BgOuV7Qf.mjs +794 -0
- package/lib/{cursor-_gHHq-Dq.mjs → cursor-CF1iHHbv.mjs} +68 -19
- package/lib/{cursor-proto-Cr_WEMuv.mjs → cursor-proto-BuNTjX3L.mjs} +823 -23
- package/lib/{devin-QpfTSl9H.mjs → devin-7OEeEb80.mjs} +112 -31
- package/lib/{devin-proto-DYhp4Ty6.mjs → devin-proto-C-QreeAv.mjs} +71 -5
- package/lib/{dom-DG3sPESc.mjs → dom-BmbZv4v9.mjs} +598 -182
- package/lib/{error-O63pC8zg.mjs → error-8LVo7tug.mjs} +725 -104
- package/lib/factory-droid-ChhmsGWb.mjs +71 -0
- package/lib/{fs-promises-shim-B2YFWkcd.mjs → fs-promises-shim-CANAxFWh.mjs} +10 -1
- package/lib/{github-copilot-BMEcw8N1.mjs → github-copilot-QwevdT30.mjs} +4 -4
- package/lib/{github-copilot-headers-DnjGEbko.mjs → github-copilot-headers-CAUZ0rUD.mjs} +138 -26
- package/lib/{gitlab-duo-DnHqJjFY.mjs → gitlab-duo-DigV7k_o.mjs} +2 -2
- package/lib/{google-antigravity-B1mSckrK.mjs → google-antigravity-D-oe0r8A.mjs} +6 -7
- package/lib/{google-gemini-cli-DJwThCCn.mjs → google-gemini-cli-CQBHgGJT.mjs} +5 -6
- package/lib/{google-oauth-shared-BctLpVUN.mjs → google-oauth-shared-CvgVIUwJ.mjs} +2 -2
- package/lib/google-validation-CdvRj4ec.mjs +126 -0
- package/lib/id-EJwVLDHE.mjs +2548 -0
- package/lib/index.mjs +248 -258
- package/lib/{kilo-BEgEwMJQ.mjs → kilo-5r6OffCa.mjs} +2 -2
- package/lib/{kimi-T9NldEIJ.mjs → kimi-DOOtbXmm.mjs} +9 -10
- package/lib/{launch-BIWNyrBB.mjs → launch-DkAjib7h.mjs} +1 -1
- package/lib/{markit-Dvp2JPdT.mjs → markit-CCcyeAds.mjs} +359 -140
- package/lib/{models-DnXMYGcJ.mjs → models-DCpDT_im.mjs} +199203 -112275
- package/lib/{muse-code-CNvqqAp6.mjs → muse-code-CYf8zj_B.mjs} +5 -5
- package/lib/{openai-codex-D5-CyTxa.mjs → openai-codex-Cj-TvSFh.mjs} +13 -10
- package/lib/{perplexity-A2PEoGaC.mjs → perplexity-Dh_Qgsmx.mjs} +68 -25
- package/lib/{pkce-BYjmugmw.mjs → pkce-DO-jBJLV.mjs} +1 -1
- package/lib/pricing-DusBEhwm.mjs +593 -0
- package/lib/{readability-uGhqb4GC.mjs → readability-XHK0o3bv.mjs} +287 -27
- package/lib/{scrapers-C1ujjV8k.mjs → scrapers-B6PADOVm.mjs} +13 -13
- package/lib/slugify-ynYWBIA5.mjs +11 -0
- package/lib/snowflake-B_jap6lz.mjs +666 -0
- package/lib/snowflake-Cu1ZyOp6.mjs +151 -0
- package/lib/{src-AELUUb5U.mjs → src-BhinCSpi.mjs} +1172 -458
- package/lib/{stream-AVOoe5Hj.mjs → stream-D2ivg3GH.mjs} +3740 -5311
- package/lib/{turndown-D6fof1OX.mjs → turndown-Vya8SavL.mjs} +82 -30
- package/lib/types/tools/hashline/native/index.d.ts +2 -2
- package/lib/types/tools/hashline/native/policy.d.ts +19 -21
- package/lib/types/tools/omp/session/agent-storage.d.ts +3 -3
- package/lib/types/tools/shared/bun-named-shim.d.ts +5 -0
- package/lib/{types-BLrLohwK.mjs → types-tH1v9uRm.mjs} +2 -2
- package/lib/{utils-cZX3f_Ls.mjs → utils-C_Ncya1r.mjs} +1737 -509
- package/lib/utils-D2tK38xq.mjs +34006 -0
- package/lib/{utils-I4I8azHN.mjs → utils-DrDwmNtu.mjs} +8 -3
- package/lib/{xai-oauth-CbeldUSV.mjs → xai-oauth-DLLzX6my.mjs} +4 -4
- package/lib/{xiaomi-Dl3VSs04.mjs → xiaomi-CqK-KivK.mjs} +2 -2
- package/lib/{zai-FyjGSypr.mjs → zai-DoJ2xPIH.mjs} +2 -2
- package/lib/{zip-DRrycScC.mjs → zip-DG1mUV6X.mjs} +1 -1
- package/package.json +8 -8
- package/lib/amazon-bedrock-Dx0rr9dY.mjs +0 -996
- package/lib/anthropic-ClCw0PV_.mjs +0 -3624
- package/lib/anthropic-wire-lbGAFzYb.mjs +0 -927
- package/lib/azure-openai-responses-WE_bgFWp.mjs +0 -285
- package/lib/bundled-references-DY3EJxZV.mjs +0 -56
- package/lib/cloudflare-ai-gateway-CrMa97Ap.mjs +0 -44
- package/lib/collapse-CkMNRNr7.mjs +0 -2304
- package/lib/cursor-C6jaIJDg.mjs +0 -283
- package/lib/cursor-D-XZYDf_.mjs +0 -4544
- package/lib/deterministic-id-CUEIMSqR.mjs +0 -17
- package/lib/devin-ExGIghmx.mjs +0 -69
- package/lib/devin-u0ljw5P6.mjs +0 -628
- package/lib/effort-BdtQMN-_.mjs +0 -23
- package/lib/event-stream-BKEtb45Y.mjs +0 -167
- package/lib/gemini-headers-Co9cVvex.mjs +0 -54
- package/lib/google-auth-qAN-6DMW.mjs +0 -232
- package/lib/google-gemini-cli-B2Aw1S5w.mjs +0 -844
- package/lib/google-lTxuq28n.mjs +0 -38
- package/lib/google-shared-Y01j9xOr.mjs +0 -856
- package/lib/google-validation-BEkNs-Bx.mjs +0 -18
- package/lib/google-vertex-DcM-FRC5.mjs +0 -99
- package/lib/hosts-00rjRzUD.mjs +0 -203
- package/lib/id-AL5obQzR.mjs +0 -71
- package/lib/idle-iterator-D0ZXEcUq.mjs +0 -366
- package/lib/ollama-6R0oXZks.mjs +0 -574
- package/lib/openai-codex-responses-C8bnBWOe.mjs +0 -3232
- package/lib/openai-completions-DNr3mUsn.mjs +0 -1430
- package/lib/openai-configuration-update-xMYUjaK_.mjs +0 -150
- package/lib/openai-reasoning-fallback-Ju-5pSR8.mjs +0 -380
- package/lib/openai-responses-CjpafAEr.mjs +0 -861
- package/lib/protobuf-BMgDYu_U.mjs +0 -717
- package/lib/provider-response-SqKSkkUc.mjs +0 -22
- package/lib/proxy-DxvCQOCA.mjs +0 -230
- package/lib/request-debug-BwPOqN_j.mjs +0 -183
- package/lib/sanitize-text-BCf5KmCf.mjs +0 -33
- package/lib/sse-debug-B-tkLCPW.mjs +0 -11
- package/lib/stream-markup-healing-CN3RsqG9.mjs +0 -267
- package/lib/strict-tool-validation-CBTz1dAA.mjs +0 -105
- package/lib/taxonomy-DSIM99rS.mjs +0 -15392
- package/lib/thinking-ngMwf_1Y.mjs +0 -336
- package/lib/type-guards-CttAANEx.mjs +0 -11
- package/lib/utils-CE3DSebh.mjs +0 -11763
- package/lib/vision-guard-BCpXWUj3.mjs +0 -77
|
@@ -1,1430 +0,0 @@
|
|
|
1
|
-
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
-
__installBunShim();
|
|
3
|
-
import { r as __exportAll } from "./fs-promises-shim-B2YFWkcd.mjs";
|
|
4
|
-
import { C as warn, N as $env, _ as parseStreamingJson, v as parseStreamingJsonThrottled } from "./src-AELUUb5U.mjs";
|
|
5
|
-
import { B as OpenAIHttpError, V as ProviderHttpError, _ as AbortError, c as finalize, n as StreamTimeoutError, s as ProviderResponseError, t as ConfigurationError } from "./error-O63pC8zg.mjs";
|
|
6
|
-
import { A as toolWireSchema, C as kStreamingLastParseLen, F as normalizeSchemaForMoonshot, L as sanitizeSchemaForGrammar, M as adaptSchemaForStrict, T as renderDemotedThinking, _ as isDemotedThinking, c as resolveCacheRetention, h as transformMessages, j as NO_STRICT, k as flattenExclusiveRequiredRootUnion, o as normalizeSystemPrompts } from "./utils-cZX3f_Ls.mjs";
|
|
7
|
-
import { At as withReplaySafeStreamRetry, C as applyChatCompletionsToolStream, D as applyOpenAIServiceTier, E as applyOpenAIGatewayRouting, H as createOpenAIStrictToolsState, I as calculateOpenAIUsageAccounting, L as clearOpenAIStrictToolsState, N as applyWireModelIdTransform, O as applyProviderReportedCost, S as applyChatCompletionsReasoningParams, St as shouldRetryWithoutStrictTools, T as applyOpenAIExtraBody, Tt as mapToOpenAICompletionsToolChoice, V as createInitialResponsesAssistantMessage, W as disableStrictToolsForScope, _t as resolveOpenAIOutputTokenParam, at as isCompiledGrammarTooLargeStrictError, dt as parseAzureDeploymentNameMap, et as getOpenAIPromptCacheKey, gt as resolveOpenAICompletionsOutputClamp, ht as resolveOpenAICompatPolicy, kt as hasVisibleAssistantContent, n as getEnvApiKey, nt as getOpenAIStrictToolsScope, st as isStrictToolsDisabledForScope, vt as resolveOpenAIRequestSetup, wt as isForcedToolChoice, xt as shouldDropAutoToolChoiceForReasoning } from "./stream-AVOoe5Hj.mjs";
|
|
8
|
-
import { s as resolveWireModelId } from "./anthropic-wire-lbGAFzYb.mjs";
|
|
9
|
-
import { t as createAbortSourceTracker } from "./abort-DZ54CaKU.mjs";
|
|
10
|
-
import { t as calculateCost } from "./models-DnXMYGcJ.mjs";
|
|
11
|
-
import { i as resolveCopilotRequestIdentity, o as wrapFetchForCopilotFallback } from "./github-copilot-headers-DnjGEbko.mjs";
|
|
12
|
-
import { n as isOpenAICompletionsVisionSupported, r as joinTextWithImagePlaceholder, t as NON_VISION_IMAGE_PLACEHOLDER } from "./vision-guard-BCpXWUj3.mjs";
|
|
13
|
-
import { t as findStrictToolSchemaViolation } from "./strict-tool-validation-CBTz1dAA.mjs";
|
|
14
|
-
import { t as AssistantMessageEventStream } from "./event-stream-BKEtb45Y.mjs";
|
|
15
|
-
import { n as clinePassClientHeaders } from "./cline-pass-BT9CBEOj.mjs";
|
|
16
|
-
import { n as getOpenAIStreamFirstEventTimeoutMs, o as iterateWithIdleTimeout, r as getOpenAIStreamIdleTimeoutMs, s as iterateWithTerminalGrace } from "./idle-iterator-D0ZXEcUq.mjs";
|
|
17
|
-
import { t as getKimiCommonHeaders } from "./kimi-T9NldEIJ.mjs";
|
|
18
|
-
import { t as notifyProviderResponse } from "./provider-response-SqKSkkUc.mjs";
|
|
19
|
-
import { a as getOpenAIReasoningEffortFallback, c as postOpenAIStream, i as createOpenAIReasoningEffortFallbackState, n as clearOpenAIReasoningEffortFallbackState, o as rememberOpenAIReasoningEffortFallback, r as createOpenAIReasoningEffortFallbackKey, s as resolveOpenAIReasoningEffortFallback, t as applyOpenAIReasoningEffortFallback } from "./openai-reasoning-fallback-Ju-5pSR8.mjs";
|
|
20
|
-
import { t as StreamMarkupHealing } from "./stream-markup-healing-CN3RsqG9.mjs";
|
|
21
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-ai@18.1.17/node_modules/@oh-my-pi/pi-ai/src/providers/openai-completions.ts
|
|
22
|
-
var openai_completions_exports = /* @__PURE__ */ __exportAll({
|
|
23
|
-
convertMessages: () => convertMessages,
|
|
24
|
-
isOpenAICompletionsProgressChunk: () => isOpenAICompletionsProgressChunk,
|
|
25
|
-
parseChunkUsage: () => parseChunkUsage,
|
|
26
|
-
streamOpenAICompletions: () => streamOpenAICompletions
|
|
27
|
-
});
|
|
28
|
-
const GEMINI_THOUGHT_SIGNATURE_NAMESPACES = ["google", "vertex"];
|
|
29
|
-
function getGeminiThoughtSignatureExtraContent(value) {
|
|
30
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
31
|
-
for (const namespace of GEMINI_THOUGHT_SIGNATURE_NAMESPACES) {
|
|
32
|
-
const providerContent = Reflect.get(value, namespace);
|
|
33
|
-
if (typeof providerContent !== "object" || providerContent === null) continue;
|
|
34
|
-
const thoughtSignature = Reflect.get(providerContent, "thought_signature");
|
|
35
|
-
if (typeof thoughtSignature !== "string" || thoughtSignature.length === 0) continue;
|
|
36
|
-
return namespace === "google" ? { google: { thought_signature: thoughtSignature } } : { vertex: { thought_signature: thoughtSignature } };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function parseGeminiThoughtSignatureExtraContent(thoughtSignature) {
|
|
40
|
-
if (!thoughtSignature) return void 0;
|
|
41
|
-
try {
|
|
42
|
-
return getGeminiThoughtSignatureExtraContent(JSON.parse(thoughtSignature));
|
|
43
|
-
} catch {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function firstPositiveNumber(...values) {
|
|
48
|
-
for (const value of values) if (typeof value === "number" && value > 0) return value;
|
|
49
|
-
return 0;
|
|
50
|
-
}
|
|
51
|
-
function hasPositiveCacheReadTokenField(rawUsage) {
|
|
52
|
-
const usageLike = rawUsage;
|
|
53
|
-
if (typeof usageLike.cached_tokens === "number" && usageLike.cached_tokens > 0) return true;
|
|
54
|
-
if (typeof usageLike.prompt_cache_hit_tokens === "number" && usageLike.prompt_cache_hit_tokens > 0) return true;
|
|
55
|
-
if (typeof usageLike.cachedContentTokenCount === "number" && usageLike.cachedContentTokenCount > 0) return true;
|
|
56
|
-
const rawPromptTokenDetails = usageLike.prompt_tokens_details;
|
|
57
|
-
if (typeof rawPromptTokenDetails !== "object" || rawPromptTokenDetails === null) return false;
|
|
58
|
-
const promptTokenDetails = rawPromptTokenDetails;
|
|
59
|
-
return typeof promptTokenDetails.cached_tokens === "number" && promptTokenDetails.cached_tokens > 0;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Normalize tool call ID for Mistral.
|
|
63
|
-
* Mistral requires tool IDs to be exactly 9 alphanumeric characters (a-z, A-Z, 0-9).
|
|
64
|
-
*/
|
|
65
|
-
function normalizeMistralToolId(id, isMistral) {
|
|
66
|
-
if (!isMistral) return id;
|
|
67
|
-
let normalized = id.replace(/[^a-zA-Z0-9]/g, "");
|
|
68
|
-
if (normalized.length < 9) normalized = normalized + "ABCDEFGHI".slice(0, 9 - normalized.length);
|
|
69
|
-
else if (normalized.length > 9) normalized = normalized.slice(0, 9);
|
|
70
|
-
return normalized;
|
|
71
|
-
}
|
|
72
|
-
function resolveOpenAICompletionsRoutingEffort(model, effort) {
|
|
73
|
-
if (!effort) return void 0;
|
|
74
|
-
if (model.thinking?.efforts.includes(effort)) return effort;
|
|
75
|
-
const compatMappedEffort = model.compat.reasoningEffortMap?.[effort];
|
|
76
|
-
if (compatMappedEffort && model.thinking?.efforts.includes(compatMappedEffort)) return compatMappedEffort;
|
|
77
|
-
const thinkingMappedEffort = model.thinking?.effortMap?.[effort];
|
|
78
|
-
if (thinkingMappedEffort && model.thinking?.efforts.includes(thinkingMappedEffort)) return thinkingMappedEffort;
|
|
79
|
-
return effort;
|
|
80
|
-
}
|
|
81
|
-
function resolveOpenAICompletionsModelId(model, options) {
|
|
82
|
-
const effort = resolveOpenAICompletionsRoutingEffort(model, options?.reasoning && !options.disableReasoning && model.reasoning ? options.reasoning : void 0);
|
|
83
|
-
const wireId = resolveWireModelId(model, effort);
|
|
84
|
-
return applyWireModelIdTransform(wireId, model.compat.wireModelIdMode, options?.openrouterVariant);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Normalize OpenAI-compatible streaming `delta.content` into plain text.
|
|
88
|
-
* Most providers stream `delta.content` as a string, but some (notably Mistral
|
|
89
|
-
* Medium 3.5 / `mistral-medium-2604`) return an array of typed content parts
|
|
90
|
-
* — e.g. `[{ type: "text", text: "Hello" }]`. Without normalization those
|
|
91
|
-
* parts get string-coerced via `text += array`, producing the literal
|
|
92
|
-
* `[object Object]` sequences observed in issue #911.
|
|
93
|
-
*
|
|
94
|
-
* Returns the joined text. Non-text parts and unknown shapes are skipped so
|
|
95
|
-
* we never emit JS object sigils as visible output.
|
|
96
|
-
*/
|
|
97
|
-
function normalizeStreamingContentText(content) {
|
|
98
|
-
if (typeof content === "string") return content;
|
|
99
|
-
if (Array.isArray(content)) {
|
|
100
|
-
let out = "";
|
|
101
|
-
for (const part of content) if (typeof part === "string") out += part;
|
|
102
|
-
else if (part && typeof part === "object") {
|
|
103
|
-
const obj = part;
|
|
104
|
-
if ((obj.type === void 0 || obj.type === "text") && typeof obj.text === "string") out += obj.text;
|
|
105
|
-
}
|
|
106
|
-
return out;
|
|
107
|
-
}
|
|
108
|
-
if (content && typeof content === "object") {
|
|
109
|
-
const obj = content;
|
|
110
|
-
if ((obj.type === void 0 || obj.type === "text") && typeof obj.text === "string") return obj.text;
|
|
111
|
-
}
|
|
112
|
-
return "";
|
|
113
|
-
}
|
|
114
|
-
function serializeToolArguments(value) {
|
|
115
|
-
if (value && typeof value === "object" && !Array.isArray(value)) try {
|
|
116
|
-
return JSON.stringify(value);
|
|
117
|
-
} catch {
|
|
118
|
-
return "{}";
|
|
119
|
-
}
|
|
120
|
-
if (typeof value === "string") {
|
|
121
|
-
const trimmed = value.trim();
|
|
122
|
-
if (trimmed.length === 0) return "{}";
|
|
123
|
-
try {
|
|
124
|
-
const parsed = JSON.parse(trimmed);
|
|
125
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return JSON.stringify(parsed);
|
|
126
|
-
} catch {}
|
|
127
|
-
return "{}";
|
|
128
|
-
}
|
|
129
|
-
return "{}";
|
|
130
|
-
}
|
|
131
|
-
function cloneStreamingArgumentValue(value) {
|
|
132
|
-
if (Array.isArray(value)) return value.map(cloneStreamingArgumentValue);
|
|
133
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) return mergeStreamingArgumentObjects(void 0, value);
|
|
134
|
-
return value;
|
|
135
|
-
}
|
|
136
|
-
function streamingArgumentValuesEqual(left, right) {
|
|
137
|
-
if (left === right) return true;
|
|
138
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
139
|
-
if (left.length !== right.length) return false;
|
|
140
|
-
for (let i = 0; i < left.length; i++) if (!streamingArgumentValuesEqual(left[i], right[i])) return false;
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
if (left !== null && typeof left === "object" && !Array.isArray(left) && right !== null && typeof right === "object" && !Array.isArray(right)) {
|
|
144
|
-
const leftObject = left;
|
|
145
|
-
const rightObject = right;
|
|
146
|
-
let leftKeys = 0;
|
|
147
|
-
for (const key in leftObject) {
|
|
148
|
-
if (!Object.hasOwn(leftObject, key) || key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
149
|
-
leftKeys++;
|
|
150
|
-
if (!Object.hasOwn(rightObject, key) || !streamingArgumentValuesEqual(leftObject[key], rightObject[key])) return false;
|
|
151
|
-
}
|
|
152
|
-
let rightKeys = 0;
|
|
153
|
-
for (const key in rightObject) {
|
|
154
|
-
if (!Object.hasOwn(rightObject, key) || key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
155
|
-
rightKeys++;
|
|
156
|
-
}
|
|
157
|
-
return leftKeys === rightKeys;
|
|
158
|
-
}
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
function streamingArgumentArrayStartsWith(value, prefix) {
|
|
162
|
-
if (prefix.length > value.length) return false;
|
|
163
|
-
for (let i = 0; i < prefix.length; i++) if (!streamingArgumentValuesEqual(value[i], prefix[i])) return false;
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
function mergeStreamingArgumentArrays(prev, fragment) {
|
|
167
|
-
if (streamingArgumentArrayStartsWith(fragment, prev)) return fragment.map(cloneStreamingArgumentValue);
|
|
168
|
-
if (streamingArgumentArrayStartsWith(prev, fragment)) return prev.map(cloneStreamingArgumentValue);
|
|
169
|
-
const merged = prev.map(cloneStreamingArgumentValue);
|
|
170
|
-
for (const value of fragment) merged.push(cloneStreamingArgumentValue(value));
|
|
171
|
-
return merged;
|
|
172
|
-
}
|
|
173
|
-
function mergeStreamingArgumentValues(prev, fragment) {
|
|
174
|
-
if (typeof prev === "string" && typeof fragment === "string") return fragment.startsWith(prev) ? fragment : prev + fragment;
|
|
175
|
-
if (Array.isArray(prev) && Array.isArray(fragment)) return mergeStreamingArgumentArrays(prev, fragment);
|
|
176
|
-
if (prev !== null && typeof prev === "object" && !Array.isArray(prev) && fragment !== null && typeof fragment === "object" && !Array.isArray(fragment)) return mergeStreamingArgumentObjects(prev, fragment);
|
|
177
|
-
return cloneStreamingArgumentValue(fragment);
|
|
178
|
-
}
|
|
179
|
-
function mergeStreamingArgumentObjects(prev, fragment) {
|
|
180
|
-
const merged = {};
|
|
181
|
-
if (prev) for (const key in prev) {
|
|
182
|
-
if (!Object.hasOwn(prev, key) || key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
183
|
-
merged[key] = cloneStreamingArgumentValue(prev[key]);
|
|
184
|
-
}
|
|
185
|
-
for (const key in fragment) {
|
|
186
|
-
if (!Object.hasOwn(fragment, key) || key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
187
|
-
merged[key] = Object.hasOwn(merged, key) ? mergeStreamingArgumentValues(merged[key], fragment[key]) : cloneStreamingArgumentValue(fragment[key]);
|
|
188
|
-
}
|
|
189
|
-
return merged;
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Check if conversation messages contain tool calls or tool results.
|
|
193
|
-
* This is needed because Anthropic (via proxy) requires the tools param
|
|
194
|
-
* to be present when messages include tool_calls or tool role messages.
|
|
195
|
-
*/
|
|
196
|
-
function hasToolHistory(messages) {
|
|
197
|
-
for (const msg of messages) {
|
|
198
|
-
if (msg.role === "toolResult") return true;
|
|
199
|
-
if (msg.role === "assistant") {
|
|
200
|
-
if (msg.content.some((block) => block.type === "toolCall")) return true;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Identify "real progress" stream chunks vs. keepalives, role-only preambles,
|
|
207
|
-
* and empty `{choices:[]}` no-ops emitted by some OpenAI-compatible endpoints.
|
|
208
|
-
* Without this filter, every keepalive resets `iterateWithIdleTimeout`'s
|
|
209
|
-
* deadline, so a provider that streams nothing but pings keeps the watchdog
|
|
210
|
-
* asleep indefinitely — observed against z.ai/GLM via OpenRouter where a
|
|
211
|
-
* subagent stalled for hours with no error surfaced.
|
|
212
|
-
*
|
|
213
|
-
* A chunk counts as progress when it carries terminal usage, a finish reason,
|
|
214
|
-
* or a model-produced delta (content / tool calls / reasoning / refusal).
|
|
215
|
-
* Role-only `delta: { role: "assistant" }` preambles do NOT count; we want the
|
|
216
|
-
* (longer) first-event timeout to keep governing until real output appears.
|
|
217
|
-
*/
|
|
218
|
-
function isOpenAICompletionsProgressChunk(chunk) {
|
|
219
|
-
if (!chunk || typeof chunk !== "object") return false;
|
|
220
|
-
const record = chunk;
|
|
221
|
-
if (record.usage) return true;
|
|
222
|
-
const choice = Array.isArray(record.choices) ? record.choices[0] : void 0;
|
|
223
|
-
if (!choice) return false;
|
|
224
|
-
if (choice.finish_reason) return true;
|
|
225
|
-
if (choice.usage) return true;
|
|
226
|
-
const delta = choice.delta;
|
|
227
|
-
if (!delta) return false;
|
|
228
|
-
const content = delta.content;
|
|
229
|
-
if (typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.length > 0) return true;
|
|
230
|
-
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) return true;
|
|
231
|
-
if (typeof delta.reasoning === "string" && delta.reasoning.length > 0) return true;
|
|
232
|
-
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) return true;
|
|
233
|
-
if (typeof delta.reasoning_text === "string" && delta.reasoning_text.length > 0) return true;
|
|
234
|
-
if (typeof delta.refusal === "string" && delta.refusal.length > 0) return true;
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
const OPENAI_COMPLETIONS_PROVIDER_SESSION_STATE_PREFIX = "openai-completions:";
|
|
238
|
-
function createOpenAICompletionsProviderSessionState() {
|
|
239
|
-
const strictToolsState = createOpenAIStrictToolsState();
|
|
240
|
-
const reasoningEffortFallbackState = createOpenAIReasoningEffortFallbackState();
|
|
241
|
-
const state = {
|
|
242
|
-
...strictToolsState,
|
|
243
|
-
...reasoningEffortFallbackState,
|
|
244
|
-
close: () => {
|
|
245
|
-
clearOpenAIStrictToolsState(state);
|
|
246
|
-
clearOpenAIReasoningEffortFallbackState(state);
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
return state;
|
|
250
|
-
}
|
|
251
|
-
function getOpenAICompletionsProviderSessionState(model, baseUrl, providerSessionState) {
|
|
252
|
-
if (!providerSessionState) return void 0;
|
|
253
|
-
const key = `${OPENAI_COMPLETIONS_PROVIDER_SESSION_STATE_PREFIX}${model.provider}:${baseUrl ?? ""}:${model.id}`;
|
|
254
|
-
const existing = providerSessionState.get(key);
|
|
255
|
-
if (existing) return existing;
|
|
256
|
-
const created = createOpenAICompletionsProviderSessionState();
|
|
257
|
-
providerSessionState.set(key, created);
|
|
258
|
-
return created;
|
|
259
|
-
}
|
|
260
|
-
const DEEPSEEK_SPECIAL_TOKEN_REGEX = /<(?:||\|)[A-Za-z0-9_.||▁]{1,64}(?:||\|)>/g;
|
|
261
|
-
const DEEPSEEK_SPECIAL_TOKEN_AT_START_REGEX = /^\s*<(?:||\|)[A-Za-z0-9_.||▁]{1,64}(?:||\|)>/;
|
|
262
|
-
const DEEPSEEK_SPECIAL_TOKEN_AT_END_REGEX = /<(?:||\|)[A-Za-z0-9_.||▁]{1,64}(?:||\|)>\s*$/;
|
|
263
|
-
const DEEPSEEK_OPEN_DELIMS = ["<|", "<|"];
|
|
264
|
-
function stripDeepseekSpecialTokens(text) {
|
|
265
|
-
const stripped = text.replace(DEEPSEEK_SPECIAL_TOKEN_REGEX, "");
|
|
266
|
-
if (stripped === text) return text;
|
|
267
|
-
let normalized = stripped;
|
|
268
|
-
if (DEEPSEEK_SPECIAL_TOKEN_AT_START_REGEX.test(text)) normalized = normalized.replace(/^\s+/u, "");
|
|
269
|
-
if (DEEPSEEK_SPECIAL_TOKEN_AT_END_REGEX.test(text)) normalized = normalized.replace(/\s+$/u, "");
|
|
270
|
-
return normalized;
|
|
271
|
-
}
|
|
272
|
-
function getTrailingPartialDeepseekToken(text) {
|
|
273
|
-
let bestIdx = -1;
|
|
274
|
-
for (const delim of DEEPSEEK_OPEN_DELIMS) {
|
|
275
|
-
const idx = text.lastIndexOf(delim);
|
|
276
|
-
if (idx > bestIdx) bestIdx = idx;
|
|
277
|
-
}
|
|
278
|
-
if (bestIdx === -1) return text.endsWith("<") ? "<" : "";
|
|
279
|
-
const tail = text.slice(bestIdx);
|
|
280
|
-
if (tail.includes("|>") || tail.includes("|>")) return "";
|
|
281
|
-
if (tail.length > 256) return "";
|
|
282
|
-
return tail;
|
|
283
|
-
}
|
|
284
|
-
const OPENAI_COMPLETIONS_FIRST_EVENT_TIMEOUT_MESSAGE = "OpenAI completions stream timed out while waiting for the first event";
|
|
285
|
-
const OPENAI_COMPLETIONS_POST_FINISH_GRACE_MS = 2500;
|
|
286
|
-
const OPENAI_COMPLETIONS_ERROR_STATUS_BY_TYPE = {
|
|
287
|
-
SERVICE_UNAVAILABLE: 503,
|
|
288
|
-
TOO_MANY_REQUESTS: 429,
|
|
289
|
-
REQUEST_TIMEOUT: 408
|
|
290
|
-
};
|
|
291
|
-
function parseOpenAICompletionsErrorStatus(value) {
|
|
292
|
-
const status = typeof value === "number" ? value : typeof value === "string" && /^\d{3}$/.test(value.trim()) ? Number(value) : void 0;
|
|
293
|
-
return status !== void 0 && Number.isInteger(status) && status >= 400 && status <= 599 ? status : void 0;
|
|
294
|
-
}
|
|
295
|
-
function createOpenAICompletionsStreamError(chunk, provider) {
|
|
296
|
-
if (!chunk || typeof chunk !== "object") return void 0;
|
|
297
|
-
const error = Reflect.get(chunk, "error");
|
|
298
|
-
const flatMessage = Reflect.get(chunk, "message");
|
|
299
|
-
const structuredError = error !== null && typeof error === "object";
|
|
300
|
-
if (!structuredError && typeof error !== "string" && typeof flatMessage !== "string") return void 0;
|
|
301
|
-
const parsed = OpenAIHttpError.parseEnvelope(chunk, void 0);
|
|
302
|
-
const detail = parsed.detail ?? "Provider returned an in-band OpenAI completions stream error";
|
|
303
|
-
if (!structuredError) return new ProviderResponseError(detail, {
|
|
304
|
-
provider,
|
|
305
|
-
kind: "runtime"
|
|
306
|
-
});
|
|
307
|
-
const typeValue = Reflect.get(error, "type");
|
|
308
|
-
const codeValue = Reflect.get(error, "code");
|
|
309
|
-
const type = typeof typeValue === "string" ? typeValue.trim() : void 0;
|
|
310
|
-
const status = parseOpenAICompletionsErrorStatus(codeValue) ?? (type ? OPENAI_COMPLETIONS_ERROR_STATUS_BY_TYPE[type.toUpperCase()] : void 0);
|
|
311
|
-
if (status === void 0) return new ProviderResponseError(detail, {
|
|
312
|
-
provider,
|
|
313
|
-
kind: "runtime"
|
|
314
|
-
});
|
|
315
|
-
return new ProviderHttpError(`${status} ${detail}`, status, { code: parsed.code });
|
|
316
|
-
}
|
|
317
|
-
const streamOpenAICompletionsOnce = (model, context, options) => {
|
|
318
|
-
const stream = new AssistantMessageEventStream();
|
|
319
|
-
(async () => {
|
|
320
|
-
const startTime = performance.now();
|
|
321
|
-
let firstTokenTime;
|
|
322
|
-
const policy = resolveOpenAICompatForRequest(model, options);
|
|
323
|
-
const output = createInitialResponsesAssistantMessage(model.api, model.provider, model.id);
|
|
324
|
-
let rawRequestDump;
|
|
325
|
-
const abortTracker = createAbortSourceTracker(options?.signal);
|
|
326
|
-
const firstEventTimeoutAbortError = new StreamTimeoutError(OPENAI_COMPLETIONS_FIRST_EVENT_TIMEOUT_MESSAGE);
|
|
327
|
-
const { requestAbortController, requestSignal } = abortTracker;
|
|
328
|
-
const onSseEvent = options?.onSseEvent;
|
|
329
|
-
let sawDoneSentinel = false;
|
|
330
|
-
const rawSseObserver = (event) => {
|
|
331
|
-
if (event.data === "[DONE]") sawDoneSentinel = true;
|
|
332
|
-
if (onSseEvent) {
|
|
333
|
-
if (!event.event && event.data && event.data !== "[DONE]") try {
|
|
334
|
-
const parsed = JSON.parse(event.data);
|
|
335
|
-
const resolvedEvent = typeof parsed.type === "string" ? parsed.type : typeof parsed.object === "string" ? parsed.object : null;
|
|
336
|
-
if (resolvedEvent) {
|
|
337
|
-
event.event = resolvedEvent;
|
|
338
|
-
event.raw = [`event: ${resolvedEvent}`, ...event.raw];
|
|
339
|
-
}
|
|
340
|
-
} catch {}
|
|
341
|
-
onSseEvent(event, model);
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
let finishOpenBlocksOnError = () => {};
|
|
345
|
-
try {
|
|
346
|
-
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
347
|
-
const idleTimeoutFallbackMs = model.compat.streamIdleTimeoutMs;
|
|
348
|
-
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(idleTimeoutFallbackMs);
|
|
349
|
-
const firstEventTimeoutMs = options?.streamFirstEventTimeoutMs ?? getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs, model.compat.streamFirstEventTimeoutMs);
|
|
350
|
-
const requestTimeoutMs = firstEventTimeoutMs !== void 0 && firstEventTimeoutMs > 0 ? firstEventTimeoutMs : void 0;
|
|
351
|
-
const { copilotPremiumRequests, baseUrl, headers, query, requestHeaders } = createRequestSetup(model, context, apiKey, options?.headers, options?.initiatorOverride, getOpenAIPromptCacheKey(options), options?.sessionId);
|
|
352
|
-
const premiumRequestsTotal = copilotPremiumRequests;
|
|
353
|
-
let appliedStrictTools = false;
|
|
354
|
-
const requestReasoningEffortFallbacks = /* @__PURE__ */ new Map();
|
|
355
|
-
const attemptedReasoningEffortFallbacks = /* @__PURE__ */ new Set();
|
|
356
|
-
let activeReasoningEffortFallbackKey;
|
|
357
|
-
let activeRequestParams;
|
|
358
|
-
const providerSessionState = getOpenAICompletionsProviderSessionState(model, baseUrl, options?.providerSessionState);
|
|
359
|
-
const strictToolsScope = getOpenAIStrictToolsScope(model, baseUrl);
|
|
360
|
-
let disableStrictTools = isStrictToolsDisabledForScope(providerSessionState, strictToolsScope);
|
|
361
|
-
const trimmedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
362
|
-
const completionsUrl = query ? `${trimmedBaseUrl}/chat/completions?${new URLSearchParams(query)}` : `${trimmedBaseUrl}/chat/completions`;
|
|
363
|
-
const createCompletionsStream = async (toolStrictModeOverride) => {
|
|
364
|
-
const builtParams = buildParams(model, context, options, disableStrictTools ? "none" : toolStrictModeOverride);
|
|
365
|
-
appliedStrictTools = builtParams.strictToolsApplied;
|
|
366
|
-
let params = builtParams.params;
|
|
367
|
-
const reasoningEffortFallbackKey = createOpenAIReasoningEffortFallbackKey("chat-completions", trimmedBaseUrl, params.model);
|
|
368
|
-
const requestReasoningEffortFallback = requestReasoningEffortFallbacks.has(reasoningEffortFallbackKey) ? requestReasoningEffortFallbacks.get(reasoningEffortFallbackKey) : getOpenAIReasoningEffortFallback(providerSessionState, reasoningEffortFallbackKey);
|
|
369
|
-
if (requestReasoningEffortFallback !== void 0) applyOpenAIReasoningEffortFallback(params, requestReasoningEffortFallback);
|
|
370
|
-
activeReasoningEffortFallbackKey = reasoningEffortFallbackKey;
|
|
371
|
-
const replacedParams = await options?.onPayload?.(params, model);
|
|
372
|
-
if (replacedParams !== void 0) params = replacedParams;
|
|
373
|
-
activeRequestParams = params;
|
|
374
|
-
rawRequestDump = {
|
|
375
|
-
provider: model.provider,
|
|
376
|
-
api: output.api,
|
|
377
|
-
model: model.id,
|
|
378
|
-
method: "POST",
|
|
379
|
-
url: completionsUrl,
|
|
380
|
-
headers: requestHeaders,
|
|
381
|
-
body: params
|
|
382
|
-
};
|
|
383
|
-
let requestTimeout;
|
|
384
|
-
if (requestTimeoutMs !== void 0) requestTimeout = setTimeout(() => abortTracker.abortLocally(firstEventTimeoutAbortError), requestTimeoutMs);
|
|
385
|
-
try {
|
|
386
|
-
const headersWithTimeout = { ...headers };
|
|
387
|
-
if (requestTimeoutMs !== void 0) headersWithTimeout["X-Stainless-Timeout"] = Math.floor(requestTimeoutMs / 1e3).toString();
|
|
388
|
-
const { events, response, requestId } = await postOpenAIStream({
|
|
389
|
-
url: completionsUrl,
|
|
390
|
-
headers: headersWithTimeout,
|
|
391
|
-
body: params,
|
|
392
|
-
signal: requestSignal,
|
|
393
|
-
fetch: wrapFetchForCopilotFallback(options?.fetch, model.provider === "github-copilot", resolveCopilotRequestIdentity(options?.headers)),
|
|
394
|
-
onSseEvent: rawSseObserver
|
|
395
|
-
});
|
|
396
|
-
await notifyProviderResponse(options, response, model, requestId);
|
|
397
|
-
return events;
|
|
398
|
-
} finally {
|
|
399
|
-
clearTimeout(requestTimeout);
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
let openaiStream;
|
|
403
|
-
try {
|
|
404
|
-
openaiStream = await createCompletionsStream();
|
|
405
|
-
} catch (error) {
|
|
406
|
-
const capturedErrorResponse = error instanceof OpenAIHttpError ? error.captured : void 0;
|
|
407
|
-
const isExplicitDisable = options?.disableReasoning === true;
|
|
408
|
-
const reasoningEffortFallback = activeReasoningEffortFallbackKey && activeRequestParams && !requestSignal.aborted ? resolveOpenAIReasoningEffortFallback(error, capturedErrorResponse, activeRequestParams, { explicitDisable: isExplicitDisable }) : void 0;
|
|
409
|
-
if (reasoningEffortFallback !== void 0 && activeReasoningEffortFallbackKey) {
|
|
410
|
-
const retryMarker = `${activeReasoningEffortFallbackKey}:${String(reasoningEffortFallback)}`;
|
|
411
|
-
if (attemptedReasoningEffortFallbacks.has(retryMarker)) throw error;
|
|
412
|
-
attemptedReasoningEffortFallbacks.add(retryMarker);
|
|
413
|
-
requestReasoningEffortFallbacks.set(activeReasoningEffortFallbackKey, reasoningEffortFallback);
|
|
414
|
-
openaiStream = await createCompletionsStream();
|
|
415
|
-
if (!isExplicitDisable) rememberOpenAIReasoningEffortFallback(providerSessionState, activeReasoningEffortFallbackKey, reasoningEffortFallback);
|
|
416
|
-
} else if (model.compat.retryWithoutStrictOnGrammarError && !disableStrictTools && isCompiledGrammarTooLargeStrictError(error, capturedErrorResponse)) {
|
|
417
|
-
disableStrictToolsForScope(providerSessionState, strictToolsScope);
|
|
418
|
-
disableStrictTools = true;
|
|
419
|
-
openaiStream = await createCompletionsStream("none");
|
|
420
|
-
} else {
|
|
421
|
-
if (!shouldRetryWithoutStrictTools(error, capturedErrorResponse, {
|
|
422
|
-
model,
|
|
423
|
-
strictToolsApplied: appliedStrictTools,
|
|
424
|
-
tools: context.tools
|
|
425
|
-
})) throw error;
|
|
426
|
-
disableStrictToolsForScope(providerSessionState, strictToolsScope);
|
|
427
|
-
disableStrictTools = true;
|
|
428
|
-
openaiStream = await createCompletionsStream("none");
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
if (premiumRequestsTotal !== void 0) output.usage.premiumRequests = premiumRequestsTotal;
|
|
432
|
-
stream.push({
|
|
433
|
-
type: "start",
|
|
434
|
-
partial: output
|
|
435
|
-
});
|
|
436
|
-
const stripDeepseekChatTemplateTokens = policy.stream.stripSpecialTokens === "deepseek";
|
|
437
|
-
const pendingToolCallBlocks = [];
|
|
438
|
-
const toolCallBlockByIndex = /* @__PURE__ */ new Map();
|
|
439
|
-
const unkeyedBatchBlocks = [];
|
|
440
|
-
const clearUnkeyedBatchSlot = (block) => {
|
|
441
|
-
for (let index = 0; index < unkeyedBatchBlocks.length; index++) if (unkeyedBatchBlocks[index] === block) unkeyedBatchBlocks[index] = void 0;
|
|
442
|
-
};
|
|
443
|
-
let currentBlock;
|
|
444
|
-
const blockIndex = (block) => {
|
|
445
|
-
if (!block) return Math.max(0, output.content.length - 1);
|
|
446
|
-
return output.content.indexOf(block);
|
|
447
|
-
};
|
|
448
|
-
const finishToolCallBlock = (block) => {
|
|
449
|
-
if (block.partialArgs === void 0) return;
|
|
450
|
-
const contentIndex = blockIndex(block);
|
|
451
|
-
if (contentIndex < 0) return;
|
|
452
|
-
if (typeof block.partialArgs === "object" && !Array.isArray(block.partialArgs)) {
|
|
453
|
-
const fullJson = JSON.stringify(block.partialArgs);
|
|
454
|
-
if (fullJson.length > 0 && fullJson !== "{}") stream.push({
|
|
455
|
-
type: "toolcall_delta",
|
|
456
|
-
contentIndex,
|
|
457
|
-
delta: fullJson,
|
|
458
|
-
partial: output
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
block.arguments = typeof block.partialArgs === "string" ? parseStreamingJson(block.partialArgs) : block.partialArgs;
|
|
462
|
-
delete block.partialArgs;
|
|
463
|
-
if (block.streamIndex !== void 0) {
|
|
464
|
-
toolCallBlockByIndex.delete(block.streamIndex);
|
|
465
|
-
delete block.streamIndex;
|
|
466
|
-
}
|
|
467
|
-
const pendingIndex = pendingToolCallBlocks.indexOf(block);
|
|
468
|
-
if (pendingIndex >= 0) pendingToolCallBlocks.splice(pendingIndex, 1);
|
|
469
|
-
clearUnkeyedBatchSlot(block);
|
|
470
|
-
stream.push({
|
|
471
|
-
type: "toolcall_end",
|
|
472
|
-
contentIndex,
|
|
473
|
-
toolCall: block,
|
|
474
|
-
partial: output
|
|
475
|
-
});
|
|
476
|
-
};
|
|
477
|
-
const finishPendingToolCallBlocks = () => {
|
|
478
|
-
for (const block of Array.from(pendingToolCallBlocks)) finishToolCallBlock(block);
|
|
479
|
-
};
|
|
480
|
-
const finishCurrentBlock = (block) => {
|
|
481
|
-
if (!block) return;
|
|
482
|
-
const contentIndex = blockIndex(block);
|
|
483
|
-
if (contentIndex < 0) return;
|
|
484
|
-
if (block.type === "text") {
|
|
485
|
-
stream.push({
|
|
486
|
-
type: "text_end",
|
|
487
|
-
contentIndex,
|
|
488
|
-
content: block.text,
|
|
489
|
-
partial: output
|
|
490
|
-
});
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
if (block.type === "thinking") {
|
|
494
|
-
stream.push({
|
|
495
|
-
type: "thinking_end",
|
|
496
|
-
contentIndex,
|
|
497
|
-
content: block.thinking,
|
|
498
|
-
partial: output
|
|
499
|
-
});
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
finishToolCallBlock(block);
|
|
503
|
-
};
|
|
504
|
-
finishOpenBlocksOnError = () => {
|
|
505
|
-
if (currentBlock?.type !== "toolCall") finishCurrentBlock(currentBlock);
|
|
506
|
-
finishPendingToolCallBlocks();
|
|
507
|
-
};
|
|
508
|
-
const appendText = (message, eventStream, text) => {
|
|
509
|
-
if (currentBlock?.type !== "text") {
|
|
510
|
-
if (currentBlock?.type !== "toolCall") finishCurrentBlock(currentBlock);
|
|
511
|
-
currentBlock = {
|
|
512
|
-
type: "text",
|
|
513
|
-
text: ""
|
|
514
|
-
};
|
|
515
|
-
message.content.push(currentBlock);
|
|
516
|
-
eventStream.push({
|
|
517
|
-
type: "text_start",
|
|
518
|
-
contentIndex: blockIndex(currentBlock),
|
|
519
|
-
partial: message
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
currentBlock.text += text;
|
|
523
|
-
eventStream.push({
|
|
524
|
-
type: "text_delta",
|
|
525
|
-
contentIndex: blockIndex(currentBlock),
|
|
526
|
-
delta: text,
|
|
527
|
-
partial: message
|
|
528
|
-
});
|
|
529
|
-
};
|
|
530
|
-
const appendThinking = (message, eventStream, thinking, signature) => {
|
|
531
|
-
if (currentBlock?.type !== "thinking" || signature !== void 0 && currentBlock.thinkingSignature !== signature) {
|
|
532
|
-
if (currentBlock?.type !== "toolCall") finishCurrentBlock(currentBlock);
|
|
533
|
-
currentBlock = {
|
|
534
|
-
type: "thinking",
|
|
535
|
-
thinking: "",
|
|
536
|
-
thinkingSignature: signature
|
|
537
|
-
};
|
|
538
|
-
message.content.push(currentBlock);
|
|
539
|
-
eventStream.push({
|
|
540
|
-
type: "thinking_start",
|
|
541
|
-
contentIndex: blockIndex(currentBlock),
|
|
542
|
-
partial: message
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
if (signature !== void 0 && !currentBlock.thinkingSignature) currentBlock.thinkingSignature = signature;
|
|
546
|
-
currentBlock.thinking += thinking;
|
|
547
|
-
eventStream.push({
|
|
548
|
-
type: "thinking_delta",
|
|
549
|
-
contentIndex: blockIndex(currentBlock),
|
|
550
|
-
delta: thinking,
|
|
551
|
-
partial: message
|
|
552
|
-
});
|
|
553
|
-
};
|
|
554
|
-
const appendTextDelta = (text) => {
|
|
555
|
-
if (!text) return;
|
|
556
|
-
if (!firstTokenTime) firstTokenTime = performance.now();
|
|
557
|
-
appendText(output, stream, text);
|
|
558
|
-
};
|
|
559
|
-
const lastCumulativeReasoningBySignature = /* @__PURE__ */ new Map();
|
|
560
|
-
const appendThinkingDelta = (thinking, signature, source = "delta") => {
|
|
561
|
-
if (!thinking) return;
|
|
562
|
-
let emittedThinking = thinking;
|
|
563
|
-
if (source === "cumulative") {
|
|
564
|
-
const key = signature ?? "";
|
|
565
|
-
const lastSnapshot = lastCumulativeReasoningBySignature.get(key) ?? "";
|
|
566
|
-
if (thinking.startsWith(lastSnapshot)) emittedThinking = thinking.slice(lastSnapshot.length);
|
|
567
|
-
lastCumulativeReasoningBySignature.set(key, thinking);
|
|
568
|
-
if (!emittedThinking) return;
|
|
569
|
-
}
|
|
570
|
-
if (!firstTokenTime) firstTokenTime = performance.now();
|
|
571
|
-
appendThinking(output, stream, emittedThinking, signature);
|
|
572
|
-
};
|
|
573
|
-
let deepseekStripBuffer = "";
|
|
574
|
-
const flushDeepseekStripBuffer = (final) => {
|
|
575
|
-
if (deepseekStripBuffer.length === 0) return;
|
|
576
|
-
let flushable;
|
|
577
|
-
if (final) {
|
|
578
|
-
flushable = deepseekStripBuffer;
|
|
579
|
-
deepseekStripBuffer = "";
|
|
580
|
-
} else {
|
|
581
|
-
const trailing = getTrailingPartialDeepseekToken(deepseekStripBuffer);
|
|
582
|
-
flushable = deepseekStripBuffer.slice(0, deepseekStripBuffer.length - trailing.length);
|
|
583
|
-
deepseekStripBuffer = trailing;
|
|
584
|
-
}
|
|
585
|
-
const stripped = stripDeepseekSpecialTokens(flushable);
|
|
586
|
-
if (stripped && (stripped === flushable || stripped.trim().length > 0)) appendTextDelta(stripped);
|
|
587
|
-
};
|
|
588
|
-
const appendProcessedText = (processedText) => {
|
|
589
|
-
if (processedText.length === 0) return;
|
|
590
|
-
if (stripDeepseekChatTemplateTokens) {
|
|
591
|
-
deepseekStripBuffer += processedText;
|
|
592
|
-
flushDeepseekStripBuffer(false);
|
|
593
|
-
} else appendTextDelta(processedText);
|
|
594
|
-
};
|
|
595
|
-
const streamMarkupHealingPattern = policy.stream.markupHealingPattern;
|
|
596
|
-
const streamMarkupHealing = streamMarkupHealingPattern ? new StreamMarkupHealing({ pattern: streamMarkupHealingPattern }) : void 0;
|
|
597
|
-
const explicitReasoningDeltasMayBeCumulative = policy.stream.reasoningDeltasMayBeCumulative;
|
|
598
|
-
let suppressHealedThinking = false;
|
|
599
|
-
let healedToolCallEmitted = false;
|
|
600
|
-
const emitHealedToolCall = (call) => {
|
|
601
|
-
finishCurrentBlock(currentBlock);
|
|
602
|
-
const block = {
|
|
603
|
-
type: "toolCall",
|
|
604
|
-
id: call.id,
|
|
605
|
-
name: call.name,
|
|
606
|
-
arguments: {},
|
|
607
|
-
partialArgs: call.arguments
|
|
608
|
-
};
|
|
609
|
-
block.arguments = parseStreamingJson(call.arguments);
|
|
610
|
-
currentBlock = block;
|
|
611
|
-
output.content.push(block);
|
|
612
|
-
stream.push({
|
|
613
|
-
type: "toolcall_start",
|
|
614
|
-
contentIndex: blockIndex(block),
|
|
615
|
-
partial: output
|
|
616
|
-
});
|
|
617
|
-
stream.push({
|
|
618
|
-
type: "toolcall_delta",
|
|
619
|
-
contentIndex: blockIndex(block),
|
|
620
|
-
delta: call.arguments,
|
|
621
|
-
partial: output
|
|
622
|
-
});
|
|
623
|
-
finishCurrentBlock(block);
|
|
624
|
-
currentBlock = void 0;
|
|
625
|
-
healedToolCallEmitted = true;
|
|
626
|
-
};
|
|
627
|
-
const emitHealingEvent = (event, suppressThinking) => {
|
|
628
|
-
if (event.type === "text") appendProcessedText(event.text);
|
|
629
|
-
else if (event.type === "thinking") {
|
|
630
|
-
if (!suppressThinking) appendThinkingDelta(event.thinking);
|
|
631
|
-
} else emitHealedToolCall(event.call);
|
|
632
|
-
};
|
|
633
|
-
const flushHealedToolCalls = () => {
|
|
634
|
-
if (!streamMarkupHealing) return;
|
|
635
|
-
const calls = streamMarkupHealing.drainCompleted();
|
|
636
|
-
for (const call of calls) emitHealedToolCall(call);
|
|
637
|
-
};
|
|
638
|
-
let streamFinishedAt;
|
|
639
|
-
let sawUsagePayload = false;
|
|
640
|
-
let awaitTrailingUsageDetails = false;
|
|
641
|
-
const applyUsagePayload = (rawUsage) => {
|
|
642
|
-
output.usage = parseChunkUsage(rawUsage, model, premiumRequestsTotal, output.timestamp);
|
|
643
|
-
sawUsagePayload = true;
|
|
644
|
-
awaitTrailingUsageDetails = !hasPositiveCacheReadTokenField(rawUsage);
|
|
645
|
-
};
|
|
646
|
-
const timedOpenaiStream = iterateWithIdleTimeout(openaiStream, {
|
|
647
|
-
idleTimeoutMs,
|
|
648
|
-
firstItemTimeoutMs: firstEventTimeoutMs,
|
|
649
|
-
firstItemErrorMessage: OPENAI_COMPLETIONS_FIRST_EVENT_TIMEOUT_MESSAGE,
|
|
650
|
-
errorMessage: "OpenAI completions stream stalled while waiting for the next event",
|
|
651
|
-
onIdle: () => requestAbortController.abort(),
|
|
652
|
-
onFirstItemTimeout: () => abortTracker.abortLocally(firstEventTimeoutAbortError),
|
|
653
|
-
abortSignal: options?.signal,
|
|
654
|
-
isProgressItem: isOpenAICompletionsProgressChunk
|
|
655
|
-
});
|
|
656
|
-
const terminalAwareStream = iterateWithTerminalGrace(timedOpenaiStream, {
|
|
657
|
-
finishedAtMs: () => streamFinishedAt,
|
|
658
|
-
graceMs: OPENAI_COMPLETIONS_POST_FINISH_GRACE_MS,
|
|
659
|
-
onGraceEnd: () => requestAbortController.abort()
|
|
660
|
-
});
|
|
661
|
-
for await (const chunk of terminalAwareStream) {
|
|
662
|
-
if (!chunk || typeof chunk !== "object") continue;
|
|
663
|
-
const streamError = createOpenAICompletionsStreamError(chunk, model.provider);
|
|
664
|
-
if (streamError) throw streamError;
|
|
665
|
-
output.responseId ||= chunk.id;
|
|
666
|
-
if (!output.upstreamProvider) {
|
|
667
|
-
const upstreamProvider = chunk.provider;
|
|
668
|
-
output.upstreamProvider = typeof upstreamProvider === "string" && upstreamProvider.length > 0 ? upstreamProvider : void 0;
|
|
669
|
-
}
|
|
670
|
-
if (chunk.usage) applyUsagePayload(chunk.usage);
|
|
671
|
-
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
672
|
-
if (!choice) {
|
|
673
|
-
if (streamFinishedAt !== void 0 && sawUsagePayload) break;
|
|
674
|
-
continue;
|
|
675
|
-
}
|
|
676
|
-
if (!chunk.usage) {
|
|
677
|
-
const choiceUsage = choice.usage;
|
|
678
|
-
if (typeof choiceUsage === "object" && choiceUsage !== null) applyUsagePayload(choiceUsage);
|
|
679
|
-
}
|
|
680
|
-
if (choice.finish_reason) {
|
|
681
|
-
const finishReasonResult = mapStopReason(choice.finish_reason);
|
|
682
|
-
output.stopReason = finishReasonResult.stopReason;
|
|
683
|
-
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
684
|
-
streamFinishedAt ??= Date.now();
|
|
685
|
-
}
|
|
686
|
-
if (choice.delta) {
|
|
687
|
-
const reasoningFields = [
|
|
688
|
-
"reasoning_content",
|
|
689
|
-
"reasoning",
|
|
690
|
-
"reasoning_text"
|
|
691
|
-
];
|
|
692
|
-
const deltaRecord = choice.delta;
|
|
693
|
-
let foundReasoningField;
|
|
694
|
-
let foundReasoningDelta = "";
|
|
695
|
-
for (const field of reasoningFields) {
|
|
696
|
-
const reasoningDelta = deltaRecord[field];
|
|
697
|
-
if (typeof reasoningDelta === "string" && reasoningDelta.length > 0) {
|
|
698
|
-
foundReasoningField = field;
|
|
699
|
-
foundReasoningDelta = reasoningDelta;
|
|
700
|
-
break;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
if (foundReasoningField) {
|
|
704
|
-
appendThinkingDelta(foundReasoningDelta, foundReasoningField, explicitReasoningDeltasMayBeCumulative ? "cumulative" : "delta");
|
|
705
|
-
suppressHealedThinking = true;
|
|
706
|
-
}
|
|
707
|
-
const normalizedDeltaText = normalizeStreamingContentText(choice.delta.content);
|
|
708
|
-
if (normalizedDeltaText.length > 0) {
|
|
709
|
-
if (!firstTokenTime) firstTokenTime = performance.now();
|
|
710
|
-
const hasStructuredToolCalls = Array.isArray(choice.delta.tool_calls) && choice.delta.tool_calls.length > 0;
|
|
711
|
-
if (streamMarkupHealing) {
|
|
712
|
-
const healingEvents = hasStructuredToolCalls ? streamMarkupHealing.feedEventsWithoutCalls(normalizedDeltaText) : streamMarkupHealing.feedEvents(normalizedDeltaText);
|
|
713
|
-
for (const event of healingEvents) emitHealingEvent(event, suppressHealedThinking);
|
|
714
|
-
} else appendProcessedText(normalizedDeltaText);
|
|
715
|
-
}
|
|
716
|
-
if (choice?.delta?.tool_calls && choice.delta.tool_calls.length > 0) {
|
|
717
|
-
const toolCalls = choice.delta.tool_calls;
|
|
718
|
-
for (let toolCallOffset = 0; toolCallOffset < toolCalls.length; toolCallOffset++) {
|
|
719
|
-
const toolCall = toolCalls[toolCallOffset];
|
|
720
|
-
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
|
|
721
|
-
const incomingName = toolCall.function?.name || "";
|
|
722
|
-
const unkeyedBatchedArrayEntry = toolCalls.length > 1 && streamIndex === void 0 && !toolCall.id;
|
|
723
|
-
let block = streamIndex !== void 0 ? toolCallBlockByIndex.get(streamIndex) : void 0;
|
|
724
|
-
if (!block && toolCall.id) block = pendingToolCallBlocks.find((candidate) => candidate.id === toolCall.id);
|
|
725
|
-
if (!block && unkeyedBatchedArrayEntry) {
|
|
726
|
-
const offsetBlock = unkeyedBatchBlocks[toolCallOffset];
|
|
727
|
-
if (offsetBlock && offsetBlock.partialArgs !== void 0) block = offsetBlock;
|
|
728
|
-
}
|
|
729
|
-
if (!block && !unkeyedBatchedArrayEntry && currentBlock?.type === "toolCall" && (!toolCall.id || currentBlock.id === toolCall.id)) block = currentBlock;
|
|
730
|
-
if (!block) {
|
|
731
|
-
if (currentBlock?.type !== "toolCall") finishCurrentBlock(currentBlock);
|
|
732
|
-
block = {
|
|
733
|
-
type: "toolCall",
|
|
734
|
-
id: toolCall.id || "",
|
|
735
|
-
name: incomingName,
|
|
736
|
-
arguments: {},
|
|
737
|
-
partialArgs: "",
|
|
738
|
-
streamIndex
|
|
739
|
-
};
|
|
740
|
-
if (streamIndex !== void 0) toolCallBlockByIndex.set(streamIndex, block);
|
|
741
|
-
pendingToolCallBlocks.push(block);
|
|
742
|
-
currentBlock = block;
|
|
743
|
-
output.content.push(block);
|
|
744
|
-
stream.push({
|
|
745
|
-
type: "toolcall_start",
|
|
746
|
-
contentIndex: blockIndex(block),
|
|
747
|
-
partial: output
|
|
748
|
-
});
|
|
749
|
-
if (unkeyedBatchedArrayEntry) unkeyedBatchBlocks[toolCallOffset] = block;
|
|
750
|
-
} else {
|
|
751
|
-
if (currentBlock !== block && currentBlock && currentBlock.type !== "toolCall") finishCurrentBlock(currentBlock);
|
|
752
|
-
currentBlock = block;
|
|
753
|
-
if (streamIndex !== void 0 && block.streamIndex === void 0) {
|
|
754
|
-
block.streamIndex = streamIndex;
|
|
755
|
-
toolCallBlockByIndex.set(streamIndex, block);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
if (toolCall.id) block.id = toolCall.id;
|
|
759
|
-
if (incomingName) block.name = incomingName;
|
|
760
|
-
const extraContent = getGeminiThoughtSignatureExtraContent(Reflect.get(toolCall, "extra_content"));
|
|
761
|
-
if (extraContent) block.thoughtSignature = JSON.stringify(extraContent);
|
|
762
|
-
let delta = "";
|
|
763
|
-
const rawArgs = toolCall.function?.arguments;
|
|
764
|
-
if (typeof rawArgs === "string") {
|
|
765
|
-
if (rawArgs.length > 0) {
|
|
766
|
-
delta = rawArgs;
|
|
767
|
-
const prev = typeof block.partialArgs === "string" ? block.partialArgs : "";
|
|
768
|
-
block.partialArgs = prev + rawArgs;
|
|
769
|
-
const throttled = parseStreamingJsonThrottled(block.partialArgs, block[kStreamingLastParseLen] ?? 0);
|
|
770
|
-
if (throttled) {
|
|
771
|
-
block.arguments = throttled.value;
|
|
772
|
-
block[kStreamingLastParseLen] = throttled.parsedLen;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
} else if (rawArgs && typeof rawArgs === "object" && !Array.isArray(rawArgs)) {
|
|
776
|
-
const merged = mergeStreamingArgumentObjects(block.partialArgs !== null && typeof block.partialArgs === "object" && !Array.isArray(block.partialArgs) ? block.partialArgs : void 0, rawArgs);
|
|
777
|
-
block.partialArgs = merged;
|
|
778
|
-
block.arguments = merged;
|
|
779
|
-
}
|
|
780
|
-
stream.push({
|
|
781
|
-
type: "toolcall_delta",
|
|
782
|
-
contentIndex: blockIndex(block),
|
|
783
|
-
delta,
|
|
784
|
-
partial: output
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
const reasoningDetails = choice.delta.reasoning_details;
|
|
789
|
-
if (Array.isArray(reasoningDetails)) for (const detail of reasoningDetails) {
|
|
790
|
-
if (!detail || typeof detail !== "object") continue;
|
|
791
|
-
const detailObject = detail;
|
|
792
|
-
if (detailObject.type === "reasoning.encrypted" && detailObject.id && detailObject.data) {
|
|
793
|
-
const matchingToolCall = output.content.find((b) => b.type === "toolCall" && b.id === detailObject.id);
|
|
794
|
-
if (matchingToolCall) matchingToolCall.thoughtSignature = JSON.stringify(detailObject);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
if (streamFinishedAt !== void 0 && sawUsagePayload && !awaitTrailingUsageDetails) break;
|
|
799
|
-
}
|
|
800
|
-
if (streamMarkupHealing) {
|
|
801
|
-
for (const event of streamMarkupHealing.flushEvents()) emitHealingEvent(event, suppressHealedThinking);
|
|
802
|
-
flushHealedToolCalls();
|
|
803
|
-
if (healedToolCallEmitted && output.stopReason === "stop") output.stopReason = "toolUse";
|
|
804
|
-
}
|
|
805
|
-
if (stripDeepseekChatTemplateTokens) flushDeepseekStripBuffer(true);
|
|
806
|
-
if (streamFinishedAt === void 0 && !sawDoneSentinel && output.content.length > 0) throw new ProviderResponseError("OpenAI completions stream closed before a finish_reason was received", {
|
|
807
|
-
provider: model.provider,
|
|
808
|
-
kind: "incomplete-stream"
|
|
809
|
-
});
|
|
810
|
-
if (currentBlock?.type === "toolCall") finishPendingToolCallBlocks();
|
|
811
|
-
else {
|
|
812
|
-
finishCurrentBlock(currentBlock);
|
|
813
|
-
finishPendingToolCallBlocks();
|
|
814
|
-
}
|
|
815
|
-
if (output.stopReason === "stop" && output.content.some((b) => b.type === "toolCall")) output.stopReason = "toolUse";
|
|
816
|
-
if (policy.stream.emptyLengthFinishIsContextError && output.stopReason === "length" && !hasVisibleAssistantContent(output)) {
|
|
817
|
-
output.stopReason = "error";
|
|
818
|
-
output.errorMessage = EMPTY_OLLAMA_LENGTH_COMPLETION_MESSAGE;
|
|
819
|
-
}
|
|
820
|
-
const localAbortReason = abortTracker.getLocalAbortReason();
|
|
821
|
-
if (localAbortReason) throw localAbortReason;
|
|
822
|
-
if (abortTracker.wasCallerAbort()) throw new AbortError();
|
|
823
|
-
if (output.stopReason === "aborted") throw new AbortError();
|
|
824
|
-
if (output.stopReason === "error") throw new ProviderResponseError(output.errorMessage || "Provider returned an error stop reason", {
|
|
825
|
-
provider: model.provider,
|
|
826
|
-
kind: "runtime"
|
|
827
|
-
});
|
|
828
|
-
output.errorMessage = void 0;
|
|
829
|
-
output.duration = performance.now() - startTime;
|
|
830
|
-
if (firstTokenTime) output.ttft = firstTokenTime - startTime;
|
|
831
|
-
stream.push({
|
|
832
|
-
type: "done",
|
|
833
|
-
reason: output.stopReason,
|
|
834
|
-
message: output
|
|
835
|
-
});
|
|
836
|
-
stream.end();
|
|
837
|
-
} catch (error) {
|
|
838
|
-
try {
|
|
839
|
-
finishOpenBlocksOnError();
|
|
840
|
-
} catch {}
|
|
841
|
-
const capturedErrorResponse = error instanceof OpenAIHttpError ? error.captured : void 0;
|
|
842
|
-
const result = await finalize(error, {
|
|
843
|
-
api: model.api,
|
|
844
|
-
provider: model.provider,
|
|
845
|
-
abortTracker,
|
|
846
|
-
rawRequestDump,
|
|
847
|
-
capturedErrorResponse
|
|
848
|
-
});
|
|
849
|
-
output.stopReason = result.stopReason;
|
|
850
|
-
output.errorStatus = result.status;
|
|
851
|
-
output.errorId = result.id;
|
|
852
|
-
output.errorMessage = result.message;
|
|
853
|
-
const rawMetadata = error?.error?.metadata?.raw;
|
|
854
|
-
if (rawMetadata) output.errorMessage += `\n${rawMetadata}`;
|
|
855
|
-
output.duration = performance.now() - startTime;
|
|
856
|
-
if (firstTokenTime) output.ttft = firstTokenTime - startTime;
|
|
857
|
-
stream.push({
|
|
858
|
-
type: "error",
|
|
859
|
-
reason: output.stopReason,
|
|
860
|
-
error: output
|
|
861
|
-
});
|
|
862
|
-
stream.end();
|
|
863
|
-
}
|
|
864
|
-
})();
|
|
865
|
-
return stream;
|
|
866
|
-
};
|
|
867
|
-
/**
|
|
868
|
-
* Retries benign empty completions and transient provider failures only before
|
|
869
|
-
* assistant output commits the attempt.
|
|
870
|
-
*/
|
|
871
|
-
const streamOpenAICompletions = (model, context, options) => withReplaySafeStreamRetry(model, context, options, streamOpenAICompletionsOnce, {
|
|
872
|
-
retryEmptyCompletion: true,
|
|
873
|
-
retryProviderErrors: true,
|
|
874
|
-
maxProviderErrorRetries: 1
|
|
875
|
-
});
|
|
876
|
-
function createRequestSetup(model, context, apiKey, extraHeaders, initiatorOverride, promptCacheSessionId, sessionId) {
|
|
877
|
-
const apiVersion = $env.AZURE_OPENAI_API_VERSION || "2024-10-21";
|
|
878
|
-
const deploymentName = parseAzureDeploymentNameMap($env.AZURE_OPENAI_DEPLOYMENT_NAME_MAP).get(model.id) ?? model.id;
|
|
879
|
-
const setup = resolveOpenAIRequestSetup(model, {
|
|
880
|
-
apiKey,
|
|
881
|
-
extraHeaders,
|
|
882
|
-
initiatorOverride,
|
|
883
|
-
promptCacheSessionId,
|
|
884
|
-
sessionId,
|
|
885
|
-
messages: context.messages,
|
|
886
|
-
defaultBaseUrl: "https://api.openai.com/v1",
|
|
887
|
-
prependHeaders: model.provider === "kimi-code" ? getKimiCommonHeaders : model.provider === "cline-pass" ? () => clinePassClientHeaders(promptCacheSessionId) : void 0,
|
|
888
|
-
alibabaCodingPlanAuth: true,
|
|
889
|
-
azureChatCompletions: {
|
|
890
|
-
apiVersion,
|
|
891
|
-
deploymentName
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
if (!setup.baseUrl) throw new ConfigurationError("OpenAI request setup did not resolve a base URL");
|
|
895
|
-
return setup;
|
|
896
|
-
}
|
|
897
|
-
function resolveOpenAICompatForRequest(model, options) {
|
|
898
|
-
return resolveOpenAICompatPolicy(model, {
|
|
899
|
-
endpoint: "chat-completions",
|
|
900
|
-
reasoning: options?.reasoning,
|
|
901
|
-
disableReasoning: options?.disableReasoning,
|
|
902
|
-
toolChoice: mapToOpenAICompletionsToolChoice(options?.toolChoice)
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
function dropOpenRouterKimiForcedToolReasoning(params, _model, policy) {
|
|
906
|
-
if (policy.reasoning.disableReason === "forced-tool-choice" && policy.reasoning.disableMode === "openrouter-enabled-false" && policy.compat.isOpenRouterHost) delete params.reasoning;
|
|
907
|
-
}
|
|
908
|
-
function hasActiveNativeKimiK3Reasoning(model, options) {
|
|
909
|
-
if (!model.compat.nativeKimiK3Reasoning || !model.reasoning) return false;
|
|
910
|
-
return options?.reasoning !== void 0 && !options.disableReasoning;
|
|
911
|
-
}
|
|
912
|
-
function isChatCompletionsPromptCacheableContentBlock(block) {
|
|
913
|
-
if (typeof block !== "object" || block === null || !("type" in block)) return false;
|
|
914
|
-
return block.type === "text" || block.type === "image_url" || block.type === "input_audio" || block.type === "file";
|
|
915
|
-
}
|
|
916
|
-
function markLatestStableChatCompletionsCacheBreakpoint(messages) {
|
|
917
|
-
let latestInputMessage = -1;
|
|
918
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
919
|
-
const message = messages[i];
|
|
920
|
-
if (message.role === "user" || message.role === "developer") {
|
|
921
|
-
latestInputMessage = i;
|
|
922
|
-
break;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (latestInputMessage <= 0) return false;
|
|
926
|
-
for (let i = latestInputMessage - 1; i >= 0; i--) {
|
|
927
|
-
const message = messages[i];
|
|
928
|
-
if (message.role !== "user" && message.role !== "developer" && message.role !== "system") continue;
|
|
929
|
-
if (typeof message.content === "string") {
|
|
930
|
-
messages[i] = {
|
|
931
|
-
...message,
|
|
932
|
-
content: [{
|
|
933
|
-
type: "text",
|
|
934
|
-
text: message.content,
|
|
935
|
-
prompt_cache_breakpoint: { mode: "explicit" }
|
|
936
|
-
}]
|
|
937
|
-
};
|
|
938
|
-
return true;
|
|
939
|
-
}
|
|
940
|
-
for (let j = message.content.length - 1; j >= 0; j--) {
|
|
941
|
-
const block = message.content[j];
|
|
942
|
-
if (!isChatCompletionsPromptCacheableContentBlock(block)) continue;
|
|
943
|
-
Object.assign(block, { prompt_cache_breakpoint: { mode: "explicit" } });
|
|
944
|
-
return true;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
return false;
|
|
948
|
-
}
|
|
949
|
-
function applyOpenAIChatCompletionsPromptCachePolicy(params, model, options) {
|
|
950
|
-
const promptCacheKey = getOpenAIPromptCacheKey(options);
|
|
951
|
-
if (model.compat.supportsPromptCacheKey && promptCacheKey !== void 0) params.prompt_cache_key = promptCacheKey;
|
|
952
|
-
const promptCache = options?.promptCache;
|
|
953
|
-
if (!promptCache || resolveCacheRetention(options?.cacheRetention) === "none") return;
|
|
954
|
-
if (!model.compat.supportsPromptCacheBreakpoints) {
|
|
955
|
-
if (promptCache.mode === "explicit") throw new ConfigurationError(`OpenAI explicit prompt caching is unsupported for ${model.provider}/${model.id}; enable compat.supportsPromptCacheBreakpoints only for a compatible endpoint.`);
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
|
-
params.prompt_cache_key = promptCacheKey;
|
|
959
|
-
params.prompt_cache_options = {
|
|
960
|
-
mode: promptCache.mode,
|
|
961
|
-
ttl: promptCache.ttl ?? model.compat.promptCacheBreakpointTtl
|
|
962
|
-
};
|
|
963
|
-
if (promptCache.mode === "explicit" && promptCache.breakpoint !== "none") markLatestStableChatCompletionsCacheBreakpoint(params.messages);
|
|
964
|
-
}
|
|
965
|
-
function buildParams(model, context, options, toolStrictModeOverride) {
|
|
966
|
-
const initialCompat = resolveOpenAICompatForRequest(model, options).compat;
|
|
967
|
-
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
968
|
-
const params = {
|
|
969
|
-
model: resolveOpenAICompletionsModelId(model, options),
|
|
970
|
-
messages: [],
|
|
971
|
-
stream: true
|
|
972
|
-
};
|
|
973
|
-
let toolStrictMode = "none";
|
|
974
|
-
let strictToolsApplied = false;
|
|
975
|
-
if (initialCompat.supportsUsageInStreaming !== false) params.stream_options = { include_usage: true };
|
|
976
|
-
if (initialCompat.supportsStore) params.store = false;
|
|
977
|
-
if (initialCompat.supportsSamplingParams) {
|
|
978
|
-
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
979
|
-
if (options?.topP !== void 0) params.top_p = options.topP;
|
|
980
|
-
if (options?.topK !== void 0) params.top_k = options.topK;
|
|
981
|
-
if (options?.minP !== void 0) params.min_p = options.minP;
|
|
982
|
-
if (initialCompat.supportsPenaltyAndStopParams) {
|
|
983
|
-
if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
|
|
984
|
-
if (options?.repetitionPenalty !== void 0) params.repetition_penalty = options.repetitionPenalty;
|
|
985
|
-
if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
if (options?.stopSequences?.length && initialCompat.supportsPenaltyAndStopParams) {
|
|
989
|
-
const seqs = options.stopSequences;
|
|
990
|
-
params.stop = seqs.length === 1 ? seqs[0] : seqs.slice(0, 4);
|
|
991
|
-
}
|
|
992
|
-
applyOpenAIServiceTier(params, options?.serviceTier, model);
|
|
993
|
-
if (context.tools?.length) {
|
|
994
|
-
const builtTools = convertTools(context.tools, initialCompat, toolStrictModeOverride);
|
|
995
|
-
params.tools = builtTools.tools;
|
|
996
|
-
toolStrictMode = builtTools.toolStrictMode;
|
|
997
|
-
strictToolsApplied = builtTools.strictToolsApplied;
|
|
998
|
-
} else if (context.tools === void 0 && hasToolHistory(context.messages)) params.tools = [];
|
|
999
|
-
if (options?.toolChoice && initialCompat.supportsToolChoice) params.tool_choice = mapToOpenAICompletionsToolChoice(options.toolChoice);
|
|
1000
|
-
const forcedToolName = typeof params.tool_choice === "object" && params.tool_choice !== null && "function" in params.tool_choice ? params.tool_choice.function.name : void 0;
|
|
1001
|
-
if (typeof params.tool_choice === "object" && params.tool_choice !== null && !initialCompat.supportsNamedToolChoice) {
|
|
1002
|
-
if (forcedToolName !== void 0 && Array.isArray(params.tools) && params.tools.some((tool) => tool.type === "function" && tool.function.name === forcedToolName)) params.tools = params.tools.filter((tool) => tool.type === "function" && tool.function.name === forcedToolName);
|
|
1003
|
-
params.tool_choice = "required";
|
|
1004
|
-
}
|
|
1005
|
-
if (forcedToolName !== void 0 && Array.isArray(params.tools) && params.tools.some((tool) => tool.type === "function" && tool.function.name === forcedToolName) && hasActiveNativeKimiK3Reasoning(model, options)) params.tool_choice = "required";
|
|
1006
|
-
if (isForcedToolChoice(params.tool_choice) && !initialCompat.supportsForcedToolChoice) params.tool_choice = "auto";
|
|
1007
|
-
if ((!Array.isArray(params.tools) || params.tools.length === 0) && (params.tool_choice === "none" || isForcedToolChoice(params.tool_choice))) delete params.tool_choice;
|
|
1008
|
-
if (forcedToolName !== void 0 && (!Array.isArray(params.tools) || !params.tools.some((tool) => tool.type === "function" && tool.function.name === forcedToolName))) delete params.tool_choice;
|
|
1009
|
-
if (shouldDropAutoToolChoiceForReasoning(model, initialCompat, params.tool_choice, options)) delete params.tool_choice;
|
|
1010
|
-
const finalPolicy = resolveOpenAICompatPolicy(model, {
|
|
1011
|
-
endpoint: "chat-completions",
|
|
1012
|
-
reasoning: options?.reasoning,
|
|
1013
|
-
disableReasoning: options?.disableReasoning,
|
|
1014
|
-
toolChoice: params.tool_choice
|
|
1015
|
-
});
|
|
1016
|
-
const compat = finalPolicy.compat;
|
|
1017
|
-
const messages = convertMessages(model, context, compat);
|
|
1018
|
-
maybeAddAnthropicCacheControl(compat, messages);
|
|
1019
|
-
params.messages = messages;
|
|
1020
|
-
const outputToken = resolveOpenAIOutputTokenParam({
|
|
1021
|
-
field: compat.maxTokensField,
|
|
1022
|
-
maxTokens: options?.maxTokens,
|
|
1023
|
-
maxTokensExplicit: options?.maxTokensExplicit ?? options?.maxTokens !== void 0,
|
|
1024
|
-
modelMaxTokens: model.maxTokens,
|
|
1025
|
-
omitMaxOutputTokens: model.omitMaxOutputTokens ?? false,
|
|
1026
|
-
isOpenRouterHost: compat.isOpenRouterHost,
|
|
1027
|
-
alwaysSendMaxTokens: compat.alwaysSendMaxTokens,
|
|
1028
|
-
providerOutputClamp: resolveOpenAICompletionsOutputClamp(model, compat)
|
|
1029
|
-
});
|
|
1030
|
-
if (outputToken) {
|
|
1031
|
-
if (outputToken.field === "max_tokens") params.max_tokens = outputToken.value;
|
|
1032
|
-
else if (outputToken.field === "max_completion_tokens") params.max_completion_tokens = outputToken.value;
|
|
1033
|
-
}
|
|
1034
|
-
applyChatCompletionsToolStream(params, model, compat);
|
|
1035
|
-
applyChatCompletionsReasoningParams(params, model, compat, {
|
|
1036
|
-
...options,
|
|
1037
|
-
toolChoice: params.tool_choice
|
|
1038
|
-
});
|
|
1039
|
-
dropOpenRouterKimiForcedToolReasoning(params, model, finalPolicy);
|
|
1040
|
-
applyOpenAIGatewayRouting(params, compat, cacheRetention !== "none");
|
|
1041
|
-
applyOpenAIExtraBody(params, compat.extraBody, { dropThinkingWhenReasoningEffort: compat.dropThinkingWhenReasoningEffort });
|
|
1042
|
-
applyOpenAIChatCompletionsPromptCachePolicy(params, model, options);
|
|
1043
|
-
return {
|
|
1044
|
-
params,
|
|
1045
|
-
toolStrictMode,
|
|
1046
|
-
strictToolsApplied
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
function parseChunkUsage(rawUsage, model, premiumRequests, timestamp) {
|
|
1050
|
-
const usageLike = rawUsage;
|
|
1051
|
-
const rawPromptTokenDetails = usageLike.prompt_tokens_details;
|
|
1052
|
-
const promptTokenDetails = typeof rawPromptTokenDetails === "object" && rawPromptTokenDetails !== null ? rawPromptTokenDetails : void 0;
|
|
1053
|
-
const rawCompletionTokenDetails = usageLike.completion_tokens_details;
|
|
1054
|
-
const completionTokenDetails = typeof rawCompletionTokenDetails === "object" && rawCompletionTokenDetails !== null ? rawCompletionTokenDetails : void 0;
|
|
1055
|
-
const completionTokens = usageLike.completion_tokens;
|
|
1056
|
-
const promptTokens = usageLike.prompt_tokens;
|
|
1057
|
-
const cachedTokens = usageLike.cached_tokens;
|
|
1058
|
-
const promptCacheHitTokens = usageLike.prompt_cache_hit_tokens;
|
|
1059
|
-
const promptCacheMissTokens = usageLike.prompt_cache_miss_tokens;
|
|
1060
|
-
const promptTokenCachedTokens = promptTokenDetails?.cached_tokens;
|
|
1061
|
-
const cachedContentTokenCount = usageLike.cachedContentTokenCount;
|
|
1062
|
-
const completionReasoningTokens = completionTokenDetails?.reasoning_tokens;
|
|
1063
|
-
const cacheWriteTokens = promptTokenDetails?.cache_write_tokens;
|
|
1064
|
-
const usage = {
|
|
1065
|
-
...calculateOpenAIUsageAccounting({
|
|
1066
|
-
promptTokens: typeof promptTokens === "number" ? promptTokens : 0,
|
|
1067
|
-
outputTokens: typeof completionTokens === "number" ? completionTokens : 0,
|
|
1068
|
-
cachedTokens: firstPositiveNumber(cachedTokens, promptCacheHitTokens, promptTokenCachedTokens, cachedContentTokenCount),
|
|
1069
|
-
reasoningTokens: typeof completionReasoningTokens === "number" ? completionReasoningTokens : 0,
|
|
1070
|
-
cacheWriteOpenRouter: typeof cacheWriteTokens === "number" ? cacheWriteTokens : void 0,
|
|
1071
|
-
cacheWriteDeepSeek: typeof promptCacheMissTokens === "number" ? promptCacheMissTokens : void 0,
|
|
1072
|
-
hasDeepSeekCacheHitAndMiss: typeof promptCacheHitTokens === "number" && typeof promptCacheMissTokens === "number"
|
|
1073
|
-
}),
|
|
1074
|
-
cost: {
|
|
1075
|
-
input: 0,
|
|
1076
|
-
output: 0,
|
|
1077
|
-
cacheRead: 0,
|
|
1078
|
-
cacheWrite: 0,
|
|
1079
|
-
total: 0
|
|
1080
|
-
},
|
|
1081
|
-
...premiumRequests !== void 0 ? { premiumRequests } : {}
|
|
1082
|
-
};
|
|
1083
|
-
calculateCost(model, usage, timestamp);
|
|
1084
|
-
applyProviderReportedCost(model, usage, rawUsage);
|
|
1085
|
-
return usage;
|
|
1086
|
-
}
|
|
1087
|
-
function maybeAddAnthropicCacheControl(compat, messages) {
|
|
1088
|
-
if (compat.cacheControlFormat !== "anthropic") return;
|
|
1089
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1090
|
-
const msg = messages[i];
|
|
1091
|
-
if (msg.role !== "user" && msg.role !== "assistant" && msg.role !== "developer") continue;
|
|
1092
|
-
const content = msg.content;
|
|
1093
|
-
if (typeof content === "string") {
|
|
1094
|
-
if (content.trim().length === 0) continue;
|
|
1095
|
-
msg.content = [Object.assign({
|
|
1096
|
-
type: "text",
|
|
1097
|
-
text: content
|
|
1098
|
-
}, { cache_control: { type: "ephemeral" } })];
|
|
1099
|
-
return;
|
|
1100
|
-
}
|
|
1101
|
-
if (!Array.isArray(content)) continue;
|
|
1102
|
-
for (let j = content.length - 1; j >= 0; j--) {
|
|
1103
|
-
const part = content[j];
|
|
1104
|
-
if (part?.type === "text" && part.text.trim().length > 0) {
|
|
1105
|
-
Object.assign(part, { cache_control: { type: "ephemeral" } });
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
function convertMessages(model, context, compat) {
|
|
1112
|
-
const params = [];
|
|
1113
|
-
const maxNormalizedToolCallIdLength = compat.requiresMistralToolIds ? 9 : compat.usesOpenAIToolCallIdLimit ? 40 : void 0;
|
|
1114
|
-
const duplicateToolCallIdSuffixPrefix = compat.requiresMistralToolIds ? "dup" : void 0;
|
|
1115
|
-
const normalizeToolCallId = (id, source) => {
|
|
1116
|
-
if (compat.requiresMistralToolIds) return normalizeMistralToolId(id, true);
|
|
1117
|
-
if (!(source !== void 0 && source.provider === model.provider && source.api === model.api && source.model === model.id) && id.includes("|")) {
|
|
1118
|
-
const [callId] = id.split("|");
|
|
1119
|
-
return callId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
|
|
1120
|
-
}
|
|
1121
|
-
if (compat.usesOpenAIToolCallIdLimit) return id.length > 40 ? id.slice(0, 40) : id;
|
|
1122
|
-
return id;
|
|
1123
|
-
};
|
|
1124
|
-
const transformedMessages = transformMessages(context.messages, model, (id, _target, source) => normalizeToolCallId(id, source), maxNormalizedToolCallIdLength, duplicateToolCallIdSuffixPrefix, compat);
|
|
1125
|
-
const remappedToolCallIds = /* @__PURE__ */ new Map();
|
|
1126
|
-
let generatedToolCallIdCounter = 0;
|
|
1127
|
-
const generateFallbackToolCallId = (seed) => {
|
|
1128
|
-
generatedToolCallIdCounter += 1;
|
|
1129
|
-
return `call_${Bun.hash(`${model.provider}:${model.id}:${seed}:${generatedToolCallIdCounter}`).toString(36)}`;
|
|
1130
|
-
};
|
|
1131
|
-
const rememberToolCallId = (originalId, normalizedId) => {
|
|
1132
|
-
const queue = remappedToolCallIds.get(originalId);
|
|
1133
|
-
if (queue) {
|
|
1134
|
-
queue.push(normalizedId);
|
|
1135
|
-
return;
|
|
1136
|
-
}
|
|
1137
|
-
remappedToolCallIds.set(originalId, [normalizedId]);
|
|
1138
|
-
};
|
|
1139
|
-
const consumeToolCallId = (originalId) => {
|
|
1140
|
-
const queue = remappedToolCallIds.get(originalId);
|
|
1141
|
-
if (!queue || queue.length === 0) return null;
|
|
1142
|
-
const nextId = queue.shift() ?? null;
|
|
1143
|
-
if (queue.length === 0) remappedToolCallIds.delete(originalId);
|
|
1144
|
-
return nextId;
|
|
1145
|
-
};
|
|
1146
|
-
const ensureToolCallId = (rawId, seed, source) => {
|
|
1147
|
-
const normalized = normalizeToolCallId(rawId, source);
|
|
1148
|
-
if (normalized.trim().length > 0) return normalized;
|
|
1149
|
-
return generateFallbackToolCallId(seed);
|
|
1150
|
-
};
|
|
1151
|
-
const systemPrompts = normalizeSystemPrompts(context.systemPrompt);
|
|
1152
|
-
if (systemPrompts.length > 0) {
|
|
1153
|
-
const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
|
|
1154
|
-
if (compat.supportsMultipleSystemMessages) for (const systemPrompt of systemPrompts) params.push({
|
|
1155
|
-
role,
|
|
1156
|
-
content: systemPrompt
|
|
1157
|
-
});
|
|
1158
|
-
else params.push({
|
|
1159
|
-
role,
|
|
1160
|
-
content: systemPrompts.join("\n\n")
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
let lastRole = null;
|
|
1164
|
-
for (let i = 0; i < transformedMessages.length; i++) {
|
|
1165
|
-
const msg = transformedMessages[i];
|
|
1166
|
-
if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && (msg.role === "user" || msg.role === "developer")) params.push({
|
|
1167
|
-
role: "assistant",
|
|
1168
|
-
content: "I have processed the tool results."
|
|
1169
|
-
});
|
|
1170
|
-
const devAsUser = !compat.supportsDeveloperRole;
|
|
1171
|
-
if (msg.role === "user" || msg.role === "developer") {
|
|
1172
|
-
const role = !devAsUser && msg.role === "developer" ? "developer" : "user";
|
|
1173
|
-
if (typeof msg.content === "string") {
|
|
1174
|
-
const text = msg.content.toWellFormed();
|
|
1175
|
-
if (text.trim().length === 0) continue;
|
|
1176
|
-
params.push({
|
|
1177
|
-
role,
|
|
1178
|
-
content: text
|
|
1179
|
-
});
|
|
1180
|
-
} else {
|
|
1181
|
-
const supportsImages = isOpenAICompletionsVisionSupported(model);
|
|
1182
|
-
const content = [];
|
|
1183
|
-
let omittedImages = false;
|
|
1184
|
-
for (const item of msg.content) if (item.type === "text") {
|
|
1185
|
-
const text = item.text.toWellFormed();
|
|
1186
|
-
if (text.trim().length === 0) continue;
|
|
1187
|
-
content.push({
|
|
1188
|
-
type: "text",
|
|
1189
|
-
text
|
|
1190
|
-
});
|
|
1191
|
-
} else if (supportsImages) content.push({
|
|
1192
|
-
type: "image_url",
|
|
1193
|
-
image_url: {
|
|
1194
|
-
url: item.url ?? `data:${item.mimeType};base64,${item.data}`,
|
|
1195
|
-
...item.detail && item.detail !== "original" ? { detail: item.detail } : {}
|
|
1196
|
-
}
|
|
1197
|
-
});
|
|
1198
|
-
else omittedImages = true;
|
|
1199
|
-
if (omittedImages) content.push({
|
|
1200
|
-
type: "text",
|
|
1201
|
-
text: NON_VISION_IMAGE_PLACEHOLDER
|
|
1202
|
-
});
|
|
1203
|
-
if (content.length === 0) continue;
|
|
1204
|
-
params.push({
|
|
1205
|
-
role: "user",
|
|
1206
|
-
content
|
|
1207
|
-
});
|
|
1208
|
-
}
|
|
1209
|
-
} else if (msg.role === "assistant") {
|
|
1210
|
-
const assistantMsg = {
|
|
1211
|
-
role: "assistant",
|
|
1212
|
-
content: null
|
|
1213
|
-
};
|
|
1214
|
-
const nonEmptyTextBlocks = msg.content.filter((b) => b.type === "text").filter((b) => b.text && b.text.trim().length > 0);
|
|
1215
|
-
if (nonEmptyTextBlocks.length > 0) assistantMsg.content = nonEmptyTextBlocks.map((b, i) => {
|
|
1216
|
-
const text = b.text.toWellFormed();
|
|
1217
|
-
return isDemotedThinking(b) && i < nonEmptyTextBlocks.length - 1 ? `${text}\n` : text;
|
|
1218
|
-
}).join("");
|
|
1219
|
-
const nonEmptyThinkingBlocks = msg.content.filter((b) => b.type === "thinking").filter((b) => b.thinking && b.thinking.trim().length > 0);
|
|
1220
|
-
if (nonEmptyThinkingBlocks.length > 0) {
|
|
1221
|
-
if (compat.requiresThinkingAsText) {
|
|
1222
|
-
const thinkingText = nonEmptyThinkingBlocks.map((b) => renderDemotedThinking(model.id, b.thinking)).join(" ");
|
|
1223
|
-
assistantMsg.content = typeof assistantMsg.content === "string" && assistantMsg.content.length > 0 ? `${thinkingText} ${assistantMsg.content}` : thinkingText;
|
|
1224
|
-
} else if (compat.requiresReasoningContentForToolCalls) {
|
|
1225
|
-
const signature = nonEmptyThinkingBlocks[0].thinkingSignature;
|
|
1226
|
-
const wireField = compat.allowsSyntheticReasoningContentForToolCalls && (signature === "reasoning_content" || signature === "reasoning" || signature === "reasoning_text") ? signature : signature === "reasoning_content" || signature === "reasoning" || signature === "reasoning_text" ? compat.reasoningContentField ?? "reasoning_content" : void 0;
|
|
1227
|
-
if (wireField) assistantMsg[wireField] = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n");
|
|
1228
|
-
} else if (compat.thinkingFormat === "zai" && model.reasoning) {
|
|
1229
|
-
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
|
1230
|
-
assistantMsg[reasoningField] = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n");
|
|
1231
|
-
} else if (compat.replayReasoningContent) {
|
|
1232
|
-
const signature = nonEmptyThinkingBlocks[0].thinkingSignature;
|
|
1233
|
-
const reasoningField = signature === "reasoning_content" || signature === "reasoning" || signature === "reasoning_text" ? signature : compat.reasoningContentField ?? "reasoning_content";
|
|
1234
|
-
assistantMsg[reasoningField] = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n");
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
if (compat.requiresReasoningContentForToolCalls) {
|
|
1238
|
-
const streamedReasoningField = nonEmptyThinkingBlocks[0]?.thinkingSignature;
|
|
1239
|
-
const reasoningField = compat.allowsSyntheticReasoningContentForToolCalls && (streamedReasoningField === "reasoning_content" || streamedReasoningField === "reasoning" || streamedReasoningField === "reasoning_text") ? streamedReasoningField : compat.reasoningContentField ?? "reasoning_content";
|
|
1240
|
-
if (!assistantMsg[reasoningField]) {
|
|
1241
|
-
const reasoning = assistantMsg.reasoning;
|
|
1242
|
-
const reasoningText = assistantMsg.reasoning_text;
|
|
1243
|
-
if (reasoning && reasoningField !== "reasoning") assistantMsg[reasoningField] = reasoning;
|
|
1244
|
-
else if (reasoningText && reasoningField !== "reasoning_text") assistantMsg[reasoningField] = reasoningText;
|
|
1245
|
-
else if (nonEmptyThinkingBlocks.length > 0) assistantMsg[reasoningField] = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n");
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
const toolCalls = msg.content.filter((b) => b.type === "toolCall");
|
|
1249
|
-
const canUseSyntheticReasoningContent = compat.requiresReasoningContentForToolCalls && compat.allowsSyntheticReasoningContentForToolCalls && (compat.thinkingFormat === "openai" || compat.thinkingFormat === "openrouter" || compat.thinkingFormat === "zai");
|
|
1250
|
-
const needsReasoningField = compat.requiresReasoningContentForAllAssistantTurns || toolCalls.length > 0;
|
|
1251
|
-
let hasReasoningField = assistantMsg.reasoning_content !== void 0 || assistantMsg.reasoning !== void 0 || assistantMsg.reasoning_text !== void 0;
|
|
1252
|
-
if (needsReasoningField && !hasReasoningField && compat.requiresReasoningContentForToolCalls && !compat.allowsSyntheticReasoningContentForToolCalls) {
|
|
1253
|
-
const allThinkingBlocks = msg.content.filter((b) => b.type === "thinking");
|
|
1254
|
-
if (allThinkingBlocks.length > 0) {
|
|
1255
|
-
const signature = allThinkingBlocks[0].thinkingSignature;
|
|
1256
|
-
if (signature === "reasoning_content" || signature === "reasoning" || signature === "reasoning_text") {
|
|
1257
|
-
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
|
1258
|
-
assistantMsg[reasoningField] = allThinkingBlocks.map((b) => b.thinking).join("\n");
|
|
1259
|
-
hasReasoningField = true;
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
if (needsReasoningField && !hasReasoningField && compat.requiresReasoningContentForToolCalls && !compat.allowsSyntheticReasoningContentForToolCalls) {
|
|
1264
|
-
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
|
1265
|
-
assistantMsg[reasoningField] = "";
|
|
1266
|
-
hasReasoningField = true;
|
|
1267
|
-
}
|
|
1268
|
-
if (toolCalls.length > 0 && canUseSyntheticReasoningContent && !hasReasoningField) {
|
|
1269
|
-
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
|
1270
|
-
assistantMsg[reasoningField] = ".";
|
|
1271
|
-
hasReasoningField = true;
|
|
1272
|
-
}
|
|
1273
|
-
if (toolCalls.length > 0) {
|
|
1274
|
-
assistantMsg.tool_calls = toolCalls.map((tc, toolCallIndex) => {
|
|
1275
|
-
const toolCallId = ensureToolCallId(tc.id, `${i}:${toolCallIndex}:${tc.name}`, msg);
|
|
1276
|
-
rememberToolCallId(tc.id, toolCallId);
|
|
1277
|
-
const replayedToolCall = {
|
|
1278
|
-
id: normalizeMistralToolId(toolCallId, compat.requiresMistralToolIds),
|
|
1279
|
-
type: "function",
|
|
1280
|
-
function: {
|
|
1281
|
-
name: tc.name,
|
|
1282
|
-
arguments: serializeToolArguments(tc.arguments)
|
|
1283
|
-
}
|
|
1284
|
-
};
|
|
1285
|
-
const extraContent = parseGeminiThoughtSignatureExtraContent(tc.thoughtSignature);
|
|
1286
|
-
if (extraContent) replayedToolCall.extra_content = extraContent;
|
|
1287
|
-
return replayedToolCall;
|
|
1288
|
-
});
|
|
1289
|
-
const reasoningDetails = toolCalls.flatMap((tc) => {
|
|
1290
|
-
const thoughtSignature = tc.thoughtSignature;
|
|
1291
|
-
if (!thoughtSignature) return [];
|
|
1292
|
-
try {
|
|
1293
|
-
const parsed = JSON.parse(thoughtSignature);
|
|
1294
|
-
return getGeminiThoughtSignatureExtraContent(parsed) ? [] : [parsed];
|
|
1295
|
-
} catch {
|
|
1296
|
-
return [];
|
|
1297
|
-
}
|
|
1298
|
-
});
|
|
1299
|
-
if (reasoningDetails.length > 0) assistantMsg.reasoning_details = reasoningDetails;
|
|
1300
|
-
}
|
|
1301
|
-
if (assistantMsg.content === null && (hasReasoningField || assistantMsg.tool_calls)) assistantMsg.content = "";
|
|
1302
|
-
const content = assistantMsg.content;
|
|
1303
|
-
const hasContent = content !== null && content !== void 0 && (typeof content === "string" ? content.length > 0 : content.length > 0);
|
|
1304
|
-
if (!hasContent && assistantMsg.tool_calls && compat.requiresAssistantContentForToolCalls) assistantMsg.content = ".";
|
|
1305
|
-
if (!hasContent && !assistantMsg.tool_calls && !hasReasoningField) continue;
|
|
1306
|
-
params.push(assistantMsg);
|
|
1307
|
-
} else if (msg.role === "toolResult") {
|
|
1308
|
-
const imageBlocks = [];
|
|
1309
|
-
let j = i;
|
|
1310
|
-
for (; j < transformedMessages.length && transformedMessages[j].role === "toolResult"; j++) {
|
|
1311
|
-
const toolMsg = transformedMessages[j];
|
|
1312
|
-
const textResult = toolMsg.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
|
|
1313
|
-
const supportsImages = isOpenAICompletionsVisionSupported(model);
|
|
1314
|
-
const hasImages = toolMsg.content.some((c) => c.type === "image");
|
|
1315
|
-
const omittedImages = hasImages && !supportsImages;
|
|
1316
|
-
const hasText = textResult.length > 0;
|
|
1317
|
-
const resolvedToolCallId = consumeToolCallId(toolMsg.toolCallId) ?? ensureToolCallId(toolMsg.toolCallId, `${j}:${toolMsg.toolName ?? "tool"}`);
|
|
1318
|
-
const toolResultMsg = {
|
|
1319
|
-
role: "tool",
|
|
1320
|
-
content: (omittedImages ? joinTextWithImagePlaceholder(textResult, true) : hasText ? textResult : hasImages ? "(see attached image)" : "").toWellFormed(),
|
|
1321
|
-
tool_call_id: normalizeMistralToolId(resolvedToolCallId, compat.requiresMistralToolIds)
|
|
1322
|
-
};
|
|
1323
|
-
if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
|
|
1324
|
-
params.push(toolResultMsg);
|
|
1325
|
-
if (hasImages && supportsImages) {
|
|
1326
|
-
for (const block of toolMsg.content) if (block.type === "image") imageBlocks.push({
|
|
1327
|
-
type: "image_url",
|
|
1328
|
-
image_url: { url: block.url ?? `data:${block.mimeType};base64,${block.data}` }
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
i = j - 1;
|
|
1333
|
-
if (imageBlocks.length > 0) {
|
|
1334
|
-
if (compat.requiresAssistantAfterToolResult) params.push({
|
|
1335
|
-
role: "assistant",
|
|
1336
|
-
content: "I have processed the tool results."
|
|
1337
|
-
});
|
|
1338
|
-
params.push({
|
|
1339
|
-
role: "user",
|
|
1340
|
-
content: [{
|
|
1341
|
-
type: "text",
|
|
1342
|
-
text: "Attached image(s) from tool result:"
|
|
1343
|
-
}, ...imageBlocks]
|
|
1344
|
-
});
|
|
1345
|
-
lastRole = "user";
|
|
1346
|
-
} else lastRole = "toolResult";
|
|
1347
|
-
continue;
|
|
1348
|
-
}
|
|
1349
|
-
lastRole = msg.role === "developer" ? model.reasoning && compat.supportsDeveloperRole ? "developer" : "system" : msg.role;
|
|
1350
|
-
}
|
|
1351
|
-
return params;
|
|
1352
|
-
}
|
|
1353
|
-
function convertTools(tools, compat, toolStrictModeOverride) {
|
|
1354
|
-
const rejectRootObjectUnion = compat.rejectRootObjectUnion;
|
|
1355
|
-
const adaptedTools = tools.map((tool) => {
|
|
1356
|
-
const strict = !NO_STRICT && compat.supportsStrictMode !== false && tool.strict !== false;
|
|
1357
|
-
const baseParameters = rejectRootObjectUnion ? flattenExclusiveRequiredRootUnion(toolWireSchema(tool)) : toolWireSchema(tool);
|
|
1358
|
-
const adapted = adaptSchemaForStrict(baseParameters, strict);
|
|
1359
|
-
return {
|
|
1360
|
-
tool,
|
|
1361
|
-
baseParameters,
|
|
1362
|
-
parameters: adapted.schema,
|
|
1363
|
-
strict: adapted.strict
|
|
1364
|
-
};
|
|
1365
|
-
});
|
|
1366
|
-
const requestedStrictMode = toolStrictModeOverride ?? compat.toolStrictMode;
|
|
1367
|
-
const toolStrictMode = requestedStrictMode === "none" ? "none" : requestedStrictMode === "all_strict" ? adaptedTools.every((tool) => tool.strict) ? "all_strict" : "none" : "mixed";
|
|
1368
|
-
const wireTools = [];
|
|
1369
|
-
let anyStrictEmitted = false;
|
|
1370
|
-
for (const { tool, baseParameters, parameters, strict } of adaptedTools) {
|
|
1371
|
-
const includeStrict = toolStrictMode === "all_strict" || toolStrictMode === "mixed" && strict;
|
|
1372
|
-
const includeExplicitFalse = !includeStrict && tool.strict === false && toolStrictMode === "mixed" && compat.supportsStrictMode !== false;
|
|
1373
|
-
const wireParameters = includeStrict ? parameters : baseParameters;
|
|
1374
|
-
const emittedParameters = compat.toolSchemaFlavor === "moonshot-mfjs" ? normalizeSchemaForMoonshot(wireParameters) : compat.toolSchemaFlavor === "grammar" ? sanitizeSchemaForGrammar(wireParameters) : wireParameters;
|
|
1375
|
-
const violation = findStrictToolSchemaViolation(emittedParameters, "#", { rejectRootObjectUnion });
|
|
1376
|
-
if (violation) {
|
|
1377
|
-
warn(`Tool "${tool.name}" omitted from the openai-completions request: its parameter schema is invalid for this provider at ${violation} (an enum/const value cannot match its declared type, or leftover xAI object-root union). Other tools are unaffected.`);
|
|
1378
|
-
continue;
|
|
1379
|
-
}
|
|
1380
|
-
if (includeStrict) anyStrictEmitted = true;
|
|
1381
|
-
wireTools.push({
|
|
1382
|
-
type: "function",
|
|
1383
|
-
function: {
|
|
1384
|
-
name: tool.name,
|
|
1385
|
-
description: tool.description || "",
|
|
1386
|
-
parameters: emittedParameters,
|
|
1387
|
-
...includeStrict ? { strict: true } : includeExplicitFalse ? { strict: false } : {}
|
|
1388
|
-
}
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
|
-
return {
|
|
1392
|
-
tools: wireTools,
|
|
1393
|
-
toolStrictMode,
|
|
1394
|
-
strictToolsApplied: wireTools.length > 0 && anyStrictEmitted
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
const EMPTY_OLLAMA_LENGTH_COMPLETION_MESSAGE = "Model returned no content: prompt filled the context window; raise Ollama num_ctx or shorten the prompt.";
|
|
1398
|
-
function mapStopReason(reason) {
|
|
1399
|
-
if (reason === null) return { stopReason: "stop" };
|
|
1400
|
-
switch (typeof reason === "string" ? reason.toLowerCase() : reason) {
|
|
1401
|
-
case "stop":
|
|
1402
|
-
case "end": return { stopReason: "stop" };
|
|
1403
|
-
case "length":
|
|
1404
|
-
case "max_tokens": return { stopReason: "length" };
|
|
1405
|
-
case "function_call":
|
|
1406
|
-
case "tool_calls": return { stopReason: "toolUse" };
|
|
1407
|
-
case "content_filter": return {
|
|
1408
|
-
stopReason: "error",
|
|
1409
|
-
errorMessage: "Provider finish_reason: content_filter"
|
|
1410
|
-
};
|
|
1411
|
-
case "network_error": return {
|
|
1412
|
-
stopReason: "error",
|
|
1413
|
-
errorMessage: "Provider finish_reason: network_error"
|
|
1414
|
-
};
|
|
1415
|
-
case "error": return {
|
|
1416
|
-
stopReason: "error",
|
|
1417
|
-
errorMessage: "Provider returned error finish_reason"
|
|
1418
|
-
};
|
|
1419
|
-
case "insufficient_system_resource": return {
|
|
1420
|
-
stopReason: "error",
|
|
1421
|
-
errorMessage: "Provider returned error finish_reason: insufficient_system_resource"
|
|
1422
|
-
};
|
|
1423
|
-
default: return {
|
|
1424
|
-
stopReason: "error",
|
|
1425
|
-
errorMessage: `Provider finish_reason: ${reason}`
|
|
1426
|
-
};
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
//#endregion
|
|
1430
|
-
export { openai_completions_exports as t };
|