@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/xai/provider.ts
2
231
  var XAI_MODELS = {
3
232
  // Grok 4.1 Fast (Latest - December 2025)
@@ -51,8 +280,8 @@ function xai(modelId, options = {}) {
51
280
  supportsVision: modelConfig.vision,
52
281
  supportsTools: modelConfig.tools,
53
282
  supportsStreaming: true,
54
- supportsJsonMode: false,
55
- // xAI doesn't support JSON mode yet
283
+ supportsJsonMode: true,
284
+ // OpenAI-compatible `response_format`
56
285
  supportsThinking: false,
57
286
  supportsPDF: false,
58
287
  maxTokens: modelConfig.maxTokens,
@@ -66,7 +295,8 @@ function xai(modelId, options = {}) {
66
295
  messages,
67
296
  tools: params.tools,
68
297
  temperature: params.temperature,
69
- max_tokens: params.maxTokens
298
+ max_tokens: params.maxTokens,
299
+ response_format: toOpenAIResponseFormat(params.responseFormat)
70
300
  });
71
301
  const choice = response.choices[0];
72
302
  const message = choice.message;
@@ -98,6 +328,7 @@ function xai(modelId, options = {}) {
98
328
  tools: params.tools,
99
329
  temperature: params.temperature,
100
330
  max_tokens: params.maxTokens,
331
+ response_format: toOpenAIResponseFormat(params.responseFormat),
101
332
  stream: true
102
333
  });
103
334
  let currentToolCall = null;
@@ -243,204 +474,6 @@ function generateToolCallId() {
243
474
  return generateId("call");
244
475
  }
245
476
 
