@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" };
@@ -447,6 +488,28 @@ function formatMessages(messages) {
447
488
  });
448
489
  }
449
490
 
491
+ // src/core/provider-error.ts
492
+ function providerErrorEvent(error, code) {
493
+ const details = error ?? {};
494
+ const event = {
495
+ type: "error",
496
+ message: error instanceof Error ? error.message : String(error),
497
+ code,
498
+ ...typeof details.status === "number" ? { status: details.status } : {},
499
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
500
+ ...typeof details.param === "string" ? { param: details.param } : {},
501
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
502
+ };
503
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
504
+ return event;
505
+ }
506
+ function throwIfAborted(signal) {
507
+ if (!signal?.aborted) return;
508
+ const error = new Error("The operation was aborted");
509
+ error.name = "AbortError";
510
+ throw error;
511
+ }
512
+
450
513
  // src/core/utils.ts
451
514
  function generateId(prefix = "id") {
452
515
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -478,39 +541,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
478
541
  const { default: OpenAI } = await import('openai');
479
542
  this.client = new OpenAI({
480
543
  apiKey: this.config.apiKey,
481
- baseURL: this.config.baseUrl
544
+ baseURL: this.config.baseUrl,
545
+ organization: this.config.organization,
546
+ defaultHeaders: this.config.headers
482
547
  });
483
548
  }
484
549
  return this.client;
485
550
  }
551
+ parameterOptions(request) {
552
+ return {
553
+ ...this.config,
554
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
555
+ };
556
+ }
557
+ toolDefinitions(request) {
558
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
559
+ return formatTools(request.actions ?? []).map((tool) => ({
560
+ name: tool.function.name,
561
+ description: tool.function.description ?? "",
562
+ location: "server",
563
+ inputSchema: tool.function.parameters
564
+ }));
565
+ }
486
566
  shouldUseResponsesApi(request) {
487
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
488
- if (!hasTools) return false;
489
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
567
+ if (this.toolDefinitions(request).length === 0) return false;
568
+ if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
490
569
  return true;
491
- }
492
- return this.requiresResponsesApiForTools(
493
- request.config?.model || this.model
570
+ return requiresOpenAIResponses(
571
+ request.config?.model || this.model,
572
+ this.config.baseUrl,
573
+ this.parameterOptions(request)
494
574
  );
495
575
  }
