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