@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
  }
@@ -512,7 +658,7 @@ function formatMessagesForGoogle(messages) {
512
658
  return {
513
659
  role: "tool",
514
660
  tool_call_id: msg.toolCallId,
515
- content: msg.content
661
+ content: textToolOutput(msg.modelOutput, msg.content)
516
662
  };
517
663
  default:
518
664
  return msg;
@@ -520,6 +666,86 @@ function formatMessagesForGoogle(messages) {
520
666
  });
521
667
  }
522
668
 
669
+ // src/core/openai-responses.ts
670
+ function resolveOpenAITransport(defaults, overrides) {
671
+ return {
672
+ api: overrides?.api ?? defaults.api ?? "auto",
673
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
674
+ };
675
+ }
676
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
677
+ const api = transport.api ?? "auto";
678
+ if (!["auto", "chat", "responses"].includes(api)) {
679
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
680
+ }
681
+ if (api === "chat") {
682
+ if (transport.responses !== void 0 || nativeToolSearch) {
683
+ throw new Error(
684
+ "Responses options and native tool search require the Responses API"
685
+ );
686
+ }
687
+ return false;
688
+ }
689
+ let officialEndpoint = !baseURL;
690
+ try {
691
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
692
+ } catch {
693
+ }
694
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
695
+ }
696
+ function responseMetadata(response) {
697
+ const output = Array.isArray(response?.output) ? response.output : [];
698
+ return {
699
+ openai: {
700
+ response: {
701
+ id: response?.id,
702
+ model: response?.model,
703
+ status: response?.status,
704
+ output,
705
+ usage: response?.usage,
706
+ store: response?.store,
707
+ refusals: output.flatMap(
708
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
709
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
710
+ ).map((part) => part.refusal) : []
711
+ )
712
+ }
713
+ }
714
+ };
715
+ }
716
+ function getResponseReplay(metadata) {
717
+ const response = metadata?.openai?.response;
718
+ if (response === void 0) return void 0;
719
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
720
+ throw new Error(
721
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
722
+ );
723
+ }
724
+ return response.output;
725
+ }
726
+
727
+ // src/core/provider-error.ts
728
+ function providerErrorEvent(error, code) {
729
+ const details = error ?? {};
730
+ const event = {
731
+ type: "error",
732
+ message: error instanceof Error ? error.message : String(error),
733
+ code,
734
+ ...typeof details.status === "number" ? { status: details.status } : {},
735
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
736
+ ...typeof details.param === "string" ? { param: details.param } : {},
737
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
738
+ };
739
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
740
+ return event;
741
+ }
742
+ function throwIfAborted(signal) {
743
+ if (!signal?.aborted) return;
744
+ const error = new Error("The operation was aborted");
745
+ error.name = "AbortError";
746
+ throw error;
747
+ }
748
+
523
749
  // src/core/utils.ts
524
750
  function generateId(prefix = "id") {
525
751
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -551,39 +777,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
551
777
  const { default: OpenAI } = await import('openai');
552
778
  this.client = new OpenAI({
553
779
  apiKey: this.config.apiKey,
554
- baseURL: this.config.baseUrl
780
+ baseURL: this.config.baseUrl,
781
+ organization: this.config.organization,
782
+ defaultHeaders: this.config.headers
555
783
  });
556
784
  }
557
785
  return this.client;
558
786
  }
787
+ parameterOptions(request) {
788
+ return {
789
+ ...this.config,
790
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
791
+ };
792
+ }
793
+ toolDefinitions(request) {
794
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
795
+ return formatTools(request.actions ?? []).map((tool) => ({
796
+ name: tool.function.name,
797
+ description: tool.function.description ?? "",
798
+ location: "server",
799
+ inputSchema: tool.function.parameters
800
+ }));
801
+ }
559
802
  shouldUseResponsesApi(request) {
560
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
561
- if (!hasTools) return false;
562
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
563
- return true;
564
- }
565
- return this.requiresResponsesApiForTools(
566
- request.config?.model || this.model
803
+ return useOpenAIResponses(
804
+ request.config?.model || this.model,
805
+ this.config.baseUrl,
806
+ this.parameterOptions(request),
807
+ resolveOpenAITransport(this.config, request.config?.openai),
808
+ this.toolDefinitions(request).length > 0,
809
+ !!(request.rawMessages ?? request.messages)?.some(
810
+ (message) => message.providerMetadata?.openai?.response
811
+ ),
812
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
567
813
  );
568
814
  }
