@yourgpt/llm-sdk 2.6.0-beta.2 → 2.6.0

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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -17
  3. package/dist/adapters/index.d.mts +11 -13
  4. package/dist/adapters/index.d.ts +11 -13
  5. package/dist/adapters/index.js +406 -114
  6. package/dist/adapters/index.mjs +406 -114
  7. package/dist/audio-K-Z3-Bce.d.mts +191 -0
  8. package/dist/audio-K-Z3-Bce.d.ts +191 -0
  9. package/dist/{base-BICaQaRS.d.mts → base-6LzczuCg.d.ts} +8 -1
  10. package/dist/{base-CpRPB8u8.d.ts → base-DySbTP8C.d.mts} +8 -1
  11. package/dist/decisions-CJzOt87t.d.mts +137 -0
  12. package/dist/decisions-CJzOt87t.d.ts +137 -0
  13. package/dist/fallback/index.d.mts +4 -4
  14. package/dist/fallback/index.d.ts +4 -4
  15. package/dist/http-BQxUT5A9.d.mts +9 -0
  16. package/dist/http-BQxUT5A9.d.ts +9 -0
  17. package/dist/index.d.mts +17 -9
  18. package/dist/index.d.ts +17 -9
  19. package/dist/index.js +698 -205
  20. package/dist/index.mjs +695 -206
  21. package/dist/providers/anthropic/index.d.mts +7 -4
  22. package/dist/providers/anthropic/index.d.ts +7 -4
  23. package/dist/providers/anthropic/index.js +389 -115
  24. package/dist/providers/anthropic/index.mjs +389 -115
  25. package/dist/providers/azure/index.d.mts +3 -3
  26. package/dist/providers/azure/index.d.ts +3 -3
  27. package/dist/providers/cloudflare/index.d.mts +11 -0
  28. package/dist/providers/cloudflare/index.d.ts +11 -0
  29. package/dist/providers/cloudflare/index.js +743 -0
  30. package/dist/providers/cloudflare/index.mjs +741 -0
  31. package/dist/providers/fireworks/index.d.mts +3 -3
  32. package/dist/providers/fireworks/index.d.ts +3 -3
  33. package/dist/providers/fireworks/index.js +181 -60
  34. package/dist/providers/fireworks/index.mjs +181 -60
  35. package/dist/providers/google/index.d.mts +3 -3
  36. package/dist/providers/google/index.d.ts +3 -3
  37. package/dist/providers/google/index.js +181 -60
  38. package/dist/providers/google/index.mjs +181 -60
  39. package/dist/providers/ollama/index.d.mts +4 -4
  40. package/dist/providers/ollama/index.d.ts +4 -4
  41. package/dist/providers/openai/index.d.mts +38 -6
  42. package/dist/providers/openai/index.d.ts +38 -6
  43. package/dist/providers/openai/index.js +1451 -85
  44. package/dist/providers/openai/index.mjs +1449 -86
  45. package/dist/providers/openrouter/index.d.mts +21 -4
  46. package/dist/providers/openrouter/index.d.ts +21 -4
  47. package/dist/providers/openrouter/index.js +1040 -131
  48. package/dist/providers/openrouter/index.mjs +1040 -132
  49. package/dist/providers/togetherai/index.d.mts +3 -3
  50. package/dist/providers/togetherai/index.d.ts +3 -3
  51. package/dist/providers/togetherai/index.js +181 -60
  52. package/dist/providers/togetherai/index.mjs +181 -60
  53. package/dist/providers/typesafe/index.d.mts +8 -0
  54. package/dist/providers/typesafe/index.d.ts +8 -0
  55. package/dist/providers/typesafe/index.js +735 -0
  56. package/dist/providers/typesafe/index.mjs +733 -0
  57. package/dist/providers/xai/index.d.mts +3 -3
  58. package/dist/providers/xai/index.d.ts +3 -3
  59. package/dist/providers/xai/index.js +181 -60
  60. package/dist/providers/xai/index.mjs +181 -60
  61. package/dist/{types-DCFqkJAy.d.ts → types-B0y6VZSQ.d.ts} +1 -1
  62. package/dist/{types-DHQEMFHA.d.mts → types-BXQMtaCX.d.ts} +17 -3
  63. package/dist/{types-C3RgRbEC.d.mts → types-BY6BVhFg.d.mts} +1 -1
  64. package/dist/{types-Dikj3En4.d.ts → types-C7_qVSSd.d.mts} +17 -3
  65. package/dist/{types-Dg1Bcg1k.d.mts → types-D7lzMMLu.d.mts} +13 -1
  66. package/dist/{types-Dg1Bcg1k.d.ts → types-D7lzMMLu.d.ts} +13 -1
  67. package/dist/yourgpt/index.d.mts +1 -1
  68. package/dist/yourgpt/index.d.ts +1 -1
  69. package/docs/decisions.md +174 -0
  70. package/docs/media.md +201 -0
  71. package/docs/multimodal-and-audio.md +9 -2
  72. package/package.json +34 -12
  73. package/dist/audio-BBSwnjo8.d.mts +0 -92
  74. package/dist/audio-BBSwnjo8.d.ts +0 -92
