@yourgpt/llm-sdk 2.6.0-beta.0 → 2.6.0-beta.1

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 (55) hide show
  1. package/README.md +77 -0
  2. package/dist/adapters/index.d.mts +11 -13
  3. package/dist/adapters/index.d.ts +11 -13
  4. package/dist/adapters/index.js +255 -164
  5. package/dist/adapters/index.mjs +255 -164
  6. package/dist/{base-C58Dsr9p.d.ts → base-BdTpUAWI.d.ts} +2 -1
  7. package/dist/{base-tNgbBaSo.d.mts → base-CkeT7u-O.d.mts} +2 -1
  8. package/dist/fallback/index.d.mts +4 -4
  9. package/dist/fallback/index.d.ts +4 -4
  10. package/dist/fallback/index.js +75 -39
  11. package/dist/fallback/index.mjs +75 -39
  12. package/dist/index.d.mts +8 -7
  13. package/dist/index.d.ts +8 -7
  14. package/dist/index.js +75 -28
  15. package/dist/index.mjs +75 -28
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/azure/index.d.mts +3 -3
  19. package/dist/providers/azure/index.d.ts +3 -3
  20. package/dist/providers/fireworks/index.d.mts +3 -3
  21. package/dist/providers/fireworks/index.d.ts +3 -3
  22. package/dist/providers/fireworks/index.js +255 -164
  23. package/dist/providers/fireworks/index.mjs +255 -164
  24. package/dist/providers/google/index.d.mts +3 -3
  25. package/dist/providers/google/index.d.ts +3 -3
  26. package/dist/providers/google/index.js +255 -164
  27. package/dist/providers/google/index.mjs +255 -164
  28. package/dist/providers/ollama/index.d.mts +4 -4
  29. package/dist/providers/ollama/index.d.ts +4 -4
  30. package/dist/providers/openai/index.d.mts +6 -23
  31. package/dist/providers/openai/index.d.ts +6 -23
  32. package/dist/providers/openai/index.js +656 -450
  33. package/dist/providers/openai/index.mjs +656 -450
  34. package/dist/providers/openrouter/index.d.mts +3 -3
  35. package/dist/providers/openrouter/index.d.ts +3 -3
  36. package/dist/providers/openrouter/index.js +255 -164
  37. package/dist/providers/openrouter/index.mjs +255 -164
  38. package/dist/providers/togetherai/index.d.mts +3 -3
  39. package/dist/providers/togetherai/index.d.ts +3 -3
  40. package/dist/providers/togetherai/index.js +255 -164
  41. package/dist/providers/togetherai/index.mjs +255 -164
  42. package/dist/providers/xai/index.d.mts +3 -3
  43. package/dist/providers/xai/index.d.ts +3 -3
  44. package/dist/providers/xai/index.js +255 -164
  45. package/dist/providers/xai/index.mjs +255 -164
  46. package/dist/{types-BkQCSiIt.d.mts → types-BW3csYo5.d.mts} +22 -1
  47. package/dist/{types-BkQCSiIt.d.ts → types-BW3csYo5.d.ts} +22 -1
  48. package/dist/{types-B6dhnguR.d.mts → types-BWRYIvl_.d.mts} +11 -4
  49. package/dist/{types-CCxPmkmK.d.ts → types-CpXcT-tY.d.ts} +11 -4
  50. package/dist/{types-BQ31QIsA.d.ts → types-DA54bSmX.d.ts} +3 -11
  51. package/dist/{types-BSSiJW2o.d.mts → types-NOswlhm9.d.mts} +3 -11
  52. package/dist/yourgpt/index.d.mts +1 -1
  53. package/dist/yourgpt/index.d.ts +1 -1
  54. package/package.json +115 -48
  55. package/LICENSE +0 -21