569
- /**
570
- * Whether this model rejects function tools on /v1/chat/completions.
571
- *
572
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
573
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
574
- * restriction and may not implement /v1/responses at all.
575
- */
576
- requiresResponsesApiForTools(modelId) {
577
- if (!modelId) return false;
578
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
579
- const baseUrl = this.config.baseUrl;
580
- if (!baseUrl) return true;
581
- try {
582
- return new URL(baseUrl).hostname === "api.openai.com";
583
- } catch {
584
- return false;
585
- }
586
- }
587
815
  /**
588
816
  * Convert one message's content to Responses content parts.
589
817
  *
@@ -614,11 +842,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
614
842
  });
615
843
  }
616
844
  buildResponsesInput(request) {
617
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
845
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
618
846
  const input = [];
619
847
  for (const message of sourceMessages) {
620
848
  if (message.role === "system") {
621
- if (!request.systemPrompt && message.content) {
849
+ if (message.content && message.content !== request.systemPrompt) {
622
850
  input.push({
623
851
  type: "message",
624
852
  role: "developer",
@@ -628,6 +856,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
628
856
  continue;
629
857
  }
630
858
  if (message.role === "assistant") {
859
+ const replay = getResponseReplay(message.providerMetadata);
860
+ if (replay) {
861
+ input.push(...replay);
862
+ continue;
863
+ }
631
864
  const content = this.toResponsesContent(message.content, "assistant");
632
865
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
633
866
  input.push({
@@ -651,7 +884,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
651
884
  input.push({
652
885
  type: "function_call_output",
653
886
  call_id: message.tool_call_id,
654
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
887
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
655
888
  });
656
889
  continue;
657
890
  }
@@ -664,6 +897,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
664
897
  }
665
898
  return input;
666
899
  }
900
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
901
+ formatRawMessages(messages, preserveProviderMetadata = false) {
902
+ return messages.map((message) => {
903
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
904
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
905
+ if (preserveProviderMetadata && modelOutput)
906
+ rest.modelOutput = modelOutput;
907
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
908
+ content = null;
909
+ }
910
+ if (Array.isArray(attachments) && attachments.length > 0) {
911
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
912
+ (attachment) => attachmentToOpenAIImage(attachment)
913
+ ).filter((image) => image !== null);
914
+ if (images.length > 0) {
915
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
916
+ content = [...parts, ...images];
917
+ }
918
+ }
919
+ return {
920
+ ...rest,
921
+ content,
922
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
923
+ };
924
+ });
925
+ }
667
926
  /**
668
927
  * Build the web_search tool for the Responses API, or undefined when web
669
928
  * search is off.
@@ -742,20 +1001,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
742
1001
  ).map((part) => part.text) : []
743
1002
  ).join("") : "";
744
1003
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
745
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
746
- id: item.call_id ?? item.id ?? generateToolCallId(),
747
- name: item.name,
748
- args: (() => {
749
- try {
750
- return JSON.parse(item.arguments ?? "{}");
751
- } catch {
752
- return {};
753
- }
754
- })()
755
- })) : [];
1004
+ const seenCallIds = /* @__PURE__ */ new Set();
1005
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1006
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1007
+ throw new Error("Responses contains an incomplete function call");
1008
+ }
1009
+ if (seenCallIds.has(item.call_id))
1010
+ throw new Error("Responses contains duplicate function call IDs");
1011
+ seenCallIds.add(item.call_id);
1012
+ const args = JSON.parse(item.arguments);
1013
+ if (!args || typeof args !== "object" || Array.isArray(args))
1014
+ throw new Error(
1015
+ "Responses function arguments must be a JSON object"
1016
+ );
1017
+ return { id: item.call_id, name: item.name, args };
1018
+ }) : [];
756
1019
  return {
757
1020
  content,
758
1021
  toolCalls,
1022
+ providerMetadata: responseMetadata(response),
759
1023
  usage: response?.usage ? {
760
1024
  promptTokens: response.usage.input_tokens ?? 0,
761
1025
  completionTokens: response.usage.output_tokens ?? 0,
@@ -766,6 +1030,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
766
1030
  }
767
1031
  buildResponsesPayload(request, stream) {
768
1032
  const openaiToolOptions = request.providerToolOptions?.openai;
1033
+ const options = resolveOpenAITransport(
1034
+ this.config,
1035
+ request.config?.openai
1036
+ ).responses;
1037
+ const parameters = buildOpenAIParameters(
1038
+ request.config?.model || this.model,
1039
+ request.config?.maxTokens ?? this.config.maxTokens,
1040
+ request.config?.temperature ?? this.config.temperature,
1041
+ this.parameterOptions(request),
1042
+ "responses"
1043
+ );
769
1044
  const responsesTextFormat = toOpenAIResponsesTextFormat(
770
1045
  request.config?.responseFormat
771
1046
  );
@@ -775,18 +1050,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
775
1050
  instructions: request.systemPrompt,
776
1051
  input: this.buildResponsesInput(request),
777
1052
  tools: this.buildResponsesTools(
778
- request.toolDefinitions ?? [],
1053
+ this.toolDefinitions(request),
779
1054
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
780
1055
  this.buildResponsesWebSearchTool(request)
781
1056
  ),
782
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
1057
+ tool_choice: openaiToolOptions?.toolChoice,
783
1058
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
784
- // Reasoning models reject `temperature` outright — sending it would swap one
785
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
786
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
787
- temperature: request.config?.temperature ?? this.config.temperature
788
- },
789
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
1059
+ ...parameters,
1060
+ ...options?.reasoningSummary ? {
1061
+ reasoning: {
1062
+ ...parameters.reasoning,
1063
+ summary: options.reasoningSummary
1064
+ }
1065
+ } : {},
1066
+ ...options?.store !== void 0 ? { store: options.store } : {},
1067
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1068
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1069
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1070
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1071
+ ...options?.store === false || options?.include ? {
1072
+ include: [
1073
+ .../* @__PURE__ */ new Set([
1074
+ ...options?.include ?? [],
1075
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1076
+ ])
1077
+ ]
1078
+ } : {},
790
1079
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
791
1080
  stream
792
1081
  };
