@yourgpt/llm-sdk 2.1.10-alpha.0 → 2.5.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/adapters/index.d.mts +4 -38
  2. package/dist/adapters/index.d.ts +4 -38
  3. package/dist/adapters/index.js +158 -325
  4. package/dist/adapters/index.mjs +158 -325
  5. package/dist/base-C58Dsr9p.d.ts +259 -0
  6. package/dist/base-tNgbBaSo.d.mts +259 -0
  7. package/dist/fallback/index.d.mts +4 -4
  8. package/dist/fallback/index.d.ts +4 -4
  9. package/dist/index.d.mts +8 -7
  10. package/dist/index.d.ts +8 -7
  11. package/dist/index.js +35 -43
  12. package/dist/index.mjs +35 -43
  13. package/dist/providers/anthropic/index.d.mts +3 -3
  14. package/dist/providers/anthropic/index.d.ts +3 -3
  15. package/dist/providers/anthropic/index.js +271 -212
  16. package/dist/providers/anthropic/index.mjs +271 -212
  17. package/dist/providers/azure/index.d.mts +3 -3
  18. package/dist/providers/azure/index.d.ts +3 -3
  19. package/dist/providers/azure/index.js +49 -1
  20. package/dist/providers/azure/index.mjs +49 -1
  21. package/dist/providers/fireworks/index.d.mts +1 -1
  22. package/dist/providers/fireworks/index.d.ts +1 -1
  23. package/dist/providers/fireworks/index.js +56 -0
  24. package/dist/providers/fireworks/index.mjs +56 -0
  25. package/dist/providers/google/index.d.mts +3 -3
  26. package/dist/providers/google/index.d.ts +3 -3
  27. package/dist/providers/google/index.js +254 -510
  28. package/dist/providers/google/index.mjs +254 -510
  29. package/dist/providers/ollama/index.d.mts +4 -4
  30. package/dist/providers/ollama/index.d.ts +4 -4
  31. package/dist/providers/ollama/index.js +10 -2
  32. package/dist/providers/ollama/index.mjs +10 -2
  33. package/dist/providers/openai/index.d.mts +3 -3
  34. package/dist/providers/openai/index.d.ts +3 -3
  35. package/dist/providers/openai/index.js +269 -529
  36. package/dist/providers/openai/index.mjs +269 -529
  37. package/dist/providers/openrouter/index.d.mts +3 -7
  38. package/dist/providers/openrouter/index.d.ts +3 -7
  39. package/dist/providers/openrouter/index.js +365 -902
  40. package/dist/providers/openrouter/index.mjs +365 -902
  41. package/dist/providers/togetherai/index.d.mts +3 -3
  42. package/dist/providers/togetherai/index.d.ts +3 -3
  43. package/dist/providers/togetherai/index.js +259 -509
  44. package/dist/providers/togetherai/index.mjs +259 -509
  45. package/dist/providers/xai/index.d.mts +3 -3
  46. package/dist/providers/xai/index.d.ts +3 -3
  47. package/dist/providers/xai/index.js +258 -513
  48. package/dist/providers/xai/index.mjs +258 -513
  49. package/dist/{types-BNCmlJMs.d.mts → types-B6dhnguR.d.mts} +1 -1
  50. package/dist/{types-DhktekQ3.d.ts → types-BQ31QIsA.d.ts} +2 -1
  51. package/dist/{types-CMMQ8s2O.d.mts → types-BSSiJW2o.d.mts} +2 -1
  52. package/dist/{base-DN1EfKnE.d.mts → types-BkQCSiIt.d.mts} +388 -214
  53. package/dist/{base-DuUNxtVg.d.ts → types-BkQCSiIt.d.ts} +388 -214
  54. package/dist/{types-Pj-vpmoT.d.ts → types-CCxPmkmK.d.ts} +1 -1
  55. package/dist/yourgpt/index.d.mts +1 -1
  56. package/dist/yourgpt/index.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/dist/types-CMvvDo-E.d.mts +0 -428
  59. package/dist/types-CMvvDo-E.d.ts +0 -428