@@ -113,6 +113,19 @@ function capabilities(model) {
113
113
  const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
114
114
  return { major, minor, reasoning, supportsNone };
115
115
  }
116
+ function filterOpenAIResponseInclude(model, include, options = {}) {
117
+ const c = capabilities(model);
118
+ let result = include;
119
+ if (options.parameterPolicy !== "passthrough" && c.reasoning && !(options.reasoningEffort === "none" && c.supportsNone) && include.includes("message.output_text.logprobs")) {
120
+ result = include.filter(
121
+ (value) => value !== "message.output_text.logprobs"
122
+ );
123
+ (options.onWarning ?? console.warn)(
124
+ `[llm-sdk] ${model}: omitted message.output_text.logprobs because reasoning is enabled.`
125
+ );
126
+ }
127
+ return result.length ? result : void 0;
128
+ }
116
129
  function requiresOpenAIResponses(model, baseUrl, options = {}) {
117
130
  if (options.parameterPolicy === "passthrough") return false;
118
131
  const c = capabilities(model);
@@ -274,6 +287,7 @@ function toOpenAIResponseFormat(rf) {
274
287
  };
275
288
  }
276
289
  function toOpenAIResponsesTextFormat(rf) {
290
+ if (rf?.type === "json_object") return { type: "json_object" };
277
291
  if (!rf || rf.type !== "json_schema") return void 0;
278
292
  return {
279
293
  type: "json_schema",
@@ -668,6 +682,63 @@ function formatMessagesForGoogle(messages) {
668
682
  });
669
683
  }
670
684
 
685
+ // src/core/provider-error.ts
686
+ function invalidProviderResponse(message) {
687
+ return Object.assign(new Error(message), {
688
+ name: "InvalidProviderResponseError",
689
+ status: 502,
690
+ code: "INVALID_PROVIDER_RESPONSE"
691
+ });
692
+ }
693
+ function providerErrorEvent(error, code) {
694
+ const details = error ?? {};
695
+ const event = {
696
+ type: "error",
697
+ message: error instanceof Error ? error.message : String(error),
698
+ code,
699
+ ...typeof details.status === "number" ? { status: details.status } : {},
700
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
701
+ ...typeof details.param === "string" ? { param: details.param } : {},
702
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
703
+ };
704
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
705
+ return event;
706
+ }
707
+ function throwIfAborted(signal) {
708
+ if (!signal?.aborted) return;
709
+ const error = new Error("The operation was aborted");
710
+ error.name = "AbortError";
711
+ throw error;
712
+ }
713
+
714
+ // src/core/tool-validation.ts
715
+ function parseToolArguments(value) {
716
+ let args = value;
717
+ if (typeof value === "string") {
718
+ try {
719
+ args = JSON.parse(value || "{}");
720
+ } catch {
721
+ throw invalidProviderResponse("Tool arguments are not valid JSON");
722
+ }
723
+ }
724
+ if (args === null || typeof args !== "object" || Array.isArray(args))
725
+ throw invalidProviderResponse("Tool arguments must be a JSON object");
726
+ return args;
727
+ }
728
+ function validateToolCalls(calls) {
729
+ const ids = /* @__PURE__ */ new Set();
730
+ for (const call of calls) {
731
+ if (!call || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || ids.has(call.id))
732
+ throw invalidProviderResponse(
733
+ "Tool calls have missing or duplicate identities"
734
+ );
735
+ ids.add(call.id);
736
+ if (typeof call.args === "string")
737
+ throw invalidProviderResponse("Parsed tool arguments must be an object");
738
+ parseToolArguments(call.args);
739
+ }
740
+ }
741
+
671
742
  // src/core/openai-responses.ts
