@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,3 +1,157 @@
1
+ // src/core/tool-output.ts
2
+ var imageTypes = /* @__PURE__ */ new Set([
3
+ "image/png",
4
+ "image/jpeg",
5
+ "image/webp",
6
+ "image/gif"
7
+ ]);
8
+ function normalizeToolOutput(value) {
9
+ const output = value;
10
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
11
+ throw new Error("Tool model output must contain a nonempty content array");
12
+ }
13
+ return {
14
+ type: "content",
15
+ content: output.content.map((part) => {
16
+ if (part?.type === "text" && typeof part.text === "string")
17
+ return { type: "text", text: part.text };
18
+ if (part?.type !== "image")
19
+ throw new Error("Unsupported tool output content type");
20
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
21
+ if (sources.length !== 1)
22
+ throw new Error(
23
+ "Tool image requires exactly one of data, url, or fileId"
24
+ );
25
+ if ("fileId" in part) {
26
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
27
+ throw new Error(
28
+ "Tool image file references must name the openai provider and fileId"
29
+ );
30
+ return { type: "image", fileId: part.fileId, provider: "openai" };
31
+ }
32
+ if ("url" in part) {
33
+ let url;
34
+ try {
35
+ url = new URL(part.url);
36
+ } catch {
37
+ throw new Error("Invalid tool image URL");
38
+ }
39
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
40
+ throw new Error(
41
+ "Tool image URL must use HTTP(S) without credentials"
42
+ );
43
+ return { type: "image", url: url.href };
44
+ }
45
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
46
+ throw new Error("Unsupported tool image media type");
47
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
48
+ if (typeof data !== "string")
49
+ throw new Error("Tool image data must be base64 or bytes");
50
+ if (data.startsWith("data:")) {
51
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
52
+ if (!match || match[1] !== part.mediaType)
53
+ throw new Error(
54
+ "Tool image data URL media type does not match mediaType"
55
+ );
56
+ data = match[2];
57
+ }
58
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
59
+ throw new Error("Invalid tool image base64 data");
60
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
61
+ throw new Error("Tool image exceeds 20 MiB");
62
+ if (Buffer.from(data, "base64").toString("base64") !== data)
63
+ throw new Error("Invalid tool image base64 data");
64
+ return { type: "image", data, mediaType: part.mediaType };
65
+ })
66
+ };
67
+ }
68
+ function toolOutputForProvider(value, provider) {
69
+ const output = normalizeToolOutput(value);
70
+ return output.content.map((part) => {
71
+ if (part.type === "text")
72
+ return {
73
+ type: provider === "responses" ? "input_text" : "text",
74
+ text: part.text
75
+ };
76
+ if (provider === "unsupported")
77
+ throw new Error(
78
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
79
+ );
80
+ if ("fileId" in part) {
81
+ if (provider !== "responses")
82
+ throw new Error(
83
+ "OpenAI image file references cannot be sent to another provider"
84
+ );
85
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
86
+ }
87
+ if (provider === "responses")
88
+ return {
89
+ type: "input_image",
90
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
91
+ detail: "auto"
92
+ };
93
+ return {
94
+ type: "image",
95
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
96
+ };
97
+ });
98
+ }
99
+ function textToolOutput(value, fallback) {
100
+ if (value === void 0) return fallback;
101
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
102
+ }
103
+
104
+ // src/core/openai-parameters.ts
105
+ function capabilities(model) {
106
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
107
+ const major = Number(gpt?.[1] ?? 0);
108
+ const minor = Number(gpt?.[2] ?? 0);
109
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
110
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
111
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
112
+ return { major, minor, reasoning, supportsNone };
113
+ }
114
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
115
+ if (options.parameterPolicy === "passthrough") return false;
116
+ const c = capabilities(model);
117
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
118
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
119
+ if (!baseUrl) return true;
120
+ try {
121
+ return new URL(baseUrl).hostname === "api.openai.com";
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
127
+ const c = capabilities(model ?? "");
128
+ const normalize = options.parameterPolicy !== "passthrough";
129
+ const effort = options.reasoningEffort;
130
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
131
+ throw new Error(`${model} does not support reasoningEffort: none`);
132
+ }
133
+ if (normalize && c.major >= 6 && effort === "minimal") {
134
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
135
+ }
136
+ const result = {
137
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
138
+ };
139
+ if (effort !== void 0) {
140
+ if (api === "responses") result.reasoning = { effort };
141
+ else result.reasoning_effort = effort;
142
+ }
143
+ if (temperature !== void 0) {
144
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
145
+ result.temperature = temperature;
146
+ } else {
147
+ (options.onWarning ?? console.warn)(
148
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
149
+ );
150
+ }
151
+ }
152
+ return result;
153
+ }
154
+
1
155
  // src/adapters/base.ts
