@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
@@ -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;
@@ -294,21 +423,14 @@ function messageToAnthropicContent(message) {
294
423
  return blocks;
295
424
  }
296
425
  function extractAnthropicSystemPrompt(messages, systemPrompt) {
297
- const parts = systemPrompt ? [systemPrompt] : [];
426
+ const explicit = typeof systemPrompt === "string" ? systemPrompt : "";
427
+ const parts = explicit ? [explicit] : [];
298
428
  for (const message of messages) {
299
429
  if (message.role !== "system") continue;
300
- if (typeof message.content === "string") {
301
- if (message.content) parts.push(message.content);
302
- } else if (Array.isArray(message.content)) {
303
- for (const block of message.content) {
304
- if (block?.type !== "text" || typeof block.text !== "string") {
305
- throw new TypeError("Anthropic system messages must contain text.");
306
- }
307
- if (block.text) parts.push(block.text);
308
- }
309
- } else {
310
- throw new TypeError("Anthropic system messages must contain text.");
311
- }
430
+ const text = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.filter(
431
+ (block) => block?.type === "text" && typeof block.text === "string"
432
+ ).map((block) => block.text).filter(Boolean).join("\n") : "";
433
+ if (text && text !== explicit) parts.push(text);
312
434
  }
313
435
  return parts.join("\n");
314
436
  }
@@ -319,6 +441,14 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
319
441
  const msg = messages[i];
320
442
  if (msg.role === "system") continue;
321
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
+ }
322
452
  const content = [];
323
453
  if (msg.content) {
324
454
  content.push({ type: "text", text: msg.content });
@@ -373,6 +503,52 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
373
503
  };
374
504
  }
375
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
+
376
552
  // src/providers/anthropic/provider.ts
377
553
  var ANTHROPIC_MODELS = {
378
554
  // Claude 4 series
@@ -482,6 +658,10 @@ function anthropic(modelId, options = {}) {
482
658
  return client;
483
659
  }
484
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
+ );
485
665
  return {
486
666
  provider: "anthropic",
487
667
  modelId,
@@ -489,8 +669,8 @@ function anthropic(modelId, options = {}) {
489
669
  supportsVision: modelConfig.vision,
490
670
  supportsTools: modelConfig.tools,
491
671
  supportsStreaming: true,
492
- supportsJsonMode: modelConfig.jsonMode,
493
- supportsThinking: modelConfig.thinking,
672
+ supportsJsonMode: anthropicModelRules(modelId).structured,
673
+ supportsThinking: modelConfig.thinking || anthropicModelRules(modelId).thinking,
494
674
  supportsPDF: modelConfig.pdf,
495
675
  maxTokens: modelConfig.maxTokens,
496
676
  supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
@@ -500,25 +680,34 @@ function anthropic(modelId, options = {}) {
500
680
  const { system, messages } = formatMessagesForAnthropic2(params.messages);
501
681
  const requestOptions = {
502
682
  model: modelId,
503
- max_tokens: params.maxTokens ?? 4096,
683
+ max_tokens: anthropicMaxTokens(params.maxTokens, thinking),
504
684
  system: system || void 0,
505
685
  messages,
506
- tools: params.tools
686
+ tools: params.tools,
687
+ ...options.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
507
688
  };
508
- if (params.temperature !== void 0) {
509
- requestOptions.temperature = params.temperature;
510
- }
511
- if (options.thinking?.enabled && modelConfig.thinking) {
512
- requestOptions.thinking = {
513
- type: "enabled",
514
- budget_tokens: options.thinking.budgetTokens ?? 1e4
515
- };
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.";
516
699
  }
700
+ if (thinking) requestOptions.thinking = thinking;
517
701
  const outputConfig = toAnthropicOutputConfig(params.responseFormat);
518
- if (outputConfig) {
519
- requestOptions.output_config = outputConfig;
702
+ if (outputConfig || options.effort) {
703
+ requestOptions.output_config = {
704
+ ...outputConfig,
705
+ ...options.effort ? { effort: options.effort } : {}
706
+ };
520
707
  }
521
- const response = await client2.messages.create(requestOptions);
708
+ const response = await client2.messages.create(requestOptions, {
709
+ signal: params.signal
710
+ });
522
711
  let text = "";
523
712
  const toolCalls = [];
524
713
  for (const block of response.content) {
@@ -532,14 +721,21 @@ function anthropic(modelId, options = {}) {
532
721
  });
533
722
  }
534
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
+ );
535
729
  return {
536
730
  text,
731
+ reasoningText: response.content.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") || void 0,
732
+ providerMetadata: anthropicReplay(response.content),
537
733
  toolCalls,
538
734
  finishReason: mapFinishReason(response.stop_reason),
539
735
  usage: {
540
- promptTokens: response.usage?.input_tokens ?? 0,
736
+ promptTokens: anthropicInputTokens(response.usage),
541
737
  completionTokens: response.usage?.output_tokens ?? 0,
542
- totalTokens: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0)
738
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage?.output_tokens ?? 0)
543
739
  },
544
740
  rawResponse: response
545
741
  };