672
743
  function resolveOpenAITransport(defaults, overrides) {
673
744
  return {
@@ -715,6 +786,54 @@ function responseMetadata(response) {
715
786
  }
716
787
  };
717
788
  }
789
+ function responseReplayMetadata(metadata) {
790
+ const response = metadata?.openai?.response;
791
+ if (!response) return metadata;
792
+ const output = response.output.map((item) => {
793
+ const fields = ["type", "id", "status"];
794
+ switch (item.type) {
795
+ case "reasoning":
796
+ fields.push("encrypted_content");
797
+ break;
798
+ case "message":
799
+ fields.push("role", "phase");
800
+ break;
801
+ case "function_call":
802
+ fields.push("call_id", "name", "namespace", "arguments");
803
+ break;
804
+ case "tool_search_call":
805
+ fields.push("call_id", "execution", "arguments");
806
+ break;
807
+ case "tool_search_output":
808
+ fields.push("call_id", "tools");
809
+ break;
810
+ case "web_search_call":
811
+ fields.push("action");
812
+ break;
813
+ default:
814
+ return stripItemDiagnostics(item);
815
+ }
816
+ const replay = Object.fromEntries(
817
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
818
+ );
819
+ if (item.type === "reasoning") replay.summary = [];
820
+ if (item.type === "message")
821
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
822
+ (part) => part?.type === "output_text" && typeof part.text === "string" ? [{ type: "output_text", text: part.text, annotations: [] }] : part?.type === "refusal" && typeof part.refusal === "string" ? [{ type: "refusal", refusal: part.refusal }] : []
823
+ ) : [];
824
+ return stripItemDiagnostics(replay);
825
+ });
826
+ return { openai: { response: { output, refusals: [] } } };
827
+ }
828
+ function stripItemDiagnostics(item) {
829
+ const {
830
+ usage: _usage,
831
+ logprobs: _logprobs,
832
+ metadata: _metadata,
833
+ ...state
834
+ } = item;
835
+ return state;
836
+ }
718
837
  function getResponseReplay(metadata) {
719
838
  const response = metadata?.openai?.response;
720
839
  if (response === void 0) return void 0;
@@ -723,29 +842,7 @@ function getResponseReplay(metadata) {
723
842
  "Invalid OpenAI Responses replay metadata: output must contain typed items"
724
843
  );
725
844
  }
726
- return response.output;
727
- }
728
-
729
- // src/core/provider-error.ts
730
- function providerErrorEvent(error, code) {
731
- const details = error ?? {};
732
- const event = {
733
- type: "error",
734
- message: error instanceof Error ? error.message : String(error),
735
- code,
736
- ...typeof details.status === "number" ? { status: details.status } : {},
737
- ...typeof details.code === "string" ? { providerCode: details.code } : {},
738
- ...typeof details.param === "string" ? { param: details.param } : {},
739
- ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
740
- };
741
- Object.defineProperty(event, "cause", { value: error, enumerable: false });
742
- return event;
743
- }
744
- function throwIfAborted(signal) {
745
- if (!signal?.aborted) return;
746
- const error = new Error("The operation was aborted");
747
- error.name = "AbortError";
748
- throw error;
845
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
749
846
  }
750
847
 
751
848
  // src/core/utils.ts
