@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,3 +1,54 @@
1
+ // src/core/openai-parameters.ts
2
+ function capabilities(model) {
3
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
4
+ const major = Number(gpt?.[1] ?? 0);
5
+ const minor = Number(gpt?.[2] ?? 0);
6
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
7
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
8
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
9
+ return { major, minor, reasoning, supportsNone };
10
+ }
11
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
12
+ if (options.parameterPolicy === "passthrough") return false;
13
+ const c = capabilities(model);
14
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
15
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
16
+ if (!baseUrl) return true;
17
+ try {
18
+ return new URL(baseUrl).hostname === "api.openai.com";
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
24
+ const c = capabilities(model ?? "");
25
+ const normalize = options.parameterPolicy !== "passthrough";
26
+ const effort = options.reasoningEffort;
27
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
28
+ throw new Error(`${model} does not support reasoningEffort: none`);
29
+ }
30
+ if (normalize && c.major >= 6 && effort === "minimal") {
31
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
32
+ }
33
+ const result = {
34
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
35
+ };
36
+ if (effort !== void 0) {
37
+ if (api === "responses") result.reasoning = { effort };
38
+ else result.reasoning_effort = effort;
39
+ }
40
+ if (temperature !== void 0) {
41
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
42
+ result.temperature = temperature;
43
+ } else {
44
+ (options.onWarning ?? console.warn)(
45
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
46
+ );
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+
1
52
  // src/adapters/base.ts
2
53
  function stringifyForDebug(value) {
3
54
  return JSON.stringify(
@@ -104,16 +155,6 @@ function normalizeObjectJsonSchema(schema) {
104
155
  }
105
156
  return normalized;
106
157
  }
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
158
  function toOpenAIResponseFormat(rf) {
118
159
  if (!rf) return void 0;
119
160
  if (rf.type === "json_object") return { type: "json_object" };
@@ -469,6 +510,28 @@ function formatMessagesForXAI(messages) {
469
510
  });
470
511
  }
471
512
 
513
+ // src/core/provider-error.ts
514
+ function providerErrorEvent(error, code) {
515
+ const details = error ?? {};
516
+ const event = {
517
+ type: "error",
518
+ message: error instanceof Error ? error.message : String(error),
519
+ code,
520
+ ...typeof details.status === "number" ? { status: details.status } : {},
521
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
522
+ ...typeof details.param === "string" ? { param: details.param } : {},
523
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
524
+ };
525
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
526
+ return event;
527
+ }
528
+ function throwIfAborted(signal) {
529
+ if (!signal?.aborted) return;
530
+ const error = new Error("The operation was aborted");
531
+ error.name = "AbortError";
532
+ throw error;
533
+ }
534
+
472
535
  // src/core/utils.ts
473
536
  function generateId(prefix = "id") {
474
537
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -500,39 +563,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
500
563
  const { default: OpenAI } = await import('openai');
501
564
  this.client = new OpenAI({
502
565
  apiKey: this.config.apiKey,
503
- baseURL: this.config.baseUrl
566
+ baseURL: this.config.baseUrl,
567
+ organization: this.config.organization,
568
+ defaultHeaders: this.config.headers
504
569
  });
505
570
  }
506
571
  return this.client;
507
572
  }
573
+ parameterOptions(request) {
574
+ return {
575
+ ...this.config,
576
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
577
+ };
578
+ }
579
+ toolDefinitions(request) {
580
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
581
+ return formatTools(request.actions ?? []).map((tool) => ({
582
+ name: tool.function.name,
583
+ description: tool.function.description ?? "",
584
+ location: "server",
585
+ inputSchema: tool.function.parameters
586
+ }));
587
+ }
508
588
  shouldUseResponsesApi(request) {
509
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
510
- if (!hasTools) return false;
511
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
589
+ if (this.toolDefinitions(request).length === 0) return false;
590
+ if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
512
591
  return true;
513
- }
514
- return this.requiresResponsesApiForTools(
515
- request.config?.model || this.model
592
+ return requiresOpenAIResponses(
593
+ request.config?.model || this.model,
594
+ this.config.baseUrl,
595
+ this.parameterOptions(request)
516
596
  );
517
597
  }
