@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
  }
@@ -432,7 +578,7 @@ function formatMessages(messages) {
432
578
  return {
433
579
  role: "tool",
434
580
  tool_call_id: msg.toolCallId,
435
- content: msg.content
581
+ content: textToolOutput(msg.modelOutput, msg.content)
436
582
  };
437
583
  default:
438
584
  return msg;
@@ -440,6 +586,86 @@ function formatMessages(messages) {
440
586
  });
441
587
  }
442
588
 
589
+ // src/core/openai-responses.ts
590
+ function resolveOpenAITransport(defaults, overrides) {
591
+ return {
592
+ api: overrides?.api ?? defaults.api ?? "auto",
593
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
594
+ };
595
+ }
596
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
597
+ const api = transport.api ?? "auto";
598
+ if (!["auto", "chat", "responses"].includes(api)) {
599
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
600
+ }
601
+ if (api === "chat") {
602
+ if (transport.responses !== void 0 || nativeToolSearch) {
603
+ throw new Error(
604
+ "Responses options and native tool search require the Responses API"
605
+ );
606
+ }
607
+ return false;
608
+ }
609
+ let officialEndpoint = !baseURL;
610
+ try {
611
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
612
+ } catch {
613
+ }
614
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
615
+ }
616
+ function responseMetadata(response) {
617
+ const output = Array.isArray(response?.output) ? response.output : [];
618
+ return {
619
+ openai: {
620
+ response: {
621
+ id: response?.id,
622
+ model: response?.model,
623
+ status: response?.status,
624
+ output,
625
+ usage: response?.usage,
626
+ store: response?.store,
627
+ refusals: output.flatMap(
628
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
629
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
630
+ ).map((part) => part.refusal) : []
631
+ )
632
+ }
633
+ }
634
+ };
635
+ }
636
+ function getResponseReplay(metadata) {
637
+ const response = metadata?.openai?.response;
638
+ if (response === void 0) return void 0;
639
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
640
+ throw new Error(
641
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
642
+ );
643
+ }
644
+ return response.output;
645
+ }
646
+
647
+ // src/core/provider-error.ts
648
+ function providerErrorEvent(error, code) {
649
+ const details = error ?? {};
650
+ const event = {
651
+ type: "error",
652
+ message: error instanceof Error ? error.message : String(error),
653
+ code,
654
+ ...typeof details.status === "number" ? { status: details.status } : {},
655
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
656
+ ...typeof details.param === "string" ? { param: details.param } : {},
657
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
658
+ };
659
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
660
+ return event;
661
+ }
662
+ function throwIfAborted(signal) {
663
+ if (!signal?.aborted) return;
664
+ const error = new Error("The operation was aborted");
665
+ error.name = "AbortError";
666
+ throw error;
667
+ }
668
+
443
669
  // src/core/utils.ts
444
670
  function generateId(prefix = "id") {
445
671
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -471,39 +697,41 @@ var OpenAIAdapter = class _OpenAIAdapter {
471
697
  const { default: OpenAI } = await import('openai');
472
698
  this.client = new OpenAI({
473
699
  apiKey: this.config.apiKey,
474
- baseURL: this.config.baseUrl
700
+ baseURL: this.config.baseUrl,
701
+ organization: this.config.organization,
702
+ defaultHeaders: this.config.headers
475
703
  });
476
704
  }
477
705
  return this.client;
478
706
  }
707
+ parameterOptions(request) {
708
+ return {
709
+ ...this.config,
710
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
711
+ };
712
+ }
713
+ toolDefinitions(request) {
714
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
715
+ return formatTools(request.actions ?? []).map((tool) => ({
716
+ name: tool.function.name,
717
+ description: tool.function.description ?? "",
718
+ location: "server",
719
+ inputSchema: tool.function.parameters
720
+ }));
721
+ }
479
722
  shouldUseResponsesApi(request) {
480
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
481
- if (!hasTools) return false;
482
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
483
- return true;
484
- }
485
- return this.requiresResponsesApiForTools(
486
- request.config?.model || this.model
723
+ return useOpenAIResponses(
724
+ request.config?.model || this.model,
725
+ this.config.baseUrl,
726
+ this.parameterOptions(request),
727
+ resolveOpenAITransport(this.config, request.config?.openai),
728
+ this.toolDefinitions(request).length > 0,
729
+ !!(request.rawMessages ?? request.messages)?.some(
730
+ (message) => message.providerMetadata?.openai?.response
731
+ ),
732
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
487
733
  );
488
734
  }