@@ -1,5 +1,98 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/openai-parameters.ts
4
+ function capabilities(model) {
5
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
6
+ const major = Number(gpt?.[1] ?? 0);
7
+ const minor = Number(gpt?.[2] ?? 0);
8
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
9
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
10
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
11
+ return { major, minor, reasoning, supportsNone };
12
+ }
13
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
14
+ if (options.parameterPolicy === "passthrough") return false;
15
+ const c = capabilities(model);
16
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
17
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
18
+ if (!baseUrl) return true;
19
+ try {
20
+ return new URL(baseUrl).hostname === "api.openai.com";
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
26
+ const c = capabilities(model ?? "");
27
+ const normalize = options.parameterPolicy !== "passthrough";
28
+ const effort = options.reasoningEffort;
29
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
30
+ throw new Error(`${model} does not support reasoningEffort: none`);
31
+ }
32
+ if (normalize && c.major >= 6 && effort === "minimal") {
33
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
34
+ }
35
+ const result = {
36
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
37
+ };
38
+ if (effort !== void 0) {
39
+ if (api === "responses") result.reasoning = { effort };
40
+ else result.reasoning_effort = effort;
41
+ }
42
+ if (temperature !== void 0) {
43
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
44
+ result.temperature = temperature;
45
+ } else {
46
+ (options.onWarning ?? console.warn)(
47
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
48
+ );
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+
54
+ // src/core/provider-error.ts
55
+ function providerErrorEvent(error, code) {
56
+ const details = error ?? {};
57
+ const event = {
58
+ type: "error",
59
+ message: error instanceof Error ? error.message : String(error),
60
+ code,
61
+ ...typeof details.status === "number" ? { status: details.status } : {},
62
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
63
+ ...typeof details.param === "string" ? { param: details.param } : {},
64
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
65
+ };
66
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
67
+ return event;
68
+ }
69
+ function errorFromEvent(event) {
70
+ if (event.cause instanceof Error) return event.cause;
71
+ return Object.assign(new Error(event.message), {
72
+ status: event.status,
73
+ code: event.providerCode ?? event.code,
74
+ param: event.param,
75
+ request_id: event.requestId
76
+ });
77
+ }
78
+ function throwIfAborted(signal) {
79
+ if (!signal?.aborted) return;
80
+ const error = new Error("The operation was aborted");
81
+ error.name = "AbortError";
82
+ throw error;
83
+ }
84
+
85
+ // src/core/utils.ts
86
+ function generateId(prefix = "id") {
87
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
88
+ }
89
+ function generateMessageId() {
90
+ return generateId("msg");
91
+ }
92
+ function generateToolCallId() {
93
+ return generateId("call");
94
+ }
95
+
3
96
  // src/adapters/base.ts
4
97
  function stringifyForDebug(value) {
5
98
  return JSON.stringify(
@@ -106,16 +199,6 @@ function normalizeObjectJsonSchema(schema) {
106
199
  }
107
200
  return normalized;
108
201
  }
109
- function isOpenAIReasoningModel(modelId) {
110
- if (!modelId) return false;
111
- return /^(o1|o3|o4|gpt-5)/i.test(modelId);
112
- }
113
- function buildOpenAITokenParams(modelId, maxTokens, temperature) {
114
- if (isOpenAIReasoningModel(modelId)) {
115
- return { max_completion_tokens: maxTokens };
116
- }
117
- return { max_tokens: maxTokens, temperature };
118
- }
119
202
  function toOpenAIResponseFormat(rf) {
120
203
  if (!rf) return void 0;
121
204
  if (rf.type === "json_object") return { type: "json_object" };
@@ -235,291 +318,6 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
235
318
  return formatted;
236
319
  }
237
320
 
238
- // src/providers/openai/provider.ts
239
- var OPENAI_MODELS = {
240
- // GPT-4o series
241
- "gpt-4o": { vision: true, tools: true, jsonMode: true, maxTokens: 128e3 },
242
- "gpt-5.2": {
243
- vision: true,
244
- tools: true,
245
- jsonMode: true,
246
- maxTokens: 128e3
247
- },
248
- "gpt-4o-2024-11-20": {
249
- vision: true,
250
- tools: true,
251
- jsonMode: true,
252
- maxTokens: 128e3
253
- },
254
- "gpt-4o-2024-08-06": {
255
- vision: true,
256
- tools: true,
257
- jsonMode: true,
258
- maxTokens: 128e3
259
- },
260
- // GPT-4 Turbo
261
- "gpt-4-turbo": {
262
- vision: true,
263
- tools: true,
264
- jsonMode: true,
265
- maxTokens: 128e3
266
- },
267
- "gpt-4-turbo-preview": {
268
- vision: false,
269
- tools: true,
270
- jsonMode: true,
271
- maxTokens: 128e3
272
- },
273
- // GPT-4
274
- "gpt-4": { vision: false, tools: true, jsonMode: false, maxTokens: 8192 },
275
- "gpt-4-32k": {
276
- vision: false,
277
- tools: true,
278
- jsonMode: false,
279
- maxTokens: 32768
280
- },
281
- // GPT-3.5
282
- "gpt-3.5-turbo": {
283
- vision: false,
284
- tools: true,
285
- jsonMode: true,
286
- maxTokens: 16385
287
- },
288
- // O1 series (reasoning)
289
- o1: { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
290
- "o1-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
291
- "o1-preview": {
292
- vision: true,
293
- tools: false,
294
- jsonMode: false,
295
- maxTokens: 128e3
296
- },
297
- // O3 series
298
- "o3-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 }
299
- };
300
- function openai(modelId, options = {}) {
301
- const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY;
302
- const baseURL = options.baseURL ?? "https://api.openai.com/v1";
303
- let client = null;
304
- async function getClient() {
305
- if (!client) {
306
- const { default: OpenAI } = await import('openai');
307
- client = new OpenAI({
308
- apiKey,
309
- baseURL,
310
- organization: options.organization,
311
- defaultHeaders: options.headers
312
- });
313
- }
314
- return client;
315
- }
316
- const modelConfig = OPENAI_MODELS[modelId] ?? OPENAI_MODELS["gpt-4o"];
317
- return {
318
- provider: "openai",
319
- modelId,
320
- capabilities: {
321
- supportsVision: modelConfig.vision,
322
- supportsTools: modelConfig.tools,
323
- supportsStreaming: true,
324
- supportsJsonMode: modelConfig.jsonMode,
325
- supportsThinking: false,
326
- supportsPDF: false,
327
- maxTokens: modelConfig.maxTokens,
328
- supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
329
- },
330
- async doGenerate(params) {
331
- const client2 = await getClient();
332
- const messages = formatMessagesForOpenAI2(params.messages);
333
- const response = await client2.chat.completions.create({
334
- model: modelId,
335
- messages,
336
- tools: params.tools,
337
- ...buildOpenAITokenParams(
338
- modelId,
339
- params.maxTokens,
340
- params.temperature
341
- ),
342
- response_format: toOpenAIResponseFormat(params.responseFormat)
343
- });
344
- const choice = response.choices[0];
345
- const message = choice.message;
346
- const toolCalls = (message.tool_calls ?? []).map(
347
- (tc) => ({
348
- id: tc.id,
349
- name: tc.function.name,
350
- args: JSON.parse(tc.function.arguments || "{}")
351
- })
352
- );
353
- return {
354
- text: message.content ?? "",
355
- toolCalls,
356
- finishReason: mapFinishReason(choice.finish_reason),
357
- usage: {
358
- promptTokens: response.usage?.prompt_tokens ?? 0,
359
- completionTokens: response.usage?.completion_tokens ?? 0,
360
- totalTokens: response.usage?.total_tokens ?? 0
361
- },
362
- rawResponse: response
363
- };
364
- },
365
- async *doStream(params) {
366
- const client2 = await getClient();
367
- const messages = formatMessagesForOpenAI2(params.messages);
368
- const stream = await client2.chat.completions.create({
369
- model: modelId,
370
- messages,
371
- tools: params.tools,
372
- ...buildOpenAITokenParams(
373
- modelId,
374
- params.maxTokens,
375
- params.temperature
376
- ),
377
- response_format: toOpenAIResponseFormat(params.responseFormat),
378
- stream: true
379
- });
380
- let currentToolCall = null;
381
- let totalPromptTokens = 0;
382
- let totalCompletionTokens = 0;
383
- for await (const chunk of stream) {
384
- if (params.signal?.aborted) {
385
- yield { type: "error", error: new Error("Aborted") };
386
- return;
387
- }
388
- const choice = chunk.choices[0];
389
- const delta = choice?.delta;
390
- if (delta?.content) {
391
- yield { type: "text-delta", text: delta.content };
392
- }
393
- if (delta?.tool_calls) {
394
- for (const tc of delta.tool_calls) {
395
- if (tc.id) {
396
- if (currentToolCall) {
397
- yield {
398
- type: "tool-call",
399
- toolCall: {
400
- id: currentToolCall.id,
401
- name: currentToolCall.name,
402
- args: JSON.parse(currentToolCall.arguments || "{}")
403
- }
404
- };
405
- }
406
- currentToolCall = {
407
- id: tc.id,
408
- name: tc.function?.name ?? "",
409
- arguments: tc.function?.arguments ?? ""
410
- };
411
- } else if (currentToolCall && tc.function?.arguments) {
412
- currentToolCall.arguments += tc.function.arguments;
413
- }
414
- }
415
- }
416
- if (choice?.finish_reason) {
417
- if (currentToolCall) {
418
- yield {
419
- type: "tool-call",
420
- toolCall: {
421
- id: currentToolCall.id,
422
- name: currentToolCall.name,
423
- args: JSON.parse(currentToolCall.arguments || "{}")
424
- }
425
- };
426
- currentToolCall = null;
427
- }
428
- if (chunk.usage) {
429
- totalPromptTokens = chunk.usage.prompt_tokens;
430
- totalCompletionTokens = chunk.usage.completion_tokens;
431
- }
432
- yield {
433
- type: "finish",
434
- finishReason: mapFinishReason(choice.finish_reason),
435
- usage: {
436
- promptTokens: totalPromptTokens,
437
- completionTokens: totalCompletionTokens,
438
- totalTokens: totalPromptTokens + totalCompletionTokens
439
- }
440
- };
441
- }
442
- }
443
- }
444
- };
445
- }
446
- function mapFinishReason(reason) {
447
- switch (reason) {
448
- case "stop":
449
- return "stop";
450
- case "length":
451
- return "length";
452
- case "tool_calls":
453
- case "function_call":
454
- return "tool-calls";
455
- case "content_filter":
456
- return "content-filter";
457
- default:
458
- return "unknown";
459
- }
460
- }
461
- function formatMessagesForOpenAI2(messages) {
462
- return messages.map((msg) => {
463
- switch (msg.role) {
464
- case "system":
465
- return { role: "system", content: msg.content };
466
- case "user":
467
- if (typeof msg.content === "string") {
468
- return { role: "user", content: msg.content };
469
- }
470
- return {
471
- role: "user",
472
- content: msg.content.map((part) => {
473
- if (part.type === "text") {
474
- return { type: "text", text: part.text };
475
- }
476
- if (part.type === "image") {
477
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
478
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
479
- return { type: "image_url", image_url: { url, detail: "auto" } };
480
- }
481
- return { type: "text", text: "" };
482
- })
483
- };
484
- case "assistant":
485
- const assistantMsg = {
486
- role: "assistant",
487
- content: msg.content
488
- };
489
- if (msg.toolCalls && msg.toolCalls.length > 0) {
490
- assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
491
- id: tc.id,
492
- type: "function",
493
- function: {
494
- name: tc.name,
495
- arguments: JSON.stringify(tc.args)
496
- }
497
- }));
498
- }
499
- return assistantMsg;
500
- case "tool":
501
- return {
502
- role: "tool",
503
- tool_call_id: msg.toolCallId,
504
- content: msg.content
505
- };
506
- default:
507
- return msg;
508
- }
509
- });
510
- }
511
-
512
- // src/core/utils.ts
513
- function generateId(prefix = "id") {
514
- return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
515
- }
516
- function generateMessageId() {
517
- return generateId("msg");
518
- }
519
- function generateToolCallId() {
520
- return generateId("call");
521
- }
522
-
523
321
  // src/adapters/openai.ts
