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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +189 -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 +610 -186
  5. package/dist/adapters/index.mjs +610 -186
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-tNgbBaSo.d.mts → base-BICaQaRS.d.mts} +8 -3
  9. package/dist/{base-C58Dsr9p.d.ts → base-CpRPB8u8.d.ts} +8 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/fallback/index.js +75 -39
  13. package/dist/fallback/index.mjs +75 -39
  14. package/dist/index.d.mts +18 -35
  15. package/dist/index.d.ts +18 -35
  16. package/dist/index.js +304 -88
  17. package/dist/index.mjs +302 -89
  18. package/dist/providers/anthropic/index.d.mts +3 -3
  19. package/dist/providers/anthropic/index.d.ts +3 -3
  20. package/dist/providers/anthropic/index.js +123 -7
  21. package/dist/providers/anthropic/index.mjs +123 -7
  22. package/dist/providers/azure/index.d.mts +3 -3
  23. package/dist/providers/azure/index.d.ts +3 -3
  24. package/dist/providers/azure/index.js +101 -4
  25. package/dist/providers/azure/index.mjs +101 -4
  26. package/dist/providers/fireworks/index.d.mts +3 -3
  27. package/dist/providers/fireworks/index.d.ts +3 -3
  28. package/dist/providers/fireworks/index.js +559 -178
  29. package/dist/providers/fireworks/index.mjs +559 -178
  30. package/dist/providers/google/index.d.mts +3 -3
  31. package/dist/providers/google/index.d.ts +3 -3
  32. package/dist/providers/google/index.js +559 -178
  33. package/dist/providers/google/index.mjs +559 -178
  34. package/dist/providers/ollama/index.d.mts +4 -4
  35. package/dist/providers/ollama/index.d.ts +4 -4
  36. package/dist/providers/ollama/index.js +87 -2
  37. package/dist/providers/ollama/index.mjs +87 -2
  38. package/dist/providers/openai/index.d.mts +25 -23
  39. package/dist/providers/openai/index.d.ts +25 -23
  40. package/dist/providers/openai/index.js +1397 -481
  41. package/dist/providers/openai/index.mjs +1396 -482
  42. package/dist/providers/openrouter/index.d.mts +3 -3
  43. package/dist/providers/openrouter/index.d.ts +3 -3
  44. package/dist/providers/openrouter/index.js +559 -178
  45. package/dist/providers/openrouter/index.mjs +559 -178
  46. package/dist/providers/togetherai/index.d.mts +3 -3
  47. package/dist/providers/togetherai/index.d.ts +3 -3
  48. package/dist/providers/togetherai/index.js +559 -178
  49. package/dist/providers/togetherai/index.mjs +559 -178
  50. package/dist/providers/xai/index.d.mts +3 -3
  51. package/dist/providers/xai/index.d.ts +3 -3
  52. package/dist/providers/xai/index.js +559 -178
  53. package/dist/providers/xai/index.mjs +559 -178
  54. package/dist/{types-B6dhnguR.d.mts → types-C3RgRbEC.d.mts} +11 -4
  55. package/dist/{types-CCxPmkmK.d.ts → types-DCFqkJAy.d.ts} +11 -4
  56. package/dist/{types-BQ31QIsA.d.ts → types-DHQEMFHA.d.mts} +3 -11
  57. package/dist/{types-BkQCSiIt.d.mts → types-Dg1Bcg1k.d.mts} +127 -3
  58. package/dist/{types-BkQCSiIt.d.ts → types-Dg1Bcg1k.d.ts} +127 -3
  59. package/dist/{types-BSSiJW2o.d.mts → types-Dikj3En4.d.ts} +3 -11
  60. package/dist/yourgpt/index.d.mts +1 -1
  61. package/dist/yourgpt/index.d.ts +1 -1
  62. package/docs/multimodal-and-audio.md +170 -0
  63. package/package.json +117 -49
  64. package/LICENSE +0 -21
