@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
  }
@@ -463,7 +609,7 @@ function formatMessagesForXAI(messages) {
463
609
  return {
464
610
  role: "tool",
465
611
  tool_call_id: msg.toolCallId,
466
- content: msg.content
612
+ content: textToolOutput(msg.modelOutput, msg.content)
467
613
  };
468
614
  default:
469
615
  return msg;
@@ -471,6 +617,86 @@ function formatMessagesForXAI(messages) {
471
617
  });
472
618
  }
473
619
 
620
+ // src/core/openai-responses.ts
621
+ function resolveOpenAITransport(defaults, overrides) {
622
+ return {
623
+ api: overrides?.api ?? defaults.api ?? "auto",
624
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
625
+ };
626
+ }
627
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
628
+ const api = transport.api ?? "auto";
629
+ if (!["auto", "chat", "responses"].includes(api)) {
630
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
631
+ }
632
+ if (api === "chat") {
633
+ if (transport.responses !== void 0 || nativeToolSearch) {
634
+ throw new Error(
635
+ "Responses options and native tool search require the Responses API"
636
+ );
637
+ }
638
+ return false;
639
+ }
640
+ let officialEndpoint = !baseURL;
641
+ try {
642
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
643
+ } catch {
644
+ }
645
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
646
+ }
647
+ function responseMetadata(response) {
648
+ const output = Array.isArray(response?.output) ? response.output : [];
649
+ return {
650
+ openai: {
651
+ response: {
652
+ id: response?.id,
653
+ model: response?.model,
654
+ status: response?.status,
655
+ output,
656
+ usage: response?.usage,
657
+ store: response?.store,
658
+ refusals: output.flatMap(
659
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
660
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
661
+ ).map((part) => part.refusal) : []
662
+ )
663
+ }
664
+ }
665
+ };
666
+ }
667
+ function getResponseReplay(metadata) {
668
+ const response = metadata?.openai?.response;
669
+ if (response === void 0) return void 0;
670
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
671
+ throw new Error(
672
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
673
+ );
674
+ }
675
+ return response.output;
676
+ }
677
+
678
+ // src/core/provider-error.ts
679
+ function providerErrorEvent(error, code) {
680
+ const details = error ?? {};
681
+ const event = {
682
+ type: "error",
683
+ message: error instanceof Error ? error.message : String(error),
684
+ code,
685
+ ...typeof details.status === "number" ? { status: details.status } : {},
686
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
687
+ ...typeof details.param === "string" ? { param: details.param } : {},
688
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
689
+ };
690
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
691
+ return event;
692
+ }
693
+ function throwIfAborted(signal) {
694
+ if (!signal?.aborted) return;
695
+ const error = new Error("The operation was aborted");
696
+ error.name = "AbortError";
697
+ throw error;
698
+ }
699
+
474
700
  // src/core/utils.ts
475
701
  function generateId(prefix = "id") {
476
702
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -502,39 +728,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
502
728
  const { default: OpenAI } = await import('openai');
503
729
  this.client = new OpenAI({
504
730
  apiKey: this.config.apiKey,
505
- baseURL: this.config.baseUrl
731
+ baseURL: this.config.baseUrl,
732
+ organization: this.config.organization,
733
+ defaultHeaders: this.config.headers
506
734
  });
507
735
  }
508
736
  return this.client;
509
737
  }
738
+ parameterOptions(request) {
739
+ return {
740
+ ...this.config,
741
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
742
+ };
743
+ }
744
+ toolDefinitions(request) {
745
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
746
+ return formatTools(request.actions ?? []).map((tool) => ({
747
+ name: tool.function.name,
748
+ description: tool.function.description ?? "",
749
+ location: "server",
750
+ inputSchema: tool.function.parameters
751
+ }));
752
+ }
510
753
  shouldUseResponsesApi(request) {
511
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
512
- if (!hasTools) return false;
513
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
514
- return true;
515
- }
516
- return this.requiresResponsesApiForTools(
517
- request.config?.model || this.model
754
+ return useOpenAIResponses(
755
+ request.config?.model || this.model,
756
+ this.config.baseUrl,
757
+ this.parameterOptions(request),
758
+ resolveOpenAITransport(this.config, request.config?.openai),
759
+ this.toolDefinitions(request).length > 0,
760
+ !!(request.rawMessages ?? request.messages)?.some(
761
+ (message) => message.providerMetadata?.openai?.response
762
+ ),
763
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
518
764
  );
519
765
  }