@@ -1,3 +1,232 @@
1
+ // src/adapters/base.ts
2
+ function stringifyForDebug(value) {
3
+ return JSON.stringify(
4
+ value,
5
+ (_key, currentValue) => {
6
+ if (typeof currentValue === "bigint") {
7
+ return currentValue.toString();
8
+ }
9
+ if (currentValue instanceof Error) {
10
+ return {
11
+ name: currentValue.name,
12
+ message: currentValue.message,
13
+ stack: currentValue.stack
14
+ };
15
+ }
16
+ return currentValue;
17
+ },
18
+ 2
19
+ );
20
+ }
21
+ function logProviderPayload(provider, label, payload, enabled) {
22
+ if (!enabled) {
23
+ return;
24
+ }
25
+ if (label.toLowerCase().includes("stream ")) {
26
+ return;
27
+ }
28
+ try {
29
+ console.log(
30
+ `[llm-sdk:${provider}] ${label}
31
+ ${stringifyForDebug(payload)}`
32
+ );
33
+ } catch (error) {
34
+ console.log(
35
+ `[llm-sdk:${provider}] ${label} (failed to stringify payload)`,
36
+ error
37
+ );
38
+ }
39
+ }
40
+ function parameterToJsonSchema(param) {
41
+ const schema = {
42
+ type: param.type
43
+ };
44
+ if (param.description) {
45
+ schema.description = param.description;
46
+ }
47
+ if (param.enum) {
48
+ schema.enum = param.enum;
49
+ }
50
+ if (param.type === "array" && param.items) {
51
+ schema.items = parameterToJsonSchema(
52
+ param.items
53
+ );
54
+ }
55
+ if (param.type === "object" && param.properties) {
56
+ schema.properties = Object.fromEntries(
57
+ Object.entries(param.properties).map(([key, prop]) => [
58
+ key,
59
+ parameterToJsonSchema(
60
+ prop
61
+ )
62
+ ])
63
+ );
64
+ schema.additionalProperties = false;
65
+ }
66
+ return schema;
67
+ }
68
+ function normalizeObjectJsonSchema(schema) {
69
+ if (!schema || typeof schema !== "object") {
70
+ return {
71
+ type: "object",
72
+ properties: {},
73
+ required: [],
74
+ additionalProperties: false
75
+ };
76
+ }
77
+ const normalized = { ...schema };
78
+ const type = normalized.type;
79
+ if (type === "object") {
80
+ const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : {};
81
+ normalized.properties = Object.fromEntries(
82
+ Object.entries(properties).map(([key, value]) => [
83
+ key,
84
+ normalizeObjectJsonSchema(value)
85
+ ])
86
+ );
87
+ const propertyKeys = Object.keys(properties);
88
+ const required = Array.isArray(normalized.required) ? normalized.required.filter(
89
+ (value) => typeof value === "string"
90
+ ) : [];
91
+ normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
92
+ if (normalized.additionalProperties === void 0) {
93
+ normalized.additionalProperties = false;
94
+ }
95
+ } else if (type === "array" && normalized.items && typeof normalized.items === "object") {
96
+ normalized.items = normalizeObjectJsonSchema(
97
+ normalized.items
98
+ );
99
+ }
100
+ return normalized;
101
+ }
102
+ function isOpenAIReasoningModel(modelId) {
103
+ if (!modelId) return false;
104
+ return /^(o1|o3|o4|gpt-5)/i.test(modelId);
105
+ }
106
+ function buildOpenAITokenParams(modelId, maxTokens, temperature) {
107
+ if (isOpenAIReasoningModel(modelId)) {
108
+ return { max_completion_tokens: maxTokens };
109
+ }
110
+ return { max_tokens: maxTokens, temperature };
111
+ }
112
+ function toOpenAIResponseFormat(rf) {
113
+ if (!rf) return void 0;
114
+ if (rf.type === "json_object") return { type: "json_object" };
115
+ return {
116
+ type: "json_schema",
117
+ json_schema: {
118
+ name: rf.json_schema.name,
119
+ schema: normalizeObjectJsonSchema(rf.json_schema.schema),
120
+ strict: rf.json_schema.strict ?? true
121
+ }
122
+ };
123
+ }
124
+ function toOpenAIResponsesTextFormat(rf) {
125
+ if (!rf || rf.type !== "json_schema") return void 0;
126
+ return {
127
+ type: "json_schema",
128
+ name: rf.json_schema.name,
129
+ schema: normalizeObjectJsonSchema(rf.json_schema.schema),
130
+ strict: rf.json_schema.strict ?? true
131
+ };
132
+ }
133
+ function formatTools(actions) {
134
+ return actions.map((action) => ({
135
+ type: "function",
136
+ function: {
137
+ name: action.name,
138
+ description: action.description,
139
+ parameters: {
140
+ type: "object",
141
+ properties: action.parameters ? Object.fromEntries(
142
+ Object.entries(action.parameters).map(([key, param]) => [
143
+ key,
144
+ parameterToJsonSchema(param)
145
+ ])
146
+ ) : {},
147
+ required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : [],
148
+ additionalProperties: false
149
+ }
150
+ }
151
+ }));
152
+ }
153
+ function hasImageAttachments(message) {
154
+ const attachments = message.metadata?.attachments;
155
+ return attachments?.some((a) => a.type === "image") ?? false;
156
+ }
157
+ function attachmentToOpenAIImage(attachment) {
158
+ if (attachment.type !== "image") return null;
159
+ let imageUrl;
160
+ if (attachment.url) {
161
+ imageUrl = attachment.url;
162
+ } else if (attachment.data) {
163
+ imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
164
+ } else {
165
+ return null;
166
+ }
167
+ return {
168
+ type: "image_url",
169
+ image_url: {
170
+ url: imageUrl,
171
+ detail: "auto"
172
+ }
173
+ };
174
+ }
175
+ function messageToOpenAIContent(message) {
176
+ const attachments = message.metadata?.attachments;
177
+ const content = message.content ?? "";
178
+ if (!hasImageAttachments(message)) {
179
+ return content;
180
+ }
181
+ const blocks = [];
182
+ if (content) {
183
+ blocks.push({ type: "text", text: content });
184
+ }
185
+ if (attachments) {
186
+ for (const attachment of attachments) {
187
+ const imageBlock = attachmentToOpenAIImage(attachment);
188
+ if (imageBlock) {
189
+ blocks.push(imageBlock);
190
+ }
191
+ }
192
+ }
193
+ return blocks;
194
+ }
195
+ function formatMessagesForOpenAI(messages, systemPrompt) {
196
+ const formatted = [];
197
+ if (systemPrompt) {
198
+ formatted.push({ role: "system", content: systemPrompt });
199
+ }
200
+ for (const msg of messages) {
201
+ if (msg.role === "system") {
202
+ formatted.push({ role: "system", content: msg.content ?? "" });
203
+ } else if (msg.role === "user") {
204
+ formatted.push({
205
+ role: "user",
206
+ content: messageToOpenAIContent(msg)
207
+ });
208
+ } else if (msg.role === "assistant") {
209
+ const hasToolCalls = msg.tool_calls && msg.tool_calls.length > 0;
210
+ const assistantMsg = {
211
+ role: "assistant",
212
+ // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
213
+ content: hasToolCalls ? msg.content || null : msg.content
214
+ };
215
+ if (hasToolCalls) {
216
+ assistantMsg.tool_calls = msg.tool_calls;
217
+ }
218
+ formatted.push(assistantMsg);
219
+ } else if (msg.role === "tool" && msg.tool_call_id) {
220
+ formatted.push({
221
+ role: "tool",
222
+ content: msg.content ?? "",
223
+ tool_call_id: msg.tool_call_id
224
+ });
225
+ }
226
+ }
227
+ return formatted;
228
+ }
229
+
1
230
  // src/providers/openrouter/provider.ts