246
- // src/adapters/base.ts
247
- function stringifyForDebug(value) {
248
- return JSON.stringify(
249
- value,
250
- (_key, currentValue) => {
251
- if (typeof currentValue === "bigint") {
252
- return currentValue.toString();
253
- }
254
- if (currentValue instanceof Error) {
255
- return {
256
- name: currentValue.name,
257
- message: currentValue.message,
258
- stack: currentValue.stack
259
- };
260
- }
261
- return currentValue;
262
- },
263
- 2
264
- );
265
- }
266
- function logProviderPayload(provider, label, payload, enabled) {
267
- if (!enabled) {
268
- return;
269
- }
270
- if (label.toLowerCase().includes("stream ")) {
271
- return;
272
- }
273
- try {
274
- console.log(
275
- `[llm-sdk:${provider}] ${label}
276
- ${stringifyForDebug(payload)}`
277
- );
278
- } catch (error) {
279
- console.log(
280
- `[llm-sdk:${provider}] ${label} (failed to stringify payload)`,
281
- error
282
- );
283
- }
284
- }
285
- function parameterToJsonSchema(param) {
286
- const schema = {
287
- type: param.type
288
- };
289
- if (param.description) {
290
- schema.description = param.description;
291
- }
292
- if (param.enum) {
293
- schema.enum = param.enum;
294
- }
295
- if (param.type === "array" && param.items) {
296
- schema.items = parameterToJsonSchema(
297
- param.items
298
- );
299
- }
300
- if (param.type === "object" && param.properties) {
301
- schema.properties = Object.fromEntries(
302
- Object.entries(param.properties).map(([key, prop]) => [
303
- key,
304
- parameterToJsonSchema(
305
- prop
306
- )
307
- ])
308
- );
309
- schema.additionalProperties = false;
310
- }
311
- return schema;
312
- }
313
- function normalizeObjectJsonSchema(schema) {
314
- if (!schema || typeof schema !== "object") {
315
- return {
316
- type: "object",
317
- properties: {},
318
- required: [],
319
- additionalProperties: false
320
- };
321
- }
322
- const normalized = { ...schema };
323
- const type = normalized.type;
324
- if (type === "object") {
325
- const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : {};
326
- normalized.properties = Object.fromEntries(
327
- Object.entries(properties).map(([key, value]) => [
328
- key,
329
- normalizeObjectJsonSchema(value)
330
- ])
331
- );
332
- const propertyKeys = Object.keys(properties);
333
- const required = Array.isArray(normalized.required) ? normalized.required.filter(
334
- (value) => typeof value === "string"
335
- ) : [];
336
- normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
337
- if (normalized.additionalProperties === void 0) {
338
- normalized.additionalProperties = false;
339
- }
340
- } else if (type === "array" && normalized.items && typeof normalized.items === "object") {
341
- normalized.items = normalizeObjectJsonSchema(
342
- normalized.items
343
- );
344
- }
345
- return normalized;
346
- }
347
- function formatTools(actions) {
348
- return actions.map((action) => ({
349
- type: "function",
350
- function: {
351
- name: action.name,
352
- description: action.description,
353
- parameters: {
354
- type: "object",
355
- properties: action.parameters ? Object.fromEntries(
356
- Object.entries(action.parameters).map(([key, param]) => [
357
- key,
358
- parameterToJsonSchema(param)
359
- ])
360
- ) : {},
361
- required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : [],
362
- additionalProperties: false
363
- }
364
- }
365
- }));
366
- }
367
- function hasImageAttachments(message) {
368
- const attachments = message.metadata?.attachments;
369
- return attachments?.some((a) => a.type === "image") ?? false;
370
- }
371
- function attachmentToOpenAIImage(attachment) {
372
- if (attachment.type !== "image") return null;
373
- let imageUrl;
374
- if (attachment.url) {
375
- imageUrl = attachment.url;
376
- } else if (attachment.data) {
377
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
378
- } else {
379
- return null;
380
- }
381
- return {
382
- type: "image_url",
383
- image_url: {
384
- url: imageUrl,
385
- detail: "auto"
386
- }
387
- };
388
- }
389
- function messageToOpenAIContent(message) {
390
- const attachments = message.metadata?.attachments;
391
- const content = message.content ?? "";
392
- if (!hasImageAttachments(message)) {
393
- return content;
394
- }
395
- const blocks = [];
396
- if (content) {
397
- blocks.push({ type: "text", text: content });
398
- }
399
- if (attachments) {
400
- for (const attachment of attachments) {
401
- const imageBlock = attachmentToOpenAIImage(attachment);
402
- if (imageBlock) {
403
- blocks.push(imageBlock);
404
- }
405
- }
406
- }
407
- return blocks;
408
- }
409
- function formatMessagesForOpenAI(messages, systemPrompt) {
410
- const formatted = [];
411
- if (systemPrompt) {
412
- formatted.push({ role: "system", content: systemPrompt });
413
- }
414
- for (const msg of messages) {
415
- if (msg.role === "system") {
416
- formatted.push({ role: "system", content: msg.content ?? "" });
417
- } else if (msg.role === "user") {
418
- formatted.push({
419
- role: "user",
420
- content: messageToOpenAIContent(msg)
421
- });
422
- } else if (msg.role === "assistant") {
423
- const hasToolCalls = msg.tool_calls && msg.tool_calls.length > 0;
424
- const assistantMsg = {
425
- role: "assistant",
426
- // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
427
- content: hasToolCalls ? msg.content || null : msg.content
428
- };
429
- if (hasToolCalls) {
430
- assistantMsg.tool_calls = msg.tool_calls;
431
- }
432
- formatted.push(assistantMsg);
433
- } else if (msg.role === "tool" && msg.tool_call_id) {
434
- formatted.push({
435
- role: "tool",
436
- content: msg.content ?? "",
437
- tool_call_id: msg.tool_call_id
438
- });
439
- }
440
- }
441
- return formatted;
442
- }
443
-
444
477
  // src/adapters/openai.ts
445
478
  var OpenAIAdapter = class _OpenAIAdapter {
446
479
  constructor(config) {
@@ -453,7 +486,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
453
486
  if (baseUrl.includes("generativelanguage.googleapis.com")) return "google";
454
487
  if (baseUrl.includes("x.ai")) return "xai";
455
488
  if (baseUrl.includes("azure")) return "azure";
456
- if (baseUrl.includes("openrouter.ai")) return "openrouter";
457
489
  return "openai";
458
490
  }
459
491
  async getClient() {
@@ -553,259 +585,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
553
585
  rawResponse: response
554
586
  };
555
587
  }