520
- /**
521
- * Whether this model rejects function tools on /v1/chat/completions.
522
- *
523
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
524
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
525
- * restriction and may not implement /v1/responses at all.
526
- */
527
- requiresResponsesApiForTools(modelId) {
528
- if (!modelId) return false;
529
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
530
- const baseUrl = this.config.baseUrl;
531
- if (!baseUrl) return true;
532
- try {
533
- return new URL(baseUrl).hostname === "api.openai.com";
534
- } catch {
535
- return false;
536
- }
537
- }
538
766
  /**
539
767
  * Convert one message's content to Responses content parts.
540
768
  *
@@ -565,11 +793,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
565
793
  });
566
794
  }
567
795
  buildResponsesInput(request) {
568
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
796
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
569
797
  const input = [];
570
798
  for (const message of sourceMessages) {
571
799
  if (message.role === "system") {
572
- if (!request.systemPrompt && message.content) {
800
+ if (message.content && message.content !== request.systemPrompt) {
573
801
  input.push({
574
802
  type: "message",
575
803
  role: "developer",
@@ -579,6 +807,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
579
807
  continue;
580
808
  }
581
809
  if (message.role === "assistant") {
810
+ const replay = getResponseReplay(message.providerMetadata);
811
+ if (replay) {
812
+ input.push(...replay);
813
+ continue;
814
+ }
582
815
  const content = this.toResponsesContent(message.content, "assistant");
583
816
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
584
817
  input.push({
@@ -602,7 +835,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
602
835
  input.push({
603
836
  type: "function_call_output",
604
837
  call_id: message.tool_call_id,
605
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
838
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
606
839
  });
607
840
  continue;
608
841
  }
@@ -615,6 +848,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
615
848
  }
616
849
  return input;
617
850
  }
851
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
852
+ formatRawMessages(messages, preserveProviderMetadata = false) {
853
+ return messages.map((message) => {
854
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
855
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
856
+ if (preserveProviderMetadata && modelOutput)
857
+ rest.modelOutput = modelOutput;
858
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
859
+ content = null;
860
+ }
861
+ if (Array.isArray(attachments) && attachments.length > 0) {
862
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
863
+ (attachment) => attachmentToOpenAIImage(attachment)
864
+ ).filter((image) => image !== null);
865
+ if (images.length > 0) {
866
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
867
+ content = [...parts, ...images];
868
+ }
869
+ }
870
+ return {
871
+ ...rest,
872
+ content,
873
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
874
+ };
875
+ });
876
+ }
618
877
  /**
619
878
  * Build the web_search tool for the Responses API, or undefined when web
620
879
  * search is off.
@@ -693,20 +952,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
693
952
  ).map((part) => part.text) : []
694
953
  ).join("") : "";
695
954
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
696
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
697
- id: item.call_id ?? item.id ?? generateToolCallId(),
698
- name: item.name,
699
- args: (() => {
700
- try {
701
- return JSON.parse(item.arguments ?? "{}");
702
- } catch {
703
- return {};
704
- }
705
- })()
706
- })) : [];
955
+ const seenCallIds = /* @__PURE__ */ new Set();
956
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
957
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
958
+ throw new Error("Responses contains an incomplete function call");
959
+ }
960
+ if (seenCallIds.has(item.call_id))
961
+ throw new Error("Responses contains duplicate function call IDs");
962
+ seenCallIds.add(item.call_id);
963
+ const args = JSON.parse(item.arguments);
964
+ if (!args || typeof args !== "object" || Array.isArray(args))
965
+ throw new Error(
966
+ "Responses function arguments must be a JSON object"
967
+ );
968
+ return { id: item.call_id, name: item.name, args };
969
+ }) : [];
707
970
  return {
708
971
  content,
709
972
  toolCalls,
973
+ providerMetadata: responseMetadata(response),
710
974
  usage: response?.usage ? {
711
975
  promptTokens: response.usage.input_tokens ?? 0,
712
976
  completionTokens: response.usage.output_tokens ?? 0,
@@ -717,6 +981,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
717
981
  }
718
982
  buildResponsesPayload(request, stream) {
719
983
  const openaiToolOptions = request.providerToolOptions?.openai;
984
+ const options = resolveOpenAITransport(
985
+ this.config,
986
+ request.config?.openai
987
+ ).responses;
988
+ const parameters = buildOpenAIParameters(
989
+ request.config?.model || this.model,
990
+ request.config?.maxTokens ?? this.config.maxTokens,
991
+ request.config?.temperature ?? this.config.temperature,
992
+ this.parameterOptions(request),
993
+ "responses"
994
+ );
720
995
  const responsesTextFormat = toOpenAIResponsesTextFormat(
721
996
  request.config?.responseFormat
722
997
  );
@@ -726,18 +1001,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
726
1001
  instructions: request.systemPrompt,
727
1002
  input: this.buildResponsesInput(request),
728
1003
  tools: this.buildResponsesTools(
729
- request.toolDefinitions ?? [],
1004
+ this.toolDefinitions(request),
730
1005
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
731
1006
  this.buildResponsesWebSearchTool(request)
732
1007
  ),
733
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
1008
+ tool_choice: openaiToolOptions?.toolChoice,
734
1009
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
735
- // Reasoning models reject `temperature` outright — sending it would swap one
736
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
737
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
738
- temperature: request.config?.temperature ?? this.config.temperature
739
- },
740
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
1010
+ ...parameters,
1011
+ ...options?.reasoningSummary ? {
1012
+ reasoning: {
1013
+ ...parameters.reasoning,
1014
+ summary: options.reasoningSummary
1015
+ }
1016
+ } : {},
1017
+ ...options?.store !== void 0 ? { store: options.store } : {},
1018
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1019
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1020
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1021
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1022
+ ...options?.store === false || options?.include ? {
1023
+ include: [
1024
+ .../* @__PURE__ */ new Set([
1025
+ ...options?.include ?? [],
1026
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1027
+ ])
1028
+ ]
1029
+ } : {},
741
1030
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
742
1031
  stream
743
1032
  };