524
322
  var OpenAIAdapter = class _OpenAIAdapter {
525
323
  constructor(config) {
@@ -540,39 +338,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
540
338
  const { default: OpenAI } = await import('openai');
541
339
  this.client = new OpenAI({
542
340
  apiKey: this.config.apiKey,
543
- baseURL: this.config.baseUrl
341
+ baseURL: this.config.baseUrl,
342
+ organization: this.config.organization,
343
+ defaultHeaders: this.config.headers
544
344
  });
545
345
  }
546
346
  return this.client;
547
347
  }
348
+ parameterOptions(request) {
349
+ return {
350
+ ...this.config,
351
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
352
+ };
353
+ }
354
+ toolDefinitions(request) {
355
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
356
+ return formatTools(request.actions ?? []).map((tool) => ({
357
+ name: tool.function.name,
358
+ description: tool.function.description ?? "",
359
+ location: "server",
360
+ inputSchema: tool.function.parameters
361
+ }));
362
+ }
548
363
  shouldUseResponsesApi(request) {
549
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
550
- if (!hasTools) return false;
551
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
364
+ if (this.toolDefinitions(request).length === 0) return false;
365
+ if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
552
366
  return true;
553
- }
554
- return this.requiresResponsesApiForTools(
555
- request.config?.model || this.model
367
+ return requiresOpenAIResponses(
368
+ request.config?.model || this.model,
369
+ this.config.baseUrl,
370
+ this.parameterOptions(request)
556
371
  );
557
372
  }