556
- /**
557
- * OpenAI reasoning models on OpenRouter (o1/o3/o4/gpt-5 family) hide their
558
- * reasoning content on the chat-completions endpoint. To surface reasoning
559
- * SUMMARIES (not raw CoT, which OpenAI never exposes) we have to use the
560
- * Responses API, which streams `response.reasoning_summary_text.delta` events.
561
- *
562
- * Match by prefix on the OpenRouter model id. Excludes openai/gpt-4o,
563
- * openai/gpt-4.1, openai/chatgpt-* — those continue on chat-completions.
564
- */
565
- isOpenAIReasoningModelOnOpenRouter(activeModel) {
566
- if (this.provider !== "openrouter") return false;
567
- return activeModel.startsWith("openai/o1") || activeModel.startsWith("openai/o3") || activeModel.startsWith("openai/o4") || activeModel.startsWith("openai/gpt-5");
568
- }
569
- /**
570
- * Convert ActionDefinition[] (the chat-completions tool shape used by the
571
- * adapter) to the Responses API tool shape.
572
- */
573
- buildResponsesToolsFromActions(actions) {
574
- if (!actions || actions.length === 0) return void 0;
575
- const formatted = formatTools(actions);
576
- return formatted.map((t) => ({
577
- type: "function",
578
- name: t.function.name,
579
- description: t.function.description,
580
- parameters: t.function.parameters
581
- }));
582
- }
583
- /**
584
- * Streaming Responses API path for OpenAI reasoning models on OpenRouter.
585
- *
586
- * Maps Responses API SSE events back to the same StreamEvent shapes the
587
- * chat-completions path emits, so downstream consumers (processChunk.ts,
588
- * frontend tool handlers, plan approval, specialist delegations) see
589
- * identical events regardless of which path produced them.
590
- *
591
- * response.reasoning_summary_text.delta → thinking:start (once) + thinking:delta
592
- * response.output_text.delta → message:delta
593
- * response.output_item.added (function_call) → action:start (queued buffer)
594
- * response.function_call_arguments.delta → action:args (progressive)
595
- * response.output_item.done (function_call) → final action:args + action:end
596
- * response.completed → message:end + done(usage)
597
- * response.error → error
598
- */
599
- async *streamWithResponsesAPI(request, activeModel, messageId) {
600
- const client = await this.getClient();
601
- const maxTokensValue = request.config?.maxTokens ?? this.config.maxTokens;
602
- const payload = {
603
- model: activeModel,
604
- input: this.buildResponsesInput(request),
605
- stream: true,
606
- reasoning: {
607
- effort: request.config?.reasoningEffort ?? "medium",
608
- summary: "auto"
609
- }
610
- };
611
- if (request.systemPrompt) payload.instructions = request.systemPrompt;
612
- if (typeof maxTokensValue === "number")
613
- payload.max_output_tokens = maxTokensValue;
614
- const tools = this.buildResponsesToolsFromActions(request.actions);
615
- if (tools && tools.length > 0) payload.tools = tools;
616
- logProviderPayload(
617
- "openai",
618
- "responses-api request payload",
619
- payload,
620
- request.debug
621
- );
622
- let stream;
623
- try {
624
- stream = await client.responses.create(payload);
625
- } catch (error) {
626
- yield {
627
- type: "error",
628
- message: error instanceof Error ? error.message : "Unknown error",
629
- code: "OPENAI_RESPONSES_ERROR"
630
- };
631
- return;
632
- }
633
- const toolBuffers = /* @__PURE__ */ new Map();
634
- const itemIdToCallId = /* @__PURE__ */ new Map();
635
- let usage;
636
- let reasoningStarted = false;
637
- let textStarted = false;
638
- let finishEmitted = false;
639
- const resolveCallId = (evt) => {
640
- if (evt?.call_id) return evt.call_id;
641
- if (evt?.item_id) return itemIdToCallId.get(evt.item_id) ?? evt.item_id;
642
- if (evt?.item?.call_id) return evt.item.call_id;
643
- if (evt?.item?.id) return evt.item.id;
644
- return "";
645
- };
646
- try {
647
- for await (const evt of stream) {
648
- logProviderPayload(
649
- "openai",
650
- "responses-api stream chunk",
651
- evt,
652
- request.debug
653
- );
654
- if (request.signal?.aborted) break;
655
- const t = evt?.type ?? "";
656
- if (t === "response.reasoning_summary_text.delta") {
657
- const delta = evt.delta ?? "";
658
- if (!delta) continue;
659
- if (!reasoningStarted) {
660
- yield { type: "thinking:start" };
661
- reasoningStarted = true;
662
- }
663
- yield { type: "thinking:delta", content: delta };
664
- continue;
665
- }
666
- if (t === "response.reasoning_summary_text.done" || t === "response.reasoning.done") {
667
- continue;
668
- }
669
- if (t === "response.output_text.delta") {
670
- const text = evt.delta ?? "";
671
- if (!text) continue;
672
- if (reasoningStarted && !textStarted) {
673
- yield { type: "thinking:end" };
674
- textStarted = true;
675
- }
676
- yield { type: "message:delta", content: text };
677
- continue;
678
- }
679
- if (t === "response.output_item.added") {
680
- const item = evt.item;
681
- if (item?.type === "function_call") {
682
- const callId = item.call_id ?? item.id ?? "";
683
- const itemId = item.id ?? callId;
684
- if (callId) {
685
- if (itemId && itemId !== callId) {
686
- itemIdToCallId.set(itemId, callId);
687
- }
688
- if (!toolBuffers.has(callId)) {
689
- toolBuffers.set(callId, {
690
- id: callId,
691
- name: item.name ?? "",
692
- arguments: item.arguments ?? "",
693
- emittedStart: false
694
- });
695
- }
696
- const buf = toolBuffers.get(callId);
697
- if (buf.name && !buf.emittedStart) {
698
- yield { type: "action:start", id: buf.id, name: buf.name };
699
- buf.emittedStart = true;
700
- }
701
- }
702
- }
703
- continue;
704
- }
705
- if (t === "response.function_call_arguments.delta") {
706
- const callId = resolveCallId(evt);
707
- const delta = evt.delta ?? "";
708
- if (!callId || !delta) continue;
709
- let buf = toolBuffers.get(callId);
710
- if (!buf) {
711
- buf = { id: callId, name: "", arguments: "", emittedStart: false };
712
- toolBuffers.set(callId, buf);
713
- }
714
- buf.arguments += delta;
715
- if (buf.emittedStart) {
716
- yield {
717
- type: "action:args",
718
- id: buf.id,
719
- args: buf.arguments
720
- };
721
- }
722
- continue;
723
- }
724
- if (t === "response.output_item.done") {
725
- const item = evt.item;
726
- if (item?.type === "function_call") {
727
- const callId = item.call_id ?? item.id ?? "";
728
- const buf = toolBuffers.get(callId);
729
- const name = buf?.name || item.name || "";
730
- const argsStr = buf?.arguments || item.arguments || "{}";
731
- if (callId && name) {
732
- if (!buf?.emittedStart) {
733
- yield { type: "action:start", id: callId, name };
734
- }
735
- yield {
736
- type: "action:args",
737
- id: callId,
738
- args: argsStr
739
- };
740
- yield {
741
- type: "action:end",
742
- id: callId,
743
- name
744
- };
745
- }
746
- toolBuffers.delete(callId);
747
- }
748
- continue;
749
- }
750
- if (t === "response.completed") {
751
- const u = evt.response?.usage;
752
- if (u) {
753
- usage = {
754
- prompt_tokens: u.input_tokens ?? 0,
755
- completion_tokens: u.output_tokens ?? 0,
756
- total_tokens: u.total_tokens ?? (u.input_tokens ?? 0) + (u.output_tokens ?? 0)
757
- };
758
- }
759
- for (const buf of toolBuffers.values()) {
760
- if (!buf.id || !buf.name) continue;
761
- if (!buf.emittedStart) {
762
- yield { type: "action:start", id: buf.id, name: buf.name };
763
- }
764
- yield {
765
- type: "action:args",
766
- id: buf.id,
767
- args: buf.arguments || "{}"
768
- };
769
- yield { type: "action:end", id: buf.id, name: buf.name };
770
- }
771
- toolBuffers.clear();
772
- if (reasoningStarted && !textStarted) {
773
- yield { type: "thinking:end" };
774
- }
775
- yield { type: "message:end" };
776
- yield { type: "done", usage };
777
- finishEmitted = true;
778
- continue;
779
- }
780
- if (t === "response.error" || t === "error") {
781
- const msg = evt.error?.message || evt.message || "Responses API error";
782
- yield {
783
- type: "error",
784
- message: msg,
785
- code: "OPENAI_RESPONSES_ERROR"
786
- };
787
- return;
788
- }
789
- }
790
- } catch (error) {
791
- yield {
792
- type: "error",
793
- message: error instanceof Error ? error.message : "Unknown error",
794
- code: "OPENAI_RESPONSES_ERROR"
795
- };
796
- return;
797
- }
798
- if (!finishEmitted) {
799
- if (reasoningStarted && !textStarted) {
800
- yield { type: "thinking:end" };
801
- }
802
- yield { type: "message:end" };
803
- yield { type: "done", usage };
804
- }
805
- }
806
588
  async completeWithResponses(request) {
807
589
  const client = await this.getClient();
808
590
  const openaiToolOptions = request.providerToolOptions?.openai;
591
+ const responsesTextFormat = toOpenAIResponsesTextFormat(
592
+ request.config?.responseFormat
593
+ );
809
594
  const payload = {
810
595
  model: request.config?.model || this.model,
811
596
  instructions: request.systemPrompt,
@@ -815,6 +600,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
815
600
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
816
601
  temperature: request.config?.temperature ?? this.config.temperature,
817
602
  max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
603
+ ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
818
604
  stream: false
819
605
  };
820
606
  logProviderPayload("openai", "request payload", payload, request.debug);
@@ -936,37 +722,21 @@ var OpenAIAdapter = class _OpenAIAdapter {
936
722
  name: openaiToolOptions.toolChoice.name
937
723
  }
938
724
  } : openaiToolOptions?.toolChoice;