@@ -794,10 +1083,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
794
1083
  }
795
1084
  async completeWithResponses(request) {
796
1085
  const client = await this.getClient();
1086
+ throwIfAborted(request.signal);
797
1087
  const payload = this.buildResponsesPayload(request, false);
798
1088
  logProviderPayload("openai", "request payload", payload, request.debug);
799
- const response = await client.responses.create(payload);
1089
+ if (!client.responses?.create)
1090
+ throw new Error(
1091
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1092
+ );
1093
+ const response = await client.responses.create(payload, {
1094
+ signal: request.signal
1095
+ });
1096
+ throwIfAborted(request.signal);
800
1097
  logProviderPayload("openai", "response payload", response, request.debug);
1098
+ if (response.status && response.status !== "completed") {
1099
+ throw Object.assign(
1100
+ new Error(
1101
+ response.error?.message ?? `Responses request ${response.status}`
1102
+ ),
1103
+ response.error
1104
+ );
1105
+ }
801
1106
  return this.parseResponsesResult(response);
802
1107
  }
803
1108
  /**
@@ -815,13 +1120,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
815
1120
  */
816
1121
  async *streamWithResponses(request) {
817
1122
  const client = await this.getClient();
1123
+ throwIfAborted(request.signal);
818
1124
  const payload = this.buildResponsesPayload(request, true);
819
1125
  logProviderPayload("openai", "request payload", payload, request.debug);
820
- const stream = await client.responses.create(payload);
1126
+ if (!client.responses?.create)
1127
+ throw new Error(
1128
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1129
+ );
1130
+ const stream = await client.responses.create(payload, {
1131
+ signal: request.signal
1132
+ });
821
1133
  const toolCalls = /* @__PURE__ */ new Map();
822
1134
  let usage;
823
1135
  const citations = [];
1136
+ let completed = false;
1137
+ let terminalResponse;
1138
+ let responseIdentity = {};
1139
+ const outputItems = /* @__PURE__ */ new Map();
1140
+ let streamedText = "";
824
1141
  function* flushToolCalls() {
1142
+ for (const call of toolCalls.values()) {
1143
+ if (!call.name)
1144
+ throw new Error("Responses tool call is missing its name");
1145
+ JSON.parse(call.arguments || "{}");
1146
+ }
825
1147
  for (const call of toolCalls.values()) {
826
1148
  yield { type: "action:start", id: call.id, name: call.name };
827
1149
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -830,19 +1152,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
830
1152
  toolCalls.clear();
831
1153
  }
832
1154
  for await (const event of stream) {
833
- if (request.signal?.aborted) break;
1155
+ throwIfAborted(request.signal);
834
1156
  logProviderPayload("openai", "stream chunk", event, request.debug);
1157
+ if (completed)
1158
+ throw new Error("Responses stream emitted an event after completion");
835
1159
  const index = event?.output_index ?? 0;
836
1160
  switch (event?.type) {
1161
+ case "response.created":
1162
+ responseIdentity = {
1163
+ id: event.response?.id,
1164
+ model: event.response?.model
1165
+ };
1166
+ break;
1167
+ case "response.output_item.done":
1168
+ if (event.item) outputItems.set(index, event.item);
1169
+ if (event.item?.type === "function_call") {
1170
+ toolCalls.set(index, {
1171
+ id: event.item.call_id,
1172
+ name: event.item.name,
1173
+ arguments: event.item.arguments
1174
+ });
1175
+ }
1176
+ break;
837
1177
  case "response.output_text.delta":
838
1178
  if (event.delta) {
1179
+ streamedText += event.delta;
839
1180
  yield { type: "message:delta", content: event.delta };
840
1181
  }
841
1182
  break;
842
1183
  case "response.output_item.added":
843
1184
  if (event.item?.type === "function_call") {
844
1185
  toolCalls.set(index, {
845
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1186
+ id: event.item.call_id ?? "",
846
1187
  name: event.item.name ?? "",
847
1188
  arguments: event.item.arguments ?? ""
848
1189
  });
@@ -860,8 +1201,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
860
1201
  }
861
1202
  break;
862
1203
  }
863
- case "response.completed":
864
- case "response.incomplete": {
1204
+ case "response.completed": {
1205
+ if (event.response?.status && event.response.status !== "completed")
1206
+ throw new Error(`Responses stream ${event.response.status}`);
1207
+ completed = true;
1208
+ terminalResponse = { ...responseIdentity, ...event.response };
865
1209
  const found = this.collectResponsesCitations(
866
1210
  event.response?.output,
867
1211
  citations.length
@@ -877,21 +1221,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
877
1221
  }
878
1222
  break;
879
1223
  }
880
- case "error":
881
- case "response.failed":
1224
+ case "response.incomplete":
882
1225
  throw new Error(
883
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1226
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
1227
+ );
1228
+ case "error":
1229
+ case "response.failed": {
1230
+ const details = event.response?.error ?? event;
1231
+ throw Object.assign(
1232
+ new Error(details.message ?? "Responses stream failed"),
1233
+ {
1234
+ code: details.code,
1235
+ param: details.param,
1236
+ status: details.status
1237
+ }
884
1238
  );
1239
+ }
1240
+ }
1241
+ }
1242
+ throwIfAborted(request.signal);
1243
+ if (!completed)
1244
+ throw new Error("Responses stream ended without a completed response");
1245
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1246
+ if (output.some((item) => item.type === "function_call"))
1247
+ toolCalls.clear();
1248
+ for (const [index, item] of output.entries()) {
1249
+ if (item.type === "function_call") {
1250
+ toolCalls.set(index, {
1251
+ id: item.call_id,
1252
+ name: item.name,
1253
+ arguments: item.arguments
1254
+ });
885
1255
  }
886
1256
  }
1257
+ const uniqueCalls = /* @__PURE__ */ new Map();
1258
+ for (const call of toolCalls.values()) {
1259
+ if (!call.id || !call.name)
1260
+ throw new Error("Responses stream contains an incomplete tool call");
1261
+ uniqueCalls.set(call.id, call);
1262
+ }
1263
+ toolCalls.clear();
1264
+ [...uniqueCalls.values()].forEach(
1265
+ (call, index) => toolCalls.set(index, call)
1266
+ );
1267
+ if (!output.length && toolCalls.size) {
1268
+ for (const call of toolCalls.values())
1269
+ output.push({
1270
+ type: "function_call",
1271
+ call_id: call.id,
1272
+ name: call.name,
1273
+ arguments: call.arguments
1274
+ });
1275
+ }
1276
+ if (!output.length && streamedText)
1277
+ output.push({
1278
+ type: "message",
1279
+ role: "assistant",
1280
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1281
+ });
1282
+ terminalResponse.output = output;
1283
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1284
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1285
+ yield {
1286
+ type: "message:delta",
1287
+ content: finalText.slice(streamedText.length)
1288
+ };
1289
+ }
887
1290
  yield* flushToolCalls();
888
1291
  if (citations.length > 0) {
889
1292
  yield { type: "citation", citations: deduplicateCitations(citations) };
890
1293
  }
891
- yield { type: "message:end" };
892
- yield { type: "done", usage };
1294
+ yield {
1295
+ type: "message:end",
1296
+ providerMetadata: responseMetadata(terminalResponse)
1297
+ };
1298
+ yield {
1299
+ type: "done",
1300
+ usage,
1301
+ providerMetadata: responseMetadata(terminalResponse)
1302
+ };
893
1303
  }