489
- /**
490
- * Whether this model rejects function tools on /v1/chat/completions.
491
- *
492
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
493
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
494
- * restriction and may not implement /v1/responses at all.
495
- */
496
- requiresResponsesApiForTools(modelId) {
497
- if (!modelId) return false;
498
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
499
- const baseUrl = this.config.baseUrl;
500
- if (!baseUrl) return true;
501
- try {
502
- return new URL(baseUrl).hostname === "api.openai.com";
503
- } catch {
504
- return false;
505
- }
506
- }
507
735
  /**
508
736
  * Convert one message's content to Responses content parts.
509
737
  *
@@ -534,11 +762,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
534
762
  });
535
763
  }
536
764
  buildResponsesInput(request) {
537
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
765
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
538
766
  const input = [];
539
767
  for (const message of sourceMessages) {
540
768
  if (message.role === "system") {
541
- if (!request.systemPrompt && message.content) {
769
+ if (message.content && message.content !== request.systemPrompt) {
542
770
  input.push({
543
771
  type: "message",
544
772
  role: "developer",
@@ -548,6 +776,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
548
776
  continue;
549
777
  }
550
778
  if (message.role === "assistant") {
779
+ const replay = getResponseReplay(message.providerMetadata);
780
+ if (replay) {
781
+ input.push(...replay);
782
+ continue;
783
+ }
551
784
  const content = this.toResponsesContent(message.content, "assistant");
552
785
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
553
786
  input.push({
@@ -571,7 +804,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
571
804
  input.push({
572
805
  type: "function_call_output",
573
806
  call_id: message.tool_call_id,
574
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
807
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
575
808
  });
576
809
  continue;
577
810
  }
@@ -584,6 +817,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
584
817
  }
585
818
  return input;
586
819
  }
820
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
821
+ formatRawMessages(messages, preserveProviderMetadata = false) {
822
+ return messages.map((message) => {
823
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
824
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
825
+ if (preserveProviderMetadata && modelOutput)
826
+ rest.modelOutput = modelOutput;
827
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
828
+ content = null;
829
+ }
830
+ if (Array.isArray(attachments) && attachments.length > 0) {
831
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
832
+ (attachment) => attachmentToOpenAIImage(attachment)
833
+ ).filter((image) => image !== null);
834
+ if (images.length > 0) {
835
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
836
+ content = [...parts, ...images];
837
+ }
838
+ }
839
+ return {
840
+ ...rest,
841
+ content,
842
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
843
+ };
844
+ });
845
+ }
587
846
  /**
588
847
  * Build the web_search tool for the Responses API, or undefined when web
589
848
  * search is off.
@@ -662,20 +921,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
662
921
  ).map((part) => part.text) : []
663
922
  ).join("") : "";
664
923
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
665
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
666
- id: item.call_id ?? item.id ?? generateToolCallId(),
667
- name: item.name,
668
- args: (() => {
669
- try {
670
- return JSON.parse(item.arguments ?? "{}");
671
- } catch {
672
- return {};
673
- }
674
- })()
675
- })) : [];
924
+ const seenCallIds = /* @__PURE__ */ new Set();
925
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
926
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
927
+ throw new Error("Responses contains an incomplete function call");
928
+ }
929
+ if (seenCallIds.has(item.call_id))
930
+ throw new Error("Responses contains duplicate function call IDs");
931
+ seenCallIds.add(item.call_id);
932
+ const args = JSON.parse(item.arguments);
933
+ if (!args || typeof args !== "object" || Array.isArray(args))
934
+ throw new Error(
935
+ "Responses function arguments must be a JSON object"
936
+ );
937
+ return { id: item.call_id, name: item.name, args };
938
+ }) : [];
676
939
  return {
677
940
  content,
678
941
  toolCalls,
942
+ providerMetadata: responseMetadata(response),
679
943
  usage: response?.usage ? {
680
944
  promptTokens: response.usage.input_tokens ?? 0,
681
945
  completionTokens: response.usage.output_tokens ?? 0,
@@ -686,6 +950,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
686
950
  }
687
951
  buildResponsesPayload(request, stream) {
688
952
  const openaiToolOptions = request.providerToolOptions?.openai;
953
+ const options = resolveOpenAITransport(
954
+ this.config,
955
+ request.config?.openai
956
+ ).responses;
957
+ const parameters = buildOpenAIParameters(
958
+ request.config?.model || this.model,
959
+ request.config?.maxTokens ?? this.config.maxTokens,
960
+ request.config?.temperature ?? this.config.temperature,
961
+ this.parameterOptions(request),
962
+ "responses"
963
+ );
689
964
  const responsesTextFormat = toOpenAIResponsesTextFormat(
690
965
  request.config?.responseFormat
691
966
  );
@@ -695,18 +970,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
695
970
  instructions: request.systemPrompt,
696
971
  input: this.buildResponsesInput(request),
697
972
  tools: this.buildResponsesTools(
698
- request.toolDefinitions ?? [],
973
+ this.toolDefinitions(request),
699
974
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
700
975
  this.buildResponsesWebSearchTool(request)
701
976
  ),
702
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
977
+ tool_choice: openaiToolOptions?.toolChoice,
703
978
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
704
- // Reasoning models reject `temperature` outright — sending it would swap one
705
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
706
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
707
- temperature: request.config?.temperature ?? this.config.temperature
708
- },
709
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
979
+ ...parameters,
980
+ ...options?.reasoningSummary ? {
981
+ reasoning: {
982
+ ...parameters.reasoning,
983
+ summary: options.reasoningSummary
984
+ }
985
+ } : {},
986
+ ...options?.store !== void 0 ? { store: options.store } : {},
987
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
988
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
989
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
990
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
991
+ ...options?.store === false || options?.include ? {
992
+ include: [
993
+ .../* @__PURE__ */ new Set([
994
+ ...options?.include ?? [],
995
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
996
+ ])
997
+ ]
998
+ } : {},
710
999
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
711
1000
  stream
712
1001
  };
