@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
  }
@@ -464,7 +610,7 @@ function formatMessagesForOpenRouter(messages) {
464
610
  return {
465
611
  role: "tool",
466
612
  tool_call_id: msg.toolCallId,
467
- content: msg.content
613
+ content: textToolOutput(msg.modelOutput, msg.content)
468
614
  };
469
615
  default:
470
616
  return msg;
@@ -495,6 +641,86 @@ async function searchOpenRouterModels(query, apiKey) {
495
641
  );
496
642
  }
497
643
 
644
+ // src/core/openai-responses.ts
645
+ function resolveOpenAITransport(defaults, overrides) {
646
+ return {
647
+ api: overrides?.api ?? defaults.api ?? "auto",
648
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
649
+ };
650
+ }
651
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
652
+ const api = transport.api ?? "auto";
653
+ if (!["auto", "chat", "responses"].includes(api)) {
654
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
655
+ }
656
+ if (api === "chat") {
657
+ if (transport.responses !== void 0 || nativeToolSearch) {
658
+ throw new Error(
659
+ "Responses options and native tool search require the Responses API"
660
+ );
661
+ }
662
+ return false;
663
+ }
664
+ let officialEndpoint = !baseURL;
665
+ try {
666
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
667
+ } catch {
668
+ }
669
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
670
+ }
671
+ function responseMetadata(response) {
672
+ const output = Array.isArray(response?.output) ? response.output : [];
673
+ return {
674
+ openai: {
675
+ response: {
676
+ id: response?.id,
677
+ model: response?.model,
678
+ status: response?.status,
679
+ output,
680
+ usage: response?.usage,
681
+ store: response?.store,
682
+ refusals: output.flatMap(
683
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
684
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
685
+ ).map((part) => part.refusal) : []
686
+ )
687
+ }
688
+ }
689
+ };
690
+ }
691
+ function getResponseReplay(metadata) {
692
+ const response = metadata?.openai?.response;
693
+ if (response === void 0) return void 0;
694
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
695
+ throw new Error(
696
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
697
+ );
698
+ }
699
+ return response.output;
700
+ }
701
+
702
+ // src/core/provider-error.ts
703
+ function providerErrorEvent(error, code) {
704
+ const details = error ?? {};
705
+ const event = {
706
+ type: "error",
707
+ message: error instanceof Error ? error.message : String(error),
708
+ code,
709
+ ...typeof details.status === "number" ? { status: details.status } : {},
710
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
711
+ ...typeof details.param === "string" ? { param: details.param } : {},
712
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
713
+ };
714
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
715
+ return event;
716
+ }
717
+ function throwIfAborted(signal) {
718
+ if (!signal?.aborted) return;
719
+ const error = new Error("The operation was aborted");
720
+ error.name = "AbortError";
721
+ throw error;
722
+ }
723
+
498
724
  // src/core/utils.ts
499
725
  function generateId(prefix = "id") {
500
726
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -526,39 +752,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
526
752
  const { default: OpenAI } = await import('openai');
527
753
  this.client = new OpenAI({
528
754
  apiKey: this.config.apiKey,
529
- baseURL: this.config.baseUrl
755
+ baseURL: this.config.baseUrl,
756
+ organization: this.config.organization,
757
+ defaultHeaders: this.config.headers
530
758
  });
531
759
  }
532
760
  return this.client;
533
761
  }
762
+ parameterOptions(request) {
763
+ return {
764
+ ...this.config,
765
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
766
+ };
767
+ }
768
+ toolDefinitions(request) {
769
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
770
+ return formatTools(request.actions ?? []).map((tool) => ({
771
+ name: tool.function.name,
772
+ description: tool.function.description ?? "",
773
+ location: "server",
774
+ inputSchema: tool.function.parameters
775
+ }));
776
+ }
534
777
  shouldUseResponsesApi(request) {
535
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
536
- if (!hasTools) return false;
537
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
538
- return true;
539
- }
540
- return this.requiresResponsesApiForTools(
541
- request.config?.model || this.model
778
+ return useOpenAIResponses(
779
+ request.config?.model || this.model,
780
+ this.config.baseUrl,
781
+ this.parameterOptions(request),
782
+ resolveOpenAITransport(this.config, request.config?.openai),
783
+ this.toolDefinitions(request).length > 0,
784
+ !!(request.rawMessages ?? request.messages)?.some(
785
+ (message) => message.providerMetadata?.openai?.response
786
+ ),
787
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
542
788
  );
543
789
  }