558
- /**
559
- * Whether this model rejects function tools on /v1/chat/completions.
560
- *
561
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
562
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
563
- * restriction and may not implement /v1/responses at all.
564
- */
565
- requiresResponsesApiForTools(modelId) {
566
- if (!modelId) return false;
567
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
568
- const baseUrl = this.config.baseUrl;
569
- if (!baseUrl) return true;
570
- try {
571
- return new URL(baseUrl).hostname === "api.openai.com";
572
- } catch {
573
- return false;
574
- }
575
- }
576
373
  /**
577
374
  * Convert one message's content to Responses content parts.
578
375
  *
@@ -603,7 +400,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
603
400
  });
604
401
  }
605
402
  buildResponsesInput(request) {
606
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
403
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
607
404
  const input = [];
608
405
  for (const message of sourceMessages) {
609
406
  if (message.role === "system") {
@@ -653,6 +450,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
653
450
  }
654
451
  return input;
655
452
  }
453
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
454
+ formatRawMessages(messages) {
455
+ return messages.map((message) => {
456
+ const { attachments, ...rest } = message;
457
+ let content = message.content;
458
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
459
+ content = null;
460
+ }
461
+ if (Array.isArray(attachments) && attachments.length > 0) {
462
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
463
+ (attachment) => attachmentToOpenAIImage(attachment)
464
+ ).filter((image) => image !== null);
465
+ if (images.length > 0) {
466
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
467
+ content = [...parts, ...images];
468
+ }
469
+ }
470
+ return { ...rest, content };
471
+ });
472
+ }
656
473
  /**
657
474
  * Build the web_search tool for the Responses API, or undefined when web
658
475
  * search is off.
@@ -734,13 +551,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
734
551
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
735
552
  id: item.call_id ?? item.id ?? generateToolCallId(),
736
553
  name: item.name,
737
- args: (() => {
738
- try {
739
- return JSON.parse(item.arguments ?? "{}");
740
- } catch {
741
- return {};
742
- }
743
- })()
554
+ args: JSON.parse(item.arguments ?? "{}")
744
555
  })) : [];
745
556
  return {
746
557
  content,
@@ -764,18 +575,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
764
575
  instructions: request.systemPrompt,
765
576
  input: this.buildResponsesInput(request),
766
577
  tools: this.buildResponsesTools(
767
- request.toolDefinitions ?? [],
578
+ this.toolDefinitions(request),
768
579
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
769
580
  this.buildResponsesWebSearchTool(request)
770
581
  ),
771
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
582
+ tool_choice: openaiToolOptions?.toolChoice,
772
583
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
773
- // Reasoning models reject `temperature` outright — sending it would swap one
774
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
775
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
776
- temperature: request.config?.temperature ?? this.config.temperature
777
- },
778
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
584
+ ...buildOpenAIParameters(
585
+ modelIdForPayload,
586
+ request.config?.maxTokens ?? this.config.maxTokens,
587
+ request.config?.temperature ?? this.config.temperature,
588
+ this.parameterOptions(request),
589
+ "responses"
590
+ ),
779
591
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
780
592
  stream
781
593
  };
@@ -783,10 +595,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
783
595
  }
784
596
  async completeWithResponses(request) {
785
597
  const client = await this.getClient();
598
+ throwIfAborted(request.signal);
786
599
  const payload = this.buildResponsesPayload(request, false);
787
600
  logProviderPayload("openai", "request payload", payload, request.debug);
788
- const response = await client.responses.create(payload);
601
+ if (!client.responses?.create)
602
+ throw new Error(
603
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
604
+ );
605
+ const response = await client.responses.create(payload, {
606
+ signal: request.signal
607
+ });
608
+ throwIfAborted(request.signal);
789
609
  logProviderPayload("openai", "response payload", response, request.debug);
610
+ if (response.status && response.status !== "completed") {
611
+ throw Object.assign(
612
+ new Error(
613
+ response.error?.message ?? `Responses request ${response.status}`
614
+ ),
615
+ response.error
616
+ );
617
+ }
790
618
  return this.parseResponsesResult(response);
791
619
  }
792
620
  /**
@@ -804,13 +632,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
804
632
  */