894
1304
  async *stream(request) {
1305
+ throwIfAborted(request.signal);
895
1306
  if (this.shouldUseResponsesApi(request)) {
896
1307
  const messageId2 = generateMessageId();
897
1308
  yield { type: "message:start", id: messageId2 };
@@ -899,47 +1310,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
899
1310
  yield* this.streamWithResponses(request);
900
1311
  return;
901
1312
  } catch (error) {
902
- yield {
903
- type: "error",
904
- message: error instanceof Error ? error.message : "Unknown error",
905
- code: "OPENAI_RESPONSES_ERROR"
906
- };
1313
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
907
1314
  return;
908
1315
  }
909
1316
  }
910
1317
  const client = await this.getClient();
911
1318
  let messages;
912
1319
  if (request.rawMessages && request.rawMessages.length > 0) {
913
- const processedMessages = request.rawMessages.map((msg) => {
914
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
915
- return { ...msg, content: null };
916
- }
917
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
918
- if (hasAttachments) {
919
- const content = [];
920
- if (msg.content) {
921
- content.push({ type: "text", text: msg.content });
922
- }
923
- for (const attachment of msg.attachments) {
924
- if (attachment.type === "image") {
925
- let imageUrl;
926
- if (attachment.url) {
927
- imageUrl = attachment.url;
928
- } else if (attachment.data) {
929
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
930
- } else {
931
- continue;
932
- }
933
- content.push({
934
- type: "image_url",
935
- image_url: { url: imageUrl, detail: "auto" }
936
- });
937
- }
938
- }
939
- return { ...msg, content, attachments: void 0 };
940
- }
941
- return msg;
942
- });
1320
+ const processedMessages = this.formatRawMessages(request.rawMessages);
943
1321
  if (request.systemPrompt) {
944
1322
  const hasSystem = processedMessages.some((m) => m.role === "system");
945
1323
  if (!hasSystem) {
@@ -959,7 +1337,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
959
1337
  request.systemPrompt
960
1338
  );
961
1339
  }