544
- /**
545
- * Whether this model rejects function tools on /v1/chat/completions.
546
- *
547
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
548
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
549
- * restriction and may not implement /v1/responses at all.
550
- */
551
- requiresResponsesApiForTools(modelId) {
552
- if (!modelId) return false;
553
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
554
- const baseUrl = this.config.baseUrl;
555
- if (!baseUrl) return true;
556
- try {
557
- return new URL(baseUrl).hostname === "api.openai.com";
558
- } catch {
559
- return false;
560
- }
561
- }
562
790
  /**
563
791
  * Convert one message's content to Responses content parts.
564
792
  *
@@ -589,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
589
817
  });
590
818
  }
591
819
  buildResponsesInput(request) {
592
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
820
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
593
821
  const input = [];
594
822
  for (const message of sourceMessages) {
595
823
  if (message.role === "system") {
596
- if (!request.systemPrompt && message.content) {
824
+ if (message.content && message.content !== request.systemPrompt) {
597
825
  input.push({
598
826
  type: "message",
599
827
  role: "developer",
@@ -603,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
603
831
  continue;
604
832
  }
605
833
  if (message.role === "assistant") {
834
+ const replay = getResponseReplay(message.providerMetadata);
835
+ if (replay) {
836
+ input.push(...replay);
837
+ continue;
838
+ }
606
839
  const content = this.toResponsesContent(message.content, "assistant");
607
840
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
608
841
  input.push({
@@ -626,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
626
859
  input.push({
627
860
  type: "function_call_output",
628
861
  call_id: message.tool_call_id,
629
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
862
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
630
863
  });
631
864
  continue;
632
865
  }
@@ -639,6 +872,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
639
872
  }
640
873
  return input;
641
874
  }
875
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
876
+ formatRawMessages(messages, preserveProviderMetadata = false) {
877
+ return messages.map((message) => {
878
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
879
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
880
+ if (preserveProviderMetadata && modelOutput)
881
+ rest.modelOutput = modelOutput;
882
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
883
+ content = null;
884
+ }
885
+ if (Array.isArray(attachments) && attachments.length > 0) {
886
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
887
+ (attachment) => attachmentToOpenAIImage(attachment)
888
+ ).filter((image) => image !== null);
889
+ if (images.length > 0) {
890
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
891
+ content = [...parts, ...images];
892
+ }
893
+ }
894
+ return {
895
+ ...rest,
896
+ content,
897
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
898
+ };
899
+ });
900
+ }
642
901
  /**
643
902
  * Build the web_search tool for the Responses API, or undefined when web
644
903
  * search is off.
@@ -717,20 +976,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
717
976
  ).map((part) => part.text) : []
718
977
  ).join("") : "";
719
978
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
720
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
721
- id: item.call_id ?? item.id ?? generateToolCallId(),
722
- name: item.name,
723
- args: (() => {
724
- try {
725
- return JSON.parse(item.arguments ?? "{}");
726
- } catch {
727
- return {};
728
- }
729
- })()
730
- })) : [];
979
+ const seenCallIds = /* @__PURE__ */ new Set();
980
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
981
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
982
+ throw new Error("Responses contains an incomplete function call");
983
+ }
984
+ if (seenCallIds.has(item.call_id))
985
+ throw new Error("Responses contains duplicate function call IDs");
986
+ seenCallIds.add(item.call_id);
987
+ const args = JSON.parse(item.arguments);
988
+ if (!args || typeof args !== "object" || Array.isArray(args))
989
+ throw new Error(
990
+ "Responses function arguments must be a JSON object"
991
+ );
992
+ return { id: item.call_id, name: item.name, args };
993
+ }) : [];
731
994
  return {
732
995
  content,
733
996
  toolCalls,
997
+ providerMetadata: responseMetadata(response),
734
998
  usage: response?.usage ? {
735
999
  promptTokens: response.usage.input_tokens ?? 0,
736
1000
  completionTokens: response.usage.output_tokens ?? 0,
@@ -741,6 +1005,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
741
1005
  }
742
1006
  buildResponsesPayload(request, stream) {
743
1007
  const openaiToolOptions = request.providerToolOptions?.openai;
1008
+ const options = resolveOpenAITransport(
1009
+ this.config,
1010
+ request.config?.openai
1011
+ ).responses;
1012
+ const parameters = buildOpenAIParameters(
1013
+ request.config?.model || this.model,
1014
+ request.config?.maxTokens ?? this.config.maxTokens,
1015
+ request.config?.temperature ?? this.config.temperature,
1016
+ this.parameterOptions(request),
1017
+ "responses"
1018
+ );
744
1019
  const responsesTextFormat = toOpenAIResponsesTextFormat(
745
1020
  request.config?.responseFormat
746
1021
  );
@@ -750,18 +1025,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
750
1025
  instructions: request.systemPrompt,
751
1026
  input: this.buildResponsesInput(request),
752
1027
  tools: this.buildResponsesTools(
753
- request.toolDefinitions ?? [],
1028
+ this.toolDefinitions(request),
754
1029
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
755
1030
  this.buildResponsesWebSearchTool(request)
756
1031
  ),
757
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
1032
+ tool_choice: openaiToolOptions?.toolChoice,
758
1033
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
759
- // Reasoning models reject `temperature` outright — sending it would swap one
760
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
761
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
762
- temperature: request.config?.temperature ?? this.config.temperature
763
- },
764
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
1034
+ ...parameters,
1035
+ ...options?.reasoningSummary ? {
1036
+ reasoning: {
1037
+ ...parameters.reasoning,
1038
+ summary: options.reasoningSummary
1039
+ }
1040
+ } : {},
1041
+ ...options?.store !== void 0 ? { store: options.store } : {},
1042
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1043
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1044
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1045
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1046
+ ...options?.store === false || options?.include ? {
1047
+ include: [
1048
+ .../* @__PURE__ */ new Set([
1049
+ ...options?.include ?? [],
1050
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1051
+ ])
1052
+ ]
1053
+ } : {},
765
1054
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
766
1055
  stream
767
1056
  };