@@ -745,10 +1034,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
745
1034
  }
746
1035
  async completeWithResponses(request) {
747
1036
  const client = await this.getClient();
1037
+ throwIfAborted(request.signal);
748
1038
  const payload = this.buildResponsesPayload(request, false);
749
1039
  logProviderPayload("openai", "request payload", payload, request.debug);
750
- const response = await client.responses.create(payload);
1040
+ if (!client.responses?.create)
1041
+ throw new Error(
1042
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1043
+ );
1044
+ const response = await client.responses.create(payload, {
1045
+ signal: request.signal
1046
+ });
1047
+ throwIfAborted(request.signal);
751
1048
  logProviderPayload("openai", "response payload", response, request.debug);
1049
+ if (response.status && response.status !== "completed") {
1050
+ throw Object.assign(
1051
+ new Error(
1052
+ response.error?.message ?? `Responses request ${response.status}`
1053
+ ),
1054
+ response.error
1055
+ );
1056
+ }
752
1057
  return this.parseResponsesResult(response);
753
1058
  }
754
1059
  /**
@@ -766,13 +1071,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
766
1071
  */
767
1072
  async *streamWithResponses(request) {
768
1073
  const client = await this.getClient();
1074
+ throwIfAborted(request.signal);
769
1075
  const payload = this.buildResponsesPayload(request, true);
770
1076
  logProviderPayload("openai", "request payload", payload, request.debug);
771
- const stream = await client.responses.create(payload);
1077
+ if (!client.responses?.create)
1078
+ throw new Error(
1079
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1080
+ );
1081
+ const stream = await client.responses.create(payload, {
1082
+ signal: request.signal
1083
+ });
772
1084
  const toolCalls = /* @__PURE__ */ new Map();
773
1085
  let usage;
774
1086
  const citations = [];
1087
+ let completed = false;
1088
+ let terminalResponse;
1089
+ let responseIdentity = {};
1090
+ const outputItems = /* @__PURE__ */ new Map();
1091
+ let streamedText = "";
775
1092
  function* flushToolCalls() {
1093
+ for (const call of toolCalls.values()) {
1094
+ if (!call.name)
1095
+ throw new Error("Responses tool call is missing its name");
1096
+ JSON.parse(call.arguments || "{}");
1097
+ }
776
1098
  for (const call of toolCalls.values()) {
777
1099
  yield { type: "action:start", id: call.id, name: call.name };
778
1100
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -781,19 +1103,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
781
1103
  toolCalls.clear();
782
1104
  }
783
1105
  for await (const event of stream) {
784
- if (request.signal?.aborted) break;
1106
+ throwIfAborted(request.signal);
785
1107
  logProviderPayload("openai", "stream chunk", event, request.debug);
1108
+ if (completed)
1109
+ throw new Error("Responses stream emitted an event after completion");
786
1110
  const index = event?.output_index ?? 0;
787
1111
  switch (event?.type) {
1112
+ case "response.created":
1113
+ responseIdentity = {
1114
+ id: event.response?.id,
1115
+ model: event.response?.model
1116
+ };
1117
+ break;
1118
+ case "response.output_item.done":
1119
+ if (event.item) outputItems.set(index, event.item);
1120
+ if (event.item?.type === "function_call") {
1121
+ toolCalls.set(index, {
1122
+ id: event.item.call_id,
1123
+ name: event.item.name,
1124
+ arguments: event.item.arguments
1125
+ });
1126
+ }
1127
+ break;
788
1128
  case "response.output_text.delta":
789
1129
  if (event.delta) {
1130
+ streamedText += event.delta;
790
1131
  yield { type: "message:delta", content: event.delta };
791
1132
  }
792
1133
  break;
793
1134
  case "response.output_item.added":
794
1135
  if (event.item?.type === "function_call") {
795
1136
  toolCalls.set(index, {
796
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1137
+ id: event.item.call_id ?? "",
797
1138
  name: event.item.name ?? "",
798
1139
  arguments: event.item.arguments ?? ""
799
1140
  });
@@ -811,8 +1152,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
811
1152
  }
812
1153
  break;
813
1154
  }
814
- case "response.completed":
815
- case "response.incomplete": {
1155
+ case "response.completed": {
1156
+ if (event.response?.status && event.response.status !== "completed")
1157
+ throw new Error(`Responses stream ${event.response.status}`);
1158
+ completed = true;
1159
+ terminalResponse = { ...responseIdentity, ...event.response };
816
1160
  const found = this.collectResponsesCitations(
817
1161
  event.response?.output,
818
1162
  citations.length
@@ -828,21 +1172,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
828
1172
  }
829
1173
  break;
830
1174
  }
831
- case "error":
832
- case "response.failed":
1175
+ case "response.incomplete":
833
1176
  throw new Error(
834
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1177
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
1178
+ );
1179
+ case "error":
1180
+ case "response.failed": {
1181
+ const details = event.response?.error ?? event;
1182
+ throw Object.assign(
1183
+ new Error(details.message ?? "Responses stream failed"),
1184
+ {
1185
+ code: details.code,
1186
+ param: details.param,
1187
+ status: details.status
1188
+ }
835
1189
  );
1190
+ }
1191
+ }
1192
+ }
1193
+ throwIfAborted(request.signal);
1194
+ if (!completed)
1195
+ throw new Error("Responses stream ended without a completed response");
1196
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1197
+ if (output.some((item) => item.type === "function_call"))
1198
+ toolCalls.clear();
1199
+ for (const [index, item] of output.entries()) {
1200
+ if (item.type === "function_call") {
1201
+ toolCalls.set(index, {
1202
+ id: item.call_id,
1203
+ name: item.name,
1204
+ arguments: item.arguments
1205
+ });
836
1206
  }
837
1207
  }
1208
+ const uniqueCalls = /* @__PURE__ */ new Map();
1209
+ for (const call of toolCalls.values()) {
1210
+ if (!call.id || !call.name)
1211
+ throw new Error("Responses stream contains an incomplete tool call");
1212
+ uniqueCalls.set(call.id, call);
1213
+ }
1214
+ toolCalls.clear();
1215
+ [...uniqueCalls.values()].forEach(
1216
+ (call, index) => toolCalls.set(index, call)
1217
+ );
1218
+ if (!output.length && toolCalls.size) {
1219
+ for (const call of toolCalls.values())
1220
+ output.push({
1221
+ type: "function_call",
1222
+ call_id: call.id,
1223
+ name: call.name,
1224
+ arguments: call.arguments
1225
+ });
1226
+ }
1227
+ if (!output.length && streamedText)
1228
+ output.push({
1229
+ type: "message",
1230
+ role: "assistant",
1231
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1232
+ });
1233
+ terminalResponse.output = output;
1234
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1235
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1236
+ yield {
1237
+ type: "message:delta",
1238
+ content: finalText.slice(streamedText.length)
1239
+ };
1240
+ }
838
1241
  yield* flushToolCalls();
839
1242
  if (citations.length > 0) {
840
1243
  yield { type: "citation", citations: deduplicateCitations(citations) };
841
1244
  }
842
- yield { type: "message:end" };
843
- yield { type: "done", usage };
1245
+ yield {
1246
+ type: "message:end",
1247
+ providerMetadata: responseMetadata(terminalResponse)
1248
+ };
1249
+ yield {
1250
+ type: "done",
1251
+ usage,
1252
+ providerMetadata: responseMetadata(terminalResponse)
1253
+ };
844
1254
  }