@@ -1,5 +1,159 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/tool-output.ts
4
+ var imageTypes = /* @__PURE__ */ new Set([
5
+ "image/png",
6
+ "image/jpeg",
7
+ "image/webp",
8
+ "image/gif"
9
+ ]);
10
+ function normalizeToolOutput(value) {
11
+ const output = value;
12
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
13
+ throw new Error("Tool model output must contain a nonempty content array");
14
+ }
15
+ return {
16
+ type: "content",
17
+ content: output.content.map((part) => {
18
+ if (part?.type === "text" && typeof part.text === "string")
19
+ return { type: "text", text: part.text };
20
+ if (part?.type !== "image")
21
+ throw new Error("Unsupported tool output content type");
22
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
23
+ if (sources.length !== 1)
24
+ throw new Error(
25
+ "Tool image requires exactly one of data, url, or fileId"
26
+ );
27
+ if ("fileId" in part) {
28
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
29
+ throw new Error(
30
+ "Tool image file references must name the openai provider and fileId"
31
+ );
32
+ return { type: "image", fileId: part.fileId, provider: "openai" };
33
+ }
34
+ if ("url" in part) {
35
+ let url;
36
+ try {
37
+ url = new URL(part.url);
38
+ } catch {
39
+ throw new Error("Invalid tool image URL");
40
+ }
41
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
42
+ throw new Error(
43
+ "Tool image URL must use HTTP(S) without credentials"
44
+ );
45
+ return { type: "image", url: url.href };
46
+ }
47
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
48
+ throw new Error("Unsupported tool image media type");
49
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
50
+ if (typeof data !== "string")
51
+ throw new Error("Tool image data must be base64 or bytes");
52
+ if (data.startsWith("data:")) {
53
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
54
+ if (!match || match[1] !== part.mediaType)
55
+ throw new Error(
56
+ "Tool image data URL media type does not match mediaType"
57
+ );
58
+ data = match[2];
59
+ }
60
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
61
+ throw new Error("Invalid tool image base64 data");
62
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
63
+ throw new Error("Tool image exceeds 20 MiB");
64
+ if (Buffer.from(data, "base64").toString("base64") !== data)
65
+ throw new Error("Invalid tool image base64 data");
66
+ return { type: "image", data, mediaType: part.mediaType };
67
+ })
68
+ };
69
+ }
70
+ function toolOutputForProvider(value, provider) {
71
+ const output = normalizeToolOutput(value);
72
+ return output.content.map((part) => {
73
+ if (part.type === "text")
74
+ return {
75
+ type: provider === "responses" ? "input_text" : "text",
76
+ text: part.text
77
+ };
78
+ if (provider === "unsupported")
79
+ throw new Error(
80
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
81
+ );
82
+ if ("fileId" in part) {
83
+ if (provider !== "responses")
84
+ throw new Error(
85
+ "OpenAI image file references cannot be sent to another provider"
86
+ );
87
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
88
+ }
89
+ if (provider === "responses")
90
+ return {
91
+ type: "input_image",
92
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
93
+ detail: "auto"
94
+ };
95
+ return {
96
+ type: "image",
97
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
98
+ };
99
+ });
100
+ }
101
+ function textToolOutput(value, fallback) {
102
+ if (value === void 0) return fallback;
103
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
104
+ }
105
+
106
+ // src/core/openai-parameters.ts
107
+ function capabilities(model) {
108
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
109
+ const major = Number(gpt?.[1] ?? 0);
110
+ const minor = Number(gpt?.[2] ?? 0);
111
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
112
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
113
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
114
+ return { major, minor, reasoning, supportsNone };
115
+ }
116
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
117
+ if (options.parameterPolicy === "passthrough") return false;
118
+ const c = capabilities(model);
119
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
120
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
121
+ if (!baseUrl) return true;
122
+ try {
123
+ return new URL(baseUrl).hostname === "api.openai.com";
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
129
+ const c = capabilities(model ?? "");
130
+ const normalize = options.parameterPolicy !== "passthrough";
131
+ const effort = options.reasoningEffort;
132
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
133
+ throw new Error(`${model} does not support reasoningEffort: none`);
134
+ }
135
+ if (normalize && c.major >= 6 && effort === "minimal") {
136
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
137
+ }
138
+ const result = {
139
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
140
+ };
141
+ if (effort !== void 0) {
142
+ if (api === "responses") result.reasoning = { effort };
143
+ else result.reasoning_effort = effort;
144
+ }
145
+ if (temperature !== void 0) {
146
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
147
+ result.temperature = temperature;
148
+ } else {
149
+ (options.onWarning ?? console.warn)(
150
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
151
+ );
152
+ }
153
+ }
154
+ return result;
155
+ }
156
+
3
157
  // src/adapters/base.ts
