@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
@@ -101,6 +101,63 @@ function textToolOutput(value, fallback) {
101
101
  return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
102
102
  }
103
103
 
104
+ // src/core/anthropic-replay.ts
105
+ function anthropicReplay(content) {
106
+ if (!Array.isArray(content) || !content.some(
107
+ (b) => b?.type === "thinking" || b?.type === "redacted_thinking"
108
+ ))
109
+ return void 0;
110
+ const blocks = content.flatMap((block) => {
111
+ if (block?.type === "thinking" && typeof block.thinking === "string" && typeof block.signature === "string")
112
+ return [
113
+ {
114
+ type: "thinking",
115
+ thinking: block.thinking,
116
+ signature: block.signature
117
+ }
118
+ ];
119
+ if (block?.type === "redacted_thinking" && typeof block.data === "string")
120
+ return [{ type: "redacted_thinking", data: block.data }];
121
+ if (block?.type === "text" && typeof block.text === "string")
122
+ return [{ type: "text", text: block.text }];
123
+ if (block?.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string")
124
+ return [
125
+ {
126
+ type: "tool_use",
127
+ id: block.id,
128
+ name: block.name,
129
+ input: block.input
130
+ }
131
+ ];
132
+ return [];
133
+ });
134
+ return { anthropic: { content: blocks } };
135
+ }
136
+ function anthropicReplayContent(metadata) {
137
+ const state = metadata?.anthropic;
138
+ return anthropicReplay(state?.content)?.anthropic?.content;
139
+ }
140
+ function collectAnthropicContent(blocks, event) {
141
+ if (event.type === "content_block_start")
142
+ blocks[event.index] = { ...event.content_block };
143
+ const block = blocks[event.index];
144
+ if (event.type !== "content_block_delta" || !block) return;
145
+ const delta = event.delta;
146
+ if (delta.type === "thinking_delta")
147
+ block.thinking = (block.thinking ?? "") + delta.thinking;
148
+ else if (delta.type === "signature_delta")
149
+ block.signature = (block.signature ?? "") + delta.signature;
150
+ else if (delta.type === "text_delta")
151
+ block.text = (block.text ?? "") + delta.text;
152
+ else if (delta.type === "input_json_delta") {
153
+ block.partialInput = (block.partialInput ?? "") + delta.partial_json;
154
+ try {
155
+ block.input = JSON.parse(block.partialInput);
156
+ } catch {
157
+ }
158
+ }
159
+ }
160
+
104
161
  // src/core/openai-parameters.ts
105
162
  function capabilities(model) {
106
163
  const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
@@ -111,6 +168,19 @@ function capabilities(model) {
111
168
  const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
112
169
  return { major, minor, reasoning, supportsNone };
113
170
  }
171
+ function filterOpenAIResponseInclude(model, include, options = {}) {
172
+ const c = capabilities(model);
173
+ let result = include;
174
+ if (options.parameterPolicy !== "passthrough" && c.reasoning && !(options.reasoningEffort === "none" && c.supportsNone) && include.includes("message.output_text.logprobs")) {
175
+ result = include.filter(
176
+ (value) => value !== "message.output_text.logprobs"
177
+ );
178
+ (options.onWarning ?? console.warn)(
179
+ `[llm-sdk] ${model}: omitted message.output_text.logprobs because reasoning is enabled.`
180
+ );
181
+ }
182
+ return result.length ? result : void 0;
183
+ }
114
184
  function requiresOpenAIResponses(model, baseUrl, options = {}) {
115
185
  if (options.parameterPolicy === "passthrough") return false;
116
186
  const c = capabilities(model);
@@ -282,7 +352,22 @@ function stripSchemaKeys(schema, keysToDrop, options = {}) {
282
352
  )) {
283
353
  if (keysToDrop.has(key)) continue;
284
354
  const renamed = options.renameKeys?.[key] ?? key;
285
- out[renamed] = stripSchemaKeys(value, keysToDrop, options);
355
+ if ([
356
+ "properties",
357
+ "$defs",
358
+ "definitions",
359
+ "patternProperties",
360
+ "dependentSchemas"
361
+ ].includes(key) && value && typeof value === "object" && !Array.isArray(value)) {
362
+ out[renamed] = Object.fromEntries(
363
+ Object.entries(value).map(([name, child]) => [
364
+ name,
365
+ stripSchemaKeys(child, keysToDrop, options)
366
+ ])
367
+ );
368
+ } else if (["enum", "const", "default", "examples"].includes(key)) {
369
+ out[renamed] = value;
370
+ } else out[renamed] = stripSchemaKeys(value, keysToDrop, options);
286
371
  }
287
372
  if (options.forceAdditionalPropertiesFalse && out.type === "object") {
288
373
  out.additionalProperties = false;
@@ -303,6 +388,7 @@ function toOpenAIResponseFormat(rf) {
303
388
  };
304
389
  }
305
390
  function toOpenAIResponsesTextFormat(rf) {
391
+ if (rf?.type === "json_object") return { type: "json_object" };
306
392
  if (!rf || rf.type !== "json_schema") return void 0;
307
393
  return {
308
394
  type: "json_schema",
@@ -524,21 +610,14 @@ function messageToOpenAIContent(message) {
524
610
  return blocks;
525
611
  }
526
612
  function extractAnthropicSystemPrompt(messages, systemPrompt) {
527
- const parts = systemPrompt ? [systemPrompt] : [];
613
+ const explicit = typeof systemPrompt === "string" ? systemPrompt : "";
614
+ const parts = explicit ? [explicit] : [];
528
615
  for (const message of messages) {
529
616
  if (message.role !== "system") continue;
530
- if (typeof message.content === "string") {
531
- if (message.content) parts.push(message.content);
532
- } else if (Array.isArray(message.content)) {
533
- for (const block of message.content) {
534
- if (block?.type !== "text" || typeof block.text !== "string") {
535
- throw new TypeError("Anthropic system messages must contain text.");
536
- }
537
- if (block.text) parts.push(block.text);
538
- }
539
- } else {
540
- throw new TypeError("Anthropic system messages must contain text.");
541
- }
617
+ const text = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.filter(
618
+ (block) => block?.type === "text" && typeof block.text === "string"
619
+ ).map((block) => block.text).filter(Boolean).join("\n") : "";
620
+ if (text && text !== explicit) parts.push(text);
542
621
  }
543
622
  return parts.join("\n");
544
623
  }
@@ -549,6 +628,14 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
549
628
  const msg = messages[i];
550
629
  if (msg.role === "system") continue;
551
630
  if (msg.role === "assistant") {
631
+ const replay = anthropicReplayContent(msg.providerMetadata);
632
+ if (replay) {
633
+ formatted.push({
634
+ role: "assistant",
635
+ content: replay
636
+ });
637
+ continue;
638
+ }
552
639
  const content = [];
553
640
  if (msg.content) {
554
641
  content.push({ type: "text", text: msg.content });
@@ -639,6 +726,63 @@ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadat
639
726
  return formatted;
640
727
  }
641
728
 
729
+ // src/core/provider-error.ts
730
+ function invalidProviderResponse(message) {
731
+ return Object.assign(new Error(message), {
732
+ name: "InvalidProviderResponseError",
733
+ status: 502,
734
+ code: "INVALID_PROVIDER_RESPONSE"
735
+ });
736
+ }
737
+ function providerErrorEvent(error, code) {
738
+ const details = error ?? {};
739
+ const event = {
740
+ type: "error",
741
+ message: error instanceof Error ? error.message : String(error),
742
+ code,
743
+ ...typeof details.status === "number" ? { status: details.status } : {},
744
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
745
+ ...typeof details.param === "string" ? { param: details.param } : {},
746
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
747
+ };
748
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
749
+ return event;
750
+ }
751
+ function throwIfAborted(signal) {
752
+ if (!signal?.aborted) return;
753
+ const error = new Error("The operation was aborted");
754
+ error.name = "AbortError";
755
+ throw error;
756
+ }
757
+
758
+ // src/core/tool-validation.ts
759
+ function parseToolArguments(value) {
760
+ let args = value;
761
+ if (typeof value === "string") {
762
+ try {
763
+ args = JSON.parse(value || "{}");
764
+ } catch {
765
+ throw invalidProviderResponse("Tool arguments are not valid JSON");
766
+ }
767
+ }
768
+ if (args === null || typeof args !== "object" || Array.isArray(args))
769
+ throw invalidProviderResponse("Tool arguments must be a JSON object");
770
+ return args;
771
+ }
772
+ function validateToolCalls(calls) {
773
+ const ids = /* @__PURE__ */ new Set();
774
+ for (const call of calls) {
775
+ if (!call || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || ids.has(call.id))
776
+ throw invalidProviderResponse(
777
+ "Tool calls have missing or duplicate identities"
778
+ );
779
+ ids.add(call.id);
780
+ if (typeof call.args === "string")
781
+ throw invalidProviderResponse("Parsed tool arguments must be an object");
782
+ parseToolArguments(call.args);
783
+ }
784
+ }
785
+
642
786
  // src/core/openai-responses.ts
643
787
  function resolveOpenAITransport(defaults, overrides) {
644
788
  return {
@@ -686,6 +830,54 @@ function responseMetadata(response) {
686
830
  }
687
831
  };
688
832
  }
833
+ function responseReplayMetadata(metadata) {
834
+ const response = metadata?.openai?.response;
835
+ if (!response) return metadata;
836
+ const output = response.output.map((item) => {
837
+ const fields = ["type", "id", "status"];
838
+ switch (item.type) {
839
+ case "reasoning":
840
+ fields.push("encrypted_content");
841
+ break;
842
+ case "message":
843
+ fields.push("role", "phase");
844
+ break;
845
+ case "function_call":
846
+ fields.push("call_id", "name", "namespace", "arguments");
847
+ break;
848
+ case "tool_search_call":
849
+ fields.push("call_id", "execution", "arguments");
850
+ break;
851
+ case "tool_search_output":
852
+ fields.push("call_id", "tools");
853
+ break;
854
+ case "web_search_call":
855
+ fields.push("action");
856
+ break;
857
+ default:
858
+ return stripItemDiagnostics(item);
859
+ }
860
+ const replay = Object.fromEntries(
861
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
862
+ );
863
+ if (item.type === "reasoning") replay.summary = [];
864
+ if (item.type === "message")
865
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
866
+ (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 }] : []
867
+ ) : [];
868
+ return stripItemDiagnostics(replay);
869
+ });
870
+ return { openai: { response: { output, refusals: [] } } };
871
+ }
872
+ function stripItemDiagnostics(item) {
873
+ const {
874
+ usage: _usage,
875
+ logprobs: _logprobs,
876
+ metadata: _metadata,
877
+ ...state
878
+ } = item;
879
+ return state;
880
+ }
689
881
  function getResponseReplay(metadata) {
690
882
  const response = metadata?.openai?.response;
691
883
  if (response === void 0) return void 0;
@@ -694,29 +886,7 @@ function getResponseReplay(metadata) {
694
886
  "Invalid OpenAI Responses replay metadata: output must contain typed items"
695
887
  );
696
888
  }
697
- return response.output;
698
- }
699
-
700
- // src/core/provider-error.ts
701
- function providerErrorEvent(error, code) {
702
- const details = error ?? {};
703
- const event = {
704
- type: "error",
705
- message: error instanceof Error ? error.message : String(error),
706
- code,
707
- ...typeof details.status === "number" ? { status: details.status } : {},
708
- ...typeof details.code === "string" ? { providerCode: details.code } : {},
709
- ...typeof details.param === "string" ? { param: details.param } : {},
710
- ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
711
- };
712
- Object.defineProperty(event, "cause", { value: error, enumerable: false });
713
- return event;
714
- }
715
- function throwIfAborted(signal) {
716
- if (!signal?.aborted) return;
717
- const error = new Error("The operation was aborted");
718
- error.name = "AbortError";
719
- throw error;
889
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
720
890
  }
721
891
 
722
892
  // src/core/utils.ts
@@ -752,6 +922,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
752
922
  apiKey: this.config.apiKey,
753
923
  baseURL: this.config.baseUrl,
754
924
  organization: this.config.organization,
925
+ project: this.config.project,
926
+ timeout: this.config.timeout,
755
927
  defaultHeaders: this.config.headers
756
928
  });