939
- const isOpenRouter = this.provider === "openrouter";
940
- const activeModel = request.config?.model || this.model;
941
- const modelSlug = activeModel.replace("openai/", "");
942
- const isOSeries = /^o[1-9]/.test(modelSlug);
943
- const isOpenAIOnOpenRouter = isOpenRouter && activeModel.startsWith("openai/");
944
- if (!this.config.disableThinking && this.isOpenAIReasoningModelOnOpenRouter(activeModel)) {
945
- yield* this.streamWithResponsesAPI(request, activeModel, messageId);
946
- return;
947
- }
948
- const maxTokensValue = request.config?.maxTokens ?? this.config.maxTokens;
725
+ const modelIdForPayload = request.config?.model || this.model;
949
726
  const payload = {
950
- model: activeModel,
727
+ model: modelIdForPayload,
951
728
  messages,
952
729
  tools: tools.length > 0 ? tools : void 0,
953
730
  tool_choice: tools.length > 0 ? toolChoice : void 0,
954
731
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
732
+ ...buildOpenAITokenParams(
733
+ modelIdForPayload,
734
+ request.config?.maxTokens ?? this.config.maxTokens,
735
+ request.config?.temperature ?? this.config.temperature
736
+ ),
737
+ response_format: toOpenAIResponseFormat(request.config?.responseFormat),
955
738
  stream: true,
956
- stream_options: { include_usage: true },
957
- // o-series: use max_completion_tokens + reasoning_effort, no temperature
958
- // regular models: use max_tokens + temperature
959
- ...isOSeries ? {
960
- max_completion_tokens: maxTokensValue,
961
- reasoning_effort: request.config?.reasoningEffort ?? "medium"
962
- } : {
963
- temperature: request.config?.temperature ?? this.config.temperature,
964
- max_tokens: maxTokensValue
965
- },
966
- // Non-OpenAI OpenRouter models support OR's reasoning/include_reasoning params.
967
- // When disableThinking=true we must explicitly send include_reasoning:false because
968
- // models like Qwen3 and DeepSeek-R1 reason by default even without the reasoning param.
969
- ...isOpenRouter && !isOpenAIOnOpenRouter ? this.config.disableThinking ? { include_reasoning: false } : { reasoning: { max_tokens: 8e3 }, include_reasoning: true } : {}
739
+ stream_options: { include_usage: true }
970
740
  };