4
158
  function stringifyForDebug(value) {
5
159
  return JSON.stringify(
@@ -106,16 +260,6 @@ function normalizeObjectJsonSchema(schema) {
106
260
  }
107
261
  return normalized;
108
262
  }
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
263
  function toOpenAIResponseFormat(rf) {
120
264
  if (!rf) return void 0;
121
265
  if (rf.type === "json_object") return { type: "json_object" };
@@ -200,7 +344,7 @@ function messageToOpenAIContent(message) {
200
344
  }
201
345
  return blocks;
202
346
  }
203
- function formatMessagesForOpenAI(messages, systemPrompt) {
347
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
204
348
  const formatted = [];
205
349
  if (systemPrompt) {
206
350
  formatted.push({ role: "system", content: systemPrompt });
@@ -218,7 +362,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
218
362
  const assistantMsg = {
219
363
  role: "assistant",
220
364
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
221
- content: hasToolCalls ? msg.content || null : msg.content
365
+ content: hasToolCalls ? msg.content || null : msg.content,
366
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
222
367
  };
223
368
  if (hasToolCalls) {
224
369
  assistantMsg.tool_calls = msg.tool_calls;
@@ -227,7 +372,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
227
372
  } else if (msg.role === "tool" && msg.tool_call_id) {
228
373
  formatted.push({
229
374
  role: "tool",
230
- content: msg.content ?? "",
375
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
376
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
231
377
  tool_call_id: msg.tool_call_id
232
378
  });
233
379
  }
@@ -434,7 +580,7 @@ function formatMessages(messages) {
434
580
  return {
435
581
  role: "tool",
436
582
  tool_call_id: msg.toolCallId,
437
- content: msg.content
583
+ content: textToolOutput(msg.modelOutput, msg.content)
438
584
  };
439
585
  default:
440
586
  return msg;
@@ -442,6 +588,86 @@ function formatMessages(messages) {
442
588
  });
443
589
  }
444
590
 
591
+ // src/core/openai-responses.ts
592
+ function resolveOpenAITransport(defaults, overrides) {
593
+ return {
594
+ api: overrides?.api ?? defaults.api ?? "auto",
595
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
596
+ };
597
+ }
598
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
599
+ const api = transport.api ?? "auto";
600
+ if (!["auto", "chat", "responses"].includes(api)) {
601
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
602
+ }
603
+ if (api === "chat") {
604
+ if (transport.responses !== void 0 || nativeToolSearch) {
605
+ throw new Error(
606
+ "Responses options and native tool search require the Responses API"
607
+ );
608
+ }
609
+ return false;
610
+ }
611
+ let officialEndpoint = !baseURL;
612
+ try {
613
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
614
+ } catch {
615
+ }
616
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
617
+ }
618
+ function responseMetadata(response) {
619
+ const output = Array.isArray(response?.output) ? response.output : [];
620
+ return {
621
+ openai: {
622
+ response: {
623
+ id: response?.id,
624
+ model: response?.model,
625
+ status: response?.status,
626
+ output,
627
+ usage: response?.usage,
628
+ store: response?.store,
629
+ refusals: output.flatMap(
630
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
631
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
632
+ ).map((part) => part.refusal) : []
633
+ )
634
+ }
635
+ }
636
+ };
637
+ }
638
+ function getResponseReplay(metadata) {
639
+ const response = metadata?.openai?.response;
640
+ if (response === void 0) return void 0;
641
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
642
+ throw new Error(
643
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
644
+ );
645
+ }
646
+ return response.output;
647
+ }
648
+
649
+ // src/core/provider-error.ts
650
+ function providerErrorEvent(error, code) {
651
+ const details = error ?? {};
652
+ const event = {
653
+ type: "error",
654
+ message: error instanceof Error ? error.message : String(error),
655
+ code,
656
+ ...typeof details.status === "number" ? { status: details.status } : {},
657
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
658
+ ...typeof details.param === "string" ? { param: details.param } : {},
659
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
660
+ };
661
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
662
+ return event;
663
+ }
664
+ function throwIfAborted(signal) {
665
+ if (!signal?.aborted) return;
666
+ const error = new Error("The operation was aborted");
667
+ error.name = "AbortError";
668
+ throw error;
669
+ }
670
+
445
671
  // src/core/utils.ts
446
672
  function generateId(prefix = "id") {
447
673
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -473,39 +699,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
473
699
  const { default: OpenAI } = await import('openai');
474
700
  this.client = new OpenAI({
475
701
  apiKey: this.config.apiKey,
476
- baseURL: this.config.baseUrl
702
+ baseURL: this.config.baseUrl,
703
+ organization: this.config.organization,
704
+ defaultHeaders: this.config.headers
477
705
  });
478
706
  }
479
707
  return this.client;
480
708
  }
709
+ parameterOptions(request) {
710
+ return {
711
+ ...this.config,
712
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
713
+ };
714
+ }
715
+ toolDefinitions(request) {
716
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
717
+ return formatTools(request.actions ?? []).map((tool) => ({
718
+ name: tool.function.name,
719
+ description: tool.function.description ?? "",
720
+ location: "server",
721
+ inputSchema: tool.function.parameters
722
+ }));
723
+ }
481
724
  shouldUseResponsesApi(request) {
482
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
483
- if (!hasTools) return false;
484
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
485
- return true;
486
- }
487
- return this.requiresResponsesApiForTools(
488
- request.config?.model || this.model
725
+ return useOpenAIResponses(
726
+ request.config?.model || this.model,
727
+ this.config.baseUrl,
728
+ this.parameterOptions(request),
729
+ resolveOpenAITransport(this.config, request.config?.openai),
730
+ this.toolDefinitions(request).length > 0,
731
+ !!(request.rawMessages ?? request.messages)?.some(
732
+ (message) => message.providerMetadata?.openai?.response
733
+ ),
734
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
489
735
  );
490
736
  }
