@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
@@ -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;
@@ -292,21 +421,14 @@ function messageToAnthropicContent(message) {
292
421
  return blocks;
293
422
  }
294
423
  function extractAnthropicSystemPrompt(messages, systemPrompt) {
295
- const parts = systemPrompt ? [systemPrompt] : [];
424
+ const explicit = typeof systemPrompt === "string" ? systemPrompt : "";
425
+ const parts = explicit ? [explicit] : [];
296
426
  for (const message of messages) {
297
427
  if (message.role !== "system") continue;
298
- if (typeof message.content === "string") {
299
- if (message.content) parts.push(message.content);
300
- } else if (Array.isArray(message.content)) {
301
- for (const block of message.content) {
302
- if (block?.type !== "text" || typeof block.text !== "string") {
303
- throw new TypeError("Anthropic system messages must contain text.");
304
- }
305
- if (block.text) parts.push(block.text);
306
- }
307
- } else {
308
- throw new TypeError("Anthropic system messages must contain text.");
309
- }
428
+ const text = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.filter(
429
+ (block) => block?.type === "text" && typeof block.text === "string"
430
+ ).map((block) => block.text).filter(Boolean).join("\n") : "";
431
+ if (text && text !== explicit) parts.push(text);
310
432
  }
311
433
  return parts.join("\n");
312
434
  }
@@ -317,6 +439,14 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
317
439
  const msg = messages[i];
318
440
  if (msg.role === "system") continue;
319
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
+ }
320
450
  const content = [];
321
451
  if (msg.content) {
322
452
  content.push({ type: "text", text: msg.content });
@@ -371,6 +501,52 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
371
501
  };
372
502
  }
373
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
+
374
550
  // src/providers/anthropic/provider.ts
375
551
  var ANTHROPIC_MODELS = {
376
552
  // Claude 4 series
@@ -480,6 +656,10 @@ function anthropic(modelId, options = {}) {
480
656
  return client;
481
657
  }
482
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
+ );
483
663
  return {
484
664
  provider: "anthropic",
485
665
  modelId,
@@ -487,8 +667,8 @@ function anthropic(modelId, options = {}) {
487
667
  supportsVision: modelConfig.vision,
488
668
  supportsTools: modelConfig.tools,
489
669
  supportsStreaming: true,
490
- supportsJsonMode: modelConfig.jsonMode,
491
- supportsThinking: modelConfig.thinking,
670
+ supportsJsonMode: anthropicModelRules(modelId).structured,
671
+ supportsThinking: modelConfig.thinking || anthropicModelRules(modelId).thinking,
492
672
  supportsPDF: modelConfig.pdf,
493
673
  maxTokens: modelConfig.maxTokens,
494
674
  supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
@@ -498,25 +678,34 @@ function anthropic(modelId, options = {}) {
498
678
  const { system, messages } = formatMessagesForAnthropic2(params.messages);
499
679
  const requestOptions = {
500
680
  model: modelId,
501
- max_tokens: params.maxTokens ?? 4096,
681
+ max_tokens: anthropicMaxTokens(params.maxTokens, thinking),
502
682
  system: system || void 0,
503
683
  messages,
504
- tools: params.tools
684
+ tools: params.tools,
685
+ ...options.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
505
686
  };
506
- if (params.temperature !== void 0) {
507
- requestOptions.temperature = params.temperature;
508
- }
509
- if (options.thinking?.enabled && modelConfig.thinking) {
510
- requestOptions.thinking = {
511
- type: "enabled",
512
- budget_tokens: options.thinking.budgetTokens ?? 1e4
513
- };
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.";
514
697
  }
698
+ if (thinking) requestOptions.thinking = thinking;
515
699
  const outputConfig = toAnthropicOutputConfig(params.responseFormat);
516
- if (outputConfig) {
517
- requestOptions.output_config = outputConfig;
700
+ if (outputConfig || options.effort) {
701
+ requestOptions.output_config = {
702
+ ...outputConfig,
703
+ ...options.effort ? { effort: options.effort } : {}
704
+ };
518
705
  }
519
- const response = await client2.messages.create(requestOptions);
706
+ const response = await client2.messages.create(requestOptions, {
707
+ signal: params.signal
708
+ });
520
709
  let text = "";
521
710
  const toolCalls = [];
522
711
  for (const block of response.content) {
@@ -530,14 +719,21 @@ function anthropic(modelId, options = {}) {
530
719
  });
531
720
  }
532
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
+ );
533
727
  return {
534
728
  text,
729
+ reasoningText: response.content.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") || void 0,
730
+ providerMetadata: anthropicReplay(response.content),
535
731
  toolCalls,
536
732
  finishReason: mapFinishReason(response.stop_reason),
537
733
  usage: {
538
- promptTokens: response.usage?.input_tokens ?? 0,
734
+ promptTokens: anthropicInputTokens(response.usage),
539
735
  completionTokens: response.usage?.output_tokens ?? 0,
540
- totalTokens: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0)
736
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage?.output_tokens ?? 0)
541
737
  },
542
738
  rawResponse: response
543
739
  };