@@ -549,37 +745,47 @@ function anthropic(modelId, options = {}) {
549
745
  const { system, messages } = formatMessagesForAnthropic2(params.messages);
550
746
  const requestOptions = {
551
747
  model: modelId,
552
- max_tokens: params.maxTokens ?? 4096,
748
+ max_tokens: anthropicMaxTokens(params.maxTokens, thinking),
553
749
  system: system || void 0,
554
750
  messages,
555
- tools: params.tools
751
+ tools: params.tools,
752
+ ...options.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
556
753
  };
557
- if (params.temperature !== void 0) {
558
- requestOptions.temperature = params.temperature;
559
- }
560
- if (options.thinking?.enabled && modelConfig.thinking) {
561
- requestOptions.thinking = {
562
- type: "enabled",
563
- budget_tokens: options.thinking.budgetTokens ?? 1e4
564
- };
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.";
565
764
  }
765
+ if (thinking) requestOptions.thinking = thinking;
566
766
  const outputConfig = toAnthropicOutputConfig(params.responseFormat);
567
- if (outputConfig) {
568
- requestOptions.output_config = outputConfig;
767
+ if (outputConfig || options.effort) {
768
+ requestOptions.output_config = {
769
+ ...outputConfig,
770
+ ...options.effort ? { effort: options.effort } : {}
771
+ };
569
772
  }
570
- const stream = await client2.messages.stream(requestOptions);
773
+ const stream = await client2.messages.stream(requestOptions, {
774
+ signal: params.signal
775
+ });
571
776
  let currentToolUse = null;
572
777
  let inputTokens = 0;
573
778
  let outputTokens = 0;
779
+ const content = [];
780
+ const toolCalls = [];
781
+ let stopReason;
574
782
  for await (const event of stream) {
575
- if (params.signal?.aborted) {
576
- yield { type: "error", error: new Error("Aborted") };
577
- return;
578
- }
783
+ collectAnthropicContent(content, event);
784
+ throwIfAborted(params.signal);
579
785
  switch (event.type) {
580
786
  case "message_start":
581
787
  if (event.message?.usage) {
582
- inputTokens = event.message.usage.input_tokens ?? 0;
788
+ inputTokens = anthropicInputTokens(event.message.usage);
583
789
  }
584
790
  break;
585
791
  case "content_block_start":
@@ -594,20 +800,19 @@ function anthropic(modelId, options = {}) {
594
800
  case "content_block_delta":
595
801
  if (event.delta?.type === "text_delta") {
596
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 };
597
805
  } else if (event.delta?.type === "input_json_delta" && currentToolUse) {
598
806
  currentToolUse.input += event.delta.partial_json;
599
807
  }
600
808
  break;
601
809
  case "content_block_stop":
602
810
  if (currentToolUse) {
603
- yield {
604
- type: "tool-call",
605
- toolCall: {
606
- id: currentToolUse.id,
607
- name: currentToolUse.name,
608
- args: JSON.parse(currentToolUse.input || "{}")
609
- }
610
- };
811
+ toolCalls.push({
812
+ id: currentToolUse.id,
813
+ name: currentToolUse.name,
814
+ args: parseToolArguments(currentToolUse.input)
815
+ });
611
816
  currentToolUse = null;
612
817
  }
613
818
  break;
@@ -616,19 +821,32 @@ function anthropic(modelId, options = {}) {
616
821
  outputTokens = event.usage.output_tokens ?? 0;
617
822
  }
618
823
  if (event.delta?.stop_reason) {
619
- yield {
620
- type: "finish",
621
- finishReason: mapFinishReason(event.delta.stop_reason),
622
- usage: {
623
- promptTokens: inputTokens,
624
- completionTokens: outputTokens,
625
- totalTokens: inputTokens + outputTokens
626
- }
627
- };
824
+ stopReason = event.delta.stop_reason;
628
825
  }
629
826
  break;
630
827
  }
631
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
+ };
632
850
  }
633
851
  };