2
156
  function stringifyForDebug(value) {
3
157
  return JSON.stringify(
@@ -104,16 +258,6 @@ function normalizeObjectJsonSchema(schema) {
104
258
  }
105
259
  return normalized;
106
260
  }
107
- function isOpenAIReasoningModel(modelId) {
108
- if (!modelId) return false;
109
- return /^(o1|o3|o4|gpt-5)/i.test(modelId);
110
- }
111
- function buildOpenAITokenParams(modelId, maxTokens, temperature) {
112
- if (isOpenAIReasoningModel(modelId)) {
113
- return { max_completion_tokens: maxTokens };
114
- }
115
- return { max_tokens: maxTokens, temperature };
116
- }
117
261
  function toOpenAIResponseFormat(rf) {
118
262
  if (!rf) return void 0;
119
263
  if (rf.type === "json_object") return { type: "json_object" };
@@ -198,7 +342,7 @@ function messageToOpenAIContent(message) {
198
342
  }
199
343
  return blocks;
200
344
  }
201
- function formatMessagesForOpenAI(messages, systemPrompt) {
345
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
202
346
  const formatted = [];
203
347
  if (systemPrompt) {
204
348
  formatted.push({ role: "system", content: systemPrompt });
@@ -216,7 +360,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
216
360
  const assistantMsg = {
217
361
  role: "assistant",
218
362
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
219
- content: hasToolCalls ? msg.content || null : msg.content
363
+ content: hasToolCalls ? msg.content || null : msg.content,
364
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
220
365
  };
221
366
  if (hasToolCalls) {
222
367
  assistantMsg.tool_calls = msg.tool_calls;
@@ -225,7 +370,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
225
370
  } else if (msg.role === "tool" && msg.tool_call_id) {
226
371
  formatted.push({
227
372
  role: "tool",
228
- content: msg.content ?? "",
373
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
374
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
229
375
  tool_call_id: msg.tool_call_id
230
376
  });
231
377
  }
@@ -462,7 +608,7 @@ function formatMessagesForOpenRouter(messages) {
462
608
  return {
463
609
  role: "tool",
464
610
  tool_call_id: msg.toolCallId,
465
- content: msg.content
611
+ content: textToolOutput(msg.modelOutput, msg.content)
466
612
  };
467
613
  default:
468
614
  return msg;
@@ -493,6 +639,86 @@ async function searchOpenRouterModels(query, apiKey) {
493
639
  );
494
640
  }
495
641
 
642
+ // src/core/openai-responses.ts
643
+ function resolveOpenAITransport(defaults, overrides) {
644
+ return {
645
+ api: overrides?.api ?? defaults.api ?? "auto",
646
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
647
+ };
648
+ }
649
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
650
+ const api = transport.api ?? "auto";
651
+ if (!["auto", "chat", "responses"].includes(api)) {
652
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
653
+ }
654
+ if (api === "chat") {
655
+ if (transport.responses !== void 0 || nativeToolSearch) {
656
+ throw new Error(
657
+ "Responses options and native tool search require the Responses API"
658
+ );
659
+ }
660
+ return false;
661
+ }
662
+ let officialEndpoint = !baseURL;
663
+ try {
664
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
665
+ } catch {
666
+ }
667
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
668
+ }
669
+ function responseMetadata(response) {
670
+ const output = Array.isArray(response?.output) ? response.output : [];
671
+ return {
672
+ openai: {
673
+ response: {
674
+ id: response?.id,
675
+ model: response?.model,
676
+ status: response?.status,
677
+ output,
678
+ usage: response?.usage,
679
+ store: response?.store,
680
+ refusals: output.flatMap(
681
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
682
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
683
+ ).map((part) => part.refusal) : []
684
+ )
685
+ }
686
+ }
687
+ };
688
+ }
689
+ function getResponseReplay(metadata) {
690
+ const response = metadata?.openai?.response;
691
+ if (response === void 0) return void 0;
692
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
693
+ throw new Error(
694
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
695
+ );
696
+ }
697
+ return response.output;
698
+ }
699
+
700
+ // src/core/provider-error.ts
701
+ function providerErrorEvent(error, code) {
702
+ const details = error ?? {};
703
+ const event = {
704
+ type: "error",
705
+ message: error instanceof Error ? error.message : String(error),
706
+ code,
707
+ ...typeof details.status === "number" ? { status: details.status } : {},
708
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
709
+ ...typeof details.param === "string" ? { param: details.param } : {},
710
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
711
+ };
712
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
713
+ return event;
714
+ }
715
+ function throwIfAborted(signal) {
716
+ if (!signal?.aborted) return;
717
+ const error = new Error("The operation was aborted");
718
+ error.name = "AbortError";
719
+ throw error;
720
+ }
721
+
496
722
  // src/core/utils.ts