@@ -547,37 +743,47 @@ function anthropic(modelId, options = {}) {
547
743
  const { system, messages } = formatMessagesForAnthropic2(params.messages);
548
744
  const requestOptions = {
549
745
  model: modelId,
550
- max_tokens: params.maxTokens ?? 4096,
746
+ max_tokens: anthropicMaxTokens(params.maxTokens, thinking),
551
747
  system: system || void 0,
552
748
  messages,
553
- tools: params.tools
749
+ tools: params.tools,
750
+ ...options.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
554
751
  };
555
- if (params.temperature !== void 0) {
556
- requestOptions.temperature = params.temperature;
557
- }
558
- if (options.thinking?.enabled && modelConfig.thinking) {
559
- requestOptions.thinking = {
560
- type: "enabled",
561
- budget_tokens: options.thinking.budgetTokens ?? 1e4
562
- };
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.";
563
762
  }
763
+ if (thinking) requestOptions.thinking = thinking;
564
764
  const outputConfig = toAnthropicOutputConfig(params.responseFormat);
565
- if (outputConfig) {
566
- requestOptions.output_config = outputConfig;
765
+ if (outputConfig || options.effort) {
766
+ requestOptions.output_config = {
767
+ ...outputConfig,
768
+ ...options.effort ? { effort: options.effort } : {}
769
+ };
567
770
  }
568
- const stream = await client2.messages.stream(requestOptions);
771
+ const stream = await client2.messages.stream(requestOptions, {
772
+ signal: params.signal
773
+ });
569
774
  let currentToolUse = null;
570
775
  let inputTokens = 0;
571
776
  let outputTokens = 0;
777
+ const content = [];
778
+ const toolCalls = [];
779
+ let stopReason;
572
780
  for await (const event of stream) {
573
- if (params.signal?.aborted) {
574
- yield { type: "error", error: new Error("Aborted") };
575
- return;
576
- }
781
+ collectAnthropicContent(content, event);
782
+ throwIfAborted(params.signal);
577
783
  switch (event.type) {
578
784
  case "message_start":
579
785
  if (event.message?.usage) {
580
- inputTokens = event.message.usage.input_tokens ?? 0;
786
+ inputTokens = anthropicInputTokens(event.message.usage);
581
787
  }
582
788
  break;
583
789
  case "content_block_start":
@@ -592,20 +798,19 @@ function anthropic(modelId, options = {}) {
592
798
  case "content_block_delta":
593
799
  if (event.delta?.type === "text_delta") {
594
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 };
595
803
  } else if (event.delta?.type === "input_json_delta" && currentToolUse) {
596
804
  currentToolUse.input += event.delta.partial_json;
597
805
  }
598
806
  break;
599
807
  case "content_block_stop":
600
808
  if (currentToolUse) {
601
- yield {
602
- type: "tool-call",
603
- toolCall: {
604
- id: currentToolUse.id,
605
- name: currentToolUse.name,
606
- args: JSON.parse(currentToolUse.input || "{}")
607
- }
608
- };
809
+ toolCalls.push({
810
+ id: currentToolUse.id,
811
+ name: currentToolUse.name,
812
+ args: parseToolArguments(currentToolUse.input)
813
+ });
609
814
  currentToolUse = null;
610
815
  }
611
816
  break;
@@ -614,19 +819,32 @@ function anthropic(modelId, options = {}) {
614
819
  outputTokens = event.usage.output_tokens ?? 0;
615
820
  }
616
821
  if (event.delta?.stop_reason) {
617
- yield {
618
- type: "finish",
619
- finishReason: mapFinishReason(event.delta.stop_reason),
620
- usage: {
621
- promptTokens: inputTokens,
622
- completionTokens: outputTokens,
623
- totalTokens: inputTokens + outputTokens
624
- }
625
- };
822
+ stopReason = event.delta.stop_reason;
626
823
  }
627
824
  break;
628
825
  }
629
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
+ };
630
848
  }
631
849
  };