@@ -714,10 +1003,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
714
1003
  }
715
1004
  async completeWithResponses(request) {
716
1005
  const client = await this.getClient();
1006
+ throwIfAborted(request.signal);
717
1007
  const payload = this.buildResponsesPayload(request, false);
718
1008
  logProviderPayload("openai", "request payload", payload, request.debug);
719
- const response = await client.responses.create(payload);
1009
+ if (!client.responses?.create)
1010
+ throw new Error(
1011
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1012
+ );
1013
+ const response = await client.responses.create(payload, {
1014
+ signal: request.signal
1015
+ });
1016
+ throwIfAborted(request.signal);
720
1017
  logProviderPayload("openai", "response payload", response, request.debug);
1018
+ if (response.status && response.status !== "completed") {
1019
+ throw Object.assign(
1020
+ new Error(
1021
+ response.error?.message ?? `Responses request ${response.status}`
1022
+ ),
1023
+ response.error
1024
+ );
1025
+ }
721
1026
  return this.parseResponsesResult(response);
722
1027
  }
723
1028
  /**
@@ -735,13 +1040,30 @@ var OpenAIAdapter = class _OpenAIAdapter {
735
1040
  */
736
1041
  async *streamWithResponses(request) {
737
1042
  const client = await this.getClient();
1043
+ throwIfAborted(request.signal);
738
1044
  const payload = this.buildResponsesPayload(request, true);
739
1045
  logProviderPayload("openai", "request payload", payload, request.debug);
740
- const stream = await client.responses.create(payload);
1046
+ if (!client.responses?.create)
1047
+ throw new Error(
1048
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
1049
+ );
1050
+ const stream = await client.responses.create(payload, {
1051
+ signal: request.signal
1052
+ });
741
1053
  const toolCalls = /* @__PURE__ */ new Map();
742
1054
  let usage;
743
1055
  const citations = [];
1056
+ let completed = false;
1057
+ let terminalResponse;
1058
+ let responseIdentity = {};
1059
+ const outputItems = /* @__PURE__ */ new Map();
1060
+ let streamedText = "";
744
1061
  function* flushToolCalls() {
1062
+ for (const call of toolCalls.values()) {
1063
+ if (!call.name)
1064
+ throw new Error("Responses tool call is missing its name");
1065
+ JSON.parse(call.arguments || "{}");
1066
+ }
745
1067
  for (const call of toolCalls.values()) {
746
1068
  yield { type: "action:start", id: call.id, name: call.name };
747
1069
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -750,19 +1072,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
750
1072
  toolCalls.clear();
751
1073
  }
752
1074
  for await (const event of stream) {
753
- if (request.signal?.aborted) break;
1075
+ throwIfAborted(request.signal);
754
1076
  logProviderPayload("openai", "stream chunk", event, request.debug);
1077
+ if (completed)
1078
+ throw new Error("Responses stream emitted an event after completion");
755
1079
  const index = event?.output_index ?? 0;
756
1080
  switch (event?.type) {
1081
+ case "response.created":
1082
+ responseIdentity = {
1083
+ id: event.response?.id,
1084
+ model: event.response?.model
1085
+ };
1086
+ break;
1087
+ case "response.output_item.done":
1088
+ if (event.item) outputItems.set(index, event.item);
1089
+ if (event.item?.type === "function_call") {
1090
+ toolCalls.set(index, {
1091
+ id: event.item.call_id,
1092
+ name: event.item.name,
1093
+ arguments: event.item.arguments
1094
+ });
1095
+ }
1096
+ break;
757
1097
  case "response.output_text.delta":
758
1098
  if (event.delta) {
1099
+ streamedText += event.delta;
759
1100
  yield { type: "message:delta", content: event.delta };
760
1101
  }
761
1102
  break;
762
1103
  case "response.output_item.added":
763
1104
  if (event.item?.type === "function_call") {
764
1105
  toolCalls.set(index, {
765
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1106
+ id: event.item.call_id ?? "",
766
1107
  name: event.item.name ?? "",
767
1108
  arguments: event.item.arguments ?? ""
768
1109
  });
@@ -780,8 +1121,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
780
1121
  }
781
1122
  break;
782
1123
  }
783
- case "response.completed":
784
- case "response.incomplete": {
1124
+ case "response.completed": {
1125
+ if (event.response?.status && event.response.status !== "completed")
1126
+ throw new Error(`Responses stream ${event.response.status}`);
1127
+ completed = true;
1128
+ terminalResponse = { ...responseIdentity, ...event.response };
785
1129
  const found = this.collectResponsesCitations(
786
1130
  event.response?.output,
787
1131
  citations.length
@@ -797,21 +1141,88 @@ var OpenAIAdapter = class _OpenAIAdapter {
797
1141
  }
798
1142
  break;
799
1143
  }
800
- case "error":
801
- case "response.failed":
1144
+ case "response.incomplete":
802
1145
  throw new Error(
803
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
1146
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
804
1147
  );
1148
+ case "error":
1149
+ case "response.failed": {
1150
+ const details = event.response?.error ?? event;
1151
+ throw Object.assign(
1152
+ new Error(details.message ?? "Responses stream failed"),
1153
+ {
1154
+ code: details.code,
1155
+ param: details.param,
1156
+ status: details.status
1157
+ }
1158
+ );
1159
+ }
1160
+ }
1161
+ }
1162
+ throwIfAborted(request.signal);
1163
+ if (!completed)
1164
+ throw new Error("Responses stream ended without a completed response");
1165
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1166
+ if (output.some((item) => item.type === "function_call"))
1167
+ toolCalls.clear();
1168
+ for (const [index, item] of output.entries()) {
1169
+ if (item.type === "function_call") {
1170
+ toolCalls.set(index, {
1171
+ id: item.call_id,
1172
+ name: item.name,
1173
+ arguments: item.arguments
1174
+ });
805
1175
  }
806
1176
  }
1177
+ const uniqueCalls = /* @__PURE__ */ new Map();
1178
+ for (const call of toolCalls.values()) {
1179
+ if (!call.id || !call.name)
1180
+ throw new Error("Responses stream contains an incomplete tool call");
1181
+ uniqueCalls.set(call.id, call);
1182
+ }
1183
+ toolCalls.clear();
1184
+ [...uniqueCalls.values()].forEach(
1185
+ (call, index) => toolCalls.set(index, call)
1186
+ );
1187
+ if (!output.length && toolCalls.size) {
1188
+ for (const call of toolCalls.values())
1189
+ output.push({
1190
+ type: "function_call",
1191
+ call_id: call.id,
1192
+ name: call.name,
1193
+ arguments: call.arguments
1194
+ });
1195
+ }
1196
+ if (!output.length && streamedText)
1197
+ output.push({
1198
+ type: "message",
1199
+ role: "assistant",
1200
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1201
+ });
1202
+ terminalResponse.output = output;
1203
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1204
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1205
+ yield {
1206
+ type: "message:delta",
1207
+ content: finalText.slice(streamedText.length)
1208
+ };
1209
+ }
807
1210
  yield* flushToolCalls();
808
1211
  if (citations.length > 0) {
809
1212
  yield { type: "citation", citations: deduplicateCitations(citations) };
810
1213
  }
811
- yield { type: "message:end" };
812
- yield { type: "done", usage };
1214
+ yield {
1215
+ type: "message:end",
1216
+ providerMetadata: responseMetadata(terminalResponse)
1217
+ };
1218
+ yield {
1219
+ type: "done",
1220
+ usage,
1221
+ providerMetadata: responseMetadata(terminalResponse)
1222
+ };
813
1223
  }
814
1224
  async *stream(request) {
1225
+ throwIfAborted(request.signal);
815
1226
  if (this.shouldUseResponsesApi(request)) {
816
1227
  const messageId2 = generateMessageId();
817
1228
  yield { type: "message:start", id: messageId2 };
@@ -819,47 +1230,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
819
1230
  yield* this.streamWithResponses(request);
820
1231
  return;
821
1232
  } catch (error) {
822
- yield {
823
- type: "error",
824
- message: error instanceof Error ? error.message : "Unknown error",
825
- code: "OPENAI_RESPONSES_ERROR"
826
- };
1233
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
827
1234
  return;
828
1235
  }
829
1236
  }
830
1237
  const client = await this.getClient();
831
1238
  let messages;
832
1239
  if (request.rawMessages && request.rawMessages.length > 0) {
833
- const processedMessages = request.rawMessages.map((msg) => {
834
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
835
- return { ...msg, content: null };
836
- }
837
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
838
- if (hasAttachments) {
839
- const content = [];
840
- if (msg.content) {
841
- content.push({ type: "text", text: msg.content });
842
- }
843
- for (const attachment of msg.attachments) {
844
- if (attachment.type === "image") {
845
- let imageUrl;
846
- if (attachment.url) {
847
- imageUrl = attachment.url;
848
- } else if (attachment.data) {
849
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
850
- } else {
851
- continue;
852
- }
853
- content.push({
854
- type: "image_url",
855
- image_url: { url: imageUrl, detail: "auto" }
856
- });
857
- }
858
- }
859
- return { ...msg, content, attachments: void 0 };
860
- }
861
- return msg;
862
- });
1240
+ const processedMessages = this.formatRawMessages(request.rawMessages);
863
1241
  if (request.systemPrompt) {
864
1242
  const hasSystem = processedMessages.some((m) => m.role === "system");
865
1243
  if (!hasSystem) {
@@ -879,7 +1257,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
879
1257
  request.systemPrompt
880
1258
  );
881
1259
  }
882
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1260
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1261
+ type: "function",
1262
+ function: {
1263
+ name: tool.name,
1264
+ description: tool.description,
1265
+ parameters: normalizeObjectJsonSchema(
1266
+ tool.inputSchema
1267
+ )
1268
+ }
1269
+ })) : formatTools(request.actions ?? []);
883
1270
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
884
1271
  if (webSearchConfig) {
885
1272
  const webSearchTool = {
@@ -908,26 +1295,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
908
1295
  tools: tools.length > 0 ? tools : void 0,
909
1296
  tool_choice: tools.length > 0 ? toolChoice : void 0,
910
1297
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
911
- ...buildOpenAITokenParams(
1298
+ ...buildOpenAIParameters(
912
1299
  modelIdForPayload,
913
1300
  request.config?.maxTokens ?? this.config.maxTokens,
914
- request.config?.temperature ?? this.config.temperature
1301
+ request.config?.temperature ?? this.config.temperature,
1302
+ this.parameterOptions(request)
915
1303
  ),
916
1304
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
917
1305
  stream: true,
918
1306
  stream_options: { include_usage: true }
919
1307
  };
920
1308
  logProviderPayload("openai", "request payload", payload, request.debug);
921
- const stream = await client.chat.completions.create(payload);
922
- let currentToolCall = null;
1309
+ const stream = await client.chat.completions.create(payload, {
1310
+ signal: request.signal
1311
+ });
1312
+ const toolCalls = /* @__PURE__ */ new Map();
1313
+ let finishReason;
923
1314
  const collectedCitations = [];
924
1315
  let citationIndex = 0;
925
1316
  let usage;
926
1317
  for await (const chunk of stream) {
927
1318
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
928
- if (request.signal?.aborted) {
929
- break;
930
- }
1319
+ throwIfAborted(request.signal);
931
1320
  const delta = chunk.choices[0]?.delta;
932
1321
  const choice = chunk.choices[0];
933
1322
  if (delta?.content) {
@@ -950,38 +1339,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
950
1339
  }
951
1340
  }
952
1341
  }
953
- if (delta?.tool_calls) {
954
- for (const toolCall of delta.tool_calls) {
955
- if (toolCall.id) {
956
- if (currentToolCall) {
957
- yield {
958
- type: "action:args",
959
- id: currentToolCall.id,
960
- args: currentToolCall.arguments
961
- };
962
- yield {
963
- type: "action:end",
964
- id: currentToolCall.id,
965
- name: currentToolCall.name
966
- };
967
- }
968
- const tcExtraContent = toolCall.extra_content;
969
- currentToolCall = {
970
- id: toolCall.id,
971
- name: toolCall.function?.name || "",
972
- arguments: toolCall.function?.arguments || "",
973
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
974
- };
975
- yield {
976
- type: "action:start",
977
- id: currentToolCall.id,
978
- name: currentToolCall.name,
979
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
980
- };
981
- } else if (currentToolCall && toolCall.function?.arguments) {
982
- currentToolCall.arguments += toolCall.function.arguments;
983
- }
984
- }
1342
+ for (const part of delta?.tool_calls ?? []) {
1343
+ const index = part.index ?? 0;
1344
+ const call = toolCalls.get(index) ?? {
1345
+ id: "",
1346
+ name: "",
1347
+ arguments: ""
1348
+ };
1349
+ if (part.id) call.id = part.id;
1350
+ if (part.function?.name) call.name += part.function.name;
1351
+ if (part.function?.arguments)
1352
+ call.arguments += part.function.arguments;
1353
+ if (part.extra_content) call.extra_content = part.extra_content;
1354
+ toolCalls.set(index, call);
985
1355
  }
986
1356
  if (chunk.usage) {
987
1357
  usage = {
@@ -990,21 +1360,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
990
1360
  total_tokens: chunk.usage.total_tokens
991
1361
  };
992
1362
  }
993
- if (choice?.finish_reason) {
994
- if (currentToolCall) {
995
- yield {
996
- type: "action:args",
997
- id: currentToolCall.id,
998
- args: currentToolCall.arguments
999
- };
1000
- yield {
1001
- type: "action:end",
1002
- id: currentToolCall.id,
1003
- name: currentToolCall.name
1004
- };
1005
- currentToolCall = null;
1006
- }
1007
- }
1363
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1364
+ }
1365
+ throwIfAborted(request.signal);
1366
+ if (!finishReason)
1367
+ throw new Error("OpenAI stream ended without a finish reason");
1368
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1369
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1370
+ }
1371
+ for (const call of toolCalls.values()) {
1372
+ if (!call.id || !call.name)
1373
+ throw new Error("OpenAI stream contains an incomplete tool call");
1374
+ JSON.parse(call.arguments || "{}");
1375
+ }
1376
+ for (const call of toolCalls.values()) {
1377
+ yield {
1378
+ type: "action:start",
1379
+ id: call.id,
1380
+ name: call.name,
1381
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1382
+ };
1383
+ yield { type: "action:args", id: call.id, args: call.arguments };
1384
+ yield { type: "action:end", id: call.id, name: call.name };
1008
1385
  }
