@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,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BkQCSiIt.mjs';
2
- import { A as AIProvider } from '../../types-BSSiJW2o.mjs';
1
+ import { L as LanguageModel } from '../../types-BW3csYo5.mjs';
2
+ import { A as AIProvider } from '../../types-NOswlhm9.mjs';
3
3
  import 'zod';
4
- import '../../base-tNgbBaSo.mjs';
4
+ import '../../base-CkeT7u-O.mjs';
5
5
 
6
6
  /**
7
7
  * OpenRouter Provider - Modern Pattern
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BkQCSiIt.js';
2
- import { A as AIProvider } from '../../types-BQ31QIsA.js';
1
+ import { L as LanguageModel } from '../../types-BW3csYo5.js';
2
+ import { A as AIProvider } from '../../types-DA54bSmX.js';
3
3
  import 'zod';
4
- import '../../base-C58Dsr9p.js';
4
+ import '../../base-BdTpUAWI.js';
5
5
 
6
6
  /**
7
7
  * OpenRouter Provider - Modern Pattern
@@ -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" };
@@ -495,6 +536,28 @@ async function searchOpenRouterModels(query, apiKey) {
495
536
  );
496
537
  }
497
538
 
539
+ // src/core/provider-error.ts
540
+ function providerErrorEvent(error, code) {
541
+ const details = error ?? {};
542
+ const event = {
543
+ type: "error",
544
+ message: error instanceof Error ? error.message : String(error),
545
+ code,
546
+ ...typeof details.status === "number" ? { status: details.status } : {},
547
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
548
+ ...typeof details.param === "string" ? { param: details.param } : {},
549
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
550
+ };
551
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
552
+ return event;
553
+ }
554
+ function throwIfAborted(signal) {
555
+ if (!signal?.aborted) return;
556
+ const error = new Error("The operation was aborted");
557
+ error.name = "AbortError";
558
+ throw error;
559
+ }
560
+
498
561
  // src/core/utils.ts
499
562
  function generateId(prefix = "id") {
500
563
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -526,39 +589,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
526
589
  const { default: OpenAI } = await import('openai');
527
590
  this.client = new OpenAI({
528
591
  apiKey: this.config.apiKey,
529
- baseURL: this.config.baseUrl
592
+ baseURL: this.config.baseUrl,
593
+ organization: this.config.organization,
594
+ defaultHeaders: this.config.headers
530
595
  });
531
596
  }
532
597
  return this.client;
533
598
  }
599
+ parameterOptions(request) {
600
+ return {
601
+ ...this.config,
602
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
603
+ };
604
+ }
605
+ toolDefinitions(request) {
606
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
607
+ return formatTools(request.actions ?? []).map((tool) => ({
608
+ name: tool.function.name,
609
+ description: tool.function.description ?? "",
610
+ location: "server",
611
+ inputSchema: tool.function.parameters
612
+ }));
613
+ }
534
614
  shouldUseResponsesApi(request) {
535
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
536
- if (!hasTools) return false;
537
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
615
+ if (this.toolDefinitions(request).length === 0) return false;
616
+ if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
538
617
  return true;
539
- }
540
- return this.requiresResponsesApiForTools(
541
- request.config?.model || this.model
618
+ return requiresOpenAIResponses(
619
+ request.config?.model || this.model,
620
+ this.config.baseUrl,
621
+ this.parameterOptions(request)
542
622
  );
543
623
  }
544
- /**
545
- * Whether this model rejects function tools on /v1/chat/completions.
546
- *
547
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
548
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
549
- * restriction and may not implement /v1/responses at all.
550
- */
551
- requiresResponsesApiForTools(modelId) {
552
- if (!modelId) return false;
553
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
554
- const baseUrl = this.config.baseUrl;
555
- if (!baseUrl) return true;
556
- try {
557
- return new URL(baseUrl).hostname === "api.openai.com";
558
- } catch {
559
- return false;
560
- }
561
- }
562
624
  /**
563
625
  * Convert one message's content to Responses content parts.
564
626
  *
@@ -589,7 +651,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
589
651
  });
590
652
  }
591
653
  buildResponsesInput(request) {
592
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
654
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
593
655
  const input = [];
594
656
  for (const message of sourceMessages) {
595
657
  if (message.role === "system") {
@@ -639,6 +701,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
639
701
  }
640
702
  return input;
641
703
  }
704
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
705
+ formatRawMessages(messages) {
706
+ return messages.map((message) => {
707
+ const { attachments, ...rest } = message;
708
+ let content = message.content;
709
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
710
+ content = null;
711
+ }
712
+ if (Array.isArray(attachments) && attachments.length > 0) {
713
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
714
+ (attachment) => attachmentToOpenAIImage(attachment)
715
+ ).filter((image) => image !== null);
716
+ if (images.length > 0) {
717
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
718
+ content = [...parts, ...images];
719
+ }
720
+ }
721
+ return { ...rest, content };
722
+ });
723
+ }
642
724
  /**
643
725
  * Build the web_search tool for the Responses API, or undefined when web
644
726
  * search is off.
@@ -720,13 +802,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
720
802
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
721
803
  id: item.call_id ?? item.id ?? generateToolCallId(),
722
804
  name: item.name,
723
- args: (() => {
724
- try {
725
- return JSON.parse(item.arguments ?? "{}");
726
- } catch {
727
- return {};
728
- }
729
- })()
805
+ args: JSON.parse(item.arguments ?? "{}")
730
806
  })) : [];
731
807
  return {
732
808
  content,
@@ -750,18 +826,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
750
826
  instructions: request.systemPrompt,
751
827
  input: this.buildResponsesInput(request),
752
828
  tools: this.buildResponsesTools(
753
- request.toolDefinitions ?? [],
829
+ this.toolDefinitions(request),
754
830
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
755
831
  this.buildResponsesWebSearchTool(request)
756
832
  ),
757
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
833
+ tool_choice: openaiToolOptions?.toolChoice,
758
834
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
759
- // Reasoning models reject `temperature` outright — sending it would swap one
760
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
761
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
762
- temperature: request.config?.temperature ?? this.config.temperature
763
- },
764
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
835
+ ...buildOpenAIParameters(
836
+ modelIdForPayload,
837
+ request.config?.maxTokens ?? this.config.maxTokens,
838
+ request.config?.temperature ?? this.config.temperature,
839
+ this.parameterOptions(request),
840
+ "responses"
841
+ ),
765
842
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
766
843
  stream
767
844
  };
@@ -769,10 +846,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
769
846
  }
770
847
  async completeWithResponses(request) {
771
848
  const client = await this.getClient();
849
+ throwIfAborted(request.signal);
772
850
  const payload = this.buildResponsesPayload(request, false);
773
851
  logProviderPayload("openai", "request payload", payload, request.debug);
774
- const response = await client.responses.create(payload);
852
+ if (!client.responses?.create)
853
+ throw new Error(
854
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
855
+ );
856
+ const response = await client.responses.create(payload, {
857
+ signal: request.signal
858
+ });
859
+ throwIfAborted(request.signal);
775
860
  logProviderPayload("openai", "response payload", response, request.debug);
861
+ if (response.status && response.status !== "completed") {
862
+ throw Object.assign(
863
+ new Error(
864
+ response.error?.message ?? `Responses request ${response.status}`
865
+ ),
866
+ response.error
867
+ );
868
+ }
776
869
  return this.parseResponsesResult(response);
777
870
  }
778
871
  /**
@@ -790,13 +883,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
790
883
  */
