@boxiaolanya2008/pi-ai 0.60.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/README.md +1198 -0
  2. package/bedrock-provider.d.ts +1 -0
  3. package/bedrock-provider.js +1 -0
  4. package/dist/api-registry.d.ts +20 -0
  5. package/dist/api-registry.d.ts.map +1 -0
  6. package/dist/api-registry.js +44 -0
  7. package/dist/api-registry.js.map +1 -0
  8. package/dist/bedrock-provider.d.ts +5 -0
  9. package/dist/bedrock-provider.d.ts.map +1 -0
  10. package/dist/bedrock-provider.js +6 -0
  11. package/dist/bedrock-provider.js.map +1 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +113 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/env-api-keys.d.ts +4 -0
  17. package/dist/env-api-keys.d.ts.map +1 -0
  18. package/dist/env-api-keys.js +85 -0
  19. package/dist/env-api-keys.js.map +1 -0
  20. package/dist/index.d.ts +23 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +21 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/models.d.ts +22 -0
  25. package/dist/models.d.ts.map +1 -0
  26. package/dist/models.generated.d.ts +4 -0
  27. package/dist/models.generated.d.ts.map +1 -0
  28. package/dist/models.generated.js +11 -0
  29. package/dist/models.generated.js.map +1 -0
  30. package/dist/models.js +50 -0
  31. package/dist/models.js.map +1 -0
  32. package/dist/oauth.d.ts +2 -0
  33. package/dist/oauth.d.ts.map +1 -0
  34. package/dist/oauth.js +2 -0
  35. package/dist/oauth.js.map +1 -0
  36. package/dist/providers/amazon-bedrock.d.ts +15 -0
  37. package/dist/providers/amazon-bedrock.d.ts.map +1 -0
  38. package/dist/providers/amazon-bedrock.js +552 -0
  39. package/dist/providers/amazon-bedrock.js.map +1 -0
  40. package/dist/providers/anthropic.d.ts +15 -0
  41. package/dist/providers/anthropic.d.ts.map +1 -0
  42. package/dist/providers/anthropic.js +677 -0
  43. package/dist/providers/anthropic.js.map +1 -0
  44. package/dist/providers/azure-openai-responses.d.ts +12 -0
  45. package/dist/providers/azure-openai-responses.d.ts.map +1 -0
  46. package/dist/providers/azure-openai-responses.js +181 -0
  47. package/dist/providers/azure-openai-responses.js.map +1 -0
  48. package/dist/providers/github-copilot-headers.d.ts +8 -0
  49. package/dist/providers/github-copilot-headers.d.ts.map +1 -0
  50. package/dist/providers/github-copilot-headers.js +26 -0
  51. package/dist/providers/github-copilot-headers.js.map +1 -0
  52. package/dist/providers/google-gemini-cli.d.ts +15 -0
  53. package/dist/providers/google-gemini-cli.d.ts.map +1 -0
  54. package/dist/providers/google-gemini-cli.js +43 -0
  55. package/dist/providers/google-gemini-cli.js.map +1 -0
  56. package/dist/providers/google-shared.d.ts +15 -0
  57. package/dist/providers/google-shared.d.ts.map +1 -0
  58. package/dist/providers/google-shared.js +226 -0
  59. package/dist/providers/google-shared.js.map +1 -0
  60. package/dist/providers/google-vertex.d.ts +15 -0
  61. package/dist/providers/google-vertex.d.ts.map +1 -0
  62. package/dist/providers/google-vertex.js +372 -0
  63. package/dist/providers/google-vertex.js.map +1 -0
  64. package/dist/providers/google.d.ts +13 -0
  65. package/dist/providers/google.d.ts.map +1 -0
  66. package/dist/providers/google.js +351 -0
  67. package/dist/providers/google.js.map +1 -0
  68. package/dist/providers/mistral.d.ts +13 -0
  69. package/dist/providers/mistral.d.ts.map +1 -0
  70. package/dist/providers/mistral.js +489 -0
  71. package/dist/providers/mistral.js.map +1 -0
  72. package/dist/providers/openai-codex-responses.d.ts +9 -0
  73. package/dist/providers/openai-codex-responses.d.ts.map +1 -0
  74. package/dist/providers/openai-codex-responses.js +672 -0
  75. package/dist/providers/openai-codex-responses.js.map +1 -0
  76. package/dist/providers/openai-completions.d.ts +15 -0
  77. package/dist/providers/openai-completions.d.ts.map +1 -0
  78. package/dist/providers/openai-completions.js +651 -0
  79. package/dist/providers/openai-completions.js.map +1 -0
  80. package/dist/providers/openai-responses-shared.d.ts +17 -0
  81. package/dist/providers/openai-responses-shared.d.ts.map +1 -0
  82. package/dist/providers/openai-responses-shared.js +420 -0
  83. package/dist/providers/openai-responses-shared.js.map +1 -0
  84. package/dist/providers/openai-responses.d.ts +10 -0
  85. package/dist/providers/openai-responses.d.ts.map +1 -0
  86. package/dist/providers/openai-responses.js +186 -0
  87. package/dist/providers/openai-responses.js.map +1 -0
  88. package/dist/providers/register-builtins.d.ts +11 -0
  89. package/dist/providers/register-builtins.d.ts.map +1 -0
  90. package/dist/providers/register-builtins.js +138 -0
  91. package/dist/providers/register-builtins.js.map +1 -0
  92. package/dist/providers/simple-options.d.ts +8 -0
  93. package/dist/providers/simple-options.d.ts.map +1 -0
  94. package/dist/providers/simple-options.js +35 -0
  95. package/dist/providers/simple-options.js.map +1 -0
  96. package/dist/providers/transform-messages.d.ts +3 -0
  97. package/dist/providers/transform-messages.d.ts.map +1 -0
  98. package/dist/providers/transform-messages.js +130 -0
  99. package/dist/providers/transform-messages.js.map +1 -0
  100. package/dist/stream.d.ts +8 -0
  101. package/dist/stream.d.ts.map +1 -0
  102. package/dist/stream.js +27 -0
  103. package/dist/stream.js.map +1 -0
  104. package/dist/types.d.ts +213 -0
  105. package/dist/types.d.ts.map +1 -0
  106. package/dist/types.js +2 -0
  107. package/dist/types.js.map +1 -0
  108. package/dist/utils/event-stream.d.ts +20 -0
  109. package/dist/utils/event-stream.d.ts.map +1 -0
  110. package/dist/utils/event-stream.js +77 -0
  111. package/dist/utils/event-stream.js.map +1 -0
  112. package/dist/utils/hash.d.ts +2 -0
  113. package/dist/utils/hash.d.ts.map +1 -0
  114. package/dist/utils/hash.js +13 -0
  115. package/dist/utils/hash.js.map +1 -0
  116. package/dist/utils/json-parse.d.ts +2 -0
  117. package/dist/utils/json-parse.d.ts.map +1 -0
  118. package/dist/utils/json-parse.js +19 -0
  119. package/dist/utils/json-parse.js.map +1 -0
  120. package/dist/utils/oauth/anthropic.d.ts +4 -0
  121. package/dist/utils/oauth/anthropic.d.ts.map +1 -0
  122. package/dist/utils/oauth/anthropic.js +61 -0
  123. package/dist/utils/oauth/anthropic.js.map +1 -0
  124. package/dist/utils/oauth/github-copilot.d.ts +16 -0
  125. package/dist/utils/oauth/github-copilot.d.ts.map +1 -0
  126. package/dist/utils/oauth/github-copilot.js +247 -0
  127. package/dist/utils/oauth/github-copilot.js.map +1 -0
  128. package/dist/utils/oauth/google-antigravity.d.ts +7 -0
  129. package/dist/utils/oauth/google-antigravity.d.ts.map +1 -0
  130. package/dist/utils/oauth/google-antigravity.js +55 -0
  131. package/dist/utils/oauth/google-antigravity.js.map +1 -0
  132. package/dist/utils/oauth/index.d.ts +18 -0
  133. package/dist/utils/oauth/index.d.ts.map +1 -0
  134. package/dist/utils/oauth/index.js +72 -0
  135. package/dist/utils/oauth/index.js.map +1 -0
  136. package/dist/utils/oauth/openai-codex.d.ts +14 -0
  137. package/dist/utils/oauth/openai-codex.d.ts.map +1 -0
  138. package/dist/utils/oauth/openai-codex.js +348 -0
  139. package/dist/utils/oauth/openai-codex.js.map +1 -0
  140. package/dist/utils/oauth/pkce.d.ts +5 -0
  141. package/dist/utils/oauth/pkce.d.ts.map +1 -0
  142. package/dist/utils/oauth/pkce.js +18 -0
  143. package/dist/utils/oauth/pkce.js.map +1 -0
  144. package/dist/utils/oauth/types.d.ts +40 -0
  145. package/dist/utils/oauth/types.d.ts.map +1 -0
  146. package/dist/utils/oauth/types.js +2 -0
  147. package/dist/utils/oauth/types.js.map +1 -0
  148. package/dist/utils/overflow.d.ts +4 -0
  149. package/dist/utils/overflow.d.ts.map +1 -0
  150. package/dist/utils/overflow.js +40 -0
  151. package/dist/utils/overflow.js.map +1 -0
  152. package/dist/utils/sanitize-unicode.d.ts +2 -0
  153. package/dist/utils/sanitize-unicode.d.ts.map +1 -0
  154. package/dist/utils/sanitize-unicode.js +4 -0
  155. package/dist/utils/sanitize-unicode.js.map +1 -0
  156. package/dist/utils/typebox-helpers.d.ts +6 -0
  157. package/dist/utils/typebox-helpers.d.ts.map +1 -0
  158. package/dist/utils/typebox-helpers.js +10 -0
  159. package/dist/utils/typebox-helpers.js.map +1 -0
  160. package/dist/utils/validation.d.ts +4 -0
  161. package/dist/utils/validation.d.ts.map +1 -0
  162. package/dist/utils/validation.js +45 -0
  163. package/dist/utils/validation.js.map +1 -0
  164. package/package.json +80 -0
