@yourgpt/llm-sdk 2.6.0-beta.0 → 2.6.0-beta.1

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 (55) hide show
  1. package/README.md +77 -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 +255 -164
  5. package/dist/adapters/index.mjs +255 -164
  6. package/dist/{base-C58Dsr9p.d.ts → base-BdTpUAWI.d.ts} +2 -1
  7. package/dist/{base-tNgbBaSo.d.mts → base-CkeT7u-O.d.mts} +2 -1
  8. package/dist/fallback/index.d.mts +4 -4
  9. package/dist/fallback/index.d.ts +4 -4
  10. package/dist/fallback/index.js +75 -39
  11. package/dist/fallback/index.mjs +75 -39
  12. package/dist/index.d.mts +8 -7
  13. package/dist/index.d.ts +8 -7
  14. package/dist/index.js +75 -28
  15. package/dist/index.mjs +75 -28
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/azure/index.d.mts +3 -3
  19. package/dist/providers/azure/index.d.ts +3 -3
  20. package/dist/providers/fireworks/index.d.mts +3 -3
  21. package/dist/providers/fireworks/index.d.ts +3 -3
  22. package/dist/providers/fireworks/index.js +255 -164
  23. package/dist/providers/fireworks/index.mjs +255 -164
  24. package/dist/providers/google/index.d.mts +3 -3
  25. package/dist/providers/google/index.d.ts +3 -3
  26. package/dist/providers/google/index.js +255 -164
  27. package/dist/providers/google/index.mjs +255 -164
  28. package/dist/providers/ollama/index.d.mts +4 -4
  29. package/dist/providers/ollama/index.d.ts +4 -4
  30. package/dist/providers/openai/index.d.mts +6 -23
  31. package/dist/providers/openai/index.d.ts +6 -23
  32. package/dist/providers/openai/index.js +656 -450
  33. package/dist/providers/openai/index.mjs +656 -450
  34. package/dist/providers/openrouter/index.d.mts +3 -3
  35. package/dist/providers/openrouter/index.d.ts +3 -3
  36. package/dist/providers/openrouter/index.js +255 -164
  37. package/dist/providers/openrouter/index.mjs +255 -164
  38. package/dist/providers/togetherai/index.d.mts +3 -3
  39. package/dist/providers/togetherai/index.d.ts +3 -3
  40. package/dist/providers/togetherai/index.js +255 -164
  41. package/dist/providers/togetherai/index.mjs +255 -164
  42. package/dist/providers/xai/index.d.mts +3 -3
  43. package/dist/providers/xai/index.d.ts +3 -3
  44. package/dist/providers/xai/index.js +255 -164
  45. package/dist/providers/xai/index.mjs +255 -164
  46. package/dist/{types-BkQCSiIt.d.mts → types-BW3csYo5.d.mts} +22 -1
  47. package/dist/{types-BkQCSiIt.d.ts → types-BW3csYo5.d.ts} +22 -1
  48. package/dist/{types-B6dhnguR.d.mts → types-BWRYIvl_.d.mts} +11 -4
  49. package/dist/{types-CCxPmkmK.d.ts → types-CpXcT-tY.d.ts} +11 -4
  50. package/dist/{types-BQ31QIsA.d.ts → types-DA54bSmX.d.ts} +3 -11
  51. package/dist/{types-BSSiJW2o.d.mts → types-NOswlhm9.d.mts} +3 -11
  52. package/dist/yourgpt/index.d.mts +1 -1
  53. package/dist/yourgpt/index.d.ts +1 -1
  54. package/package.json +115 -48
  55. package/LICENSE +0 -21
