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