634
852
  }
@@ -646,7 +864,7 @@ function mapFinishReason(reason) {
646
864
  }
647
865
  }
648
866
  function formatMessagesForAnthropic2(messages) {
649
- let system = "";
867
+ const system = extractAnthropicSystemPrompt(messages);
650
868
  const formatted = [];
651
869
  const pendingToolResults = [];
652
870
  const flushToolResults = () => {
@@ -672,7 +890,6 @@ function formatMessagesForAnthropic2(messages) {
672
890
  };
673
891
  for (const msg of messages) {
674
892
  if (msg.role === "system") {
675
- system += (system ? "\n" : "") + msg.content;
676
893
  continue;
677
894
  }
678
895
  if (msg.role === "assistant" && pendingToolResults.length > 0) {
@@ -710,6 +927,11 @@ function formatMessagesForAnthropic2(messages) {
710
927
  formatted.push({ role: "user", content });
711
928
  }
712
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
+ }
713
935
  const content = [];
714
936
  if (msg.content) {
715
937
  content.push({ type: "text", text: msg.content });
@@ -765,7 +987,10 @@ var AnthropicAdapter = class {
765
987
  if (!this.client) {
766
988
  const { default: Anthropic } = await import('@anthropic-ai/sdk');
767
989
  this.client = new Anthropic({
768
- apiKey: this.config.apiKey
990
+ apiKey: this.config.apiKey,
991
+ baseURL: this.config.baseUrl,
992
+ timeout: this.config.timeout,
993
+ defaultHeaders: this.config.headers
769
994
  });
770
995
  }
771
996
  return this.client;
@@ -802,6 +1027,15 @@ var AnthropicAdapter = class {
802
1027
  lastToolCallIds = [];
803
1028
  toolResultIndex = 0;
804
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
+ }
805
1039
  const content = [];
806
1040
  if (msg.content && typeof msg.content === "string" && msg.content.trim()) {
807
1041
  content.push({ type: "text", text: msg.content });
@@ -977,7 +1211,7 @@ var AnthropicAdapter = class {
977
1211
  */
978
1212
  buildRequestOptions(request) {
979
1213
  const responseFormat = request.config?.responseFormat;
980
- 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." : "";
981
1215
  let systemMessage;
982
1216
  let messages;
983
1217
  if (request.rawMessages && request.rawMessages.length > 0) {
@@ -999,7 +1233,17 @@ var AnthropicAdapter = class {
999
1233
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
1000
1234
  request.toolDefinitions ?? [],
1001
1235
  anthropicNativeSearch.variant
1002
- ) : 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) => ({
1003
1247
  name: action.name,
1004
1248
  description: action.description,
1005
1249
  input_schema: {
@@ -1041,12 +1285,25 @@ var AnthropicAdapter = class {
1041
1285
  };
1042
1286
  }
1043
1287
  }
1288
+ const thinking = anthropicThinking(
1289
+ request.config?.model || this.model,
1290
+ this.config.thinking
1291
+ );
1044
1292
  const options = {
1045
1293
  model: request.config?.model || this.model,
1046
- max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
1294
+ max_tokens: anthropicMaxTokens(
1295
+ request.config?.maxTokens ?? this.config.maxTokens,
1296
+ thinking
1297
+ ),
1047
1298
  system: systemMessage,
1048
1299
  messages,
1049
- 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
+ )
1050
1307
  };
1051
1308
  const anthropicToolOptions = request.providerToolOptions?.anthropic;
1052
1309
  if (tools.length > 0 && anthropicToolOptions) {
@@ -1065,15 +1322,13 @@ var AnthropicAdapter = class {
1065
1322
  options.server_tool_configuration = serverToolConfiguration;
1066
1323
  }
1067
1324
  const outputConfig = toAnthropicOutputConfig(responseFormat);
1068
- if (outputConfig) {
1069
- options.output_config = outputConfig;
1070
- }
1071
- if (this.config.thinking?.type === "enabled") {
1072
- options.thinking = {
1073
- type: "enabled",
1074
- 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 } : {}
1075
1329
  };
1076
1330
  }
1331
+ if (thinking) options.thinking = thinking;
1077
1332
  return { options, messages };
1078
1333
  }
1079
1334
  /**
@@ -1093,7 +1348,9 @@ var AnthropicAdapter = class {
1093
1348
  nonStreamingOptions,
1094
1349
  request.debug
1095
1350
  );
1096
- const response = await client.messages.create(nonStreamingOptions);
1351
+ const response = await client.messages.create(nonStreamingOptions, {
1352
+ signal: request.signal
1353
+ });
1097
1354
  logProviderPayload(
1098
1355
  "anthropic",
1099
1356
  "response payload",
@@ -1116,14 +1373,20 @@ var AnthropicAdapter = class {
1116
1373
  });
1117
1374
  }
1118
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
+ );
1119
1381
  return {
1120
1382
  content,
1383
+ providerMetadata: anthropicReplay(response.content),
1121
1384
  toolCalls,
1122
1385
  thinking: thinking || void 0,
1123
1386
  usage: response.usage ? {
1124
- promptTokens: response.usage.input_tokens ?? 0,
1387
+ promptTokens: anthropicInputTokens(response.usage),
1125
1388
  completionTokens: response.usage.output_tokens ?? 0,
1126
- totalTokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0)
1389
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage.output_tokens ?? 0)
1127
1390
  } : void 0,
1128
1391
  rawResponse: response
1129
1392
  };
@@ -1143,28 +1406,33 @@ var AnthropicAdapter = class {
1143
1406
  options,
1144
1407
  request.debug
1145
1408
  );
1146
- const stream = await client.messages.stream(options);
1409
+ const stream = await client.messages.stream(options, {
1410
+ signal: request.signal
1411
+ });
1147
1412
  let currentToolUse = null;
1148
1413
  let isInThinkingBlock = false;
1414
+ const content = [];
1415
+ const toolCalls = [];
1416
+ let stopReason;
1149
1417
  const collectedCitations = [];
1150
1418
  let citationIndex = 0;
1151
1419
  let usage;
1152
1420
  for await (const event of stream) {
1421
+ collectAnthropicContent(content, event);
1153
1422
  logProviderPayload("anthropic", "stream event", event, request.debug);
1154
- if (request.signal?.aborted) {
1155
- break;
1156
- }
1423
+ throwIfAborted(request.signal);
1157
1424
  switch (event.type) {
1158
1425
  case "message_start":
1159
1426
  if (event.message?.usage) {
1160
1427
  usage = {
1161
- prompt_tokens: event.message.usage.input_tokens,
1428
+ prompt_tokens: anthropicInputTokens(event.message.usage),
1162
1429
  completion_tokens: 0,
1163
- total_tokens: event.message.usage.input_tokens
1430
+ total_tokens: anthropicInputTokens(event.message.usage)
1164
1431
  };
1165
1432
  }
1166
1433
  break;
1167
1434
  case "message_delta":
1435
+ if (event.delta?.stop_reason) stopReason = event.delta.stop_reason;
1168
1436
  if (event.usage && usage) {
1169
1437
  usage.completion_tokens = event.usage.output_tokens;
1170
1438
  usage.total_tokens = usage.prompt_tokens + event.usage.output_tokens;
@@ -1177,13 +1445,6 @@ var AnthropicAdapter = class {
1177
1445
  name: event.content_block.name,
1178
1446
  input: ""
1179
1447
  };
1180
- if (currentToolUse.name !== "web_search") {
1181
- yield {
1182
- type: "action:start",
1183
- id: currentToolUse.id,
1184
- name: currentToolUse.name
1185
- };
1186
- }
1187
1448
  } else if (event.content_block.type === "web_search_tool_result") {
1188
1449
  const webSearchResult = event.content_block;
1189
1450
  if (webSearchResult.content && Array.isArray(webSearchResult.content)) {
@@ -1232,16 +1493,11 @@ var AnthropicAdapter = class {
1232
1493
  case "content_block_stop":
1233
1494
  if (currentToolUse) {
1234
1495
  if (currentToolUse.name !== "web_search") {
1235
- yield {
1236
- type: "action:args",
1237
- id: currentToolUse.id,
1238
- args: currentToolUse.input
1239
- };
1240
- yield {
1241
- type: "action:end",
1496
+ toolCalls.push({
1242
1497
  id: currentToolUse.id,
1243
- name: currentToolUse.name
1244
- };
1498
+ name: currentToolUse.name,
1499
+ args: parseToolArguments(currentToolUse.input)
1500
+ });
1245
1501
  }
1246
1502
  currentToolUse = null;
1247
1503
  }
@@ -1254,18 +1510,33 @@ var AnthropicAdapter = class {
1254
1510
  break;
1255
1511
  }
1256
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
+ }
1257
1532
  if (collectedCitations.length > 0) {
1258
1533
  const uniqueCitations = deduplicateCitations(collectedCitations);
1259
1534
  yield { type: "citation", citations: uniqueCitations };
1260
1535
  }
1261
1536
  yield { type: "message:end" };
1262
- yield { type: "done", usage };
1537
+ yield { type: "done", usage, providerMetadata: anthropicReplay(content) };
1263
1538
  } catch (error) {
1264
- yield {
1265
- type: "error",
1266
- message: error instanceof Error ? error.message : "Unknown error",
1267
- code: "ANTHROPIC_ERROR"
1268
- };
1539
+ yield providerErrorEvent(error, "ANTHROPIC_ERROR");
1269
1540
  }
1270
1541
  }
1271
1542
  };
@@ -1381,7 +1652,11 @@ function createAnthropic(config = {}) {
1381
1652
  apiKey,
1382
1653
  model: modelId,
1383
1654
  baseUrl: config.baseUrl,
1384
- 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
1385
1660
  });
1386
1661
  };
1387
1662
  const getCapabilities = (modelId) => {
@@ -1389,7 +1664,7 @@ function createAnthropic(config = {}) {
1389
1664
  return {
1390
1665
  supportsVision: model.vision,
1391
1666
  supportsTools: model.tools,
1392
- supportsThinking: model.thinking,
1667
+ supportsThinking: anthropicModelRules(modelId).thinking,
1393
1668
  supportsStreaming: true,
1394
1669
  supportsPDF: true,
1395
1670
  supportsAudio: false,
@@ -1401,8 +1676,7 @@ function createAnthropic(config = {}) {
1401
1676
  "image/gif",
1402
1677
  "image/webp"
1403
1678
  ],
1404
- // Native `output_config.format` — GA on Claude 3.5 and newer.
1405
- supportsJsonMode: true,
1679
+ supportsJsonMode: anthropicModelRules(modelId).structured,
1406
1680
  supportsSystemMessages: true
1407
1681
  };
1408
1682
  };