491
- /**
492
- * Whether this model rejects function tools on /v1/chat/completions.
493
- *
494
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
495
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
496
- * restriction and may not implement /v1/responses at all.
497
- */
498
- requiresResponsesApiForTools(modelId) {
499
- if (!modelId) return false;
500
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
501
- const baseUrl = this.config.baseUrl;
502
- if (!baseUrl) return true;
503
- try {
504
- return new URL(baseUrl).hostname === "api.openai.com";
505
- } catch {
506
- return false;
507
- }
508
- }
509
737
  /**
510
738
  * Convert one message's content to Responses content parts.
511
739
  *
@@ -536,11 +764,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
536
764
  });
537
765
  }
538
766
  buildResponsesInput(request) {
539
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
767
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
540
768
  const input = [];
541
769
  for (const message of sourceMessages) {
542
770
  if (message.role === "system") {
543
- if (!request.systemPrompt && message.content) {
771
+ if (message.content && message.content !== request.systemPrompt) {
544
772
  input.push({
545
773
  type: "message",
546
774
  role: "developer",
@@ -550,6 +778,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
550
778
  continue;
551
779
  }
552
780
  if (message.role === "assistant") {
781
+ const replay = getResponseReplay(message.providerMetadata);
782
+ if (replay) {
783
+ input.push(...replay);
784
+ continue;
785
+ }
553
786
  const content = this.toResponsesContent(message.content, "assistant");
554
787
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
555
788
  input.push({
@@ -573,7 +806,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
573
806
  input.push({
574
807
  type: "function_call_output",
575
808
  call_id: message.tool_call_id,
576
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
809
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
577
810
  });
578
811
  continue;
579
812
  }
@@ -586,6 +819,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
586
819
  }
587
820
  return input;
588
821
  }
822
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
823
+ formatRawMessages(messages, preserveProviderMetadata = false) {
824
+ return messages.map((message) => {
825
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
826
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
827
+ if (preserveProviderMetadata && modelOutput)
828
+ rest.modelOutput = modelOutput;
829
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
830
+ content = null;
831
+ }
832
+ if (Array.isArray(attachments) && attachments.length > 0) {
833
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
834
+ (attachment) => attachmentToOpenAIImage(attachment)
835
+ ).filter((image) => image !== null);
836
+ if (images.length > 0) {
837
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
838
+ content = [...parts, ...images];
839
+ }
840
+ }
841
+ return {
842
+ ...rest,
843
+ content,
844
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
845
+ };
846
+ });
847
+ }
589
848
  /**
590
849
  * Build the web_search tool for the Responses API, or undefined when web
591
850
  * search is off.
@@ -664,20 +923,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
664
923
  ).map((part) => part.text) : []
665
924
  ).join("") : "";
666
925
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
667
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
668
- id: item.call_id ?? item.id ?? generateToolCallId(),
669
- name: item.name,
670
- args: (() => {
671
- try {
672
- return JSON.parse(item.arguments ?? "{}");
673
- } catch {
674
- return {};
675
- }
676
- })()
677
- })) : [];
926
+ const seenCallIds = /* @__PURE__ */ new Set();
927
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
928
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
929
+ throw new Error("Responses contains an incomplete function call");
930
+ }
931
+ if (seenCallIds.has(item.call_id))
932
+ throw new Error("Responses contains duplicate function call IDs");
933
+ seenCallIds.add(item.call_id);
934
+ const args = JSON.parse(item.arguments);
935
+ if (!args || typeof args !== "object" || Array.isArray(args))
936
+ throw new Error(
937
+ "Responses function arguments must be a JSON object"
938
+ );
939
+ return { id: item.call_id, name: item.name, args };
940
+ }) : [];
678
941
  return {
679
942
  content,
680
943
  toolCalls,
944
+ providerMetadata: responseMetadata(response),
681
945
  usage: response?.usage ? {
682
946
  promptTokens: response.usage.input_tokens ?? 0,
683
947
  completionTokens: response.usage.output_tokens ?? 0,
@@ -688,6 +952,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
688
952
  }
689
953
  buildResponsesPayload(request, stream) {
690
954
  const openaiToolOptions = request.providerToolOptions?.openai;
955
+ const options = resolveOpenAITransport(
956
+ this.config,
957
+ request.config?.openai
958
+ ).responses;
959
+ const parameters = buildOpenAIParameters(
960
+ request.config?.model || this.model,
961
+ request.config?.maxTokens ?? this.config.maxTokens,
962
+ request.config?.temperature ?? this.config.temperature,
963
+ this.parameterOptions(request),
964
+ "responses"
965
+ );
691
966
  const responsesTextFormat = toOpenAIResponsesTextFormat(
692
967
  request.config?.responseFormat
693
968
  );
@@ -697,18 +972,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
697
972
  instructions: request.systemPrompt,
698
973
  input: this.buildResponsesInput(request),
699
974
  tools: this.buildResponsesTools(
700
- request.toolDefinitions ?? [],
975
+ this.toolDefinitions(request),
701
976
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
702
977
  this.buildResponsesWebSearchTool(request)
703
978
  ),
704
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
979
+ tool_choice: openaiToolOptions?.toolChoice,
705
980
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
706
- // Reasoning models reject `temperature` outright — sending it would swap one
707
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
708
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
709
- temperature: request.config?.temperature ?? this.config.temperature
710
- },
711
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
981
+ ...parameters,
982
+ ...options?.reasoningSummary ? {
983
+ reasoning: {
984
+ ...parameters.reasoning,
985
+ summary: options.reasoningSummary
986
+ }
987
+ } : {},
988
+ ...options?.store !== void 0 ? { store: options.store } : {},
989
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
990
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
991
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
992
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
993
+ ...options?.store === false || options?.include ? {
994
+ include: [
995
+ .../* @__PURE__ */ new Set([
996
+ ...options?.include ?? [],
997
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
998
+ ])
999
+ ]
1000
+ } : {},
712
1001
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
713
1002
  stream
714
1003
  };