@@ -1,5 +1,56 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/openai-parameters.ts
4
+ function capabilities(model) {
5
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
6
+ const major = Number(gpt?.[1] ?? 0);
7
+ const minor = Number(gpt?.[2] ?? 0);
8
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
9
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
10
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
11
+ return { major, minor, reasoning, supportsNone };
12
+ }
13
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
14
+ if (options.parameterPolicy === "passthrough") return false;
15
+ const c = capabilities(model);
16
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
17
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
18
+ if (!baseUrl) return true;
19
+ try {
20
+ return new URL(baseUrl).hostname === "api.openai.com";
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
26
+ const c = capabilities(model ?? "");
27
+ const normalize = options.parameterPolicy !== "passthrough";
28
+ const effort = options.reasoningEffort;
29
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
30
+ throw new Error(`${model} does not support reasoningEffort: none`);
31
+ }
32
+ if (normalize && c.major >= 6 && effort === "minimal") {
33
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
34
+ }
35
+ const result = {
36
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
37
+ };
38
+ if (effort !== void 0) {
39
+ if (api === "responses") result.reasoning = { effort };
40
+ else result.reasoning_effort = effort;
41
+ }
42
+ if (temperature !== void 0) {
43
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
44
+ result.temperature = temperature;
45
+ } else {
46
+ (options.onWarning ?? console.warn)(
47
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
48
+ );
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+
3
54
  // src/adapters/base.ts
4
55
  function stringifyForDebug(value) {
5
56
  return JSON.stringify(
@@ -106,16 +157,6 @@ function normalizeObjectJsonSchema(schema) {
106
157
  }
107
158
  return normalized;
108
159
  }
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
160
  function toOpenAIResponseFormat(rf) {
120
161
  if (!rf) return void 0;
121
162
  if (rf.type === "json_object") return { type: "json_object" };
@@ -522,6 +563,28 @@ function formatMessagesForGoogle(messages) {
522
563
  });
523
564
  }
524
565
 
566
+ // src/core/provider-error.ts
567
+ function providerErrorEvent(error, code) {
568
+ const details = error ?? {};
569
+ const event = {
570
+ type: "error",
571
+ message: error instanceof Error ? error.message : String(error),
572
+ code,
573
+ ...typeof details.status === "number" ? { status: details.status } : {},
574
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
575
+ ...typeof details.param === "string" ? { param: details.param } : {},
576
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
577
+ };
578
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
579
+ return event;
580
+ }
581
+ function throwIfAborted(signal) {
582
+ if (!signal?.aborted) return;
583
+ const error = new Error("The operation was aborted");
584
+ error.name = "AbortError";
585
+ throw error;
586
+ }
587
+
525
588
  // src/core/utils.ts
526
589
  function generateId(prefix = "id") {
527
590
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -553,39 +616,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
553
616
  const { default: OpenAI } = await import('openai');
554
617
  this.client = new OpenAI({
555
618
  apiKey: this.config.apiKey,
556
- baseURL: this.config.baseUrl
619
+ baseURL: this.config.baseUrl,
620
+ organization: this.config.organization,
621
+ defaultHeaders: this.config.headers
557
622
  });
558
623
  }
559
624
  return this.client;
560
625
  }
626
+ parameterOptions(request) {
627
+ return {
628
+ ...this.config,
629
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
630
+ };
631
+ }
632
+ toolDefinitions(request) {
633
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
634
+ return formatTools(request.actions ?? []).map((tool) => ({
635
+ name: tool.function.name,
636
+ description: tool.function.description ?? "",
637
+ location: "server",
638
+ inputSchema: tool.function.parameters
639
+ }));
640
+ }
561
641
  shouldUseResponsesApi(request) {
562
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
563
- if (!hasTools) return false;
564
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
642
+ if (this.toolDefinitions(request).length === 0) return false;
643
+ if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
565
644
  return true;
566
- }
567
- return this.requiresResponsesApiForTools(
568
- request.config?.model || this.model
645
+ return requiresOpenAIResponses(
646
+ request.config?.model || this.model,
647
+ this.config.baseUrl,
648
+ this.parameterOptions(request)
569
649
  );
570
650
  }
