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