791
884
  async *streamWithResponses(request) {
792
885
  const client = await this.getClient();
886
+ throwIfAborted(request.signal);
793
887
  const payload = this.buildResponsesPayload(request, true);
794
888
  logProviderPayload("openai", "request payload", payload, request.debug);
795
- const stream = await client.responses.create(payload);
889
+ if (!client.responses?.create)
890
+ throw new Error(
891
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
892
+ );
893
+ const stream = await client.responses.create(payload, {
894
+ signal: request.signal
895
+ });
796
896
  const toolCalls = /* @__PURE__ */ new Map();
797
897
  let usage;
798
898
  const citations = [];
899
+ let completed = false;
799
900
  function* flushToolCalls() {
901
+ for (const call of toolCalls.values()) {
902
+ if (!call.name)
903
+ throw new Error("Responses tool call is missing its name");
904
+ JSON.parse(call.arguments || "{}");
905
+ }
800
906
  for (const call of toolCalls.values()) {
801
907
  yield { type: "action:start", id: call.id, name: call.name };
802
908
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -805,7 +911,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
805
911
  toolCalls.clear();
806
912
  }
807
913
  for await (const event of stream) {
808
- if (request.signal?.aborted) break;
914
+ throwIfAborted(request.signal);
809
915
  logProviderPayload("openai", "stream chunk", event, request.debug);
810
916
  const index = event?.output_index ?? 0;
811
917
  switch (event?.type) {
@@ -835,8 +941,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
835
941
  }
836
942
  break;
837
943
  }
838
- case "response.completed":
839
- case "response.incomplete": {
944
+ case "response.completed": {
945
+ completed = true;
840
946
  const found = this.collectResponsesCitations(
841
947
  event.response?.output,
842
948
  citations.length
@@ -852,13 +958,27 @@ var OpenAIAdapter = class _OpenAIAdapter {
852
958
  }
853
959
  break;
854
960
  }
855
- case "error":
856
- case "response.failed":
961
+ case "response.incomplete":
857
962
  throw new Error(
858
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
963
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
859
964
  );
965
+ case "error":
966
+ case "response.failed": {
967
+ const details = event.response?.error ?? event;
968
+ throw Object.assign(
969
+ new Error(details.message ?? "Responses stream failed"),
970
+ {
971
+ code: details.code,
972
+ param: details.param,
973
+ status: details.status
974
+ }
975
+ );
976
+ }
860
977
  }
861
978
  }
979
+ throwIfAborted(request.signal);
980
+ if (!completed)
981
+ throw new Error("Responses stream ended without a completed response");
862
982
  yield* flushToolCalls();
863
983
  if (citations.length > 0) {
864
984
  yield { type: "citation", citations: deduplicateCitations(citations) };
@@ -867,6 +987,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
867
987
  yield { type: "done", usage };
868
988
  }
869
989
  async *stream(request) {
990
+ throwIfAborted(request.signal);
870
991
  if (this.shouldUseResponsesApi(request)) {
871
992
  const messageId2 = generateMessageId();
872
993
  yield { type: "message:start", id: messageId2 };
@@ -874,47 +995,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
874
995
  yield* this.streamWithResponses(request);
875
996
  return;
876
997
  } catch (error) {
877
- yield {
878
- type: "error",
879
- message: error instanceof Error ? error.message : "Unknown error",
880
- code: "OPENAI_RESPONSES_ERROR"
881
- };
998
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
882
999
  return;
883
1000
  }
884
1001
  }
885
1002
  const client = await this.getClient();
886
1003
  let messages;
887
1004
  if (request.rawMessages && request.rawMessages.length > 0) {
888
- const processedMessages = request.rawMessages.map((msg) => {
889
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
890
- return { ...msg, content: null };
891
- }
892
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
893
- if (hasAttachments) {
894
- const content = [];
895
- if (msg.content) {
896
- content.push({ type: "text", text: msg.content });
897
- }
898
- for (const attachment of msg.attachments) {
899
- if (attachment.type === "image") {
900
- let imageUrl;
901
- if (attachment.url) {
902
- imageUrl = attachment.url;
903
- } else if (attachment.data) {
904
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
905
- } else {
906
- continue;
907
- }
908
- content.push({
909
- type: "image_url",
910
- image_url: { url: imageUrl, detail: "auto" }
911
- });
912
- }
913
- }
914
- return { ...msg, content, attachments: void 0 };
915
- }
916
- return msg;
917
- });
1005
+ const processedMessages = this.formatRawMessages(request.rawMessages);
918
1006
  if (request.systemPrompt) {
919
1007
  const hasSystem = processedMessages.some((m) => m.role === "system");
920
1008
  if (!hasSystem) {
@@ -934,7 +1022,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
934
1022
  request.systemPrompt
935
1023
  );
936
1024
  }
937
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1025
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1026
+ type: "function",
1027
+ function: {
1028
+ name: tool.name,
1029
+ description: tool.description,
1030
+ parameters: normalizeObjectJsonSchema(
1031
+ tool.inputSchema
1032
+ )
1033
+ }
1034
+ })) : formatTools(request.actions ?? []);
938
1035
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
939
1036
  if (webSearchConfig) {
940
1037
  const webSearchTool = {
@@ -963,26 +1060,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
963
1060
  tools: tools.length > 0 ? tools : void 0,
964
1061
  tool_choice: tools.length > 0 ? toolChoice : void 0,
965
1062
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
966
- ...buildOpenAITokenParams(
1063
+ ...buildOpenAIParameters(
967
1064
  modelIdForPayload,
968
1065
  request.config?.maxTokens ?? this.config.maxTokens,
969
- request.config?.temperature ?? this.config.temperature
1066
+ request.config?.temperature ?? this.config.temperature,
1067
+ this.parameterOptions(request)
970
1068
  ),
971
1069
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
972
1070
  stream: true,
973
1071
  stream_options: { include_usage: true }
974
1072
  };
975
1073
  logProviderPayload("openai", "request payload", payload, request.debug);
976
- const stream = await client.chat.completions.create(payload);
977
- let currentToolCall = null;
1074
+ const stream = await client.chat.completions.create(payload, {
1075
+ signal: request.signal
1076
+ });
1077
+ const toolCalls = /* @__PURE__ */ new Map();
1078
+ let finishReason;
978
1079
  const collectedCitations = [];
979
1080
  let citationIndex = 0;
980
1081
  let usage;
981
1082
  for await (const chunk of stream) {
982
1083
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
983
- if (request.signal?.aborted) {
984
- break;
985
- }
1084
+ throwIfAborted(request.signal);
986
1085
  const delta = chunk.choices[0]?.delta;
987
1086
  const choice = chunk.choices[0];
988
1087
  if (delta?.content) {
@@ -1005,38 +1104,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1005
1104
  }
1006
1105
  }
1007
1106
  }
1008
- if (delta?.tool_calls) {
1009
- for (const toolCall of delta.tool_calls) {
1010
- if (toolCall.id) {
1011
- if (currentToolCall) {
1012
- yield {
1013
- type: "action:args",
1014
- id: currentToolCall.id,
1015
- args: currentToolCall.arguments
1016
- };
1017
- yield {
1018
- type: "action:end",
1019
- id: currentToolCall.id,
1020
- name: currentToolCall.name
1021
- };
1022
- }
1023
- const tcExtraContent = toolCall.extra_content;
1024
- currentToolCall = {
1025
- id: toolCall.id,
1026
- name: toolCall.function?.name || "",
1027
- arguments: toolCall.function?.arguments || "",
1028
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1029
- };
1030
- yield {
1031
- type: "action:start",
1032
- id: currentToolCall.id,
1033
- name: currentToolCall.name,
1034
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1035
- };
1036
- } else if (currentToolCall && toolCall.function?.arguments) {
1037
- currentToolCall.arguments += toolCall.function.arguments;
1038
- }
1039
- }
1107
+ for (const part of delta?.tool_calls ?? []) {
1108
+ const index = part.index ?? 0;
1109
+ const call = toolCalls.get(index) ?? {
1110
+ id: "",
1111
+ name: "",
1112
+ arguments: ""
1113
+ };
1114
+ if (part.id) call.id = part.id;
1115
+ if (part.function?.name) call.name += part.function.name;
1116
+ if (part.function?.arguments)
1117
+ call.arguments += part.function.arguments;
1118
+ if (part.extra_content) call.extra_content = part.extra_content;
1119
+ toolCalls.set(index, call);
1040
1120
  }
1041
1121
  if (chunk.usage) {
1042
1122
  usage = {
@@ -1045,21 +1125,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1045
1125
  total_tokens: chunk.usage.total_tokens
1046
1126
  };
1047
1127
  }
1048
- if (choice?.finish_reason) {
1049
- if (currentToolCall) {
1050
- yield {
1051
- type: "action:args",
1052
- id: currentToolCall.id,
1053
- args: currentToolCall.arguments
1054
- };
1055
- yield {
1056
- type: "action:end",
1057
- id: currentToolCall.id,
1058
- name: currentToolCall.name
1059
- };
1060
- currentToolCall = null;
1061
- }
1062
- }
1128
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1129
+ }
1130
+ throwIfAborted(request.signal);
1131
+ if (!finishReason)
1132
+ throw new Error("OpenAI stream ended without a finish reason");
1133
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1134
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1135
+ }
1136
+ for (const call of toolCalls.values()) {
1137
+ if (!call.id || !call.name)
1138
+ throw new Error("OpenAI stream contains an incomplete tool call");
1139
+ JSON.parse(call.arguments || "{}");
1140
+ }
1141
+ for (const call of toolCalls.values()) {
1142
+ yield {
1143
+ type: "action:start",
1144
+ id: call.id,
1145
+ name: call.name,
1146
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1147
+ };
1148
+ yield { type: "action:args", id: call.id, args: call.arguments };
1149
+ yield { type: "action:end", id: call.id, name: call.name };
1063
1150
  }