962
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1340
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1341
+ type: "function",
1342
+ function: {
1343
+ name: tool.name,
1344
+ description: tool.description,
1345
+ parameters: normalizeObjectJsonSchema(
1346
+ tool.inputSchema
1347
+ )
1348
+ }
1349
+ })) : formatTools(request.actions ?? []);
963
1350
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
964
1351
  if (webSearchConfig) {
965
1352
  const webSearchTool = {
@@ -988,26 +1375,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
988
1375
  tools: tools.length > 0 ? tools : void 0,
989
1376
  tool_choice: tools.length > 0 ? toolChoice : void 0,
990
1377
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
991
- ...buildOpenAITokenParams(
1378
+ ...buildOpenAIParameters(
992
1379
  modelIdForPayload,
993
1380
  request.config?.maxTokens ?? this.config.maxTokens,
994
- request.config?.temperature ?? this.config.temperature
1381
+ request.config?.temperature ?? this.config.temperature,
1382
+ this.parameterOptions(request)
995
1383
  ),
996
1384
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
997
1385
  stream: true,
998
1386
  stream_options: { include_usage: true }
999
1387
  };
1000
1388
  logProviderPayload("openai", "request payload", payload, request.debug);
1001
- const stream = await client.chat.completions.create(payload);
1002
- let currentToolCall = null;
1389
+ const stream = await client.chat.completions.create(payload, {
1390
+ signal: request.signal
1391
+ });
1392
+ const toolCalls = /* @__PURE__ */ new Map();
1393
+ let finishReason;
1003
1394
  const collectedCitations = [];
1004
1395
  let citationIndex = 0;
1005
1396
  let usage;
1006
1397
  for await (const chunk of stream) {
1007
1398
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
1008
- if (request.signal?.aborted) {
1009
- break;
1010
- }
1399
+ throwIfAborted(request.signal);
1011
1400
  const delta = chunk.choices[0]?.delta;
1012
1401
  const choice = chunk.choices[0];
1013
1402
  if (delta?.content) {
@@ -1030,38 +1419,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1030
1419
  }
1031
1420
  }
1032
1421
  }
1033
- if (delta?.tool_calls) {
1034
- for (const toolCall of delta.tool_calls) {
1035
- if (toolCall.id) {
1036
- if (currentToolCall) {
1037
- yield {
1038
- type: "action:args",
1039
- id: currentToolCall.id,
1040
- args: currentToolCall.arguments
1041
- };
1042
- yield {
1043
- type: "action:end",
1044
- id: currentToolCall.id,
1045
- name: currentToolCall.name
1046
- };
1047
- }
1048
- const tcExtraContent = toolCall.extra_content;
1049
- currentToolCall = {
1050
- id: toolCall.id,
1051
- name: toolCall.function?.name || "",
1052
- arguments: toolCall.function?.arguments || "",
1053
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1054
- };
1055
- yield {
1056
- type: "action:start",
1057
- id: currentToolCall.id,
1058
- name: currentToolCall.name,
1059
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1060
- };
1061
- } else if (currentToolCall && toolCall.function?.arguments) {
1062
- currentToolCall.arguments += toolCall.function.arguments;
1063
- }
1064
- }
1422
+ for (const part of delta?.tool_calls ?? []) {
1423
+ const index = part.index ?? 0;
1424
+ const call = toolCalls.get(index) ?? {
1425
+ id: "",
1426
+ name: "",
1427
+ arguments: ""
1428
+ };
1429
+ if (part.id) call.id = part.id;
1430
+ if (part.function?.name) call.name += part.function.name;
1431
+ if (part.function?.arguments)
1432
+ call.arguments += part.function.arguments;
1433
+ if (part.extra_content) call.extra_content = part.extra_content;
1434
+ toolCalls.set(index, call);
1065
1435
  }
1066
1436
  if (chunk.usage) {
1067
1437
  usage = {
@@ -1070,21 +1440,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1070
1440
  total_tokens: chunk.usage.total_tokens
1071
1441
  };
1072
1442
  }
1073
- if (choice?.finish_reason) {
1074
- if (currentToolCall) {
1075
- yield {
1076
- type: "action:args",
1077
- id: currentToolCall.id,
1078
- args: currentToolCall.arguments
1079
- };
1080
- yield {
1081
- type: "action:end",
1082
- id: currentToolCall.id,
1083
- name: currentToolCall.name
1084
- };
1085
- currentToolCall = null;
1086
- }
1087
- }
1443
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1444
+ }
1445
+ throwIfAborted(request.signal);
1446
+ if (!finishReason)
1447
+ throw new Error("OpenAI stream ended without a finish reason");
1448
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1449
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1450
+ }
1451
+ for (const call of toolCalls.values()) {
1452
+ if (!call.id || !call.name)
1453
+ throw new Error("OpenAI stream contains an incomplete tool call");
1454
+ JSON.parse(call.arguments || "{}");
1455
+ }
1456
+ for (const call of toolCalls.values()) {
1457
+ yield {
1458
+ type: "action:start",
1459
+ id: call.id,
1460
+ name: call.name,
1461
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1462
+ };
1463
+ yield { type: "action:args", id: call.id, args: call.arguments };
1464
+ yield { type: "action:end", id: call.id, name: call.name };
1088
1465
  }