@@ -769,10 +1058,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
769
1058
  }
770
1059
  async completeWithResponses(request) {
771
1060
  const client = await this.getClient();
1061
+ throwIfAborted(request.signal);
772
1062
  const payload = this.buildResponsesPayload(request, false);
773
1063
  logProviderPayload("openai", "request payload", payload, request.debug);
774
- const response = await client.responses.create(payload);
1064
+ if (!client.responses?.create)
1065
+ throw new Error(
1066
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1067
+ );
1068
+ const response = await client.responses.create(payload, {
1069
+ signal: request.signal
1070
+ });
1071
+ throwIfAborted(request.signal);
775
1072
  logProviderPayload("openai", "response payload", response, request.debug);
1073
+ if (response.status && response.status !== "completed") {
1074
+ throw Object.assign(
1075
+ new Error(
1076
+ response.error?.message ?? `Responses request ${response.status}`
1077
+ ),
1078
+ response.error
1079
+ );
1080
+ }
776
1081
  return this.parseResponsesResult(response);
777
1082
  }
778
1083
  /**
@@ -790,13 +1095,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
790
1095
  */
791
1096
  async *streamWithResponses(request) {
792
1097
  const client = await this.getClient();
1098
+ throwIfAborted(request.signal);
793
1099
  const payload = this.buildResponsesPayload(request, true);
794
1100
  logProviderPayload("openai", "request payload", payload, request.debug);
795
- const stream = await client.responses.create(payload);
1101
+ if (!client.responses?.create)
1102
+ throw new Error(
1103
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1104
+ );
1105
+ const stream = await client.responses.create(payload, {
1106
+ signal: request.signal
1107
+ });
796
1108
  const toolCalls = /* @__PURE__ */ new Map();
797
1109
  let usage;
798
1110
  const citations = [];
1111
+ let completed = false;
1112
+ let terminalResponse;
1113
+ let responseIdentity = {};
1114
+ const outputItems = /* @__PURE__ */ new Map();
1115
+ let streamedText = "";
799
1116
  function* flushToolCalls() {
1117
+ for (const call of toolCalls.values()) {
1118
+ if (!call.name)
1119
+ throw new Error("Responses tool call is missing its name");
1120
+ JSON.parse(call.arguments || "{}");
1121
+ }
800
1122
  for (const call of toolCalls.values()) {
801
1123
  yield { type: "action:start", id: call.id, name: call.name };
802
1124
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -805,19 +1127,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
805
1127
  toolCalls.clear();
806
1128
  }
807
1129
  for await (const event of stream) {
808
- if (request.signal?.aborted) break;
1130
+ throwIfAborted(request.signal);
809
1131
  logProviderPayload("openai", "stream chunk", event, request.debug);
1132
+ if (completed)
1133
+ throw new Error("Responses stream emitted an event after completion");
810
1134
  const index = event?.output_index ?? 0;
811
1135
  switch (event?.type) {
1136
+ case "response.created":
1137
+ responseIdentity = {
1138
+ id: event.response?.id,
1139
+ model: event.response?.model
1140
+ };
1141
+ break;
1142
+ case "response.output_item.done":
1143
+ if (event.item) outputItems.set(index, event.item);
1144
+ if (event.item?.type === "function_call") {
1145
+ toolCalls.set(index, {
1146
+ id: event.item.call_id,
1147
+ name: event.item.name,
1148
+ arguments: event.item.arguments
1149
+ });
1150
+ }
1151
+ break;
812
1152
  case "response.output_text.delta":
813
1153
  if (event.delta) {
1154
+ streamedText += event.delta;
814
1155
  yield { type: "message:delta", content: event.delta };
815
1156
  }
816
1157
  break;
817
1158
  case "response.output_item.added":
818
1159
  if (event.item?.type === "function_call") {
819
1160
  toolCalls.set(index, {
820
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1161
+ id: event.item.call_id ?? "",
821
1162
  name: event.item.name ?? "",
822
1163
  arguments: event.item.arguments ?? ""
823
1164
  });
@@ -835,8 +1176,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
835
1176
  }
836
1177
  break;
837
1178
  }
838
- case "response.completed":
839
- case "response.incomplete": {
1179
+ case "response.completed": {
1180
+ if (event.response?.status && event.response.status !== "completed")
1181
+ throw new Error(`Responses stream ${event.response.status}`);
1182
+ completed = true;
1183
+ terminalResponse = { ...responseIdentity, ...event.response };
840
1184
  const found = this.collectResponsesCitations(
841
1185
  event.response?.output,
842
1186
  citations.length
@@ -852,21 +1196,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
852
1196
  }
853
1197
  break;
854
1198
  }
855
- case "error":
856
- case "response.failed":
1199
+ case "response.incomplete":
857
1200
  throw new Error(
858
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1201
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
1202
+ );
1203
+ case "error":
1204
+ case "response.failed": {
1205
+ const details = event.response?.error ?? event;
1206
+ throw Object.assign(
1207
+ new Error(details.message ?? "Responses stream failed"),
1208
+ {
1209
+ code: details.code,
1210
+ param: details.param,
1211
+ status: details.status
1212
+ }
859
1213
  );
1214
+ }
1215
+ }
1216
+ }
1217
+ throwIfAborted(request.signal);
1218
+ if (!completed)
1219
+ throw new Error("Responses stream ended without a completed response");
1220
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1221
+ if (output.some((item) => item.type === "function_call"))
1222
+ toolCalls.clear();
1223
+ for (const [index, item] of output.entries()) {
1224
+ if (item.type === "function_call") {
1225
+ toolCalls.set(index, {
1226
+ id: item.call_id,
1227
+ name: item.name,
1228
+ arguments: item.arguments
1229
+ });
860
1230
  }
861
1231
  }
1232
+ const uniqueCalls = /* @__PURE__ */ new Map();
1233
+ for (const call of toolCalls.values()) {
1234
+ if (!call.id || !call.name)
1235
+ throw new Error("Responses stream contains an incomplete tool call");
1236
+ uniqueCalls.set(call.id, call);
1237
+ }
1238
+ toolCalls.clear();
1239
+ [...uniqueCalls.values()].forEach(
1240
+ (call, index) => toolCalls.set(index, call)
1241
+ );
1242
+ if (!output.length && toolCalls.size) {
1243
+ for (const call of toolCalls.values())
1244
+ output.push({
1245
+ type: "function_call",
1246
+ call_id: call.id,
1247
+ name: call.name,
1248
+ arguments: call.arguments
1249
+ });
1250
+ }
1251
+ if (!output.length && streamedText)
1252
+ output.push({
1253
+ type: "message",
1254
+ role: "assistant",
1255
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1256
+ });
1257
+ terminalResponse.output = output;
1258
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1259
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1260
+ yield {
1261
+ type: "message:delta",
1262
+ content: finalText.slice(streamedText.length)
1263
+ };
1264
+ }
862
1265
  yield* flushToolCalls();
863
1266
  if (citations.length > 0) {
864
1267
  yield { type: "citation", citations: deduplicateCitations(citations) };
865
1268
  }
866
- yield { type: "message:end" };
867
- yield { type: "done", usage };
1269
+ yield {
1270
+ type: "message:end",
1271
+ providerMetadata: responseMetadata(terminalResponse)
1272
+ };
1273
+ yield {
1274
+ type: "done",
1275
+ usage,
1276
+ providerMetadata: responseMetadata(terminalResponse)
1277
+ };
868
1278
  }