518
- /**
519
- * Whether this model rejects function tools on /v1/chat/completions.
520
- *
521
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
522
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
523
- * restriction and may not implement /v1/responses at all.
524
- */
525
- requiresResponsesApiForTools(modelId) {
526
- if (!modelId) return false;
527
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
528
- const baseUrl = this.config.baseUrl;
529
- if (!baseUrl) return true;
530
- try {
531
- return new URL(baseUrl).hostname === "api.openai.com";
532
- } catch {
533
- return false;
534
- }
535
- }
536
598
  /**
537
599
  * Convert one message's content to Responses content parts.
538
600
  *
@@ -563,7 +625,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
563
625
  });
564
626
  }
565
627
  buildResponsesInput(request) {
566
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
628
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
567
629
  const input = [];
568
630
  for (const message of sourceMessages) {
569
631
  if (message.role === "system") {
@@ -613,6 +675,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
613
675
  }
614
676
  return input;
615
677
  }
678
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
679
+ formatRawMessages(messages) {
680
+ return messages.map((message) => {
681
+ const { attachments, ...rest } = message;
682
+ let content = message.content;
683
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
684
+ content = null;
685
+ }
686
+ if (Array.isArray(attachments) && attachments.length > 0) {
687
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
688
+ (attachment) => attachmentToOpenAIImage(attachment)
689
+ ).filter((image) => image !== null);
690
+ if (images.length > 0) {
691
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
692
+ content = [...parts, ...images];
693
+ }
694
+ }
695
+ return { ...rest, content };
696
+ });
697
+ }
616
698
  /**
617
699
  * Build the web_search tool for the Responses API, or undefined when web
618
700
  * search is off.
@@ -694,13 +776,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
694
776
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
695
777
  id: item.call_id ?? item.id ?? generateToolCallId(),
696
778
  name: item.name,
697
- args: (() => {
698
- try {
699
- return JSON.parse(item.arguments ?? "{}");
700
- } catch {
701
- return {};
702
- }
703
- })()
779
+ args: JSON.parse(item.arguments ?? "{}")
704
780
  })) : [];
705
781
  return {
706
782
  content,
@@ -724,18 +800,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
724
800
  instructions: request.systemPrompt,
725
801
  input: this.buildResponsesInput(request),
726
802
  tools: this.buildResponsesTools(
727
- request.toolDefinitions ?? [],
803
+ this.toolDefinitions(request),
728
804
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
729
805
  this.buildResponsesWebSearchTool(request)
730
806
  ),
731
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
807
+ tool_choice: openaiToolOptions?.toolChoice,
732
808
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
733
- // Reasoning models reject `temperature` outright — sending it would swap one
734
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
735
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
736
- temperature: request.config?.temperature ?? this.config.temperature
737
- },
738
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
809
+ ...buildOpenAIParameters(
810
+ modelIdForPayload,
811
+ request.config?.maxTokens ?? this.config.maxTokens,
812
+ request.config?.temperature ?? this.config.temperature,
813
+ this.parameterOptions(request),
814
+ "responses"
815
+ ),
739
816
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
740
817
  stream
741
818
  };
@@ -743,10 +820,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
743
820
  }
744
821
  async completeWithResponses(request) {
745
822
  const client = await this.getClient();
823
+ throwIfAborted(request.signal);
746
824
  const payload = this.buildResponsesPayload(request, false);
747
825
  logProviderPayload("openai", "request payload", payload, request.debug);
748
- const response = await client.responses.create(payload);
826
+ if (!client.responses?.create)
827
+ throw new Error(
828
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
829
+ );
830
+ const response = await client.responses.create(payload, {
831
+ signal: request.signal
832
+ });
833
+ throwIfAborted(request.signal);
749
834
  logProviderPayload("openai", "response payload", response, request.debug);
835
+ if (response.status && response.status !== "completed") {
836
+ throw Object.assign(
837
+ new Error(
838
+ response.error?.message ?? `Responses request ${response.status}`
839
+ ),
840
+ response.error
841
+ );
842
+ }
750
843
  return this.parseResponsesResult(response);
751
844
  }
752
845
  /**
@@ -764,13 +857,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
764
857
  */