496
- /**
497
- * Whether this model rejects function tools on /v1/chat/completions.
498
- *
499
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
500
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
501
- * restriction and may not implement /v1/responses at all.
502
- */
503
- requiresResponsesApiForTools(modelId) {
504
- if (!modelId) return false;
505
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
506
- const baseUrl = this.config.baseUrl;
507
- if (!baseUrl) return true;
508
- try {
509
- return new URL(baseUrl).hostname === "api.openai.com";
510
- } catch {
511
- return false;
512
- }
513
- }
514
576
  /**
515
577
  * Convert one message's content to Responses content parts.
516
578
  *
@@ -541,7 +603,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
541
603
  });
542
604
  }
543
605
  buildResponsesInput(request) {
544
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
606
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
545
607
  const input = [];
546
608
  for (const message of sourceMessages) {
547
609
  if (message.role === "system") {
@@ -591,6 +653,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
591
653
  }
592
654
  return input;
593
655
  }
656
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
657
+ formatRawMessages(messages) {
658
+ return messages.map((message) => {
659
+ const { attachments, ...rest } = message;
660
+ let content = message.content;
661
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
662
+ content = null;
663
+ }
664
+ if (Array.isArray(attachments) && attachments.length > 0) {
665
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
666
+ (attachment) => attachmentToOpenAIImage(attachment)
667
+ ).filter((image) => image !== null);
668
+ if (images.length > 0) {
669
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
670
+ content = [...parts, ...images];
671
+ }
672
+ }
673
+ return { ...rest, content };
674
+ });
675
+ }
594
676
  /**
595
677
  * Build the web_search tool for the Responses API, or undefined when web
596
678
  * search is off.
@@ -672,13 +754,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
672
754
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
673
755
  id: item.call_id ?? item.id ?? generateToolCallId(),
674
756
  name: item.name,
675
- args: (() => {
676
- try {
677
- return JSON.parse(item.arguments ?? "{}");
678
- } catch {
679
- return {};
680
- }
681
- })()
757
+ args: JSON.parse(item.arguments ?? "{}")
682
758
  })) : [];
683
759
  return {
684
760
  content,
@@ -702,18 +778,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
702
778
  instructions: request.systemPrompt,
703
779
  input: this.buildResponsesInput(request),
704
780
  tools: this.buildResponsesTools(
705
- request.toolDefinitions ?? [],
781
+ this.toolDefinitions(request),
706
782
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
707
783
  this.buildResponsesWebSearchTool(request)
708
784
  ),
709
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
785
+ tool_choice: openaiToolOptions?.toolChoice,
710
786
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
711
- // Reasoning models reject `temperature` outright — sending it would swap one
712
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
713
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
714
- temperature: request.config?.temperature ?? this.config.temperature
715
- },
716
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
787
+ ...buildOpenAIParameters(
788
+ modelIdForPayload,
789
+ request.config?.maxTokens ?? this.config.maxTokens,
790
+ request.config?.temperature ?? this.config.temperature,
791
+ this.parameterOptions(request),
792
+ "responses"
793
+ ),
717
794
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
718
795
  stream
719
796
  };
@@ -721,10 +798,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
721
798
  }
722
799
  async completeWithResponses(request) {
723
800
  const client = await this.getClient();
801
+ throwIfAborted(request.signal);
724
802
  const payload = this.buildResponsesPayload(request, false);
725
803
  logProviderPayload("openai", "request payload", payload, request.debug);
726
- const response = await client.responses.create(payload);
804
+ if (!client.responses?.create)
805
+ throw new Error(
806
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
807
+ );
808
+ const response = await client.responses.create(payload, {
809
+ signal: request.signal
810
+ });
811
+ throwIfAborted(request.signal);
727
812
  logProviderPayload("openai", "response payload", response, request.debug);
813
+ if (response.status && response.status !== "completed") {
814
+ throw Object.assign(
815
+ new Error(
816
+ response.error?.message ?? `Responses request ${response.status}`
817
+ ),
818
+ response.error
819
+ );
820
+ }
728
821
  return this.parseResponsesResult(response);
729
822
  }
730
823
  /**
@@ -742,13 +835,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
742
835
  */