497
723
  function generateId(prefix = "id") {
498
724
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -524,39 +750,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
524
750
  const { default: OpenAI } = await import('openai');
525
751
  this.client = new OpenAI({
526
752
  apiKey: this.config.apiKey,
527
- baseURL: this.config.baseUrl
753
+ baseURL: this.config.baseUrl,
754
+ organization: this.config.organization,
755
+ defaultHeaders: this.config.headers
528
756
  });
529
757
  }
530
758
  return this.client;
531
759
  }
760
+ parameterOptions(request) {
761
+ return {
762
+ ...this.config,
763
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
764
+ };
765
+ }
766
+ toolDefinitions(request) {
767
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
768
+ return formatTools(request.actions ?? []).map((tool) => ({
769
+ name: tool.function.name,
770
+ description: tool.function.description ?? "",
771
+ location: "server",
772
+ inputSchema: tool.function.parameters
773
+ }));
774
+ }
532
775
  shouldUseResponsesApi(request) {
533
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
534
- if (!hasTools) return false;
535
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
536
- return true;
537
- }
538
- return this.requiresResponsesApiForTools(
539
- request.config?.model || this.model
776
+ return useOpenAIResponses(
777
+ request.config?.model || this.model,
778
+ this.config.baseUrl,
779
+ this.parameterOptions(request),
780
+ resolveOpenAITransport(this.config, request.config?.openai),
781
+ this.toolDefinitions(request).length > 0,
782
+ !!(request.rawMessages ?? request.messages)?.some(
783
+ (message) => message.providerMetadata?.openai?.response
784
+ ),
785
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
540
786
  );
541
787
  }