1064
1151
  if (collectedCitations.length > 0) {
1065
1152
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1068,26 +1155,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1068
1155
  yield { type: "message:end" };
1069
1156
  yield { type: "done", usage };
1070
1157
  } catch (error) {
1071
- yield {
1072
- type: "error",
1073
- message: error instanceof Error ? error.message : "Unknown error",
1074
- code: `${this.provider.toUpperCase()}_ERROR`
1075
- };
1158
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1076
1159
  }
1077
1160
  }
1078
1161
  async complete(request) {
1162
+ throwIfAborted(request.signal);
1079
1163
  if (this.shouldUseResponsesApi(request)) {
1080
1164
  return this.completeWithResponses(request);
1081
1165
  }
1082
1166
  const client = await this.getClient();
1083
1167
  let messages;
1084
1168
  if (request.rawMessages && request.rawMessages.length > 0) {
1085
- const sanitized = request.rawMessages.map((msg) => {
1086
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1087
- return { ...msg, content: null };
1088
- }
1089
- return msg;
1090
- });
1169
+ const sanitized = this.formatRawMessages(request.rawMessages);
1091
1170
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1092
1171
  messages = [
1093
1172
  { role: "system", content: request.systemPrompt },
@@ -1102,7 +1181,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1102
1181
  request.systemPrompt
1103
1182
  );
1104
1183
  }