805
633
  async *streamWithResponses(request) {
806
634
  const client = await this.getClient();
635
+ throwIfAborted(request.signal);
807
636
  const payload = this.buildResponsesPayload(request, true);
808
637
  logProviderPayload("openai", "request payload", payload, request.debug);
809
- const stream = await client.responses.create(payload);
638
+ if (!client.responses?.create)
639
+ throw new Error(
640
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
641
+ );
642
+ const stream = await client.responses.create(payload, {
643
+ signal: request.signal
644
+ });
810
645
  const toolCalls = /* @__PURE__ */ new Map();
811
646
  let usage;
812
647
  const citations = [];
648
+ let completed = false;
813
649
  function* flushToolCalls() {
650
+ for (const call of toolCalls.values()) {
651
+ if (!call.name)
652
+ throw new Error("Responses tool call is missing its name");
653
+ JSON.parse(call.arguments || "{}");
654
+ }
814
655
  for (const call of toolCalls.values()) {
815
656
  yield { type: "action:start", id: call.id, name: call.name };
816
657
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -819,7 +660,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
819
660
  toolCalls.clear();
820
661
  }
821
662
  for await (const event of stream) {
822
- if (request.signal?.aborted) break;
663
+ throwIfAborted(request.signal);
823
664
  logProviderPayload("openai", "stream chunk", event, request.debug);
824
665
  const index = event?.output_index ?? 0;
825
666
  switch (event?.type) {
@@ -849,8 +690,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
849
690
  }
850
691
  break;
851
692
  }
852
- case "response.completed":
853
- case "response.incomplete": {
693
+ case "response.completed": {
694
+ completed = true;
854
695
  const found = this.collectResponsesCitations(
855
696
  event.response?.output,
856
697
  citations.length
@@ -866,13 +707,27 @@ var OpenAIAdapter = class _OpenAIAdapter {
866
707
  }
867
708
  break;
868
709
  }
869
- case "error":
870
- case "response.failed":
710
+ case "response.incomplete":
871
711
  throw new Error(
872
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
712
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
873
713
  );
714
+ case "error":
715
+ case "response.failed": {
716
+ const details = event.response?.error ?? event;
717
+ throw Object.assign(
718
+ new Error(details.message ?? "Responses stream failed"),
719
+ {
720
+ code: details.code,
721
+ param: details.param,
722
+ status: details.status
723
+ }
724
+ );
725
+ }
874
726
  }
875
727
  }
728
+ throwIfAborted(request.signal);
729
+ if (!completed)
730
+ throw new Error("Responses stream ended without a completed response");
876
731
  yield* flushToolCalls();
877
732
  if (citations.length > 0) {
878
733
  yield { type: "citation", citations: deduplicateCitations(citations) };
@@ -881,6 +736,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
881
736
  yield { type: "done", usage };
882
737
  }
883
738
  async *stream(request) {
739
+ throwIfAborted(request.signal);
884
740
  if (this.shouldUseResponsesApi(request)) {
885
741
  const messageId2 = generateMessageId();
886
742
  yield { type: "message:start", id: messageId2 };
@@ -888,47 +744,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
888
744
  yield* this.streamWithResponses(request);
889
745
  return;
890
746
  } catch (error) {
891
- yield {
892
- type: "error",
893
- message: error instanceof Error ? error.message : "Unknown error",
894
- code: "OPENAI_RESPONSES_ERROR"
895
- };
747
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
896
748
  return;
897
749
  }
898
750
  }
899
751
  const client = await this.getClient();
900
752
  let messages;
901
753
  if (request.rawMessages && request.rawMessages.length > 0) {
902
- const processedMessages = request.rawMessages.map((msg) => {
903
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
904
- return { ...msg, content: null };
905
- }
906
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
907
- if (hasAttachments) {
908
- const content = [];
909
- if (msg.content) {
910
- content.push({ type: "text", text: msg.content });
911
- }
912
- for (const attachment of msg.attachments) {
913
- if (attachment.type === "image") {
914
- let imageUrl;
915
- if (attachment.url) {
916
- imageUrl = attachment.url;
917
- } else if (attachment.data) {
918
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
919
- } else {
920
- continue;
921
- }
922
- content.push({
923
- type: "image_url",
924
- image_url: { url: imageUrl, detail: "auto" }
925
- });
926
- }
927
- }
928
- return { ...msg, content, attachments: void 0 };
929
- }
930
- return msg;
931
- });
754
+ const processedMessages = this.formatRawMessages(request.rawMessages);
932
755
  if (request.systemPrompt) {
933
756
  const hasSystem = processedMessages.some((m) => m.role === "system");
934
757
  if (!hasSystem) {
@@ -948,7 +771,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
948
771
  request.systemPrompt
949
772
  );
950
773
  }
951
- const tools = request.actions?.length ? formatTools(request.actions) : [];
774
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
775
+ type: "function",
776
+ function: {
777
+ name: tool.name,
778
+ description: tool.description,
779
+ parameters: normalizeObjectJsonSchema(
780
+ tool.inputSchema
781
+ )
782
+ }
783
+ })) : formatTools(request.actions ?? []);
952
784
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
953
785
  if (webSearchConfig) {
954
786
  const webSearchTool = {
@@ -977,26 +809,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
977
809
  tools: tools.length > 0 ? tools : void 0,
978
810
  tool_choice: tools.length > 0 ? toolChoice : void 0,
979
811
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
980
- ...buildOpenAITokenParams(
812
+ ...buildOpenAIParameters(
981
813
  modelIdForPayload,
982
814
  request.config?.maxTokens ?? this.config.maxTokens,
983
- request.config?.temperature ?? this.config.temperature
815
+ request.config?.temperature ?? this.config.temperature,
816
+ this.parameterOptions(request)
984
817
  ),
985
818
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
986
819
  stream: true,
987
820
  stream_options: { include_usage: true }
988
821
  };
989
822
  logProviderPayload("openai", "request payload", payload, request.debug);
990
- const stream = await client.chat.completions.create(payload);
991
- let currentToolCall = null;
823
+ const stream = await client.chat.completions.create(payload, {
824
+ signal: request.signal
825
+ });
826
+ const toolCalls = /* @__PURE__ */ new Map();
827
+ let finishReason;
992
828
  const collectedCitations = [];
993
829
  let citationIndex = 0;
994
830
  let usage;
995
831
  for await (const chunk of stream) {
996
832
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
997
- if (request.signal?.aborted) {
998
- break;
999
- }
833
+ throwIfAborted(request.signal);
1000
834
  const delta = chunk.choices[0]?.delta;
1001
835
  const choice = chunk.choices[0];
1002
836
  if (delta?.content) {
@@ -1019,38 +853,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1019
853
  }
1020
854
  }
1021
855
  }
1022
- if (delta?.tool_calls) {
1023
- for (const toolCall of delta.tool_calls) {
1024
- if (toolCall.id) {
1025
- if (currentToolCall) {
1026
- yield {
1027
- type: "action:args",
1028
- id: currentToolCall.id,
1029
- args: currentToolCall.arguments
1030
- };
1031
- yield {
1032
- type: "action:end",
1033
- id: currentToolCall.id,
1034
- name: currentToolCall.name
1035
- };
1036
- }
1037
- const tcExtraContent = toolCall.extra_content;
1038
- currentToolCall = {
1039
- id: toolCall.id,
1040
- name: toolCall.function?.name || "",
1041
- arguments: toolCall.function?.arguments || "",
1042
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1043
- };
1044
- yield {
1045
- type: "action:start",
1046
- id: currentToolCall.id,
1047
- name: currentToolCall.name,
1048
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1049
- };
1050
- } else if (currentToolCall && toolCall.function?.arguments) {
1051
- currentToolCall.arguments += toolCall.function.arguments;
1052
- }
1053
- }
856
+ for (const part of delta?.tool_calls ?? []) {
857
+ const index = part.index ?? 0;
858
+ const call = toolCalls.get(index) ?? {
859
+ id: "",
860
+ name: "",
861
+ arguments: ""
862
+ };
863
+ if (part.id) call.id = part.id;
864
+ if (part.function?.name) call.name += part.function.name;
865
+ if (part.function?.arguments)
866
+ call.arguments += part.function.arguments;
867
+ if (part.extra_content) call.extra_content = part.extra_content;
868
+ toolCalls.set(index, call);
1054
869
  }
1055
870
  if (chunk.usage) {
1056
871
  usage = {
@@ -1059,21 +874,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1059
874
  total_tokens: chunk.usage.total_tokens
1060
875
  };
1061
876
  }
1062
- if (choice?.finish_reason) {
1063
- if (currentToolCall) {
1064
- yield {
1065
- type: "action:args",
1066
- id: currentToolCall.id,
1067
- args: currentToolCall.arguments
1068
- };
1069
- yield {
1070
- type: "action:end",
1071
- id: currentToolCall.id,
1072
- name: currentToolCall.name
1073
- };
1074
- currentToolCall = null;
1075
- }
1076
- }
877
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
878
+ }
879
+ throwIfAborted(request.signal);
880
+ if (!finishReason)
881
+ throw new Error("OpenAI stream ended without a finish reason");
882
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
883
+ throw new Error("OpenAI stream ended with incomplete tool calls");
884
+ }
885
+ for (const call of toolCalls.values()) {
886
+ if (!call.id || !call.name)
887
+ throw new Error("OpenAI stream contains an incomplete tool call");
888
+ JSON.parse(call.arguments || "{}");
889
+ }
890
+ for (const call of toolCalls.values()) {
891
+ yield {
892
+ type: "action:start",
893
+ id: call.id,
894
+ name: call.name,
895
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
896
+ };
897
+ yield { type: "action:args", id: call.id, args: call.arguments };
898
+ yield { type: "action:end", id: call.id, name: call.name };
1077
899
  }