845
1255
  async *stream(request) {
1256
+ throwIfAborted(request.signal);
846
1257
  if (this.shouldUseResponsesApi(request)) {
847
1258
  const messageId2 = generateMessageId();
848
1259
  yield { type: "message:start", id: messageId2 };
@@ -850,47 +1261,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
850
1261
  yield* this.streamWithResponses(request);
851
1262
  return;
852
1263
  } catch (error) {
853
- yield {
854
- type: "error",
855
- message: error instanceof Error ? error.message : "Unknown error",
856
- code: "OPENAI_RESPONSES_ERROR"
857
- };
1264
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
858
1265
  return;
859
1266
  }
860
1267
  }
861
1268
  const client = await this.getClient();
862
1269
  let messages;
863
1270
  if (request.rawMessages && request.rawMessages.length > 0) {
864
- const processedMessages = request.rawMessages.map((msg) => {
865
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
866
- return { ...msg, content: null };
867
- }
868
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
869
- if (hasAttachments) {
870
- const content = [];
871
- if (msg.content) {
872
- content.push({ type: "text", text: msg.content });
873
- }
874
- for (const attachment of msg.attachments) {
875
- if (attachment.type === "image") {
876
- let imageUrl;
877
- if (attachment.url) {
878
- imageUrl = attachment.url;
879
- } else if (attachment.data) {
880
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
881
- } else {
882
- continue;
883
- }
884
- content.push({
885
- type: "image_url",
886
- image_url: { url: imageUrl, detail: "auto" }
887
- });
888
- }
889
- }
890
- return { ...msg, content, attachments: void 0 };
891
- }
892
- return msg;
893
- });
1271
+ const processedMessages = this.formatRawMessages(request.rawMessages);
894
1272
  if (request.systemPrompt) {
895
1273
  const hasSystem = processedMessages.some((m) => m.role === "system");
896
1274
  if (!hasSystem) {
@@ -910,7 +1288,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
910
1288
  request.systemPrompt
911
1289
  );
912
1290
  }