571
- /**
572
- * Whether this model rejects function tools on /v1/chat/completions.
573
- *
574
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
575
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
576
- * restriction and may not implement /v1/responses at all.
577
- */
578
- requiresResponsesApiForTools(modelId) {
579
- if (!modelId) return false;
580
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
581
- const baseUrl = this.config.baseUrl;
582
- if (!baseUrl) return true;
583
- try {
584
- return new URL(baseUrl).hostname === "api.openai.com";
585
- } catch {
586
- return false;
587
- }
588
- }
589
651
  /**
590
652
  * Convert one message's content to Responses content parts.
591
653
  *
@@ -616,7 +678,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
616
678
  });
617
679
  }
618
680
  buildResponsesInput(request) {
619
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
681
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
620
682
  const input = [];
621
683
  for (const message of sourceMessages) {
622
684
  if (message.role === "system") {
@@ -666,6 +728,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
666
728
  }
667
729
  return input;
668
730
  }
731
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
732
+ formatRawMessages(messages) {
733
+ return messages.map((message) => {
734
+ const { attachments, ...rest } = message;
735
+ let content = message.content;
736
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
737
+ content = null;
738
+ }
739
+ if (Array.isArray(attachments) && attachments.length > 0) {
740
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
741
+ (attachment) => attachmentToOpenAIImage(attachment)
742
+ ).filter((image) => image !== null);
743
+ if (images.length > 0) {
744
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
745
+ content = [...parts, ...images];
746
+ }
747
+ }
748
+ return { ...rest, content };
749
+ });
750
+ }
669
751
  /**
670
752
  * Build the web_search tool for the Responses API, or undefined when web
671
753
  * search is off.
@@ -747,13 +829,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
747
829
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
748
830
  id: item.call_id ?? item.id ?? generateToolCallId(),
749
831
  name: item.name,
750
- args: (() => {
751
- try {
752
- return JSON.parse(item.arguments ?? "{}");
753
- } catch {
754
- return {};
755
- }
756
- })()
832
+ args: JSON.parse(item.arguments ?? "{}")
757
833
  })) : [];
758
834
  return {
759
835
  content,
@@ -777,18 +853,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
777
853
  instructions: request.systemPrompt,
778
854
  input: this.buildResponsesInput(request),
779
855
  tools: this.buildResponsesTools(
780
- request.toolDefinitions ?? [],
856
+ this.toolDefinitions(request),
781
857
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
782
858
  this.buildResponsesWebSearchTool(request)
783
859
  ),
784
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
860
+ tool_choice: openaiToolOptions?.toolChoice,
785
861
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
786
- // Reasoning models reject `temperature` outright — sending it would swap one
787
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
788
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
789
- temperature: request.config?.temperature ?? this.config.temperature
790
- },
791
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
862
+ ...buildOpenAIParameters(
863
+ modelIdForPayload,
864
+ request.config?.maxTokens ?? this.config.maxTokens,
865
+ request.config?.temperature ?? this.config.temperature,
866
+ this.parameterOptions(request),
867
+ "responses"
868
+ ),
792
869
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
793
870
  stream
794
871
  };
@@ -796,10 +873,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
796
873
  }
797
874
  async completeWithResponses(request) {
798
875
  const client = await this.getClient();
876
+ throwIfAborted(request.signal);
799
877
  const payload = this.buildResponsesPayload(request, false);
800
878
  logProviderPayload("openai", "request payload", payload, request.debug);
801
- const response = await client.responses.create(payload);
879
+ if (!client.responses?.create)
880
+ throw new Error(
881
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
882
+ );
883
+ const response = await client.responses.create(payload, {
884
+ signal: request.signal
885
+ });
886
+ throwIfAborted(request.signal);
802
887
  logProviderPayload("openai", "response payload", response, request.debug);
888
+ if (response.status && response.status !== "completed") {
889
+ throw Object.assign(
890
+ new Error(
891
+ response.error?.message ?? `Responses request ${response.status}`
892
+ ),
893
+ response.error
894
+ );
895
+ }
803
896
  return this.parseResponsesResult(response);
804
897
  }
805
898
  /**
@@ -817,13 +910,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
817
910
  */