1078
900
  if (collectedCitations.length > 0) {
1079
901
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1082,26 +904,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1082
904
  yield { type: "message:end" };
1083
905
  yield { type: "done", usage };
1084
906
  } catch (error) {
1085
- yield {
1086
- type: "error",
1087
- message: error instanceof Error ? error.message : "Unknown error",
1088
- code: `${this.provider.toUpperCase()}_ERROR`
1089
- };
907
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1090
908
  }
1091
909
  }
1092
910
  async complete(request) {
911
+ throwIfAborted(request.signal);
1093
912
  if (this.shouldUseResponsesApi(request)) {
1094
913
  return this.completeWithResponses(request);
1095
914
  }
1096
915
  const client = await this.getClient();
1097
916
  let messages;
1098
917
  if (request.rawMessages && request.rawMessages.length > 0) {
1099
- const sanitized = request.rawMessages.map((msg) => {
1100
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1101
- return { ...msg, content: null };
1102
- }
1103
- return msg;
1104
- });
918
+ const sanitized = this.formatRawMessages(request.rawMessages);
1105
919
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1106
920
  messages = [
1107
921
  { role: "system", content: request.systemPrompt },
@@ -1116,7 +930,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1116
930
  request.systemPrompt
1117
931
  );
1118
932
  }
1119
- const tools = request.actions?.length ? formatTools(request.actions) : [];
933
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
934
+ type: "function",
935
+ function: {
936
+ name: tool.name,
937
+ description: tool.description,
938
+ parameters: normalizeObjectJsonSchema(
939
+ tool.inputSchema
940
+ )
941
+ }
942
+ })) : formatTools(request.actions ?? []);
1120
943
  const openaiToolOptions = request.providerToolOptions?.openai;
1121
944
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1122
945
  type: "function",
@@ -1131,16 +954,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1131
954
  tools: tools.length > 0 ? tools : void 0,
1132
955
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1133
956
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1134
- ...buildOpenAITokenParams(
957
+ ...buildOpenAIParameters(
1135
958
  modelIdForCompletePayload,
1136
959
  request.config?.maxTokens ?? this.config.maxTokens,
1137
- request.config?.temperature ?? this.config.temperature
960
+ request.config?.temperature ?? this.config.temperature,
961
+ this.parameterOptions(request)
1138
962
  ),
1139
963
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1140
964
  stream: false
1141
965
  };
1142
966
  logProviderPayload("openai", "request payload", payload, request.debug);
1143
- const response = await client.chat.completions.create(payload);
967
+ const response = await client.chat.completions.create(payload, {
968
+ signal: request.signal
969
+ });
1144
970
  logProviderPayload("openai", "response payload", response, request.debug);
1145
971
  const choice = response.choices?.[0];
1146
972
  const message = choice?.message;
@@ -1190,6 +1016,383 @@ function createOpenAIAdapter(config) {
1190
1016
  return new OpenAIAdapter(config);
1191
1017
  }
1192
1018
 