542
- /**
543
- * Whether this model rejects function tools on /v1/chat/completions.
544
- *
545
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
546
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
547
- * restriction and may not implement /v1/responses at all.
548
- */
549
- requiresResponsesApiForTools(modelId) {
550
- if (!modelId) return false;
551
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
552
- const baseUrl = this.config.baseUrl;
553
- if (!baseUrl) return true;
554
- try {
555
- return new URL(baseUrl).hostname === "api.openai.com";
556
- } catch {
557
- return false;
558
- }
559
- }
560
788
  /**
561
789
  * Convert one message's content to Responses content parts.
562
790
  *
@@ -587,11 +815,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
587
815
  });
588
816
  }
589
817
  buildResponsesInput(request) {
590
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
818
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
591
819
  const input = [];
592
820
  for (const message of sourceMessages) {
593
821
  if (message.role === "system") {
594
- if (!request.systemPrompt && message.content) {
822
+ if (message.content && message.content !== request.systemPrompt) {
595
823
  input.push({
596
824
  type: "message",
597
825
  role: "developer",
@@ -601,6 +829,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
601
829
  continue;
602
830
  }
603
831
  if (message.role === "assistant") {
832
+ const replay = getResponseReplay(message.providerMetadata);
833
+ if (replay) {
834
+ input.push(...replay);
835
+ continue;
836
+ }
604
837
  const content = this.toResponsesContent(message.content, "assistant");
605
838
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
606
839
  input.push({
@@ -624,7 +857,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
624
857
  input.push({
625
858
  type: "function_call_output",
626
859
  call_id: message.tool_call_id,
627
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
860
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
628
861
  });
629
862
  continue;
630
863
  }
@@ -637,6 +870,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
637
870
  }
638
871
  return input;
639
872
  }
873
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
874
+ formatRawMessages(messages, preserveProviderMetadata = false) {
875
+ return messages.map((message) => {
876
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
877
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
878
+ if (preserveProviderMetadata && modelOutput)
879
+ rest.modelOutput = modelOutput;
880
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
881
+ content = null;
882
+ }
883
+ if (Array.isArray(attachments) && attachments.length > 0) {
884
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
885
+ (attachment) => attachmentToOpenAIImage(attachment)
886
+ ).filter((image) => image !== null);
887
+ if (images.length > 0) {
888
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
889
+ content = [...parts, ...images];
890
+ }
891
+ }
892
+ return {
893
+ ...rest,
894
+ content,
895
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
896
+ };
897
+ });
898
+ }
640
899
  /**
641
900
  * Build the web_search tool for the Responses API, or undefined when web
642
901
  * search is off.
@@ -715,20 +974,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
715
974
  ).map((part) => part.text) : []
716
975
  ).join("") : "";
717
976
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
718
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
719
- id: item.call_id ?? item.id ?? generateToolCallId(),
720
- name: item.name,
721
- args: (() => {
722
- try {
723
- return JSON.parse(item.arguments ?? "{}");
724
- } catch {
725
- return {};
726
- }
727
- })()
728
- })) : [];
977
+ const seenCallIds = /* @__PURE__ */ new Set();
978
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
979
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
980
+ throw new Error("Responses contains an incomplete function call");
981
+ }
982
+ if (seenCallIds.has(item.call_id))
983
+ throw new Error("Responses contains duplicate function call IDs");
984
+ seenCallIds.add(item.call_id);
985
+ const args = JSON.parse(item.arguments);
986
+ if (!args || typeof args !== "object" || Array.isArray(args))
987
+ throw new Error(
988
+ "Responses function arguments must be a JSON object"
989
+ );
990
+ return { id: item.call_id, name: item.name, args };
991
+ }) : [];
729
992
  return {
730
993
  content,
731
994
  toolCalls,
995
+ providerMetadata: responseMetadata(response),
732
996
  usage: response?.usage ? {
733
997
  promptTokens: response.usage.input_tokens ?? 0,
734
998
  completionTokens: response.usage.output_tokens ?? 0,
@@ -739,6 +1003,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
739
1003
  }
740
1004
  buildResponsesPayload(request, stream) {
741
1005
  const openaiToolOptions = request.providerToolOptions?.openai;
1006
+ const options = resolveOpenAITransport(
1007
+ this.config,
1008
+ request.config?.openai
1009
+ ).responses;
1010
+ const parameters = buildOpenAIParameters(
1011
+ request.config?.model || this.model,
1012
+ request.config?.maxTokens ?? this.config.maxTokens,
1013
+ request.config?.temperature ?? this.config.temperature,
1014
+ this.parameterOptions(request),
1015
+ "responses"
1016
+ );
742
1017
  const responsesTextFormat = toOpenAIResponsesTextFormat(
743
1018
  request.config?.responseFormat
744
1019
  );
@@ -748,18 +1023,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
748
1023
  instructions: request.systemPrompt,
749
1024
  input: this.buildResponsesInput(request),
750
1025
  tools: this.buildResponsesTools(
751
- request.toolDefinitions ?? [],
1026
+ this.toolDefinitions(request),
752
1027
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
753
1028
  this.buildResponsesWebSearchTool(request)
754
1029
  ),
755
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
1030
+ tool_choice: openaiToolOptions?.toolChoice,
756
1031
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
757
- // Reasoning models reject `temperature` outright — sending it would swap one
758
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
759
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
760
- temperature: request.config?.temperature ?? this.config.temperature
761
- },
762
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
1032
+ ...parameters,
1033
+ ...options?.reasoningSummary ? {
1034
+ reasoning: {
1035
+ ...parameters.reasoning,
1036
+ summary: options.reasoningSummary
1037
+ }
1038
+ } : {},
1039
+ ...options?.store !== void 0 ? { store: options.store } : {},
1040
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1041
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1042
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1043
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1044
+ ...options?.store === false || options?.include ? {
1045
+ include: [
1046
+ .../* @__PURE__ */ new Set([
1047
+ ...options?.include ?? [],
1048
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1049
+ ])
1050
+ ]
1051
+ } : {},
763
1052
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
764
1053
  stream
765
1054
  };
@@ -767,10 +1056,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
767
1056
  }