1089
1466
  if (collectedCitations.length > 0) {
1090
1467
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1093,26 +1470,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1093
1470
  yield { type: "message:end" };
1094
1471
  yield { type: "done", usage };
1095
1472
  } catch (error) {
1096
- yield {
1097
- type: "error",
1098
- message: error instanceof Error ? error.message : "Unknown error",
1099
- code: `${this.provider.toUpperCase()}_ERROR`
1100
- };
1473
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1101
1474
  }
1102
1475
  }
1103
1476
  async complete(request) {
1477
+ throwIfAborted(request.signal);
1104
1478
  if (this.shouldUseResponsesApi(request)) {
1105
1479
  return this.completeWithResponses(request);
1106
1480
  }
1107
1481
  const client = await this.getClient();
1108
1482
  let messages;
1109
1483
  if (request.rawMessages && request.rawMessages.length > 0) {
1110
- const sanitized = request.rawMessages.map((msg) => {
1111
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1112
- return { ...msg, content: null };
1113
- }
1114
- return msg;
1115
- });
1484
+ const sanitized = this.formatRawMessages(request.rawMessages);
1116
1485
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1117
1486
  messages = [
1118
1487
  { role: "system", content: request.systemPrompt },
@@ -1127,7 +1496,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1127
1496
  request.systemPrompt
1128
1497
  );
1129
1498
  }