818
911
  async *streamWithResponses(request) {
819
912
  const client = await this.getClient();
913
+ throwIfAborted(request.signal);
820
914
  const payload = this.buildResponsesPayload(request, true);
821
915
  logProviderPayload("openai", "request payload", payload, request.debug);
822
- const stream = await client.responses.create(payload);
916
+ if (!client.responses?.create)
917
+ throw new Error(
918
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
919
+ );
920
+ const stream = await client.responses.create(payload, {
921
+ signal: request.signal
922
+ });
823
923
  const toolCalls = /* @__PURE__ */ new Map();
824
924
  let usage;
825
925
  const citations = [];
926
+ let completed = false;
826
927
  function* flushToolCalls() {
928
+ for (const call of toolCalls.values()) {
929
+ if (!call.name)
930
+ throw new Error("Responses tool call is missing its name");
931
+ JSON.parse(call.arguments || "{}");
932
+ }
827
933
  for (const call of toolCalls.values()) {
828
934
  yield { type: "action:start", id: call.id, name: call.name };
829
935
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -832,7 +938,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
832
938
  toolCalls.clear();
833
939
  }
834
940
  for await (const event of stream) {
835
- if (request.signal?.aborted) break;
941
+ throwIfAborted(request.signal);
836
942
  logProviderPayload("openai", "stream chunk", event, request.debug);
837
943
  const index = event?.output_index ?? 0;
838
944
  switch (event?.type) {
@@ -862,8 +968,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
862
968
  }
863
969
  break;
864
970
  }
865
- case "response.completed":
866
- case "response.incomplete": {
971
+ case "response.completed": {
972
+ completed = true;
867
973
  const found = this.collectResponsesCitations(
868
974
  event.response?.output,
869
975
  citations.length
@@ -879,13 +985,27 @@ var OpenAIAdapter = class _OpenAIAdapter {
879
985
  }
880
986
  break;
881
987
  }
882
- case "error":
883
- case "response.failed":
988
+ case "response.incomplete":
884
989
  throw new Error(
885
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
990
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
886
991
  );
992
+ case "error":
993
+ case "response.failed": {
994
+ const details = event.response?.error ?? event;
995
+ throw Object.assign(
996
+ new Error(details.message ?? "Responses stream failed"),
997
+ {
998
+ code: details.code,
999
+ param: details.param,
1000
+ status: details.status
1001
+ }
1002
+ );
1003
+ }
887
1004
  }
888
1005
  }
1006
+ throwIfAborted(request.signal);
1007
+ if (!completed)
1008
+ throw new Error("Responses stream ended without a completed response");
889
1009
  yield* flushToolCalls();
890
1010
  if (citations.length > 0) {
891
1011
  yield { type: "citation", citations: deduplicateCitations(citations) };
@@ -894,6 +1014,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
894
1014
  yield { type: "done", usage };
895
1015
  }
896
1016
  async *stream(request) {
1017
+ throwIfAborted(request.signal);
897
1018
  if (this.shouldUseResponsesApi(request)) {
898
1019
  const messageId2 = generateMessageId();
899
1020
  yield { type: "message:start", id: messageId2 };
@@ -901,47 +1022,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
901
1022
  yield* this.streamWithResponses(request);
902
1023
  return;
903
1024
  } catch (error) {
904
- yield {
905
- type: "error",
906
- message: error instanceof Error ? error.message : "Unknown error",
907
- code: "OPENAI_RESPONSES_ERROR"
908
- };
1025
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
909
1026
  return;
910
1027
  }
911
1028
  }
912
1029
  const client = await this.getClient();
913
1030
  let messages;
914
1031
  if (request.rawMessages && request.rawMessages.length > 0) {
915
- const processedMessages = request.rawMessages.map((msg) => {
916
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
917
- return { ...msg, content: null };
918
- }
919
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
920
- if (hasAttachments) {
921
- const content = [];
922
- if (msg.content) {
923
- content.push({ type: "text", text: msg.content });
924
- }
925
- for (const attachment of msg.attachments) {
926
- if (attachment.type === "image") {
927
- let imageUrl;
928
- if (attachment.url) {
929
- imageUrl = attachment.url;
930
- } else if (attachment.data) {
931
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
932
- } else {
933
- continue;
934
- }
935
- content.push({
936
- type: "image_url",
937
- image_url: { url: imageUrl, detail: "auto" }
938
- });
939
- }
940
- }
941
- return { ...msg, content, attachments: void 0 };
942
- }
943
- return msg;
944
- });
1032
+ const processedMessages = this.formatRawMessages(request.rawMessages);
945
1033
  if (request.systemPrompt) {
946
1034
  const hasSystem = processedMessages.some((m) => m.role === "system");
947
1035
  if (!hasSystem) {
@@ -961,7 +1049,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
961
1049
  request.systemPrompt
962
1050
  );
963
1051
  }
964
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1052
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1053
+ type: "function",
1054
+ function: {
1055
+ name: tool.name,
1056
+ description: tool.description,
1057
+ parameters: normalizeObjectJsonSchema(
1058
+ tool.inputSchema
1059
+ )
1060
+ }
1061
+ })) : formatTools(request.actions ?? []);
965
1062
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
966
1063
  if (webSearchConfig) {
967
1064
  const webSearchTool = {
@@ -990,26 +1087,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
990
1087
  tools: tools.length > 0 ? tools : void 0,
991
1088
  tool_choice: tools.length > 0 ? toolChoice : void 0,
992
1089
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
993
- ...buildOpenAITokenParams(
1090
+ ...buildOpenAIParameters(
994
1091
  modelIdForPayload,
995
1092
  request.config?.maxTokens ?? this.config.maxTokens,
996
- request.config?.temperature ?? this.config.temperature
1093
+ request.config?.temperature ?? this.config.temperature,
1094
+ this.parameterOptions(request)
997
1095
  ),
998
1096
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
999
1097
  stream: true,
1000
1098
  stream_options: { include_usage: true }
1001
1099
  };
1002
1100
  logProviderPayload("openai", "request payload", payload, request.debug);
1003
- const stream = await client.chat.completions.create(payload);
1004
- let currentToolCall = null;
1101
+ const stream = await client.chat.completions.create(payload, {
1102
+ signal: request.signal
1103
+ });
1104
+ const toolCalls = /* @__PURE__ */ new Map();
1105
+ let finishReason;
1005
1106
  const collectedCitations = [];
1006
1107
  let citationIndex = 0;
1007
1108
  let usage;
1008
1109
  for await (const chunk of stream) {
1009
1110
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
1010
- if (request.signal?.aborted) {
1011
- break;
1012
- }
1111
+ throwIfAborted(request.signal);
1013
1112
  const delta = chunk.choices[0]?.delta;
1014
1113
  const choice = chunk.choices[0];
1015
1114
  if (delta?.content) {
@@ -1032,38 +1131,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1032
1131
  }
1033
1132
  }
1034
1133
  }
1035
- if (delta?.tool_calls) {
1036
- for (const toolCall of delta.tool_calls) {
1037
- if (toolCall.id) {
1038
- if (currentToolCall) {
1039
- yield {
1040
- type: "action:args",
1041
- id: currentToolCall.id,
1042
- args: currentToolCall.arguments
1043
- };
1044
- yield {
1045
- type: "action:end",
1046
- id: currentToolCall.id,
1047
- name: currentToolCall.name
1048
- };
1049
- }
1050
- const tcExtraContent = toolCall.extra_content;
1051
- currentToolCall = {
1052
- id: toolCall.id,
1053
- name: toolCall.function?.name || "",
1054
- arguments: toolCall.function?.arguments || "",
1055
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1056
- };
1057
- yield {
1058
- type: "action:start",
1059
- id: currentToolCall.id,
1060
- name: currentToolCall.name,
1061
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1062
- };
1063
- } else if (currentToolCall && toolCall.function?.arguments) {
1064
- currentToolCall.arguments += toolCall.function.arguments;
1065
- }
1066
- }
1134
+ for (const part of delta?.tool_calls ?? []) {
1135
+ const index = part.index ?? 0;
1136
+ const call = toolCalls.get(index) ?? {
1137
+ id: "",
1138
+ name: "",
1139
+ arguments: ""
1140
+ };
1141
+ if (part.id) call.id = part.id;
1142
+ if (part.function?.name) call.name += part.function.name;
1143
+ if (part.function?.arguments)
1144
+ call.arguments += part.function.arguments;
1145
+ if (part.extra_content) call.extra_content = part.extra_content;
1146
+ toolCalls.set(index, call);
1067
1147
  }
1068
1148
  if (chunk.usage) {
1069
1149
  usage = {
@@ -1072,21 +1152,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1072
1152
  total_tokens: chunk.usage.total_tokens
1073
1153
  };
1074
1154
  }
1075
- if (choice?.finish_reason) {
1076
- if (currentToolCall) {
1077
- yield {
1078
- type: "action:args",
1079
- id: currentToolCall.id,
1080
- args: currentToolCall.arguments
1081
- };
1082
- yield {
1083
- type: "action:end",
1084
- id: currentToolCall.id,
1085
- name: currentToolCall.name
1086
- };
1087
- currentToolCall = null;
1088
- }
1089
- }
1155
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1156
+ }
1157
+ throwIfAborted(request.signal);
1158
+ if (!finishReason)
1159
+ throw new Error("OpenAI stream ended without a finish reason");
1160
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1161
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1162
+ }
1163
+ for (const call of toolCalls.values()) {
1164
+ if (!call.id || !call.name)
1165
+ throw new Error("OpenAI stream contains an incomplete tool call");
1166
+ JSON.parse(call.arguments || "{}");
1167
+ }
1168
+ for (const call of toolCalls.values()) {
1169
+ yield {
1170
+ type: "action:start",
1171
+ id: call.id,
1172
+ name: call.name,
1173
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1174
+ };
1175
+ yield { type: "action:args", id: call.id, args: call.arguments };
1176
+ yield { type: "action:end", id: call.id, name: call.name };
1090
1177
  }