768
1057
  async completeWithResponses(request) {
769
1058
  const client = await this.getClient();
1059
+ throwIfAborted(request.signal);
770
1060
  const payload = this.buildResponsesPayload(request, false);
771
1061
  logProviderPayload("openai", "request payload", payload, request.debug);
772
- const response = await client.responses.create(payload);
1062
+ if (!client.responses?.create)
1063
+ throw new Error(
1064
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1065
+ );
1066
+ const response = await client.responses.create(payload, {
1067
+ signal: request.signal
1068
+ });
1069
+ throwIfAborted(request.signal);
773
1070
  logProviderPayload("openai", "response payload", response, request.debug);
1071
+ if (response.status && response.status !== "completed") {
1072
+ throw Object.assign(
1073
+ new Error(
1074
+ response.error?.message ?? `Responses request ${response.status}`
1075
+ ),
1076
+ response.error
1077
+ );
1078
+ }
774
1079
  return this.parseResponsesResult(response);
775
1080
  }
776
1081
  /**
@@ -788,13 +1093,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
788
1093
  */
789
1094
  async *streamWithResponses(request) {
790
1095
  const client = await this.getClient();
1096
+ throwIfAborted(request.signal);
791
1097
  const payload = this.buildResponsesPayload(request, true);
792
1098
  logProviderPayload("openai", "request payload", payload, request.debug);
793
- const stream = await client.responses.create(payload);
1099
+ if (!client.responses?.create)
1100
+ throw new Error(
1101
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1102
+ );
1103
+ const stream = await client.responses.create(payload, {
1104
+ signal: request.signal
1105
+ });
794
1106
  const toolCalls = /* @__PURE__ */ new Map();
795
1107
  let usage;
796
1108
  const citations = [];
1109
+ let completed = false;
1110
+ let terminalResponse;
1111
+ let responseIdentity = {};
1112
+ const outputItems = /* @__PURE__ */ new Map();
1113
+ let streamedText = "";
797
1114
  function* flushToolCalls() {
1115
+ for (const call of toolCalls.values()) {
1116
+ if (!call.name)
1117
+ throw new Error("Responses tool call is missing its name");
1118
+ JSON.parse(call.arguments || "{}");
1119
+ }
798
1120
  for (const call of toolCalls.values()) {
799
1121
  yield { type: "action:start", id: call.id, name: call.name };
800
1122
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -803,19 +1125,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
803
1125
  toolCalls.clear();
804
1126
  }
805
1127
  for await (const event of stream) {
806
- if (request.signal?.aborted) break;
1128
+ throwIfAborted(request.signal);
807
1129
  logProviderPayload("openai", "stream chunk", event, request.debug);
1130
+ if (completed)
1131
+ throw new Error("Responses stream emitted an event after completion");
808
1132
  const index = event?.output_index ?? 0;
809
1133
  switch (event?.type) {
1134
+ case "response.created":
1135
+ responseIdentity = {
1136
+ id: event.response?.id,
1137
+ model: event.response?.model
1138
+ };
1139
+ break;
1140
+ case "response.output_item.done":
1141
+ if (event.item) outputItems.set(index, event.item);
1142
+ if (event.item?.type === "function_call") {
1143
+ toolCalls.set(index, {
1144
+ id: event.item.call_id,
1145
+ name: event.item.name,
1146
+ arguments: event.item.arguments
1147
+ });
1148
+ }
1149
+ break;
810
1150
  case "response.output_text.delta":
811
1151
  if (event.delta) {
1152
+ streamedText += event.delta;
812
1153
  yield { type: "message:delta", content: event.delta };
813
1154
  }
814
1155
  break;
815
1156
  case "response.output_item.added":
816
1157
  if (event.item?.type === "function_call") {
817
1158
  toolCalls.set(index, {
818
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1159
+ id: event.item.call_id ?? "",
819
1160
  name: event.item.name ?? "",
820
1161
  arguments: event.item.arguments ?? ""
821
1162
  });
@@ -833,8 +1174,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
833
1174
  }
834
1175
  break;
835
1176
  }
836
- case "response.completed":
837
- case "response.incomplete": {
1177
+ case "response.completed": {
1178
+ if (event.response?.status && event.response.status !== "completed")
1179
+ throw new Error(`Responses stream ${event.response.status}`);
1180
+ completed = true;
1181
+ terminalResponse = { ...responseIdentity, ...event.response };
838
1182
  const found = this.collectResponsesCitations(
839
1183
  event.response?.output,
840
1184
  citations.length
@@ -850,21 +1194,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
850
1194
  }
851
1195
  break;
852
1196
  }
853
- case "error":
854
- case "response.failed":
1197
+ case "response.incomplete":
855
1198
  throw new Error(
856
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1199
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
1200
+ );
1201
+ case "error":
1202
+ case "response.failed": {
1203
+ const details = event.response?.error ?? event;
1204
+ throw Object.assign(
1205
+ new Error(details.message ?? "Responses stream failed"),
1206
+ {
1207
+ code: details.code,
1208
+ param: details.param,
1209
+ status: details.status
1210
+ }
857
1211
  );
1212
+ }
1213
+ }
1214
+ }
1215
+ throwIfAborted(request.signal);
1216
+ if (!completed)
1217
+ throw new Error("Responses stream ended without a completed response");
1218
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1219
+ if (output.some((item) => item.type === "function_call"))
1220
+ toolCalls.clear();
1221
+ for (const [index, item] of output.entries()) {
1222
+ if (item.type === "function_call") {
1223
+ toolCalls.set(index, {
1224
+ id: item.call_id,
1225
+ name: item.name,
1226
+ arguments: item.arguments
1227
+ });
858
1228
  }
859
1229
  }
1230
+ const uniqueCalls = /* @__PURE__ */ new Map();
1231
+ for (const call of toolCalls.values()) {
1232
+ if (!call.id || !call.name)
1233
+ throw new Error("Responses stream contains an incomplete tool call");
1234
+ uniqueCalls.set(call.id, call);
1235
+ }
1236
+ toolCalls.clear();
1237
+ [...uniqueCalls.values()].forEach(
1238
+ (call, index) => toolCalls.set(index, call)
1239
+ );
1240
+ if (!output.length && toolCalls.size) {
1241
+ for (const call of toolCalls.values())
1242
+ output.push({
1243
+ type: "function_call",
1244
+ call_id: call.id,
1245
+ name: call.name,
1246
+ arguments: call.arguments
1247
+ });
1248
+ }
1249
+ if (!output.length && streamedText)
1250
+ output.push({
1251
+ type: "message",
1252
+ role: "assistant",
1253
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1254
+ });
1255
+ terminalResponse.output = output;
1256
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1257
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1258
+ yield {
1259
+ type: "message:delta",
1260
+ content: finalText.slice(streamedText.length)
1261
+ };
1262
+ }
860
1263
  yield* flushToolCalls();
861
1264
  if (citations.length > 0) {
862
1265
  yield { type: "citation", citations: deduplicateCitations(citations) };
863
1266
  }
864
- yield { type: "message:end" };
865
- yield { type: "done", usage };
1267
+ yield {
1268
+ type: "message:end",
1269
+ providerMetadata: responseMetadata(terminalResponse)
1270
+ };
1271
+ yield {
1272
+ type: "done",
1273
+ usage,
1274
+ providerMetadata: responseMetadata(terminalResponse)
1275
+ };
866
1276
  }