1019
+ // src/providers/openai/provider.ts
1020
+ var OPENAI_MODELS = {
1021
+ // GPT-4o series
1022
+ "gpt-4o": { vision: true, tools: true, jsonMode: true, maxTokens: 128e3 },
1023
+ "gpt-5.2": {
1024
+ vision: true,
1025
+ tools: true,
1026
+ jsonMode: true,
1027
+ maxTokens: 128e3
1028
+ },
1029
+ "gpt-4o-2024-11-20": {
1030
+ vision: true,
1031
+ tools: true,
1032
+ jsonMode: true,
1033
+ maxTokens: 128e3
1034
+ },
1035
+ "gpt-4o-2024-08-06": {
1036
+ vision: true,
1037
+ tools: true,
1038
+ jsonMode: true,
1039
+ maxTokens: 128e3
1040
+ },
1041
+ // GPT-4 Turbo
1042
+ "gpt-4-turbo": {
1043
+ vision: true,
1044
+ tools: true,
1045
+ jsonMode: true,
1046
+ maxTokens: 128e3
1047
+ },
1048
+ "gpt-4-turbo-preview": {
1049
+ vision: false,
1050
+ tools: true,
1051
+ jsonMode: true,
1052
+ maxTokens: 128e3
1053
+ },
1054
+ // GPT-4
1055
+ "gpt-4": { vision: false, tools: true, jsonMode: false, maxTokens: 8192 },
1056
+ "gpt-4-32k": {
1057
+ vision: false,
1058
+ tools: true,
1059
+ jsonMode: false,
1060
+ maxTokens: 32768
1061
+ },
1062
+ // GPT-3.5
1063
+ "gpt-3.5-turbo": {
1064
+ vision: false,
1065
+ tools: true,
1066
+ jsonMode: true,
1067
+ maxTokens: 16385
1068
+ },
1069
+ // O1 series (reasoning)
1070
+ o1: { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
1071
+ "o1-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
1072
+ "o1-preview": {
1073
+ vision: true,
1074
+ tools: false,
1075
+ jsonMode: false,
1076
+ maxTokens: 128e3
1077
+ },
1078
+ // O3 series
1079
+ "o3-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 }
1080
+ };
1081
+ function openai(modelId, options = {}) {
1082
+ const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY;
1083
+ const baseURL = options.baseURL ?? "https://api.openai.com/v1";
1084
+ let client = null;
1085
+ async function getClient() {
1086
+ if (!client) {
1087
+ const { default: OpenAI } = await import('openai');
1088
+ client = new OpenAI({
1089
+ apiKey,
1090
+ baseURL,
1091
+ organization: options.organization,
1092
+ defaultHeaders: options.headers
1093
+ });
1094
+ }
1095
+ return client;
1096
+ }
1097
+ function parameterOptions(params) {
1098
+ return {
1099
+ ...options,
1100
+ reasoningEffort: params.reasoningEffort ?? options.reasoningEffort
1101
+ };
1102
+ }
1103
+ function useResponses(params) {
1104
+ return !!params.tools?.length && requiresOpenAIResponses(modelId, baseURL, parameterOptions(params));
1105
+ }
1106
+ const responsesAdapter = new OpenAIAdapter({
1107
+ ...options,
1108
+ apiKey: apiKey ?? "",
1109
+ model: modelId,
1110
+ baseUrl: baseURL
1111
+ });
1112
+ function responsesRequest(params) {
1113
+ return {
1114
+ messages: [],
1115
+ rawMessages: formatMessagesForOpenAI2(params.messages),
1116
+ toolDefinitions: params.tools?.map((raw) => {
1117
+ const tool = raw;
1118
+ if (tool.type !== "function")
1119
+ throw new Error(
1120
+ "Only function tools are supported by this OpenAI generation path"
1121
+ );
1122
+ return {
1123
+ name: tool.function.name,
1124
+ description: tool.function.description ?? "",
1125
+ location: "server",
1126
+ inputSchema: tool.function.parameters
1127
+ };
1128
+ }),
1129
+ config: {
1130
+ temperature: params.temperature,
1131
+ maxTokens: params.maxTokens,
1132
+ responseFormat: params.responseFormat,
1133
+ reasoningEffort: parameterOptions(params).reasoningEffort
1134
+ },
1135
+ signal: params.signal
1136
+ };
1137
+ }
1138
+ const modelConfig = OPENAI_MODELS[modelId] ?? OPENAI_MODELS["gpt-4o"];
1139
+ return {
1140
+ provider: "openai",
1141
+ modelId,
1142
+ capabilities: {
1143
+ supportsVision: modelConfig.vision,
1144
+ supportsTools: modelConfig.tools,
1145
+ supportsStreaming: true,
1146
+ supportsJsonMode: modelConfig.jsonMode,
1147
+ supportsThinking: false,
1148
+ supportsPDF: false,
1149
+ maxTokens: modelConfig.maxTokens,
1150
+ supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
1151
+ },
1152
+ async doGenerate(params) {
1153
+ throwIfAborted(params.signal);
1154
+ if (useResponses(params)) {
1155
+ const result = await responsesAdapter.complete(
1156
+ responsesRequest(params)
1157
+ );
1158
+ return {
1159
+ text: result.content,
1160
+ toolCalls: result.toolCalls,
1161
+ finishReason: result.toolCalls.length ? "tool-calls" : "stop",
1162
+ usage: result.usage ?? {
1163
+ promptTokens: 0,
1164
+ completionTokens: 0,
1165
+ totalTokens: 0
1166
+ },
1167
+ rawResponse: result.rawResponse
1168
+ };
1169
+ }
1170
+ const client2 = await getClient();
1171
+ const messages = formatMessagesForOpenAI2(params.messages);
1172
+ const response = await client2.chat.completions.create(
1173
+ {
1174
+ model: modelId,
1175
+ messages,
1176
+ tools: params.tools,
1177
+ ...buildOpenAIParameters(
1178
+ modelId,
1179
+ params.maxTokens,
1180
+ params.temperature,
1181
+ parameterOptions(params)
1182
+ ),
1183
+ response_format: toOpenAIResponseFormat(params.responseFormat)
1184
+ },
1185
+ { signal: params.signal }
1186
+ );
1187
+ const choice = response.choices[0];
1188
+ const message = choice.message;
1189
+ const toolCalls = (message.tool_calls ?? []).map(
1190
+ (tc) => ({
1191
+ id: tc.id,
1192
+ name: tc.function.name,
1193
+ args: JSON.parse(tc.function.arguments || "{}")
1194
+ })
1195
+ );
1196
+ return {
1197
+ text: message.content ?? "",
1198
+ toolCalls,
1199
+ finishReason: mapFinishReason(choice.finish_reason),
1200
+ usage: {
1201
+ promptTokens: response.usage?.prompt_tokens ?? 0,
1202
+ completionTokens: response.usage?.completion_tokens ?? 0,
1203
+ totalTokens: response.usage?.total_tokens ?? 0
1204
+ },
1205
+ rawResponse: response
1206
+ };
1207
+ },
1208
+ async *doStream(params) {
1209
+ throwIfAborted(params.signal);
1210
+ if (useResponses(params)) {
1211
+ const calls2 = /* @__PURE__ */ new Map();
1212
+ let hasTools = false;
1213
+ for await (const event of responsesAdapter.stream(
1214
+ responsesRequest(params)
1215
+ )) {
1216
+ switch (event.type) {
1217
+ case "message:delta":
1218
+ yield { type: "text-delta", text: event.content };
1219
+ break;
1220
+ case "action:start":
1221
+ calls2.set(event.id, { name: event.name, args: "" });
1222
+ break;
1223
+ case "action:args": {
1224
+ const call = calls2.get(event.id);
1225
+ if (call) call.args += event.args;
1226
+ break;
1227
+ }
1228
+ case "action:end": {
1229
+ const call = calls2.get(event.id);
1230
+ if (!call)
1231
+ throw new Error("Responses tool call has no matching start");
1232
+ hasTools = true;
1233
+ yield {
1234
+ type: "tool-call",
1235
+ toolCall: {
1236
+ id: event.id,
1237
+ name: call.name,
1238
+ args: JSON.parse(call.args || "{}")
1239
+ }
1240
+ };
1241
+ calls2.delete(event.id);
1242
+ break;
1243
+ }
1244
+ case "error":
1245
+ throw errorFromEvent(event);
1246
+ case "done":
1247
+ yield {
1248
+ type: "finish",
1249
+ finishReason: hasTools ? "tool-calls" : "stop",
1250
+ usage: {
1251
+ promptTokens: event.usage?.prompt_tokens ?? 0,
1252
+ completionTokens: event.usage?.completion_tokens ?? 0,
1253
+ totalTokens: event.usage?.total_tokens ?? 0
1254
+ }
1255
+ };
1256
+ break;
1257
+ }
1258
+ }
1259
+ return;
1260
+ }
1261
+ const client2 = await getClient();
1262
+ const messages = formatMessagesForOpenAI2(params.messages);
1263
+ const stream = await client2.chat.completions.create(
1264
+ {
1265
+ model: modelId,
1266
+ messages,
1267
+ tools: params.tools,
1268
+ ...buildOpenAIParameters(
1269
+ modelId,
1270
+ params.maxTokens,
1271
+ params.temperature,
1272
+ parameterOptions(params)
1273
+ ),
1274
+ response_format: toOpenAIResponseFormat(params.responseFormat),
1275
+ stream: true,
1276
+ stream_options: { include_usage: true }
1277
+ },
1278
+ { signal: params.signal }
1279
+ );
1280
+ const toolCalls = /* @__PURE__ */ new Map();
1281
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
1282
+ let finishReason;
1283
+ for await (const chunk of stream) {
1284
+ throwIfAborted(params.signal);
1285
+ if (chunk.usage) {
1286
+ usage = {
1287
+ promptTokens: chunk.usage.prompt_tokens ?? 0,
1288
+ completionTokens: chunk.usage.completion_tokens ?? 0,
1289
+ totalTokens: chunk.usage.total_tokens ?? (chunk.usage.prompt_tokens ?? 0) + (chunk.usage.completion_tokens ?? 0)
1290
+ };
1291
+ }
1292
+ const choice = chunk.choices?.[0];
1293
+ if (choice?.delta?.content)
1294
+ yield { type: "text-delta", text: choice.delta.content };
1295
+ for (const part of choice?.delta?.tool_calls ?? []) {
1296
+ const index = part.index ?? 0;
1297
+ const call = toolCalls.get(index) ?? {
1298
+ id: "",
1299
+ name: "",
1300
+ arguments: ""
1301
+ };
1302
+ if (part.id) call.id = part.id;
1303
+ if (part.function?.name) call.name += part.function.name;
1304
+ if (part.function?.arguments)
1305
+ call.arguments += part.function.arguments;
1306
+ toolCalls.set(index, call);
1307
+ }
1308
+ if (choice?.finish_reason)
1309
+ finishReason = mapFinishReason(choice.finish_reason);
1310
+ }
1311
+ throwIfAborted(params.signal);
1312
+ if (!finishReason)
1313
+ throw new Error("OpenAI stream ended without a finish reason");
1314
+ const calls = [...toolCalls.values()].map((call) => {
1315
+ if (!call.id || !call.name)
1316
+ throw new Error("OpenAI stream contains an incomplete tool call");
1317
+ return {
1318
+ id: call.id,
1319
+ name: call.name,
1320
+ args: JSON.parse(call.arguments || "{}")
1321
+ };
1322
+ });
1323
+ if (calls.length && finishReason !== "tool-calls")
1324
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1325
+ for (const toolCall of calls) yield { type: "tool-call", toolCall };
1326
+ yield { type: "finish", finishReason, usage };
1327
+ }
1328
+ };
1329
+ }
1330
+ function mapFinishReason(reason) {
1331
+ switch (reason) {
1332
+ case "stop":
1333
+ return "stop";
1334
+ case "length":
1335
+ return "length";
1336
+ case "tool_calls":
1337
+ case "function_call":
1338
+ return "tool-calls";
1339
+ case "content_filter":
1340
+ return "content-filter";
1341
+ default:
1342
+ return "unknown";
1343
+ }
1344
+ }
1345
+ function formatMessagesForOpenAI2(messages) {
1346
+ return messages.map((msg) => {
1347
+ switch (msg.role) {
1348
+ case "system":
1349
+ return { role: "system", content: msg.content };
1350
+ case "user":
1351
+ if (typeof msg.content === "string") {
1352
+ return { role: "user", content: msg.content };
1353
+ }
1354
+ return {
1355
+ role: "user",
1356
+ content: msg.content.map((part) => {
1357
+ if (part.type === "text") {
1358
+ return { type: "text", text: part.text };
1359
+ }
1360
+ if (part.type === "image") {
1361
+ const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
1362
+ const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
1363
+ return { type: "image_url", image_url: { url, detail: "auto" } };
1364
+ }
1365
+ return { type: "text", text: "" };
1366
+ })
1367
+ };
1368
+ case "assistant":
1369
+ const assistantMsg = {
1370
+ role: "assistant",
1371
+ content: msg.content
1372
+ };
1373
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
1374
+ assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
1375
+ id: tc.id,
1376
+ type: "function",
1377
+ function: {
1378
+ name: tc.name,
1379
+ arguments: JSON.stringify(tc.args)
1380
+ }
1381
+ }));
1382
+ }
1383
+ return assistantMsg;
1384
+ case "tool":
1385
+ return {
1386
+ role: "tool",
1387
+ tool_call_id: msg.toolCallId,
1388
+ content: msg.content
1389
+ };
1390
+ default:
1391
+ return msg;
1392
+ }
1393
+ });
1394
+ }
1395
+
1193
1396
  // src/providers/types.ts
1194
1397
  function createCallableProvider(providerFn, properties) {
1195
1398
  Object.defineProperty(providerFn, "name", {
@@ -1320,7 +1523,10 @@ function createOpenAI(config = {}) {
1320
1523
  return createOpenAIAdapter({
1321
1524
  apiKey,
1322
1525
  model: modelId,
1323
- baseUrl: config.baseUrl
1526
+ baseUrl: config.baseUrl,
1527
+ reasoningEffort: config.reasoningEffort,
1528
+ parameterPolicy: config.parameterPolicy,
1529
+ onWarning: config.onWarning
1324
1530
  });
1325
1531
  };
1326
1532
  const getCapabilities = (modelId) => {