765
858
  async *streamWithResponses(request) {
766
859
  const client = await this.getClient();
860
+ throwIfAborted(request.signal);
767
861
  const payload = this.buildResponsesPayload(request, true);
768
862
  logProviderPayload("openai", "request payload", payload, request.debug);
769
- const stream = await client.responses.create(payload);
863
+ if (!client.responses?.create)
864
+ throw new Error(
865
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
866
+ );
867
+ const stream = await client.responses.create(payload, {
868
+ signal: request.signal
869
+ });
770
870
  const toolCalls = /* @__PURE__ */ new Map();
771
871
  let usage;
772
872
  const citations = [];
873
+ let completed = false;
773
874
  function* flushToolCalls() {
875
+ for (const call of toolCalls.values()) {
876
+ if (!call.name)
877
+ throw new Error("Responses tool call is missing its name");
878
+ JSON.parse(call.arguments || "{}");
879
+ }
774
880
  for (const call of toolCalls.values()) {
775
881
  yield { type: "action:start", id: call.id, name: call.name };
776
882
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -779,7 +885,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
779
885
  toolCalls.clear();
780
886
  }
781
887
  for await (const event of stream) {
782
- if (request.signal?.aborted) break;
888
+ throwIfAborted(request.signal);
783
889
  logProviderPayload("openai", "stream chunk", event, request.debug);
784
890
  const index = event?.output_index ?? 0;
785
891
  switch (event?.type) {
@@ -809,8 +915,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
809
915
  }
810
916
  break;
811
917
  }
812
- case "response.completed":
813
- case "response.incomplete": {
918
+ case "response.completed": {
919
+ completed = true;
814
920
  const found = this.collectResponsesCitations(
815
921
  event.response?.output,
816
922
  citations.length
@@ -826,13 +932,27 @@ var OpenAIAdapter = class _OpenAIAdapter {
826
932
  }
827
933
  break;
828
934
  }
829
- case "error":
830
- case "response.failed":
935
+ case "response.incomplete":
831
936
  throw new Error(
832
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
937
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
833
938
  );
939
+ case "error":
940
+ case "response.failed": {
941
+ const details = event.response?.error ?? event;
942
+ throw Object.assign(
943
+ new Error(details.message ?? "Responses stream failed"),
944
+ {
945
+ code: details.code,
946
+ param: details.param,
947
+ status: details.status
948
+ }
949
+ );
950
+ }
834
951
  }
835
952
  }
953
+ throwIfAborted(request.signal);
954
+ if (!completed)
955
+ throw new Error("Responses stream ended without a completed response");
836
956
  yield* flushToolCalls();
837
957
  if (citations.length > 0) {
838
958
  yield { type: "citation", citations: deduplicateCitations(citations) };
@@ -841,6 +961,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
841
961
  yield { type: "done", usage };
842
962
  }
843
963
  async *stream(request) {
964
+ throwIfAborted(request.signal);
844
965
  if (this.shouldUseResponsesApi(request)) {
845
966
  const messageId2 = generateMessageId();
846
967
  yield { type: "message:start", id: messageId2 };
@@ -848,47 +969,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
848
969
  yield* this.streamWithResponses(request);
849
970
  return;
850
971
  } catch (error) {
851
- yield {
852
- type: "error",
853
- message: error instanceof Error ? error.message : "Unknown error",
854
- code: "OPENAI_RESPONSES_ERROR"
855
- };
972
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
856
973
  return;
857
974
  }
858
975
  }
859
976
  const client = await this.getClient();
860
977
  let messages;
861
978
  if (request.rawMessages && request.rawMessages.length > 0) {
862
- const processedMessages = request.rawMessages.map((msg) => {
863
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
864
- return { ...msg, content: null };
865
- }
866
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
867
- if (hasAttachments) {
868
- const content = [];
869
- if (msg.content) {
870
- content.push({ type: "text", text: msg.content });
871
- }
872
- for (const attachment of msg.attachments) {
873
- if (attachment.type === "image") {
874
- let imageUrl;
875
- if (attachment.url) {
876
- imageUrl = attachment.url;
877
- } else if (attachment.data) {
878
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
879
- } else {
880
- continue;
881
- }
882
- content.push({
883
- type: "image_url",
884
- image_url: { url: imageUrl, detail: "auto" }
885
- });
886
- }
887
- }
888
- return { ...msg, content, attachments: void 0 };
889
- }
890
- return msg;
891
- });
979
+ const processedMessages = this.formatRawMessages(request.rawMessages);
892
980
  if (request.systemPrompt) {
893
981
  const hasSystem = processedMessages.some((m) => m.role === "system");
894
982
  if (!hasSystem) {
@@ -908,7 +996,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
908
996
  request.systemPrompt
909
997
  );
910
998
  }
911
- const tools = request.actions?.length ? formatTools(request.actions) : [];
999
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1000
+ type: "function",
1001
+ function: {
1002
+ name: tool.name,
1003
+ description: tool.description,
1004
+ parameters: normalizeObjectJsonSchema(
1005
+ tool.inputSchema
1006
+ )
1007
+ }
1008
+ })) : formatTools(request.actions ?? []);
912
1009
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
913
1010
  if (webSearchConfig) {
914
1011
  const webSearchTool = {
@@ -937,26 +1034,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
937
1034
  tools: tools.length > 0 ? tools : void 0,
938
1035
  tool_choice: tools.length > 0 ? toolChoice : void 0,
939
1036
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
940
- ...buildOpenAITokenParams(
1037
+ ...buildOpenAIParameters(
941
1038
  modelIdForPayload,
942
1039
  request.config?.maxTokens ?? this.config.maxTokens,
943
- request.config?.temperature ?? this.config.temperature
1040
+ request.config?.temperature ?? this.config.temperature,
1041
+ this.parameterOptions(request)
944
1042
  ),
945
1043
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
946
1044
  stream: true,
947
1045
  stream_options: { include_usage: true }
948
1046
  };
949
1047
  logProviderPayload("openai", "request payload", payload, request.debug);
950
- const stream = await client.chat.completions.create(payload);
951
- let currentToolCall = null;
1048
+ const stream = await client.chat.completions.create(payload, {
1049
+ signal: request.signal
1050
+ });
1051
+ const toolCalls = /* @__PURE__ */ new Map();
1052
+ let finishReason;
952
1053
  const collectedCitations = [];
953
1054
  let citationIndex = 0;
954
1055
  let usage;
955
1056
  for await (const chunk of stream) {
956
1057
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
957
- if (request.signal?.aborted) {
958
- break;
959
- }
1058
+ throwIfAborted(request.signal);
960
1059
  const delta = chunk.choices[0]?.delta;
961
1060
  const choice = chunk.choices[0];
962
1061
  if (delta?.content) {
@@ -979,38 +1078,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
979
1078
  }
980
1079
  }
981
1080
  }
982
- if (delta?.tool_calls) {
983
- for (const toolCall of delta.tool_calls) {
984
- if (toolCall.id) {
985
- if (currentToolCall) {
986
- yield {
987
- type: "action:args",
988
- id: currentToolCall.id,
989
- args: currentToolCall.arguments
990
- };
991
- yield {
992
- type: "action:end",
993
- id: currentToolCall.id,
994
- name: currentToolCall.name
995
- };
996
- }
997
- const tcExtraContent = toolCall.extra_content;
998
- currentToolCall = {
999
- id: toolCall.id,
1000
- name: toolCall.function?.name || "",
1001
- arguments: toolCall.function?.arguments || "",
1002
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1003
- };
1004
- yield {
1005
- type: "action:start",
1006
- id: currentToolCall.id,
1007
- name: currentToolCall.name,
1008
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1009
- };
1010
- } else if (currentToolCall && toolCall.function?.arguments) {
1011
- currentToolCall.arguments += toolCall.function.arguments;
1012
- }
1013
- }
1081
+ for (const part of delta?.tool_calls ?? []) {
1082
+ const index = part.index ?? 0;
1083
+ const call = toolCalls.get(index) ?? {
1084
+ id: "",
1085
+ name: "",
1086
+ arguments: ""
1087
+ };
1088
+ if (part.id) call.id = part.id;
1089
+ if (part.function?.name) call.name += part.function.name;
1090
+ if (part.function?.arguments)
1091
+ call.arguments += part.function.arguments;
1092
+ if (part.extra_content) call.extra_content = part.extra_content;
1093
+ toolCalls.set(index, call);
1014
1094
  }
1015
1095
  if (chunk.usage) {
1016
1096
  usage = {
@@ -1019,21 +1099,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1019
1099
  total_tokens: chunk.usage.total_tokens
1020
1100
  };
1021
1101
  }
1022
- if (choice?.finish_reason) {
1023
- if (currentToolCall) {
1024
- yield {
1025
- type: "action:args",
1026
- id: currentToolCall.id,
1027
- args: currentToolCall.arguments
1028
- };
1029
- yield {
1030
- type: "action:end",
1031
- id: currentToolCall.id,
1032
- name: currentToolCall.name
1033
- };
1034
- currentToolCall = null;
1035
- }
1036
- }
1102
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1103
+ }
1104
+ throwIfAborted(request.signal);
1105
+ if (!finishReason)
1106
+ throw new Error("OpenAI stream ended without a finish reason");
1107
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1108
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1109
+ }
1110
+ for (const call of toolCalls.values()) {
1111
+ if (!call.id || !call.name)
1112
+ throw new Error("OpenAI stream contains an incomplete tool call");
1113
+ JSON.parse(call.arguments || "{}");
1114
+ }
1115
+ for (const call of toolCalls.values()) {
1116
+ yield {
1117
+ type: "action:start",
1118
+ id: call.id,
1119
+ name: call.name,
1120
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1121
+ };
1122
+ yield { type: "action:args", id: call.id, args: call.arguments };
1123
+ yield { type: "action:end", id: call.id, name: call.name };
1037
1124
  }