869
1279
  async *stream(request) {
1280
+ throwIfAborted(request.signal);
870
1281
  if (this.shouldUseResponsesApi(request)) {
871
1282
  const messageId2 = generateMessageId();
872
1283
  yield { type: "message:start", id: messageId2 };
@@ -874,47 +1285,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
874
1285
  yield* this.streamWithResponses(request);
875
1286
  return;
876
1287
  } catch (error) {
877
- yield {
878
- type: "error",
879
- message: error instanceof Error ? error.message : "Unknown error",
880
- code: "OPENAI_RESPONSES_ERROR"
881
- };
1288
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
882
1289
  return;
883
1290
  }
884
1291
  }
885
1292
  const client = await this.getClient();
886
1293
  let messages;
887
1294
  if (request.rawMessages && request.rawMessages.length > 0) {
888
- const processedMessages = request.rawMessages.map((msg) => {
889
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
890
- return { ...msg, content: null };
891
- }
892
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
893
- if (hasAttachments) {
894
- const content = [];
895
- if (msg.content) {
896
- content.push({ type: "text", text: msg.content });
897
- }
898
- for (const attachment of msg.attachments) {
899
- if (attachment.type === "image") {
900
- let imageUrl;
901
- if (attachment.url) {
902
- imageUrl = attachment.url;
903
- } else if (attachment.data) {
904
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
905
- } else {
906
- continue;
907
- }
908
- content.push({
909
- type: "image_url",
910
- image_url: { url: imageUrl, detail: "auto" }
911
- });
912
- }
913
- }
914
- return { ...msg, content, attachments: void 0 };
915
- }
916
- return msg;
917
- });
1295
+ const processedMessages = this.formatRawMessages(request.rawMessages);
918
1296
  if (request.systemPrompt) {
919
1297
  const hasSystem = processedMessages.some((m) => m.role === "system");
920
1298
  if (!hasSystem) {
@@ -934,7 +1312,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
934
1312
  request.systemPrompt
935
1313
  );
936
1314
  }