1091
1178
  if (collectedCitations.length > 0) {
1092
1179
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1095,26 +1182,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1095
1182
  yield { type: "message:end" };
1096
1183
  yield { type: "done", usage };
1097
1184
  } catch (error) {
1098
- yield {
1099
- type: "error",
1100
- message: error instanceof Error ? error.message : "Unknown error",
1101
- code: `${this.provider.toUpperCase()}_ERROR`
1102
- };
1185
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1103
1186
  }
1104
1187
  }
1105
1188
  async complete(request) {
1189
+ throwIfAborted(request.signal);
1106
1190
  if (this.shouldUseResponsesApi(request)) {
1107
1191
  return this.completeWithResponses(request);
1108
1192
  }
1109
1193
  const client = await this.getClient();
1110
1194
  let messages;
1111
1195
  if (request.rawMessages && request.rawMessages.length > 0) {
1112
- const sanitized = request.rawMessages.map((msg) => {
1113
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1114
- return { ...msg, content: null };
1115
- }
1116
- return msg;
1117
- });
1196
+ const sanitized = this.formatRawMessages(request.rawMessages);
1118
1197
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1119
1198
  messages = [
1120
1199
  { role: "system", content: request.systemPrompt },
@@ -1129,7 +1208,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1129
1208
  request.systemPrompt
1130
1209
  );
1131
1210
  }