1038
1125
  if (collectedCitations.length > 0) {
1039
1126
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1042,26 +1129,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1042
1129
  yield { type: "message:end" };
1043
1130
  yield { type: "done", usage };
1044
1131
  } catch (error) {
1045
- yield {
1046
- type: "error",
1047
- message: error instanceof Error ? error.message : "Unknown error",
1048
- code: `${this.provider.toUpperCase()}_ERROR`
1049
- };
1132
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1050
1133
  }
1051
1134
  }
1052
1135
  async complete(request) {
1136
+ throwIfAborted(request.signal);
1053
1137
  if (this.shouldUseResponsesApi(request)) {
1054
1138
  return this.completeWithResponses(request);
1055
1139
  }
1056
1140
  const client = await this.getClient();
1057
1141
  let messages;
1058
1142
  if (request.rawMessages && request.rawMessages.length > 0) {
1059
- const sanitized = request.rawMessages.map((msg) => {
1060
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1061
- return { ...msg, content: null };
1062
- }
1063
- return msg;
1064
- });
1143
+ const sanitized = this.formatRawMessages(request.rawMessages);
1065
1144
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1066
1145
  messages = [
1067
1146
  { role: "system", content: request.systemPrompt },
@@ -1076,7 +1155,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1076
1155
  request.systemPrompt
1077
1156
  );
1078
1157
  }