937
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1315
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1316
+ type: "function",
1317
+ function: {
1318
+ name: tool.name,
1319
+ description: tool.description,
1320
+ parameters: normalizeObjectJsonSchema(
1321
+ tool.inputSchema
1322
+ )
1323
+ }
1324
+ })) : formatTools(request.actions ?? []);
938
1325
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
939
1326
  if (webSearchConfig) {
940
1327
  const webSearchTool = {
@@ -963,26 +1350,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
963
1350
  tools: tools.length > 0 ? tools : void 0,
964
1351
  tool_choice: tools.length > 0 ? toolChoice : void 0,
965
1352
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
966
- ...buildOpenAITokenParams(
1353
+ ...buildOpenAIParameters(
967
1354
  modelIdForPayload,
968
1355
  request.config?.maxTokens ?? this.config.maxTokens,
969
- request.config?.temperature ?? this.config.temperature
1356
+ request.config?.temperature ?? this.config.temperature,
1357
+ this.parameterOptions(request)
970
1358
  ),
971
1359
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
972
1360
  stream: true,
973
1361
  stream_options: { include_usage: true }
974
1362
  };
975
1363
  logProviderPayload("openai", "request payload", payload, request.debug);
976
- const stream = await client.chat.completions.create(payload);
977
- let currentToolCall = null;
1364
+ const stream = await client.chat.completions.create(payload, {
1365
+ signal: request.signal
1366
+ });
1367
+ const toolCalls = /* @__PURE__ */ new Map();
1368
+ let finishReason;
978
1369
  const collectedCitations = [];
979
1370
  let citationIndex = 0;
980
1371
  let usage;
981
1372
  for await (const chunk of stream) {
982
1373
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
983
- if (request.signal?.aborted) {
984
- break;
985
- }
1374
+ throwIfAborted(request.signal);
986
1375
  const delta = chunk.choices[0]?.delta;
987
1376
  const choice = chunk.choices[0];
988
1377
  if (delta?.content) {
@@ -1005,38 +1394,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1005
1394
  }
1006
1395
  }
1007
1396
  }
1008
- if (delta?.tool_calls) {
1009
- for (const toolCall of delta.tool_calls) {
1010
- if (toolCall.id) {
1011
- if (currentToolCall) {
1012
- yield {
1013
- type: "action:args",
1014
- id: currentToolCall.id,
1015
- args: currentToolCall.arguments
1016
- };
1017
- yield {
1018
- type: "action:end",
1019
- id: currentToolCall.id,
1020
- name: currentToolCall.name
1021
- };
1022
- }
1023
- const tcExtraContent = toolCall.extra_content;
1024
- currentToolCall = {
1025
- id: toolCall.id,
1026
- name: toolCall.function?.name || "",
1027
- arguments: toolCall.function?.arguments || "",
1028
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1029
- };
1030
- yield {
1031
- type: "action:start",
1032
- id: currentToolCall.id,
1033
- name: currentToolCall.name,
1034
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1035
- };
1036
- } else if (currentToolCall && toolCall.function?.arguments) {
1037
- currentToolCall.arguments += toolCall.function.arguments;
1038
- }
1039
- }
1397
+ for (const part of delta?.tool_calls ?? []) {
1398
+ const index = part.index ?? 0;
1399
+ const call = toolCalls.get(index) ?? {
1400
+ id: "",
1401
+ name: "",
1402
+ arguments: ""
1403
+ };
1404
+ if (part.id) call.id = part.id;
1405
+ if (part.function?.name) call.name += part.function.name;
1406
+ if (part.function?.arguments)
1407
+ call.arguments += part.function.arguments;
1408
+ if (part.extra_content) call.extra_content = part.extra_content;
1409
+ toolCalls.set(index, call);
1040
1410
  }
1041
1411
  if (chunk.usage) {
1042
1412
  usage = {
@@ -1045,21 +1415,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1045
1415
  total_tokens: chunk.usage.total_tokens
1046
1416
  };
1047
1417
  }
1048
- if (choice?.finish_reason) {
1049
- if (currentToolCall) {
1050
- yield {
1051
- type: "action:args",
1052
- id: currentToolCall.id,
1053
- args: currentToolCall.arguments
1054
- };
1055
- yield {
1056
- type: "action:end",
1057
- id: currentToolCall.id,
1058
- name: currentToolCall.name
1059
- };
1060
- currentToolCall = null;
1061
- }
1062
- }
1418
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1419
+ }
1420
+ throwIfAborted(request.signal);
1421
+ if (!finishReason)
1422
+ throw new Error("OpenAI stream ended without a finish reason");
1423
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1424
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1425
+ }
1426
+ for (const call of toolCalls.values()) {
1427
+ if (!call.id || !call.name)
1428
+ throw new Error("OpenAI stream contains an incomplete tool call");
1429
+ JSON.parse(call.arguments || "{}");
1430
+ }
1431
+ for (const call of toolCalls.values()) {
1432
+ yield {
1433
+ type: "action:start",
1434
+ id: call.id,
1435
+ name: call.name,
1436
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1437
+ };
1438
+ yield { type: "action:args", id: call.id, args: call.arguments };
1439
+ yield { type: "action:end", id: call.id, name: call.name };
1063
1440
  }