1009
1386
  if (collectedCitations.length > 0) {
1010
1387
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1013,26 +1390,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1013
1390
  yield { type: "message:end" };
1014
1391
  yield { type: "done", usage };
1015
1392
  } catch (error) {
1016
- yield {
1017
- type: "error",
1018
- message: error instanceof Error ? error.message : "Unknown error",
1019
- code: `${this.provider.toUpperCase()}_ERROR`
1020
- };
1393
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1021
1394
  }
1022
1395
  }
1023
1396
  async complete(request) {
1397
+ throwIfAborted(request.signal);
1024
1398
  if (this.shouldUseResponsesApi(request)) {
1025
1399
  return this.completeWithResponses(request);
1026
1400
  }
1027
1401
  const client = await this.getClient();
1028
1402
  let messages;
1029
1403
  if (request.rawMessages && request.rawMessages.length > 0) {
1030
- const sanitized = request.rawMessages.map((msg) => {
1031
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1032
- return { ...msg, content: null };
1033
- }
1034
- return msg;
1035
- });
1404
+ const sanitized = this.formatRawMessages(request.rawMessages);
1036
1405
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1037
1406
  messages = [
1038
1407
  { role: "system", content: request.systemPrompt },
@@ -1047,7 +1416,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1047
1416
  request.systemPrompt
1048
1417
  );
1049
1418
  }