1132
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1211
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1212
+ type: "function",
1213
+ function: {
1214
+ name: tool.name,
1215
+ description: tool.description,
1216
+ parameters: normalizeObjectJsonSchema(
1217
+ tool.inputSchema
1218
+ )
1219
+ }
1220
+ })) : formatTools(request.actions ?? []);
1133
1221
  const openaiToolOptions = request.providerToolOptions?.openai;
1134
1222
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1135
1223
  type: "function",
@@ -1144,16 +1232,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1144
1232
  tools: tools.length > 0 ? tools : void 0,
1145
1233
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1146
1234
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1147
- ...buildOpenAITokenParams(
1235
+ ...buildOpenAIParameters(
1148
1236
  modelIdForCompletePayload,
1149
1237
  request.config?.maxTokens ?? this.config.maxTokens,
1150
- request.config?.temperature ?? this.config.temperature
1238
+ request.config?.temperature ?? this.config.temperature,
1239
+ this.parameterOptions(request)
1151
1240
  ),
1152
1241
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1153
1242
  stream: false
1154
1243
  };
1155
1244
  logProviderPayload("openai", "request payload", payload, request.debug);
1156
- const response = await client.chat.completions.create(payload);
1245
+ const response = await client.chat.completions.create(payload, {
1246
+ signal: request.signal
1247
+ });
1157
1248
  logProviderPayload("openai", "response payload", response, request.debug);
1158
1249
  const choice = response.choices?.[0];
1159
1250
  const message = choice?.message;