1064
1441
  if (collectedCitations.length > 0) {
1065
1442
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1068,26 +1445,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1068
1445
  yield { type: "message:end" };
1069
1446
  yield { type: "done", usage };
1070
1447
  } catch (error) {
1071
- yield {
1072
- type: "error",
1073
- message: error instanceof Error ? error.message : "Unknown error",
1074
- code: `${this.provider.toUpperCase()}_ERROR`
1075
- };
1448
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1076
1449
  }
1077
1450
  }
1078
1451
  async complete(request) {
1452
+ throwIfAborted(request.signal);
1079
1453
  if (this.shouldUseResponsesApi(request)) {
1080
1454
  return this.completeWithResponses(request);
1081
1455
  }
1082
1456
  const client = await this.getClient();
1083
1457
  let messages;
1084
1458
  if (request.rawMessages && request.rawMessages.length > 0) {
1085
- const sanitized = request.rawMessages.map((msg) => {
1086
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1087
- return { ...msg, content: null };
1088
- }
1089
- return msg;
1090
- });
1459
+ const sanitized = this.formatRawMessages(request.rawMessages);
1091
1460
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1092
1461
  messages = [
1093
1462
  { role: "system", content: request.systemPrompt },
@@ -1102,7 +1471,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1102
1471
  request.systemPrompt
1103
1472
  );
1104
1473
  }