867
1277
  async *stream(request) {
1278
+ throwIfAborted(request.signal);
868
1279
  if (this.shouldUseResponsesApi(request)) {
869
1280
  const messageId2 = generateMessageId();
870
1281
  yield { type: "message:start", id: messageId2 };
@@ -872,47 +1283,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
872
1283
  yield* this.streamWithResponses(request);
873
1284
  return;
874
1285
  } catch (error) {
875
- yield {
876
- type: "error",
877
- message: error instanceof Error ? error.message : "Unknown error",
878
- code: "OPENAI_RESPONSES_ERROR"
879
- };
1286
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
880
1287
  return;
881
1288
  }
882
1289
  }
883
1290
  const client = await this.getClient();
884
1291
  let messages;
885
1292
  if (request.rawMessages && request.rawMessages.length > 0) {
886
- const processedMessages = request.rawMessages.map((msg) => {
887
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
888
- return { ...msg, content: null };
889
- }
890
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
891
- if (hasAttachments) {
892
- const content = [];
893
- if (msg.content) {
894
- content.push({ type: "text", text: msg.content });
895
- }
896
- for (const attachment of msg.attachments) {
897
- if (attachment.type === "image") {
898
- let imageUrl;
899
- if (attachment.url) {
900
- imageUrl = attachment.url;
901
- } else if (attachment.data) {
902
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
903
- } else {
904
- continue;
905
- }
906
- content.push({
907
- type: "image_url",
908
- image_url: { url: imageUrl, detail: "auto" }
909
- });
910
- }
911
- }
912
- return { ...msg, content, attachments: void 0 };
913
- }
914
- return msg;
915
- });
1293
+ const processedMessages = this.formatRawMessages(request.rawMessages);
916
1294
  if (request.systemPrompt) {
917
1295
  const hasSystem = processedMessages.some((m) => m.role === "system");
918
1296
  if (!hasSystem) {
@@ -932,7 +1310,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
932
1310
  request.systemPrompt
933
1311
  );
934
1312
  }