@@ -716,10 +1005,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
716
1005
  }
717
1006
  async completeWithResponses(request) {
718
1007
  const client = await this.getClient();
1008
+ throwIfAborted(request.signal);
719
1009
  const payload = this.buildResponsesPayload(request, false);
720
1010
  logProviderPayload("openai", "request payload", payload, request.debug);
721
- const response = await client.responses.create(payload);
1011
+ if (!client.responses?.create)
1012
+ throw new Error(
1013
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1014
+ );
1015
+ const response = await client.responses.create(payload, {
1016
+ signal: request.signal
1017
+ });
1018
+ throwIfAborted(request.signal);
722
1019
  logProviderPayload("openai", "response payload", response, request.debug);
1020
+ if (response.status && response.status !== "completed") {
1021
+ throw Object.assign(
1022
+ new Error(
1023
+ response.error?.message ?? `Responses request ${response.status}`
1024
+ ),
1025
+ response.error
1026
+ );
1027
+ }
723
1028
  return this.parseResponsesResult(response);
724
1029
  }
725
1030
  /**
@@ -737,13 +1042,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
737
1042
  */
738
1043
  async *streamWithResponses(request) {
739
1044
  const client = await this.getClient();
1045
+ throwIfAborted(request.signal);
740
1046
  const payload = this.buildResponsesPayload(request, true);
741
1047
  logProviderPayload("openai", "request payload", payload, request.debug);
742
- const stream = await client.responses.create(payload);
1048
+ if (!client.responses?.create)
1049
+ throw new Error(
1050
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1051
+ );
1052
+ const stream = await client.responses.create(payload, {
1053
+ signal: request.signal
1054
+ });
743
1055
  const toolCalls = /* @__PURE__ */ new Map();
744
1056
  let usage;
745
1057
  const citations = [];
1058
+ let completed = false;
1059
+ let terminalResponse;
1060
+ let responseIdentity = {};
1061
+ const outputItems = /* @__PURE__ */ new Map();
1062
+ let streamedText = "";
746
1063
  function* flushToolCalls() {
1064
+ for (const call of toolCalls.values()) {
1065
+ if (!call.name)
1066
+ throw new Error("Responses tool call is missing its name");
1067
+ JSON.parse(call.arguments || "{}");
1068
+ }
747
1069
  for (const call of toolCalls.values()) {
748
1070
  yield { type: "action:start", id: call.id, name: call.name };
749
1071
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -752,19 +1074,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
752
1074
  toolCalls.clear();
753
1075
  }
754
1076
  for await (const event of stream) {
755
- if (request.signal?.aborted) break;
1077
+ throwIfAborted(request.signal);
756
1078
  logProviderPayload("openai", "stream chunk", event, request.debug);
1079
+ if (completed)
1080
+ throw new Error("Responses stream emitted an event after completion");
757
1081
  const index = event?.output_index ?? 0;
758
1082
  switch (event?.type) {
1083
+ case "response.created":
1084
+ responseIdentity = {
1085
+ id: event.response?.id,
1086
+ model: event.response?.model
1087
+ };
1088
+ break;
1089
+ case "response.output_item.done":
1090
+ if (event.item) outputItems.set(index, event.item);
1091
+ if (event.item?.type === "function_call") {
1092
+ toolCalls.set(index, {
1093
+ id: event.item.call_id,
1094
+ name: event.item.name,
1095
+ arguments: event.item.arguments
1096
+ });
1097
+ }
1098
+ break;
759
1099
  case "response.output_text.delta":
760
1100
  if (event.delta) {
1101
+ streamedText += event.delta;
761
1102
  yield { type: "message:delta", content: event.delta };
762
1103
  }
763
1104
  break;
764
1105
  case "response.output_item.added":
765
1106
  if (event.item?.type === "function_call") {
766
1107
  toolCalls.set(index, {
767
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1108
+ id: event.item.call_id ?? "",
768
1109
  name: event.item.name ?? "",
769
1110
  arguments: event.item.arguments ?? ""
770
1111
  });
@@ -782,8 +1123,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
782
1123
  }
783
1124
  break;
784
1125
  }
785
- case "response.completed":
786
- case "response.incomplete": {
1126
+ case "response.completed": {
1127
+ if (event.response?.status && event.response.status !== "completed")
1128
+ throw new Error(`Responses stream ${event.response.status}`);
1129
+ completed = true;
1130
+ terminalResponse = { ...responseIdentity, ...event.response };
787
1131
  const found = this.collectResponsesCitations(
788
1132
  event.response?.output,
789
1133
  citations.length
@@ -799,21 +1143,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
799
1143
  }
800
1144
  break;
801
1145
  }
802
- case "error":
803
- case "response.failed":
1146
+ case "response.incomplete":
804
1147
  throw new Error(
805
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1148
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
806
1149
  );
1150
+ case "error":
1151
+ case "response.failed": {
1152
+ const details = event.response?.error ?? event;
1153
+ throw Object.assign(
1154
+ new Error(details.message ?? "Responses stream failed"),
1155
+ {
1156
+ code: details.code,
1157
+ param: details.param,
1158
+ status: details.status
1159
+ }
1160
+ );
1161
+ }
1162
+ }
1163
+ }
1164
+ throwIfAborted(request.signal);
1165
+ if (!completed)
1166
+ throw new Error("Responses stream ended without a completed response");
1167
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1168
+ if (output.some((item) => item.type === "function_call"))
1169
+ toolCalls.clear();
1170
+ for (const [index, item] of output.entries()) {
1171
+ if (item.type === "function_call") {
1172
+ toolCalls.set(index, {
1173
+ id: item.call_id,
1174
+ name: item.name,
1175
+ arguments: item.arguments
1176
+ });
807
1177
  }
808
1178
  }
1179
+ const uniqueCalls = /* @__PURE__ */ new Map();
1180
+ for (const call of toolCalls.values()) {
1181
+ if (!call.id || !call.name)
1182
+ throw new Error("Responses stream contains an incomplete tool call");
1183
+ uniqueCalls.set(call.id, call);
1184
+ }
1185
+ toolCalls.clear();
1186
+ [...uniqueCalls.values()].forEach(
1187
+ (call, index) => toolCalls.set(index, call)
1188
+ );
1189
+ if (!output.length && toolCalls.size) {
1190
+ for (const call of toolCalls.values())
1191
+ output.push({
1192
+ type: "function_call",
1193
+ call_id: call.id,
1194
+ name: call.name,
1195
+ arguments: call.arguments
1196
+ });
1197
+ }
1198
+ if (!output.length && streamedText)
1199
+ output.push({
1200
+ type: "message",
1201
+ role: "assistant",
1202
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1203
+ });
1204
+ terminalResponse.output = output;
1205
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1206
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1207
+ yield {
1208
+ type: "message:delta",
1209
+ content: finalText.slice(streamedText.length)
1210
+ };
1211
+ }
809
1212
  yield* flushToolCalls();
810
1213
  if (citations.length > 0) {
811
1214
  yield { type: "citation", citations: deduplicateCitations(citations) };
812
1215
  }
813
- yield { type: "message:end" };
814
- yield { type: "done", usage };
1216
+ yield {
1217
+ type: "message:end",
1218
+ providerMetadata: responseMetadata(terminalResponse)
1219
+ };
1220
+ yield {
1221
+ type: "done",
1222
+ usage,
1223
+ providerMetadata: responseMetadata(terminalResponse)
1224
+ };
815
1225
  }