632
850
  }
@@ -644,7 +862,7 @@ function mapFinishReason(reason) {
644
862
  }
645
863
  }
646
864
  function formatMessagesForAnthropic2(messages) {
647
- let system = "";
865
+ const system = extractAnthropicSystemPrompt(messages);
648
866
  const formatted = [];
649
867
  const pendingToolResults = [];
650
868
  const flushToolResults = () => {
@@ -670,7 +888,6 @@ function formatMessagesForAnthropic2(messages) {
670
888
  };
671
889
  for (const msg of messages) {
672
890
  if (msg.role === "system") {
673
- system += (system ? "\n" : "") + msg.content;
674
891
  continue;
675
892
  }
676
893
  if (msg.role === "assistant" && pendingToolResults.length > 0) {
@@ -708,6 +925,11 @@ function formatMessagesForAnthropic2(messages) {
708
925
  formatted.push({ role: "user", content });
709
926
  }
710
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
+ }
711
933
  const content = [];
712
934
  if (msg.content) {
713
935
  content.push({ type: "text", text: msg.content });
@@ -763,7 +985,10 @@ var AnthropicAdapter = class {
763
985
  if (!this.client) {
764
986
  const { default: Anthropic } = await import('@anthropic-ai/sdk');
765
987
  this.client = new Anthropic({
766
- apiKey: this.config.apiKey
988
+ apiKey: this.config.apiKey,
989
+ baseURL: this.config.baseUrl,
990
+ timeout: this.config.timeout,
991
+ defaultHeaders: this.config.headers
767
992
  });
768
993
  }
769
994
  return this.client;
@@ -800,6 +1025,15 @@ var AnthropicAdapter = class {
800
1025
  lastToolCallIds = [];
801
1026
  toolResultIndex = 0;
802
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
+ }
803
1037
  const content = [];
804
1038
  if (msg.content && typeof msg.content === "string" && msg.content.trim()) {
805
1039
  content.push({ type: "text", text: msg.content });
@@ -975,7 +1209,7 @@ var AnthropicAdapter = class {
975
1209
  */
976
1210
  buildRequestOptions(request) {
977
1211
  const responseFormat = request.config?.responseFormat;
978
- 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." : "";
979
1213
  let systemMessage;
980
1214
  let messages;
981
1215
  if (request.rawMessages && request.rawMessages.length > 0) {
@@ -997,7 +1231,17 @@ var AnthropicAdapter = class {
997
1231
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
998
1232
  request.toolDefinitions ?? [],
999
1233
  anthropicNativeSearch.variant
1000
- ) : 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) => ({
1001
1245
  name: action.name,
1002
1246
  description: action.description,
1003
1247
  input_schema: {
@@ -1039,12 +1283,25 @@ var AnthropicAdapter = class {
1039
1283
  };
1040
1284
  }
1041
1285
  }
1286
+ const thinking = anthropicThinking(
1287
+ request.config?.model || this.model,
1288
+ this.config.thinking
1289
+ );
1042
1290
  const options = {
1043
1291
  model: request.config?.model || this.model,
1044
- max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
1292
+ max_tokens: anthropicMaxTokens(
1293
+ request.config?.maxTokens ?? this.config.maxTokens,
1294
+ thinking
1295
+ ),
1045
1296
  system: systemMessage,
1046
1297
  messages,
1047
- 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
+ )
1048
1305
  };
1049
1306
  const anthropicToolOptions = request.providerToolOptions?.anthropic;
1050
1307
  if (tools.length > 0 && anthropicToolOptions) {
@@ -1063,15 +1320,13 @@ var AnthropicAdapter = class {
1063
1320
  options.server_tool_configuration = serverToolConfiguration;
1064
1321
  }
1065
1322
  const outputConfig = toAnthropicOutputConfig(responseFormat);
1066
- if (outputConfig) {
1067
- options.output_config = outputConfig;
1068
- }
1069
- if (this.config.thinking?.type === "enabled") {
1070
- options.thinking = {
1071
- type: "enabled",
1072
- 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 } : {}
1073
1327
  };
1074
1328
  }
1329
+ if (thinking) options.thinking = thinking;
1075
1330
  return { options, messages };
1076
1331
  }
1077
1332
  /**
@@ -1091,7 +1346,9 @@ var AnthropicAdapter = class {
1091
1346
  nonStreamingOptions,
1092
1347
  request.debug
1093
1348
  );
1094
- const response = await client.messages.create(nonStreamingOptions);
1349
+ const response = await client.messages.create(nonStreamingOptions, {
1350
+ signal: request.signal
1351
+ });
1095
1352
  logProviderPayload(
1096
1353
  "anthropic",
1097
1354
  "response payload",
@@ -1114,14 +1371,20 @@ var AnthropicAdapter = class {
1114
1371
  });
1115
1372
  }
1116
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
+ );
1117
1379
  return {
1118
1380
  content,
1381
+ providerMetadata: anthropicReplay(response.content),
1119
1382
  toolCalls,
1120
1383
  thinking: thinking || void 0,
1121
1384
  usage: response.usage ? {
1122
- promptTokens: response.usage.input_tokens ?? 0,
1385
+ promptTokens: anthropicInputTokens(response.usage),
1123
1386
  completionTokens: response.usage.output_tokens ?? 0,
1124
- totalTokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0)
1387
+ totalTokens: anthropicInputTokens(response.usage) + (response.usage.output_tokens ?? 0)
1125
1388
  } : void 0,
1126
1389
  rawResponse: response
1127
1390
  };
@@ -1141,28 +1404,33 @@ var AnthropicAdapter = class {
1141
1404
  options,
1142
1405
  request.debug
1143
1406
  );
1144
- const stream = await client.messages.stream(options);
1407
+ const stream = await client.messages.stream(options, {
1408
+ signal: request.signal
1409
+ });
1145
1410
  let currentToolUse = null;
1146
1411
  let isInThinkingBlock = false;
1412
+ const content = [];
1413
+ const toolCalls = [];
1414
+ let stopReason;
1147
1415
  const collectedCitations = [];
1148
1416
  let citationIndex = 0;
1149
1417
  let usage;
1150
1418
  for await (const event of stream) {
1419
+ collectAnthropicContent(content, event);
1151
1420
  logProviderPayload("anthropic", "stream event", event, request.debug);
1152
- if (request.signal?.aborted) {
1153
- break;
1154
- }
1421
+ throwIfAborted(request.signal);
1155
1422
  switch (event.type) {
1156
1423
  case "message_start":
1157
1424
  if (event.message?.usage) {
1158
1425
  usage = {
1159
- prompt_tokens: event.message.usage.input_tokens,
1426
+ prompt_tokens: anthropicInputTokens(event.message.usage),
1160
1427
  completion_tokens: 0,
1161
- total_tokens: event.message.usage.input_tokens
1428
+ total_tokens: anthropicInputTokens(event.message.usage)
1162
1429
  };
1163
1430
  }
1164
1431
  break;
1165
1432
  case "message_delta":
1433
+ if (event.delta?.stop_reason) stopReason = event.delta.stop_reason;
1166
1434
  if (event.usage && usage) {
1167
1435
  usage.completion_tokens = event.usage.output_tokens;
1168
1436
  usage.total_tokens = usage.prompt_tokens + event.usage.output_tokens;
@@ -1175,13 +1443,6 @@ var AnthropicAdapter = class {
1175
1443
  name: event.content_block.name,
1176
1444
  input: ""
1177
1445
  };
1178
- if (currentToolUse.name !== "web_search") {
1179
- yield {
1180
- type: "action:start",
1181
- id: currentToolUse.id,
1182
- name: currentToolUse.name
1183
- };
1184
- }
1185
1446
  } else if (event.content_block.type === "web_search_tool_result") {
1186
1447
  const webSearchResult = event.content_block;
1187
1448
  if (webSearchResult.content && Array.isArray(webSearchResult.content)) {
@@ -1230,16 +1491,11 @@ var AnthropicAdapter = class {
1230
1491
  case "content_block_stop":
1231
1492
  if (currentToolUse) {
1232
1493
  if (currentToolUse.name !== "web_search") {
1233
- yield {
1234
- type: "action:args",
1235
- id: currentToolUse.id,
1236
- args: currentToolUse.input
1237
- };
1238
- yield {
1239
- type: "action:end",
1494
+ toolCalls.push({
1240
1495
  id: currentToolUse.id,
1241
- name: currentToolUse.name
1242
- };
1496
+ name: currentToolUse.name,
1497
+ args: parseToolArguments(currentToolUse.input)
1498
+ });
1243
1499
  }
1244
1500
  currentToolUse = null;
1245
1501
  }
@@ -1252,18 +1508,33 @@ var AnthropicAdapter = class {
1252
1508
  break;
1253
1509
  }
1254
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
+ }
1255
1530
  if (collectedCitations.length > 0) {
1256
1531
  const uniqueCitations = deduplicateCitations(collectedCitations);
1257
1532
  yield { type: "citation", citations: uniqueCitations };
1258
1533
  }
1259
1534
  yield { type: "message:end" };
1260
- yield { type: "done", usage };
1535
+ yield { type: "done", usage, providerMetadata: anthropicReplay(content) };
1261
1536
  } catch (error) {
1262
- yield {
1263
- type: "error",
1264
- message: error instanceof Error ? error.message : "Unknown error",
1265
- code: "ANTHROPIC_ERROR"
1266
- };
1537
+ yield providerErrorEvent(error, "ANTHROPIC_ERROR");
1267
1538
  }
1268
1539
  }
1269
1540
  };
@@ -1379,7 +1650,11 @@ function createAnthropic(config = {}) {
1379
1650
  apiKey,
1380
1651
  model: modelId,
1381
1652
  baseUrl: config.baseUrl,
1382
- 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
1383
1658
  });
1384
1659
  };
1385
1660
  const getCapabilities = (modelId) => {
@@ -1387,7 +1662,7 @@ function createAnthropic(config = {}) {
1387
1662
  return {
1388
1663
  supportsVision: model.vision,
1389
1664
  supportsTools: model.tools,
1390
- supportsThinking: model.thinking,
1665
+ supportsThinking: anthropicModelRules(modelId).thinking,
1391
1666
  supportsStreaming: true,
1392
1667
  supportsPDF: true,
1393
1668
  supportsAudio: false,
@@ -1399,8 +1674,7 @@ function createAnthropic(config = {}) {
1399
1674
  "image/gif",
1400
1675
  "image/webp"
1401
1676
  ],
1402
- // Native `output_config.format` — GA on Claude 3.5 and newer.
1403
- supportsJsonMode: true,
1677
+ supportsJsonMode: anthropicModelRules(modelId).structured,
1404
1678
  supportsSystemMessages: true
1405
1679
  };
1406
1680
  };