757
929
  }
@@ -874,7 +1046,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
874
1046
  formatRawMessages(messages, preserveProviderMetadata = false) {
875
1047
  return messages.map((message) => {
876
1048
  const { attachments, providerMetadata, modelOutput, ...rest } = message;
877
- let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
1049
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
878
1050
  if (preserveProviderMetadata && modelOutput)
879
1051
  rest.modelOutput = modelOutput;
880
1052
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
@@ -977,17 +1149,32 @@ var OpenAIAdapter = class _OpenAIAdapter {
977
1149
  const seenCallIds = /* @__PURE__ */ new Set();
978
1150
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
979
1151
  if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
980
- throw new Error("Responses contains an incomplete function call");
1152
+ throw invalidProviderResponse(
1153
+ "Responses contains an incomplete function call"
1154
+ );
981
1155
  }
982
1156
  if (seenCallIds.has(item.call_id))
983
- throw new Error("Responses contains duplicate function call IDs");
1157
+ throw invalidProviderResponse(
1158
+ "Responses contains duplicate function call IDs"
1159
+ );
984
1160
  seenCallIds.add(item.call_id);
985
- const args = JSON.parse(item.arguments);
1161
+ let args;
1162
+ try {
1163
+ args = JSON.parse(item.arguments);
1164
+ } catch {
1165
+ throw invalidProviderResponse(
1166
+ "Responses function arguments must be valid JSON"
1167
+ );
1168
+ }
986
1169
  if (!args || typeof args !== "object" || Array.isArray(args))
987
- throw new Error(
1170
+ throw invalidProviderResponse(
988
1171
  "Responses function arguments must be a JSON object"
989
1172
  );
990
- return { id: item.call_id, name: item.name, args };
1173
+ return {
1174
+ id: item.call_id,
1175
+ name: item.name,
1176
+ args
1177
+ };
991
1178
  }) : [];
992
1179
  return {
993
1180
  content,
@@ -1018,6 +1205,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1018
1205
  request.config?.responseFormat
1019
1206
  );
1020
1207
  const modelIdForPayload = request.config?.model || this.model;
1208
+ const include = filterOpenAIResponseInclude(
1209
+ modelIdForPayload,
1210
+ [
1211
+ .../* @__PURE__ */ new Set([
1212
+ ...options?.include ?? [],
1213
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1214
+ ])
1215
+ ],
1216
+ this.parameterOptions(request)
1217
+ );
1021
1218
  const payload = {
1022
1219
  model: modelIdForPayload,
1023
1220
  instructions: request.systemPrompt,
@@ -1041,14 +1238,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
1041
1238
  ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1042
1239
  ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1043
1240
  ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1044
- ...options?.store === false || options?.include ? {
1045
- include: [
1046
- .../* @__PURE__ */ new Set([
1047
- ...options?.include ?? [],
1048
- ...options?.store === false ? ["reasoning.encrypted_content"] : []
1049
- ])
1050
- ]
1051
- } : {},
1241
+ ...include ? { include } : {},
1052
1242
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
1053
1243
  stream
1054
1244
  };
@@ -1230,7 +1420,9 @@ var OpenAIAdapter = class _OpenAIAdapter {
1230
1420
  const uniqueCalls = /* @__PURE__ */ new Map();
1231
1421
  for (const call of toolCalls.values()) {
1232
1422
  if (!call.id || !call.name)
1233
- throw new Error("Responses stream contains an incomplete tool call");
1423
+ throw invalidProviderResponse(
1424
+ "Responses stream contains an incomplete tool call"
1425
+ );
1234
1426
  uniqueCalls.set(call.id, call);
1235
1427
  }
1236
1428
  toolCalls.clear();
@@ -1417,15 +1609,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1417
1609
  }
1418
1610
  throwIfAborted(request.signal);
1419
1611
  if (!finishReason)
1420
- throw new Error("OpenAI stream ended without a finish reason");
1612
+ throw invalidProviderResponse(
1613
+ "OpenAI stream ended without a finish reason"
1614
+ );
1421
1615
  if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
1422
- throw new Error("OpenAI stream ended with incomplete tool calls");
1423
- }
1424
- for (const call of toolCalls.values()) {
1425
- if (!call.id || !call.name)
1426
- throw new Error("OpenAI stream contains an incomplete tool call");
1427
- JSON.parse(call.arguments || "{}");
1616
+ throw invalidProviderResponse(
1617
+ "OpenAI stream ended with incomplete tool calls"
1618
+ );
1428
1619
  }
1620
+ validateToolCalls(
1621
+ [...toolCalls.values()].map((call) => ({
1622
+ ...call,
1623
+ args: parseToolArguments(call.arguments)
1624
+ }))
1625
+ );
1429
1626
  for (const call of toolCalls.values()) {
1430
1627
  yield {
1431
1628
  type: "action:start",
@@ -1509,20 +1706,20 @@ var OpenAIAdapter = class _OpenAIAdapter {
1509
1706
  logProviderPayload("openai", "response payload", response, request.debug);
1510
1707
  const choice = response.choices?.[0];
1511
1708
  const message = choice?.message;
1709
+ const toolCalls = (message?.tool_calls ?? []).map((toolCall) => ({
1710
+ id: toolCall.id,
1711
+ name: toolCall.function?.name,
1712
+ args: parseToolArguments(toolCall.function?.arguments ?? "{}"),
1713
+ ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {}
1714
+ }));
1715
+ validateToolCalls(toolCalls);
1716
+ if (toolCalls.length && choice?.finish_reason && !["tool_calls", "function_call"].includes(choice.finish_reason))
1717
+ throw invalidProviderResponse(
1718
+ "OpenAI response ended with incomplete tool calls"
1719
+ );
1512
1720
  return {
1513
1721
  content: message?.content ?? "",
1514
- toolCalls: message?.tool_calls?.map((toolCall) => ({
1515
- id: toolCall.id ?? generateToolCallId(),
1516
- name: toolCall.function?.name ?? "",
1517
- args: (() => {
1518
- try {
1519
- return JSON.parse(toolCall.function?.arguments ?? "{}");
1520
- } catch {
1521
- return {};
1522
- }
1523
- })(),
1524
- ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {}
1525
- })) ?? [],
1722
+ toolCalls,
1526
1723
  usage: response.usage ? {
1527
1724
  promptTokens: response.usage.prompt_tokens,
1528
1725
  completionTokens: response.usage.completion_tokens,
@@ -1555,6 +1752,52 @@ function createOpenAIAdapter(config) {
1555
1752
  return new OpenAIAdapter(config);
1556
1753
  }
1557
1754
 
1755
+ // src/core/anthropic-parameters.ts
1756
+ function anthropicMaxTokens(maxTokens, thinking) {
1757
+ return maxTokens ?? Math.max(4096, Number(thinking?.budget_tokens ?? 0) + 1);
1758
+ }
1759
+ function anthropicModelRules(model) {
1760
+ const match = /^claude-(opus|sonnet|haiku)-(\d+)(?:-(\d{1,2})(?=-|$))?(?:-|$)/i.exec(
1761
+ model
1762
+ );
1763
+ const family = match?.[1]?.toLowerCase();
1764
+ const major = Number(match?.[2] ?? 0);
1765
+ const minor = Number(match?.[3] ?? 0);
1766
+ const adaptive = major >= 5 || major === 4 && minor >= 6;
1767
+ return {
1768
+ structured: major >= 5 || major === 4 && minor >= 5,
1769
+ thinking: major >= 4 || /^claude-3-7-/.test(model),
1770
+ adaptive,
1771
+ rejectsSampling: major >= 5 || family === "opus" && major === 4 && minor >= 7
1772
+ };
1773
+ }
1774
+ function anthropicInputTokens(usage) {
1775
+ return (usage?.input_tokens ?? 0) + (usage?.cache_creation_input_tokens ?? 0) + (usage?.cache_read_input_tokens ?? 0);
1776
+ }
1777
+ function anthropicThinking(model, thinking) {
1778
+ if (!thinking) return void 0;
1779
+ if (thinking.type === "enabled") {
1780
+ if (anthropicModelRules(model).adaptive) {
1781
+ console.warn(
1782
+ `[llm-sdk] ${model}: using adaptive thinking instead of a legacy token budget.`
1783
+ );
1784
+ return { type: "adaptive" };
1785
+ }
1786
+ return { type: "enabled", budget_tokens: thinking.budgetTokens ?? 1e4 };
1787
+ }
1788
+ return { ...thinking };
1789
+ }
1790
+ function anthropicTemperature(model, temperature, thinking = false) {
1791
+ if (temperature === void 0) return {};
1792
+ if (thinking || anthropicModelRules(model).rejectsSampling) {
1793
+ console.warn(
1794
+ `[llm-sdk] ${model}: omitted temperature because this model or thinking mode does not support it.`
1795
+ );
1796
+ return {};
1797
+ }
1798
+ return { temperature };
1799
+ }
1800
+
1558
1801
  // src/adapters/anthropic.ts
1559
1802
  var AnthropicAdapter = class {
1560
1803
  constructor(config) {
@@ -1566,7 +1809,10 @@ var AnthropicAdapter = class {
1566
1809
  if (!this.client) {
1567
1810
  const { default: Anthropic } = await import('@anthropic-ai/sdk');
1568
1811
  this.client = new Anthropic({
1569
- apiKey: this.config.apiKey
1812
+ apiKey: this.config.apiKey,
1813
+ baseURL: this.config.baseUrl,
1814
+ timeout: this.config.timeout,
1815
+ defaultHeaders: this.config.headers
1570
1816
  });
1571
1817
  }
1572
1818
  return this.client;
@@ -1603,6 +1849,15 @@ var AnthropicAdapter = class {
1603
1849
  lastToolCallIds = [];
1604
1850
  toolResultIndex = 0;
1605
1851
  }
1852
+ const replay = anthropicReplayContent(
1853
+ msg.providerMetadata
1854
+ );
1855
+ if (replay) {
1856
+ messages.push({ role: "assistant", content: replay });
1857
+ lastToolCallIds = replay.filter((b) => b.type === "tool_use").map((b) => b.id);
1858
+ toolResultIndex = 0;
1859
+ continue;
1860
+ }
1606
1861
  const content = [];
1607
1862
  if (msg.content && typeof msg.content === "string" && msg.content.trim()) {
1608
1863
  content.push({ type: "text", text: msg.content });
@@ -1778,7 +2033,7 @@ var AnthropicAdapter = class {
1778
2033
  */
1779
2034
  buildRequestOptions(request) {
1780
2035
  const responseFormat = request.config?.responseFormat;
1781
- const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
2036
+ const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text. Do not include Markdown code fences." : "";
1782
2037
  let systemMessage;
1783
2038
  let messages;
1784
2039
  if (request.rawMessages && request.rawMessages.length > 0) {
@@ -1800,7 +2055,17 @@ var AnthropicAdapter = class {
1800
2055
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
1801
2056
  request.toolDefinitions ?? [],
1802
2057
  anthropicNativeSearch.variant
1803
- ) : request.actions?.map((action) => ({
2058
+ ) : request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
2059
+ name: tool.name,
2060
+ description: tool.description,
2061
+ // Keep the original schema. Legacy ActionParameter conversion
2062
+ // cannot represent unions, null, or nested required constraints.
2063
+ input_schema: tool.inputSchema ?? {
2064
+ type: "object",
2065
+ properties: {},
2066
+ required: []
2067
+ }
2068
+ })) : request.actions?.map((action) => ({
1804
2069
  name: action.name,
1805
2070
  description: action.description,
1806
2071
  input_schema: {
@@ -1842,12 +2107,25 @@ var AnthropicAdapter = class {
1842
2107
  };
1843
2108
  }
1844
2109
  }
2110
+ const thinking = anthropicThinking(
2111
+ request.config?.model || this.model,
2112
+ this.config.thinking
2113
+ );
1845
2114
  const options = {
1846
2115
  model: request.config?.model || this.model,
1847
- max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
2116
+ max_tokens: anthropicMaxTokens(
2117
+ request.config?.maxTokens ?? this.config.maxTokens,
2118
+ thinking
2119
+ ),
1848
2120
  system: systemMessage,
1849
2121
  messages,
1850
- tools: tools.length ? tools : void 0
2122
+ tools: tools.length ? tools : void 0,
2123
+ ...this.config.cacheControl ? { cache_control: { type: "ephemeral" } } : {},
2124
+ ...anthropicTemperature(
2125
+ request.config?.model || this.model,
2126
+ request.config?.temperature ?? this.config.temperature,
2127
+ !!this.config.thinking && this.config.thinking.type !== "disabled"
2128
+ )
1851
2129
  };
1852
2130
  const anthropicToolOptions = request.providerToolOptions?.anthropic;
1853
2131
  if (tools.length > 0 && anthropicToolOptions) {
@@ -1866,15 +2144,13 @@ var AnthropicAdapter = class {
1866
2144
  options.server_tool_configuration = serverToolConfiguration;
1867
2145
  }
1868
2146
  const outputConfig = toAnthropicOutputConfig(responseFormat);
1869
- if (outputConfig) {
1870
- options.output_config = outputConfig;
1871
- }
1872
- if (this.config.thinking?.type === "enabled") {
1873
- options.thinking = {
1874
- type: "enabled",
1875
- budget_tokens: this.config.thinking.budgetTokens || 1e4
2147
+ if (outputConfig || this.config.effort) {
2148
+ options.output_config = {
2149
+ ...outputConfig,
2150
+ ...this.config.effort ? { effort: this.config.effort } : {}
1876
2151
  };
1877
2152
  }
2153
+ if (thinking) options.thinking = thinking;
1878
2154
  return { options, messages };
1879
2155
  }
1880
2156
  /**
@@ -1894,7 +2170,9 @@ var AnthropicAdapter = class {
1894
2170
  nonStreamingOptions,
1895
2171
  request.debug
1896
2172
  );
1897
- const response = await client.messages.create(nonStreamingOptions);
2173
+ const response = await client.messages.create(nonStreamingOptions, {
2174
+ signal: request.signal
2175
+ });
1898
2176
  logProviderPayload(
1899
2177
  "anthropic",
1900
2178
  "response payload",
@@ -1917,14 +2195,20 @@ var AnthropicAdapter = class {
1917
2195
  });
1918
2196
  }
1919
2197
  }
2198
+ validateToolCalls(toolCalls);
2199
+ if (toolCalls.length && response.stop_reason !== "tool_use")
2200
+ throw invalidProviderResponse(
2201
+ "Anthropic response ended with incomplete tool calls"
2202
+ );
1920
2203
  return {
1921
2204
  content,
2205
+ providerMetadata: anthropicReplay(response.content),
1922
2206
  toolCalls,
1923
2207
  thinking: thinking || void 0,
1924
2208
  usage: response.usage ? {
1925
- promptTokens: response.usage.input_tokens ?? 0,
2209
+ promptTokens: anthropicInputTokens(response.usage),
1926
2210
  completionTokens: response.usage.output_tokens ?? 0,
1927
- totalTokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0)
2211
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage.output_tokens ?? 0)
1928
2212
  } : void 0,
1929
2213
  rawResponse: response
1930
2214
  };
@@ -1944,28 +2228,33 @@ var AnthropicAdapter = class {
1944
2228
  options,
1945
2229
  request.debug
1946
2230
  );
1947
- const stream = await client.messages.stream(options);
2231
+ const stream = await client.messages.stream(options, {
2232
+ signal: request.signal
2233
+ });
1948
2234
  let currentToolUse = null;
1949
2235
  let isInThinkingBlock = false;
2236
+ const content = [];
2237
+ const toolCalls = [];
2238
+ let stopReason;
1950
2239
  const collectedCitations = [];
1951
2240
  let citationIndex = 0;
1952
2241
  let usage;
1953
2242
  for await (const event of stream) {
2243
+ collectAnthropicContent(content, event);
1954
2244
  logProviderPayload("anthropic", "stream event", event, request.debug);
1955
- if (request.signal?.aborted) {
1956
- break;
1957
- }
2245
+ throwIfAborted(request.signal);
1958
2246
  switch (event.type) {
1959
2247
  case "message_start":
1960
2248
  if (event.message?.usage) {
1961
2249
  usage = {
1962
- prompt_tokens: event.message.usage.input_tokens,
2250
+ prompt_tokens: anthropicInputTokens(event.message.usage),
1963
2251
  completion_tokens: 0,
1964
- total_tokens: event.message.usage.input_tokens
2252
+ total_tokens: anthropicInputTokens(event.message.usage)
1965
2253
  };
1966
2254
  }
1967
2255
  break;
1968
2256
  case "message_delta":
2257
+ if (event.delta?.stop_reason) stopReason = event.delta.stop_reason;
1969
2258
  if (event.usage && usage) {
1970
2259
  usage.completion_tokens = event.usage.output_tokens;
1971
2260
  usage.total_tokens = usage.prompt_tokens + event.usage.output_tokens;
@@ -1978,13 +2267,6 @@ var AnthropicAdapter = class {
1978
2267
  name: event.content_block.name,
1979
2268
  input: ""
1980
2269
  };
1981
- if (currentToolUse.name !== "web_search") {
1982
- yield {
1983
- type: "action:start",
1984
- id: currentToolUse.id,
1985
- name: currentToolUse.name
1986
- };
1987
- }
1988
2270
  } else if (event.content_block.type === "web_search_tool_result") {
1989
2271
  const webSearchResult = event.content_block;
1990
2272
  if (webSearchResult.content && Array.isArray(webSearchResult.content)) {
@@ -2033,16 +2315,11 @@ var AnthropicAdapter = class {
2033
2315
  case "content_block_stop":
2034
2316
  if (currentToolUse) {
2035
2317
  if (currentToolUse.name !== "web_search") {
2036
- yield {
2037
- type: "action:args",
2038
- id: currentToolUse.id,
2039
- args: currentToolUse.input
2040
- };
2041
- yield {
2042
- type: "action:end",
2318
+ toolCalls.push({
2043
2319
  id: currentToolUse.id,
2044
- name: currentToolUse.name
2045
- };
2320
+ name: currentToolUse.name,
2321
+ args: parseToolArguments(currentToolUse.input)
2322
+ });
2046
2323
  }
2047
2324
  currentToolUse = null;
2048
2325
  }
@@ -2055,18 +2332,33 @@ var AnthropicAdapter = class {
2055
2332
  break;
2056
2333
  }
2057
2334
  }
2335
+ throwIfAborted(request.signal);
2336
+ if (!stopReason || currentToolUse)
2337
+ throw invalidProviderResponse(
2338
+ "Anthropic stream ended before its turn completed"
2339
+ );
2340
+ validateToolCalls(toolCalls);
2341
+ if (toolCalls.length && stopReason !== "tool_use")
2342
+ throw invalidProviderResponse(
2343
+ "Anthropic stream ended with incomplete tool calls"
2344
+ );
2345
+ for (const call of toolCalls) {
2346
+ yield { type: "action:start", id: call.id, name: call.name };
2347
+ yield {
2348
+ type: "action:args",
2349
+ id: call.id,
2350
+ args: JSON.stringify(call.args)
2351
+ };
2352
+ yield { type: "action:end", id: call.id, name: call.name };
2353
+ }
2058
2354
  if (collectedCitations.length > 0) {
2059
2355
  const uniqueCitations = deduplicateCitations2(collectedCitations);
2060
2356
  yield { type: "citation", citations: uniqueCitations };
2061
2357
  }
2062
2358
  yield { type: "message:end" };
2063
- yield { type: "done", usage };
2359
+ yield { type: "done", usage, providerMetadata: anthropicReplay(content) };
2064
2360
  } catch (error) {
2065
- yield {
2066
- type: "error",
2067
- message: error instanceof Error ? error.message : "Unknown error",
2068
- code: "ANTHROPIC_ERROR"
2069
- };
2361
+ yield providerErrorEvent(error, "ANTHROPIC_ERROR");
2070
2362
  }
2071
2363
  }
2072
2364
  };