@@ -0,0 +1,651 @@
1
+ import OpenAI from "openai";
2
+ import { getEnvApiKey } from "../env-api-keys.js";
3
+ import { calculateCost, supportsXhigh } from "../models.js";
4
+ import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
+ import { parseStreamingJson } from "../utils/json-parse.js";
6
+ import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
7
+ import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
8
+ import { buildBaseOptions, clampReasoning } from "./simple-options.js";
9
+ import { transformMessages } from "./transform-messages.js";
10
+ function hasToolHistory(messages) {
11
+ for (const msg of messages) {
12
+ if (msg.role === "toolResult") {
13
+ return true;
14
+ }
15
+ if (msg.role === "assistant") {
16
+ if (msg.content.some((block) => block.type === "toolCall")) {
17
+ return true;
18
+ }
19
+ }
20
+ }
21
+ return false;
22
+ }
23
+ export const streamOpenAICompletions = (model, context, options) => {
24
+ const stream = new AssistantMessageEventStream();
25
+ (async () => {
26
+ const output = {
27
+ role: "assistant",
28
+ content: [],
29
+ api: model.api,
30
+ provider: model.provider,
31
+ model: model.id,
32
+ usage: {
33
+ input: 0,
34
+ output: 0,
35
+ cacheRead: 0,
36
+ cacheWrite: 0,
37
+ totalTokens: 0,
38
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
39
+ },
40
+ stopReason: "stop",
41
+ timestamp: Date.now(),
42
+ };
43
+ try {
44
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
45
+ const client = createClient(model, context, apiKey, options?.headers);
46
+ let params = buildParams(model, context, options);
47
+ const nextParams = await options?.onPayload?.(params, model);
48
+ if (nextParams !== undefined) {
49
+ params = nextParams;
50
+ }
51
+ const openaiStream = await client.chat.completions.create(params, { signal: options?.signal });
52
+ stream.push({ type: "start", partial: output });
53
+ let currentBlock = null;
54
+ const blocks = output.content;
55
+ const blockIndex = () => blocks.length - 1;
56
+ const finishCurrentBlock = (block) => {
57
+ if (block) {
58
+ if (block.type === "text") {
59
+ stream.push({
60
+ type: "text_end",
61
+ contentIndex: blockIndex(),
62
+ content: block.text,
63
+ partial: output,
64
+ });
65
+ }
66
+ else if (block.type === "thinking") {
67
+ stream.push({
68
+ type: "thinking_end",
69
+ contentIndex: blockIndex(),
70
+ content: block.thinking,
71
+ partial: output,
72
+ });
73
+ }
74
+ else if (block.type === "toolCall") {
75
+ block.arguments = parseStreamingJson(block.partialArgs);
76
+ delete block.partialArgs;
77
+ stream.push({
78
+ type: "toolcall_end",
79
+ contentIndex: blockIndex(),
80
+ toolCall: block,
81
+ partial: output,
82
+ });
83
+ }
84
+ }
85
+ };
86
+ for await (const chunk of openaiStream) {
87
+ if (chunk.usage) {
88
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens || 0;
89
+ const reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens || 0;
90
+ const input = (chunk.usage.prompt_tokens || 0) - cachedTokens;
91
+ const outputTokens = (chunk.usage.completion_tokens || 0) + reasoningTokens;
92
+ output.usage = {
93
+ input,
94
+ output: outputTokens,
95
+ cacheRead: cachedTokens,
96
+ cacheWrite: 0,
97
+ totalTokens: input + outputTokens + cachedTokens,
98
+ cost: {
99
+ input: 0,
100
+ output: 0,
101
+ cacheRead: 0,
102
+ cacheWrite: 0,
103
+ total: 0,
104
+ },
105
+ };
106
+ calculateCost(model, output.usage);
107
+ }
108
+ const choice = chunk.choices?.[0];
109
+ if (!choice)
110
+ continue;
111
+ if (choice.finish_reason) {
112
+ output.stopReason = mapStopReason(choice.finish_reason);
113
+ }
114
+ if (choice.delta) {
115
+ if (choice.delta.content !== null &&
116
+ choice.delta.content !== undefined &&
117
+ choice.delta.content.length > 0) {
118
+ if (!currentBlock || currentBlock.type !== "text") {
119
+ finishCurrentBlock(currentBlock);
120
+ currentBlock = { type: "text", text: "" };
121
+ output.content.push(currentBlock);
122
+ stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
123
+ }
124
+ if (currentBlock.type === "text") {
125
+ currentBlock.text += choice.delta.content;
126
+ stream.push({
127
+ type: "text_delta",
128
+ contentIndex: blockIndex(),
129
+ delta: choice.delta.content,
130
+ partial: output,
131
+ });
132
+ }
133
+ }
134
+ const reasoningFields = ["reasoning_content", "reasoning", "reasoning_text"];
135
+ let foundReasoningField = null;
136
+ for (const field of reasoningFields) {
137
+ if (choice.delta[field] !== null &&
138
+ choice.delta[field] !== undefined &&
139
+ choice.delta[field].length > 0) {
140
+ if (!foundReasoningField) {
141
+ foundReasoningField = field;
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ if (foundReasoningField) {
147
+ if (!currentBlock || currentBlock.type !== "thinking") {
148
+ finishCurrentBlock(currentBlock);
149
+ currentBlock = {
150
+ type: "thinking",
151
+ thinking: "",
152
+ thinkingSignature: foundReasoningField,
153
+ };
154
+ output.content.push(currentBlock);
155
+ stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
156
+ }
157
+ if (currentBlock.type === "thinking") {
158
+ const delta = choice.delta[foundReasoningField];
159
+ currentBlock.thinking += delta;
160
+ stream.push({
161
+ type: "thinking_delta",
162
+ contentIndex: blockIndex(),
163
+ delta,
164
+ partial: output,
165
+ });
166
+ }
167
+ }
168
+ if (choice?.delta?.tool_calls) {
169
+ for (const toolCall of choice.delta.tool_calls) {
170
+ if (!currentBlock ||
171
+ currentBlock.type !== "toolCall" ||
172
+ (toolCall.id && currentBlock.id !== toolCall.id)) {
173
+ finishCurrentBlock(currentBlock);
174
+ currentBlock = {
175
+ type: "toolCall",
176
+ id: toolCall.id || "",
177
+ name: toolCall.function?.name || "",
178
+ arguments: {},
179
+ partialArgs: "",
180
+ };
181
+ output.content.push(currentBlock);
182
+ stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
183
+ }
184
+ if (currentBlock.type === "toolCall") {
185
+ if (toolCall.id)
186
+ currentBlock.id = toolCall.id;
187
+ if (toolCall.function?.name)
188
+ currentBlock.name = toolCall.function.name;
189
+ let delta = "";
190
+ if (toolCall.function?.arguments) {
191
+ delta = toolCall.function.arguments;
192
+ currentBlock.partialArgs += toolCall.function.arguments;
193
+ currentBlock.arguments = parseStreamingJson(currentBlock.partialArgs);
194
+ }
195
+ stream.push({
196
+ type: "toolcall_delta",
197
+ contentIndex: blockIndex(),
198
+ delta,
199
+ partial: output,
200
+ });
201
+ }
202
+ }
203
+ }
204
+ const reasoningDetails = choice.delta.reasoning_details;
205
+ if (reasoningDetails && Array.isArray(reasoningDetails)) {
206
+ for (const detail of reasoningDetails) {
207
+ if (detail.type === "reasoning.encrypted" && detail.id && detail.data) {
208
+ const matchingToolCall = output.content.find((b) => b.type === "toolCall" && b.id === detail.id);
209
+ if (matchingToolCall) {
210
+ matchingToolCall.thoughtSignature = JSON.stringify(detail);
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
216
+ }
217
+ finishCurrentBlock(currentBlock);
218
+ if (options?.signal?.aborted) {
219
+ throw new Error("Request was aborted");
220
+ }
221
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
222
+ throw new Error("An unknown error occurred");
223
+ }
224
+ stream.push({ type: "done", reason: output.stopReason, message: output });
225
+ stream.end();
226
+ }
227
+ catch (error) {
228
+ for (const block of output.content)
229
+ delete block.index;
230
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
231
+ output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
232
+ const rawMetadata = error?.error?.metadata?.raw;
233
+ if (rawMetadata)
234
+ output.errorMessage += `\n${rawMetadata}`;
235
+ stream.push({ type: "error", reason: output.stopReason, error: output });
236
+ stream.end();
237
+ }
238
+ })();
239
+ return stream;
240
+ };
241
+ export const streamSimpleOpenAICompletions = (model, context, options) => {
242
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider);
243
+ if (!apiKey) {
244
+ throw new Error(`No API key for provider: ${model.provider}`);
245
+ }
246
+ const base = buildBaseOptions(model, options, apiKey);
247
+ const reasoningEffort = supportsXhigh(model) ? options?.reasoning : clampReasoning(options?.reasoning);
248
+ const toolChoice = options?.toolChoice;
249
+ return streamOpenAICompletions(model, context, {
250
+ ...base,
251
+ reasoningEffort,
252
+ toolChoice,
253
+ });
254
+ };
255
+ function createClient(model, context, apiKey, optionsHeaders) {
256
+ if (!apiKey) {
257
+ if (!process.env.OPENAI_API_KEY) {
258
+ throw new Error("OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.");
259
+ }
260
+ apiKey = process.env.OPENAI_API_KEY;
261
+ }
262
+ const headers = { ...model.headers };
263
+ if (model.provider === "github-copilot") {
264
+ const hasImages = hasCopilotVisionInput(context.messages);
265
+ const copilotHeaders = buildCopilotDynamicHeaders({
266
+ messages: context.messages,
267
+ hasImages,
268
+ });
269
+ Object.assign(headers, copilotHeaders);
270
+ }
271
+ if (optionsHeaders) {
272
+ Object.assign(headers, optionsHeaders);
273
+ }
274
+ return new OpenAI({
275
+ apiKey,
276
+ baseURL: model.baseUrl,
277
+ dangerouslyAllowBrowser: true,
278
+ defaultHeaders: headers,
279
+ });
280
+ }
281
+ function buildParams(model, context, options) {
282
+ const compat = getCompat(model);
283
+ const messages = convertMessages(model, context, compat);
284
+ maybeAddOpenRouterAnthropicCacheControl(model, messages);
285
+ const params = {
286
+ model: model.id,
287
+ messages,
288
+ stream: true,
289
+ };
290
+ if (compat.supportsUsageInStreaming !== false) {
291
+ params.stream_options = { include_usage: true };
292
+ }
293
+ if (compat.supportsStore) {
294
+ params.store = false;
295
+ }
296
+ if (options?.maxTokens) {
297
+ if (compat.maxTokensField === "max_tokens") {
298
+ params.max_tokens = options.maxTokens;
299
+ }
300
+ else {
301
+ params.max_completion_tokens = options.maxTokens;
302
+ }
303
+ }
304
+ if (options?.temperature !== undefined) {
305
+ params.temperature = options.temperature;
306
+ }
307
+ if (context.tools) {
308
+ params.tools = convertTools(context.tools, compat);
309
+ }
310
+ else if (hasToolHistory(context.messages)) {
311
+ params.tools = [];
312
+ }
313
+ if (options?.toolChoice) {
314
+ params.tool_choice = options.toolChoice;
315
+ }
316
+ if ((compat.thinkingFormat === "zai" || compat.thinkingFormat === "qwen") && model.reasoning) {
317
+ params.enable_thinking = !!options?.reasoningEffort;
318
+ }
319
+ else if (options?.reasoningEffort && model.reasoning && compat.supportsReasoningEffort) {
320
+ params.reasoning_effort = mapReasoningEffort(options.reasoningEffort, compat.reasoningEffortMap);
321
+ }
322
+ if (model.baseUrl.includes("openrouter.ai") && model.compat?.openRouterRouting) {
323
+ params.provider = model.compat.openRouterRouting;
324
+ }
325
+ if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
326
+ const routing = model.compat.vercelGatewayRouting;
327
+ if (routing.only || routing.order) {
328
+ const gatewayOptions = {};
329
+ if (routing.only)
330
+ gatewayOptions.only = routing.only;
331
+ if (routing.order)
332
+ gatewayOptions.order = routing.order;
333
+ params.providerOptions = { gateway: gatewayOptions };
334
+ }
335
+ }
336
+ return params;
337
+ }
338
+ function mapReasoningEffort(effort, reasoningEffortMap) {
339
+ return reasoningEffortMap[effort] ?? effort;
340
+ }
341
+ function maybeAddOpenRouterAnthropicCacheControl(model, messages) {
342
+ if (model.provider !== "openrouter" || !model.id.startsWith("anthropic/"))
343
+ return;
344
+ for (let i = messages.length - 1; i >= 0; i--) {
345
+ const msg = messages[i];
346
+ if (msg.role !== "user" && msg.role !== "assistant")
347
+ continue;
348
+ const content = msg.content;
349
+ if (typeof content === "string") {
350
+ msg.content = [
351
+ Object.assign({ type: "text", text: content }, { cache_control: { type: "ephemeral" } }),
352
+ ];
353
+ return;
354
+ }
355
+ if (!Array.isArray(content))
356
+ continue;
357
+ for (let j = content.length - 1; j >= 0; j--) {
358
+ const part = content[j];
359
+ if (part?.type === "text") {
360
+ Object.assign(part, { cache_control: { type: "ephemeral" } });
361
+ return;
362
+ }
363
+ }
364
+ }
365
+ }
366
+ export function convertMessages(model, context, compat) {
367
+ const params = [];
368
+ const normalizeToolCallId = (id) => {
369
+ if (id.includes("|")) {
370
+ const [callId] = id.split("|");
371
+ return callId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
372
+ }
373
+ if (model.provider === "openai")
374
+ return id.length > 40 ? id.slice(0, 40) : id;
375
+ return id;
376
+ };
377
+ const transformedMessages = transformMessages(context.messages, model, (id) => normalizeToolCallId(id));
378
+ if (context.systemPrompt) {
379
+ const useDeveloperRole = model.reasoning && compat.supportsDeveloperRole;
380
+ const role = useDeveloperRole ? "developer" : "system";
381
+ params.push({ role: role, content: sanitizeSurrogates(context.systemPrompt) });
382
+ }
383
+ let lastRole = null;
384
+ for (let i = 0; i < transformedMessages.length; i++) {
385
+ const msg = transformedMessages[i];
386
+ if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && msg.role === "user") {
387
+ params.push({
388
+ role: "assistant",
389
+ content: "I have processed the tool results.",
390
+ });
391
+ }
392
+ if (msg.role === "user") {
393
+ if (typeof msg.content === "string") {
394
+ params.push({
395
+ role: "user",
396
+ content: sanitizeSurrogates(msg.content),
397
+ });
398
+ }
399
+ else {
400
+ const content = msg.content.map((item) => {
401
+ if (item.type === "text") {
402
+ return {
403
+ type: "text",
404
+ text: sanitizeSurrogates(item.text),
405
+ };
406
+ }
407
+ else {
408
+ return {
409
+ type: "image_url",
410
+ image_url: {
411
+ url: `data:${item.mimeType};base64,${item.data}`,
412
+ },
413
+ };
414
+ }
415
+ });
416
+ const filteredContent = !model.input.includes("image")
417
+ ? content.filter((c) => c.type !== "image_url")
418
+ : content;
419
+ if (filteredContent.length === 0)
420
+ continue;
421
+ params.push({
422
+ role: "user",
423
+ content: filteredContent,
424
+ });
425
+ }
426
+ }
427
+ else if (msg.role === "assistant") {
428
+ const assistantMsg = {
429
+ role: "assistant",
430
+ content: compat.requiresAssistantAfterToolResult ? "" : null,
431
+ };
432
+ const textBlocks = msg.content.filter((b) => b.type === "text");
433
+ const nonEmptyTextBlocks = textBlocks.filter((b) => b.text && b.text.trim().length > 0);
434
+ if (nonEmptyTextBlocks.length > 0) {
435
+ if (model.provider === "github-copilot") {
436
+ assistantMsg.content = nonEmptyTextBlocks.map((b) => sanitizeSurrogates(b.text)).join("");
437
+ }
438
+ else {
439
+ assistantMsg.content = nonEmptyTextBlocks.map((b) => {
440
+ return { type: "text", text: sanitizeSurrogates(b.text) };
441
+ });
442
+ }
443
+ }
444
+ const thinkingBlocks = msg.content.filter((b) => b.type === "thinking");
445
+ const nonEmptyThinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking.trim().length > 0);
446
+ if (nonEmptyThinkingBlocks.length > 0) {
447
+ if (compat.requiresThinkingAsText) {
448
+ const thinkingText = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n\n");
449
+ const textContent = assistantMsg.content;
450
+ if (textContent) {
451
+ textContent.unshift({ type: "text", text: thinkingText });
452
+ }
453
+ else {
454
+ assistantMsg.content = [{ type: "text", text: thinkingText }];
455
+ }
456
+ }
457
+ else {
458
+ const signature = nonEmptyThinkingBlocks[0].thinkingSignature;
459
+ if (signature && signature.length > 0) {
460
+ assistantMsg[signature] = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n");
461
+ }
462
+ }
463
+ }
464
+ const toolCalls = msg.content.filter((b) => b.type === "toolCall");
465
+ if (toolCalls.length > 0) {
466
+ assistantMsg.tool_calls = toolCalls.map((tc) => ({
467
+ id: tc.id,
468
+ type: "function",
469
+ function: {
470
+ name: tc.name,
471
+ arguments: JSON.stringify(tc.arguments),
472
+ },
473
+ }));
474
+ const reasoningDetails = toolCalls
475
+ .filter((tc) => tc.thoughtSignature)
476
+ .map((tc) => {
477
+ try {
478
+ return JSON.parse(tc.thoughtSignature);
479
+ }
480
+ catch {
481
+ return null;
482
+ }
483
+ })
484
+ .filter(Boolean);
485
+ if (reasoningDetails.length > 0) {
486
+ assistantMsg.reasoning_details = reasoningDetails;
487
+ }
488
+ }
489
+ const content = assistantMsg.content;
490
+ const hasContent = content !== null &&
491
+ content !== undefined &&
492
+ (typeof content === "string" ? content.length > 0 : content.length > 0);
493
+ if (!hasContent && !assistantMsg.tool_calls) {
494
+ continue;
495
+ }
496
+ params.push(assistantMsg);
497
+ }
498
+ else if (msg.role === "toolResult") {
499
+ const imageBlocks = [];
500
+ let j = i;
501
+ for (; j < transformedMessages.length && transformedMessages[j].role === "toolResult"; j++) {
502
+ const toolMsg = transformedMessages[j];
503
+ const textResult = toolMsg.content
504
+ .filter((c) => c.type === "text")
505
+ .map((c) => c.text)
506
+ .join("\n");
507
+ const hasImages = toolMsg.content.some((c) => c.type === "image");
508
+ const hasText = textResult.length > 0;
509
+ const toolResultMsg = {
510
+ role: "tool",
511
+ content: sanitizeSurrogates(hasText ? textResult : "(see attached image)"),
512
+ tool_call_id: toolMsg.toolCallId,
513
+ };
514
+ if (compat.requiresToolResultName && toolMsg.toolName) {
515
+ toolResultMsg.name = toolMsg.toolName;
516
+ }
517
+ params.push(toolResultMsg);
518
+ if (hasImages && model.input.includes("image")) {
519
+ for (const block of toolMsg.content) {
520
+ if (block.type === "image") {
521
+ imageBlocks.push({
522
+ type: "image_url",
523
+ image_url: {
524
+ url: `data:${block.mimeType};base64,${block.data}`,
525
+ },
526
+ });
527
+ }
528
+ }
529
+ }
530
+ }
531
+ i = j - 1;
532
+ if (imageBlocks.length > 0) {
533
+ if (compat.requiresAssistantAfterToolResult) {
534
+ params.push({
535
+ role: "assistant",
536
+ content: "I have processed the tool results.",
537
+ });
538
+ }
539
+ params.push({
540
+ role: "user",
541
+ content: [
542
+ {
543
+ type: "text",
544
+ text: "Attached image(s) from tool result:",
545
+ },
546
+ ...imageBlocks,
547
+ ],
548
+ });
549
+ lastRole = "user";
550
+ }
551
+ else {
552
+ lastRole = "toolResult";
553
+ }
554
+ continue;
555
+ }
556
+ lastRole = msg.role;
557
+ }
558
+ return params;
559
+ }
560
+ function convertTools(tools, compat) {
561
+ return tools.map((tool) => ({
562
+ type: "function",
563
+ function: {
564
+ name: tool.name,
565
+ description: tool.description,
566
+ parameters: tool.parameters,
567
+ ...(compat.supportsStrictMode !== false && { strict: false }),
568
+ },
569
+ }));
570
+ }
571
+ function mapStopReason(reason) {
572
+ if (reason === null)
573
+ return "stop";
574
+ switch (reason) {
575
+ case "stop":
576
+ return "stop";
577
+ case "length":
578
+ return "length";
579
+ case "function_call":
580
+ case "tool_calls":
581
+ return "toolUse";
582
+ case "content_filter":
583
+ return "error";
584
+ default: {
585
+ const _exhaustive = reason;
586
+ throw new Error(`Unhandled stop reason: ${_exhaustive}`);
587
+ }
588
+ }
589
+ }
590
+ function detectCompat(model) {
591
+ const provider = model.provider;
592
+ const baseUrl = model.baseUrl;
593
+ const isZai = provider === "zai" || baseUrl.includes("api.z.ai");
594
+ const isNonStandard = provider === "cerebras" ||
595
+ baseUrl.includes("cerebras.ai") ||
596
+ provider === "xai" ||
597
+ baseUrl.includes("api.x.ai") ||
598
+ baseUrl.includes("chutes.ai") ||
599
+ baseUrl.includes("deepseek.com") ||
600
+ isZai ||
601
+ provider === "opencode" ||
602
+ baseUrl.includes("opencode.ai");
603
+ const useMaxTokens = baseUrl.includes("chutes.ai");
604
+ const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
605
+ const isGroq = provider === "groq" || baseUrl.includes("groq.com");
606
+ const reasoningEffortMap = isGroq && model.id === "qwen/qwen3-32b"
607
+ ? {
608
+ minimal: "default",
609
+ low: "default",
610
+ medium: "default",
611
+ high: "default",
612
+ xhigh: "default",
613
+ }
614
+ : {};
615
+ return {
616
+ supportsStore: !isNonStandard,
617
+ supportsDeveloperRole: !isNonStandard,
618
+ supportsReasoningEffort: !isGrok && !isZai,
619
+ reasoningEffortMap,
620
+ supportsUsageInStreaming: true,
621
+ maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
622
+ requiresToolResultName: false,
623
+ requiresAssistantAfterToolResult: false,
624
+ requiresThinkingAsText: false,
625
+ thinkingFormat: isZai ? "zai" : "openai",
626
+ openRouterRouting: {},
627
+ vercelGatewayRouting: {},
628
+ supportsStrictMode: true,
629
+ };
630
+ }
631
+ function getCompat(model) {
632
+ const detected = detectCompat(model);
633
+ if (!model.compat)
634
+ return detected;
635
+ return {
636
+ supportsStore: model.compat.supportsStore ?? detected.supportsStore,
637
+ supportsDeveloperRole: model.compat.supportsDeveloperRole ?? detected.supportsDeveloperRole,
638
+ supportsReasoningEffort: model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort,
639
+ reasoningEffortMap: model.compat.reasoningEffortMap ?? detected.reasoningEffortMap,
640
+ supportsUsageInStreaming: model.compat.supportsUsageInStreaming ?? detected.supportsUsageInStreaming,
641
+ maxTokensField: model.compat.maxTokensField ?? detected.maxTokensField,
642
+ requiresToolResultName: model.compat.requiresToolResultName ?? detected.requiresToolResultName,
643
+ requiresAssistantAfterToolResult: model.compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
644
+ requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
645
+ thinkingFormat: model.compat.thinkingFormat ?? detected.thinkingFormat,
646
+ openRouterRouting: model.compat.openRouterRouting ?? {},
647
+ vercelGatewayRouting: model.compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
648
+ supportsStrictMode: model.compat.supportsStrictMode ?? detected.supportsStrictMode,
649
+ };
650
+ }
651
+ //# sourceMappingURL=openai-completions.js.map