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