935
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1313
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1314
+ type: "function",
1315
+ function: {
1316
+ name: tool.name,
1317
+ description: tool.description,
1318
+ parameters: normalizeObjectJsonSchema(
1319
+ tool.inputSchema
1320
+ )
1321
+ }
1322
+ })) : formatTools(request.actions ?? []);
936
1323
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
937
1324
  if (webSearchConfig) {
938
1325
  const webSearchTool = {
@@ -961,26 +1348,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
961
1348
  tools: tools.length > 0 ? tools : void 0,
962
1349
  tool_choice: tools.length > 0 ? toolChoice : void 0,
963
1350
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
964
- ...buildOpenAITokenParams(
1351
+ ...buildOpenAIParameters(
965
1352
  modelIdForPayload,
966
1353
  request.config?.maxTokens ?? this.config.maxTokens,
967
- request.config?.temperature ?? this.config.temperature
1354
+ request.config?.temperature ?? this.config.temperature,
1355
+ this.parameterOptions(request)
968
1356
  ),
969
1357
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
970
1358
  stream: true,
971
1359
  stream_options: { include_usage: true }
972
1360
  };
973
1361
  logProviderPayload("openai", "request payload", payload, request.debug);
974
- const stream = await client.chat.completions.create(payload);
975
- let currentToolCall = null;
1362
+ const stream = await client.chat.completions.create(payload, {
1363
+ signal: request.signal
1364
+ });
1365
+ const toolCalls = /* @__PURE__ */ new Map();
1366
+ let finishReason;
976
1367
  const collectedCitations = [];
977
1368
  let citationIndex = 0;
978
1369
  let usage;
979
1370
  for await (const chunk of stream) {
980
1371
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
981
- if (request.signal?.aborted) {
982
- break;
983
- }
1372
+ throwIfAborted(request.signal);
984
1373
  const delta = chunk.choices[0]?.delta;
985
1374
  const choice = chunk.choices[0];
986
1375
  if (delta?.content) {
@@ -1003,38 +1392,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1003
1392
  }
1004
1393
  }
1005
1394
  }
1006
- if (delta?.tool_calls) {
1007
- for (const toolCall of delta.tool_calls) {
1008
- if (toolCall.id) {
1009
- if (currentToolCall) {
1010
- yield {
1011
- type: "action:args",
1012
- id: currentToolCall.id,
1013
- args: currentToolCall.arguments
1014
- };
1015
- yield {
1016
- type: "action:end",
1017
- id: currentToolCall.id,
1018
- name: currentToolCall.name
1019
- };
1020
- }
1021
- const tcExtraContent = toolCall.extra_content;
1022
- currentToolCall = {
1023
- id: toolCall.id,
1024
- name: toolCall.function?.name || "",
1025
- arguments: toolCall.function?.arguments || "",
1026
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1027
- };
1028
- yield {
1029
- type: "action:start",
1030
- id: currentToolCall.id,
1031
- name: currentToolCall.name,
1032
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1033
- };
1034
- } else if (currentToolCall && toolCall.function?.arguments) {
1035
- currentToolCall.arguments += toolCall.function.arguments;
1036
- }
1037
- }
1395
+ for (const part of delta?.tool_calls ?? []) {
1396
+ const index = part.index ?? 0;
1397
+ const call = toolCalls.get(index) ?? {
1398
+ id: "",
1399
+ name: "",
1400
+ arguments: ""
1401
+ };
1402
+ if (part.id) call.id = part.id;
1403
+ if (part.function?.name) call.name += part.function.name;
1404
+ if (part.function?.arguments)
1405
+ call.arguments += part.function.arguments;
1406
+ if (part.extra_content) call.extra_content = part.extra_content;
1407
+ toolCalls.set(index, call);
1038
1408
  }
1039
1409
  if (chunk.usage) {
1040
1410
  usage = {
@@ -1043,21 +1413,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1043
1413
  total_tokens: chunk.usage.total_tokens
1044
1414
  };
1045
1415
  }
1046
- if (choice?.finish_reason) {
1047
- if (currentToolCall) {
1048
- yield {
1049
- type: "action:args",
1050
- id: currentToolCall.id,
1051
- args: currentToolCall.arguments
1052
- };
1053
- yield {
1054
- type: "action:end",
1055
- id: currentToolCall.id,
1056
- name: currentToolCall.name
1057
- };
1058
- currentToolCall = null;
1059
- }
1060
- }
1416
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1417
+ }
1418
+ throwIfAborted(request.signal);
1419
+ if (!finishReason)
1420
+ throw new Error("OpenAI stream ended without a finish reason");
1421
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1422
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1423
+ }
1424
+ for (const call of toolCalls.values()) {
1425
+ if (!call.id || !call.name)
1426
+ throw new Error("OpenAI stream contains an incomplete tool call");
1427
+ JSON.parse(call.arguments || "{}");
1428
+ }
1429
+ for (const call of toolCalls.values()) {
1430
+ yield {
1431
+ type: "action:start",
1432
+ id: call.id,
1433
+ name: call.name,
1434
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1435
+ };
1436
+ yield { type: "action:args", id: call.id, args: call.arguments };
1437
+ yield { type: "action:end", id: call.id, name: call.name };
1061
1438
  }