2
231
  var DEFAULT_MODEL_CONFIG = {
3
232
  vision: true,
@@ -5,9 +234,6 @@ var DEFAULT_MODEL_CONFIG = {
5
234
  jsonMode: true,
6
235
  maxTokens: 128e3
7
236
  };
8
- function isOpenAIReasoningModel(modelId) {
9
- return modelId.startsWith("openai/o1") || modelId.startsWith("openai/o3") || modelId.startsWith("openai/o4") || modelId.startsWith("openai/gpt-5");
10
- }
11
237
  function openrouter(modelId, options = {}) {
12
238
  const apiKey = options.apiKey ?? process.env.OPENROUTER_API_KEY;
13
239
  const baseURL = options.baseURL ?? "https://openrouter.ai/api/v1";
@@ -39,7 +265,7 @@ function openrouter(modelId, options = {}) {
39
265
  supportsTools: modelConfig.tools,
40
266
  supportsStreaming: true,
41
267
  supportsJsonMode: modelConfig.jsonMode,
42
- supportsThinking: true,
268
+ supportsThinking: false,
43
269
  supportsPDF: false,
44
270
  maxTokens: modelConfig.maxTokens,
45
271
  supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
@@ -59,6 +285,10 @@ function openrouter(modelId, options = {}) {
59
285
  if (options.providerPreferences) {
60
286
  requestBody.provider = options.providerPreferences;
61
287
  }
288
+ const responseFormat = toOpenAIResponseFormat(params.responseFormat);
289
+ if (responseFormat) {
290
+ requestBody.response_format = responseFormat;
291
+ }
62
292
  const response = await client2.chat.completions.create(requestBody);
63
293
  const choice = response.choices[0];
64
294
  const message = choice.message;
@@ -82,11 +312,6 @@ function openrouter(modelId, options = {}) {
82
312
  };
83
313
  },
84
314
  async *doStream(params) {
85
- if (!options.disableThinking && isOpenAIReasoningModel(modelId)) {
86
- const client3 = await getClient();
87
- yield* doStreamResponsesAPI(client3, modelId, params);
88
- return;
89
- }
90
315
  const client2 = await getClient();
91
316
  const messages = formatMessagesForOpenRouter(params.messages);
92
317
  const requestBody = {
@@ -94,8 +319,7 @@ function openrouter(modelId, options = {}) {
94
319
  messages,
95
320
  temperature: params.temperature,
96
321
  max_tokens: params.maxTokens,
97
- stream: true,
98
- ...!options.disableThinking ? { reasoning: { max_tokens: 8e3 }, include_reasoning: true } : {}
322
+ stream: true
99
323
  };
100
324
  if (params.tools) {
101
325
  requestBody.tools = params.tools;
@@ -103,11 +327,14 @@ function openrouter(modelId, options = {}) {
103
327
  if (options.providerPreferences) {
104
328
  requestBody.provider = options.providerPreferences;
105
329
  }
330
+ const responseFormat = toOpenAIResponseFormat(params.responseFormat);
331
+ if (responseFormat) {
332
+ requestBody.response_format = responseFormat;
333
+ }
106
334
  const stream = await client2.chat.completions.create(requestBody);
107
335
  let currentToolCall = null;
108
336
  let totalPromptTokens = 0;
109
337
  let totalCompletionTokens = 0;
110
- let orReasoningStarted = false;
111
338
  for await (const chunk of stream) {
112
339
  if (params.signal?.aborted) {
113
340
  yield { type: "error", error: new Error("Aborted") };
@@ -118,20 +345,6 @@ function openrouter(modelId, options = {}) {
118
345
  if (delta?.content) {
119
346
  yield { type: "text-delta", text: delta.content };
120
347
  }
121
- const rc = delta?.reasoning_content ?? delta?.reasoning ?? null;
122
- if (rc) {
123
- const rcText = typeof rc === "string" ? rc : Array.isArray(rc) && rc[0]?.text ? rc[0].text : "";
124
- if (rcText) {
125
- if (!orReasoningStarted) {
126
- yield { type: "thinking:start" };
127
- orReasoningStarted = true;
128
- }
129
- yield { type: "thinking:delta", content: rcText };
130
- }
131
- } else if (orReasoningStarted && (delta?.content || choice?.finish_reason)) {
132
- yield { type: "thinking:end" };
133
- orReasoningStarted = false;
134
- }
135
348
  if (delta?.tool_calls) {
136
349
  for (const tc of delta.tool_calls) {
137
350
  if (tc.id) {
@@ -171,578 +384,118 @@ function openrouter(modelId, options = {}) {
171
384
  totalPromptTokens = chunk.usage.prompt_tokens;
172
385
  totalCompletionTokens = chunk.usage.completion_tokens;
173
386
  }
174
- yield {
175
- type: "finish",
176
- finishReason: mapFinishReason(choice.finish_reason),
177
- usage: {
178
- promptTokens: totalPromptTokens,
179
- completionTokens: totalCompletionTokens,
180
- totalTokens: totalPromptTokens + totalCompletionTokens
181
- }
182
- };
183
- }
184
- }
185
- }
186
- };
187
- }
188
- function mapFinishReason(reason) {
189
- switch (reason) {
190
- case "stop":
191
- return "stop";
192
- case "length":
193
- return "length";
194
- case "tool_calls":
195
- case "function_call":
196
- return "tool-calls";
197
- case "content_filter":
198
- return "content-filter";
199
- default:
200
- return "unknown";
201
- }
202
- }
203
- function formatMessagesForOpenRouter(messages) {
204
- return messages.map((msg) => {
205
- switch (msg.role) {
206
- case "system":
207
- return { role: "system", content: msg.content };
208
- case "user":
209
- if (typeof msg.content === "string") {
210
- return { role: "user", content: msg.content };
211
- }
212
- return {
213
- role: "user",
214
- content: msg.content.map((part) => {
215
- if (part.type === "text") {
216
- return { type: "text", text: part.text };
217
- }
218
- if (part.type === "image") {
219
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
220
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
221
- return { type: "image_url", image_url: { url, detail: "auto" } };
222
- }
223
- return { type: "text", text: "" };
224
- })
225
- };
226
- case "assistant":
227
- const assistantMsg = {
228
- role: "assistant",
229
- content: msg.content
230
- };
231
- if (msg.toolCalls && msg.toolCalls.length > 0) {
232
- assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
233
- id: tc.id,
234
- type: "function",
235
- function: {
236
- name: tc.name,
237
- arguments: JSON.stringify(tc.args)
238
- }
239
- }));
240
- }
241
- return assistantMsg;
242
- case "tool":
243
- return {
244
- role: "tool",
245
- tool_call_id: msg.toolCallId,
246
- content: msg.content
247
- };
248
- default:
249
- return msg;
250
- }
251
- });
252
- }
253
- function formatMessagesForResponsesAPI(messages) {
254
- const out = [];
255
- for (const msg of messages) {
256
- if (msg.role === "system") {
257
- out.push({
258
- type: "message",
259
- role: "system",
260
- content: [
261
- {
262
- type: "input_text",
263
- text: typeof msg.content === "string" ? msg.content : ""
264
- }
265
- ]
266
- });
267
- continue;
268
- }
269
- if (msg.role === "user") {
270
- const parts = [];
271
- if (typeof msg.content === "string") {
272
- parts.push({ type: "input_text", text: msg.content });
273
- } else {
274
- for (const part of msg.content) {
275
- if (part.type === "text") {
276
- parts.push({ type: "input_text", text: part.text });
277
- } else if (part.type === "image") {
278
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
279
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
280
- parts.push({ type: "input_image", image_url: url });
281
- }
282
- }
283
- }
284
- out.push({ type: "message", role: "user", content: parts });
285
- continue;
286
- }
287
- if (msg.role === "assistant") {
288
- if (msg.toolCalls && msg.toolCalls.length > 0) {
289
- for (const tc of msg.toolCalls) {
290
- out.push({
291
- type: "function_call",
292
- call_id: tc.id,
293
- name: tc.name,
294
- arguments: JSON.stringify(tc.args ?? {})
295
- });
296
- }
297
- if (typeof msg.content === "string" && msg.content.length > 0) {
298
- out.push({
299
- type: "message",
300
- role: "assistant",
301
- content: [{ type: "output_text", text: msg.content }]
302
- });
303
- }
304
- } else {
305
- const text = typeof msg.content === "string" ? msg.content : "";
306
- out.push({
307
- type: "message",
308
- role: "assistant",
309
- content: [{ type: "output_text", text }]
310
- });
311
- }
312
- continue;
313
- }
314
- if (msg.role === "tool") {
315
- out.push({
316
- type: "function_call_output",
317
- call_id: msg.toolCallId,
318
- output: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
319
- });
320
- continue;
321
- }
322
- }
323
- return out;
324
- }
325
- function formatToolsForResponsesAPI(tools) {
326
- if (!tools || tools.length === 0) return void 0;
327
- return tools.map((t) => {
328
- if (t?.name && t?.parameters && t?.type === "function") return t;
329
- const fn = t?.function ?? t;
330
- return {
331
- type: "function",
332
- name: fn.name,
333
- description: fn.description,
334
- parameters: fn.parameters ?? { type: "object", properties: {} }
335
- };
336
- });
337
- }
338
- async function* doStreamResponsesAPI(client, modelId, params) {
339
- const systemTexts = [];
340
- const nonSystem = [];
341
- for (const m of params.messages) {
342
- if (m.role === "system" && typeof m.content === "string") {
343
- systemTexts.push(m.content);
344
- } else {
345
- nonSystem.push(m);
346
- }
347
- }
348
- const instructions = systemTexts.join("\n\n") || void 0;
349
- const input = formatMessagesForResponsesAPI(nonSystem);
350
- const requestBody = {
351
- model: modelId,
352
- input,
353
- stream: true,
354
- reasoning: { effort: "medium", summary: "auto" }
355
- };
356
- if (instructions) requestBody.instructions = instructions;
357
- if (typeof params.maxTokens === "number")
358
- requestBody.max_output_tokens = params.maxTokens;
359
- if (typeof params.temperature === "number")
360
- requestBody.temperature = params.temperature;
361
- const tools = formatToolsForResponsesAPI(params.tools);
362
- if (tools) requestBody.tools = tools;
363
- let stream;
364
- try {
365
- stream = await client.responses.create(requestBody);
366
- } catch (err) {
367
- yield {
368
- type: "error",
369
- error: err instanceof Error ? err : new Error(String(err))
370
- };
371
- return;
372
- }
373
- const toolCalls = /* @__PURE__ */ new Map();
374
- let reasoningStarted = false;
375
- let textStarted = false;
376
- let totalPromptTokens = 0;
377
- let totalCompletionTokens = 0;
378
- let finishEmitted = false;
379
- for await (const evt of stream) {
380
- if (params.signal?.aborted) {
381
- yield { type: "error", error: new Error("Aborted") };
382
- return;
383
- }
384
- const t = evt?.type ?? "";
385
- if (t === "response.reasoning_summary_text.delta") {
386
- const delta = evt.delta ?? "";
387
- if (!delta) continue;
388
- if (!reasoningStarted) {
389
- yield { type: "thinking:start" };
390
- reasoningStarted = true;
391
- }
392
- yield { type: "thinking:delta", content: delta };
393
- continue;
394
- }
395
- if (t === "response.reasoning_summary_text.done" || t === "response.reasoning.done") {
396
- continue;
397
- }
398
- if (t === "response.output_text.delta") {
399
- const text = evt.delta ?? "";
400
- if (!text) continue;
401
- if (reasoningStarted && !textStarted) {
402
- yield { type: "thinking:end" };
403
- textStarted = true;
404
- }
405
- yield { type: "text-delta", text };
406
- continue;
407
- }
408
- if (t === "response.output_item.added") {
409
- const item = evt.item;
410
- if (item?.type === "function_call") {
411
- const id = item.call_id ?? item.id ?? "";
412
- if (id) {
413
- toolCalls.set(id, {
414
- id,
415
- name: item.name ?? "",
416
- arguments: item.arguments ?? ""
417
- });
418
- }
419
- }
420
- continue;
421
- }
422
- if (t === "response.function_call_arguments.delta") {
423
- const id = evt.call_id ?? evt.item_id ?? "";
424
- const delta = evt.delta ?? "";
425
- if (!id || !delta) continue;
426
- const existing = toolCalls.get(id);
427
- if (existing) {
428
- existing.arguments += delta;
429
- } else {
430
- toolCalls.set(id, { id, name: "", arguments: delta });
431
- }
432
- continue;
433
- }
434
- if (t === "response.output_item.done") {
435
- const item = evt.item;
436
- if (item?.type === "function_call") {
437
- const id = item.call_id ?? item.id ?? "";
438
- const tc = toolCalls.get(id);
439
- const name = tc?.name || item.name || "";
440
- const argsStr = tc?.arguments || item.arguments || "{}";
441
- let args = {};
442
- try {
443
- args = JSON.parse(argsStr || "{}");
444
- } catch {
445
- args = {};
446
- }
447
- if (id && name) {
448
- yield {
449
- type: "tool-call",
450
- toolCall: { id, name, args }
451
- };
452
- }
453
- toolCalls.delete(id);
454
- }
455
- continue;
456
- }
457
- if (t === "response.completed") {
458
- const usage = evt.response?.usage;
459
- if (usage) {
460
- totalPromptTokens = usage.input_tokens ?? 0;
461
- totalCompletionTokens = usage.output_tokens ?? 0;
462
- }
463
- for (const tc of toolCalls.values()) {
464
- let args = {};
465
- try {
466
- args = JSON.parse(tc.arguments || "{}");
467
- } catch {
468
- args = {};
469
- }
470
- if (tc.id && tc.name) {
471
- yield {
472
- type: "tool-call",
473
- toolCall: { id: tc.id, name: tc.name, args }
474
- };
475
- }
476
- }
477
- toolCalls.clear();
478
- if (reasoningStarted && !textStarted) {
479
- yield { type: "thinking:end" };
480
- }
481
- const finishReason = toolCalls.size > 0 ? "tool-calls" : "stop";
482
- yield {
483
- type: "finish",
484
- finishReason,
485
- usage: {
486
- promptTokens: totalPromptTokens,
487
- completionTokens: totalCompletionTokens,
488
- totalTokens: totalPromptTokens + totalCompletionTokens
489
- }
490
- };
491
- finishEmitted = true;
492
- continue;
493
- }
494
- if (t === "response.error" || t === "error") {
495
- const msg = evt.error?.message || evt.message || "Responses API error";
496
- yield { type: "error", error: new Error(msg) };
497
- return;
498
- }
499
- }
500
- if (!finishEmitted) {
501
- if (reasoningStarted && !textStarted) {
502
- yield { type: "thinking:end" };
503
- }
504
- yield {
505
- type: "finish",
506
- finishReason: "stop",
507
- usage: {
508
- promptTokens: totalPromptTokens,
509
- completionTokens: totalCompletionTokens,
510
- totalTokens: totalPromptTokens + totalCompletionTokens
511
- }
512
- };
513
- }
514
- }
515
- async function fetchOpenRouterModels(apiKey) {
516
- const headers = {
517
- "Content-Type": "application/json"
518
- };
519
- if (apiKey) {
520
- headers["Authorization"] = `Bearer ${apiKey}`;
521
- }
522
- const response = await fetch("https://openrouter.ai/api/v1/models", {
523
- headers
524
- });
525
- if (!response.ok) {
526
- throw new Error(`Failed to fetch models: ${response.statusText}`);
527
- }
528
- const data = await response.json();
529
- return data.data || [];
530
- }
531
- async function searchOpenRouterModels(query, apiKey) {
532
- const models = await fetchOpenRouterModels(apiKey);
533
- const lowerQuery = query.toLowerCase();
534
- return models.filter(
535
- (model) => model.id.toLowerCase().includes(lowerQuery) || model.name.toLowerCase().includes(lowerQuery)
536
- );
537
- }
538
-
539
- // src/core/utils.ts
540
- function generateId(prefix = "id") {
541
- return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
542
- }
543
- function generateMessageId() {
544
- return generateId("msg");
545
- }
546
- function generateToolCallId() {
547
- return generateId("call");
548
- }
549
-
550
- // src/adapters/base.ts
551
- function stringifyForDebug(value) {
552
- return JSON.stringify(
553
- value,
554
- (_key, currentValue) => {
555
- if (typeof currentValue === "bigint") {
556
- return currentValue.toString();
557
- }
558
- if (currentValue instanceof Error) {
559
- return {
560
- name: currentValue.name,
561
- message: currentValue.message,
562
- stack: currentValue.stack
563
- };
564
- }
565
- return currentValue;
566
- },
567
- 2
568
- );
569
- }
570
- function logProviderPayload(provider, label, payload, enabled) {
571
- if (!enabled) {
572
- return;
573
- }
574
- if (label.toLowerCase().includes("stream ")) {
575
- return;
576
- }
577
- try {
578
- console.log(
579
- `[llm-sdk:${provider}] ${label}
580
- ${stringifyForDebug(payload)}`
581
- );
582
- } catch (error) {
583
- console.log(
584
- `[llm-sdk:${provider}] ${label} (failed to stringify payload)`,
585
- error
586
- );
587
- }
588
- }
589
- function parameterToJsonSchema(param) {
590
- const schema = {
591
- type: param.type
592
- };
593
- if (param.description) {
594
- schema.description = param.description;
595
- }
596
- if (param.enum) {
597
- schema.enum = param.enum;
598
- }
599
- if (param.type === "array" && param.items) {
600
- schema.items = parameterToJsonSchema(
601
- param.items
602
- );
603
- }
604
- if (param.type === "object" && param.properties) {
605
- schema.properties = Object.fromEntries(
606
- Object.entries(param.properties).map(([key, prop]) => [
607
- key,
608
- parameterToJsonSchema(
609
- prop
610
- )
611
- ])
612
- );
613
- schema.additionalProperties = false;
614
- }
615
- return schema;
616
- }
617
- function normalizeObjectJsonSchema(schema) {
618
- if (!schema || typeof schema !== "object") {
619
- return {
620
- type: "object",
621
- properties: {},
622
- required: [],
623
- additionalProperties: false
624
- };
625
- }
626
- const normalized = { ...schema };
627
- const type = normalized.type;
628
- if (type === "object") {
629
- const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : {};
630
- normalized.properties = Object.fromEntries(
631
- Object.entries(properties).map(([key, value]) => [
632
- key,
633
- normalizeObjectJsonSchema(value)
634
- ])
635
- );
636
- const propertyKeys = Object.keys(properties);
637
- const required = Array.isArray(normalized.required) ? normalized.required.filter(
638
- (value) => typeof value === "string"
639
- ) : [];
640
- normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
641
- if (normalized.additionalProperties === void 0) {
642
- normalized.additionalProperties = false;
643
- }
644
- } else if (type === "array" && normalized.items && typeof normalized.items === "object") {
645
- normalized.items = normalizeObjectJsonSchema(
646
- normalized.items
647
- );
648
- }
649
- return normalized;
650
- }
651
- function formatTools(actions) {
652
- return actions.map((action) => ({
653
- type: "function",
654
- function: {
655
- name: action.name,
656
- description: action.description,
657
- parameters: {
658
- type: "object",
659
- properties: action.parameters ? Object.fromEntries(
660
- Object.entries(action.parameters).map(([key, param]) => [
661
- key,
662
- parameterToJsonSchema(param)
663
- ])
664
- ) : {},
665
- required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : [],
666
- additionalProperties: false
387
+ yield {
388
+ type: "finish",
389
+ finishReason: mapFinishReason(choice.finish_reason),
390
+ usage: {
391
+ promptTokens: totalPromptTokens,
392
+ completionTokens: totalCompletionTokens,
393
+ totalTokens: totalPromptTokens + totalCompletionTokens
394
+ }
395
+ };
396
+ }
667
397
  }
668
398
  }
669
- }));
670
- }
671
- function hasImageAttachments(message) {
672
- const attachments = message.metadata?.attachments;
673
- return attachments?.some((a) => a.type === "image") ?? false;
674
- }
675
- function attachmentToOpenAIImage(attachment) {
676
- if (attachment.type !== "image") return null;
677
- let imageUrl;
678
- if (attachment.url) {
679
- imageUrl = attachment.url;
680
- } else if (attachment.data) {
681
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
682
- } else {
683
- return null;
684
- }
685
- return {
686
- type: "image_url",
687
- image_url: {
688
- url: imageUrl,
689
- detail: "auto"
690
- }
691
399
  };
692
400
  }
693
- function messageToOpenAIContent(message) {
694
- const attachments = message.metadata?.attachments;
695
- const content = message.content ?? "";
696
- if (!hasImageAttachments(message)) {
697
- return content;
698
- }
699
- const blocks = [];
700
- if (content) {
701
- blocks.push({ type: "text", text: content });
401
+ function mapFinishReason(reason) {
402
+ switch (reason) {
403
+ case "stop":
404
+ return "stop";
405
+ case "length":
406
+ return "length";
407
+ case "tool_calls":
408
+ case "function_call":
409
+ return "tool-calls";
410
+ case "content_filter":
411
+ return "content-filter";
412
+ default:
413
+ return "unknown";
702
414
  }
703
- if (attachments) {
704
- for (const attachment of attachments) {
705
- const imageBlock = attachmentToOpenAIImage(attachment);
706
- if (imageBlock) {
707
- blocks.push(imageBlock);
708
- }
415
+ }
416
+ function formatMessagesForOpenRouter(messages) {
417
+ return messages.map((msg) => {
418
+ switch (msg.role) {
419
+ case "system":
420
+ return { role: "system", content: msg.content };
421
+ case "user":
422
+ if (typeof msg.content === "string") {
423
+ return { role: "user", content: msg.content };
424
+ }
425
+ return {
426
+ role: "user",
427
+ content: msg.content.map((part) => {
428
+ if (part.type === "text") {
429
+ return { type: "text", text: part.text };
430
+ }
431
+ if (part.type === "image") {
432
+ const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
433
+ const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
434
+ return { type: "image_url", image_url: { url, detail: "auto" } };
435
+ }
436
+ return { type: "text", text: "" };
437
+ })
438
+ };
439
+ case "assistant":
440
+ const assistantMsg = {
441
+ role: "assistant",
442
+ content: msg.content
443
+ };
444
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
445
+ assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
446
+ id: tc.id,
447
+ type: "function",
448
+ function: {
449
+ name: tc.name,
450
+ arguments: JSON.stringify(tc.args)
451
+ }
452
+ }));
453
+ }
454
+ return assistantMsg;
455
+ case "tool":
456
+ return {
457
+ role: "tool",
458
+ tool_call_id: msg.toolCallId,
459
+ content: msg.content
460
+ };
461
+ default:
462
+ return msg;
709
463
  }
710
- }
711
- return blocks;
464
+ });
712
465
  }
713
- function formatMessagesForOpenAI(messages, systemPrompt) {
714
- const formatted = [];
715
- if (systemPrompt) {
716
- formatted.push({ role: "system", content: systemPrompt });
466
+ async function fetchOpenRouterModels(apiKey) {
467
+ const headers = {
468
+ "Content-Type": "application/json"
469
+ };
470
+ if (apiKey) {
471
+ headers["Authorization"] = `Bearer ${apiKey}`;
717
472
  }
718
- for (const msg of messages) {
719
- if (msg.role === "system") {
720
- formatted.push({ role: "system", content: msg.content ?? "" });
721
- } else if (msg.role === "user") {
722
- formatted.push({
723
- role: "user",
724
- content: messageToOpenAIContent(msg)
725
- });
726
- } else if (msg.role === "assistant") {
727
- const hasToolCalls = msg.tool_calls && msg.tool_calls.length > 0;
728
- const assistantMsg = {
729
- role: "assistant",
730
- // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
731
- content: hasToolCalls ? msg.content || null : msg.content
732
- };
733
- if (hasToolCalls) {
734
- assistantMsg.tool_calls = msg.tool_calls;
735
- }
736
- formatted.push(assistantMsg);
737
- } else if (msg.role === "tool" && msg.tool_call_id) {
738
- formatted.push({
739
- role: "tool",
740
- content: msg.content ?? "",
741
- tool_call_id: msg.tool_call_id
742
- });
743
- }
473
+ const response = await fetch("https://openrouter.ai/api/v1/models", {
474
+ headers
475
+ });
476
+ if (!response.ok) {
477
+ throw new Error(`Failed to fetch models: ${response.statusText}`);
744
478
  }
745
- return formatted;
479
+ const data = await response.json();
480
+ return data.data || [];
481
+ }
482
+ async function searchOpenRouterModels(query, apiKey) {
483
+ const models = await fetchOpenRouterModels(apiKey);
484
+ const lowerQuery = query.toLowerCase();
485
+ return models.filter(
486
+ (model) => model.id.toLowerCase().includes(lowerQuery) || model.name.toLowerCase().includes(lowerQuery)
487
+ );
488
+ }
489
+
490
+ // src/core/utils.ts
491
+ function generateId(prefix = "id") {
492
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
493
+ }
494
+ function generateMessageId() {
495
+ return generateId("msg");
496
+ }
497
+ function generateToolCallId() {
498
+ return generateId("call");
746
499
  }
747
500
 
748
501
  // src/adapters/openai.ts
@@ -757,7 +510,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
757
510
  if (baseUrl.includes("generativelanguage.googleapis.com")) return "google";
758
511
  if (baseUrl.includes("x.ai")) return "xai";
759
512
  if (baseUrl.includes("azure")) return "azure";
760
- if (baseUrl.includes("openrouter.ai")) return "openrouter";
761
513
  return "openai";
762
514
  }
763
515
  async getClient() {
@@ -857,259 +609,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
857
609
  rawResponse: response
858
610
  };
859
611
  }
860
- /**
861
- * OpenAI reasoning models on OpenRouter (o1/o3/o4/gpt-5 family) hide their
862
- * reasoning content on the chat-completions endpoint. To surface reasoning
863
- * SUMMARIES (not raw CoT, which OpenAI never exposes) we have to use the
864
- * Responses API, which streams `response.reasoning_summary_text.delta` events.
865
- *
866
- * Match by prefix on the OpenRouter model id. Excludes openai/gpt-4o,
867
- * openai/gpt-4.1, openai/chatgpt-* — those continue on chat-completions.
868
- */
869
- isOpenAIReasoningModelOnOpenRouter(activeModel) {
870
- if (this.provider !== "openrouter") return false;
871
- return activeModel.startsWith("openai/o1") || activeModel.startsWith("openai/o3") || activeModel.startsWith("openai/o4") || activeModel.startsWith("openai/gpt-5");
872
- }
873
- /**
874
- * Convert ActionDefinition[] (the chat-completions tool shape used by the
875
- * adapter) to the Responses API tool shape.
876
- */
877
- buildResponsesToolsFromActions(actions) {
878
- if (!actions || actions.length === 0) return void 0;
879
- const formatted = formatTools(actions);
880
- return formatted.map((t) => ({
881
- type: "function",
882
- name: t.function.name,
883
- description: t.function.description,
884
- parameters: t.function.parameters
885
- }));
886
- }
887
- /**
888
- * Streaming Responses API path for OpenAI reasoning models on OpenRouter.
889
- *
890
- * Maps Responses API SSE events back to the same StreamEvent shapes the
891
- * chat-completions path emits, so downstream consumers (processChunk.ts,
892
- * frontend tool handlers, plan approval, specialist delegations) see
893
- * identical events regardless of which path produced them.
894
- *
895
- * response.reasoning_summary_text.delta → thinking:start (once) + thinking:delta
896
- * response.output_text.delta → message:delta
897
- * response.output_item.added (function_call) → action:start (queued buffer)
898
- * response.function_call_arguments.delta → action:args (progressive)
899
- * response.output_item.done (function_call) → final action:args + action:end
900
- * response.completed → message:end + done(usage)
901
- * response.error → error
902
- */
903
- async *streamWithResponsesAPI(request, activeModel, messageId) {
904
- const client = await this.getClient();
905
- const maxTokensValue = request.config?.maxTokens ?? this.config.maxTokens;
906
- const payload = {
907
- model: activeModel,
908
- input: this.buildResponsesInput(request),
909
- stream: true,
910
- reasoning: {
911
- effort: request.config?.reasoningEffort ?? "medium",
912
- summary: "auto"
913
- }
914
- };
915
- if (request.systemPrompt) payload.instructions = request.systemPrompt;
916
- if (typeof maxTokensValue === "number")
917
- payload.max_output_tokens = maxTokensValue;
918
- const tools = this.buildResponsesToolsFromActions(request.actions);
919
- if (tools && tools.length > 0) payload.tools = tools;
920
- logProviderPayload(
921
- "openai",
922
- "responses-api request payload",
923
- payload,
924
- request.debug
925
- );
926
- let stream;
927
- try {
928
- stream = await client.responses.create(payload);
929
- } catch (error) {
930
- yield {
931
- type: "error",
932
- message: error instanceof Error ? error.message : "Unknown error",
933
- code: "OPENAI_RESPONSES_ERROR"
934
- };
935
- return;
936
- }
937
- const toolBuffers = /* @__PURE__ */ new Map();
938
- const itemIdToCallId = /* @__PURE__ */ new Map();
939
- let usage;
940
- let reasoningStarted = false;
941
- let textStarted = false;
942
- let finishEmitted = false;
943
- const resolveCallId = (evt) => {
944
- if (evt?.call_id) return evt.call_id;
945
- if (evt?.item_id) return itemIdToCallId.get(evt.item_id) ?? evt.item_id;
946
- if (evt?.item?.call_id) return evt.item.call_id;
947
- if (evt?.item?.id) return evt.item.id;
948
- return "";
949
- };
950
- try {
951
- for await (const evt of stream) {
952
- logProviderPayload(
953
- "openai",
954
- "responses-api stream chunk",
955
- evt,
956
- request.debug
957
- );
958
- if (request.signal?.aborted) break;
959
- const t = evt?.type ?? "";
960
- if (t === "response.reasoning_summary_text.delta") {
961
- const delta = evt.delta ?? "";
962
- if (!delta) continue;
963
- if (!reasoningStarted) {
964
- yield { type: "thinking:start" };
965
- reasoningStarted = true;
966
- }
967
- yield { type: "thinking:delta", content: delta };
968
- continue;
969
- }
970
- if (t === "response.reasoning_summary_text.done" || t === "response.reasoning.done") {
971
- continue;
972
- }
973
- if (t === "response.output_text.delta") {
974
- const text = evt.delta ?? "";
975
- if (!text) continue;
976
- if (reasoningStarted && !textStarted) {
977
- yield { type: "thinking:end" };
978
- textStarted = true;
979
- }
980
- yield { type: "message:delta", content: text };
981
- continue;
982
- }
983
- if (t === "response.output_item.added") {
984
- const item = evt.item;
985
- if (item?.type === "function_call") {
986
- const callId = item.call_id ?? item.id ?? "";
987
- const itemId = item.id ?? callId;
988
- if (callId) {
989
- if (itemId && itemId !== callId) {
990
- itemIdToCallId.set(itemId, callId);
991
- }
992
- if (!toolBuffers.has(callId)) {
993
- toolBuffers.set(callId, {
994
- id: callId,
995
- name: item.name ?? "",
996
- arguments: item.arguments ?? "",
997
- emittedStart: false
998
- });
999
- }
1000
- const buf = toolBuffers.get(callId);
1001
- if (buf.name && !buf.emittedStart) {
1002
- yield { type: "action:start", id: buf.id, name: buf.name };
1003
- buf.emittedStart = true;
1004
- }
1005
- }
1006
- }
1007
- continue;
1008
- }
1009
- if (t === "response.function_call_arguments.delta") {
1010
- const callId = resolveCallId(evt);
1011
- const delta = evt.delta ?? "";
1012
- if (!callId || !delta) continue;
1013
- let buf = toolBuffers.get(callId);
1014
- if (!buf) {
1015
- buf = { id: callId, name: "", arguments: "", emittedStart: false };
1016
- toolBuffers.set(callId, buf);
1017
- }
1018
- buf.arguments += delta;
1019
- if (buf.emittedStart) {
1020
- yield {
1021
- type: "action:args",
1022
- id: buf.id,
1023
- args: buf.arguments
1024
- };
1025
- }
1026
- continue;
1027
- }
1028
- if (t === "response.output_item.done") {
1029
- const item = evt.item;
1030
- if (item?.type === "function_call") {
1031
- const callId = item.call_id ?? item.id ?? "";
1032
- const buf = toolBuffers.get(callId);
1033
- const name = buf?.name || item.name || "";
1034
- const argsStr = buf?.arguments || item.arguments || "{}";
1035
- if (callId && name) {
1036
- if (!buf?.emittedStart) {
1037
- yield { type: "action:start", id: callId, name };
1038
- }
1039
- yield {
1040
- type: "action:args",
1041
- id: callId,
1042
- args: argsStr
1043
- };
1044
- yield {
1045
- type: "action:end",
1046
- id: callId,
1047
- name
1048
- };
1049
- }
1050
- toolBuffers.delete(callId);
1051
- }
1052
- continue;
1053
- }
1054
- if (t === "response.completed") {
1055
- const u = evt.response?.usage;
1056
- if (u) {
1057
- usage = {
1058
- prompt_tokens: u.input_tokens ?? 0,
1059
- completion_tokens: u.output_tokens ?? 0,
1060
- total_tokens: u.total_tokens ?? (u.input_tokens ?? 0) + (u.output_tokens ?? 0)
1061
- };
1062
- }
1063
- for (const buf of toolBuffers.values()) {
1064
- if (!buf.id || !buf.name) continue;
1065
- if (!buf.emittedStart) {
1066
- yield { type: "action:start", id: buf.id, name: buf.name };
1067
- }
1068
- yield {
1069
- type: "action:args",
1070
- id: buf.id,
1071
- args: buf.arguments || "{}"
1072
- };
1073
- yield { type: "action:end", id: buf.id, name: buf.name };
1074
- }
1075
- toolBuffers.clear();
1076
- if (reasoningStarted && !textStarted) {
1077
- yield { type: "thinking:end" };
1078
- }
1079
- yield { type: "message:end" };
1080
- yield { type: "done", usage };
1081
- finishEmitted = true;
1082
- continue;
1083
- }
1084
- if (t === "response.error" || t === "error") {
1085
- const msg = evt.error?.message || evt.message || "Responses API error";
1086
- yield {
1087
- type: "error",
1088
- message: msg,
1089
- code: "OPENAI_RESPONSES_ERROR"
1090
- };
1091
- return;
1092
- }
1093
- }
1094
- } catch (error) {
1095
- yield {
1096
- type: "error",
1097
- message: error instanceof Error ? error.message : "Unknown error",
1098
- code: "OPENAI_RESPONSES_ERROR"
1099
- };
1100
- return;
1101
- }
1102
- if (!finishEmitted) {
1103
- if (reasoningStarted && !textStarted) {
1104
- yield { type: "thinking:end" };
1105
- }
1106
- yield { type: "message:end" };
1107
- yield { type: "done", usage };
1108
- }
1109
- }
1110
612
  async completeWithResponses(request) {
1111
613
  const client = await this.getClient();
1112
614
  const openaiToolOptions = request.providerToolOptions?.openai;
615
+ const responsesTextFormat = toOpenAIResponsesTextFormat(
616
+ request.config?.responseFormat
617
+ );
1113
618
  const payload = {
1114
619
  model: request.config?.model || this.model,
1115
620
  instructions: request.systemPrompt,
@@ -1119,6 +624,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
1119
624
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
1120
625
  temperature: request.config?.temperature ?? this.config.temperature,
1121
626
  max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
627
+ ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
1122
628
  stream: false
1123
629
  };
1124
630
  logProviderPayload("openai", "request payload", payload, request.debug);
@@ -1240,37 +746,21 @@ var OpenAIAdapter = class _OpenAIAdapter {
1240
746
  name: openaiToolOptions.toolChoice.name
1241
747
  }
1242
748
  } : openaiToolOptions?.toolChoice;
1243
- const isOpenRouter = this.provider === "openrouter";
1244
- const activeModel = request.config?.model || this.model;
1245
- const modelSlug = activeModel.replace("openai/", "");
1246
- const isOSeries = /^o[1-9]/.test(modelSlug);
1247
- const isOpenAIOnOpenRouter = isOpenRouter && activeModel.startsWith("openai/");
1248
- if (!this.config.disableThinking && this.isOpenAIReasoningModelOnOpenRouter(activeModel)) {
1249
- yield* this.streamWithResponsesAPI(request, activeModel, messageId);
1250
- return;
1251
- }
1252
- const maxTokensValue = request.config?.maxTokens ?? this.config.maxTokens;
749
+ const modelIdForPayload = request.config?.model || this.model;
1253
750
  const payload = {
1254
- model: activeModel,
751
+ model: modelIdForPayload,
1255
752
  messages,
1256
753
  tools: tools.length > 0 ? tools : void 0,
1257
754
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1258
755
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
756
+ ...buildOpenAITokenParams(
757
+ modelIdForPayload,
758
+ request.config?.maxTokens ?? this.config.maxTokens,
759
+ request.config?.temperature ?? this.config.temperature
760
+ ),
761
+ response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1259
762
  stream: true,
1260
- stream_options: { include_usage: true },
1261
- // o-series: use max_completion_tokens + reasoning_effort, no temperature
1262
- // regular models: use max_tokens + temperature
1263
- ...isOSeries ? {
1264
- max_completion_tokens: maxTokensValue,
1265
- reasoning_effort: request.config?.reasoningEffort ?? "medium"
1266
- } : {
1267
- temperature: request.config?.temperature ?? this.config.temperature,
1268
- max_tokens: maxTokensValue
1269
- },
1270
- // Non-OpenAI OpenRouter models support OR's reasoning/include_reasoning params.
1271
- // When disableThinking=true we must explicitly send include_reasoning:false because
1272
- // models like Qwen3 and DeepSeek-R1 reason by default even without the reasoning param.
1273
- ...isOpenRouter && !isOpenAIOnOpenRouter ? this.config.disableThinking ? { include_reasoning: false } : { reasoning: { max_tokens: 8e3 }, include_reasoning: true } : {}
763
+ stream_options: { include_usage: true }
1274
764
  };
1275
765
  logProviderPayload("openai", "request payload", payload, request.debug);
1276
766
  const stream = await client.chat.completions.create(payload);
@@ -1278,7 +768,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
1278
768
  const collectedCitations = [];
1279
769
  let citationIndex = 0;
1280
770
  let usage;
1281
- let adapterReasoningStarted = false;
1282
771
  for await (const chunk of stream) {
1283
772
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
1284
773
  if (request.signal?.aborted) {
@@ -1289,22 +778,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
1289
778
  if (delta?.content) {
1290
779
  yield { type: "message:delta", content: delta.content };
1291
780
  }
1292
- if (isOpenRouter) {
1293
- const rc = delta?.reasoning_content ?? delta?.reasoning ?? null;
1294
- if (rc) {
1295
- const rcText = typeof rc === "string" ? rc : Array.isArray(rc) && rc[0]?.text ? rc[0].text : "";
1296
- if (rcText) {
1297
- if (!adapterReasoningStarted) {
1298
- yield { type: "thinking:start" };
1299
- adapterReasoningStarted = true;
1300
- }
1301
- yield { type: "thinking:delta", content: rcText };
1302
- }
1303
- } else if (adapterReasoningStarted && (delta?.content || choice?.finish_reason)) {
1304
- yield { type: "thinking:end" };
1305
- adapterReasoningStarted = false;
1306
- }
1307
- }
1308
781
  const annotations = delta?.annotations;
1309
782
  if (annotations && annotations.length > 0) {
1310
783
  for (const annotation of annotations) {
@@ -1352,11 +825,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
1352
825
  };
1353
826
  } else if (currentToolCall && toolCall.function?.arguments) {
1354
827
  currentToolCall.arguments += toolCall.function.arguments;
1355
- yield {
1356
- type: "action:args",
1357
- id: currentToolCall.id,
1358
- args: currentToolCall.arguments
1359
- };
1360
828
  }
1361
829
  }
1362
830
  }
@@ -1432,24 +900,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1432
900
  name: openaiToolOptions.toolChoice.name
1433
901
  }
1434
902
  } : openaiToolOptions?.toolChoice;
1435
- const activeModel2 = request.config?.model || this.model;
1436
- const modelSlug2 = activeModel2.replace("openai/", "");
1437
- const isOSeries2 = /^o[1-9]/.test(modelSlug2);
1438
- const maxTokensValue2 = request.config?.maxTokens ?? this.config.maxTokens;
903
+ const modelIdForCompletePayload = request.config?.model || this.model;
1439
904
  const payload = {
1440
- model: activeModel2,
905
+ model: modelIdForCompletePayload,
1441
906
  messages,
1442
907
  tools: tools.length > 0 ? tools : void 0,
1443
908
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1444
909
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1445
- stream: false,
1446
- ...isOSeries2 ? {
1447
- max_completion_tokens: maxTokensValue2,
1448
- reasoning_effort: request.config?.reasoningEffort ?? "medium"
1449
- } : {
1450
- temperature: request.config?.temperature ?? this.config.temperature,
1451
- max_tokens: maxTokensValue2
1452
- }
910
+ ...buildOpenAITokenParams(
911
+ modelIdForCompletePayload,
912
+ request.config?.maxTokens ?? this.config.maxTokens,
913
+ request.config?.temperature ?? this.config.temperature
914
+ ),
915
+ response_format: toOpenAIResponseFormat(request.config?.responseFormat),
916
+ stream: false
1453
917
  };
1454
918
  logProviderPayload("openai", "request payload", payload, request.debug);
1455
919
  const response = await client.chat.completions.create(payload);
@@ -1532,8 +996,7 @@ function createOpenRouter(config = {}) {
1532
996
  return createOpenAIAdapter({
1533
997
  apiKey,
1534
998
  model: modelId,
1535
- baseUrl,
1536
- disableThinking: config.disableThinking
999
+ baseUrl
1537
1000
  });
1538
1001
  };
1539
1002
  const getCapabilities = (modelId) => {