1130
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1499
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1500
+ type: "function",
1501
+ function: {
1502
+ name: tool.name,
1503
+ description: tool.description,
1504
+ parameters: normalizeObjectJsonSchema(
1505
+ tool.inputSchema
1506
+ )
1507
+ }
1508
+ })) : formatTools(request.actions ?? []);
1131
1509
  const openaiToolOptions = request.providerToolOptions?.openai;
1132
1510
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1133
1511
  type: "function",
@@ -1142,16 +1520,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1142
1520
  tools: tools.length > 0 ? tools : void 0,
1143
1521
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1144
1522
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1145
- ...buildOpenAITokenParams(
1523
+ ...buildOpenAIParameters(
1146
1524
  modelIdForCompletePayload,
1147
1525
  request.config?.maxTokens ?? this.config.maxTokens,
1148
- request.config?.temperature ?? this.config.temperature
1526
+ request.config?.temperature ?? this.config.temperature,
1527
+ this.parameterOptions(request)
1149
1528
  ),
1150
1529
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1151
1530
  stream: false
1152
1531
  };
1153
1532
  logProviderPayload("openai", "request payload", payload, request.debug);
1154
- const response = await client.chat.completions.create(payload);
1533
+ const response = await client.chat.completions.create(payload, {
1534
+ signal: request.signal
1535
+ });
1155
1536
  logProviderPayload("openai", "response payload", response, request.debug);
1156
1537
  const choice = response.choices?.[0];
1157
1538
  const message = choice?.message;