971
741
  logProviderPayload("openai", "request payload", payload, request.debug);
972
742
  const stream = await client.chat.completions.create(payload);
@@ -974,7 +744,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
974
744
  const collectedCitations = [];
975
745
  let citationIndex = 0;
976
746
  let usage;
977
- let adapterReasoningStarted = false;
978
747
  for await (const chunk of stream) {
979
748
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
980
749
  if (request.signal?.aborted) {
@@ -985,22 +754,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
985
754
  if (delta?.content) {
986
755
  yield { type: "message:delta", content: delta.content };
987
756
  }
988
- if (isOpenRouter) {
989
- const rc = delta?.reasoning_content ?? delta?.reasoning ?? null;
990
- if (rc) {
991
- const rcText = typeof rc === "string" ? rc : Array.isArray(rc) && rc[0]?.text ? rc[0].text : "";
992
- if (rcText) {
993
- if (!adapterReasoningStarted) {
994
- yield { type: "thinking:start" };
995
- adapterReasoningStarted = true;
996
- }
997
- yield { type: "thinking:delta", content: rcText };
998
- }
999
- } else if (adapterReasoningStarted && (delta?.content || choice?.finish_reason)) {
1000
- yield { type: "thinking:end" };
1001
- adapterReasoningStarted = false;
1002
- }
1003
- }
1004
757
  const annotations = delta?.annotations;
1005
758
  if (annotations && annotations.length > 0) {
1006
759
  for (const annotation of annotations) {
@@ -1048,11 +801,6 @@ var OpenAIAdapter = class _OpenAIAdapter {
1048
801
  };
1049
802
  } else if (currentToolCall && toolCall.function?.arguments) {
1050
803
  currentToolCall.arguments += toolCall.function.arguments;
1051
- yield {
1052
- type: "action:args",
1053
- id: currentToolCall.id,
1054
- args: currentToolCall.arguments
1055
- };
1056
804
  }
1057
805
  }
1058
806
  }
@@ -1128,24 +876,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1128
876
  name: openaiToolOptions.toolChoice.name
1129
877
  }