743
836
  async *streamWithResponses(request) {
744
837
  const client = await this.getClient();
838
+ throwIfAborted(request.signal);
745
839
  const payload = this.buildResponsesPayload(request, true);
746
840
  logProviderPayload("openai", "request payload", payload, request.debug);
747
- const stream = await client.responses.create(payload);
841
+ if (!client.responses?.create)
842
+ throw new Error(
843
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
844
+ );
845
+ const stream = await client.responses.create(payload, {
846
+ signal: request.signal
847
+ });
748
848
  const toolCalls = /* @__PURE__ */ new Map();
749
849
  let usage;
750
850
  const citations = [];
851
+ let completed = false;
751
852
  function* flushToolCalls() {
853
+ for (const call of toolCalls.values()) {
854
+ if (!call.name)
855
+ throw new Error("Responses tool call is missing its name");
856
+ JSON.parse(call.arguments || "{}");
857
+ }
752
858
  for (const call of toolCalls.values()) {
753
859
  yield { type: "action:start", id: call.id, name: call.name };
754
860
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -757,7 +863,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
757
863
  toolCalls.clear();
758
864
  }
759
865
  for await (const event of stream) {
760
- if (request.signal?.aborted) break;
866
+ throwIfAborted(request.signal);
761
867
  logProviderPayload("openai", "stream chunk", event, request.debug);
762
868
  const index = event?.output_index ?? 0;
763
869
  switch (event?.type) {
@@ -787,8 +893,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
787
893
  }
788
894
  break;
789
895
  }
790
- case "response.completed":
791
- case "response.incomplete": {
896
+ case "response.completed": {
897
+ completed = true;
792
898
  const found = this.collectResponsesCitations(
793
899
  event.response?.output,
794
900
  citations.length
@@ -804,13 +910,27 @@ var OpenAIAdapter = class _OpenAIAdapter {
804
910
  }
805
911
  break;
806
912
  }
807
- case "error":
808
- case "response.failed":
913
+ case "response.incomplete":
809
914
  throw new Error(
810
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
915
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
811
916
  );
917
+ case "error":
918
+ case "response.failed": {
919
+ const details = event.response?.error ?? event;
920
+ throw Object.assign(
921
+ new Error(details.message ?? "Responses stream failed"),
922
+ {
923
+ code: details.code,
924
+ param: details.param,
925
+ status: details.status
926
+ }
927
+ );
928
+ }
812
929
  }
813
930
  }
931
+ throwIfAborted(request.signal);
932
+ if (!completed)
933
+ throw new Error("Responses stream ended without a completed response");
814
934
  yield* flushToolCalls();
815
935
  if (citations.length > 0) {
816
936
  yield { type: "citation", citations: deduplicateCitations(citations) };
@@ -819,6 +939,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
819
939
  yield { type: "done", usage };
820
940
  }
821
941
  async *stream(request) {
942
+ throwIfAborted(request.signal);
822
943
  if (this.shouldUseResponsesApi(request)) {
823
944
  const messageId2 = generateMessageId();
824
945
  yield { type: "message:start", id: messageId2 };
@@ -826,47 +947,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
826
947
  yield* this.streamWithResponses(request);
827
948
  return;
828
949
  } catch (error) {
829
- yield {
830
- type: "error",
831
- message: error instanceof Error ? error.message : "Unknown error",
832
- code: "OPENAI_RESPONSES_ERROR"
833
- };
950
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
834
951
  return;
835
952
  }
836
953
  }
837
954
  const client = await this.getClient();
838
955
  let messages;
839
956
  if (request.rawMessages && request.rawMessages.length > 0) {
840
- const processedMessages = request.rawMessages.map((msg) => {
841
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
842
- return { ...msg, content: null };
843
- }
844
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
845
- if (hasAttachments) {
846
- const content = [];
847
- if (msg.content) {
848
- content.push({ type: "text", text: msg.content });
849
- }
850
- for (const attachment of msg.attachments) {
851
- if (attachment.type === "image") {
852
- let imageUrl;
853
- if (attachment.url) {
854
- imageUrl = attachment.url;
855
- } else if (attachment.data) {
856
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
857
- } else {
858
- continue;
859
- }
860
- content.push({
861
- type: "image_url",
862
- image_url: { url: imageUrl, detail: "auto" }
863
- });
864
- }
865
- }
866
- return { ...msg, content, attachments: void 0 };
867
- }
868
- return msg;
869
- });
957
+ const processedMessages = this.formatRawMessages(request.rawMessages);
870
958
  if (request.systemPrompt) {
871
959
  const hasSystem = processedMessages.some((m) => m.role === "system");
872
960
  if (!hasSystem) {
@@ -886,7 +974,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
886
974
  request.systemPrompt
887
975
  );
888
976
  }
889
- const tools = request.actions?.length ? formatTools(request.actions) : [];
977
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
978
+ type: "function",
979
+ function: {
980
+ name: tool.name,
981
+ description: tool.description,
982
+ parameters: normalizeObjectJsonSchema(
983
+ tool.inputSchema
984
+ )
985
+ }
986
+ })) : formatTools(request.actions ?? []);
890
987
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
891
988
  if (webSearchConfig) {
892
989
  const webSearchTool = {
@@ -915,26 +1012,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
915
1012
  tools: tools.length > 0 ? tools : void 0,
916
1013
  tool_choice: tools.length > 0 ? toolChoice : void 0,
917
1014
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
918
- ...buildOpenAITokenParams(
1015
+ ...buildOpenAIParameters(
919
1016
  modelIdForPayload,
920
1017
  request.config?.maxTokens ?? this.config.maxTokens,
921
- request.config?.temperature ?? this.config.temperature
1018
+ request.config?.temperature ?? this.config.temperature,
1019
+ this.parameterOptions(request)
922
1020
  ),
923
1021
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
924
1022
  stream: true,
925
1023
  stream_options: { include_usage: true }
926
1024
  };
927
1025
  logProviderPayload("openai", "request payload", payload, request.debug);
928
- const stream = await client.chat.completions.create(payload);
929
- let currentToolCall = null;
1026
+ const stream = await client.chat.completions.create(payload, {
1027
+ signal: request.signal
1028
+ });
1029
+ const toolCalls = /* @__PURE__ */ new Map();
1030
+ let finishReason;
930
1031
  const collectedCitations = [];
931
1032
  let citationIndex = 0;
932
1033
  let usage;
933
1034
  for await (const chunk of stream) {
934
1035
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
935
- if (request.signal?.aborted) {
936
- break;
937
- }
1036
+ throwIfAborted(request.signal);
938
1037
  const delta = chunk.choices[0]?.delta;
939
1038
  const choice = chunk.choices[0];
940
1039
  if (delta?.content) {
@@ -957,38 +1056,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
957
1056
  }
958
1057
  }
959
1058
  }
960
- if (delta?.tool_calls) {
961
- for (const toolCall of delta.tool_calls) {
962
- if (toolCall.id) {
963
- if (currentToolCall) {
964
- yield {
965
- type: "action:args",
966
- id: currentToolCall.id,
967
- args: currentToolCall.arguments
968
- };
969
- yield {
970
- type: "action:end",
971
- id: currentToolCall.id,
972
- name: currentToolCall.name
973
- };
974
- }
975
- const tcExtraContent = toolCall.extra_content;
976
- currentToolCall = {
977
- id: toolCall.id,
978
- name: toolCall.function?.name || "",
979
- arguments: toolCall.function?.arguments || "",
980
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
981
- };
982
- yield {
983
- type: "action:start",
984
- id: currentToolCall.id,
985
- name: currentToolCall.name,
986
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
987
- };
988
- } else if (currentToolCall && toolCall.function?.arguments) {
989
- currentToolCall.arguments += toolCall.function.arguments;
990
- }
991
- }
1059
+ for (const part of delta?.tool_calls ?? []) {
1060
+ const index = part.index ?? 0;
1061
+ const call = toolCalls.get(index) ?? {
1062
+ id: "",
1063
+ name: "",
1064
+ arguments: ""
1065
+ };
1066
+ if (part.id) call.id = part.id;
1067
+ if (part.function?.name) call.name += part.function.name;
1068
+ if (part.function?.arguments)
1069
+ call.arguments += part.function.arguments;
1070
+ if (part.extra_content) call.extra_content = part.extra_content;
1071
+ toolCalls.set(index, call);
992
1072
  }
993
1073
  if (chunk.usage) {
994
1074
  usage = {
@@ -997,21 +1077,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
997
1077
  total_tokens: chunk.usage.total_tokens
998
1078
  };
999
1079
  }
1000
- if (choice?.finish_reason) {
1001
- if (currentToolCall) {
1002
- yield {
1003
- type: "action:args",
1004
- id: currentToolCall.id,
1005
- args: currentToolCall.arguments
1006
- };
1007
- yield {
1008
- type: "action:end",
1009
- id: currentToolCall.id,
1010
- name: currentToolCall.name
1011
- };
1012
- currentToolCall = null;
1013
- }
1014
- }
1080
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
1081
+ }
1082
+ throwIfAborted(request.signal);
1083
+ if (!finishReason)
1084
+ throw new Error("OpenAI stream ended without a finish reason");
1085
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1086
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1087
+ }
1088
+ for (const call of toolCalls.values()) {
1089
+ if (!call.id || !call.name)
1090
+ throw new Error("OpenAI stream contains an incomplete tool call");
1091
+ JSON.parse(call.arguments || "{}");
1092
+ }
1093
+ for (const call of toolCalls.values()) {
1094
+ yield {
1095
+ type: "action:start",
1096
+ id: call.id,
1097
+ name: call.name,
1098
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
1099
+ };
1100
+ yield { type: "action:args", id: call.id, args: call.arguments };
1101
+ yield { type: "action:end", id: call.id, name: call.name };
1015
1102
  }