@@ -755,9 +852,6 @@ function generateId(prefix = "id") {
755
852
  function generateMessageId() {
756
853
  return generateId("msg");
757
854
  }
758
- function generateToolCallId() {
759
- return generateId("call");
760
- }
761
855
 
762
856
  // src/adapters/openai.ts
763
857
  var OpenAIAdapter = class _OpenAIAdapter {
@@ -781,6 +875,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
781
875
  apiKey: this.config.apiKey,
782
876
  baseURL: this.config.baseUrl,
783
877
  organization: this.config.organization,
878
+ project: this.config.project,
879
+ timeout: this.config.timeout,
784
880
  defaultHeaders: this.config.headers
785
881
  });
786
882
  }
@@ -903,7 +999,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
903
999
  formatRawMessages(messages, preserveProviderMetadata = false) {
904
1000
  return messages.map((message) => {
905
1001
  const { attachments, providerMetadata, modelOutput, ...rest } = message;
906
- let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
1002
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
907
1003
  if (preserveProviderMetadata && modelOutput)
908
1004
  rest.modelOutput = modelOutput;
909
1005
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
@@ -1006,17 +1102,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
1006
1102
  const seenCallIds = /* @__PURE__ */ new Set();
1007
1103
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1008
1104
  if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1009
- throw new Error("Responses contains an incomplete function call");
1105
+ throw invalidProviderResponse(
1106
+ "Responses contains an incomplete function call"
1107
+ );
1010
1108
  }
1011
1109
  if (seenCallIds.has(item.call_id))
1012
- throw new Error("Responses contains duplicate function call IDs");
1110
+ throw invalidProviderResponse(
1111
+ "Responses contains duplicate function call IDs"
1112
+ );
1013
1113
  seenCallIds.add(item.call_id);
1014
- const args = JSON.parse(item.arguments);
1114
+ let args;
1115
+ try {
1116
+ args = JSON.parse(item.arguments);
1117
+ } catch {
1118
+ throw invalidProviderResponse(
1119
+ "Responses function arguments must be valid JSON"
1120
+ );
1121
+ }
1015
1122
  if (!args || typeof args !== "object" || Array.isArray(args))
1016
- throw new Error(
1123
+ throw invalidProviderResponse(
1017
1124
  "Responses function arguments must be a JSON object"
1018
1125
  );
1019
- return { id: item.call_id, name: item.name, args };
1126
+ return {
1127
+ id: item.call_id,
1128
+ name: item.name,
1129
+ args
1130
+ };
1020
1131
  }) : [];
1021
1132
  return {
1022
1133
  content,
@@ -1047,6 +1158,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1047
1158
  request.config?.responseFormat
1048
1159
  );
1049
1160
  const modelIdForPayload = request.config?.model || this.model;
1161
+ const include = filterOpenAIResponseInclude(
1162
+ modelIdForPayload,
1163
+ [
1164
+ .../* @__PURE__ */ new Set([
1165
+ ...options?.include ?? [],
1166
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1167
+ ])
1168
+ ],
1169
+ this.parameterOptions(request)
1170
+ );
1050
1171
  const payload = {
1051
1172
  model: modelIdForPayload,
1052
1173
  instructions: request.systemPrompt,
@@ -1070,14 +1191,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
1070
1191
  ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1071
1192
  ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1072
1193
  ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1073
- ...options?.store === false || options?.include ? {
1074
- include: [
1075
- .../* @__PURE__ */ new Set([
1076
- ...options?.include ?? [],
1077
- ...options?.store === false ? ["reasoning.encrypted_content"] : []
1078
- ])
1079
- ]
1080
- } : {},
1194
+ ...include ? { include } : {},
1081
1195
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
1082
1196
  stream
1083
1197
  };
@@ -1259,7 +1373,9 @@ var OpenAIAdapter = class _OpenAIAdapter {
1259
1373
  const uniqueCalls = /* @__PURE__ */ new Map();
1260
1374
  for (const call of toolCalls.values()) {
1261
1375
  if (!call.id || !call.name)
1262
- throw new Error("Responses stream contains an incomplete tool call");
1376
+ throw invalidProviderResponse(
1377
+ "Responses stream contains an incomplete tool call"
1378
+ );
1263
1379
  uniqueCalls.set(call.id, call);
1264
1380
  }
1265
1381
  toolCalls.clear();
@@ -1446,15 +1562,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1446
1562
  }
1447
1563
  throwIfAborted(request.signal);
1448
1564
  if (!finishReason)
1449
- throw new Error("OpenAI stream ended without a finish reason");
1565
+ throw invalidProviderResponse(
1566
+ "OpenAI stream ended without a finish reason"
1567
+ );
1450
1568
  if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1451
- throw new Error("OpenAI stream ended with incomplete tool calls");
1452
- }
1453
- for (const call of toolCalls.values()) {
1454
- if (!call.id || !call.name)
1455
- throw new Error("OpenAI stream contains an incomplete tool call");
1456
- JSON.parse(call.arguments || "{}");
1569
+ throw invalidProviderResponse(
1570
+ "OpenAI stream ended with incomplete tool calls"
1571
+ );
1457
1572
  }