816
1226
  async *stream(request) {
1227
+ throwIfAborted(request.signal);
817
1228
  if (this.shouldUseResponsesApi(request)) {
818
1229
  const messageId2 = generateMessageId();
819
1230
  yield { type: "message:start", id: messageId2 };
@@ -821,47 +1232,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
821
1232
  yield* this.streamWithResponses(request);
822
1233
  return;
823
1234
  } catch (error) {
824
- yield {
825
- type: "error",
826
- message: error instanceof Error ? error.message : "Unknown error",
827
- code: "OPENAI_RESPONSES_ERROR"
828
- };
1235
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
829
1236
  return;
830
1237
  }
831
1238
  }
832
1239
  const client = await this.getClient();
833
1240
  let messages;
834
1241
  if (request.rawMessages && request.rawMessages.length > 0) {
835
- const processedMessages = request.rawMessages.map((msg) => {
836
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
837
- return { ...msg, content: null };
838
- }
839
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
840
- if (hasAttachments) {
841
- const content = [];
842
- if (msg.content) {
843
- content.push({ type: "text", text: msg.content });
844
- }
845
- for (const attachment of msg.attachments) {
846
- if (attachment.type === "image") {
847
- let imageUrl;
848
- if (attachment.url) {
849
- imageUrl = attachment.url;
850
- } else if (attachment.data) {
851
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
852
- } else {
853
- continue;
854
- }
855
- content.push({
856
- type: "image_url",
857
- image_url: { url: imageUrl, detail: "auto" }
858
- });
859
- }
860
- }
861
- return { ...msg, content, attachments: void 0 };
862
- }
863
- return msg;
864
- });
1242
+ const processedMessages = this.formatRawMessages(request.rawMessages);
865
1243
  if (request.systemPrompt) {
866
1244
  const hasSystem = processedMessages.some((m) => m.role === "system");
867
1245
  if (!hasSystem) {
@@ -881,7 +1259,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
881
1259
  request.systemPrompt
882
1260
  );
883
1261
  }