913
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1291
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1292
+ type: "function",
1293
+ function: {
1294
+ name: tool.name,
1295
+ description: tool.description,
1296
+ parameters: normalizeObjectJsonSchema(
1297
+ tool.inputSchema
1298
+ )
1299
+ }
1300
+ })) : formatTools(request.actions ?? []);
914
1301
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
915
1302
  if (webSearchConfig) {
916
1303
  const webSearchTool = {
@@ -939,26 +1326,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
939
1326
  tools: tools.length > 0 ? tools : void 0,
940
1327
  tool_choice: tools.length > 0 ? toolChoice : void 0,
941
1328
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
942
- ...buildOpenAITokenParams(
1329
+ ...buildOpenAIParameters(
943
1330
  modelIdForPayload,
944
1331
  request.config?.maxTokens ?? this.config.maxTokens,
945
- request.config?.temperature ?? this.config.temperature
1332
+ request.config?.temperature ?? this.config.temperature,
1333
+ this.parameterOptions(request)
946
1334
  ),
947
1335
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
948
1336
  stream: true,
949
1337
  stream_options: { include_usage: true }
950
1338
  };
951
1339
  logProviderPayload("openai", "request payload", payload, request.debug);
952
- const stream = await client.chat.completions.create(payload);
953
- let currentToolCall = null;
1340
+ const stream = await client.chat.completions.create(payload, {
1341
+ signal: request.signal
1342
+ });
1343
+ const toolCalls = /* @__PURE__ */ new Map();
1344
+ let finishReason;
954
1345
  const collectedCitations = [];
955
1346
  let citationIndex = 0;
956
1347
  let usage;
957
1348
  for await (const chunk of stream) {
958
1349
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
959
- if (request.signal?.aborted) {
960
- break;
961
- }
1350
+ throwIfAborted(request.signal);
962
1351
  const delta = chunk.choices[0]?.delta;
963
1352
  const choice = chunk.choices[0];
964
1353
  if (delta?.content) {
@@ -981,38 +1370,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
981
1370
  }
982
1371
  }
983
1372
  }
984
- if (delta?.tool_calls) {
985
- for (const toolCall of delta.tool_calls) {
986
- if (toolCall.id) {
987
- if (currentToolCall) {
988
- yield {
989
- type: "action:args",
990
- id: currentToolCall.id,
991
- args: currentToolCall.arguments
992
- };
993
- yield {
994
- type: "action:end",
995
- id: currentToolCall.id,
996
- name: currentToolCall.name
997
- };
998
- }
999
- const tcExtraContent = toolCall.extra_content;
1000
- currentToolCall = {
1001
- id: toolCall.id,
1002
- name: toolCall.function?.name || "",
1003
- arguments: toolCall.function?.arguments || "",
1004
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1005
- };
1006
- yield {
1007
- type: "action:start",
1008
- id: currentToolCall.id,
1009
- name: currentToolCall.name,
1010
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1011
- };
1012
- } else if (currentToolCall && toolCall.function?.arguments) {
1013
- currentToolCall.arguments += toolCall.function.arguments;
1014
- }
1015
- }
1373
+ for (const part of delta?.tool_calls ?? []) {
1374
+ const index = part.index ?? 0;
1375
+ const call = toolCalls.get(index) ?? {
1376
+ id: "",
1377
+ name: "",
1378
+ arguments: ""
1379
+ };
1380
+ if (part.id) call.id = part.id;
1381
+ if (part.function?.name) call.name += part.function.name;
1382
+ if (part.function?.arguments)
1383
+ call.arguments += part.function.arguments;
1384
+ if (part.extra_content) call.extra_content = part.extra_content;
1385
+ toolCalls.set(index, call);
1016
1386
  }
1017
1387
  if (chunk.usage) {
1018
1388
  usage = {
@@ -1021,21 +1391,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1021
1391
  total_tokens: chunk.usage.total_tokens
1022
1392
  };
1023
1393
  }
1024
- if (choice?.finish_reason) {
1025
- if (currentToolCall) {
1026
- yield {
1027
- type: "action:args",
1028
- id: currentToolCall.id,
1029
- args: currentToolCall.arguments
1030
- };
1031
- yield {
1032
- type: "action:end",
1033
- id: currentToolCall.id,
1034
- name: currentToolCall.name
1035
- };
1036
- currentToolCall = null;
1037
- }
1038
- }
1394
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1395
+ }
1396
+ throwIfAborted(request.signal);
1397
+ if (!finishReason)
1398
+ throw new Error("OpenAI stream ended without a finish reason");
1399
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1400
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1401
+ }
1402
+ for (const call of toolCalls.values()) {
1403
+ if (!call.id || !call.name)
1404
+ throw new Error("OpenAI stream contains an incomplete tool call");
1405
+ JSON.parse(call.arguments || "{}");
1406
+ }
1407
+ for (const call of toolCalls.values()) {
1408
+ yield {
1409
+ type: "action:start",
1410
+ id: call.id,
1411
+ name: call.name,
1412
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1413
+ };
1414
+ yield { type: "action:args", id: call.id, args: call.arguments };
1415
+ yield { type: "action:end", id: call.id, name: call.name };
1039
1416
  }