1062
1439
  if (collectedCitations.length > 0) {
1063
1440
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1066,26 +1443,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1066
1443
  yield { type: "message:end" };
1067
1444
  yield { type: "done", usage };
1068
1445
  } catch (error) {
1069
- yield {
1070
- type: "error",
1071
- message: error instanceof Error ? error.message : "Unknown error",
1072
- code: `${this.provider.toUpperCase()}_ERROR`
1073
- };
1446
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1074
1447
  }
1075
1448
  }
1076
1449
  async complete(request) {
1450
+ throwIfAborted(request.signal);
1077
1451
  if (this.shouldUseResponsesApi(request)) {
1078
1452
  return this.completeWithResponses(request);
1079
1453
  }
1080
1454
  const client = await this.getClient();
1081
1455
  let messages;
1082
1456
  if (request.rawMessages && request.rawMessages.length > 0) {
1083
- const sanitized = request.rawMessages.map((msg) => {
1084
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1085
- return { ...msg, content: null };
1086
- }
1087
- return msg;
1088
- });
1457
+ const sanitized = this.formatRawMessages(request.rawMessages);
1089
1458
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1090
1459
  messages = [
1091
1460
  { role: "system", content: request.systemPrompt },
@@ -1100,7 +1469,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1100
1469
  request.systemPrompt
1101
1470
  );
1102
1471
  }
1103
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1472
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1473
+ type: "function",
1474
+ function: {
1475
+ name: tool.name,
1476
+ description: tool.description,
1477
+ parameters: normalizeObjectJsonSchema(
1478
+ tool.inputSchema
1479
+ )
1480
+ }
1481
+ })) : formatTools(request.actions ?? []);
1104
1482
  const openaiToolOptions = request.providerToolOptions?.openai;
1105
1483
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1106
1484
  type: "function",
@@ -1115,16 +1493,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1115
1493
  tools: tools.length > 0 ? tools : void 0,
1116
1494
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1117
1495
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1118
- ...buildOpenAITokenParams(
1496
+ ...buildOpenAIParameters(
1119
1497
  modelIdForCompletePayload,
1120
1498
  request.config?.maxTokens ?? this.config.maxTokens,
1121
- request.config?.temperature ?? this.config.temperature
1499
+ request.config?.temperature ?? this.config.temperature,
1500
+ this.parameterOptions(request)
1122
1501
  ),
1123
1502
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1124
1503
  stream: false
1125
1504
  };
1126
1505
  logProviderPayload("openai", "request payload", payload, request.debug);
1127
- const response = await client.chat.completions.create(payload);
1506
+ const response = await client.chat.completions.create(payload, {
1507
+ signal: request.signal
1508
+ });
1128
1509
  logProviderPayload("openai", "response payload", response, request.debug);
1129
1510
  const choice = response.choices?.[0];
1130
1511
  const message = choice?.message;