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