@openclaw/ai 0.0.0 → 2026.7.1-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -3
  3. package/dist/anthropic-B5gZQM5X.mjs +1383 -0
  4. package/dist/api-registry-BXYnCOIR.d.mts +33 -0
  5. package/dist/azure-openai-responses-DNoSk8Uy.mjs +141 -0
  6. package/dist/azure-openai-responses-client-compat-a_O_GVQV.mjs +41 -0
  7. package/dist/diagnostics-BaTA9eVl.d.mts +25 -0
  8. package/dist/diagnostics-COpOtRwq.mjs +36 -0
  9. package/dist/diagnostics.d.mts +2 -0
  10. package/dist/diagnostics.mjs +2 -0
  11. package/dist/env-api-keys-CtMlqaQ4.mjs +171 -0
  12. package/dist/event-stream-0nZeBKl2.d.mts +26 -0
  13. package/dist/event-stream-ReMmOTzX.mjs +65 -0
  14. package/dist/event-stream.d.mts +2 -0
  15. package/dist/event-stream.mjs +2 -0
  16. package/dist/github-copilot-headers-BsH5cqGj.mjs +48 -0
  17. package/dist/google-D6sIQ1bL.mjs +55 -0
  18. package/dist/google-shared-ZPSl2qTi.mjs +548 -0
  19. package/dist/google-vertex-rDGwkoZK.mjs +111 -0
  20. package/dist/hash-CHgqbJmD.mjs +16 -0
  21. package/dist/headers-B_e4-1J0.mjs +9 -0
  22. package/dist/host-4t713IeR.mjs +37 -0
  23. package/dist/index-BoTnz8cv.d.mts +74 -0
  24. package/dist/index.d.mts +69 -0
  25. package/dist/index.mjs +7 -0
  26. package/dist/internal/anthropic.d.mts +234 -0
  27. package/dist/internal/anthropic.mjs +4 -0
  28. package/dist/internal/openai.d.mts +244 -0
  29. package/dist/internal/openai.mjs +7 -0
  30. package/dist/internal/runtime.d.mts +245 -0
  31. package/dist/internal/runtime.mjs +176 -0
  32. package/dist/internal/shared.d.mts +48 -0
  33. package/dist/internal/shared.mjs +3 -0
  34. package/dist/json-parse-DzNSIQBq.mjs +134 -0
  35. package/dist/llm-request-activity-CehVkZP-.mjs +35 -0
  36. package/dist/mistral-CePVNdws.mjs +563 -0
  37. package/dist/model-utils-DgmOla96.mjs +69 -0
  38. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +39 -0
  39. package/dist/openai-chatgpt-responses-DVC4Bk_A.mjs +1068 -0
  40. package/dist/openai-completions-B9QLIq2U.mjs +844 -0
  41. package/dist/openai-responses-B6LylGxM.mjs +136 -0
  42. package/dist/openai-responses-shared-sj2YUPYc.mjs +1944 -0
  43. package/dist/openai-tool-projection-BknoV11q.mjs +195 -0
  44. package/dist/providers.d.mts +11 -0
  45. package/dist/providers.mjs +109 -0
  46. package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
  47. package/dist/sanitize-unicode-BZiVbGwK.d.mts +24 -0
  48. package/dist/sanitize-unicode-DT5o51ur.mjs +26 -0
  49. package/dist/src-CZ503MYJ.mjs +99 -0
  50. package/dist/stream-CREqxHgU.mjs +74 -0
  51. package/dist/stream-first-event-timeout-RjWszj8c.mjs +106 -0
  52. package/dist/streaming-byte-guard-BrbkbwUu.mjs +46 -0
  53. package/dist/tool-schema-json-projection-BXtBc_mD.mjs +74 -0
  54. package/dist/transform-messages-BhGF_fF4.mjs +507 -0
  55. package/dist/types-BVVgDSdq.d.mts +1 -0
  56. package/dist/types-DRgdPqaZ.d.mts +587 -0
  57. package/dist/types.d.mts +6 -0
  58. package/dist/types.mjs +5 -0
  59. package/dist/validation-BDMWOr8d.d.mts +9 -0
  60. package/dist/validation-FrchoOlv.mjs +199 -0
  61. package/dist/validation.d.mts +2 -0
  62. package/dist/validation.mjs +2 -0
  63. package/npm-shrinkwrap.json +645 -0
  64. package/package.json +74 -2