1105
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1184
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1185
+ type: "function",
1186
+ function: {
1187
+ name: tool.name,
1188
+ description: tool.description,
1189
+ parameters: normalizeObjectJsonSchema(
1190
+ tool.inputSchema
1191
+ )
1192
+ }
1193
+ })) : formatTools(request.actions ?? []);
1106
1194
  const openaiToolOptions = request.providerToolOptions?.openai;
1107
1195
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1108
1196
  type: "function",
@@ -1117,16 +1205,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1117
1205
  tools: tools.length > 0 ? tools : void 0,
1118
1206
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1119
1207
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1120
- ...buildOpenAITokenParams(
1208
+ ...buildOpenAIParameters(
1121
1209
  modelIdForCompletePayload,
1122
1210
  request.config?.maxTokens ?? this.config.maxTokens,
1123
- request.config?.temperature ?? this.config.temperature
1211
+ request.config?.temperature ?? this.config.temperature,
1212
+ this.parameterOptions(request)
1124
1213
  ),
1125
1214
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1126
1215
  stream: false
1127
1216
  };
1128
1217
  logProviderPayload("openai", "request payload", payload, request.debug);
1129
- const response = await client.chat.completions.create(payload);
1218
+ const response = await client.chat.completions.create(payload, {
1219
+ signal: request.signal
1220
+ });
1130
1221
  logProviderPayload("openai", "response payload", response, request.debug);
1131
1222
  const choice = response.choices?.[0];
1132
1223
  const message = choice?.message;