1079
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1158
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1159
+ type: "function",
1160
+ function: {
1161
+ name: tool.name,
1162
+ description: tool.description,
1163
+ parameters: normalizeObjectJsonSchema(
1164
+ tool.inputSchema
1165
+ )
1166
+ }
1167
+ })) : formatTools(request.actions ?? []);
1080
1168
  const openaiToolOptions = request.providerToolOptions?.openai;
1081
1169
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1082
1170
  type: "function",
@@ -1091,16 +1179,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1091
1179
  tools: tools.length > 0 ? tools : void 0,
1092
1180
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1093
1181
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1094
- ...buildOpenAITokenParams(
1182
+ ...buildOpenAIParameters(
1095
1183
  modelIdForCompletePayload,
1096
1184
  request.config?.maxTokens ?? this.config.maxTokens,
1097
- request.config?.temperature ?? this.config.temperature
1185
+ request.config?.temperature ?? this.config.temperature,
1186
+ this.parameterOptions(request)
1098
1187
  ),
1099
1188
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1100
1189
  stream: false
1101
1190
  };
1102
1191
  logProviderPayload("openai", "request payload", payload, request.debug);
1103
- const response = await client.chat.completions.create(payload);
1192
+ const response = await client.chat.completions.create(payload, {
1193
+ signal: request.signal
1194
+ });
1104
1195
  logProviderPayload("openai", "response payload", response, request.debug);
1105
1196
  const choice = response.choices?.[0];
1106
1197
  const message = choice?.message;