@@ -0,0 +1,844 @@
1
+ import { n as getEnvApiKey, r as __exportAll } from "./env-api-keys-CtMlqaQ4.mjs";
2
+ import { t as AssistantMessageEventStream } from "./event-stream-ReMmOTzX.mjs";
3
+ import { n as getAiTransportHost } from "./host-4t713IeR.mjs";
4
+ import { n as calculateCost, r as clampThinkingLevel, t as applyProviderReportedUsageCost } from "./model-utils-DgmOla96.mjs";
5
+ import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
6
+ import { n as parseStreamingJson } from "./json-parse-DzNSIQBq.mjs";
7
+ import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
8
+ import { b as stripSystemPromptCacheBoundary, i as extractToolResultText, n as describeToolResultMediaPlaceholder, o as buildBaseOptions, t as transformMessages, y as splitSystemPromptCacheBoundary } from "./transform-messages-BhGF_fF4.mjs";
9
+ import { a as resolveCacheRetention, i as resolveCloudflareBaseUrl, n as hasCopilotVisionInput, r as isCloudflareProvider, t as buildCopilotDynamicHeaders } from "./github-copilot-headers-BsH5cqGj.mjs";
10
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-axhAdUwg.mjs";
11
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-RjWszj8c.mjs";
12
+ import { a as clampOpenAIPromptCacheKey, n as reconcileOpenAICompletionsToolChoice, t as projectOpenAITools } from "./openai-tool-projection-BknoV11q.mjs";
13
+ import OpenAI from "openai";
14
+ //#region packages/ai/src/providers/openai-stop-reason.ts
15
+ function mapOpenAIStopReason(reason, options) {
16
+ if (reason === null) return { stopReason: "stop" };
17
+ switch (reason) {
18
+ case "stop":
19
+ case "end": return { stopReason: "stop" };
20
+ case "length": return { stopReason: "length" };
21
+ case "function_call":
22
+ case "tool_calls": return { stopReason: "toolUse" };
23
+ case "tool_call":
24
+ if (options?.allowSingularToolCall) return { stopReason: "toolUse" };
25
+ break;
26
+ case "content_filter": return {
27
+ stopReason: "error",
28
+ errorMessage: "Provider finish_reason: content_filter"
29
+ };
30
+ case "network_error": return {
31
+ stopReason: "error",
32
+ errorMessage: "Provider finish_reason: network_error"
33
+ };
34
+ }
35
+ return {
36
+ stopReason: "error",
37
+ errorMessage: `Provider finish_reason: ${reason}`
38
+ };
39
+ }
40
+ //#endregion
41
+ //#region packages/ai/src/providers/openai-completions.ts
42
+ var openai_completions_exports = /* @__PURE__ */ __exportAll({
43
+ convertMessages: () => convertMessages,
44
+ streamOpenAICompletions: () => streamOpenAICompletions,
45
+ streamSimpleOpenAICompletions: () => streamSimpleOpenAICompletions
46
+ });
47
+ /**
48
+ * Check if conversation messages contain tool calls or tool results.
49
+ * This is needed because Anthropic (via proxy) requires the tools param
50
+ * to be present when messages include tool_calls or tool role messages.
51
+ */
52
+ function hasToolHistory(messages) {
53
+ for (const msg of messages) {
54
+ if (msg.role === "toolResult") return true;
55
+ if (msg.role === "assistant") {
56
+ if (Array.isArray(msg.content) && msg.content.some((block) => block.type === "toolCall")) return true;
57
+ }
58
+ }
59
+ return false;
60
+ }
61
+ function isTextContentBlock(block) {
62
+ return block.type === "text";
63
+ }
64
+ function isThinkingContentBlock(block) {
65
+ return block.type === "thinking";
66
+ }
67
+ function isToolCallBlock(block) {
68
+ return block.type === "toolCall";
69
+ }
70
+ function isImageContentBlock(block) {
71
+ return block.type === "image";
72
+ }
73
+ const EMPTY_TOOL_RESULT_TEXT = "(no output)";
74
+ function sanitizeToolResultText(text, fallback) {
75
+ const sanitized = sanitizeSurrogates(text);
76
+ return sanitized.trim().length > 0 ? sanitized : fallback;
77
+ }
78
+ const streamOpenAICompletions = (model, context, options) => {
79
+ const stream = new AssistantMessageEventStream();
80
+ (async () => {
81
+ const output = {
82
+ role: "assistant",
83
+ content: [],
84
+ api: model.api,
85
+ provider: model.provider,
86
+ model: model.id,
87
+ usage: {
88
+ input: 0,
89
+ output: 0,
90
+ cacheRead: 0,
91
+ cacheWrite: 0,
92
+ totalTokens: 0,
93
+ cost: {
94
+ input: 0,
95
+ output: 0,
96
+ cacheRead: 0,
97
+ cacheWrite: 0,
98
+ total: 0
99
+ }
100
+ },
101
+ stopReason: "stop",
102
+ timestamp: Date.now()
103
+ };
104
+ let firstEventAbort;
105
+ try {
106
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
107
+ const compat = getCompat(model);
108
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
109
+ const cacheSessionId = cacheRetention === "none" ? void 0 : options?.sessionId;
110
+ const client = createClient(model, context, apiKey, options?.headers, cacheSessionId, compat);
111
+ let params = buildParams(model, context, options, compat, cacheRetention);
112
+ const nextParams = await options?.onPayload?.(params, model);
113
+ if (nextParams !== void 0) params = nextParams;
114
+ firstEventAbort = createFirstStreamEventAbortController(options?.signal);
115
+ const requestOptions = {
116
+ signal: firstEventAbort.signal,
117
+ ...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
118
+ ...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
119
+ };
120
+ const { data: openaiStream, response } = await client.chat.completions.create(params, requestOptions).withResponse();
121
+ await options?.onResponse?.({
122
+ status: response.status,
123
+ headers: headersToRecord(response.headers)
124
+ }, model);
125
+ stream.push({
126
+ type: "start",
127
+ partial: output
128
+ });
129
+ let textBlock = null;
130
+ let thinkingBlock = null;
131
+ let hasFinishReason = false;
132
+ const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
133
+ const toolCallBlocksById = /* @__PURE__ */ new Map();
134
+ const toolCallBlocksByFirstId = /* @__PURE__ */ new Map();
135
+ const blocks = output.content;
136
+ const finishedBlocks = /* @__PURE__ */ new Set();
137
+ const contentIndices = /* @__PURE__ */ new WeakMap();
138
+ const appendBlock = (block) => {
139
+ contentIndices.set(block, blocks.length);
140
+ blocks.push(block);
141
+ };
142
+ const getContentIndex = (block) => contentIndices.get(block) ?? -1;
143
+ const rememberFirstToolCallById = (id, block) => {
144
+ if (!toolCallBlocksByFirstId.has(id)) toolCallBlocksByFirstId.set(id, block);
145
+ };
146
+ const finishBlock = (block) => {
147
+ const contentIndex = getContentIndex(block);
148
+ if (contentIndex === -1 || finishedBlocks.has(block)) return;
149
+ finishedBlocks.add(block);
150
+ if (block.type === "text") stream.push({
151
+ type: "text_end",
152
+ contentIndex,
153
+ content: block.text,
154
+ partial: output
155
+ });
156
+ else if (block.type === "thinking") stream.push({
157
+ type: "thinking_end",
158
+ contentIndex,
159
+ content: block.thinking,
160
+ partial: output
161
+ });
162
+ else if (block.type === "toolCall") {
163
+ block.arguments = parseStreamingJson(block.partialArgs);
164
+ delete block.partialArgs;
165
+ delete block.streamIndex;
166
+ stream.push({
167
+ type: "toolcall_end",
168
+ contentIndex,
169
+ toolCall: block,
170
+ partial: output
171
+ });
172
+ }
173
+ };
174
+ const ensureTextBlock = () => {
175
+ if (!textBlock) {
176
+ textBlock = {
177
+ type: "text",
178
+ text: ""
179
+ };
180
+ appendBlock(textBlock);
181
+ stream.push({
182
+ type: "text_start",
183
+ contentIndex: getContentIndex(textBlock),
184
+ partial: output
185
+ });
186
+ }
187
+ return textBlock;
188
+ };
189
+ const ensureThinkingBlock = (thinkingSignature) => {
190
+ if (!thinkingBlock) {
191
+ thinkingBlock = {
192
+ type: "thinking",
193
+ thinking: "",
194
+ thinkingSignature
195
+ };
196
+ appendBlock(thinkingBlock);
197
+ stream.push({
198
+ type: "thinking_start",
199
+ contentIndex: getContentIndex(thinkingBlock),
200
+ partial: output
201
+ });
202
+ }
203
+ return thinkingBlock;
204
+ };
205
+ const sealNativeReasoningBeforeText = () => {
206
+ if (thinkingBlock && !reasoningTagTextPartitioner.isInsideReasoning()) {
207
+ finishBlock(thinkingBlock);
208
+ thinkingBlock = null;
209
+ }
210
+ };
211
+ const appendTextDelta = (delta) => {
212
+ sealNativeReasoningBeforeText();
213
+ const block = ensureTextBlock();
214
+ block.text += delta;
215
+ stream.push({
216
+ type: "text_delta",
217
+ contentIndex: getContentIndex(block),
218
+ delta,
219
+ partial: output
220
+ });
221
+ };
222
+ const appendThinkingDelta = (thinkingSignature, delta) => {
223
+ const block = ensureThinkingBlock(thinkingSignature);
224
+ block.thinking += delta;
225
+ stream.push({
226
+ type: "thinking_delta",
227
+ contentIndex: getContentIndex(block),
228
+ delta,
229
+ partial: output
230
+ });
231
+ };
232
+ const ensureToolCallBlock = (toolCall) => {
233
+ const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
234
+ let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
235
+ if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
236
+ if (!block) {
237
+ block = {
238
+ type: "toolCall",
239
+ id: toolCall.id || "",
240
+ name: toolCall.function?.name || "",
241
+ arguments: {},
242
+ partialArgs: "",
243
+ streamIndex
244
+ };
245
+ if (streamIndex !== void 0) toolCallBlocksByIndex.set(streamIndex, block);
246
+ if (toolCall.id) {
247
+ toolCallBlocksById.set(toolCall.id, block);
248
+ rememberFirstToolCallById(toolCall.id, block);
249
+ }
250
+ appendBlock(block);
251
+ stream.push({
252
+ type: "toolcall_start",
253
+ contentIndex: getContentIndex(block),
254
+ partial: output
255
+ });
256
+ }
257
+ if (streamIndex !== void 0 && block.streamIndex === void 0) {
258
+ block.streamIndex = streamIndex;
259
+ toolCallBlocksByIndex.set(streamIndex, block);
260
+ }
261
+ if (toolCall.id) toolCallBlocksById.set(toolCall.id, block);
262
+ return block;
263
+ };
264
+ const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
265
+ const appendPartitionedContent = (text, hasMirroredReasoning) => {
266
+ const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(text) : reasoningTagTextPartitioner.pushVisible(text);
267
+ for (const delta of routedDeltas) if (delta.kind === "text") appendTextDelta(delta.text);
268
+ };
269
+ const flushPartitionedContent = () => {
270
+ for (const delta of reasoningTagTextPartitioner.flush()) if (delta.kind === "text") appendTextDelta(delta.text);
271
+ };
272
+ const guardedOpenaiStream = withFirstStreamEventTimeout(openaiStream, {
273
+ provider: model.provider,
274
+ api: model.api,
275
+ model: model.id,
276
+ timeoutMs: getFirstStreamEventTimeoutMs(options) ?? 0,
277
+ stage: "completions",
278
+ abort: firstEventAbort.abort,
279
+ onTimeout: getFirstStreamEventTimeoutHandler(options),
280
+ 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."
281
+ });
282
+ for await (const chunk of guardedOpenaiStream) {
283
+ if (!chunk || typeof chunk !== "object") continue;
284
+ output.responseId ||= chunk.id;
285
+ if (typeof chunk.model === "string" && chunk.model.length > 0 && chunk.model !== model.id) output.responseModel ||= chunk.model;
286
+ if (chunk.usage) output.usage = parseChunkUsage(chunk.usage, model);
287
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
288
+ if (!choice) continue;
289
+ const choiceUsage = choice.usage;
290
+ if (!chunk.usage && choiceUsage) output.usage = parseChunkUsage(choiceUsage, model);
291
+ if (choice.finish_reason) {
292
+ const finishReasonResult = mapOpenAIStopReason(choice.finish_reason);
293
+ output.stopReason = finishReasonResult.stopReason;
294
+ if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
295
+ hasFinishReason = true;
296
+ }
297
+ const choiceDelta = choice.delta ?? choice.message;
298
+ if (choiceDelta) {
299
+ const reasoningFields = [
300
+ "reasoning_content",
301
+ "reasoning",
302
+ "reasoning_text"
303
+ ];
304
+ const deltaFields = choiceDelta;
305
+ const shouldEmitReasoning = Boolean(model.reasoning && options?.reasoningEffort);
306
+ let foundReasoningField = null;
307
+ for (const field of reasoningFields) {
308
+ const value = deltaFields[field];
309
+ if (typeof value === "string" && value.length > 0) {
310
+ foundReasoningField = field;
311
+ break;
312
+ }
313
+ }
314
+ if (foundReasoningField) reasoningTagTextPartitioner.markStrict();
315
+ if (shouldEmitReasoning && foundReasoningField) {
316
+ const delta = deltaFields[foundReasoningField];
317
+ if (typeof delta === "string" && delta.length > 0) appendThinkingDelta(model.provider === "opencode-go" && foundReasoningField === "reasoning" ? "reasoning_content" : foundReasoningField, delta);
318
+ }
319
+ if (choiceDelta.content !== null && choiceDelta.content !== void 0 && choiceDelta.content.length > 0) appendPartitionedContent(choiceDelta.content, Boolean(foundReasoningField));
320
+ const refusalText = typeof choiceDelta.refusal === "string" ? choiceDelta.refusal : "";
321
+ if (refusalText.length > 0) appendPartitionedContent(refusalText, Boolean(foundReasoningField));
322
+ if (choiceDelta.tool_calls) {
323
+ flushPartitionedContent();
324
+ sealNativeReasoningBeforeText();
325
+ for (const toolCall of choiceDelta.tool_calls) {
326
+ const block = ensureToolCallBlock(toolCall);
327
+ if (!block.id && toolCall.id) {
328
+ block.id = toolCall.id;
329
+ toolCallBlocksById.set(toolCall.id, block);
330
+ rememberFirstToolCallById(toolCall.id, block);
331
+ }
332
+ if (!block.name && toolCall.function?.name) block.name = toolCall.function.name;
333
+ let delta = "";
334
+ if (toolCall.function?.arguments) {
335
+ delta = toolCall.function.arguments;
336
+ block.partialArgs = (block.partialArgs ?? "") + toolCall.function.arguments;
337
+ block.arguments = parseStreamingJson(block.partialArgs);
338
+ }
339
+ stream.push({
340
+ type: "toolcall_delta",
341
+ contentIndex: getContentIndex(block),
342
+ delta,
343
+ partial: output
344
+ });
345
+ }
346
+ }
347
+ const reasoningDetails = choiceDelta.reasoning_details;
348
+ if (reasoningDetails && Array.isArray(reasoningDetails)) {
349
+ for (const detail of reasoningDetails) if (detail.type === "reasoning.encrypted" && detail.id && detail.data) {
350
+ const matchingToolCall = toolCallBlocksByFirstId.get(detail.id);
351
+ if (matchingToolCall) matchingToolCall.thoughtSignature = JSON.stringify(detail);
352
+ }
353
+ }
354
+ }
355
+ }
356
+ flushPartitionedContent();
357
+ for (const block of blocks) finishBlock(block);
358
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
359
+ if (output.stopReason === "aborted") throw new Error("Request was aborted");
360
+ if (output.stopReason === "error") throw new Error(output.errorMessage || "Provider returned an error stop reason");
361
+ if (!hasFinishReason) throw new Error("Stream ended without finish_reason");
362
+ const hasToolCalls = output.content.some((block) => block.type === "toolCall");
363
+ const hasVisibleText = output.content.some((block) => block.type === "text" && block.text.trim().length > 0);
364
+ if (output.stopReason === "toolUse" && !hasToolCalls) output.stopReason = "stop";
365
+ if (output.stopReason === "stop" && hasToolCalls && !hasVisibleText) output.stopReason = "toolUse";
366
+ if (hasToolCalls && output.stopReason !== "toolUse") output.content = output.content.filter((block) => block.type !== "toolCall");
367
+ stream.push({
368
+ type: "done",
369
+ reason: output.stopReason,
370
+ message: output
371
+ });
372
+ stream.end();
373
+ } catch (error) {
374
+ for (const block of output.content) {
375
+ delete block.index;
376
+ delete block.partialArgs;
377
+ delete block.streamIndex;
378
+ }
379
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
380
+ output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
381
+ const rawMetadata = error?.error?.metadata?.raw;
382
+ if (rawMetadata) output.errorMessage += `\n${rawMetadata}`;
383
+ stream.push({
384
+ type: "error",
385
+ reason: output.stopReason,
386
+ error: output
387
+ });
388
+ stream.end();
389
+ } finally {
390
+ firstEventAbort?.dispose();
391
+ }
392
+ })();
393
+ return stream;
394
+ };
395
+ const streamSimpleOpenAICompletions = (model, context, options) => {
396
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider);
397
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
398
+ const base = buildBaseOptions(model, options, apiKey);
399
+ const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : void 0;
400
+ const reasoningEffort = clampedReasoning === "off" ? void 0 : clampedReasoning === "max" ? "xhigh" : clampedReasoning;
401
+ const toolChoice = options?.toolChoice;
402
+ return streamOpenAICompletions(model, context, {
403
+ ...base,
404
+ reasoningEffort,
405
+ toolChoice
406
+ });
407
+ };
408
+ function createClient(model, context, apiKey, optionsHeaders, sessionId, compat = getCompat(model)) {
409
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
410
+ const headers = { ...model.headers };
411
+ if (model.provider === "github-copilot") {
412
+ const hasImages = hasCopilotVisionInput(context.messages);
413
+ const copilotHeaders = buildCopilotDynamicHeaders({
414
+ messages: context.messages,
415
+ hasImages
416
+ });
417
+ Object.assign(headers, copilotHeaders);
418
+ }
419
+ if (sessionId && compat.sendSessionAffinityHeaders) {
420
+ headers.session_id = sessionId;
421
+ headers["x-client-request-id"] = sessionId;
422
+ headers["x-session-affinity"] = sessionId;
423
+ }
424
+ if (optionsHeaders) Object.assign(headers, optionsHeaders);
425
+ const defaultHeaders = model.provider === "cloudflare-ai-gateway" ? {
426
+ ...headers,
427
+ Authorization: headers.Authorization ?? null,
428
+ "cf-aig-authorization": `Bearer ${apiKey}`
429
+ } : headers;
430
+ return new OpenAI({
431
+ apiKey,
432
+ baseURL: isCloudflareProvider(model.provider) ? resolveCloudflareBaseUrl(model) : model.baseUrl,
433
+ dangerouslyAllowBrowser: true,
434
+ defaultHeaders,
435
+ fetch: getAiTransportHost().buildModelFetch(model)
436
+ });
437
+ }
438
+ function buildParams(model, context, options, compat = getCompat(model), cacheRetention = resolveCacheRetention(options?.cacheRetention)) {
439
+ const cacheControl = getCompatCacheControl(compat, cacheRetention);
440
+ const cacheOptOutIndexes = /* @__PURE__ */ new Set();
441
+ const messages = convertMessages(model, context, compat, {
442
+ cacheOptOutIndexes,
443
+ preserveSystemPromptCacheBoundary: cacheControl !== void 0
444
+ });
445
+ const supportsPromptCacheKey = model.baseUrl.includes("api.openai.com") || compat.supportsPromptCacheKey;
446
+ const promptCacheKey = supportsPromptCacheKey && cacheRetention !== "none" ? clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId) : void 0;
447
+ const params = {
448
+ model: model.id,
449
+ messages,
450
+ stream: true,
451
+ prompt_cache_key: promptCacheKey,
452
+ prompt_cache_retention: supportsPromptCacheKey && cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : void 0
453
+ };
454
+ if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
455
+ if (compat.supportsStore) params.store = false;
456
+ if (options?.maxTokens) {
457
+ const maxTokens = clampOpenAICompletionsMaxTokens(model, options.maxTokens);
458
+ if (compat.maxTokensField === "max_tokens") params.max_tokens = maxTokens;
459
+ else params.max_completion_tokens = maxTokens;
460
+ }
461
+ if (options?.temperature !== void 0) params.temperature = options.temperature;
462
+ if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
463
+ let toolProjection;
464
+ if (context.tools) {
465
+ const converted = convertTools(context.tools, compat);
466
+ toolProjection = converted.projection;
467
+ if (converted.tools.length > 0) params.tools = converted.tools;
468
+ else if (hasToolHistory(context.messages)) params.tools = [];
469
+ if (compat.zaiToolStream && converted.tools.length > 0) params.tool_stream = true;
470
+ } else if (hasToolHistory(context.messages)) params.tools = [];
471
+ if (cacheControl) applyAnthropicCacheControl(messages, params.tools, cacheControl, cacheOptOutIndexes);
472
+ if (options?.toolChoice) {
473
+ const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, toolProjection ?? projectOpenAITools([]));
474
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
475
+ }
476
+ if (compat.thinkingFormat === "zai" && model.reasoning) params.enable_thinking = Boolean(options?.reasoningEffort);
477
+ else if (compat.thinkingFormat === "qwen" && model.reasoning) params.enable_thinking = Boolean(options?.reasoningEffort);
478
+ else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = {
479
+ enable_thinking: Boolean(options?.reasoningEffort),
480
+ preserve_thinking: true
481
+ };
482
+ else if (compat.thinkingFormat === "deepseek" && model.reasoning) {
483
+ params.thinking = { type: options?.reasoningEffort ? "enabled" : "disabled" };
484
+ if (options?.reasoningEffort && compat.supportsReasoningEffort) params.reasoning_effort = model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
485
+ } else if (compat.thinkingFormat === "openrouter" && model.reasoning) {
486
+ const openRouterParams = params;
487
+ if (options?.reasoningEffort) openRouterParams.reasoning = { effort: model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort };
488
+ else if (model.thinkingLevelMap?.off !== null) openRouterParams.reasoning = { effort: model.thinkingLevelMap?.off ?? "none" };
489
+ } else if (compat.thinkingFormat === "together" && model.reasoning) {
490
+ const togetherParams = params;
491
+ togetherParams.reasoning = { enabled: Boolean(options?.reasoningEffort) };
492
+ if (options?.reasoningEffort && compat.supportsReasoningEffort) togetherParams.reasoning_effort = model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
493
+ } else if (options?.reasoningEffort && model.reasoning && compat.supportsReasoningEffort) params.reasoning_effort = model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort;
494
+ else if (!options?.reasoningEffort && model.reasoning && compat.supportsReasoningEffort) {
495
+ const offValue = model.thinkingLevelMap?.off;
496
+ if (typeof offValue === "string") params.reasoning_effort = offValue;
497
+ }
498
+ if (model.baseUrl.includes("openrouter.ai") && model.compat?.openRouterRouting) params.provider = model.compat.openRouterRouting;
499
+ if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
500
+ const routing = model.compat.vercelGatewayRouting;
501
+ if (routing.only || routing.order) {
502
+ const gatewayOptions = {};
503
+ if (routing.only) gatewayOptions.only = routing.only;
504
+ if (routing.order) gatewayOptions.order = routing.order;
505
+ params.providerOptions = { gateway: gatewayOptions };
506
+ }
507
+ }
508
+ return params;
509
+ }
510
+ function clampOpenAICompletionsMaxTokens(model, requestedMaxTokens) {
511
+ const modelMaxTokens = typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
512
+ return modelMaxTokens === void 0 || requestedMaxTokens <= modelMaxTokens ? requestedMaxTokens : modelMaxTokens;
513
+ }
514
+ function getCompatCacheControl(compat, cacheRetention) {
515
+ if (compat.cacheControlFormat !== "anthropic" || cacheRetention === "none") return;
516
+ const ttl = cacheRetention === "long" && compat.supportsLongCacheRetention ? "1h" : void 0;
517
+ return {
518
+ type: "ephemeral",
519
+ ...ttl ? { ttl } : {}
520
+ };
521
+ }
522
+ function applyAnthropicCacheControl(messages, tools, cacheControl, cacheOptOutIndexes) {
523
+ addCacheControlToSystemPrompt(messages, cacheControl);
524
+ addCacheControlToLastTool(tools, cacheControl);
525
+ addCacheControlToLastConversationMessage(messages, cacheControl, cacheOptOutIndexes);
526
+ }
527
+ function addCacheControlToSystemPrompt(messages, cacheControl) {
528
+ for (const message of messages) if (message.role === "system" || message.role === "developer") {
529
+ addCacheControlToInstructionMessage(message, cacheControl);
530
+ return;
531
+ }
532
+ }
533
+ function addCacheControlToLastConversationMessage(messages, cacheControl, cacheOptOutIndexes) {
534
+ for (let i = messages.length - 1; i >= 0; i--) {
535
+ const message = messages[i];
536
+ if (cacheOptOutIndexes.has(i)) continue;
537
+ if (message.role === "user" || message.role === "assistant") {
538
+ if (addCacheControlToMessage(message, cacheControl)) return;
539
+ }
540
+ }
541
+ }
542
+ function addCacheControlToLastTool(tools, cacheControl) {
543
+ if (!tools || tools.length === 0) return;
544
+ const lastTool = tools[tools.length - 1];
545
+ lastTool.cache_control = cacheControl;
546
+ }
547
+ function addCacheControlToInstructionMessage(message, cacheControl) {
548
+ return addCacheControlToTextContent(message, cacheControl);
549
+ }
550
+ function addCacheControlToMessage(message, cacheControl) {
551
+ if (message.role === "user" || message.role === "assistant") return addCacheControlToTextContent(message, cacheControl);
552
+ return false;
553
+ }
554
+ function addCacheControlToTextContent(message, cacheControl) {
555
+ const content = message.content;
556
+ if (typeof content === "string") {
557
+ if (content.length === 0) return false;
558
+ message.content = buildCacheControlledTextParts(content, cacheControl);
559
+ return true;
560
+ }
561
+ if (!Array.isArray(content)) return false;
562
+ for (let i = content.length - 1; i >= 0; i--) {
563
+ const part = content[i];
564
+ if (part?.type === "text") {
565
+ const text = part.text;
566
+ content.splice(i, 1, ...buildCacheControlledTextParts(text, cacheControl));
567
+ return true;
568
+ }
569
+ }
570
+ return false;
571
+ }
572
+ function buildCacheControlledTextParts(text, cacheControl) {
573
+ const split = splitSystemPromptCacheBoundary(text);
574
+ if (!split) return [{
575
+ type: "text",
576
+ text,
577
+ cache_control: cacheControl
578
+ }];
579
+ const parts = [];
580
+ if (split.stablePrefix) parts.push({
581
+ type: "text",
582
+ text: split.stablePrefix,
583
+ cache_control: cacheControl
584
+ });
585
+ if (split.dynamicSuffix) parts.push({
586
+ type: "text",
587
+ text: split.dynamicSuffix
588
+ });
589
+ return parts.length > 0 ? parts : [{
590
+ type: "text",
591
+ text: ""
592
+ }];
593
+ }
594
+ function convertMessages(model, context, compat, options = {}) {
595
+ const params = [];
596
+ const normalizeToolCallId = (id) => {
597
+ if (id.includes("|")) {
598
+ const [callId] = id.split("|");
599
+ return callId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
600
+ }
601
+ if (model.provider === "openai") return id.length > 40 ? id.slice(0, 40) : id;
602
+ return id;
603
+ };
604
+ const transformedMessages = transformMessages(context.messages, model, (id) => normalizeToolCallId(id));
605
+ if (context.systemPrompt) {
606
+ const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
607
+ const systemPrompt = options.preserveSystemPromptCacheBoundary ? context.systemPrompt : stripSystemPromptCacheBoundary(context.systemPrompt);
608
+ params.push({
609
+ role,
610
+ content: sanitizeSurrogates(systemPrompt)
611
+ });
612
+ }
613
+ let lastRole = null;
614
+ for (let i = 0; i < transformedMessages.length; i++) {
615
+ const msg = transformedMessages[i];
616
+ if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && msg.role === "user") params.push({
617
+ role: "assistant",
618
+ content: "I have processed the tool results."
619
+ });
620
+ if (msg.role === "user") {
621
+ const isRuntimeContextCarrier = msg.runtimeContextCarrier === true;
622
+ if (typeof msg.content === "string") {
623
+ const userParam = {
624
+ role: "user",
625
+ content: sanitizeSurrogates(msg.content)
626
+ };
627
+ if (isRuntimeContextCarrier) options.cacheOptOutIndexes?.add(params.length);
628
+ params.push(userParam);
629
+ } else {
630
+ const content = msg.content.map((item) => {
631
+ if (item.type === "text") return {
632
+ type: "text",
633
+ text: sanitizeSurrogates(item.text)
634
+ };
635
+ return {
636
+ type: "image_url",
637
+ image_url: { url: `data:${item.mimeType};base64,${item.data}` }
638
+ };
639
+ });
640
+ if (content.length === 0) continue;
641
+ const userParam = {
642
+ role: "user",
643
+ content
644
+ };
645
+ if (isRuntimeContextCarrier) options.cacheOptOutIndexes?.add(params.length);
646
+ params.push(userParam);
647
+ }
648
+ } else if (msg.role === "assistant") {
649
+ const assistantMsg = {
650
+ role: "assistant",
651
+ content: compat.requiresAssistantAfterToolResult ? "" : null
652
+ };
653
+ const assistantTextParts = msg.content.filter(isTextContentBlock).filter((block) => block.text.trim().length > 0).map((block) => ({
654
+ type: "text",
655
+ text: sanitizeSurrogates(block.text)
656
+ }));
657
+ const assistantText = assistantTextParts.map((part) => part.text).join("");
658
+ const nonEmptyThinkingBlocks = msg.content.filter(isThinkingContentBlock).filter((block) => block.thinking.trim().length > 0);
659
+ if (nonEmptyThinkingBlocks.length > 0) if (compat.requiresThinkingAsText) assistantMsg.content = [{
660
+ type: "text",
661
+ text: nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n\n")
662
+ }, ...assistantTextParts];
663
+ else {
664
+ if (assistantText.length > 0) assistantMsg.content = assistantText;
665
+ let signature = nonEmptyThinkingBlocks[0].thinkingSignature;
666
+ if (model.provider === "opencode-go" && signature === "reasoning") signature = "reasoning_content";
667
+ if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => block.thinking).join("\n");
668
+ }
669
+ else if (assistantText.length > 0) assistantMsg.content = assistantText;
670
+ const toolCalls = msg.content.filter(isToolCallBlock);
671
+ if (toolCalls.length > 0) {
672
+ assistantMsg.tool_calls = toolCalls.map((tc) => ({
673
+ id: tc.id,
674
+ type: "function",
675
+ function: {
676
+ name: tc.name,
677
+ arguments: JSON.stringify(tc.arguments)
678
+ }
679
+ }));
680
+ const reasoningDetails = toolCalls.filter((tc) => tc.thoughtSignature).map((tc) => {
681
+ try {
682
+ return JSON.parse(tc.thoughtSignature);
683
+ } catch {
684
+ return null;
685
+ }
686
+ }).filter(Boolean);
687
+ if (reasoningDetails.length > 0) assistantMsg.reasoning_details = reasoningDetails;
688
+ }
689
+ if (compat.requiresReasoningContentOnAssistantMessages && model.reasoning && assistantMsg.reasoning_content === void 0) assistantMsg.reasoning_content = "";
690
+ const content = assistantMsg.content;
691
+ if (!(content !== null && content !== void 0 && (typeof content === "string" ? content.length > 0 : content.length > 0)) && !assistantMsg.tool_calls) continue;
692
+ params.push(assistantMsg);
693
+ } else if (msg.role === "toolResult") {
694
+ const imageBlocks = [];
695
+ let j = i;
696
+ for (; j < transformedMessages.length && transformedMessages[j].role === "toolResult"; j++) {
697
+ const toolMsg = transformedMessages[j];
698
+ const textResult = extractToolResultText(toolMsg.content);
699
+ const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
700
+ const hasImages = toolMsg.content.some((c) => c.type === "image");
701
+ const toolResultMsg = {
702
+ role: "tool",
703
+ content: sanitizeToolResultText(textResult, mediaPlaceholder ?? EMPTY_TOOL_RESULT_TEXT),
704
+ tool_call_id: toolMsg.toolCallId
705
+ };
706
+ if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
707
+ params.push(toolResultMsg);
708
+ if (hasImages && model.input.includes("image")) {
709
+ for (const block of toolMsg.content) if (isImageContentBlock(block)) imageBlocks.push({
710
+ type: "image_url",
711
+ image_url: { url: `data:${block.mimeType};base64,${block.data}` }
712
+ });
713
+ }
714
+ }
715
+ i = j - 1;
716
+ if (imageBlocks.length > 0) {
717
+ if (compat.requiresAssistantAfterToolResult) params.push({
718
+ role: "assistant",
719
+ content: "I have processed the tool results."
720
+ });
721
+ params.push({
722
+ role: "user",
723
+ content: [{
724
+ type: "text",
725
+ text: "Attached image(s) from tool result:"
726
+ }, ...imageBlocks]
727
+ });
728
+ lastRole = "user";
729
+ } else lastRole = "toolResult";
730
+ continue;
731
+ }
732
+ lastRole = msg.role;
733
+ }
734
+ return params;
735
+ }
736
+ function convertTools(tools, compat) {
737
+ const projection = projectOpenAITools(tools);
738
+ return {
739
+ projection,
740
+ tools: projection.tools.map((tool) => ({
741
+ type: "function",
742
+ function: {
743
+ name: tool.name,
744
+ description: tool.description,
745
+ parameters: tool.parameters,
746
+ ...compat.supportsStrictMode && { strict: false }
747
+ }
748
+ }))
749
+ };
750
+ }
751
+ function parseChunkUsage(rawUsage, model) {
752
+ const promptTokens = rawUsage.prompt_tokens || 0;
753
+ const cacheReadTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? rawUsage.prompt_cache_hit_tokens ?? 0;
754
+ const cacheWriteTokens = rawUsage.prompt_tokens_details?.cache_write_tokens || 0;
755
+ const input = Math.max(0, promptTokens - cacheReadTokens - cacheWriteTokens);
756
+ const outputTokens = rawUsage.completion_tokens || 0;
757
+ const usage = {
758
+ input,
759
+ output: outputTokens,
760
+ cacheRead: cacheReadTokens,
761
+ cacheWrite: cacheWriteTokens,
762
+ totalTokens: input + outputTokens + cacheReadTokens + cacheWriteTokens,
763
+ cost: {
764
+ input: 0,
765
+ output: 0,
766
+ cacheRead: 0,
767
+ cacheWrite: 0,
768
+ total: 0
769
+ }
770
+ };
771
+ calculateCost(model, usage);
772
+ applyProviderReportedUsageCost(usage, rawUsage.cost);
773
+ return usage;
774
+ }
775
+ /**
776
+ * Detect compatibility settings from provider and baseUrl for known providers.
777
+ * Provider takes precedence over URL-based detection since it's explicitly configured.
778
+ * Returns a fully resolved OpenAICompletionsCompat object with all fields set.
779
+ */
780
+ function detectCompat(model) {
781
+ const provider = model.provider;
782
+ const baseUrl = model.baseUrl;
783
+ const isZai = provider === "zai" || baseUrl.includes("api.z.ai");
784
+ const isTogether = provider === "together" || baseUrl.includes("api.together.ai") || baseUrl.includes("api.together.xyz");
785
+ const isMoonshot = provider === "moonshotai" || provider === "moonshotai-cn" || baseUrl.includes("api.moonshot.");
786
+ const isCloudflareWorkersAI = provider === "cloudflare-workers-ai" || baseUrl.includes("api.cloudflare.com");
787
+ const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com");
788
+ const isNonStandard = provider === "cerebras" || baseUrl.includes("cerebras.ai") || provider === "xai" || baseUrl.includes("api.x.ai") || isTogether || baseUrl.includes("chutes.ai") || baseUrl.includes("deepseek.com") || isZai || isMoonshot || provider === "opencode" || baseUrl.includes("opencode.ai") || isCloudflareWorkersAI || isCloudflareAiGateway;
789
+ const useMaxTokens = baseUrl.includes("chutes.ai") || isMoonshot || isCloudflareAiGateway || isTogether;
790
+ const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
791
+ const isDeepSeek = provider === "deepseek" || baseUrl.includes("deepseek.com");
792
+ const isXiaomi = provider === "xiaomi" || baseUrl.includes("xiaomimimo.com");
793
+ const cacheControlFormat = provider === "openrouter" && model.id.startsWith("anthropic/") ? "anthropic" : void 0;
794
+ return {
795
+ supportsStore: !isNonStandard,
796
+ supportsDeveloperRole: !isNonStandard,
797
+ supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway,
798
+ supportsUsageInStreaming: true,
799
+ maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
800
+ requiresToolResultName: false,
801
+ requiresAssistantAfterToolResult: false,
802
+ requiresThinkingAsText: false,
803
+ requiresReasoningContentOnAssistantMessages: isDeepSeek || isXiaomi,
804
+ thinkingFormat: isDeepSeek ? "deepseek" : isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : provider === "openrouter" || baseUrl.includes("openrouter.ai") ? "openrouter" : "openai",
805
+ openRouterRouting: {},
806
+ vercelGatewayRouting: {},
807
+ zaiToolStream: false,
808
+ supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway,
809
+ cacheControlFormat,
810
+ sendSessionAffinityHeaders: false,
811
+ supportsPromptCacheKey: false,
812
+ supportsLongCacheRetention: !(isTogether || isCloudflareWorkersAI || isCloudflareAiGateway)
813
+ };
814
+ }
815
+ /**
816
+ * Get resolved compatibility settings for a model.
817
+ * Uses explicit model.compat if provided, otherwise auto-detects from provider/URL.
818
+ */
819
+ function getCompat(model) {
820
+ const detected = detectCompat(model);
821
+ if (!model.compat) return detected;
822
+ return {
823
+ supportsStore: model.compat.supportsStore ?? detected.supportsStore,
824
+ supportsDeveloperRole: model.compat.supportsDeveloperRole ?? detected.supportsDeveloperRole,
825
+ supportsReasoningEffort: model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort,
826
+ supportsUsageInStreaming: model.compat.supportsUsageInStreaming ?? detected.supportsUsageInStreaming,
827
+ maxTokensField: model.compat.maxTokensField ?? detected.maxTokensField,
828
+ requiresToolResultName: model.compat.requiresToolResultName ?? detected.requiresToolResultName,
829
+ requiresAssistantAfterToolResult: model.compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
830
+ requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
831
+ requiresReasoningContentOnAssistantMessages: model.compat.requiresReasoningContentOnAssistantMessages ?? detected.requiresReasoningContentOnAssistantMessages,
832
+ thinkingFormat: model.compat.thinkingFormat ?? detected.thinkingFormat,
833
+ openRouterRouting: model.compat.openRouterRouting ?? {},
834
+ vercelGatewayRouting: model.compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
835
+ zaiToolStream: model.compat.zaiToolStream ?? detected.zaiToolStream,
836
+ supportsStrictMode: model.compat.supportsStrictMode ?? detected.supportsStrictMode,
837
+ cacheControlFormat: model.compat.cacheControlFormat ?? detected.cacheControlFormat,
838
+ sendSessionAffinityHeaders: model.compat.sendSessionAffinityHeaders ?? detected.sendSessionAffinityHeaders,
839
+ supportsPromptCacheKey: model.compat.supportsPromptCacheKey ?? detected.supportsPromptCacheKey,
840
+ supportsLongCacheRetention: model.compat.supportsLongCacheRetention ?? detected.supportsLongCacheRetention
841
+ };
842
+ }
843
+ //#endregion
844
+ export { mapOpenAIStopReason as a, streamSimpleOpenAICompletions as i, openai_completions_exports as n, streamOpenAICompletions as r, convertMessages as t };