1573
+ validateToolCalls(
1574
+ [...toolCalls.values()].map((call) => ({
1575
+ ...call,
1576
+ args: parseToolArguments(call.arguments)
1577
+ }))
1578
+ );
1458
1579
  for (const call of toolCalls.values()) {
1459
1580
  yield {
1460
1581
  type: "action:start",
@@ -1538,20 +1659,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1538
1659
  logProviderPayload("openai", "response payload", response, request.debug);
1539
1660
  const choice = response.choices?.[0];
1540
1661
  const message = choice?.message;
1662
+ const toolCalls = (message?.tool_calls ?? []).map((toolCall) => ({
1663
+ id: toolCall.id,
1664
+ name: toolCall.function?.name,
1665
+ args: parseToolArguments(toolCall.function?.arguments ?? "{}"),
1666
+ ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {}
1667
+ }));
1668
+ validateToolCalls(toolCalls);
1669
+ if (toolCalls.length && choice?.finish_reason && !["tool_calls", "function_call"].includes(choice.finish_reason))
1670
+ throw invalidProviderResponse(
1671
+ "OpenAI response ended with incomplete tool calls"
1672
+ );
1541
1673
  return {
1542
1674
  content: message?.content ?? "",
1543
- toolCalls: message?.tool_calls?.map((toolCall) => ({
1544
- id: toolCall.id ?? generateToolCallId(),
1545
- name: toolCall.function?.name ?? "",
1546
- args: (() => {
1547
- try {
1548
- return JSON.parse(toolCall.function?.arguments ?? "{}");
1549
- } catch {
1550
- return {};
1551
- }
1552
- })(),
1553
- ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {}
1554
- })) ?? [],
1675
+ toolCalls,
1555
1676
  usage: response.usage ? {
1556
1677
  promptTokens: response.usage.prompt_tokens,
1557
1678
  completionTokens: response.usage.completion_tokens,
@@ -111,6 +111,19 @@ function capabilities(model) {
111
111
  const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
112
112
  return { major, minor, reasoning, supportsNone };
113
113
  }
114
+ function filterOpenAIResponseInclude(model, include, options = {}) {
115
+ const c = capabilities(model);
116
+ let result = include;
117
+ if (options.parameterPolicy !== "passthrough" && c.reasoning && !(options.reasoningEffort === "none" && c.supportsNone) && include.includes("message.output_text.logprobs")) {
118
+ result = include.filter(
119
+ (value) => value !== "message.output_text.logprobs"
120
+ );
121
+ (options.onWarning ?? console.warn)(
122
+ `[llm-sdk] ${model}: omitted message.output_text.logprobs because reasoning is enabled.`
123
+ );
124
+ }
125
+ return result.length ? result : void 0;
126
+ }
114
127
  function requiresOpenAIResponses(model, baseUrl, options = {}) {
115
128
  if (options.parameterPolicy === "passthrough") return false;
116
129
  const c = capabilities(model);
@@ -272,6 +285,7 @@ function toOpenAIResponseFormat(rf) {
272
285
  };
273
286
  }
274
287
  function toOpenAIResponsesTextFormat(rf) {
288
+ if (rf?.type === "json_object") return { type: "json_object" };
275
289
  if (!rf || rf.type !== "json_schema") return void 0;
276
290
  return {
277
291
  type: "json_schema",
@@ -666,6 +680,63 @@ function formatMessagesForGoogle(messages) {
666
680
  });
667
681
  }
668
682
 
683
+ // src/core/provider-error.ts
684
+ function invalidProviderResponse(message) {
685
+ return Object.assign(new Error(message), {
686
+ name: "InvalidProviderResponseError",
687
+ status: 502,
688
+ code: "INVALID_PROVIDER_RESPONSE"
689
+ });
690
+ }
691
+ function providerErrorEvent(error, code) {
692
+ const details = error ?? {};
693
+ const event = {
694
+ type: "error",
695
+ message: error instanceof Error ? error.message : String(error),
696
+ code,
697
+ ...typeof details.status === "number" ? { status: details.status } : {},
698
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
699
+ ...typeof details.param === "string" ? { param: details.param } : {},
700
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
701
+ };
702
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
703
+ return event;
704
+ }
705
+ function throwIfAborted(signal) {
706
+ if (!signal?.aborted) return;
707
+ const error = new Error("The operation was aborted");
708
+ error.name = "AbortError";
709
+ throw error;
710
+ }
711
+
712
+ // src/core/tool-validation.ts
713
+ function parseToolArguments(value) {
714
+ let args = value;
715
+ if (typeof value === "string") {
716
+ try {
717
+ args = JSON.parse(value || "{}");
718
+ } catch {
719
+ throw invalidProviderResponse("Tool arguments are not valid JSON");
720
+ }
721
+ }
722
+ if (args === null || typeof args !== "object" || Array.isArray(args))
723
+ throw invalidProviderResponse("Tool arguments must be a JSON object");
724
+ return args;
725
+ }
726
+ function validateToolCalls(calls) {
727
+ const ids = /* @__PURE__ */ new Set();
728
+ for (const call of calls) {
729
+ if (!call || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || ids.has(call.id))
730
+ throw invalidProviderResponse(
731
+ "Tool calls have missing or duplicate identities"
732
+ );
733
+ ids.add(call.id);
734
+ if (typeof call.args === "string")
735
+ throw invalidProviderResponse("Parsed tool arguments must be an object");
736
+ parseToolArguments(call.args);
737
+ }
738
+ }
739
+
669
740
  // src/core/openai-responses.ts
670
741
  function resolveOpenAITransport(defaults, overrides) {
671
742
  return {
@@ -713,6 +784,54 @@ function responseMetadata(response) {
713
784
  }
714
785
  };
715
786
  }
787
+ function responseReplayMetadata(metadata) {
788
+ const response = metadata?.openai?.response;
789
+ if (!response) return metadata;
790
+ const output = response.output.map((item) => {
791
+ const fields = ["type", "id", "status"];
792
+ switch (item.type) {
793
+ case "reasoning":
794
+ fields.push("encrypted_content");
795
+ break;
796
+ case "message":
797
+ fields.push("role", "phase");
798
+ break;
799
+ case "function_call":
800
+ fields.push("call_id", "name", "namespace", "arguments");
801
+ break;
802
+ case "tool_search_call":
803
+ fields.push("call_id", "execution", "arguments");
804
+ break;
805
+ case "tool_search_output":
806
+ fields.push("call_id", "tools");
807
+ break;
808
+ case "web_search_call":
809
+ fields.push("action");
810
+ break;
811
+ default:
812
+ return stripItemDiagnostics(item);
813
+ }
814
+ const replay = Object.fromEntries(
815
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
816
+ );
817
+ if (item.type === "reasoning") replay.summary = [];
818
+ if (item.type === "message")
819
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
820
+ (part) => part?.type === "output_text" && typeof part.text === "string" ? [{ type: "output_text", text: part.text, annotations: [] }] : part?.type === "refusal" && typeof part.refusal === "string" ? [{ type: "refusal", refusal: part.refusal }] : []
821
+ ) : [];
822
+ return stripItemDiagnostics(replay);
823
+ });
824
+ return { openai: { response: { output, refusals: [] } } };
825
+ }
826
+ function stripItemDiagnostics(item) {
827
+ const {
828
+ usage: _usage,
829
+ logprobs: _logprobs,
830
+ metadata: _metadata,
831
+ ...state
832
+ } = item;
833
+ return state;
834
+ }
716
835
  function getResponseReplay(metadata) {
717
836
  const response = metadata?.openai?.response;
718
837
  if (response === void 0) return void 0;
@@ -721,29 +840,7 @@ function getResponseReplay(metadata) {
721
840
  "Invalid OpenAI Responses replay metadata: output must contain typed items"
722
841
  );
723
842
  }
724
- return response.output;
725
- }
726
-
727
- // src/core/provider-error.ts
728
- function providerErrorEvent(error, code) {
729
- const details = error ?? {};
730
- const event = {
731
- type: "error",
732
- message: error instanceof Error ? error.message : String(error),
733
- code,
734
- ...typeof details.status === "number" ? { status: details.status } : {},
735
- ...typeof details.code === "string" ? { providerCode: details.code } : {},
736
- ...typeof details.param === "string" ? { param: details.param } : {},
737
- ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
738
- };
739
- Object.defineProperty(event, "cause", { value: error, enumerable: false });
740
- return event;
741
- }
742
- function throwIfAborted(signal) {
743
- if (!signal?.aborted) return;
744
- const error = new Error("The operation was aborted");
745
- error.name = "AbortError";
746
- throw error;
843
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
747
844
  }
748
845
 
749
846
  // src/core/utils.ts
@@ -753,9 +850,6 @@ function generateId(prefix = "id") {
753
850
  function generateMessageId() {
754
851
  return generateId("msg");
755
852
  }
756
- function generateToolCallId() {
757
- return generateId("call");
758
- }
759
853
 
760
854
  // src/adapters/openai.ts
761
855
  var OpenAIAdapter = class _OpenAIAdapter {
@@ -779,6 +873,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
779
873
  apiKey: this.config.apiKey,
780
874
  baseURL: this.config.baseUrl,
781
875
  organization: this.config.organization,
876
+ project: this.config.project,
877
+ timeout: this.config.timeout,
782
878
  defaultHeaders: this.config.headers
783
879
  });
784
880
  }
@@ -901,7 +997,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
901
997
  formatRawMessages(messages, preserveProviderMetadata = false) {
902
998
  return messages.map((message) => {
903
999
  const { attachments, providerMetadata, modelOutput, ...rest } = message;
904
- let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
1000
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
905
1001
  if (preserveProviderMetadata && modelOutput)
906
1002
  rest.modelOutput = modelOutput;
907
1003
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
@@ -1004,17 +1100,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
1004
1100
  const seenCallIds = /* @__PURE__ */ new Set();
1005
1101
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1006
1102
  if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1007
- throw new Error("Responses contains an incomplete function call");
1103
+ throw invalidProviderResponse(
1104
+ "Responses contains an incomplete function call"
1105
+ );
1008
1106
  }
1009
1107
  if (seenCallIds.has(item.call_id))
1010
- throw new Error("Responses contains duplicate function call IDs");
1108
+ throw invalidProviderResponse(
1109
+ "Responses contains duplicate function call IDs"
1110
+ );
1011
1111
  seenCallIds.add(item.call_id);
1012
- const args = JSON.parse(item.arguments);
1112
+ let args;
1113
+ try {
1114
+ args = JSON.parse(item.arguments);
1115
+ } catch {
1116
+ throw invalidProviderResponse(
1117
+ "Responses function arguments must be valid JSON"
1118
+ );
1119
+ }
1013
1120
  if (!args || typeof args !== "object" || Array.isArray(args))
1014
- throw new Error(
1121
+ throw invalidProviderResponse(
1015
1122
  "Responses function arguments must be a JSON object"
1016
1123
  );
1017
- return { id: item.call_id, name: item.name, args };
1124
+ return {
1125
+ id: item.call_id,
1126
+ name: item.name,
1127
+ args
1128
+ };
1018
1129
  }) : [];
1019
1130
  return {
1020
1131
  content,
@@ -1045,6 +1156,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1045
1156
  request.config?.responseFormat
1046
1157
  );
1047
1158
  const modelIdForPayload = request.config?.model || this.model;
1159
+ const include = filterOpenAIResponseInclude(
1160
+ modelIdForPayload,
1161
+ [
1162
+ .../* @__PURE__ */ new Set([
1163
+ ...options?.include ?? [],
1164
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1165
+ ])
1166
+ ],
1167
+ this.parameterOptions(request)
1168
+ );
1048
1169
  const payload = {
1049
1170
  model: modelIdForPayload,
1050
1171
  instructions: request.systemPrompt,
@@ -1068,14 +1189,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
1068
1189
  ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1069
1190
  ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1070
1191
  ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1071
- ...options?.store === false || options?.include ? {
1072
- include: [
1073
- .../* @__PURE__ */ new Set([
1074
- ...options?.include ?? [],
1075
- ...options?.store === false ? ["reasoning.encrypted_content"] : []
1076
- ])
1077
- ]
1078
- } : {},
1192
+ ...include ? { include } : {},
1079
1193
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
1080
1194
  stream
1081
1195
  };
@@ -1257,7 +1371,9 @@ var OpenAIAdapter = class _OpenAIAdapter {
1257
1371
  const uniqueCalls = /* @__PURE__ */ new Map();
1258
1372
  for (const call of toolCalls.values()) {
1259
1373
  if (!call.id || !call.name)
1260
- throw new Error("Responses stream contains an incomplete tool call");
1374
+ throw invalidProviderResponse(
1375
+ "Responses stream contains an incomplete tool call"
1376
+ );
1261
1377
  uniqueCalls.set(call.id, call);
1262
1378
  }
1263
1379
  toolCalls.clear();
@@ -1444,15 +1560,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1444
1560
  }
1445
1561
  throwIfAborted(request.signal);
1446
1562
  if (!finishReason)
1447
- throw new Error("OpenAI stream ended without a finish reason");
1563
+ throw invalidProviderResponse(
1564
+ "OpenAI stream ended without a finish reason"
1565
+ );
1448
1566
  if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1449
- throw new Error("OpenAI stream ended with incomplete tool calls");
1450
- }
1451
- for (const call of toolCalls.values()) {
1452
- if (!call.id || !call.name)
1453
- throw new Error("OpenAI stream contains an incomplete tool call");
1454
- JSON.parse(call.arguments || "{}");
1567
+ throw invalidProviderResponse(
1568
+ "OpenAI stream ended with incomplete tool calls"
1569
+ );
1455
1570
  }
1571
+ validateToolCalls(
1572
+ [...toolCalls.values()].map((call) => ({
1573
+ ...call,
1574
+ args: parseToolArguments(call.arguments)
1575
+ }))
1576
+ );
1456
1577
  for (const call of toolCalls.values()) {
1457
1578
  yield {
1458
1579
  type: "action:start",
@@ -1536,20 +1657,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1536
1657
  logProviderPayload("openai", "response payload", response, request.debug);
1537
1658
  const choice = response.choices?.[0];
1538
1659
  const message = choice?.message;
1660
+ const toolCalls = (message?.tool_calls ?? []).map((toolCall) => ({
1661
+ id: toolCall.id,
1662
+ name: toolCall.function?.name,
1663
+ args: parseToolArguments(toolCall.function?.arguments ?? "{}"),
1664
+ ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {}
1665
+ }));
1666
+ validateToolCalls(toolCalls);
1667
+ if (toolCalls.length && choice?.finish_reason && !["tool_calls", "function_call"].includes(choice.finish_reason))
1668
+ throw invalidProviderResponse(
1669
+ "OpenAI response ended with incomplete tool calls"
1670
+ );
1539
1671
  return {
1540
1672
  content: message?.content ?? "",
1541
- toolCalls: message?.tool_calls?.map((toolCall) => ({
1542
- id: toolCall.id ?? generateToolCallId(),
1543
- name: toolCall.function?.name ?? "",
1544
- args: (() => {
1545
- try {
1546
- return JSON.parse(toolCall.function?.arguments ?? "{}");
1547
- } catch {
1548
- return {};
1549
- }
1550
- })(),
1551
- ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {}
1552
- })) ?? [],
1673
+ toolCalls,
1553
1674
  usage: response.usage ? {
1554
1675
  promptTokens: response.usage.prompt_tokens,
1555
1676
  completionTokens: response.usage.completion_tokens,