@framers/agentos 0.10.22 → 0.10.24
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.
- package/README.md +1 -1
- package/dist/api/AgentOS.d.ts.map +1 -1
- package/dist/api/AgentOS.js +3 -2
- package/dist/api/AgentOS.js.map +1 -1
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +25 -10
- package/dist/api/agent.js.map +1 -1
- package/dist/api/embedText.d.ts.map +1 -1
- package/dist/api/embedText.js +53 -17
- package/dist/api/embedText.js.map +1 -1
- package/dist/api/generateImage.d.ts.map +1 -1
- package/dist/api/generateImage.js +51 -7
- package/dist/api/generateImage.js.map +1 -1
- package/dist/api/generateText.d.ts +50 -7
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +265 -35
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/model.d.ts +25 -1
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +108 -3
- package/dist/api/model.js.map +1 -1
- package/dist/api/runtime/SelfImprovementSessionManager.d.ts +34 -2
- package/dist/api/runtime/SelfImprovementSessionManager.d.ts.map +1 -1
- package/dist/api/runtime/SelfImprovementSessionManager.js +119 -33
- package/dist/api/runtime/SelfImprovementSessionManager.js.map +1 -1
- package/dist/api/runtime/global-default.d.ts +9 -1
- package/dist/api/runtime/global-default.d.ts.map +1 -1
- package/dist/api/runtime/global-default.js.map +1 -1
- package/dist/api/runtime/provider-defaults.d.ts.map +1 -1
- package/dist/api/runtime/provider-defaults.js +10 -4
- package/dist/api/runtime/provider-defaults.js.map +1 -1
- package/dist/api/runtime/tool-emulation/history.d.ts +16 -0
- package/dist/api/runtime/tool-emulation/history.d.ts.map +1 -0
- package/dist/api/runtime/tool-emulation/history.js +89 -0
- package/dist/api/runtime/tool-emulation/history.js.map +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/index.js +1 -0
- package/dist/api/runtime/tool-emulation/index.js.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.d.ts +6 -0
- package/dist/api/runtime/tool-emulation/loop.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.js +1 -0
- package/dist/api/runtime/tool-emulation/loop.js.map +1 -1
- package/dist/api/sessionTranscript.d.ts +34 -0
- package/dist/api/sessionTranscript.d.ts.map +1 -1
- package/dist/api/sessionTranscript.js +61 -1
- package/dist/api/sessionTranscript.js.map +1 -1
- package/dist/api/streamText.d.ts.map +1 -1
- package/dist/api/streamText.js +120 -24
- package/dist/api/streamText.js.map +1 -1
- package/dist/api/types/AgentOSInput.d.ts +11 -0
- package/dist/api/types/AgentOSInput.d.ts.map +1 -1
- package/dist/api/types.d.ts +14 -6
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/cognition/emergent/AdaptPersonalityTool.d.ts +13 -4
- package/dist/cognition/emergent/AdaptPersonalityTool.d.ts.map +1 -1
- package/dist/cognition/emergent/AdaptPersonalityTool.js +32 -23
- package/dist/cognition/emergent/AdaptPersonalityTool.js.map +1 -1
- package/dist/cognition/emergent/EmergentCapabilityEngine.d.ts +18 -6
- package/dist/cognition/emergent/EmergentCapabilityEngine.d.ts.map +1 -1
- package/dist/cognition/emergent/EmergentCapabilityEngine.js.map +1 -1
- package/dist/cognition/emergent/SelfEvaluateTool.d.ts +10 -3
- package/dist/cognition/emergent/SelfEvaluateTool.d.ts.map +1 -1
- package/dist/cognition/emergent/SelfEvaluateTool.js +3 -2
- package/dist/cognition/emergent/SelfEvaluateTool.js.map +1 -1
- package/dist/cognition/substrate/CognitiveMemoryBridge.d.ts +51 -2
- package/dist/cognition/substrate/CognitiveMemoryBridge.d.ts.map +1 -1
- package/dist/cognition/substrate/CognitiveMemoryBridge.js +84 -2
- package/dist/cognition/substrate/CognitiveMemoryBridge.js.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts +8 -4
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.js +17 -5
- package/dist/cognition/substrate/ConversationHistoryManager.js.map +1 -1
- package/dist/cognition/substrate/GMI.d.ts +92 -5
- package/dist/cognition/substrate/GMI.d.ts.map +1 -1
- package/dist/cognition/substrate/GMI.js +291 -65
- package/dist/cognition/substrate/GMI.js.map +1 -1
- package/dist/cognition/substrate/GMIManager.d.ts +12 -0
- package/dist/cognition/substrate/GMIManager.d.ts.map +1 -1
- package/dist/cognition/substrate/GMIManager.js +22 -16
- package/dist/cognition/substrate/GMIManager.js.map +1 -1
- package/dist/cognition/substrate/IGMI.d.ts +23 -0
- package/dist/cognition/substrate/IGMI.d.ts.map +1 -1
- package/dist/cognition/substrate/IGMI.js.map +1 -1
- package/dist/cognition/substrate/MetapromptExecutor.d.ts +183 -28
- package/dist/cognition/substrate/MetapromptExecutor.d.ts.map +1 -1
- package/dist/cognition/substrate/MetapromptExecutor.js +431 -108
- package/dist/cognition/substrate/MetapromptExecutor.js.map +1 -1
- package/dist/cognition/substrate/personas/IPersonaDefinition.d.ts +5 -0
- package/dist/cognition/substrate/personas/IPersonaDefinition.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/PersonaValidation.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/PersonaValidation.js +30 -0
- package/dist/cognition/substrate/personas/PersonaValidation.js.map +1 -1
- package/dist/cognition/substrate/personas/SoulLoader.d.ts +14 -1
- package/dist/cognition/substrate/personas/SoulLoader.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/SoulLoader.js +45 -12
- package/dist/cognition/substrate/personas/SoulLoader.js.map +1 -1
- package/dist/cognition/substrate/personas/definitions/default_assistant_persona.json +3 -35
- package/dist/cognition/substrate/personas/hexaco.d.ts +34 -0
- package/dist/cognition/substrate/personas/hexaco.d.ts.map +1 -0
- package/dist/cognition/substrate/personas/hexaco.js +66 -0
- package/dist/cognition/substrate/personas/hexaco.js.map +1 -0
- package/dist/cognition/substrate/userFeedback.d.ts +69 -0
- package/dist/cognition/substrate/userFeedback.d.ts.map +1 -0
- package/dist/cognition/substrate/userFeedback.js +107 -0
- package/dist/cognition/substrate/userFeedback.js.map +1 -0
- package/dist/core/conversation/ConversationMessage.d.ts +9 -0
- package/dist/core/conversation/ConversationMessage.d.ts.map +1 -1
- package/dist/core/conversation/ConversationMessage.js.map +1 -1
- package/dist/core/llm/IPromptEngine.d.ts +9 -0
- package/dist/core/llm/IPromptEngine.d.ts.map +1 -1
- package/dist/core/llm/IPromptEngine.js.map +1 -1
- package/dist/core/llm/PromptEngine.d.ts +30 -1
- package/dist/core/llm/PromptEngine.d.ts.map +1 -1
- package/dist/core/llm/PromptEngine.js +229 -92
- package/dist/core/llm/PromptEngine.js.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.d.ts +20 -0
- package/dist/core/llm/providers/AIModelProviderManager.d.ts.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.js +35 -5
- package/dist/core/llm/providers/AIModelProviderManager.js.map +1 -1
- package/dist/core/llm/providers/IProvider.d.ts +28 -8
- package/dist/core/llm/providers/IProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts +135 -49
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.js +323 -114
- package/dist/core/llm/providers/implementations/AnthropicProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js +15 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js +51 -24
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts +25 -6
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.js +108 -90
- package/dist/core/llm/providers/implementations/GeminiProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts +11 -0
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.js +32 -2
- package/dist/core/llm/providers/implementations/MistralProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OllamaProvider.js +7 -7
- package/dist/core/llm/providers/implementations/OllamaProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts +53 -2
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.js +84 -17
- package/dist/core/llm/providers/implementations/OpenAIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js +8 -3
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts +3 -3
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.js +25 -13
- package/dist/core/llm/providers/implementations/TogetherProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/cacheLeakDetector.d.ts +15 -7
- package/dist/core/llm/providers/implementations/cacheLeakDetector.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/cacheLeakDetector.js +12 -7
- package/dist/core/llm/providers/implementations/cacheLeakDetector.js.map +1 -1
- package/dist/core/llm/providers/model-cache-capabilities.d.ts +24 -9
- package/dist/core/llm/providers/model-cache-capabilities.d.ts.map +1 -1
- package/dist/core/llm/providers/model-cache-capabilities.js +59 -44
- package/dist/core/llm/providers/model-cache-capabilities.js.map +1 -1
- package/dist/core/llm/providers/model-effort.d.ts +14 -0
- package/dist/core/llm/providers/model-effort.d.ts.map +1 -1
- package/dist/core/llm/providers/model-effort.js +22 -0
- package/dist/core/llm/providers/model-effort.js.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts +13 -6
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.js +14 -7
- package/dist/core/llm/providers/model-forced-tool-choice.js.map +1 -1
- package/dist/core/llm/providers/model-thinking.d.ts +64 -13
- package/dist/core/llm/providers/model-thinking.d.ts.map +1 -1
- package/dist/core/llm/providers/model-thinking.js +52 -12
- package/dist/core/llm/providers/model-thinking.js.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.d.ts +26 -7
- package/dist/core/llm/providers/openrouter-only-params.d.ts.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.js +58 -9
- package/dist/core/llm/providers/openrouter-only-params.js.map +1 -1
- package/dist/core/llm/providers/url-secrets.d.ts +42 -0
- package/dist/core/llm/providers/url-secrets.d.ts.map +1 -0
- package/dist/core/llm/providers/url-secrets.js +64 -0
- package/dist/core/llm/providers/url-secrets.js.map +1 -0
- package/dist/core/llm/streaming/StreamingBatcher.d.ts +4 -3
- package/dist/core/llm/streaming/StreamingBatcher.d.ts.map +1 -1
- package/dist/core/llm/streaming/StreamingBatcher.js +8 -3
- package/dist/core/llm/streaming/StreamingBatcher.js.map +1 -1
- package/dist/core/safety/LLMProviderHealthRegistry.d.ts +4 -0
- package/dist/core/safety/LLMProviderHealthRegistry.d.ts.map +1 -1
- package/dist/core/safety/LLMProviderHealthRegistry.js +26 -0
- package/dist/core/safety/LLMProviderHealthRegistry.js.map +1 -1
- package/dist/core/utils/sha256.d.ts +19 -0
- package/dist/core/utils/sha256.d.ts.map +1 -0
- package/dist/core/utils/sha256.js +101 -0
- package/dist/core/utils/sha256.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestration/pipeline/query/types.js +3 -3
- package/dist/orchestration/pipeline/query/types.js.map +1 -1
- package/dist/orchestration/planning/ProviderAssignmentEngine.js +2 -2
- package/dist/orchestration/planning/ProviderAssignmentEngine.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// File: backend/agentos/core/llm/providers/implementations/AnthropicProvider.ts
|
|
2
|
-
import {
|
|
2
|
+
import { stripForeignVendorParams } from '../openrouter-only-params.js';
|
|
3
3
|
import { AnthropicProviderError } from '../errors/AnthropicProviderError.js';
|
|
4
4
|
import { ApiKeyPool } from '../../../providers/ApiKeyPool.js';
|
|
5
|
-
import { resolveThinkingPayload } from '../model-thinking.js';
|
|
5
|
+
import { resolveThinkingOff, resolveThinkingPayload } from '../model-thinking.js';
|
|
6
6
|
import { modelSupportsForcedToolChoice } from '../model-forced-tool-choice.js';
|
|
7
7
|
import { modelSupportsStrictToolUse, toolInputSchemaSupportsStrict, toolInputSchemaWithExplicitNoExtraProps, } from '../model-strict-tool-use.js';
|
|
8
|
-
import {
|
|
8
|
+
import { resolveAnthropicEffort } from '../model-effort.js';
|
|
9
9
|
import { computeRetryBackoffMs } from './retry-backoff.js';
|
|
10
10
|
import { recordCacheUsage } from './cacheLeakDetector.js';
|
|
11
11
|
import { resolveCacheCapabilities } from '../model-cache-capabilities.js';
|
|
12
|
+
import { redactUrlSecrets } from '../url-secrets.js';
|
|
12
13
|
// Re-export so callers that already reach for Anthropic model-capability
|
|
13
14
|
// predicates (modelSupportsTemperature lives here too) find this one next to
|
|
14
15
|
// it, even though its source of truth is the pure model-forced-tool-choice
|
|
@@ -21,11 +22,13 @@ export { modelSupportsForcedToolChoice };
|
|
|
21
22
|
* Whether the given Claude model id accepts the `temperature` parameter.
|
|
22
23
|
*
|
|
23
24
|
* Anthropic deprecated `temperature` on reasoning-default models. Opus 4.7,
|
|
24
|
-
* Opus 4.8, Opus 5, Opus 5.5, Sonnet 5, Fable 5 and Fable 5.1
|
|
25
|
-
* that include it with HTTP 400 "`temperature` is deprecated
|
|
26
|
-
* Opus 5, Opus 5.5 and Fable 5.1 were live-probed on
|
|
27
|
-
*
|
|
28
|
-
*
|
|
25
|
+
* Opus 4.8, Opus 5, Opus 5.5, Sonnet 5, Sonnet 5.5, Fable 5 and Fable 5.1
|
|
26
|
+
* reject requests that include it with HTTP 400 "`temperature` is deprecated
|
|
27
|
+
* for this model." Opus 5, Opus 5.5 and Fable 5.1 were live-probed on
|
|
28
|
+
* 2026-09-29 and Sonnet 5.5 on 2026-09-30. Opus 5.5, Sonnet 5.5 and Fable 5.1
|
|
29
|
+
* match the existing `opus-5`, `sonnet-5` and `fable-5` alternatives on
|
|
30
|
+
* purpose, because `\b` matches at the hyphen before their trailing version
|
|
31
|
+
* digit.
|
|
29
32
|
* Every earlier Claude model (Opus ≤ 4.6, Sonnet 4.6 and earlier, Haiku) still accepts it.
|
|
30
33
|
* The same family also rejects `top_p` / `top_k`, so {@link buildRequestPayload}
|
|
31
34
|
* gates `top_p` on this predicate too.
|
|
@@ -43,8 +46,8 @@ export { modelSupportsForcedToolChoice };
|
|
|
43
46
|
*/
|
|
44
47
|
export function modelSupportsTemperature(modelId) {
|
|
45
48
|
// Reasoning-default models that reject `temperature` (and `top_p` / `top_k`):
|
|
46
|
-
// Claude Opus 4.7 / 4.8 / 5 / 5.5, Sonnet 5, Fable 5 / 5.1, and any
|
|
47
|
-
// variant. Future reasoning-first siblings get added here as Anthropic
|
|
49
|
+
// Claude Opus 4.7 / 4.8 / 5 / 5.5, Sonnet 5 / 5.5, Fable 5 / 5.1, and any
|
|
50
|
+
// dated variant. Future reasoning-first siblings get added here as Anthropic
|
|
48
51
|
// releases them, in lockstep with modelSupportsThinking.
|
|
49
52
|
return !/^claude-(opus-4-(7|8)|opus-5|sonnet-5|fable-5)\b/i.test(modelId);
|
|
50
53
|
}
|
|
@@ -77,17 +80,22 @@ export function normalizeCacheDiagnostics(diagnostics) {
|
|
|
77
80
|
/**
|
|
78
81
|
* Static catalog of well-known Anthropic models and their metadata.
|
|
79
82
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
83
|
+
* Prices are USD per 1M tokens, verified against Anthropic's pricing page and
|
|
84
|
+
* model pages on 2026-09-30. A cache hit costs 0.1x the input price except
|
|
85
|
+
* where a row sets `pricePer1MTokensCacheRead`: Opus 5.5 (0.05x) and Fable 5.1
|
|
86
|
+
* (0.025x) differ, and Fable 5 states its standard 0.1x rate explicitly so
|
|
87
|
+
* the Fable 5.1 rate is not mistaken for it. Cache writes cost 1.25x the input
|
|
88
|
+
* price at the 5-minute TTL and 2x at the 1-hour TTL on every model; see
|
|
89
|
+
* {@link estimateAnthropicCostUSD}.
|
|
82
90
|
*
|
|
83
91
|
* `outputTokenLimit` is the model's real max output ceiling, surfaced via
|
|
84
|
-
* getModelInfo for callers that want to size requests. It is informational
|
|
85
|
-
*
|
|
86
|
-
* falls back to `config.defaultMaxTokens`,
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* Haiku 4.5
|
|
92
|
+
* getModelInfo for callers that want to size requests. It is informational,
|
|
93
|
+
* not the per-request default: when a caller omits `maxTokens`, the request
|
|
94
|
+
* falls back to `config.defaultMaxTokens`, but a per-call `maxTokens` is
|
|
95
|
+
* clamped to it via {@link clampAnthropicMaxTokens}. Anthropic's limits:
|
|
96
|
+
* Opus 5.5, Opus 5, Opus 4.6-4.8, Fable 5.1, Fable 5, Sonnet 5.5, Sonnet 5 and
|
|
97
|
+
* Sonnet 4.6 take 128K output with a 1M context; Opus 4.5, Sonnet 4.5 and
|
|
98
|
+
* Haiku 4.5 take 64K output with a 200K context.
|
|
91
99
|
*/
|
|
92
100
|
const ANTHROPIC_MODELS = [
|
|
93
101
|
{
|
|
@@ -100,6 +108,8 @@ const ANTHROPIC_MODELS = [
|
|
|
100
108
|
outputTokenLimit: 128000,
|
|
101
109
|
pricePer1MTokensInput: 4,
|
|
102
110
|
pricePer1MTokensOutput: 20,
|
|
111
|
+
// Cache hits and refreshes bill at 0.05x the input price.
|
|
112
|
+
pricePer1MTokensCacheRead: 0.2,
|
|
103
113
|
supportsStreaming: true,
|
|
104
114
|
status: 'active',
|
|
105
115
|
},
|
|
@@ -113,6 +123,8 @@ const ANTHROPIC_MODELS = [
|
|
|
113
123
|
outputTokenLimit: 128000,
|
|
114
124
|
pricePer1MTokensInput: 10,
|
|
115
125
|
pricePer1MTokensOutput: 50,
|
|
126
|
+
// Cache hits and refreshes bill at 0.025x the input price.
|
|
127
|
+
pricePer1MTokensCacheRead: 0.25,
|
|
116
128
|
supportsStreaming: true,
|
|
117
129
|
status: 'active',
|
|
118
130
|
},
|
|
@@ -126,6 +138,8 @@ const ANTHROPIC_MODELS = [
|
|
|
126
138
|
outputTokenLimit: 128000,
|
|
127
139
|
pricePer1MTokensInput: 10,
|
|
128
140
|
pricePer1MTokensOutput: 50,
|
|
141
|
+
// The standard 0.1x rate. Fable 5.1's 0.025x does not apply to Fable 5.
|
|
142
|
+
pricePer1MTokensCacheRead: 1,
|
|
129
143
|
supportsStreaming: true,
|
|
130
144
|
status: 'active',
|
|
131
145
|
},
|
|
@@ -181,6 +195,35 @@ const ANTHROPIC_MODELS = [
|
|
|
181
195
|
supportsStreaming: true,
|
|
182
196
|
status: 'active',
|
|
183
197
|
},
|
|
198
|
+
{
|
|
199
|
+
// The dated id is the canonical row: the bare alias `claude-opus-4-5`
|
|
200
|
+
// resolves to it through the prefix fallback in resolveModelCatalogEntry,
|
|
201
|
+
// as `claude-haiku-4-5` resolves to the dated Haiku row.
|
|
202
|
+
modelId: 'claude-opus-4-5-20251101',
|
|
203
|
+
providerId: 'anthropic',
|
|
204
|
+
displayName: 'Claude Opus 4.5',
|
|
205
|
+
description: 'Legacy Opus, still served. Superseded by Claude Opus 5.5.',
|
|
206
|
+
capabilities: ['chat', 'tool_use', 'vision_input'],
|
|
207
|
+
contextWindowSize: 200000,
|
|
208
|
+
outputTokenLimit: 64000,
|
|
209
|
+
pricePer1MTokensInput: 5,
|
|
210
|
+
pricePer1MTokensOutput: 25,
|
|
211
|
+
supportsStreaming: true,
|
|
212
|
+
status: 'active',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
modelId: 'claude-sonnet-5-5',
|
|
216
|
+
providerId: 'anthropic',
|
|
217
|
+
displayName: 'Claude Sonnet 5.5',
|
|
218
|
+
description: 'Current Sonnet for everyday coding, agent and enterprise work; adaptive thinking on by default.',
|
|
219
|
+
capabilities: ['chat', 'tool_use', 'vision_input'],
|
|
220
|
+
contextWindowSize: 1000000,
|
|
221
|
+
outputTokenLimit: 128000,
|
|
222
|
+
pricePer1MTokensInput: 2,
|
|
223
|
+
pricePer1MTokensOutput: 10,
|
|
224
|
+
supportsStreaming: true,
|
|
225
|
+
status: 'active',
|
|
226
|
+
},
|
|
184
227
|
{
|
|
185
228
|
modelId: 'claude-sonnet-5',
|
|
186
229
|
providerId: 'anthropic',
|
|
@@ -189,13 +232,14 @@ const ANTHROPIC_MODELS = [
|
|
|
189
232
|
capabilities: ['chat', 'tool_use', 'vision_input'],
|
|
190
233
|
contextWindowSize: 1000000,
|
|
191
234
|
outputTokenLimit: 128000,
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
// the 2026-07-20..26
|
|
196
|
-
// sonnet-5 leg calls
|
|
197
|
-
|
|
198
|
-
|
|
235
|
+
// $2/$10 is the standard price: Anthropic kept its launch pricing and
|
|
236
|
+
// cancelled the increase to $3/$15 scheduled for 2026-09-01 (pricing
|
|
237
|
+
// page, 2026-09-30). The row must stay in this table: without it the
|
|
238
|
+
// model prices as costUSD undefined, which turned the 2026-07-20..26
|
|
239
|
+
// quota-outage fallback traffic (thousands of sonnet-5 leg calls a day)
|
|
240
|
+
// into unmetered spend.
|
|
241
|
+
pricePer1MTokensInput: 2,
|
|
242
|
+
pricePer1MTokensOutput: 10,
|
|
199
243
|
supportsStreaming: true,
|
|
200
244
|
status: 'active',
|
|
201
245
|
},
|
|
@@ -293,9 +337,10 @@ export function clampAnthropicMaxTokens(modelId, requested) {
|
|
|
293
337
|
* Resolves a caller-supplied model id to its ANTHROPIC_MODELS catalog row.
|
|
294
338
|
* Exact id match wins; otherwise a dated snapshot (`claude-sonnet-5-20260101`)
|
|
295
339
|
* or bare alias is matched by prefix — the SAME resolution the max-tokens
|
|
296
|
-
* clamp uses, shared so pricing
|
|
297
|
-
* row a model id means (
|
|
298
|
-
* snapshot id as costUSD undefined:
|
|
340
|
+
* clamp uses, shared so pricing ({@link estimateAnthropicCostUSD}) and
|
|
341
|
+
* clamping can never disagree about which row a model id means (an
|
|
342
|
+
* exact-only match prices every dated snapshot id as costUSD undefined:
|
|
343
|
+
* unmetered spend).
|
|
299
344
|
*
|
|
300
345
|
* The longest catalog id that prefixes `modelId` wins. Catalog ids nest:
|
|
301
346
|
* `claude-opus-5` is a prefix of `claude-opus-5-5`. A first-match scan makes
|
|
@@ -315,9 +360,13 @@ export function resolveAnthropicModelEntry(modelId) {
|
|
|
315
360
|
* @param catalog Model rows to search, in any order.
|
|
316
361
|
* @param modelId Caller-supplied model id, bare or dated.
|
|
317
362
|
* @returns The exact row, else the row with the longest id that prefixes
|
|
318
|
-
* `modelId`, else the first row whose id starts with `modelId`.
|
|
363
|
+
* `modelId`, else the first row whose id starts with `modelId`. An empty id
|
|
364
|
+
* resolves to nothing: every catalog id starts with `''`, so the last rule
|
|
365
|
+
* would otherwise price a response with no model echo as the first row.
|
|
319
366
|
*/
|
|
320
367
|
export function resolveModelCatalogEntry(catalog, modelId) {
|
|
368
|
+
if (!modelId)
|
|
369
|
+
return undefined;
|
|
321
370
|
const exact = catalog.find((m) => m.modelId === modelId);
|
|
322
371
|
if (exact)
|
|
323
372
|
return exact;
|
|
@@ -329,9 +378,64 @@ export function resolveModelCatalogEntry(catalog, modelId) {
|
|
|
329
378
|
}
|
|
330
379
|
return longest ?? catalog.find((m) => m.modelId.startsWith(modelId));
|
|
331
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Estimates the USD cost of one Anthropic Messages API call from its usage.
|
|
383
|
+
*
|
|
384
|
+
* | Tokens | Price |
|
|
385
|
+
* | --- | --- |
|
|
386
|
+
* | `input_tokens` | the input price |
|
|
387
|
+
* | `cache_read_input_tokens` | the row's `pricePer1MTokensCacheRead`, else 0.1x the input price |
|
|
388
|
+
* | 5-minute cache writes | 1.25x the input price |
|
|
389
|
+
* | 1-hour cache writes | 2x the input price |
|
|
390
|
+
* | `output_tokens` | the output price |
|
|
391
|
+
*
|
|
392
|
+
* `input_tokens` excludes cached tokens, so the input components add up
|
|
393
|
+
* without double counting. The 1-hour share of the writes comes from
|
|
394
|
+
* `cache_creation.ephemeral_1h_input_tokens`: a response without that split
|
|
395
|
+
* is priced as 5-minute writes only, and a 1-hour count above the write total
|
|
396
|
+
* is capped at the total.
|
|
397
|
+
*
|
|
398
|
+
* @param modelId Model id, bare or dated, resolved with
|
|
399
|
+
* {@link resolveAnthropicModelEntry}.
|
|
400
|
+
* @param usage Token counts from the response.
|
|
401
|
+
* @returns The cost in USD, or `undefined` when the catalog has no price for
|
|
402
|
+
* the model (unknown, empty or unpriced id).
|
|
403
|
+
*/
|
|
404
|
+
export function estimateAnthropicCostUSD(modelId, usage) {
|
|
405
|
+
const info = resolveAnthropicModelEntry(modelId);
|
|
406
|
+
const inputPrice = info?.pricePer1MTokensInput;
|
|
407
|
+
const outputPrice = info?.pricePer1MTokensOutput;
|
|
408
|
+
if (!inputPrice || !outputPrice)
|
|
409
|
+
return undefined;
|
|
410
|
+
const cacheReadPrice = info?.pricePer1MTokensCacheRead ?? inputPrice * 0.1;
|
|
411
|
+
const split = usage.cache_creation ?? undefined;
|
|
412
|
+
const written = usage.cache_creation_input_tokens
|
|
413
|
+
?? ((split?.ephemeral_5m_input_tokens ?? 0) + (split?.ephemeral_1h_input_tokens ?? 0));
|
|
414
|
+
const writtenOneHour = Math.min(Math.max(split?.ephemeral_1h_input_tokens ?? 0, 0), written);
|
|
415
|
+
// Tokens times USD per 1M tokens: the sum is in millionths of a dollar.
|
|
416
|
+
const microDollars = usage.input_tokens * inputPrice
|
|
417
|
+
+ (usage.cache_read_input_tokens ?? 0) * cacheReadPrice
|
|
418
|
+
+ (written - writtenOneHour) * inputPrice * 1.25
|
|
419
|
+
+ writtenOneHour * inputPrice * 2
|
|
420
|
+
+ usage.output_tokens * outputPrice;
|
|
421
|
+
return microDollars / 1000000;
|
|
422
|
+
}
|
|
332
423
|
// ---------------------------------------------------------------------------
|
|
333
424
|
// Provider implementation
|
|
334
425
|
// ---------------------------------------------------------------------------
|
|
426
|
+
/** Keys of warnings already printed by {@link warnOnce}. */
|
|
427
|
+
const warnedKeys = new Set();
|
|
428
|
+
/**
|
|
429
|
+
* Prints `message` once per process for `key`, so a setting applied to every
|
|
430
|
+
* request (thinking off on an always-on model, a capped effort) warns once
|
|
431
|
+
* rather than on every call.
|
|
432
|
+
*/
|
|
433
|
+
function warnOnce(key, message) {
|
|
434
|
+
if (warnedKeys.has(key))
|
|
435
|
+
return;
|
|
436
|
+
warnedKeys.add(key);
|
|
437
|
+
console.warn(message);
|
|
438
|
+
}
|
|
335
439
|
/**
|
|
336
440
|
* @class AnthropicProvider
|
|
337
441
|
* @implements {IProvider}
|
|
@@ -427,7 +531,10 @@ export class AnthropicProvider {
|
|
|
427
531
|
* @param {ModelCompletionOptions} options - Completion options. `maxTokens` is strongly
|
|
428
532
|
* recommended; defaults to {@link AnthropicProviderConfig.defaultMaxTokens} if omitted.
|
|
429
533
|
* @returns {Promise<ModelCompletionResponse>} A normalized completion response.
|
|
430
|
-
* @throws {AnthropicProviderError} On authentication, validation, or network
|
|
534
|
+
* @throws {AnthropicProviderError} On authentication, validation, or network
|
|
535
|
+
* errors, and with code `content_filter` when the model refuses
|
|
536
|
+
* (`stop_reason: 'refusal'`); partial output from a refused turn is
|
|
537
|
+
* discarded.
|
|
431
538
|
*/
|
|
432
539
|
async generateCompletion(modelId, messages, options) {
|
|
433
540
|
this.ensureInitialized();
|
|
@@ -442,8 +549,8 @@ export class AnthropicProvider {
|
|
|
442
549
|
if (this.config.streamCompletions === false) {
|
|
443
550
|
const payload = this.buildRequestPayload(modelId, messages, options, false);
|
|
444
551
|
const apiResponse = await this.makeApiRequest('/v1/messages', 'POST', payload, options.requestTimeout);
|
|
445
|
-
this.recordCacheLeakSample(modelId, payload, apiResponse);
|
|
446
|
-
return this.mapResponseToCompletion(apiResponse, structuredOutputName);
|
|
552
|
+
this.recordCacheLeakSample(modelId, payload, apiResponse, options.cache === false);
|
|
553
|
+
return this.mapResponseToCompletion(apiResponse, structuredOutputName, modelId);
|
|
447
554
|
}
|
|
448
555
|
// Default: ride the SSE path so parseSseStream's idle watchdog bounds
|
|
449
556
|
// mid-body stalls. A non-streaming POST cannot tell a hung connection
|
|
@@ -454,8 +561,8 @@ export class AnthropicProvider {
|
|
|
454
561
|
// let the caller (or the retry loop below) recover in seconds.
|
|
455
562
|
const payload = this.buildRequestPayload(modelId, messages, options, true);
|
|
456
563
|
const apiResponse = await this.streamMessagesToResponse(payload, options.requestTimeout);
|
|
457
|
-
this.recordCacheLeakSample(modelId, payload, apiResponse);
|
|
458
|
-
return this.mapResponseToCompletion(apiResponse, structuredOutputName);
|
|
564
|
+
this.recordCacheLeakSample(modelId, payload, apiResponse, options.cache === false);
|
|
565
|
+
return this.mapResponseToCompletion(apiResponse, structuredOutputName, modelId);
|
|
459
566
|
}
|
|
460
567
|
/**
|
|
461
568
|
* Feed one request's cache usage to the leak detector (see
|
|
@@ -464,9 +571,12 @@ export class AnthropicProvider {
|
|
|
464
571
|
* 256 chars of the system prompt, read from the just-built payload so it
|
|
465
572
|
* reflects what actually went to the wire. Fail-open by construction.
|
|
466
573
|
*
|
|
574
|
+
* @param cacheOptOut True when the caller turned caching off for this
|
|
575
|
+
* request (`options.cache === false`). Its uncached input is deliberate,
|
|
576
|
+
* so the detector does not count it toward the `unmarked` warning.
|
|
467
577
|
* @private
|
|
468
578
|
*/
|
|
469
|
-
recordCacheLeakSample(modelId, payload, apiResponse) {
|
|
579
|
+
recordCacheLeakSample(modelId, payload, apiResponse, cacheOptOut = false) {
|
|
470
580
|
try {
|
|
471
581
|
const system = payload.system;
|
|
472
582
|
// Bound both branches to the 256-char callsite-identity window the
|
|
@@ -483,6 +593,7 @@ export class AnthropicProvider {
|
|
|
483
593
|
uncachedInputTokens: apiResponse.usage?.input_tokens ?? 0,
|
|
484
594
|
cacheReadTokens: apiResponse.usage?.cache_read_input_tokens ?? 0,
|
|
485
595
|
cacheCreationTokens: apiResponse.usage?.cache_creation_input_tokens ?? 0,
|
|
596
|
+
...(cacheOptOut ? { cacheOptOut: true } : {}),
|
|
486
597
|
});
|
|
487
598
|
}
|
|
488
599
|
catch {
|
|
@@ -564,6 +675,11 @@ export class AnthropicProvider {
|
|
|
564
675
|
* {@link mapResponseToCompletion} stays the single mapping path
|
|
565
676
|
* (structured output, stop reasons, cost, thinking blocks).
|
|
566
677
|
*
|
|
678
|
+
* A refusal returns normally with `stop_reason: 'refusal'` and its
|
|
679
|
+
* `stop_details`, even when it cut a tool_use input off mid-JSON:
|
|
680
|
+
* {@link mapResponseToCompletion} raises it as a content-policy error, and
|
|
681
|
+
* a transport error here would be retried instead.
|
|
682
|
+
*
|
|
567
683
|
* @throws {AnthropicProviderError} `STREAM_ERROR_EVENT` on an SSE error
|
|
568
684
|
* event, `STREAM_INCOMPLETE` when the stream ends before `message_delta`
|
|
569
685
|
* or a tool_use input JSON is truncated, plus whatever
|
|
@@ -574,10 +690,12 @@ export class AnthropicProvider {
|
|
|
574
690
|
let id = `anthropic-stream-${Date.now()}`;
|
|
575
691
|
let model = '';
|
|
576
692
|
let stopReason = null;
|
|
693
|
+
let stopDetails;
|
|
577
694
|
let inputTokens = 0;
|
|
578
695
|
let outputTokens = 0;
|
|
579
696
|
let cacheCreationTokens;
|
|
580
697
|
let cacheReadTokens;
|
|
698
|
+
let cacheCreationSplit;
|
|
581
699
|
let diagnostics;
|
|
582
700
|
let sawMessageDelta = false;
|
|
583
701
|
const blocks = new Map();
|
|
@@ -597,6 +715,8 @@ export class AnthropicProvider {
|
|
|
597
715
|
inputTokens = event.message.usage?.input_tokens ?? 0;
|
|
598
716
|
cacheCreationTokens = event.message.usage?.cache_creation_input_tokens;
|
|
599
717
|
cacheReadTokens = event.message.usage?.cache_read_input_tokens;
|
|
718
|
+
// The 5m/1h split of the cache writes prices the 1h share at 2x.
|
|
719
|
+
cacheCreationSplit = event.message.usage?.cache_creation;
|
|
600
720
|
// Cache diagnostics ride message_start in streaming responses.
|
|
601
721
|
diagnostics = event.message.diagnostics;
|
|
602
722
|
break;
|
|
@@ -647,6 +767,7 @@ export class AnthropicProvider {
|
|
|
647
767
|
case 'message_delta': {
|
|
648
768
|
sawMessageDelta = true;
|
|
649
769
|
stopReason = (event.delta.stop_reason ?? null);
|
|
770
|
+
stopDetails = event.delta.stop_details ?? stopDetails;
|
|
650
771
|
// Anthropic reports the cumulative output total here — latest wins.
|
|
651
772
|
outputTokens = event.usage?.output_tokens ?? outputTokens;
|
|
652
773
|
break;
|
|
@@ -663,6 +784,9 @@ export class AnthropicProvider {
|
|
|
663
784
|
throw new AnthropicProviderError('Stream ended before message_delta — response incomplete (connection dropped mid-message).', 'STREAM_INCOMPLETE');
|
|
664
785
|
}
|
|
665
786
|
const content = Array.from(blocks.entries())
|
|
787
|
+
// A refusal can cut a tool input off mid-JSON. The refused turn is
|
|
788
|
+
// discarded whole, so its tool calls are dropped rather than parsed.
|
|
789
|
+
.filter(([, block]) => stopReason !== 'refusal' || block.type !== 'tool_use')
|
|
666
790
|
.sort((a, b) => a[0] - b[0])
|
|
667
791
|
.map(([, block]) => {
|
|
668
792
|
if (block.type === 'tool_use') {
|
|
@@ -691,11 +815,13 @@ export class AnthropicProvider {
|
|
|
691
815
|
model,
|
|
692
816
|
stop_reason: stopReason,
|
|
693
817
|
stop_sequence: null,
|
|
818
|
+
...(stopDetails !== undefined && { stop_details: stopDetails }),
|
|
694
819
|
usage: {
|
|
695
820
|
input_tokens: inputTokens,
|
|
696
821
|
output_tokens: outputTokens,
|
|
697
822
|
...(cacheCreationTokens !== undefined && { cache_creation_input_tokens: cacheCreationTokens }),
|
|
698
823
|
...(cacheReadTokens !== undefined && { cache_read_input_tokens: cacheReadTokens }),
|
|
824
|
+
...(cacheCreationSplit != null && { cache_creation: cacheCreationSplit }),
|
|
699
825
|
},
|
|
700
826
|
...(diagnostics !== undefined && { diagnostics }),
|
|
701
827
|
};
|
|
@@ -718,7 +844,10 @@ export class AnthropicProvider {
|
|
|
718
844
|
* @param {ChatMessage[]} messages - Conversation messages.
|
|
719
845
|
* @param {ModelCompletionOptions} options - Completion options.
|
|
720
846
|
* @returns {AsyncGenerator<ModelCompletionResponse>} Incremental response chunks.
|
|
721
|
-
* @throws {AnthropicProviderError} On connection
|
|
847
|
+
* @throws {AnthropicProviderError} On connection errors, and with code
|
|
848
|
+
* `content_filter` when the model refuses (`stop_reason: 'refusal'`),
|
|
849
|
+
* before or after text has streamed. Other stream errors end the stream
|
|
850
|
+
* with an error chunk.
|
|
722
851
|
*/
|
|
723
852
|
async *generateCompletionStream(modelId, messages, options) {
|
|
724
853
|
this.ensureInitialized();
|
|
@@ -737,6 +866,8 @@ export class AnthropicProvider {
|
|
|
737
866
|
let outputTokens = 0;
|
|
738
867
|
let cacheCreationTokens;
|
|
739
868
|
let cacheReadTokens;
|
|
869
|
+
/** The 5m/1h split of the cache writes, reported on message_start. */
|
|
870
|
+
let cacheCreationSplit;
|
|
740
871
|
/**
|
|
741
872
|
* Cache-diagnostics verdict, delivered ONLY on message_start (no later
|
|
742
873
|
* SSE event repeats it). `undefined` = the API sent none (not opted in);
|
|
@@ -779,6 +910,7 @@ export class AnthropicProvider {
|
|
|
779
910
|
// accumulate path at streamToCompletion does the same).
|
|
780
911
|
cacheCreationTokens = event.message.usage?.cache_creation_input_tokens;
|
|
781
912
|
cacheReadTokens = event.message.usage?.cache_read_input_tokens;
|
|
913
|
+
cacheCreationSplit = event.message.usage?.cache_creation;
|
|
782
914
|
// Cache diagnostics ride message_start in streaming responses
|
|
783
915
|
// (mirrors the streamToCompletion aggregate path).
|
|
784
916
|
streamDiagnostics = event.message.diagnostics;
|
|
@@ -894,8 +1026,14 @@ export class AnthropicProvider {
|
|
|
894
1026
|
// Cache-aware like the non-streaming path: input_tokens
|
|
895
1027
|
// excludes cached tokens, so dropping the cache counts here
|
|
896
1028
|
// undercosted every streamed turn once automatic caching
|
|
897
|
-
// landed (reads
|
|
898
|
-
costUSD:
|
|
1029
|
+
// landed (cache reads and writes are real spend).
|
|
1030
|
+
costUSD: estimateAnthropicCostUSD(modelId, {
|
|
1031
|
+
input_tokens: inputTokens,
|
|
1032
|
+
output_tokens: outputTokens,
|
|
1033
|
+
cache_read_input_tokens: cacheReadTokens,
|
|
1034
|
+
cache_creation_input_tokens: cacheCreationTokens,
|
|
1035
|
+
cache_creation: cacheCreationSplit,
|
|
1036
|
+
}),
|
|
899
1037
|
...(cacheCreationTokens !== undefined && {
|
|
900
1038
|
cacheCreationInputTokens: cacheCreationTokens,
|
|
901
1039
|
}),
|
|
@@ -925,7 +1063,14 @@ export class AnthropicProvider {
|
|
|
925
1063
|
cache_read_input_tokens: cacheReadTokens,
|
|
926
1064
|
}),
|
|
927
1065
|
},
|
|
928
|
-
});
|
|
1066
|
+
}, options.cache === false);
|
|
1067
|
+
// A refusal ends the turn unsuccessfully, whatever streamed before
|
|
1068
|
+
// it. Anthropic's contract is to discard the partial output; a
|
|
1069
|
+
// final chunk would let the caller keep it as the answer and run
|
|
1070
|
+
// tool calls parsed out of it.
|
|
1071
|
+
if (event.delta.stop_reason === 'refusal') {
|
|
1072
|
+
throw this.refusalError(modelId, event.delta.stop_details, accumulatedContent, usage);
|
|
1073
|
+
}
|
|
929
1074
|
yield {
|
|
930
1075
|
id: responseId,
|
|
931
1076
|
object: 'chat.completion.chunk',
|
|
@@ -974,6 +1119,12 @@ export class AnthropicProvider {
|
|
|
974
1119
|
}
|
|
975
1120
|
}
|
|
976
1121
|
catch (streamError) {
|
|
1122
|
+
// A refusal propagates as a thrown content-policy error, as
|
|
1123
|
+
// GeminiProvider's safety block does, so the fallback chain can act on
|
|
1124
|
+
// it instead of reading an error chunk.
|
|
1125
|
+
if (streamError instanceof AnthropicProviderError && streamError.code === 'content_filter') {
|
|
1126
|
+
throw streamError;
|
|
1127
|
+
}
|
|
977
1128
|
const message = streamError instanceof Error ? streamError.message : 'Anthropic stream processing error';
|
|
978
1129
|
console.error(`AnthropicProvider stream error for model ${modelId}:`, message);
|
|
979
1130
|
yield {
|
|
@@ -1234,14 +1385,39 @@ export class AnthropicProvider {
|
|
|
1234
1385
|
delete payload.temperature;
|
|
1235
1386
|
delete payload.top_p;
|
|
1236
1387
|
}
|
|
1388
|
+
// --- Thinking off (options.thinking === false) ---
|
|
1389
|
+
// Leaving the field out keeps the model's default, which is thinking ON
|
|
1390
|
+
// for Opus 5 and Sonnet 5+, so turning it off takes the model's own
|
|
1391
|
+
// shape. Some models accept that shape only at effort high or below; the
|
|
1392
|
+
// effort block below caps it there.
|
|
1393
|
+
let thinkingOffEffortCap;
|
|
1394
|
+
if (!thinkingResolved && options.thinking === false) {
|
|
1395
|
+
const off = resolveThinkingOff(modelId);
|
|
1396
|
+
if (off.kind === 'send') {
|
|
1397
|
+
payload.thinking = off.thinking;
|
|
1398
|
+
thinkingOffEffortCap = off.maxEffort;
|
|
1399
|
+
}
|
|
1400
|
+
else if (off.kind === 'always_on') {
|
|
1401
|
+
warnOnce(`thinking-always-on:${modelId}`, `[agentos] AnthropicProvider: thinking cannot be turned off on ${modelId}; the request runs ` +
|
|
1402
|
+
`with adaptive thinking. Lower options.effort to reduce it.`);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1237
1405
|
// --- Effort (output_config.effort) ---
|
|
1238
1406
|
// Reasoning depth + token-spend control on effort-capable Claude models
|
|
1239
1407
|
// (Opus 4.5+/Sonnet 4.6/Fable/Mythos). Independent of thinking + tool_choice
|
|
1240
|
-
// — it rides on output_config. Dropped on unsupported models
|
|
1241
|
-
// values so an out-of-range
|
|
1242
|
-
|
|
1408
|
+
// — it rides on output_config. Dropped on unsupported models or invalid
|
|
1409
|
+
// values, and lowered to a level the model takes, so an out-of-range
|
|
1410
|
+
// effort never returns 400 (see resolveAnthropicEffort).
|
|
1411
|
+
let effort = resolveAnthropicEffort(modelId, options.effort);
|
|
1412
|
+
if (thinkingOffEffortCap && (effort === 'xhigh' || effort === 'max')) {
|
|
1413
|
+
warnOnce(`thinking-off-effort:${modelId}`, `[agentos] AnthropicProvider: ${modelId} turns thinking off only at effort ` +
|
|
1414
|
+
`${thinkingOffEffortCap} or below; sending effort '${thinkingOffEffortCap}' ` +
|
|
1415
|
+
`instead of '${effort}'.`);
|
|
1416
|
+
effort = thinkingOffEffortCap;
|
|
1417
|
+
}
|
|
1418
|
+
if (effort) {
|
|
1243
1419
|
const oc = payload.output_config ?? {};
|
|
1244
|
-
payload.output_config = { ...oc, effort
|
|
1420
|
+
payload.output_config = { ...oc, effort };
|
|
1245
1421
|
}
|
|
1246
1422
|
// --- Cache diagnostics (beta cache-diagnosis-2026-04-07) ---
|
|
1247
1423
|
// Opt-in observability: the API compares this request against the one
|
|
@@ -1332,8 +1508,9 @@ export class AnthropicProvider {
|
|
|
1332
1508
|
payload.tool_choice = { type: 'tool', name: sf.tool.name };
|
|
1333
1509
|
}
|
|
1334
1510
|
// Pass through any custom model params — minus OpenRouter's routing
|
|
1335
|
-
// controls
|
|
1336
|
-
// the same params object across hosts; see
|
|
1511
|
+
// controls and Gemini's request fields, which only those vendors' bodies
|
|
1512
|
+
// accept (a fallback leg reuses the same params object across hosts; see
|
|
1513
|
+
// openrouter-only-params).
|
|
1337
1514
|
//
|
|
1338
1515
|
// This runs BEFORE the forced tool_choice reconciliation below so that a
|
|
1339
1516
|
// `tool_choice` injected through customModelParams is reconciled too —
|
|
@@ -1348,7 +1525,7 @@ export class AnthropicProvider {
|
|
|
1348
1525
|
let customSystemOverride = false;
|
|
1349
1526
|
let customMessagesOverride = false;
|
|
1350
1527
|
{
|
|
1351
|
-
const passthrough =
|
|
1528
|
+
const passthrough = stripForeignVendorParams(options.customModelParams);
|
|
1352
1529
|
if (passthrough) {
|
|
1353
1530
|
customSystemOverride = Object.prototype.hasOwnProperty.call(passthrough, 'system');
|
|
1354
1531
|
customMessagesOverride = Object.prototype.hasOwnProperty.call(passthrough, 'messages');
|
|
@@ -1372,7 +1549,10 @@ export class AnthropicProvider {
|
|
|
1372
1549
|
{
|
|
1373
1550
|
const tc = payload.tool_choice;
|
|
1374
1551
|
if (tc && (tc.type === 'any' || tc.type === 'tool')) {
|
|
1375
|
-
|
|
1552
|
+
// Only a thinking-on shape conflicts with a forced tool_choice; the
|
|
1553
|
+
// off shapes (disabled, between_tools) do not.
|
|
1554
|
+
const thinkingType = payload.thinking?.type;
|
|
1555
|
+
const thinkingActive = thinkingType === 'adaptive' || thinkingType === 'enabled';
|
|
1376
1556
|
const modelRejectsForced = !modelSupportsForcedToolChoice(modelId);
|
|
1377
1557
|
if (thinkingActive || modelRejectsForced) {
|
|
1378
1558
|
const reason = thinkingActive
|
|
@@ -1756,6 +1936,30 @@ export class AnthropicProvider {
|
|
|
1756
1936
|
}],
|
|
1757
1937
|
};
|
|
1758
1938
|
}
|
|
1939
|
+
// --- Assistant text turn that carries thinking ---
|
|
1940
|
+
// A session transcript records a final answer's signed thinking; it
|
|
1941
|
+
// replays verbatim ahead of the answer's content, like a tool turn's.
|
|
1942
|
+
// The content converts exactly as it would without thinking, so text
|
|
1943
|
+
// parts keep their cache_control breakpoints. For models that do not
|
|
1944
|
+
// retain prior thinking, the caller strips it from every assistant turn
|
|
1945
|
+
// but the last one before this runs.
|
|
1946
|
+
if (msg.role === 'assistant' && msg.thinkingBlocks?.length) {
|
|
1947
|
+
const rest = this.toAnthropicMessage({ ...msg, thinkingBlocks: undefined });
|
|
1948
|
+
const restContent = rest.content;
|
|
1949
|
+
const blocks = Array.isArray(restContent)
|
|
1950
|
+
? restContent
|
|
1951
|
+
: typeof restContent === 'string' && restContent
|
|
1952
|
+
? [{ type: 'text', text: restContent }]
|
|
1953
|
+
: [];
|
|
1954
|
+
// An answer with no content keeps the plain shape: Anthropic rejects an
|
|
1955
|
+
// assistant turn that holds only thinking.
|
|
1956
|
+
if (blocks.length === 0)
|
|
1957
|
+
return rest;
|
|
1958
|
+
const thinking = msg.thinkingBlocks.map((tb) => tb.type === 'thinking'
|
|
1959
|
+
? { type: 'thinking', thinking: tb.thinking, signature: tb.signature }
|
|
1960
|
+
: { type: 'redacted_thinking', data: tb.data });
|
|
1961
|
+
return { role: 'assistant', content: [...thinking, ...blocks] };
|
|
1962
|
+
}
|
|
1759
1963
|
// --- Multimodal content (vision) ---
|
|
1760
1964
|
if (Array.isArray(msg.content)) {
|
|
1761
1965
|
const anthropicContent = [];
|
|
@@ -1865,15 +2069,43 @@ export class AnthropicProvider {
|
|
|
1865
2069
|
* the stop reason from Anthropic's vocabulary to IProvider conventions.
|
|
1866
2070
|
*
|
|
1867
2071
|
* @param {AnthropicMessagesResponse} apiResponse - Raw Anthropic response.
|
|
2072
|
+
* @param structuredOutputName - Name of the forced structured-output tool,
|
|
2073
|
+
* whose input becomes the message content.
|
|
2074
|
+
* @param requestedModelId - The model the request named. Prices the call
|
|
2075
|
+
* when the response carries no model echo.
|
|
1868
2076
|
* @returns {ModelCompletionResponse} Normalized completion response.
|
|
2077
|
+
* @throws {AnthropicProviderError} With code `content_filter` when the model
|
|
2078
|
+
* refused (`stop_reason: 'refusal'`), whether or not it wrote any output
|
|
2079
|
+
* first.
|
|
1869
2080
|
* @private
|
|
1870
2081
|
*/
|
|
1871
|
-
mapResponseToCompletion(apiResponse, structuredOutputName) {
|
|
2082
|
+
mapResponseToCompletion(apiResponse, structuredOutputName, requestedModelId) {
|
|
2083
|
+
const pricedModelId = apiResponse.model || requestedModelId || '';
|
|
1872
2084
|
// Collect text content
|
|
1873
2085
|
const textParts = apiResponse.content
|
|
1874
2086
|
.filter(block => block.type === 'text' && block.text)
|
|
1875
2087
|
.map(block => block.text);
|
|
1876
2088
|
let fullText = textParts.join('');
|
|
2089
|
+
const usage = {
|
|
2090
|
+
promptTokens: apiResponse.usage.input_tokens,
|
|
2091
|
+
completionTokens: apiResponse.usage.output_tokens,
|
|
2092
|
+
totalTokens: apiResponse.usage.input_tokens + apiResponse.usage.output_tokens,
|
|
2093
|
+
costUSD: estimateAnthropicCostUSD(pricedModelId, apiResponse.usage),
|
|
2094
|
+
cacheCreationInputTokens: apiResponse.usage.cache_creation_input_tokens,
|
|
2095
|
+
cacheReadInputTokens: apiResponse.usage.cache_read_input_tokens,
|
|
2096
|
+
// Anthropic's input_tokens EXCLUDES cache reads/writes; the
|
|
2097
|
+
// provider-independent inclusive input total adds them back
|
|
2098
|
+
// (OpenAI/OpenRouter report prompt_tokens already inclusive).
|
|
2099
|
+
inclusiveInputTokens: apiResponse.usage.input_tokens
|
|
2100
|
+
+ (apiResponse.usage.cache_read_input_tokens ?? 0)
|
|
2101
|
+
+ (apiResponse.usage.cache_creation_input_tokens ?? 0),
|
|
2102
|
+
};
|
|
2103
|
+
// A refused turn is discarded whole, even when the model wrote text or
|
|
2104
|
+
// tool_use blocks before refusing, so it never reaches tool-call mapping
|
|
2105
|
+
// and the caller can never run a tool from it.
|
|
2106
|
+
if (apiResponse.stop_reason === 'refusal') {
|
|
2107
|
+
throw this.refusalError(pricedModelId, apiResponse.stop_details, fullText, usage);
|
|
2108
|
+
}
|
|
1877
2109
|
// Collect tool_use blocks and convert to OpenAI-style tool_calls
|
|
1878
2110
|
const toolCalls = apiResponse.content
|
|
1879
2111
|
.filter(block => block.type === 'tool_use')
|
|
@@ -1913,20 +2145,6 @@ export class AnthropicProvider {
|
|
|
1913
2145
|
}
|
|
1914
2146
|
const hasToolCalls = toolCalls.length > 0;
|
|
1915
2147
|
const finishReason = this.mapStopReason(apiResponse.stop_reason);
|
|
1916
|
-
const usage = {
|
|
1917
|
-
promptTokens: apiResponse.usage.input_tokens,
|
|
1918
|
-
completionTokens: apiResponse.usage.output_tokens,
|
|
1919
|
-
totalTokens: apiResponse.usage.input_tokens + apiResponse.usage.output_tokens,
|
|
1920
|
-
costUSD: this.estimateCost(apiResponse.usage.input_tokens, apiResponse.usage.output_tokens, apiResponse.model, apiResponse.usage.cache_read_input_tokens, apiResponse.usage.cache_creation_input_tokens),
|
|
1921
|
-
cacheCreationInputTokens: apiResponse.usage.cache_creation_input_tokens,
|
|
1922
|
-
cacheReadInputTokens: apiResponse.usage.cache_read_input_tokens,
|
|
1923
|
-
// Anthropic's input_tokens EXCLUDES cache reads/writes; the
|
|
1924
|
-
// provider-independent inclusive input total adds them back
|
|
1925
|
-
// (OpenAI/OpenRouter report prompt_tokens already inclusive).
|
|
1926
|
-
inclusiveInputTokens: apiResponse.usage.input_tokens
|
|
1927
|
-
+ (apiResponse.usage.cache_read_input_tokens ?? 0)
|
|
1928
|
-
+ (apiResponse.usage.cache_creation_input_tokens ?? 0),
|
|
1929
|
-
};
|
|
1930
2148
|
const choice = {
|
|
1931
2149
|
index: 0,
|
|
1932
2150
|
message: {
|
|
@@ -1955,7 +2173,14 @@ export class AnthropicProvider {
|
|
|
1955
2173
|
* - `end_turn` → `"stop"` (natural completion)
|
|
1956
2174
|
* - `tool_use` → `"tool_calls"` (model wants to invoke tools)
|
|
1957
2175
|
* - `max_tokens` → `"length"` (hit token limit)
|
|
2176
|
+
* - `model_context_window_exceeded` → `"length"` (output cut off at the
|
|
2177
|
+
* context window)
|
|
1958
2178
|
* - `stop_sequence` → `"stop"` (hit a caller-specified stop sequence)
|
|
2179
|
+
* - `refusal` → `"content_filter"`, the name GeminiProvider and
|
|
2180
|
+
* OpenAIProvider use. Both response paths raise a refusal as an error
|
|
2181
|
+
* before a finish reason is reported; the mapping keeps the vocabulary
|
|
2182
|
+
* complete.
|
|
2183
|
+
* - anything else (such as `pause_turn`) passes through unchanged.
|
|
1959
2184
|
*
|
|
1960
2185
|
* @param {string | null} stopReason - Anthropic's stop_reason value.
|
|
1961
2186
|
* @returns {string} Normalized finish reason.
|
|
@@ -1966,10 +2191,32 @@ export class AnthropicProvider {
|
|
|
1966
2191
|
case 'end_turn': return 'stop';
|
|
1967
2192
|
case 'tool_use': return 'tool_calls';
|
|
1968
2193
|
case 'max_tokens': return 'length';
|
|
2194
|
+
case 'model_context_window_exceeded': return 'length';
|
|
1969
2195
|
case 'stop_sequence': return 'stop';
|
|
2196
|
+
case 'refusal': return 'content_filter';
|
|
1970
2197
|
default: return stopReason ?? 'stop';
|
|
1971
2198
|
}
|
|
1972
2199
|
}
|
|
2200
|
+
/**
|
|
2201
|
+
* The error a refused turn raises. Code `content_filter` is what
|
|
2202
|
+
* `isContentPolicyRefusal` recognizes, so the generateText and streamText
|
|
2203
|
+
* fallback chains act on it, and the provider-health registry does not
|
|
2204
|
+
* count it as a provider failure. The message carries no HTTP status or
|
|
2205
|
+
* rate-limit wording, which retry classifiers grep for.
|
|
2206
|
+
*
|
|
2207
|
+
* @param modelId Model that refused.
|
|
2208
|
+
* @param stopDetails The response's `stop_details`, kept verbatim.
|
|
2209
|
+
* @param partialText Text the model wrote before refusing, discarded as an
|
|
2210
|
+
* answer but kept for diagnostics.
|
|
2211
|
+
* @param usage Tokens the refused call consumed.
|
|
2212
|
+
* @returns An error with code `content_filter` and Anthropic error type
|
|
2213
|
+
* `refusal`.
|
|
2214
|
+
* @private
|
|
2215
|
+
*/
|
|
2216
|
+
refusalError(modelId, stopDetails, partialText, usage) {
|
|
2217
|
+
const category = stopDetails?.category;
|
|
2218
|
+
return new AnthropicProviderError(`Claude declined the request on ${modelId || 'the requested model'} (stop_reason refusal${category ? `, category ${category}` : ''}).`, 'content_filter', undefined, 'refusal', { stopReason: 'refusal', stopDetails: stopDetails ?? null, partialText, usage });
|
|
2219
|
+
}
|
|
1973
2220
|
/**
|
|
1974
2221
|
* Assembles completed tool calls from the streaming accumulator.
|
|
1975
2222
|
*
|
|
@@ -1989,53 +2236,6 @@ export class AnthropicProvider {
|
|
|
1989
2236
|
},
|
|
1990
2237
|
}));
|
|
1991
2238
|
}
|
|
1992
|
-
/**
|
|
1993
|
-
* Estimates USD cost for a given model and token counts.
|
|
1994
|
-
*
|
|
1995
|
-
* Looks up pricing from the static model catalog. Returns undefined
|
|
1996
|
-
* if the model is not found in the catalog.
|
|
1997
|
-
*
|
|
1998
|
-
* @param {number} inputTokens - Number of input tokens.
|
|
1999
|
-
* @param {number} outputTokens - Number of output tokens.
|
|
2000
|
-
* @param {string} modelId - Model identifier for pricing lookup.
|
|
2001
|
-
* @returns {number | undefined} Estimated cost in USD.
|
|
2002
|
-
* @private
|
|
2003
|
-
*/
|
|
2004
|
-
/**
|
|
2005
|
-
* Estimate cost in USD for a completion, including Anthropic's prompt-
|
|
2006
|
-
* caching tier pricing.
|
|
2007
|
-
*
|
|
2008
|
-
* Anthropic billing tiers (as of 2025):
|
|
2009
|
-
* input_tokens × 1.00 × base input rate (non-cached input)
|
|
2010
|
-
* cache_read_input_tokens × 0.10 × base input rate (cache hit)
|
|
2011
|
-
* cache_creation_input_tokens × 1.25 × base input rate (5-min TTL write)
|
|
2012
|
-
* output_tokens × 1.00 × base output rate
|
|
2013
|
-
*
|
|
2014
|
-
* The API's `input_tokens` field already EXCLUDES cached tokens, so we
|
|
2015
|
-
* sum three separate components for total input cost. Previous
|
|
2016
|
-
* implementation used only `input_tokens` × rate, which happened to
|
|
2017
|
-
* be correct for the non-cached portion but hid cache creation cost
|
|
2018
|
-
* and ignored cache read cost entirely — meaning reported costUSD
|
|
2019
|
-
* was always BELOW true billed amount whenever caching was active.
|
|
2020
|
-
*
|
|
2021
|
-
* 1-hour TTL cache-creation rate is 2× the base input rate, not 1.25×.
|
|
2022
|
-
* We can't tell which TTL was used from the response, so we assume
|
|
2023
|
-
* the default 5-minute tier. For long-lived cached contexts the
|
|
2024
|
-
* reported cost will under-estimate by the 0.75× difference on
|
|
2025
|
-
* creation tokens (minor; mostly one-shot at run start).
|
|
2026
|
-
*/
|
|
2027
|
-
estimateCost(inputTokens, outputTokens, modelId, cacheReadTokens, cacheCreationTokens) {
|
|
2028
|
-
const info = resolveAnthropicModelEntry(modelId);
|
|
2029
|
-
if (!info?.pricePer1MTokensInput || !info?.pricePer1MTokensOutput)
|
|
2030
|
-
return undefined;
|
|
2031
|
-
const inputPrice = info.pricePer1MTokensInput;
|
|
2032
|
-
const outputPrice = info.pricePer1MTokensOutput;
|
|
2033
|
-
const nonCachedInput = (inputTokens / 1000000) * inputPrice;
|
|
2034
|
-
const cachedRead = ((cacheReadTokens ?? 0) / 1000000) * inputPrice * 0.10;
|
|
2035
|
-
const cachedCreate = ((cacheCreationTokens ?? 0) / 1000000) * inputPrice * 1.25;
|
|
2036
|
-
const output = (outputTokens / 1000000) * outputPrice;
|
|
2037
|
-
return nonCachedInput + cachedRead + cachedCreate + output;
|
|
2038
|
-
}
|
|
2039
2239
|
/**
|
|
2040
2240
|
* Builds an abort chunk for early stream termination.
|
|
2041
2241
|
*
|
|
@@ -2175,7 +2375,9 @@ export class AnthropicProvider {
|
|
|
2175
2375
|
lastError = new AnthropicProviderError(`Request timed out after ${effRequestTimeout}ms.`, 'REQUEST_TIMEOUT');
|
|
2176
2376
|
}
|
|
2177
2377
|
else {
|
|
2178
|
-
|
|
2378
|
+
// fetch names the URL (credentials included) when it rejects one,
|
|
2379
|
+
// and quotes a header value it rejects, which carries the key.
|
|
2380
|
+
lastError = new AnthropicProviderError(redactUrlSecrets(error instanceof Error ? error.message : 'Network or unknown error', this.config.baseURL, [apiKey]), 'NETWORK_ERROR');
|
|
2179
2381
|
}
|
|
2180
2382
|
if (attempt === this.config.maxRetries - 1)
|
|
2181
2383
|
break;
|
|
@@ -2264,7 +2466,9 @@ export class AnthropicProvider {
|
|
|
2264
2466
|
clearTimeout(timeoutId);
|
|
2265
2467
|
if (error instanceof AnthropicProviderError)
|
|
2266
2468
|
throw error;
|
|
2267
|
-
|
|
2469
|
+
// Masked like makeApiRequest's network failures: fetch's rejection
|
|
2470
|
+
// can name a credential-bearing base URL or quote the key header.
|
|
2471
|
+
throw new AnthropicProviderError(redactUrlSecrets(error instanceof Error ? error.message : 'Failed to connect to Anthropic stream.', this.config.baseURL, [apiKey]), 'STREAM_CONNECTION_FAILED');
|
|
2268
2472
|
}
|
|
2269
2473
|
}
|
|
2270
2474
|
/**
|
|
@@ -2365,9 +2569,14 @@ export class AnthropicProvider {
|
|
|
2365
2569
|
/* diagnostics must never break the request path */
|
|
2366
2570
|
}
|
|
2367
2571
|
}
|
|
2368
|
-
/**
|
|
2572
|
+
/**
|
|
2573
|
+
* True when the request enables extended thinking or replays thinking
|
|
2574
|
+
* blocks. `{ type: 'disabled' }` turns thinking off; `between_tools` still
|
|
2575
|
+
* returns thinking blocks between tool calls, so it counts.
|
|
2576
|
+
*/
|
|
2369
2577
|
payloadUsesThinking(body) {
|
|
2370
|
-
|
|
2578
|
+
const thinkingType = body.thinking?.type;
|
|
2579
|
+
if (body.thinking != null && thinkingType !== 'disabled')
|
|
2371
2580
|
return true;
|
|
2372
2581
|
const messages = body.messages;
|
|
2373
2582
|
if (!Array.isArray(messages))
|