@openclaw/ai 2026.7.2-beta.5 → 2026.7.2-beta.7
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/dist/{anthropic-CQVj3le6.mjs → anthropic-CH4UUnZr.mjs} +14 -463
- package/dist/anthropic-usage-DWU-x8MI.mjs +459 -0
- package/dist/{azure-openai-responses-DIYgsqFM.mjs → azure-openai-responses-CImcwB83.mjs} +4 -3
- package/dist/azure-openai-responses-client-compat-C7K7QfUE.mjs +62 -0
- package/dist/cache-retention-0x979a5V.mjs +12 -0
- package/dist/deferred-event-buffer-DAvyP7qA.mjs +19 -0
- package/dist/github-copilot-headers-NCJtz9i0.mjs +37 -0
- package/dist/{google-f-A8xrae.mjs → google-CtSg0iTS.mjs} +3 -3
- package/dist/{google-shared-J6qvYINH.mjs → google-shared-DNBz5rcD.mjs} +5 -3
- package/dist/{google-vertex-D3yMVXIY.mjs → google-vertex-31f1uS9L.mjs} +3 -3
- package/dist/host-Dog2WQiR.mjs +369 -0
- package/dist/index.mjs +1 -1
- package/dist/internal/anthropic.d.mts +3 -53
- package/dist/internal/anthropic.mjs +3 -2
- package/dist/internal/openai.d.mts +259 -2
- package/dist/internal/openai.mjs +8 -4
- package/dist/internal/runtime.mjs +6 -4
- package/dist/internal/shared.d.mts +1 -1
- package/dist/internal/shared.mjs +5 -1
- package/dist/{llm-request-activity-CehVkZP-.mjs → llm-request-activity-BjtkplhG.mjs} +1 -19
- package/dist/{mistral-CKV-TOQj.mjs → mistral-CWmpvWYh.mjs} +5 -3
- package/dist/{openai-chatgpt-responses-CedIj0hk.mjs → openai-chatgpt-responses-B84Ibtrd.mjs} +18 -13
- package/dist/openai-completions-DsOxhOD1.mjs +630 -0
- package/dist/openai-completions-compat-DBWjXoMZ.d.mts +43 -0
- package/dist/openai-reasoning-compat-YgeLncHw.mjs +396 -0
- package/dist/openai-responses-BT7A3sLu.mjs +138 -0
- package/dist/openai-responses-shared-pXl6Wd8S.mjs +392 -0
- package/dist/{openai-D3PD6PE-.mjs → openai-responses-stream-internal-Cw5txaGW.mjs} +1409 -1863
- package/dist/openai-tool-projection-OhX64DoP.mjs +215 -0
- package/dist/{provider-error-apVOZI6G.mjs → provider-error-CAEvRjry.mjs} +1 -1
- package/dist/provider-options-D8bB3z9b.d.mts +144 -0
- package/dist/providers.mjs +8 -8
- package/dist/{stream-first-event-timeout-C3OgBjIk.mjs → reasoning-tag-text-partitioner-CGDyLWUR.mjs} +1 -86
- package/dist/simple-options-9lhRrN73.mjs +50 -0
- package/dist/stream-first-event-timeout-BBys9hSb.mjs +86 -0
- package/dist/tls-certificate-errors-DXSpluKI.mjs +93 -0
- package/dist/tool-result-text-CTpIRbYd.mjs +225 -0
- package/dist/{github-copilot-headers-BCoBNmL7.mjs → tool-schema-json-projection-BwNu3nDi.mjs} +1 -48
- package/dist/transform-messages-C8mBqZxF.mjs +2 -0
- package/dist/{transport-stream-shared-BbMELSI4.mjs → transport-stream-shared-D81p90xq.mjs} +4 -4
- package/dist/transports.d.mts +13 -33
- package/dist/transports.mjs +75 -34
- package/package.json +4 -4
- package/dist/host-XYGZcgO8.mjs +0 -98
- package/dist/openai-BPor_3WI.d.mts +0 -358
- package/dist/openai-completions-CiSutyu0.mjs +0 -1223
- package/dist/shared-CdjNZd35.mjs +0 -634
|
@@ -1,1223 +0,0 @@
|
|
|
1
|
-
import { n as getEnvApiKey, r as __exportAll } from "./env-api-keys-DrgeBuva.mjs";
|
|
2
|
-
import { t as AssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
|
|
3
|
-
import { n as getAiTransportHost } from "./host-XYGZcgO8.mjs";
|
|
4
|
-
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
5
|
-
import { A as buildBaseOptions, E as isImageWithMediaPayload, O as isRecord, S as describeToolResultMediaPlaceholder, a as splitSystemPromptCacheBoundary, o as stripSystemPromptCacheBoundary, u as transformMessages, w as extractToolResultText, y as normalizeLowercaseStringOrEmpty } from "./shared-CdjNZd35.mjs";
|
|
6
|
-
import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
|
|
7
|
-
import { c as calculateCost, l as clampThinkingLevel, s as applyProviderReportedUsageCost } from "./number-coercion-DvG7SNMg.mjs";
|
|
8
|
-
import { d as transportAbortError } from "./transport-stream-shared-BbMELSI4.mjs";
|
|
9
|
-
import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
|
|
10
|
-
import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
|
|
11
|
-
import { t as formatProviderError } from "./provider-error-apVOZI6G.mjs";
|
|
12
|
-
import { a as resolveCacheRetention, i as resolveCloudflareBaseUrl, n as hasCopilotVisionInput, o as projectRuntimeToolInputSchema, r as isCloudflareProvider, t as buildCopilotDynamicHeaders } from "./github-copilot-headers-BCoBNmL7.mjs";
|
|
13
|
-
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, o as createReasoningTagTextPartitioner, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-C3OgBjIk.mjs";
|
|
14
|
-
import OpenAI from "openai";
|
|
15
|
-
//#region packages/ai/src/transports/host-policy.ts
|
|
16
|
-
function buildGuardedModelFetch(model, timeoutMs, options) {
|
|
17
|
-
const host = getAiTransportHost();
|
|
18
|
-
if (options !== void 0) return host.buildModelFetch(model, timeoutMs, options) ?? globalThis.fetch;
|
|
19
|
-
if (timeoutMs !== void 0) return host.buildModelFetch(model, timeoutMs) ?? globalThis.fetch;
|
|
20
|
-
return host.buildModelFetch(model) ?? globalThis.fetch;
|
|
21
|
-
}
|
|
22
|
-
function resolveProviderEndpoint(baseUrl) {
|
|
23
|
-
return { endpointClass: getAiTransportHost().resolveProviderEndpointClass(baseUrl) };
|
|
24
|
-
}
|
|
25
|
-
function resolveProviderRequestCapabilities(input) {
|
|
26
|
-
return getAiTransportHost().resolveProviderRequestCapabilities(input);
|
|
27
|
-
}
|
|
28
|
-
function resolveProviderRequestPolicyConfig(input) {
|
|
29
|
-
return { headers: getAiTransportHost().resolveProviderRequestHeaders(input) };
|
|
30
|
-
}
|
|
31
|
-
function resolveModelRequestTimeoutMs(model, timeoutMs) {
|
|
32
|
-
return timeoutMs ?? getAiTransportHost().resolveModelRequestTimeoutMs(model);
|
|
33
|
-
}
|
|
34
|
-
function resolveOpenAIStrictToolSetting(model, options) {
|
|
35
|
-
return getAiTransportHost().resolveOpenAIStrictToolSetting(model, options);
|
|
36
|
-
}
|
|
37
|
-
function transformTransportMessages(messages, model, normalizeToolCallId, options) {
|
|
38
|
-
return getAiTransportHost().transformTransportMessages(messages, model, normalizeToolCallId, options);
|
|
39
|
-
}
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region packages/ai/src/utils/assistant-text-phase.ts
|
|
42
|
-
function isAssistantTextPhaseBlock(block) {
|
|
43
|
-
if (!block || typeof block !== "object") return false;
|
|
44
|
-
const record = block;
|
|
45
|
-
return record.type === "text" && typeof record.text === "string";
|
|
46
|
-
}
|
|
47
|
-
function encodeAssistantTextSignatureV1(id, phase) {
|
|
48
|
-
return JSON.stringify({
|
|
49
|
-
v: 1,
|
|
50
|
-
id,
|
|
51
|
-
...phase ? { phase } : {}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
/** Tags unphased narration before a tool-call event becomes consumer-visible. */
|
|
55
|
-
function tagPendingCommentaryText(content) {
|
|
56
|
-
const textBlocks = content.filter(isAssistantTextPhaseBlock);
|
|
57
|
-
let commentaryIndex = textBlocks.filter((block) => block.textSignature !== void 0).length;
|
|
58
|
-
const tagged = /* @__PURE__ */ new Map();
|
|
59
|
-
for (const block of textBlocks) {
|
|
60
|
-
if (block.text.trim().length === 0 || block.textSignature !== void 0) continue;
|
|
61
|
-
const signature = encodeAssistantTextSignatureV1(`commentary-${commentaryIndex}`, "commentary");
|
|
62
|
-
block.textSignature = signature;
|
|
63
|
-
tagged.set(block, signature);
|
|
64
|
-
commentaryIndex += 1;
|
|
65
|
-
}
|
|
66
|
-
return tagged;
|
|
67
|
-
}
|
|
68
|
-
/** Rolls back only the exact provisional signatures created by this transport turn. */
|
|
69
|
-
function clearPendingCommentaryText(tags) {
|
|
70
|
-
for (const [block, signature] of tags) if (block.textSignature === signature) delete block.textSignature;
|
|
71
|
-
tags.clear();
|
|
72
|
-
}
|
|
73
|
-
function rememberPendingCommentaryTags(target, tagged) {
|
|
74
|
-
for (const [block, signature] of tagged) target.set(block, signature);
|
|
75
|
-
}
|
|
76
|
-
//#endregion
|
|
77
|
-
//#region packages/ai/src/providers/openai-response-format.ts
|
|
78
|
-
const JSON_SCHEMA_RESPONSE_FORMAT_NAME = "openclaw_response";
|
|
79
|
-
const OLLAMA_CLOUD_ORIGIN = "https://ollama.com";
|
|
80
|
-
function isKnownOpenAIJsonSchemaModelId(modelId) {
|
|
81
|
-
if (typeof modelId !== "string") return false;
|
|
82
|
-
if (/^gpt-5(?:[.-]|$)/i.test(modelId) || /^gpt-4\.1(?:-|$)/i.test(modelId)) return true;
|
|
83
|
-
const gpt4o = /^gpt-4o(-mini)?(?:-(\d{4}-\d{2}-\d{2}))?$/i.exec(modelId);
|
|
84
|
-
if (gpt4o) {
|
|
85
|
-
const snapshot = gpt4o[2];
|
|
86
|
-
return !snapshot || snapshot >= (gpt4o[1] ? "2024-07-18" : "2024-08-06");
|
|
87
|
-
}
|
|
88
|
-
return /^(?:o1|o3(?:-mini|-pro)?|o4-mini)(?:-\d{4}-\d{2}-\d{2})?$/i.test(modelId);
|
|
89
|
-
}
|
|
90
|
-
function shouldOmitOllamaCompatResponseFormat(params) {
|
|
91
|
-
if (!params.provider.includes("ollama")) return false;
|
|
92
|
-
if (params.hasTools()) return true;
|
|
93
|
-
try {
|
|
94
|
-
return new URL(params.baseUrl).origin === OLLAMA_CLOUD_ORIGIN;
|
|
95
|
-
} catch {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Maps the shared JSON Schema option to Chat Completions while preserving the
|
|
101
|
-
* older provider-shaped json_object/json_schema inputs accepted by model params.
|
|
102
|
-
*/
|
|
103
|
-
function resolveOpenAICompletionsResponseFormat(responseFormat, supportsJsonSchemaResponseFormat) {
|
|
104
|
-
if (!responseFormat) return;
|
|
105
|
-
if (responseFormat.type === "json_object") return responseFormat;
|
|
106
|
-
if (responseFormat.type === "text") return responseFormat;
|
|
107
|
-
if (responseFormat.type === "json_schema" && isRecord(responseFormat.json_schema)) return responseFormat;
|
|
108
|
-
if (!supportsJsonSchemaResponseFormat) return;
|
|
109
|
-
return {
|
|
110
|
-
type: "json_schema",
|
|
111
|
-
json_schema: {
|
|
112
|
-
name: JSON_SCHEMA_RESPONSE_FORMAT_NAME,
|
|
113
|
-
schema: responseFormat
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region packages/ai/src/transports/openai-completions-compat.ts
|
|
119
|
-
function isDefaultRouteProvider(provider, ...ids) {
|
|
120
|
-
return provider !== void 0 && ids.includes(provider);
|
|
121
|
-
}
|
|
122
|
-
/** Resolves default request flags for an OpenAI-compatible completions endpoint. */
|
|
123
|
-
function resolveOpenAICompletionsCompatDefaults(input) {
|
|
124
|
-
const { provider, modelId, endpointClass, knownProviderFamily, supportsNativeStreamingUsageCompat = false, supportsOpenAICompletionsStreamingUsageCompat = false, usesExplicitProxyLikeEndpoint = false } = input;
|
|
125
|
-
const isDefaultRoute = endpointClass === "default";
|
|
126
|
-
const usesConfiguredNonOpenAIEndpoint = endpointClass !== "default" && endpointClass !== "openai-public";
|
|
127
|
-
const isMoonshot = knownProviderFamily === "moonshot" || endpointClass === "moonshot-native";
|
|
128
|
-
const isMoonshotLike = isMoonshot || knownProviderFamily === "modelstudio" || endpointClass === "modelstudio-native";
|
|
129
|
-
const isModelStudioLike = knownProviderFamily === "modelstudio" || endpointClass === "modelstudio-native" || isDefaultRoute && isDefaultRouteProvider(provider, "dashscope", "modelstudio", "qwen");
|
|
130
|
-
const isZai = endpointClass === "zai-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "zai");
|
|
131
|
-
const isDeepSeek = endpointClass === "deepseek-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "deepseek");
|
|
132
|
-
const isTogether = knownProviderFamily === "together" || input.baseUrl?.includes("api.together.ai") === true || input.baseUrl?.includes("api.together.xyz") === true || isDefaultRoute && isDefaultRouteProvider(input.provider, "together");
|
|
133
|
-
const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || input.baseUrl?.includes("gateway.ai.cloudflare.com") === true;
|
|
134
|
-
const isXiaomi = endpointClass === "xiaomi-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "xiaomi");
|
|
135
|
-
const isNonStandard = endpointClass === "cerebras-native" || endpointClass === "chutes-native" || endpointClass === "deepseek-native" || endpointClass === "mistral-public" || endpointClass === "opencode-native" || endpointClass === "xai-native" || isXiaomi || isZai || isDefaultRoute && isDefaultRouteProvider(input.provider, "cerebras", "chutes", "deepseek", "opencode", "xai");
|
|
136
|
-
const isOpenRouterLike = input.provider === "openrouter" || endpointClass === "openrouter";
|
|
137
|
-
const isLocalEndpoint = endpointClass === "local";
|
|
138
|
-
const usesMaxTokens = endpointClass === "chutes-native" || endpointClass === "mistral-public" || knownProviderFamily === "mistral" || isMoonshot || isCloudflareAiGateway || isZai || isTogether || isDefaultRoute && isDefaultRouteProvider(provider, "chutes");
|
|
139
|
-
return {
|
|
140
|
-
supportsStore: !isNonStandard && knownProviderFamily !== "mistral" && !usesExplicitProxyLikeEndpoint,
|
|
141
|
-
supportsDeveloperRole: !isNonStandard && !isMoonshotLike && !usesConfiguredNonOpenAIEndpoint,
|
|
142
|
-
supportsReasoningEffort: !isZai && !isTogether && knownProviderFamily !== "mistral" && endpointClass !== "xai-native" && !usesExplicitProxyLikeEndpoint,
|
|
143
|
-
supportsUsageInStreaming: supportsOpenAICompletionsStreamingUsageCompat || !isNonStandard && (isLocalEndpoint || !usesConfiguredNonOpenAIEndpoint || supportsNativeStreamingUsageCompat),
|
|
144
|
-
maxTokensField: usesMaxTokens ? "max_tokens" : "max_completion_tokens",
|
|
145
|
-
thinkingFormat: isDeepSeek || isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : isOpenRouterLike ? "openrouter" : "openai",
|
|
146
|
-
visibleReasoningDetailTypes: isOpenRouterLike ? ["response.output_text", "response.text"] : [],
|
|
147
|
-
supportsStrictMode: !isZai && !usesConfiguredNonOpenAIEndpoint,
|
|
148
|
-
supportsJsonSchemaResponseFormat: (endpointClass === "openai-public" || isDefaultRoute && isDefaultRouteProvider(provider, "openai")) && isKnownOpenAIJsonSchemaModelId(modelId),
|
|
149
|
-
requiresReasoningContentOnAssistantMessages: isDeepSeek || isXiaomi,
|
|
150
|
-
requiresNonEmptyUserOrAssistantMessage: isModelStudioLike,
|
|
151
|
-
cacheControlFormat: provider === "openrouter" && modelId?.startsWith("anthropic/") === true ? "anthropic" : void 0,
|
|
152
|
-
sessionAffinityFormat: isOpenRouterLike ? "openrouter" : "openai",
|
|
153
|
-
supportsLongCacheRetention: 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")
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function resolveOpenAICompletionsCompatDefaultsFromCapabilities(input) {
|
|
157
|
-
return resolveOpenAICompletionsCompatDefaults(input);
|
|
158
|
-
}
|
|
159
|
-
/** Detects endpoint capabilities and defaults for an OpenAI-completions model. */
|
|
160
|
-
function detectOpenAICompletionsCompat(model, resolveCapabilities = resolveProviderRequestCapabilities) {
|
|
161
|
-
const capabilities = resolveCapabilities({
|
|
162
|
-
provider: model.provider,
|
|
163
|
-
api: "openai-completions",
|
|
164
|
-
baseUrl: model.baseUrl,
|
|
165
|
-
capability: "llm",
|
|
166
|
-
transport: "stream",
|
|
167
|
-
modelId: model.id,
|
|
168
|
-
compat: model.compat && typeof model.compat === "object" ? model.compat : void 0
|
|
169
|
-
});
|
|
170
|
-
return {
|
|
171
|
-
capabilities,
|
|
172
|
-
defaults: resolveOpenAICompletionsCompatDefaultsFromCapabilities({
|
|
173
|
-
provider: model.provider,
|
|
174
|
-
modelId: model.id,
|
|
175
|
-
baseUrl: model.baseUrl,
|
|
176
|
-
...capabilities
|
|
177
|
-
})
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
function resolveSessionAffinity(model, detectedFormat) {
|
|
181
|
-
if (model.compat?.sendSessionAffinityHeaders !== true) return "none";
|
|
182
|
-
if (detectedFormat === "openrouter" || model.compat.thinkingFormat === "openrouter" || model.compat.openRouterRouting !== void 0) return "openrouter";
|
|
183
|
-
return "openai";
|
|
184
|
-
}
|
|
185
|
-
/** Applies explicit model overrides once on top of the canonical transport defaults. */
|
|
186
|
-
function resolveOpenAICompletionsCompat(model, resolveCapabilities = resolveProviderRequestCapabilities) {
|
|
187
|
-
const { defaults } = detectOpenAICompletionsCompat(model, resolveCapabilities);
|
|
188
|
-
const configured = model.compat;
|
|
189
|
-
return {
|
|
190
|
-
supportsStore: configured?.supportsStore ?? defaults.supportsStore,
|
|
191
|
-
supportsDeveloperRole: configured?.supportsDeveloperRole ?? defaults.supportsDeveloperRole,
|
|
192
|
-
supportsReasoningEffort: configured?.supportsReasoningEffort ?? defaults.supportsReasoningEffort,
|
|
193
|
-
supportsUsageInStreaming: configured?.supportsUsageInStreaming ?? defaults.supportsUsageInStreaming,
|
|
194
|
-
maxTokensField: configured?.maxTokensField ?? defaults.maxTokensField,
|
|
195
|
-
requiresToolResultName: configured?.requiresToolResultName ?? false,
|
|
196
|
-
requiresAssistantAfterToolResult: configured?.requiresAssistantAfterToolResult ?? false,
|
|
197
|
-
requiresThinkingAsText: configured?.requiresThinkingAsText ?? false,
|
|
198
|
-
requiresReasoningContentOnAssistantMessages: configured?.requiresReasoningContentOnAssistantMessages ?? defaults.requiresReasoningContentOnAssistantMessages,
|
|
199
|
-
thinkingFormat: configured?.thinkingFormat ?? defaults.thinkingFormat,
|
|
200
|
-
openRouterRouting: configured?.openRouterRouting,
|
|
201
|
-
vercelGatewayRouting: configured?.vercelGatewayRouting ?? {},
|
|
202
|
-
zaiToolStream: configured?.zaiToolStream ?? false,
|
|
203
|
-
supportsStrictMode: configured?.supportsStrictMode ?? defaults.supportsStrictMode,
|
|
204
|
-
supportsJsonSchemaResponseFormat: configured?.supportsJsonSchemaResponseFormat ?? defaults.supportsJsonSchemaResponseFormat,
|
|
205
|
-
cacheControlFormat: configured?.cacheControlFormat ?? defaults.cacheControlFormat,
|
|
206
|
-
sessionAffinity: resolveSessionAffinity(model, defaults.sessionAffinityFormat),
|
|
207
|
-
supportsPromptCacheKey: configured?.supportsPromptCacheKey ?? false,
|
|
208
|
-
supportsLongCacheRetention: configured?.supportsLongCacheRetention ?? defaults.supportsLongCacheRetention,
|
|
209
|
-
visibleReasoningDetailTypes: configured && "visibleReasoningDetailTypes" in configured ? configured.visibleReasoningDetailTypes ?? defaults.visibleReasoningDetailTypes : defaults.visibleReasoningDetailTypes,
|
|
210
|
-
requiresNonEmptyUserOrAssistantMessage: defaults.requiresNonEmptyUserOrAssistantMessage
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.openAICompletionsCompatTestApi")] = { resolveOpenAICompletionsCompatDefaults };
|
|
214
|
-
//#endregion
|
|
215
|
-
//#region packages/ai/src/transports/openai-reasoning-compat.ts
|
|
216
|
-
/**
|
|
217
|
-
* OpenAI reasoning-effort compatibility helpers.
|
|
218
|
-
*
|
|
219
|
-
* Keeps provider metadata and built-in model exceptions on one path before request payloads are built.
|
|
220
|
-
*/
|
|
221
|
-
const OPENAI_MEDIUM_ONLY_REASONING_MODEL_IDS = /* @__PURE__ */ new Set(["gpt-5.1-codex-mini"]);
|
|
222
|
-
function readCompatReasoningEffortMap(compat) {
|
|
223
|
-
if (!compat || typeof compat !== "object") return {};
|
|
224
|
-
const rawMap = compat.reasoningEffortMap;
|
|
225
|
-
if (!rawMap || typeof rawMap !== "object") return {};
|
|
226
|
-
return Object.fromEntries(Object.entries(rawMap).filter((entry) => typeof entry[0] === "string" && typeof entry[1] === "string"));
|
|
227
|
-
}
|
|
228
|
-
/** Resolves the reasoning effort remap for an OpenAI-compatible model. */
|
|
229
|
-
function resolveOpenAIReasoningEffortMap(model, fallbackMap = {}) {
|
|
230
|
-
const provider = normalizeLowercaseStringOrEmpty(model.provider ?? "");
|
|
231
|
-
const id = normalizeLowercaseStringOrEmpty(model.id ?? "");
|
|
232
|
-
const builtinMap = provider === "openai" && OPENAI_MEDIUM_ONLY_REASONING_MODEL_IDS.has(id) ? {
|
|
233
|
-
minimal: "medium",
|
|
234
|
-
low: "medium"
|
|
235
|
-
} : {};
|
|
236
|
-
return {
|
|
237
|
-
...fallbackMap,
|
|
238
|
-
...builtinMap,
|
|
239
|
-
...readCompatReasoningEffortMap(model.compat)
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
//#endregion
|
|
243
|
-
//#region packages/ai/src/providers/openai-stop-reason.ts
|
|
244
|
-
function mapOpenAIStopReason(reason, options) {
|
|
245
|
-
if (reason === null) return { stopReason: "stop" };
|
|
246
|
-
switch (reason) {
|
|
247
|
-
case "stop":
|
|
248
|
-
case "end": return { stopReason: "stop" };
|
|
249
|
-
case "length": return { stopReason: "length" };
|
|
250
|
-
case "function_call":
|
|
251
|
-
case "tool_calls": return { stopReason: "toolUse" };
|
|
252
|
-
case "tool_call":
|
|
253
|
-
if (options?.allowSingularToolCall) return { stopReason: "toolUse" };
|
|
254
|
-
break;
|
|
255
|
-
case "content_filter": return {
|
|
256
|
-
stopReason: "error",
|
|
257
|
-
errorMessage: "Provider finish_reason: content_filter"
|
|
258
|
-
};
|
|
259
|
-
case "network_error": return {
|
|
260
|
-
stopReason: "error",
|
|
261
|
-
errorMessage: "Provider finish_reason: network_error"
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
return {
|
|
265
|
-
stopReason: "error",
|
|
266
|
-
errorMessage: `Provider finish_reason: ${reason}`
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
//#endregion
|
|
270
|
-
//#region packages/ai/src/providers/openai-tool-projection.ts
|
|
271
|
-
function unreadableToolDiagnostic(toolIndex) {
|
|
272
|
-
return {
|
|
273
|
-
toolIndex,
|
|
274
|
-
violations: [`tool[${toolIndex}] is unreadable`]
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
/** Snapshots direct/custom tool descriptors before OpenAI payload construction. */
|
|
278
|
-
function projectOpenAITools(tools) {
|
|
279
|
-
let inputToolCount;
|
|
280
|
-
try {
|
|
281
|
-
inputToolCount = tools.length;
|
|
282
|
-
} catch {
|
|
283
|
-
return {
|
|
284
|
-
inputToolCount: 0,
|
|
285
|
-
tools: [],
|
|
286
|
-
diagnostics: [unreadableToolDiagnostic(0)]
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
const projectedTools = [];
|
|
290
|
-
const diagnostics = [];
|
|
291
|
-
for (let toolIndex = 0; toolIndex < inputToolCount; toolIndex += 1) {
|
|
292
|
-
let tool;
|
|
293
|
-
try {
|
|
294
|
-
const candidate = tools[toolIndex];
|
|
295
|
-
if (!candidate) {
|
|
296
|
-
diagnostics.push(unreadableToolDiagnostic(toolIndex));
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
tool = candidate;
|
|
300
|
-
} catch {
|
|
301
|
-
diagnostics.push(unreadableToolDiagnostic(toolIndex));
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
let name;
|
|
305
|
-
try {
|
|
306
|
-
name = tool.name;
|
|
307
|
-
} catch {
|
|
308
|
-
diagnostics.push({
|
|
309
|
-
toolIndex,
|
|
310
|
-
violations: [`tool[${toolIndex}].name is unreadable`]
|
|
311
|
-
});
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
if (typeof name !== "string" || !name) {
|
|
315
|
-
diagnostics.push({
|
|
316
|
-
toolIndex,
|
|
317
|
-
violations: [`tool[${toolIndex}].name is empty`]
|
|
318
|
-
});
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
let parameters;
|
|
322
|
-
try {
|
|
323
|
-
parameters = tool.parameters;
|
|
324
|
-
} catch {
|
|
325
|
-
diagnostics.push({
|
|
326
|
-
toolIndex,
|
|
327
|
-
toolName: name,
|
|
328
|
-
violations: [`${name}.parameters is unreadable`]
|
|
329
|
-
});
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
const schemaProjection = projectRuntimeToolInputSchema(parameters ?? {}, `${name}.parameters`);
|
|
333
|
-
if (!isRecord(schemaProjection.schema) || schemaProjection.violations.length > 0) {
|
|
334
|
-
diagnostics.push({
|
|
335
|
-
toolIndex,
|
|
336
|
-
toolName: name,
|
|
337
|
-
violations: schemaProjection.violations.length > 0 ? schemaProjection.violations : [`${name}.parameters must be a JSON object schema`]
|
|
338
|
-
});
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
let descriptionValue;
|
|
342
|
-
try {
|
|
343
|
-
descriptionValue = tool.description;
|
|
344
|
-
} catch {}
|
|
345
|
-
const description = typeof descriptionValue === "string" ? descriptionValue : void 0;
|
|
346
|
-
projectedTools.push({
|
|
347
|
-
toolIndex,
|
|
348
|
-
name,
|
|
349
|
-
...description !== void 0 ? { description } : {},
|
|
350
|
-
parameters: schemaProjection.schema
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
return {
|
|
354
|
-
inputToolCount,
|
|
355
|
-
tools: projectedTools,
|
|
356
|
-
diagnostics
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
function requireProjectedFunction(name, projection, choiceLabel) {
|
|
360
|
-
if (!projection.tools.some((tool) => tool.name === name)) throw new Error(`${choiceLabel} requested unavailable tool "${name}" after schema conversion`);
|
|
361
|
-
}
|
|
362
|
-
/** Keeps Responses tool choices aligned with surviving function schemas. */
|
|
363
|
-
function reconcileOpenAIResponsesToolChoice(choice, projection) {
|
|
364
|
-
if (choice === "auto") return projection.tools.length > 0 ? choice : void 0;
|
|
365
|
-
if (choice === "required") {
|
|
366
|
-
if (projection.tools.length === 0) throw new Error("OpenAI Responses tool_choice requires a tool, but no tools survived schema conversion");
|
|
367
|
-
return choice;
|
|
368
|
-
}
|
|
369
|
-
if (choice === "none" || !isRecord(choice)) return choice;
|
|
370
|
-
const choiceType = choice.type;
|
|
371
|
-
if (choiceType === "function") {
|
|
372
|
-
const functionName = choice.name;
|
|
373
|
-
if (typeof functionName !== "string") return choice;
|
|
374
|
-
requireProjectedFunction(functionName, projection, "OpenAI Responses tool_choice");
|
|
375
|
-
return {
|
|
376
|
-
type: "function",
|
|
377
|
-
name: functionName
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
if (choiceType !== "allowed_tools") return choice;
|
|
381
|
-
const mode = choice.mode;
|
|
382
|
-
const tools = choice.tools;
|
|
383
|
-
if (mode !== "auto" && mode !== "required" || !Array.isArray(tools)) return choice;
|
|
384
|
-
const normalizedAllowedTools = [];
|
|
385
|
-
for (const tool of tools) {
|
|
386
|
-
if (!isRecord(tool) || tool.type !== "function") {
|
|
387
|
-
normalizedAllowedTools.push(tool);
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
const functionName = tool.name;
|
|
391
|
-
if (typeof functionName === "string" && projection.tools.some((projectedTool) => projectedTool.name === functionName)) normalizedAllowedTools.push({
|
|
392
|
-
type: "function",
|
|
393
|
-
name: functionName
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
if (normalizedAllowedTools.length === 0) {
|
|
397
|
-
if (mode === "auto") return "none";
|
|
398
|
-
throw new Error("OpenAI Responses tool_choice requires a tool, but no allowed tools survived schema conversion");
|
|
399
|
-
}
|
|
400
|
-
return {
|
|
401
|
-
type: "allowed_tools",
|
|
402
|
-
mode,
|
|
403
|
-
tools: normalizedAllowedTools
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
/** Keeps Chat Completions tool choices aligned with surviving function schemas. */
|
|
407
|
-
function reconcileOpenAICompletionsToolChoice(choice, projection) {
|
|
408
|
-
if (choice === "auto") return projection.tools.length > 0 ? choice : void 0;
|
|
409
|
-
if (choice === "required") {
|
|
410
|
-
if (projection.tools.length === 0) throw new Error("OpenAI Chat Completions tool_choice requires a tool, but no tools survived schema conversion");
|
|
411
|
-
return choice;
|
|
412
|
-
}
|
|
413
|
-
if (choice === "none" || !isRecord(choice)) return choice;
|
|
414
|
-
const choiceType = choice.type;
|
|
415
|
-
if (choiceType === "custom") throw new Error("OpenAI Chat Completions custom tool_choice is unsupported because this adapter emits function tools only");
|
|
416
|
-
if (choiceType === "function") {
|
|
417
|
-
const functionChoice = choice.function;
|
|
418
|
-
if (!isRecord(functionChoice)) return choice;
|
|
419
|
-
const functionName = functionChoice.name;
|
|
420
|
-
if (typeof functionName !== "string") return choice;
|
|
421
|
-
requireProjectedFunction(functionName, projection, "OpenAI Chat Completions tool_choice");
|
|
422
|
-
return {
|
|
423
|
-
type: "function",
|
|
424
|
-
function: { name: functionName }
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
if (choiceType !== "allowed_tools") return choice;
|
|
428
|
-
const allowedConfig = choice.allowed_tools;
|
|
429
|
-
if (!isRecord(allowedConfig)) return choice;
|
|
430
|
-
const mode = allowedConfig.mode;
|
|
431
|
-
const tools = allowedConfig.tools;
|
|
432
|
-
if (mode !== "auto" && mode !== "required" || !Array.isArray(tools)) return choice;
|
|
433
|
-
const normalizedAllowedTools = [];
|
|
434
|
-
for (const tool of tools) {
|
|
435
|
-
if (!isRecord(tool) || tool.type !== "function") continue;
|
|
436
|
-
const functionChoice = tool.function;
|
|
437
|
-
const functionName = isRecord(functionChoice) ? functionChoice.name : void 0;
|
|
438
|
-
if (typeof functionName === "string" && projection.tools.some((projectedTool) => projectedTool.name === functionName)) normalizedAllowedTools.push({
|
|
439
|
-
type: "function",
|
|
440
|
-
function: { name: functionName }
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
if (normalizedAllowedTools.length === 0) {
|
|
444
|
-
if (mode === "auto") return "none";
|
|
445
|
-
throw new Error("OpenAI Chat Completions tool_choice requires a tool, but no allowed tools survived schema conversion");
|
|
446
|
-
}
|
|
447
|
-
return {
|
|
448
|
-
type: "allowed_tools",
|
|
449
|
-
allowed_tools: {
|
|
450
|
-
mode,
|
|
451
|
-
tools: normalizedAllowedTools
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
//#endregion
|
|
456
|
-
//#region packages/ai/src/providers/openai-completions.ts
|
|
457
|
-
var openai_completions_exports = /* @__PURE__ */ __exportAll({
|
|
458
|
-
convertMessages: () => convertMessages,
|
|
459
|
-
streamOpenAICompletions: () => streamOpenAICompletions,
|
|
460
|
-
streamSimpleOpenAICompletions: () => streamSimpleOpenAICompletions
|
|
461
|
-
});
|
|
462
|
-
/**
|
|
463
|
-
* Check if conversation messages contain tool calls or tool results.
|
|
464
|
-
* This is needed because Anthropic (via proxy) requires the tools param
|
|
465
|
-
* to be present when messages include tool_calls or tool role messages.
|
|
466
|
-
*/
|
|
467
|
-
function hasToolHistory(messages) {
|
|
468
|
-
for (const msg of messages) {
|
|
469
|
-
if (msg.role === "toolResult") return true;
|
|
470
|
-
if (msg.role === "assistant") {
|
|
471
|
-
if (Array.isArray(msg.content) && msg.content.some((block) => block.type === "toolCall")) return true;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return false;
|
|
475
|
-
}
|
|
476
|
-
function isTextContentBlock(block) {
|
|
477
|
-
return block.type === "text";
|
|
478
|
-
}
|
|
479
|
-
function isThinkingContentBlock(block) {
|
|
480
|
-
return block.type === "thinking";
|
|
481
|
-
}
|
|
482
|
-
function isToolCallBlock(block) {
|
|
483
|
-
return block.type === "toolCall";
|
|
484
|
-
}
|
|
485
|
-
const EMPTY_TOOL_RESULT_TEXT = "(no output)";
|
|
486
|
-
function sanitizeToolResultText(text, fallback) {
|
|
487
|
-
const sanitized = sanitizeSurrogates(text);
|
|
488
|
-
return sanitized.trim().length > 0 ? sanitized : fallback;
|
|
489
|
-
}
|
|
490
|
-
function isEncryptedReasoningDetail(detail) {
|
|
491
|
-
if (typeof detail !== "object" || detail === null) return false;
|
|
492
|
-
const candidate = detail;
|
|
493
|
-
return candidate.type === "reasoning.encrypted" && typeof candidate.id === "string" && candidate.id.length > 0 && typeof candidate.data === "string" && candidate.data.length > 0;
|
|
494
|
-
}
|
|
495
|
-
const streamOpenAICompletions = (model, context, options) => {
|
|
496
|
-
const stream = new AssistantMessageEventStream();
|
|
497
|
-
(async () => {
|
|
498
|
-
const output = {
|
|
499
|
-
role: "assistant",
|
|
500
|
-
content: [],
|
|
501
|
-
api: model.api,
|
|
502
|
-
provider: model.provider,
|
|
503
|
-
model: model.id,
|
|
504
|
-
usage: {
|
|
505
|
-
input: 0,
|
|
506
|
-
output: 0,
|
|
507
|
-
cacheRead: 0,
|
|
508
|
-
cacheWrite: 0,
|
|
509
|
-
totalTokens: 0,
|
|
510
|
-
cost: {
|
|
511
|
-
input: 0,
|
|
512
|
-
output: 0,
|
|
513
|
-
cacheRead: 0,
|
|
514
|
-
cacheWrite: 0,
|
|
515
|
-
total: 0
|
|
516
|
-
}
|
|
517
|
-
},
|
|
518
|
-
stopReason: "stop",
|
|
519
|
-
timestamp: Date.now()
|
|
520
|
-
};
|
|
521
|
-
const provisionalCommentaryTags = /* @__PURE__ */ new Map();
|
|
522
|
-
let firstEventAbort;
|
|
523
|
-
try {
|
|
524
|
-
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
525
|
-
const compat = resolveOpenAICompletionsCompat(model);
|
|
526
|
-
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
|
|
527
|
-
const cacheSessionId = cacheRetention === "none" ? void 0 : options?.sessionId;
|
|
528
|
-
const client = createClient(model, context, apiKey, options?.headers, cacheSessionId, compat);
|
|
529
|
-
let params = buildParams(model, context, options, compat, cacheRetention);
|
|
530
|
-
const nextParams = await options?.onPayload?.(params, model);
|
|
531
|
-
if (nextParams !== void 0) params = nextParams;
|
|
532
|
-
firstEventAbort = createFirstStreamEventAbortController(options?.signal);
|
|
533
|
-
const requestOptions = {
|
|
534
|
-
signal: firstEventAbort.signal,
|
|
535
|
-
...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
|
|
536
|
-
maxRetries: options?.maxRetries ?? 0
|
|
537
|
-
};
|
|
538
|
-
const { data: openaiStream, response } = await client.chat.completions.create(params, requestOptions).withResponse();
|
|
539
|
-
await options?.onResponse?.({
|
|
540
|
-
status: response.status,
|
|
541
|
-
headers: headersToRecord(response.headers)
|
|
542
|
-
}, model);
|
|
543
|
-
stream.push({
|
|
544
|
-
type: "start",
|
|
545
|
-
partial: output
|
|
546
|
-
});
|
|
547
|
-
let textBlock = null;
|
|
548
|
-
let thinkingBlock = null;
|
|
549
|
-
let hasFinishReason = false;
|
|
550
|
-
const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
|
|
551
|
-
const toolCallBlocksById = /* @__PURE__ */ new Map();
|
|
552
|
-
const toolCallBlocksByFirstId = /* @__PURE__ */ new Map();
|
|
553
|
-
const pendingReasoningDetailsByToolCallId = /* @__PURE__ */ new Map();
|
|
554
|
-
const blocks = output.content;
|
|
555
|
-
const finishedBlocks = /* @__PURE__ */ new Set();
|
|
556
|
-
const contentIndices = /* @__PURE__ */ new WeakMap();
|
|
557
|
-
const appendBlock = (block) => {
|
|
558
|
-
contentIndices.set(block, blocks.length);
|
|
559
|
-
blocks.push(block);
|
|
560
|
-
};
|
|
561
|
-
const getContentIndex = (block) => contentIndices.get(block) ?? -1;
|
|
562
|
-
const rememberFirstToolCallById = (id, block) => {
|
|
563
|
-
if (toolCallBlocksByFirstId.has(id)) return;
|
|
564
|
-
toolCallBlocksByFirstId.set(id, block);
|
|
565
|
-
const pendingDetail = pendingReasoningDetailsByToolCallId.get(id);
|
|
566
|
-
if (pendingDetail) {
|
|
567
|
-
block.thoughtSignature = pendingDetail;
|
|
568
|
-
pendingReasoningDetailsByToolCallId.delete(id);
|
|
569
|
-
}
|
|
570
|
-
};
|
|
571
|
-
const finishBlock = (block) => {
|
|
572
|
-
const contentIndex = getContentIndex(block);
|
|
573
|
-
if (contentIndex === -1 || finishedBlocks.has(block)) return;
|
|
574
|
-
finishedBlocks.add(block);
|
|
575
|
-
if (block.type === "text") stream.push({
|
|
576
|
-
type: "text_end",
|
|
577
|
-
contentIndex,
|
|
578
|
-
content: block.text,
|
|
579
|
-
partial: output
|
|
580
|
-
});
|
|
581
|
-
else if (block.type === "thinking") stream.push({
|
|
582
|
-
type: "thinking_end",
|
|
583
|
-
contentIndex,
|
|
584
|
-
content: block.thinking,
|
|
585
|
-
partial: output
|
|
586
|
-
});
|
|
587
|
-
else if (block.type === "toolCall") {
|
|
588
|
-
delete block.partialArgs;
|
|
589
|
-
delete block.streamIndex;
|
|
590
|
-
stream.push({
|
|
591
|
-
type: "toolcall_end",
|
|
592
|
-
contentIndex,
|
|
593
|
-
toolCall: block,
|
|
594
|
-
partial: output
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
const ensureTextBlock = () => {
|
|
599
|
-
if (!textBlock) {
|
|
600
|
-
textBlock = {
|
|
601
|
-
type: "text",
|
|
602
|
-
text: ""
|
|
603
|
-
};
|
|
604
|
-
appendBlock(textBlock);
|
|
605
|
-
stream.push({
|
|
606
|
-
type: "text_start",
|
|
607
|
-
contentIndex: getContentIndex(textBlock),
|
|
608
|
-
partial: output
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
return textBlock;
|
|
612
|
-
};
|
|
613
|
-
const ensureThinkingBlock = (thinkingSignature) => {
|
|
614
|
-
if (!thinkingBlock) {
|
|
615
|
-
thinkingBlock = {
|
|
616
|
-
type: "thinking",
|
|
617
|
-
thinking: "",
|
|
618
|
-
thinkingSignature
|
|
619
|
-
};
|
|
620
|
-
appendBlock(thinkingBlock);
|
|
621
|
-
stream.push({
|
|
622
|
-
type: "thinking_start",
|
|
623
|
-
contentIndex: getContentIndex(thinkingBlock),
|
|
624
|
-
partial: output
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
return thinkingBlock;
|
|
628
|
-
};
|
|
629
|
-
const sealNativeReasoningBeforeText = () => {
|
|
630
|
-
if (thinkingBlock && !reasoningTagTextPartitioner.isInsideReasoning()) {
|
|
631
|
-
finishBlock(thinkingBlock);
|
|
632
|
-
thinkingBlock = null;
|
|
633
|
-
}
|
|
634
|
-
};
|
|
635
|
-
const appendTextDelta = (delta) => {
|
|
636
|
-
sealNativeReasoningBeforeText();
|
|
637
|
-
const block = ensureTextBlock();
|
|
638
|
-
block.text += delta;
|
|
639
|
-
stream.push({
|
|
640
|
-
type: "text_delta",
|
|
641
|
-
contentIndex: getContentIndex(block),
|
|
642
|
-
delta,
|
|
643
|
-
partial: output
|
|
644
|
-
});
|
|
645
|
-
};
|
|
646
|
-
const appendThinkingDelta = (thinkingSignature, delta) => {
|
|
647
|
-
const block = ensureThinkingBlock(thinkingSignature);
|
|
648
|
-
block.thinking += delta;
|
|
649
|
-
stream.push({
|
|
650
|
-
type: "thinking_delta",
|
|
651
|
-
contentIndex: getContentIndex(block),
|
|
652
|
-
delta,
|
|
653
|
-
partial: output
|
|
654
|
-
});
|
|
655
|
-
};
|
|
656
|
-
const ensureToolCallBlock = (toolCall) => {
|
|
657
|
-
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
|
|
658
|
-
let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
|
|
659
|
-
if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
|
|
660
|
-
if (!block) {
|
|
661
|
-
block = {
|
|
662
|
-
type: "toolCall",
|
|
663
|
-
id: toolCall.id || "",
|
|
664
|
-
name: toolCall.function?.name || "",
|
|
665
|
-
arguments: {},
|
|
666
|
-
partialArgs: "",
|
|
667
|
-
streamIndex
|
|
668
|
-
};
|
|
669
|
-
if (streamIndex !== void 0) toolCallBlocksByIndex.set(streamIndex, block);
|
|
670
|
-
if (toolCall.id) {
|
|
671
|
-
toolCallBlocksById.set(toolCall.id, block);
|
|
672
|
-
rememberFirstToolCallById(toolCall.id, block);
|
|
673
|
-
}
|
|
674
|
-
appendBlock(block);
|
|
675
|
-
stream.push({
|
|
676
|
-
type: "toolcall_start",
|
|
677
|
-
contentIndex: getContentIndex(block),
|
|
678
|
-
partial: output
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
if (streamIndex !== void 0 && block.streamIndex === void 0) {
|
|
682
|
-
block.streamIndex = streamIndex;
|
|
683
|
-
toolCallBlocksByIndex.set(streamIndex, block);
|
|
684
|
-
}
|
|
685
|
-
if (toolCall.id) toolCallBlocksById.set(toolCall.id, block);
|
|
686
|
-
return block;
|
|
687
|
-
};
|
|
688
|
-
const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
|
|
689
|
-
const appendPartitionedContent = (text, hasMirroredReasoning) => {
|
|
690
|
-
const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(text) : reasoningTagTextPartitioner.pushVisible(text);
|
|
691
|
-
for (const delta of routedDeltas) if (delta.kind === "text") appendTextDelta(delta.text);
|
|
692
|
-
};
|
|
693
|
-
const flushPartitionedContent = () => {
|
|
694
|
-
for (const delta of reasoningTagTextPartitioner.flush()) if (delta.kind === "text") appendTextDelta(delta.text);
|
|
695
|
-
};
|
|
696
|
-
const guardedOpenaiStream = withFirstStreamEventTimeout(openaiStream, {
|
|
697
|
-
provider: model.provider,
|
|
698
|
-
api: model.api,
|
|
699
|
-
model: model.id,
|
|
700
|
-
timeoutMs: getFirstStreamEventTimeoutMs(options) ?? 0,
|
|
701
|
-
stage: "completions",
|
|
702
|
-
abort: firstEventAbort.abort,
|
|
703
|
-
onTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
704
|
-
hint: "The provider may be stalled while parsing the tool payload; retry with a smaller tool surface or enable OPENCLAW_DEBUG_MODEL_PAYLOAD=tools to inspect exposed tools."
|
|
705
|
-
});
|
|
706
|
-
for await (const chunk of guardedOpenaiStream) {
|
|
707
|
-
if (!chunk || typeof chunk !== "object") continue;
|
|
708
|
-
output.responseId ||= chunk.id;
|
|
709
|
-
if (typeof chunk.model === "string" && chunk.model.length > 0 && chunk.model !== model.id) output.responseModel ||= chunk.model;
|
|
710
|
-
if (chunk.usage) output.usage = parseChunkUsage(chunk.usage, model);
|
|
711
|
-
const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
|
|
712
|
-
if (!choice) continue;
|
|
713
|
-
const choiceUsage = choice.usage;
|
|
714
|
-
if (!chunk.usage && choiceUsage) output.usage = parseChunkUsage(choiceUsage, model);
|
|
715
|
-
if (choice.finish_reason) {
|
|
716
|
-
const finishReasonResult = mapOpenAIStopReason(choice.finish_reason);
|
|
717
|
-
output.stopReason = finishReasonResult.stopReason;
|
|
718
|
-
if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
|
|
719
|
-
hasFinishReason = true;
|
|
720
|
-
}
|
|
721
|
-
const choiceDelta = choice.delta ?? choice.message;
|
|
722
|
-
if (choiceDelta) {
|
|
723
|
-
const reasoningFields = [
|
|
724
|
-
"reasoning_content",
|
|
725
|
-
"reasoning",
|
|
726
|
-
"reasoning_text"
|
|
727
|
-
];
|
|
728
|
-
const deltaFields = choiceDelta;
|
|
729
|
-
const shouldEmitReasoning = Boolean(model.reasoning && options?.reasoningEffort);
|
|
730
|
-
let foundReasoningField = null;
|
|
731
|
-
for (const field of reasoningFields) {
|
|
732
|
-
const value = deltaFields[field];
|
|
733
|
-
if (typeof value === "string" && value.length > 0) {
|
|
734
|
-
foundReasoningField = field;
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
if (foundReasoningField) reasoningTagTextPartitioner.markStrict();
|
|
739
|
-
if (shouldEmitReasoning && foundReasoningField) {
|
|
740
|
-
const delta = deltaFields[foundReasoningField];
|
|
741
|
-
if (typeof delta === "string" && delta.length > 0) appendThinkingDelta(model.provider === "opencode-go" && foundReasoningField === "reasoning" ? "reasoning_content" : foundReasoningField, delta);
|
|
742
|
-
}
|
|
743
|
-
if (choiceDelta.content !== null && choiceDelta.content !== void 0 && choiceDelta.content.length > 0) appendPartitionedContent(choiceDelta.content, Boolean(foundReasoningField));
|
|
744
|
-
const refusalText = typeof choiceDelta.refusal === "string" ? choiceDelta.refusal : "";
|
|
745
|
-
if (refusalText.length > 0) appendPartitionedContent(refusalText, Boolean(foundReasoningField));
|
|
746
|
-
if (choiceDelta.tool_calls && choiceDelta.tool_calls.length > 0) {
|
|
747
|
-
flushPartitionedContent();
|
|
748
|
-
sealNativeReasoningBeforeText();
|
|
749
|
-
rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
|
|
750
|
-
for (const toolCall of choiceDelta.tool_calls) {
|
|
751
|
-
const block = ensureToolCallBlock(toolCall);
|
|
752
|
-
if (!block.id && toolCall.id) {
|
|
753
|
-
block.id = toolCall.id;
|
|
754
|
-
toolCallBlocksById.set(toolCall.id, block);
|
|
755
|
-
rememberFirstToolCallById(toolCall.id, block);
|
|
756
|
-
}
|
|
757
|
-
if (!block.name && toolCall.function?.name) block.name = toolCall.function.name;
|
|
758
|
-
let delta = "";
|
|
759
|
-
if (toolCall.function?.arguments) {
|
|
760
|
-
delta = toolCall.function.arguments;
|
|
761
|
-
block.partialArgs = (block.partialArgs ?? "") + toolCall.function.arguments;
|
|
762
|
-
block.arguments = parseStreamingJson(block.partialArgs);
|
|
763
|
-
}
|
|
764
|
-
stream.push({
|
|
765
|
-
type: "toolcall_delta",
|
|
766
|
-
contentIndex: getContentIndex(block),
|
|
767
|
-
delta,
|
|
768
|
-
partial: output
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
const reasoningDetails = choiceDelta.reasoning_details;
|
|
773
|
-
if (Array.isArray(reasoningDetails)) {
|
|
774
|
-
for (const detail of reasoningDetails) if (isEncryptedReasoningDetail(detail)) {
|
|
775
|
-
const serializedDetail = JSON.stringify(detail);
|
|
776
|
-
const matchingToolCall = toolCallBlocksByFirstId.get(detail.id);
|
|
777
|
-
if (matchingToolCall) matchingToolCall.thoughtSignature = serializedDetail;
|
|
778
|
-
else pendingReasoningDetailsByToolCallId.set(detail.id, serializedDetail);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
flushPartitionedContent();
|
|
784
|
-
for (const block of blocks) finishBlock(block);
|
|
785
|
-
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
786
|
-
if (output.stopReason === "aborted") throw new Error("Request was aborted");
|
|
787
|
-
if (output.stopReason === "error") throw new Error(output.errorMessage || "Provider returned an error stop reason");
|
|
788
|
-
if (!hasFinishReason) throw new Error("Stream ended without finish_reason");
|
|
789
|
-
const hasToolCalls = output.content.some((block) => block.type === "toolCall");
|
|
790
|
-
const hasVisibleText = output.content.some((block) => block.type === "text" && block.text.trim().length > 0);
|
|
791
|
-
if (output.stopReason === "toolUse" && !hasToolCalls) output.stopReason = "stop";
|
|
792
|
-
if (output.stopReason === "stop" && hasToolCalls && !hasVisibleText) output.stopReason = "toolUse";
|
|
793
|
-
if (hasToolCalls && output.stopReason !== "toolUse") output.content = output.content.filter((block) => block.type !== "toolCall");
|
|
794
|
-
if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
|
|
795
|
-
if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
|
|
796
|
-
stream.push({
|
|
797
|
-
type: "done",
|
|
798
|
-
reason: output.stopReason,
|
|
799
|
-
message: output
|
|
800
|
-
});
|
|
801
|
-
stream.end();
|
|
802
|
-
} catch (error) {
|
|
803
|
-
for (const block of output.content) {
|
|
804
|
-
delete block.index;
|
|
805
|
-
delete block.partialArgs;
|
|
806
|
-
delete block.streamIndex;
|
|
807
|
-
}
|
|
808
|
-
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
809
|
-
output.errorMessage = formatProviderError(error);
|
|
810
|
-
const rawMetadata = error?.error?.metadata?.raw;
|
|
811
|
-
if (rawMetadata && !output.errorMessage.includes(rawMetadata)) output.errorMessage += `\n${rawMetadata}`;
|
|
812
|
-
stream.push({
|
|
813
|
-
type: "error",
|
|
814
|
-
reason: output.stopReason,
|
|
815
|
-
error: output
|
|
816
|
-
});
|
|
817
|
-
stream.end();
|
|
818
|
-
} finally {
|
|
819
|
-
firstEventAbort?.dispose();
|
|
820
|
-
}
|
|
821
|
-
})();
|
|
822
|
-
return stream;
|
|
823
|
-
};
|
|
824
|
-
const streamSimpleOpenAICompletions = (model, context, options) => {
|
|
825
|
-
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
|
|
826
|
-
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
827
|
-
const base = buildBaseOptions(model, options, apiKey);
|
|
828
|
-
const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : void 0;
|
|
829
|
-
const reasoningEffort = clampedReasoning === "off" ? void 0 : clampedReasoning === "max" ? "xhigh" : clampedReasoning;
|
|
830
|
-
const toolChoice = options?.toolChoice;
|
|
831
|
-
return streamOpenAICompletions(model, context, {
|
|
832
|
-
...base,
|
|
833
|
-
reasoningEffort,
|
|
834
|
-
toolChoice
|
|
835
|
-
});
|
|
836
|
-
};
|
|
837
|
-
function createClient(model, context, apiKey, optionsHeaders, sessionId, compat = resolveOpenAICompletionsCompat(model)) {
|
|
838
|
-
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
839
|
-
const headers = { ...model.headers };
|
|
840
|
-
if (model.provider === "github-copilot") {
|
|
841
|
-
const hasImages = hasCopilotVisionInput(context.messages);
|
|
842
|
-
const copilotHeaders = buildCopilotDynamicHeaders({
|
|
843
|
-
messages: context.messages,
|
|
844
|
-
hasImages
|
|
845
|
-
});
|
|
846
|
-
Object.assign(headers, copilotHeaders);
|
|
847
|
-
}
|
|
848
|
-
if (sessionId && compat.sessionAffinity !== "none") if (compat.sessionAffinity === "openrouter") headers["x-session-id"] = sessionId;
|
|
849
|
-
else {
|
|
850
|
-
headers.session_id = sessionId;
|
|
851
|
-
headers["x-client-request-id"] = sessionId;
|
|
852
|
-
headers["x-session-affinity"] = sessionId;
|
|
853
|
-
}
|
|
854
|
-
if (optionsHeaders) Object.assign(headers, optionsHeaders);
|
|
855
|
-
const defaultHeaders = model.provider === "cloudflare-ai-gateway" ? {
|
|
856
|
-
...headers,
|
|
857
|
-
Authorization: headers.Authorization ?? null,
|
|
858
|
-
"cf-aig-authorization": `Bearer ${apiKey}`
|
|
859
|
-
} : headers;
|
|
860
|
-
return new OpenAI({
|
|
861
|
-
apiKey,
|
|
862
|
-
baseURL: isCloudflareProvider(model.provider) ? resolveCloudflareBaseUrl(model) : model.baseUrl,
|
|
863
|
-
dangerouslyAllowBrowser: true,
|
|
864
|
-
defaultHeaders,
|
|
865
|
-
fetch: getAiTransportHost().buildModelFetch(model)
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
function buildParams(model, context, options, compat = resolveOpenAICompletionsCompat(model), cacheRetention = resolveCacheRetention(options?.cacheRetention)) {
|
|
869
|
-
const cacheControl = getCompatCacheControl(compat, cacheRetention);
|
|
870
|
-
const cacheOptOutIndexes = /* @__PURE__ */ new Set();
|
|
871
|
-
const messages = convertMessages(model, context, compat, {
|
|
872
|
-
cacheOptOutIndexes,
|
|
873
|
-
preserveSystemPromptCacheBoundary: cacheControl !== void 0
|
|
874
|
-
});
|
|
875
|
-
const supportsPromptCacheKey = model.baseUrl.includes("api.openai.com") || compat.supportsPromptCacheKey;
|
|
876
|
-
const promptCacheKey = supportsPromptCacheKey && cacheRetention !== "none" ? clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId) : void 0;
|
|
877
|
-
const params = {
|
|
878
|
-
model: model.id,
|
|
879
|
-
messages,
|
|
880
|
-
stream: true,
|
|
881
|
-
prompt_cache_key: promptCacheKey,
|
|
882
|
-
prompt_cache_retention: supportsPromptCacheKey && cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : void 0
|
|
883
|
-
};
|
|
884
|
-
if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
|
|
885
|
-
if (compat.supportsStore) params.store = false;
|
|
886
|
-
if (options?.maxTokens) {
|
|
887
|
-
const maxTokens = clampOpenAICompletionsMaxTokens(model, options.maxTokens);
|
|
888
|
-
if (compat.maxTokensField === "max_tokens") params.max_tokens = maxTokens;
|
|
889
|
-
else params.max_completion_tokens = maxTokens;
|
|
890
|
-
}
|
|
891
|
-
if (options?.temperature !== void 0) params.temperature = options.temperature;
|
|
892
|
-
if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
|
|
893
|
-
const requestedResponseFormat = options?.responseFormat;
|
|
894
|
-
const responseFormat = requestedResponseFormat === void 0 ? void 0 : resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
|
|
895
|
-
provider: model.provider,
|
|
896
|
-
baseUrl: model.baseUrl,
|
|
897
|
-
hasTools: () => Boolean(context.tools?.length)
|
|
898
|
-
}) ? void 0 : requestedResponseFormat, compat.supportsJsonSchemaResponseFormat);
|
|
899
|
-
if (responseFormat !== void 0) params.response_format = responseFormat;
|
|
900
|
-
let toolProjection;
|
|
901
|
-
if (context.tools) {
|
|
902
|
-
const converted = convertTools(context.tools, compat);
|
|
903
|
-
toolProjection = converted.projection;
|
|
904
|
-
if (converted.tools.length > 0) params.tools = converted.tools;
|
|
905
|
-
else if (hasToolHistory(context.messages)) params.tools = [];
|
|
906
|
-
if (compat.zaiToolStream && converted.tools.length > 0) params.tool_stream = true;
|
|
907
|
-
} else if (hasToolHistory(context.messages)) params.tools = [];
|
|
908
|
-
if (cacheControl) applyAnthropicCacheControl(messages, params.tools, cacheControl, cacheOptOutIndexes);
|
|
909
|
-
if (options?.toolChoice) {
|
|
910
|
-
const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, toolProjection ?? projectOpenAITools([]));
|
|
911
|
-
if (toolChoice !== void 0) params.tool_choice = toolChoice;
|
|
912
|
-
}
|
|
913
|
-
const reasoningEffortMap = resolveOpenAIReasoningEffortMap(model);
|
|
914
|
-
const reasoningEffort = options?.reasoningEffort === void 0 ? void 0 : reasoningEffortMap[options.reasoningEffort] ?? model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
|
|
915
|
-
const reasoningEnabled = reasoningEffort !== void 0;
|
|
916
|
-
const offReasoningEffort = reasoningEffortMap.off ?? model.thinkingLevelMap?.off;
|
|
917
|
-
if (compat.thinkingFormat === "zai" && model.reasoning) params.thinking = reasoningEnabled ? {
|
|
918
|
-
type: "enabled",
|
|
919
|
-
clear_thinking: false
|
|
920
|
-
} : { type: "disabled" };
|
|
921
|
-
else if (compat.thinkingFormat === "qwen" && model.reasoning) params.enable_thinking = reasoningEnabled;
|
|
922
|
-
else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = {
|
|
923
|
-
enable_thinking: reasoningEnabled,
|
|
924
|
-
preserve_thinking: true
|
|
925
|
-
};
|
|
926
|
-
else if (compat.thinkingFormat === "deepseek" && model.reasoning) {
|
|
927
|
-
params.thinking = { type: reasoningEnabled ? "enabled" : "disabled" };
|
|
928
|
-
if (reasoningEnabled && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
929
|
-
} else if (compat.thinkingFormat === "openrouter" && model.reasoning) {
|
|
930
|
-
const openRouterParams = params;
|
|
931
|
-
if (reasoningEnabled) openRouterParams.reasoning = { effort: reasoningEffort };
|
|
932
|
-
else if (offReasoningEffort !== null) openRouterParams.reasoning = { effort: offReasoningEffort ?? "none" };
|
|
933
|
-
} else if (compat.thinkingFormat === "together" && model.reasoning) {
|
|
934
|
-
const togetherParams = params;
|
|
935
|
-
togetherParams.reasoning = { enabled: reasoningEnabled };
|
|
936
|
-
if (reasoningEnabled && compat.supportsReasoningEffort) togetherParams.reasoning_effort = reasoningEffort;
|
|
937
|
-
} else if (reasoningEnabled && model.reasoning && compat.supportsReasoningEffort) params.reasoning_effort = reasoningEffort;
|
|
938
|
-
else if (model.reasoning && compat.supportsReasoningEffort) {
|
|
939
|
-
if (typeof offReasoningEffort === "string") params.reasoning_effort = offReasoningEffort;
|
|
940
|
-
}
|
|
941
|
-
if (compat.openRouterRouting) params.provider = compat.openRouterRouting;
|
|
942
|
-
if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
|
|
943
|
-
const routing = model.compat.vercelGatewayRouting;
|
|
944
|
-
if (routing.only || routing.order) {
|
|
945
|
-
const gatewayOptions = {};
|
|
946
|
-
if (routing.only) gatewayOptions.only = routing.only;
|
|
947
|
-
if (routing.order) gatewayOptions.order = routing.order;
|
|
948
|
-
params.providerOptions = { gateway: gatewayOptions };
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return params;
|
|
952
|
-
}
|
|
953
|
-
function clampOpenAICompletionsMaxTokens(model, requestedMaxTokens) {
|
|
954
|
-
const modelMaxTokens = typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
|
|
955
|
-
return modelMaxTokens === void 0 || requestedMaxTokens <= modelMaxTokens ? requestedMaxTokens : modelMaxTokens;
|
|
956
|
-
}
|
|
957
|
-
function getCompatCacheControl(compat, cacheRetention) {
|
|
958
|
-
if (compat.cacheControlFormat !== "anthropic" || cacheRetention === "none") return;
|
|
959
|
-
const ttl = cacheRetention === "long" && compat.supportsLongCacheRetention ? "1h" : void 0;
|
|
960
|
-
return {
|
|
961
|
-
type: "ephemeral",
|
|
962
|
-
...ttl ? { ttl } : {}
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
function applyAnthropicCacheControl(messages, tools, cacheControl, cacheOptOutIndexes) {
|
|
966
|
-
addCacheControlToSystemPrompt(messages, cacheControl);
|
|
967
|
-
addCacheControlToLastTool(tools, cacheControl);
|
|
968
|
-
addCacheControlToLastConversationMessage(messages, cacheControl, cacheOptOutIndexes);
|
|
969
|
-
}
|
|
970
|
-
function addCacheControlToSystemPrompt(messages, cacheControl) {
|
|
971
|
-
for (const message of messages) if (message.role === "system" || message.role === "developer") {
|
|
972
|
-
addCacheControlToInstructionMessage(message, cacheControl);
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
function addCacheControlToLastConversationMessage(messages, cacheControl, cacheOptOutIndexes) {
|
|
977
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
978
|
-
const message = messages[i];
|
|
979
|
-
if (!message || cacheOptOutIndexes.has(i)) continue;
|
|
980
|
-
if (message.role === "user" || message.role === "assistant") {
|
|
981
|
-
if (addCacheControlToMessage(message, cacheControl)) return;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
function addCacheControlToLastTool(tools, cacheControl) {
|
|
986
|
-
if (!tools || tools.length === 0) return;
|
|
987
|
-
const lastTool = tools.at(-1);
|
|
988
|
-
if (!lastTool) return;
|
|
989
|
-
lastTool.cache_control = cacheControl;
|
|
990
|
-
}
|
|
991
|
-
function addCacheControlToInstructionMessage(message, cacheControl) {
|
|
992
|
-
return addCacheControlToTextContent(message, cacheControl);
|
|
993
|
-
}
|
|
994
|
-
function addCacheControlToMessage(message, cacheControl) {
|
|
995
|
-
if (message.role === "user" || message.role === "assistant") return addCacheControlToTextContent(message, cacheControl);
|
|
996
|
-
return false;
|
|
997
|
-
}
|
|
998
|
-
function addCacheControlToTextContent(message, cacheControl) {
|
|
999
|
-
const content = message.content;
|
|
1000
|
-
if (typeof content === "string") {
|
|
1001
|
-
if (content.length === 0) return false;
|
|
1002
|
-
message.content = buildCacheControlledTextParts(content, cacheControl);
|
|
1003
|
-
return true;
|
|
1004
|
-
}
|
|
1005
|
-
if (!Array.isArray(content)) return false;
|
|
1006
|
-
for (let i = content.length - 1; i >= 0; i--) {
|
|
1007
|
-
const part = content[i];
|
|
1008
|
-
if (part?.type === "text") {
|
|
1009
|
-
const text = part.text;
|
|
1010
|
-
content.splice(i, 1, ...buildCacheControlledTextParts(text, cacheControl));
|
|
1011
|
-
return true;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
return false;
|
|
1015
|
-
}
|
|
1016
|
-
function buildCacheControlledTextParts(text, cacheControl) {
|
|
1017
|
-
const split = splitSystemPromptCacheBoundary(text);
|
|
1018
|
-
if (!split) return [{
|
|
1019
|
-
type: "text",
|
|
1020
|
-
text,
|
|
1021
|
-
cache_control: cacheControl
|
|
1022
|
-
}];
|
|
1023
|
-
const parts = [];
|
|
1024
|
-
if (split.stablePrefix) parts.push({
|
|
1025
|
-
type: "text",
|
|
1026
|
-
text: split.stablePrefix,
|
|
1027
|
-
cache_control: cacheControl
|
|
1028
|
-
});
|
|
1029
|
-
if (split.dynamicSuffix) parts.push({
|
|
1030
|
-
type: "text",
|
|
1031
|
-
text: split.dynamicSuffix
|
|
1032
|
-
});
|
|
1033
|
-
return parts.length > 0 ? parts : [{
|
|
1034
|
-
type: "text",
|
|
1035
|
-
text: ""
|
|
1036
|
-
}];
|
|
1037
|
-
}
|
|
1038
|
-
function convertMessages(model, context, compat, options = {}) {
|
|
1039
|
-
const params = [];
|
|
1040
|
-
const normalizeToolCallId = (id) => {
|
|
1041
|
-
if (id.includes("|")) return id.slice(0, id.indexOf("|")).replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
|
|
1042
|
-
if (model.provider === "openai") return id.length > 40 ? id.slice(0, 40) : id;
|
|
1043
|
-
return id;
|
|
1044
|
-
};
|
|
1045
|
-
const transformedMessages = transformMessages(context.messages, model, (id) => normalizeToolCallId(id));
|
|
1046
|
-
if (context.systemPrompt) {
|
|
1047
|
-
const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
|
|
1048
|
-
const systemPrompt = options.preserveSystemPromptCacheBoundary ? context.systemPrompt : stripSystemPromptCacheBoundary(context.systemPrompt);
|
|
1049
|
-
params.push({
|
|
1050
|
-
role,
|
|
1051
|
-
content: sanitizeSurrogates(systemPrompt)
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
let lastRole = null;
|
|
1055
|
-
for (let i = 0; i < transformedMessages.length; i++) {
|
|
1056
|
-
const msg = transformedMessages[i];
|
|
1057
|
-
if (!msg) continue;
|
|
1058
|
-
if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && msg.role === "user") params.push({
|
|
1059
|
-
role: "assistant",
|
|
1060
|
-
content: "I have processed the tool results."
|
|
1061
|
-
});
|
|
1062
|
-
if (msg.role === "user") {
|
|
1063
|
-
const isRuntimeContextCarrier = msg.runtimeContextCarrier === true;
|
|
1064
|
-
if (typeof msg.content === "string") {
|
|
1065
|
-
const userParam = {
|
|
1066
|
-
role: "user",
|
|
1067
|
-
content: sanitizeSurrogates(msg.content)
|
|
1068
|
-
};
|
|
1069
|
-
if (isRuntimeContextCarrier) options.cacheOptOutIndexes?.add(params.length);
|
|
1070
|
-
params.push(userParam);
|
|
1071
|
-
} else {
|
|
1072
|
-
const content = msg.content.map((item) => {
|
|
1073
|
-
if (item.type === "text") return {
|
|
1074
|
-
type: "text",
|
|
1075
|
-
text: sanitizeSurrogates(item.text)
|
|
1076
|
-
};
|
|
1077
|
-
return {
|
|
1078
|
-
type: "image_url",
|
|
1079
|
-
image_url: { url: `data:${item.mimeType};base64,${item.data}` }
|
|
1080
|
-
};
|
|
1081
|
-
});
|
|
1082
|
-
if (content.length === 0) continue;
|
|
1083
|
-
const userParam = {
|
|
1084
|
-
role: "user",
|
|
1085
|
-
content
|
|
1086
|
-
};
|
|
1087
|
-
if (isRuntimeContextCarrier) options.cacheOptOutIndexes?.add(params.length);
|
|
1088
|
-
params.push(userParam);
|
|
1089
|
-
}
|
|
1090
|
-
} else if (msg.role === "assistant") {
|
|
1091
|
-
const assistantMsg = {
|
|
1092
|
-
role: "assistant",
|
|
1093
|
-
content: compat.requiresAssistantAfterToolResult ? "" : null
|
|
1094
|
-
};
|
|
1095
|
-
const assistantTextParts = msg.content.filter(isTextContentBlock).filter((block) => block.text.trim().length > 0).map((block) => ({
|
|
1096
|
-
type: "text",
|
|
1097
|
-
text: sanitizeSurrogates(block.text)
|
|
1098
|
-
}));
|
|
1099
|
-
const assistantText = assistantTextParts.map((part) => part.text).join("");
|
|
1100
|
-
const nonEmptyThinkingBlocks = msg.content.filter(isThinkingContentBlock).filter((block) => block.thinking.trim().length > 0);
|
|
1101
|
-
if (nonEmptyThinkingBlocks.length > 0) if (compat.requiresThinkingAsText) assistantMsg.content = [{
|
|
1102
|
-
type: "text",
|
|
1103
|
-
text: nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n\n")
|
|
1104
|
-
}, ...assistantTextParts];
|
|
1105
|
-
else {
|
|
1106
|
-
if (assistantText.length > 0) assistantMsg.content = assistantText;
|
|
1107
|
-
let signature = nonEmptyThinkingBlocks.at(0)?.thinkingSignature;
|
|
1108
|
-
if (model.provider === "opencode-go" && signature === "reasoning") signature = "reasoning_content";
|
|
1109
|
-
if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => block.thinking).join("\n");
|
|
1110
|
-
}
|
|
1111
|
-
else if (assistantText.length > 0) assistantMsg.content = assistantText;
|
|
1112
|
-
const toolCalls = msg.content.filter(isToolCallBlock);
|
|
1113
|
-
if (toolCalls.length > 0) {
|
|
1114
|
-
assistantMsg.tool_calls = toolCalls.map((tc) => ({
|
|
1115
|
-
id: tc.id,
|
|
1116
|
-
type: "function",
|
|
1117
|
-
function: {
|
|
1118
|
-
name: tc.name,
|
|
1119
|
-
arguments: JSON.stringify(tc.arguments)
|
|
1120
|
-
}
|
|
1121
|
-
}));
|
|
1122
|
-
const reasoningDetails = toolCalls.flatMap((tc) => {
|
|
1123
|
-
const signature = tc.thoughtSignature;
|
|
1124
|
-
if (!signature) return [];
|
|
1125
|
-
try {
|
|
1126
|
-
const parsed = JSON.parse(signature);
|
|
1127
|
-
return parsed ? [parsed] : [];
|
|
1128
|
-
} catch {
|
|
1129
|
-
return [];
|
|
1130
|
-
}
|
|
1131
|
-
});
|
|
1132
|
-
if (reasoningDetails.length > 0) assistantMsg.reasoning_details = reasoningDetails;
|
|
1133
|
-
}
|
|
1134
|
-
if (compat.requiresReasoningContentOnAssistantMessages && model.reasoning && assistantMsg.reasoning_content === void 0) assistantMsg.reasoning_content = "";
|
|
1135
|
-
const content = assistantMsg.content;
|
|
1136
|
-
if (!(content !== null && content !== void 0 && (typeof content === "string" ? content.length > 0 : content.length > 0)) && !assistantMsg.tool_calls) continue;
|
|
1137
|
-
params.push(assistantMsg);
|
|
1138
|
-
} else if (msg.role === "toolResult") {
|
|
1139
|
-
const imageBlocks = [];
|
|
1140
|
-
let j = i;
|
|
1141
|
-
while (j < transformedMessages.length) {
|
|
1142
|
-
const toolMsg = transformedMessages.at(j);
|
|
1143
|
-
if (toolMsg?.role !== "toolResult") break;
|
|
1144
|
-
const textResult = extractToolResultText(toolMsg.content);
|
|
1145
|
-
const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
|
|
1146
|
-
const hasImages = toolMsg.content.some(isImageWithMediaPayload);
|
|
1147
|
-
const toolResultMsg = {
|
|
1148
|
-
role: "tool",
|
|
1149
|
-
content: sanitizeToolResultText(textResult, mediaPlaceholder ?? EMPTY_TOOL_RESULT_TEXT),
|
|
1150
|
-
tool_call_id: toolMsg.toolCallId
|
|
1151
|
-
};
|
|
1152
|
-
if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
|
|
1153
|
-
params.push(toolResultMsg);
|
|
1154
|
-
if (hasImages && model.input.includes("image")) {
|
|
1155
|
-
for (const block of toolMsg.content) if (isImageWithMediaPayload(block)) imageBlocks.push({
|
|
1156
|
-
type: "image_url",
|
|
1157
|
-
image_url: { url: `data:${block.mimeType};base64,${block.data}` }
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
j += 1;
|
|
1161
|
-
}
|
|
1162
|
-
i = j - 1;
|
|
1163
|
-
if (imageBlocks.length > 0) {
|
|
1164
|
-
if (compat.requiresAssistantAfterToolResult) params.push({
|
|
1165
|
-
role: "assistant",
|
|
1166
|
-
content: "I have processed the tool results."
|
|
1167
|
-
});
|
|
1168
|
-
params.push({
|
|
1169
|
-
role: "user",
|
|
1170
|
-
content: [{
|
|
1171
|
-
type: "text",
|
|
1172
|
-
text: "Attached image(s) from tool result:"
|
|
1173
|
-
}, ...imageBlocks]
|
|
1174
|
-
});
|
|
1175
|
-
lastRole = "user";
|
|
1176
|
-
} else lastRole = "toolResult";
|
|
1177
|
-
continue;
|
|
1178
|
-
}
|
|
1179
|
-
lastRole = msg.role;
|
|
1180
|
-
}
|
|
1181
|
-
return params;
|
|
1182
|
-
}
|
|
1183
|
-
function convertTools(tools, compat) {
|
|
1184
|
-
const projection = projectOpenAITools(tools);
|
|
1185
|
-
return {
|
|
1186
|
-
projection,
|
|
1187
|
-
tools: projection.tools.map((tool) => ({
|
|
1188
|
-
type: "function",
|
|
1189
|
-
function: {
|
|
1190
|
-
name: tool.name,
|
|
1191
|
-
description: tool.description,
|
|
1192
|
-
parameters: tool.parameters,
|
|
1193
|
-
...compat.supportsStrictMode && { strict: false }
|
|
1194
|
-
}
|
|
1195
|
-
}))
|
|
1196
|
-
};
|
|
1197
|
-
}
|
|
1198
|
-
function parseChunkUsage(rawUsage, model) {
|
|
1199
|
-
const promptTokens = rawUsage.prompt_tokens || 0;
|
|
1200
|
-
const cacheReadTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? rawUsage.prompt_cache_hit_tokens ?? 0;
|
|
1201
|
-
const cacheWriteTokens = rawUsage.prompt_tokens_details?.cache_write_tokens || 0;
|
|
1202
|
-
const input = Math.max(0, promptTokens - cacheReadTokens - cacheWriteTokens);
|
|
1203
|
-
const outputTokens = rawUsage.completion_tokens || 0;
|
|
1204
|
-
const usage = {
|
|
1205
|
-
input,
|
|
1206
|
-
output: outputTokens,
|
|
1207
|
-
cacheRead: cacheReadTokens,
|
|
1208
|
-
cacheWrite: cacheWriteTokens,
|
|
1209
|
-
totalTokens: input + outputTokens + cacheReadTokens + cacheWriteTokens,
|
|
1210
|
-
cost: {
|
|
1211
|
-
input: 0,
|
|
1212
|
-
output: 0,
|
|
1213
|
-
cacheRead: 0,
|
|
1214
|
-
cacheWrite: 0,
|
|
1215
|
-
total: 0
|
|
1216
|
-
}
|
|
1217
|
-
};
|
|
1218
|
-
calculateCost(model, usage);
|
|
1219
|
-
applyProviderReportedUsageCost(usage, rawUsage.cost);
|
|
1220
|
-
return usage;
|
|
1221
|
-
}
|
|
1222
|
-
//#endregion
|
|
1223
|
-
export { transformTransportMessages as C, resolveProviderRequestPolicyConfig as S, buildGuardedModelFetch as _, projectOpenAITools as a, resolveProviderEndpoint as b, mapOpenAIStopReason as c, resolveOpenAICompletionsCompat as d, resolveOpenAICompletionsResponseFormat as f, tagPendingCommentaryText as g, rememberPendingCommentaryTags as h, streamSimpleOpenAICompletions as i, resolveOpenAIReasoningEffortMap as l, clearPendingCommentaryText as m, openai_completions_exports as n, reconcileOpenAICompletionsToolChoice as o, shouldOmitOllamaCompatResponseFormat as p, streamOpenAICompletions as r, reconcileOpenAIResponsesToolChoice as s, convertMessages as t, detectOpenAICompletionsCompat as u, resolveModelRequestTimeoutMs as v, resolveProviderRequestCapabilities as x, resolveOpenAIStrictToolSetting as y };
|