1050
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1419
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1420
+ type: "function",
1421
+ function: {
1422
+ name: tool.name,
1423
+ description: tool.description,
1424
+ parameters: normalizeObjectJsonSchema(
1425
+ tool.inputSchema
1426
+ )
1427
+ }
1428
+ })) : formatTools(request.actions ?? []);
1051
1429
  const openaiToolOptions = request.providerToolOptions?.openai;
1052
1430
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1053
1431
  type: "function",
@@ -1062,16 +1440,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1062
1440
  tools: tools.length > 0 ? tools : void 0,
1063
1441
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1064
1442
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1065
- ...buildOpenAITokenParams(
1443
+ ...buildOpenAIParameters(
1066
1444
  modelIdForCompletePayload,
1067
1445
  request.config?.maxTokens ?? this.config.maxTokens,
1068
- request.config?.temperature ?? this.config.temperature
1446
+ request.config?.temperature ?? this.config.temperature,
1447
+ this.parameterOptions(request)
1069
1448
  ),
1070
1449
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1071
1450
  stream: false
1072
1451
  };
1073
1452
  logProviderPayload("openai", "request payload", payload, request.debug);
1074
- const response = await client.chat.completions.create(payload);
1453
+ const response = await client.chat.completions.create(payload, {
1454
+ signal: request.signal
1455
+ });
1075
1456
  logProviderPayload("openai", "response payload", response, request.debug);
1076
1457
  const choice = response.choices?.[0];
1077
1458
  const message = choice?.message;