884
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1262
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1263
+ type: "function",
1264
+ function: {
1265
+ name: tool.name,
1266
+ description: tool.description,
1267
+ parameters: normalizeObjectJsonSchema(
1268
+ tool.inputSchema
1269
+ )
1270
+ }
1271
+ })) : formatTools(request.actions ?? []);
885
1272
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
886
1273
  if (webSearchConfig) {
887
1274
  const webSearchTool = {
@@ -910,26 +1297,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
910
1297
  tools: tools.length > 0 ? tools : void 0,
911
1298
  tool_choice: tools.length > 0 ? toolChoice : void 0,
912
1299
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
913
- ...buildOpenAITokenParams(
1300
+ ...buildOpenAIParameters(
914
1301
  modelIdForPayload,
915
1302
  request.config?.maxTokens ?? this.config.maxTokens,
916
- request.config?.temperature ?? this.config.temperature
1303
+ request.config?.temperature ?? this.config.temperature,
1304
+ this.parameterOptions(request)
917
1305
  ),
918
1306
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
919
1307
  stream: true,
920
1308
  stream_options: { include_usage: true }
921
1309
  };
922
1310
  logProviderPayload("openai", "request payload", payload, request.debug);
923
- const stream = await client.chat.completions.create(payload);
924
- let currentToolCall = null;
1311
+ const stream = await client.chat.completions.create(payload, {
1312
+ signal: request.signal
1313
+ });
1314
+ const toolCalls = /* @__PURE__ */ new Map();
1315
+ let finishReason;
925
1316
  const collectedCitations = [];
926
1317
  let citationIndex = 0;
927
1318
  let usage;
928
1319
  for await (const chunk of stream) {
929
1320
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
930
- if (request.signal?.aborted) {
931
- break;
932
- }
1321
+ throwIfAborted(request.signal);
933
1322
  const delta = chunk.choices[0]?.delta;
934
1323
  const choice = chunk.choices[0];
935
1324
  if (delta?.content) {
@@ -952,38 +1341,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
952
1341
  }
953
1342
  }
954
1343
  }
955
- if (delta?.tool_calls) {
956
- for (const toolCall of delta.tool_calls) {
957
- if (toolCall.id) {
958
- if (currentToolCall) {
959
- yield {
960
- type: "action:args",
961
- id: currentToolCall.id,
962
- args: currentToolCall.arguments
963
- };
964
- yield {
965
- type: "action:end",
966
- id: currentToolCall.id,
967
- name: currentToolCall.name
968
- };
969
- }
970
- const tcExtraContent = toolCall.extra_content;
971
- currentToolCall = {
972
- id: toolCall.id,
973
- name: toolCall.function?.name || "",
974
- arguments: toolCall.function?.arguments || "",
975
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
976
- };
977
- yield {
978
- type: "action:start",
979
- id: currentToolCall.id,
980
- name: currentToolCall.name,
981
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
982
- };
983
- } else if (currentToolCall && toolCall.function?.arguments) {
984
- currentToolCall.arguments += toolCall.function.arguments;
985
- }
986
- }
1344
+ for (const part of delta?.tool_calls ?? []) {
1345
+ const index = part.index ?? 0;
1346
+ const call = toolCalls.get(index) ?? {
1347
+ id: "",
1348
+ name: "",
1349
+ arguments: ""
1350
+ };
1351
+ if (part.id) call.id = part.id;
1352
+ if (part.function?.name) call.name += part.function.name;
1353
+ if (part.function?.arguments)
1354
+ call.arguments += part.function.arguments;
1355
+ if (part.extra_content) call.extra_content = part.extra_content;
1356
+ toolCalls.set(index, call);
987
1357
  }
988
1358
  if (chunk.usage) {
989
1359
  usage = {
@@ -992,21 +1362,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
992
1362
  total_tokens: chunk.usage.total_tokens
993
1363
  };
994
1364
  }
995
- if (choice?.finish_reason) {
996
- if (currentToolCall) {
997
- yield {
998
- type: "action:args",
999
- id: currentToolCall.id,
1000
- args: currentToolCall.arguments
1001
- };
1002
- yield {
1003
- type: "action:end",
1004
- id: currentToolCall.id,
1005
- name: currentToolCall.name
1006
- };
1007
- currentToolCall = null;
1008
- }
1009
- }
1365
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1366
+ }
1367
+ throwIfAborted(request.signal);
1368
+ if (!finishReason)
1369
+ throw new Error("OpenAI stream ended without a finish reason");
1370
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1371
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1372
+ }
1373
+ for (const call of toolCalls.values()) {
1374
+ if (!call.id || !call.name)
1375
+ throw new Error("OpenAI stream contains an incomplete tool call");
1376
+ JSON.parse(call.arguments || "{}");
1377
+ }
1378
+ for (const call of toolCalls.values()) {
1379
+ yield {
1380
+ type: "action:start",
1381
+ id: call.id,
1382
+ name: call.name,
1383
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1384
+ };
1385
+ yield { type: "action:args", id: call.id, args: call.arguments };
1386
+ yield { type: "action:end", id: call.id, name: call.name };
1010
1387
  }