1040
1417
  if (collectedCitations.length > 0) {
1041
1418
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1044,26 +1421,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1044
1421
  yield { type: "message:end" };
1045
1422
  yield { type: "done", usage };
1046
1423
  } catch (error) {
1047
- yield {
1048
- type: "error",
1049
- message: error instanceof Error ? error.message : "Unknown error",
1050
- code: `${this.provider.toUpperCase()}_ERROR`
1051
- };
1424
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1052
1425
  }
1053
1426
  }
1054
1427
  async complete(request) {
1428
+ throwIfAborted(request.signal);
1055
1429
  if (this.shouldUseResponsesApi(request)) {
1056
1430
  return this.completeWithResponses(request);
1057
1431
  }
1058
1432
  const client = await this.getClient();
1059
1433
  let messages;
1060
1434
  if (request.rawMessages && request.rawMessages.length > 0) {
1061
- const sanitized = request.rawMessages.map((msg) => {
1062
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1063
- return { ...msg, content: null };
1064
- }
1065
- return msg;
1066
- });
1435
+ const sanitized = this.formatRawMessages(request.rawMessages);
1067
1436
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1068
1437
  messages = [
1069
1438
  { role: "system", content: request.systemPrompt },
@@ -1078,7 +1447,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1078
1447
  request.systemPrompt
1079
1448
  );
1080
1449
  }