1130
878
  } : openaiToolOptions?.toolChoice;
1131
- const activeModel2 = request.config?.model || this.model;
1132
- const modelSlug2 = activeModel2.replace("openai/", "");
1133
- const isOSeries2 = /^o[1-9]/.test(modelSlug2);
1134
- const maxTokensValue2 = request.config?.maxTokens ?? this.config.maxTokens;
879
+ const modelIdForCompletePayload = request.config?.model || this.model;
1135
880
  const payload = {
1136
- model: activeModel2,
881
+ model: modelIdForCompletePayload,
1137
882
  messages,
1138
883
  tools: tools.length > 0 ? tools : void 0,
1139
884
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1140
885
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1141
- stream: false,
1142
- ...isOSeries2 ? {
1143
- max_completion_tokens: maxTokensValue2,
1144
- reasoning_effort: request.config?.reasoningEffort ?? "medium"
1145
- } : {
1146
- temperature: request.config?.temperature ?? this.config.temperature,
1147
- max_tokens: maxTokensValue2
1148
- }
886
+ ...buildOpenAITokenParams(
887
+ modelIdForCompletePayload,
888
+ request.config?.maxTokens ?? this.config.maxTokens,
889
+ request.config?.temperature ?? this.config.temperature
890
+ ),
891
+ response_format: toOpenAIResponseFormat(request.config?.responseFormat),
892
+ stream: false
1149
893
  };
1150
894
  logProviderPayload("openai", "request payload", payload, request.debug);
1151
895
  const response = await client.chat.completions.create(payload);
@@ -1340,7 +1084,8 @@ function createXAI(config = {}) {
1340
1084
  supportsVideo: false,
1341
1085
  maxTokens: model.maxTokens,
1342
1086
  supportedImageTypes: model.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : [],
1343
- supportsJsonMode: false,
1087
+ // xAI accepts OpenAI-compatible `response_format` on grok-2-1212+.
1088
+ supportsJsonMode: true,
1344
1089
  supportsSystemMessages: true
1345
1090
  };
1346
1091
  };