1016
1103
  if (collectedCitations.length > 0) {
1017
1104
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1020,26 +1107,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1020
1107
  yield { type: "message:end" };
1021
1108
  yield { type: "done", usage };
1022
1109
  } catch (error) {
1023
- yield {
1024
- type: "error",
1025
- message: error instanceof Error ? error.message : "Unknown error",
1026
- code: `${this.provider.toUpperCase()}_ERROR`
1027
- };
1110
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1028
1111
  }
1029
1112
  }
1030
1113
  async complete(request) {
1114
+ throwIfAborted(request.signal);
1031
1115
  if (this.shouldUseResponsesApi(request)) {
1032
1116
  return this.completeWithResponses(request);
1033
1117
  }
1034
1118
  const client = await this.getClient();
1035
1119
  let messages;
1036
1120
  if (request.rawMessages && request.rawMessages.length > 0) {
1037
- const sanitized = request.rawMessages.map((msg) => {
1038
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1039
- return { ...msg, content: null };
1040
- }
1041
- return msg;
1042
- });
1121
+ const sanitized = this.formatRawMessages(request.rawMessages);
1043
1122
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1044
1123
  messages = [
1045
1124
  { role: "system", content: request.systemPrompt },
@@ -1054,7 +1133,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1054
1133
  request.systemPrompt
1055
1134
  );
1056
1135
  }
