@yourgpt/llm-sdk 2.6.0-beta.3 → 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 +46 -10
  3. package/dist/adapters/index.d.mts +11 -13
  4. package/dist/adapters/index.d.ts +11 -13
  5. package/dist/adapters/index.js +327 -100
  6. package/dist/adapters/index.mjs +327 -100
  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 +14 -8
  18. package/dist/index.d.ts +14 -8
  19. package/dist/index.js +599 -195
  20. package/dist/index.mjs +596 -196
  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 +383 -102
  24. package/dist/providers/anthropic/index.mjs +383 -102
  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 +108 -59
  34. package/dist/providers/fireworks/index.mjs +108 -59
  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 +108 -59
  38. package/dist/providers/google/index.mjs +108 -59
  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 +1378 -84
  44. package/dist/providers/openai/index.mjs +1376 -85
  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 +967 -130
  48. package/dist/providers/openrouter/index.mjs +967 -131
  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 +108 -59
  52. package/dist/providers/togetherai/index.mjs +108 -59
  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 +108 -59
  60. package/dist/providers/xai/index.mjs +108 -59
  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
@@ -87,6 +87,120 @@ function toolOutputForProvider(value, provider) {
87
87
  });
88
88
  }
89
89
 
90
+ // src/core/provider-error.ts
91
+ function invalidProviderResponse(message) {
92
+ return Object.assign(new Error(message), {
93
+ name: "InvalidProviderResponseError",
94
+ status: 502,
95
+ code: "INVALID_PROVIDER_RESPONSE"
96
+ });
97
+ }
98
+ function providerErrorEvent(error, code) {
99
+ const details = error ?? {};
100
+ const event = {
101
+ type: "error",
102
+ message: error instanceof Error ? error.message : String(error),
103
+ code,
104
+ ...typeof details.status === "number" ? { status: details.status } : {},
105
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
106
+ ...typeof details.param === "string" ? { param: details.param } : {},
107
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
108
+ };
109
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
110
+ return event;
111
+ }
112
+ function throwIfAborted(signal) {
113
+ if (!signal?.aborted) return;
114
+ const error = new Error("The operation was aborted");
115
+ error.name = "AbortError";
116
+ throw error;
117
+ }
118
+
119
+ // src/core/tool-validation.ts
120
+ function parseToolArguments(value) {
121
+ let args = value;
122
+ if (typeof value === "string") {
123
+ try {
124
+ args = JSON.parse(value || "{}");
125
+ } catch {
126
+ throw invalidProviderResponse("Tool arguments are not valid JSON");
127
+ }
128
+ }
129
+ if (args === null || typeof args !== "object" || Array.isArray(args))
130
+ throw invalidProviderResponse("Tool arguments must be a JSON object");
131
+ return args;
132
+ }
133
+ function validateToolCalls(calls) {
134
+ const ids = /* @__PURE__ */ new Set();
135
+ for (const call of calls) {
136
+ if (!call || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || ids.has(call.id))
137
+ throw invalidProviderResponse(
138
+ "Tool calls have missing or duplicate identities"
139
+ );
140
+ ids.add(call.id);
141
+ if (typeof call.args === "string")
142
+ throw invalidProviderResponse("Parsed tool arguments must be an object");
143
+ parseToolArguments(call.args);
144
+ }
145
+ }
146
+
147
+ // src/core/anthropic-replay.ts
148
+ function anthropicReplay(content) {
149
+ if (!Array.isArray(content) || !content.some(
150
+ (b) => b?.type === "thinking" || b?.type === "redacted_thinking"
151
+ ))
152
+ return void 0;
153
+ const blocks = content.flatMap((block) => {
154
+ if (block?.type === "thinking" && typeof block.thinking === "string" && typeof block.signature === "string")
155
+ return [
156
+ {
157
+ type: "thinking",
158
+ thinking: block.thinking,
159
+ signature: block.signature
160
+ }
161
+ ];
162
+ if (block?.type === "redacted_thinking" && typeof block.data === "string")
163
+ return [{ type: "redacted_thinking", data: block.data }];
164
+ if (block?.type === "text" && typeof block.text === "string")
165
+ return [{ type: "text", text: block.text }];
166
+ if (block?.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string")
167
+ return [
168
+ {
169
+ type: "tool_use",
170
+ id: block.id,
171
+ name: block.name,
172
+ input: block.input
173
+ }
174
+ ];
175
+ return [];
176
+ });
177
+ return { anthropic: { content: blocks } };
178
+ }
179
+ function anthropicReplayContent(metadata) {
180
+ const state = metadata?.anthropic;
181
+ return anthropicReplay(state?.content)?.anthropic?.content;
182
+ }
183
+ function collectAnthropicContent(blocks, event) {
184
+ if (event.type === "content_block_start")
185
+ blocks[event.index] = { ...event.content_block };
186
+ const block = blocks[event.index];
187
+ if (event.type !== "content_block_delta" || !block) return;
188
+ const delta = event.delta;
189
+ if (delta.type === "thinking_delta")
190
+ block.thinking = (block.thinking ?? "") + delta.thinking;
191
+ else if (delta.type === "signature_delta")
192
+ block.signature = (block.signature ?? "") + delta.signature;
193
+ else if (delta.type === "text_delta")
194
+ block.text = (block.text ?? "") + delta.text;
195
+ else if (delta.type === "input_json_delta") {
196
+ block.partialInput = (block.partialInput ?? "") + delta.partial_json;
197
+ try {
198
+ block.input = JSON.parse(block.partialInput);
199
+ } catch {
200
+ }
201
+ }
202
+ }
203
+
90
204
  // src/adapters/base.ts
91
205
  function stringifyForDebug(value) {
92
206
  return JSON.stringify(
@@ -165,7 +279,22 @@ function stripSchemaKeys(schema, keysToDrop, options = {}) {
165
279
  )) {
166
280
  if (keysToDrop.has(key)) continue;
167
281
  const renamed = options.renameKeys?.[key] ?? key;
168
- out[renamed] = stripSchemaKeys(value, keysToDrop, options);
282
+ if ([
283
+ "properties",
284
+ "$defs",
285
+ "definitions",
286
+ "patternProperties",
287
+ "dependentSchemas"
288
+ ].includes(key) && value && typeof value === "object" && !Array.isArray(value)) {
289
+ out[renamed] = Object.fromEntries(
290
+ Object.entries(value).map(([name, child]) => [
291
+ name,
292
+ stripSchemaKeys(child, keysToDrop, options)
293
+ ])
294
+ );
295
+ } else if (["enum", "const", "default", "examples"].includes(key)) {
296
+ out[renamed] = value;
297
+ } else out[renamed] = stripSchemaKeys(value, keysToDrop, options);
169
298
  }
170
299
  if (options.forceAdditionalPropertiesFalse && out.type === "object") {
171
300
  out.additionalProperties = false;
@@ -312,6 +441,14 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
312
441
  const msg = messages[i];
313
442
  if (msg.role === "system") continue;
314
443
  if (msg.role === "assistant") {
444
+ const replay = anthropicReplayContent(msg.providerMetadata);
445
+ if (replay) {
446
+ formatted.push({
447
+ role: "assistant",
448
+ content: replay
449
+ });
450
+ continue;
451
+ }
315
452
  const content = [];
316
453
  if (msg.content) {
317
454
  content.push({ type: "text", text: msg.content });
@@ -366,6 +503,52 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
366
503
  };
367
504
  }
368
505
 
506
+ // src/core/anthropic-parameters.ts
507
+ function anthropicMaxTokens(maxTokens, thinking) {
508
+ return maxTokens ?? Math.max(4096, Number(thinking?.budget_tokens ?? 0) + 1);
509
+ }
510
+ function anthropicModelRules(model) {
511
+ const match = /^claude-(opus|sonnet|haiku)-(\d+)(?:-(\d{1,2})(?=-|$))?(?:-|$)/i.exec(
512
+ model
513
+ );
514
+ const family = match?.[1]?.toLowerCase();
515
+ const major = Number(match?.[2] ?? 0);
516
+ const minor = Number(match?.[3] ?? 0);
517
+ const adaptive = major >= 5 || major === 4 && minor >= 6;
518
+ return {
519
+ structured: major >= 5 || major === 4 && minor >= 5,
520
+ thinking: major >= 4 || /^claude-3-7-/.test(model),
521
+ adaptive,
522
+ rejectsSampling: major >= 5 || family === "opus" && major === 4 && minor >= 7
523
+ };
524
+ }
525
+ function anthropicInputTokens(usage) {
526
+ return (usage?.input_tokens ?? 0) + (usage?.cache_creation_input_tokens ?? 0) + (usage?.cache_read_input_tokens ?? 0);
527
+ }
528
+ function anthropicThinking(model, thinking) {
529
+ if (!thinking) return void 0;
530
+ if (thinking.type === "enabled") {
531
+ if (anthropicModelRules(model).adaptive) {
532
+ console.warn(
533
+ `[llm-sdk] ${model}: using adaptive thinking instead of a legacy token budget.`
534
+ );
535
+ return { type: "adaptive" };
536
+ }
537
+ return { type: "enabled", budget_tokens: thinking.budgetTokens ?? 1e4 };
538
+ }
539
+ return { ...thinking };
540
+ }
541
+ function anthropicTemperature(model, temperature, thinking = false) {
542
+ if (temperature === void 0) return {};
543
+ if (thinking || anthropicModelRules(model).rejectsSampling) {
544
+ console.warn(
545
+ `[llm-sdk] ${model}: omitted temperature because this model or thinking mode does not support it.`
546
+ );
547
+ return {};
548
+ }
549
+ return { temperature };
550
+ }
551
+
369
552
  // src/providers/anthropic/provider.ts
370
553
  var ANTHROPIC_MODELS = {
371
554
  // Claude 4 series
@@ -475,6 +658,10 @@ function anthropic(modelId, options = {}) {
475
658
  return client;
476
659
  }
477
660
  const modelConfig = ANTHROPIC_MODELS[modelId] ?? ANTHROPIC_MODELS["claude-3-5-sonnet-latest"];
661
+ const thinking = anthropicThinking(
662
+ modelId,
663
+ options.thinking && ("enabled" in options.thinking ? options.thinking.enabled ? { type: "enabled", budgetTokens: options.thinking.budgetTokens } : void 0 : options.thinking)
664
+ );
478
665
  return {
479
666
  provider: "anthropic",
480
667
  modelId,
@@ -482,8 +669,8 @@ function anthropic(modelId, options = {}) {
482
669
  supportsVision: modelConfig.vision,
483
670
  supportsTools: modelConfig.tools,
484
671
  supportsStreaming: true,
485
- supportsJsonMode: modelConfig.jsonMode,
486
- supportsThinking: modelConfig.thinking,
672
+ supportsJsonMode: anthropicModelRules(modelId).structured,
673
+ supportsThinking: modelConfig.thinking || anthropicModelRules(modelId).thinking,
487
674
  supportsPDF: modelConfig.pdf,
488
675
  maxTokens: modelConfig.maxTokens,
489
676
  supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
@@ -493,25 +680,34 @@ function anthropic(modelId, options = {}) {
493
680
  const { system, messages } = formatMessagesForAnthropic2(params.messages);
494
681
  const requestOptions = {
495
682
  model: modelId,
496
- max_tokens: params.maxTokens ?? 4096,
683
+ max_tokens: anthropicMaxTokens(params.maxTokens, thinking),
497
684
  system: system || void 0,
498
685
  messages,
499
- tools: params.tools
686
+ tools: params.tools,
687
+ ...options.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
500
688
  };
501
- if (params.temperature !== void 0) {
502
- requestOptions.temperature = params.temperature;
503
- }
504
- if (options.thinking?.enabled && modelConfig.thinking) {
505
- requestOptions.thinking = {
506
- type: "enabled",
507
- budget_tokens: options.thinking.budgetTokens ?? 1e4
508
- };
689
+ Object.assign(
690
+ requestOptions,
691
+ anthropicTemperature(
692
+ modelId,
693
+ params.temperature,
694
+ !!thinking && thinking.type !== "disabled"
695
+ )
696
+ );
697
+ if (params.responseFormat?.type === "json_object") {
698
+ requestOptions.system = (system || "") + "\n\nRespond with a single JSON object and no other text. Do not include Markdown code fences.";
509
699
  }
700
+ if (thinking) requestOptions.thinking = thinking;
510
701
  const outputConfig = toAnthropicOutputConfig(params.responseFormat);
511
- if (outputConfig) {
512
- requestOptions.output_config = outputConfig;
702
+ if (outputConfig || options.effort) {
703
+ requestOptions.output_config = {
704
+ ...outputConfig,
705
+ ...options.effort ? { effort: options.effort } : {}
706
+ };
513
707
  }
514
- const response = await client2.messages.create(requestOptions);
708
+ const response = await client2.messages.create(requestOptions, {
709
+ signal: params.signal
710
+ });
515
711
  let text = "";
516
712
  const toolCalls = [];
517
713
  for (const block of response.content) {
@@ -525,14 +721,21 @@ function anthropic(modelId, options = {}) {
525
721
  });
526
722
  }
527
723
  }
724
+ validateToolCalls(toolCalls);
725
+ if (toolCalls.length && response.stop_reason !== "tool_use")
726
+ throw invalidProviderResponse(
727
+ "Anthropic response ended with incomplete tool calls"
728
+ );
528
729
  return {
529
730
  text,
731
+ reasoningText: response.content.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") || void 0,
732
+ providerMetadata: anthropicReplay(response.content),
530
733
  toolCalls,
531
734
  finishReason: mapFinishReason(response.stop_reason),
532
735
  usage: {
533
- promptTokens: response.usage?.input_tokens ?? 0,
736
+ promptTokens: anthropicInputTokens(response.usage),
534
737
  completionTokens: response.usage?.output_tokens ?? 0,
535
- totalTokens: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0)
738
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage?.output_tokens ?? 0)
536
739
  },
537
740
  rawResponse: response
538
741
  };
@@ -542,37 +745,47 @@ function anthropic(modelId, options = {}) {
542
745
  const { system, messages } = formatMessagesForAnthropic2(params.messages);
543
746
  const requestOptions = {
544
747
  model: modelId,
545
- max_tokens: params.maxTokens ?? 4096,
748
+ max_tokens: anthropicMaxTokens(params.maxTokens, thinking),
546
749
  system: system || void 0,
547
750
  messages,
548
- tools: params.tools
751
+ tools: params.tools,
752
+ ...options.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
549
753
  };
550
- if (params.temperature !== void 0) {
551
- requestOptions.temperature = params.temperature;
552
- }
553
- if (options.thinking?.enabled && modelConfig.thinking) {
554
- requestOptions.thinking = {
555
- type: "enabled",
556
- budget_tokens: options.thinking.budgetTokens ?? 1e4
557
- };
754
+ Object.assign(
755
+ requestOptions,
756
+ anthropicTemperature(
757
+ modelId,
758
+ params.temperature,
759
+ !!thinking && thinking.type !== "disabled"
760
+ )
761
+ );
762
+ if (params.responseFormat?.type === "json_object") {
763
+ requestOptions.system = (system || "") + "\n\nRespond with a single JSON object and no other text. Do not include Markdown code fences.";
558
764
  }
765
+ if (thinking) requestOptions.thinking = thinking;
559
766
  const outputConfig = toAnthropicOutputConfig(params.responseFormat);
560
- if (outputConfig) {
561
- requestOptions.output_config = outputConfig;
767
+ if (outputConfig || options.effort) {
768
+ requestOptions.output_config = {
769
+ ...outputConfig,
770
+ ...options.effort ? { effort: options.effort } : {}
771
+ };
562
772
  }
563
- const stream = await client2.messages.stream(requestOptions);
773
+ const stream = await client2.messages.stream(requestOptions, {
774
+ signal: params.signal
775
+ });
564
776
  let currentToolUse = null;
565
777
  let inputTokens = 0;
566
778
  let outputTokens = 0;
779
+ const content = [];
780
+ const toolCalls = [];
781
+ let stopReason;
567
782
  for await (const event of stream) {
568
- if (params.signal?.aborted) {
569
- yield { type: "error", error: new Error("Aborted") };
570
- return;
571
- }
783
+ collectAnthropicContent(content, event);
784
+ throwIfAborted(params.signal);
572
785
  switch (event.type) {
573
786
  case "message_start":
574
787
  if (event.message?.usage) {
575
- inputTokens = event.message.usage.input_tokens ?? 0;
788
+ inputTokens = anthropicInputTokens(event.message.usage);
576
789
  }
577
790
  break;
578
791
  case "content_block_start":
@@ -587,20 +800,19 @@ function anthropic(modelId, options = {}) {
587
800
  case "content_block_delta":
588
801
  if (event.delta?.type === "text_delta") {
589
802
  yield { type: "text-delta", text: event.delta.text };
803
+ } else if (event.delta?.type === "thinking_delta") {
804
+ yield { type: "reasoning-delta", text: event.delta.thinking };
590
805
  } else if (event.delta?.type === "input_json_delta" && currentToolUse) {
591
806
  currentToolUse.input += event.delta.partial_json;
592
807
  }
593
808
  break;
594
809
  case "content_block_stop":
595
810
  if (currentToolUse) {
596
- yield {
597
- type: "tool-call",
598
- toolCall: {
599
- id: currentToolUse.id,
600
- name: currentToolUse.name,
601
- args: JSON.parse(currentToolUse.input || "{}")
602
- }
603
- };
811
+ toolCalls.push({
812
+ id: currentToolUse.id,
813
+ name: currentToolUse.name,
814
+ args: parseToolArguments(currentToolUse.input)
815
+ });
604
816
  currentToolUse = null;
605
817
  }
606
818
  break;
@@ -609,19 +821,32 @@ function anthropic(modelId, options = {}) {
609
821
  outputTokens = event.usage.output_tokens ?? 0;
610
822
  }
611
823
  if (event.delta?.stop_reason) {
612
- yield {
613
- type: "finish",
614
- finishReason: mapFinishReason(event.delta.stop_reason),
615
- usage: {
616
- promptTokens: inputTokens,
617
- completionTokens: outputTokens,
618
- totalTokens: inputTokens + outputTokens
619
- }
620
- };
824
+ stopReason = event.delta.stop_reason;
621
825
  }
622
826
  break;
623
827
  }
624
828
  }
829
+ throwIfAborted(params.signal);
830
+ if (!stopReason || currentToolUse)
831
+ throw invalidProviderResponse(
832
+ "Anthropic stream ended before its turn completed"
833
+ );
834
+ validateToolCalls(toolCalls);
835
+ if (toolCalls.length && stopReason !== "tool_use")
836
+ throw invalidProviderResponse(
837
+ "Anthropic stream ended with incomplete tool calls"
838
+ );
839
+ for (const toolCall of toolCalls) yield { type: "tool-call", toolCall };
840
+ yield {
841
+ type: "finish",
842
+ finishReason: mapFinishReason(stopReason),
843
+ providerMetadata: anthropicReplay(content),
844
+ usage: {
845
+ promptTokens: inputTokens,
846
+ completionTokens: outputTokens,
847
+ totalTokens: inputTokens + outputTokens
848
+ }
849
+ };
625
850
  }
626
851
  };
627
852
  }
@@ -639,7 +864,7 @@ function mapFinishReason(reason) {
639
864
  }
640
865
  }
641
866
  function formatMessagesForAnthropic2(messages) {
642
- let system = "";
867
+ const system = extractAnthropicSystemPrompt(messages);
643
868
  const formatted = [];
644
869
  const pendingToolResults = [];
645
870
  const flushToolResults = () => {
@@ -665,7 +890,6 @@ function formatMessagesForAnthropic2(messages) {
665
890
  };
666
891
  for (const msg of messages) {
667
892
  if (msg.role === "system") {
668
- system += (system ? "\n" : "") + msg.content;
669
893
  continue;
670
894
  }
671
895
  if (msg.role === "assistant" && pendingToolResults.length > 0) {
@@ -703,6 +927,11 @@ function formatMessagesForAnthropic2(messages) {
703
927
  formatted.push({ role: "user", content });
704
928
  }
705
929
  } else if (msg.role === "assistant") {
930
+ const replay = anthropicReplayContent(msg.providerMetadata);
931
+ if (replay) {
932
+ formatted.push({ role: "assistant", content: replay });
933
+ continue;
934
+ }
706
935
  const content = [];
707
936
  if (msg.content) {
708
937
  content.push({ type: "text", text: msg.content });
@@ -758,7 +987,10 @@ var AnthropicAdapter = class {
758
987
  if (!this.client) {
759
988
  const { default: Anthropic } = await import('@anthropic-ai/sdk');
760
989
  this.client = new Anthropic({
761
- apiKey: this.config.apiKey
990
+ apiKey: this.config.apiKey,
991
+ baseURL: this.config.baseUrl,
992
+ timeout: this.config.timeout,
993
+ defaultHeaders: this.config.headers
762
994
  });
763
995
  }
764
996
  return this.client;
@@ -795,6 +1027,15 @@ var AnthropicAdapter = class {
795
1027
  lastToolCallIds = [];
796
1028
  toolResultIndex = 0;
797
1029
  }
1030
+ const replay = anthropicReplayContent(
1031
+ msg.providerMetadata
1032
+ );
1033
+ if (replay) {
1034
+ messages.push({ role: "assistant", content: replay });
1035
+ lastToolCallIds = replay.filter((b) => b.type === "tool_use").map((b) => b.id);
1036
+ toolResultIndex = 0;
1037
+ continue;
1038
+ }
798
1039
  const content = [];
799
1040
  if (msg.content && typeof msg.content === "string" && msg.content.trim()) {
800
1041
  content.push({ type: "text", text: msg.content });
@@ -970,7 +1211,7 @@ var AnthropicAdapter = class {
970
1211
  */
971
1212
  buildRequestOptions(request) {
972
1213
  const responseFormat = request.config?.responseFormat;
973
- const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
1214
+ const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text. Do not include Markdown code fences." : "";
974
1215
  let systemMessage;
975
1216
  let messages;
976
1217
  if (request.rawMessages && request.rawMessages.length > 0) {
@@ -992,7 +1233,17 @@ var AnthropicAdapter = class {
992
1233
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
993
1234
  request.toolDefinitions ?? [],
994
1235
  anthropicNativeSearch.variant
995
- ) : request.actions?.map((action) => ({
1236
+ ) : request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
1237
+ name: tool.name,
1238
+ description: tool.description,
1239
+ // Keep the original schema. Legacy ActionParameter conversion
1240
+ // cannot represent unions, null, or nested required constraints.
1241
+ input_schema: tool.inputSchema ?? {
1242
+ type: "object",
1243
+ properties: {},
1244
+ required: []
1245
+ }
1246
+ })) : request.actions?.map((action) => ({
996
1247
  name: action.name,
997
1248
  description: action.description,
998
1249
  input_schema: {
@@ -1034,12 +1285,25 @@ var AnthropicAdapter = class {
1034
1285
  };
1035
1286
  }
1036
1287
  }
1288
+ const thinking = anthropicThinking(
1289
+ request.config?.model || this.model,
1290
+ this.config.thinking
1291
+ );
1037
1292
  const options = {
1038
1293
  model: request.config?.model || this.model,
1039
- max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
1294
+ max_tokens: anthropicMaxTokens(
1295
+ request.config?.maxTokens ?? this.config.maxTokens,
1296
+ thinking
1297
+ ),
1040
1298
  system: systemMessage,
1041
1299
  messages,
1042
- tools: tools.length ? tools : void 0
1300
+ tools: tools.length ? tools : void 0,
1301
+ ...this.config.cacheControl ? { cache_control: { type: "ephemeral" } } : {},
1302
+ ...anthropicTemperature(
1303
+ request.config?.model || this.model,
1304
+ request.config?.temperature ?? this.config.temperature,
1305
+ !!this.config.thinking && this.config.thinking.type !== "disabled"
1306
+ )
1043
1307
  };
1044
1308
  const anthropicToolOptions = request.providerToolOptions?.anthropic;
1045
1309
  if (tools.length > 0 && anthropicToolOptions) {
@@ -1058,15 +1322,13 @@ var AnthropicAdapter = class {
1058
1322
  options.server_tool_configuration = serverToolConfiguration;
1059
1323
  }
1060
1324
  const outputConfig = toAnthropicOutputConfig(responseFormat);
1061
- if (outputConfig) {
1062
- options.output_config = outputConfig;
1063
- }
1064
- if (this.config.thinking?.type === "enabled") {
1065
- options.thinking = {
1066
- type: "enabled",
1067
- budget_tokens: this.config.thinking.budgetTokens || 1e4
1325
+ if (outputConfig || this.config.effort) {
1326
+ options.output_config = {
1327
+ ...outputConfig,
1328
+ ...this.config.effort ? { effort: this.config.effort } : {}
1068
1329
  };
1069
1330
  }
1331
+ if (thinking) options.thinking = thinking;
1070
1332
  return { options, messages };
1071
1333
  }
1072
1334
  /**
@@ -1086,7 +1348,9 @@ var AnthropicAdapter = class {
1086
1348
  nonStreamingOptions,
1087
1349
  request.debug
1088
1350
  );
1089
- const response = await client.messages.create(nonStreamingOptions);
1351
+ const response = await client.messages.create(nonStreamingOptions, {
1352
+ signal: request.signal
1353
+ });
1090
1354
  logProviderPayload(
1091
1355
  "anthropic",
1092
1356
  "response payload",
@@ -1109,14 +1373,20 @@ var AnthropicAdapter = class {
1109
1373
  });
1110
1374
  }
1111
1375
  }
1376
+ validateToolCalls(toolCalls);
1377
+ if (toolCalls.length && response.stop_reason !== "tool_use")
1378
+ throw invalidProviderResponse(
1379
+ "Anthropic response ended with incomplete tool calls"
1380
+ );
1112
1381
  return {
1113
1382
  content,
1383
+ providerMetadata: anthropicReplay(response.content),
1114
1384
  toolCalls,
1115
1385
  thinking: thinking || void 0,
1116
1386
  usage: response.usage ? {
1117
- promptTokens: response.usage.input_tokens ?? 0,
1387
+ promptTokens: anthropicInputTokens(response.usage),
1118
1388
  completionTokens: response.usage.output_tokens ?? 0,
1119
- totalTokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0)
1389
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage.output_tokens ?? 0)
1120
1390
  } : void 0,
1121
1391
  rawResponse: response
1122
1392
  };
@@ -1136,28 +1406,33 @@ var AnthropicAdapter = class {
1136
1406
  options,
1137
1407
  request.debug
1138
1408
  );
1139
- const stream = await client.messages.stream(options);
1409
+ const stream = await client.messages.stream(options, {
1410
+ signal: request.signal
1411
+ });
1140
1412
  let currentToolUse = null;
1141
1413
  let isInThinkingBlock = false;
1414
+ const content = [];
1415
+ const toolCalls = [];
1416
+ let stopReason;
1142
1417
  const collectedCitations = [];
1143
1418
  let citationIndex = 0;
1144
1419
  let usage;
1145
1420
  for await (const event of stream) {
1421
+ collectAnthropicContent(content, event);
1146
1422
  logProviderPayload("anthropic", "stream event", event, request.debug);
1147
- if (request.signal?.aborted) {
1148
- break;
1149
- }
1423
+ throwIfAborted(request.signal);
1150
1424
  switch (event.type) {
1151
1425
  case "message_start":
1152
1426
  if (event.message?.usage) {
1153
1427
  usage = {
1154
- prompt_tokens: event.message.usage.input_tokens,
1428
+ prompt_tokens: anthropicInputTokens(event.message.usage),
1155
1429
  completion_tokens: 0,
1156
- total_tokens: event.message.usage.input_tokens
1430
+ total_tokens: anthropicInputTokens(event.message.usage)
1157
1431
  };
1158
1432
  }
1159
1433
  break;
1160
1434
  case "message_delta":
1435
+ if (event.delta?.stop_reason) stopReason = event.delta.stop_reason;
1161
1436
  if (event.usage && usage) {
1162
1437
  usage.completion_tokens = event.usage.output_tokens;
1163
1438
  usage.total_tokens = usage.prompt_tokens + event.usage.output_tokens;
@@ -1170,13 +1445,6 @@ var AnthropicAdapter = class {
1170
1445
  name: event.content_block.name,
1171
1446
  input: ""
1172
1447
  };
1173
- if (currentToolUse.name !== "web_search") {
1174
- yield {
1175
- type: "action:start",
1176
- id: currentToolUse.id,
1177
- name: currentToolUse.name
1178
- };
1179
- }
1180
1448
  } else if (event.content_block.type === "web_search_tool_result") {
1181
1449
  const webSearchResult = event.content_block;
1182
1450
  if (webSearchResult.content && Array.isArray(webSearchResult.content)) {
@@ -1225,16 +1493,11 @@ var AnthropicAdapter = class {
1225
1493
  case "content_block_stop":
1226
1494
  if (currentToolUse) {
1227
1495
  if (currentToolUse.name !== "web_search") {
1228
- yield {
1229
- type: "action:args",
1496
+ toolCalls.push({
1230
1497
  id: currentToolUse.id,
1231
- args: currentToolUse.input
1232
- };
1233
- yield {
1234
- type: "action:end",
1235
- id: currentToolUse.id,
1236
- name: currentToolUse.name
1237
- };
1498
+ name: currentToolUse.name,
1499
+ args: parseToolArguments(currentToolUse.input)
1500
+ });
1238
1501
  }
1239
1502
  currentToolUse = null;
1240
1503
  }
@@ -1247,18 +1510,33 @@ var AnthropicAdapter = class {
1247
1510
  break;
1248
1511
  }
1249
1512
  }
1513
+ throwIfAborted(request.signal);
1514
+ if (!stopReason || currentToolUse)
1515
+ throw invalidProviderResponse(
1516
+ "Anthropic stream ended before its turn completed"
1517
+ );
1518
+ validateToolCalls(toolCalls);
1519
+ if (toolCalls.length && stopReason !== "tool_use")
1520
+ throw invalidProviderResponse(
1521
+ "Anthropic stream ended with incomplete tool calls"
1522
+ );
1523
+ for (const call of toolCalls) {
1524
+ yield { type: "action:start", id: call.id, name: call.name };
1525
+ yield {
1526
+ type: "action:args",
1527
+ id: call.id,
1528
+ args: JSON.stringify(call.args)
1529
+ };
1530
+ yield { type: "action:end", id: call.id, name: call.name };
1531
+ }
1250
1532
  if (collectedCitations.length > 0) {
1251
1533
  const uniqueCitations = deduplicateCitations(collectedCitations);
1252
1534
  yield { type: "citation", citations: uniqueCitations };
1253
1535
  }
1254
1536
  yield { type: "message:end" };
1255
- yield { type: "done", usage };
1537
+ yield { type: "done", usage, providerMetadata: anthropicReplay(content) };
1256
1538
  } catch (error) {
1257
- yield {
1258
- type: "error",
1259
- message: error instanceof Error ? error.message : "Unknown error",
1260
- code: "ANTHROPIC_ERROR"
1261
- };
1539
+ yield providerErrorEvent(error, "ANTHROPIC_ERROR");
1262
1540
  }
1263
1541
  }
1264
1542
  };
@@ -1374,7 +1652,11 @@ function createAnthropic(config = {}) {
1374
1652
  apiKey,
1375
1653
  model: modelId,
1376
1654
  baseUrl: config.baseUrl,
1377
- thinking: config.thinkingBudget ? { type: "enabled", budgetTokens: config.thinkingBudget } : void 0
1655
+ timeout: config.timeout,
1656
+ headers: config.headers,
1657
+ cacheControl: config.cacheControl,
1658
+ thinking: config.thinking ?? (config.thinkingBudget ? { type: "enabled", budgetTokens: config.thinkingBudget } : void 0),
1659
+ effort: config.effort
1378
1660
  });
1379
1661
  };
1380
1662
  const getCapabilities = (modelId) => {
@@ -1382,7 +1664,7 @@ function createAnthropic(config = {}) {
1382
1664
  return {
1383
1665
  supportsVision: model.vision,
1384
1666
  supportsTools: model.tools,
1385
- supportsThinking: model.thinking,
1667
+ supportsThinking: anthropicModelRules(modelId).thinking,
1386
1668
  supportsStreaming: true,
1387
1669
  supportsPDF: true,
1388
1670
  supportsAudio: false,
@@ -1394,8 +1676,7 @@ function createAnthropic(config = {}) {
1394
1676
  "image/gif",
1395
1677
  "image/webp"
1396
1678
  ],
1397
- // Native `output_config.format` — GA on Claude 3.5 and newer.
1398
- supportsJsonMode: true,
1679
+ supportsJsonMode: anthropicModelRules(modelId).structured,
1399
1680
  supportsSystemMessages: true
1400
1681
  };
1401
1682
  };