1105
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1474
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1475
+ type: "function",
1476
+ function: {
1477
+ name: tool.name,
1478
+ description: tool.description,
1479
+ parameters: normalizeObjectJsonSchema(
1480
+ tool.inputSchema
1481
+ )
1482
+ }
1483
+ })) : formatTools(request.actions ?? []);
1106
1484
  const openaiToolOptions = request.providerToolOptions?.openai;
1107
1485
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1108
1486
  type: "function",
@@ -1117,16 +1495,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1117
1495
  tools: tools.length > 0 ? tools : void 0,
1118
1496
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1119
1497
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1120
- ...buildOpenAITokenParams(
1498
+ ...buildOpenAIParameters(
1121
1499
  modelIdForCompletePayload,
1122
1500
  request.config?.maxTokens ?? this.config.maxTokens,
1123
- request.config?.temperature ?? this.config.temperature
1501
+ request.config?.temperature ?? this.config.temperature,
1502
+ this.parameterOptions(request)
1124
1503
  ),
1125
1504
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1126
1505
  stream: false
1127
1506
  };
1128
1507
  logProviderPayload("openai", "request payload", payload, request.debug);
1129
- const response = await client.chat.completions.create(payload);
1508
+ const response = await client.chat.completions.create(payload, {
1509
+ signal: request.signal
1510
+ });
1130
1511
  logProviderPayload("openai", "response payload", response, request.debug);
1131
1512
  const choice = response.choices?.[0];
1132
1513
  const message = choice?.message;