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