1011
1388
  if (collectedCitations.length > 0) {
1012
1389
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1015,26 +1392,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1015
1392
  yield { type: "message:end" };
1016
1393
  yield { type: "done", usage };
1017
1394
  } catch (error) {
1018
- yield {
1019
- type: "error",
1020
- message: error instanceof Error ? error.message : "Unknown error",
1021
- code: `${this.provider.toUpperCase()}_ERROR`
1022
- };
1395
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1023
1396
  }
1024
1397
  }
1025
1398
  async complete(request) {
1399
+ throwIfAborted(request.signal);
1026
1400
  if (this.shouldUseResponsesApi(request)) {
1027
1401
  return this.completeWithResponses(request);
1028
1402
  }
1029
1403
  const client = await this.getClient();
1030
1404
  let messages;
1031
1405
  if (request.rawMessages && request.rawMessages.length > 0) {
1032
- const sanitized = request.rawMessages.map((msg) => {
1033
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1034
- return { ...msg, content: null };
1035
- }
1036
- return msg;
1037
- });
1406
+ const sanitized = this.formatRawMessages(request.rawMessages);
1038
1407
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1039
1408
  messages = [
1040
1409
  { role: "system", content: request.systemPrompt },
@@ -1049,7 +1418,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1049
1418
  request.systemPrompt
1050
1419
  );
1051
1420
  }
1052
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1421
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1422
+ type: "function",
1423
+ function: {
1424
+ name: tool.name,
1425
+ description: tool.description,
1426
+ parameters: normalizeObjectJsonSchema(
1427
+ tool.inputSchema
1428
+ )
1429
+ }
1430
+ })) : formatTools(request.actions ?? []);
1053
1431
  const openaiToolOptions = request.providerToolOptions?.openai;
1054
1432
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1055
1433
  type: "function",
@@ -1064,16 +1442,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1064
1442
  tools: tools.length > 0 ? tools : void 0,
1065
1443
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1066
1444
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1067
- ...buildOpenAITokenParams(
1445
+ ...buildOpenAIParameters(
1068
1446
  modelIdForCompletePayload,
1069
1447
  request.config?.maxTokens ?? this.config.maxTokens,
1070
- request.config?.temperature ?? this.config.temperature
1448
+ request.config?.temperature ?? this.config.temperature,
1449
+ this.parameterOptions(request)
1071
1450
  ),
1072
1451
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1073
1452
  stream: false
1074
1453
  };
1075
1454
  logProviderPayload("openai", "request payload", payload, request.debug);
1076
- const response = await client.chat.completions.create(payload);
1455
+ const response = await client.chat.completions.create(payload, {
1456
+ signal: request.signal
1457
+ });
1077
1458
  logProviderPayload("openai", "response payload", response, request.debug);
1078
1459
  const choice = response.choices?.[0];
1079
1460
  const message = choice?.message;