1081
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1450
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1451
+ type: "function",
1452
+ function: {
1453
+ name: tool.name,
1454
+ description: tool.description,
1455
+ parameters: normalizeObjectJsonSchema(
1456
+ tool.inputSchema
1457
+ )
1458
+ }
1459
+ })) : formatTools(request.actions ?? []);
1082
1460
  const openaiToolOptions = request.providerToolOptions?.openai;
1083
1461
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1084
1462
  type: "function",
@@ -1093,16 +1471,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1093
1471
  tools: tools.length > 0 ? tools : void 0,
1094
1472
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1095
1473
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1096
- ...buildOpenAITokenParams(
1474
+ ...buildOpenAIParameters(
1097
1475
  modelIdForCompletePayload,
1098
1476
  request.config?.maxTokens ?? this.config.maxTokens,
1099
- request.config?.temperature ?? this.config.temperature
1477
+ request.config?.temperature ?? this.config.temperature,
1478
+ this.parameterOptions(request)
1100
1479
  ),
1101
1480
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1102
1481
  stream: false
1103
1482
  };
1104
1483
  logProviderPayload("openai", "request payload", payload, request.debug);
1105
- const response = await client.chat.completions.create(payload);
1484
+ const response = await client.chat.completions.create(payload, {
1485
+ signal: request.signal
1486
+ });
1106
1487
  logProviderPayload("openai", "response payload", response, request.debug);
1107
1488
  const choice = response.choices?.[0];
1108
1489
  const message = choice?.message;