1057
- const tools = request.actions?.length ? formatTools(request.actions) : [];
1136
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1137
+ type: "function",
1138
+ function: {
1139
+ name: tool.name,
1140
+ description: tool.description,
1141
+ parameters: normalizeObjectJsonSchema(
1142
+ tool.inputSchema
1143
+ )
1144
+ }
1145
+ })) : formatTools(request.actions ?? []);
1058
1146
  const openaiToolOptions = request.providerToolOptions?.openai;
1059
1147
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1060
1148
  type: "function",
@@ -1069,16 +1157,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1069
1157
  tools: tools.length > 0 ? tools : void 0,
1070
1158
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1071
1159
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1072
- ...buildOpenAITokenParams(
1160
+ ...buildOpenAIParameters(
1073
1161
  modelIdForCompletePayload,
1074
1162
  request.config?.maxTokens ?? this.config.maxTokens,
1075
- request.config?.temperature ?? this.config.temperature
1163
+ request.config?.temperature ?? this.config.temperature,
1164
+ this.parameterOptions(request)
1076
1165
  ),
1077
1166
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1078
1167
  stream: false
1079
1168
  };
1080
1169
  logProviderPayload("openai", "request payload", payload, request.debug);
1081
- const response = await client.chat.completions.create(payload);
1170
+ const response = await client.chat.completions.create(payload, {
1171
+ signal: request.signal
1172
+ });
1082
1173
  logProviderPayload("openai", "response payload", response, request.debug);
1083
1174
  const choice = response.choices?.[0];
1084
1175
  const message = choice?.message;