Claude model integration for Amplifier via Anthropic API.
Offline contracts execute without real keys. Provision with
uv sync --all-extras --dev, then
uv pip install "amplifier-foundation @ git+https://github.com/microsoft/amplifier-foundation@a34b770ca604b88230449ac728d41dc23a2863a9".
Run uv run python -I -S tests/run_offline.py -q. The runner sanitizes inherited
credentials, supplies a fresh owned test home (including Windows profile
variables), and creates the default pytest temp parent on a clean checkout.
The test home is separate from pytest's temp tree and does not reuse inherited
home/config locations. It stays alive through later dependency finalizers;
shutdown then removes it and their home-scoped cache writes before final
accounting. After isolation and environment sanitation, Windows first performs
the real stdlib OS-version query to populate its own platform cache. Its
completion and process count are separate bootstrap facts, not vendor requests
or a zero-process guarantee; no OS identity or command output is recorded.
The runner then installs a Python
audit deny-and-record hook before site initialization, real Core/Foundation
imports and pytest collection. From that boundary, audited
DNS/socket attempts and unguarded subprocesses fail the run, even when provider
fallback catches the exception. The sole INET connect exception is Windows'
exact stdlib socketpair implementation connecting its own IPv4 stream socket to
its live loopback listener's exact address (needed for asyncio's self-pipe).
Receipts count this as internal_socketpair_connect, not a vendor request.
Ordinary loopback connects, DNS and datagram sends remain denied; Linux predicate
or fallback checks do not establish native Windows qualification. Pure Windows
predicate tests use explicit socket facts; native fallback observations retain
safe rejecting-clause/exception-type diagnostics and fail closed on missing
listener capabilities. Guarded children use an explicit exact Python executable;
Windows serialized audit commands must round-trip canonically with the same
isolated no-site runner prefix. Shell or ambiguous commands remain denied.
This is isolation for trusted tests, not a
tamper-proof security sandbox for hostile Python, forks or native code outside
Python's audit surface. Receipts are written to
ai_working/tmp/offline-receipt.json; they contain local source paths and are
private diagnostics, not public CI artifacts.
Messages-only unit mocks receive explicitly synthetic, unspecified Models
metadata only with the SDK-constructed default HTTP client and unchanged
transports. Explicit caller clients (even the same SDK wrapper/transport types),
SDK transport overrides and metadata/error mocks remain authoritative.
Inherited mount contracts use scoped nonfunctional credentials.
The real image-understanding test is a separate required live gate, not a
synthetic test. Offline inventory deselects it and records it as UNMET.
After separate authorization for a paid vendor call and credential provisioning,
run uv run pytest --required-live -q. Missing credentials fail closed, rather
than silently skipping. Offline green does not satisfy that live requirement.
Family discovery sorts semantic versions before snapshots, so multi-digit minor
releases cannot be hidden from routing by lexical menu filtering.
- Python 3.11+
- UV - Fast Python package manager
# macOS/Linux/WSL
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"Provides access to Anthropic's Claude models (Claude 4 series: Sonnet, Opus, Haiku) as an LLM provider for Amplifier.
Module Type: Provider
Mount Point: providers
Entry Point: amplifier_module_provider_anthropic:mount
await provider.list_models() returns list[ModelInfo] from the bound
endpoint/account's Models API. The default recommendations select the latest active
numeric version in each Opus, Sonnet, Haiku and Fable family, plus a distinct
newer preview. Matching aliases win over snapshots; snapshot-only ties use
the newest date. Both YYYYMMDD and YYYY-MM-DD suffixes are recognized.
Unknown families/spellings are conservatively omitted from recommendations.
await provider.list_models() # Frozen provider defaults
await provider.list_models(filters={}) # All actual IDs, including old/unknown IDs
await provider.list_models(filters={"include": ["CLAUDE-HAIKU-*"]})
await provider.list_models(filters={"include": []}) # No recommendationsAn explicit mapping replaces, not merges with, configured defaults.
Common keys are include, exclude, and hide_dated; this provider also has
latest_per_family and lifecycle. Patterns use case-insensitive, OS-independent globs;
exclusion wins, original IDs remain exact. hide_dated suppresses a snapshot
only if its matching alias survived the include/exclude selection.
config.model_filters takes precedence over legacy filtered;
otherwise filtered: false means unfiltered and filtered: true uses the
recommendation defaults. Config and call filters are copied immutably.
get_model_list_info() exposes fresh v1 metadata, including filters_schema,
default_filters, unfiltered: {}, and override: "replace".
The SDK floor and lock are anthropic==1.12.0 (dependency range remains <2).
Its native lifecycle filter accepts active, deprecated, and retired;
its limit range is 1–1000. Defaults request lifecycle=["active"], limit=1000,
and follow every validated after_id cursor. {} explicitly requests all three
stages: omitting the native argument would hide retired records. Custom mappings
also default to all stages unless they specify lifecycle.
Raw lifecycle values are validated and checked locally before recommendations,
even if a proxy ignores the native filter. These are catalog lifecycle claims,
not proof of account inference entitlement. The original bound client and SDK
header behavior are preserved, including configured workspace headers.
Each call fetches afresh. Genuine empty is []; malformed raw JSON, inconsistent
or repeated cursors, and failures raise typed, sanitized kernel errors, never a
partial result or cached/static fallback. Raw validation precedes SDK coercion.
The candidate-only discover_models() API is superseded by list_models.
The existing fallback-family cache updates only after complete success, using
active stable known-family entries independently of include/exclude filters; listing
never reads that cache. Selection-only inference settings and explicitly chosen
older models remain usable and unchanged.
Known-model capability mapping and enable_1m_context policy remain intact.
source: "provider-api" describes inventory provenance, not the source of every
metadata value. For opaque IDs with no known capability mapping, supplied API
limits are retained; absent limits use 0 to denote unknown, not a usable
budget, with empty capabilities/defaults. No Sonnet capabilities are inferred.
claude-sonnet-5-5- Claude Sonnet 5.5 (default; explicit model pins remain unchanged)claude-sonnet-5- Claude Sonnet 5 (previous default; still supported)claude-opus-5- Claude Opus 5 (most capable)claude-opus-5-5- Claude Opus 5.5 (adaptive thinking; $4/$20 input/output per MTok)claude-haiku-4-5- Claude Haiku 4.5 (previous Haiku; manual thinking)claude-haiku-5-5- Claude Haiku 5.5 (adaptive thinking; exact fixed model ID)
Haiku 5.5 supports text, vision, ordinary function tools, streaming, a native
1M context ceiling, 128K normal output, and a 512-token cache minimum. The
existing enable_1m_context policy remains opt-in: without it the provider
advertises a 200K context budget; it does not change the vendor's native limit.
The Sonnet 5.5 global default and all explicit model/effort pins remain unchanged.
No dated Haiku 5.5 alias is assumed.
Thinking defaults to adaptive on, with the vendor's medium effort when
effort is omitted. low, medium, high, xhigh, and max reach
output_config.effort without a manual budget. extended_thinking: false
sends genuine thinking: {type: disabled} at high or below; xhigh/max
with disabled thinking fails locally. Manual thinking_type: enabled,
thinking_budget_tokens, and Sonnet's between_tools mode are unsupported.
Normal sampling is omitted. Expert overrides reintroducing sampling or forbidden
thinking controls fail before transport. Assistant prefill also fails locally:
use structured outputs or system instructions and append a user turn, rather
than changing signed history.
Forced required/any and named ordinary function tool choices are permitted
and suppress thinking for that turn; a later auto/default turn resumes adaptive
thinking. Ordered thinking (including empty signed blocks), tool calls/results,
and images are retained. Synthetic offline signatures prove storage fidelity,
not vendor/account replay acceptance.
The model's native computer capability is computer_toolset_20260801, not
Haiku 4.5's legacy computer type. Explicit neutral v1 intent can compile to this
GA toolset on the exact first-party HTTPS endpoint. Legacy native declarations
still fail locally on Haiku 5.5; ordinary function tools, even one named
computer, are unaffected. Offline adapter contracts do not establish live
vendor/account replay or installed executor readiness.
Published first-party USD/MTok rates, checked 2026-11-14:
| Prompt length | Input | Output | Cache write 5m / 1h | Cache read |
|---|---|---|---|---|
| Up to 100,000 tokens | $0.10 | $0.50 | $0.125 / $0.20 | $0.01 |
| Above 100,000 tokens | $0.50 | $2.50 | $0.625 / $1.00 | $0.05 |
The tier counts fresh input + cache reads + cache writes once. Over 100,000 prompt tokens, the whole request (including output) uses the higher prices. Each request is priced independently; earlier requests are not repriced. Runtime uses the response usage and model, never a preflight token estimate. A complete TTL split controls 5m/1h write billing. Aggregate-only writes need an unambiguous TTL in the final request; inconsistent/incomplete splits or unresolved modifiers leave cost unavailable rather than falsely precise.
Usage.cost_usd carries the Decimal token cost; JSON and llm:response expose
a decimal string or null. Unknown costs include a machine-readable
metadata.anthropic_cost_unavailable reason (for example,
cache_write_ttl_unavailable, response_speed_unavailable, or
model_rates_unavailable). True zero is not unknown.
The provider's registered session.cost contribution reports cost_usd: null
after any unknown-cost call, including when later calls have known costs. Its
known subtotal is not reported as a complete total. External consumers that sum
only known costs may still show an unchanged or incomplete total: provider
accounting alone does not establish consumer/UI completeness or a free call.
An interrupted or cancelled stream that has emitted an SDK event also records
an unknown-cost attempt, even if a later retry succeeds. Validation failures and
requests rejected before a stream starts do not fabricate a billable attempt.
Costs do not determine model discovery ranking.
Sonnet 5 now uses its permanent $2/$10 input/output rates, with $0.20 reads
and $2.50/$4 writes. Fable 5.2 uses $8/$40, $0.20 reads, and $10/$16 writes.
Confirmed response fast mode doubles eligible model token costs, including
Fable 5.2; US inference multiplies all categories by1.1. Geography uses the
recognized actual usage.inference_geo first, then an explicit effective
final-wire value (including extra_body precedence) if actual geo is absent.
An invalid nonnull actual geo remains unknown. When both are absent, current
geo-capable models return inference_geo_unavailable: an omitted parameter uses
the workspace default, which can be US. Legacy geo-incapable models retain
standard prices. This does not change request defaults or force global routing.
The standalone compute_cost() helper retains its documented global default;
runtime conversion does not assume that workspace setting.
Pricing support does not enable new request capabilities. These are standard
first-party token charges, not invoices, negotiated discounts, or separate
server-tool fees.
list_models() derives fixed Core Pricing and runtime rates from the same
provider-local Decimal source. Haiku has pricing=None: a flat rate would be
misleading. Provider-local AnthropicModelInfo(ModelInfo) adds optional
pricing_details (not inference defaults), retained by model_dump():
{
"version": 1,
"kind": "tiered",
"currency": "USD",
"unit": "per_million_tokens",
"basis": "standard_global",
"modifiers": {"us_inference": "1.1"},
"threshold_tokens": 100000,
"threshold_basis": "input_tokens+cache_read_input_tokens+cache_creation_input_tokens",
"tiers": [
{"up_to": 100000, "rates": {"input": "0.10", "output": "0.50", "cache_read": "0.01", "cache_write_5m": "0.125", "cache_write_1h": "0.20"}},
{"above": 100000, "rates": {"input": "0.50", "output": "2.50", "cache_read": "0.05", "cache_write_5m": "0.625", "cache_write_1h": "1.00"}}
]
}Fixed models use kind:"fixed" and rates instead of tier fields. Rates and
multipliers are decimal strings; eligible models include
modifiers.confirmed_fast:"2". Consumers should validate the version/shape,
preserve unknown as unknown, never insert descriptive fields into inference
configuration, and use actual usage cost instead of recomputing from menu rates.
Old Core without the flat pricing field still receives the provider-local
extension without importing a new Core symbol. Unsupported IDs, including
Mythos, receive no guessed rates.
Sources: Haiku overview, migration, thinking, and pricing. Haiku 4.5's manual-thinking behavior is unchanged.
Opus 5.5 supports normal text, vision, function tools, streaming, effort, and
prompt-cost tracking. Its thinking is always adaptive: an unset effort uses the
vendor's medium effort, and extended_thinking: false cannot disable model
thinking (the provider warns and preserves any selected effort and output cap).
Only on Anthropic's first-party endpoint, Opus 5.5 adapts one explicit native
computer declaration (the documented legacy computer_20241022,
computer_20250124, or computer_20251124, or
computer_toolset_20260801) to computer_toolset_20260801. The adapter
preserves the ToolSpec name as the executor dispatch alias and maps the native
action member into input.action; ordinary function tools, including one named
computer, remain ordinary function tools. The actual request assembly guards
this first-party-only boundary: custom/proxy endpoints fail locally rather than
claiming vendor-native toolset support, even though recognized Opus 5.5
capabilities advertise the native path. This adapter is not a claim that the new
toolset is fully equivalent to prior native-computer contracts. Native computer
actions are single-action responses: the request uses tool_choice: auto with
the request-wide disable_parallel_tool_use: true; forced/named choice
remains unsupported on Opus 5.5. key.repeat is passed through when the model
emits it. An old executor without repeat support presses once and reports
success even when repeat > 1. Update the computer-action executor to honor
key.repeat before, or together with, activating this native Opus 5.5 path.
Provider-derived headers do not add a legacy computer-use beta for the new
toolset; an explicitly configured beta header is retained. As elsewhere in this provider, expert
extra_request_params.tool_choice is an explicit wire-level override and wins
over this derived default. This does not change the default model.
Tools opt in through an explicit ToolSpec.computer_use extra, transported by
the existing native-tool-spec mechanism. Names do not opt in. ToolSpec.name
remains the local dispatch alias; ToolSpec.parameters remains the canonical
function argument schema.
ToolSpec(
name="desktop",
parameters=canonical_schema,
type="function",
computer_use={
"version": 1,
"operations": ["screenshot", "mouse_move", "left_click", "key"],
"image_space": {"width": 1280, "height": 720, "unit": "pixel"},
"unavailable_operations": {},
},
)Supply actual cached screenshot dimensions, not physical monitor dimensions or
estimates. Only the exact Sonnet/Haiku 5.5 IDs and the existing recognized Opus
5.5 IDs use this provider's GA native compiler. It explicitly disables every
member not listed in operations; unavailable operations cannot also be enabled.
No computer-use beta header, legacy display declaration, concrete computer-bundle
import, peer-provider import or native capability discovery is needed.
Unknown image dimensions (image_space: None), oversized screenshots,
unverified models and unsupported endpoints use the original ordinary function
schema with intent/diagnostics removed. Optional computer_use.native_required: True refuses those cases instead of falling back. Malformed intent, ambiguous
aliases and mixed neutral/legacy declarations always fail locally. Model-only
expert overrides are compiled using the final effective model; overrides of
tools/messages/system/tool choice are refused for neutral requests. Use public
request fields instead.
The native path uses auto/none only and disables parallel tool use request-wide.
One vendor member becomes one local-alias singleton canonical batch:
{"version":1,"actions":[{"action":"left_click","coordinate":[426,87]}],"capture_after":true}Coordinates stay in full-screenshot pixels; durations stay in seconds and scroll
amounts in wheel notches. The adapter supports the 17 documented GA member names
when explicitly enabled, but refuses unqualified modifiers, repeated key presses
other than repeat: 1, path variants, missing drag starts, noninteger native
durations/amounts and invalid geometry. It does not guess alternative semantics.
Pending safety requirements or unknown native member metadata are refused before
returning executable calls; no acknowledgement is fabricated. Native replies
without the declared family are refused unless they name a separately declared
ordinary function (for example, an ordinary screenshot tool).
Both canonical content and compatibility tool calls retain provider-owned
provenance. Replay reconstructs the original vendor input, never the executor
batch, and binds tool results by call ID. Alias/image-space/provenance changes
refuse replay rather than silently downgrading it. Text, images and is_error
survive count/create/stream and second-turn projection without mutating the
caller. Old installed tools without neutral intent keep their existing flat
native inputs; they do not receive the new batch contract.
Protocol sources: computer tool reference, toolset reference, and Haiku migration. Repository-native offline tests use real Core and SDK types with transport fakes. Paid native vendor checks, signed/account replay, actual final screenshots, platform/backend input and the required live-vision gate remain separate qualification requirements. Offline pricing checks do not establish billed cost.
[[providers]]
module = "provider-anthropic"
name = "anthropic"
config = {
default_model = "claude-sonnet-5-5",
max_tokens = 8192,
temperature = 1.0, # Silently ignored by models without sampling support (sonnet-5, opus-4.7+)
raw = false # Include full request/response payloads in llm:* events
}See the Configuration Reference table below for every surviving key.
Anthropic binds preserved thinking to its preceding system, tools and
messages. Compaction, a replaced observation, changed tools or resumed history
can legally change that prefix. The provider never replays old observations,
rewrites canonical history, re-executes tools, or guesses validity from a local
cache. It preserves native thinking/redacted blocks and tool results in order;
Anthropic validates prefix, model and account compatibility.
thinking_prefix_mismatch_behavior defaults to auto. On the exact first-party
HTTPS endpoint and documented preserved-thinking models (Fable/Mythos 5.1–5.2,
Opus/Sonnet/Haiku 5.5), it sends
thinking.block_binding.prefix_mismatch_behavior: drop_block with
thinking-binding-controls-2026-08-01. This lets the server remove only failing
thinking and its dependent chain; unchanged-prefix reasoning survives. Fable
5.2 accepts this control without specifying a thinking mode. Sonnet/Haiku 5.5
require adaptive mode; the provider does not change disabled/between-tools modes
to enable the control. Unsupported modes/endpoints retain their native behavior,
including nonretryable errors rather than speculative stripping or retries.
Set this config key (or per-call option) to error for strict diagnostics,
drop_block to explicitly opt a compatible custom endpoint into the native
contract, or YAML null to leave vendor defaults unchanged. An explicit native
block_binding in extra_request_params.thinking or
extra_request_params.extra_body.thinking wins, including error, {} or null.
Explicit null thinking and an explicitly emptied beta header are not repopulated.
No API compatibility is claimed for custom endpoints.
Responses retain the vendor's input_transformations in
metadata.anthropic_input_transformations, including empty arrays. Streaming
captures message_start and any replacement report on a final message_delta.
Paths/reasons distinguish prefix drops, model/account incompatibility and allowed
mismatches. Count preflight uses the same effective thinking, content and headers
as dispatch; it never registers generation provenance or triggers tool effects.
Tampered/undecryptable signatures remain nonretryable errors.
Selective dropping is compatibility recovery, not a cure for avoidable prefix churn. Repeated drops can increase output tokens, latency and change quality. Keep stable context unchanged, append new instructions when safe, and monitor the transformation report. See Anthropic's preserved-thinking contract.
New native blocks carry internal
thinking_provenance: {version: 1, provider: anthropic} extras. Explicitly foreign
provider signatures are never submitted as native Anthropic signatures: readable
summaries become text and foreign opaque redacted data is omitted. Legacy
untagged signed blocks are replay candidates, not authenticated origin evidence;
the server still validates them. Provenance is stripped from the native wire and
does not lock otherwise compatible Anthropic model switches.
The reasoning_effort config key (canonical — it matches the kernel's portable
request.reasoning_effort field) sets a session-level default reasoning effort
applied to every request, so you can opt into stronger reasoning once
instead of supplying it per-request. The legacy effort key remains a working
alias; when both are set, reasoning_effort wins (a warning is logged).
providers:
- module: provider-anthropic
config:
default_model: claude-opus-4-8
reasoning_effort: xhigh # legacy alias: effortThis enables extended thinking. In this provider, effort (like the kernel's
portable request.reasoning_effort) deliberately maps to Anthropic extended
thinking — setting it engages thinking and controls its depth, the same way
OpenAI's reasoning effort engages its reasoning. At the Anthropic API level
effort and thinking are independent primitives; coupling them is Amplifier's
"reason harder" product semantics. Because it turns thinking on for every call,
leave effort blank unless you want stronger (and more expensive) reasoning by
default.
Accepted values:
| Value | Meaning | Availability |
|---|---|---|
low |
Minimal thinking, most token-efficient | All thinking-capable models |
medium |
Balanced | All thinking-capable models |
high |
High intensity (not the omitted default on Haiku 5.5 or Opus 5.5) | All thinking-capable models |
xhigh |
Extended capability for long-horizon agentic/coding work | Opus 4.7+, Haiku 5.5 |
max |
Maximum capability, no effort-based token constraint | Opus 4.8+, Haiku 5.5 (normal output caps still apply) |
Precedence (highest wins): per-call effort kwarg → request.reasoning_effort
(set by the orchestrator) → this effort config default. Note the per-call
effort kwarg is an output_config.effort-only override; the reasoning_effort
chain is what enables thinking.
Notes:
- Invalid values (e.g.
ultra,EXTRA HIGH) are normalised (trimmed/lowercased) and, if still unrecognised, ignored with a warning — they never silently turn thinking on. output_config.effortis only emitted for models the capability matrix marks as supporting it (including Opus 4.7+ and Haiku 5.5). On other thinking-capable models the extended-thinking mapping still applies. Broadening this to Sonnet 4.6 and Opus 4.5/4.6 (which Anthropic also supports) is tracked as a follow-up.xhigh/maxare omitted fromoutput_config.efforton models whose capability matrix doesn't list them (a warning is logged), falling back to adaptive thinking.- This key is exposed through
amplifier provider use(shown for thinking-capable models), so it can be set interactively without hand-editing YAML.
thinking.budget_tokens is the entire reasoning dial on models without
output_config.effort support -- Haiku 4.5 among them, where effort never
reaches the wire at all. Two config keys control it:
providers:
- module: provider-anthropic
config:
default_model: claude-haiku-4-5
extended_thinking: true # turn thinking on WITHOUT choosing an effort
thinking_budget_tokens: 8000 # explicit budget, honored as writtenPrecedence (highest wins): per-call thinking_budget_tokens kwarg ->
thinking_budget_tokens config -> the budget implied by reasoning_effort
(low -> 4096, everything else -> the model default) -> the model default.
An explicit budget outranks the effort-implied one, because the effort ladder
is a derived default and a configured number is caller intent. Setting only
reasoning_effort is unchanged: you still get 4096 for low and the model
default otherwise.
Turning thinking on: reasoning_effort enables thinking as a side effect, but
it also selects a thinking depth and, on Opus 4.7+, sets output_config.effort.
extended_thinking: true enables thinking and nothing else, which is what you
want when the budget is the only dial you care about. extended_thinking: false
is an explicit opt-out that overrides a configured reasoning_effort; a per-call
extended_thinking kwarg overrides both.
It is never silently discarded. If an explicitly requested budget does not
reach thinking.budget_tokens, a warning names the key, the value you asked for,
the value actually sent, and why. That happens when:
| Situation | What is sent | Remedy |
|---|---|---|
| Thinking is not enabled | no thinking block at all |
set reasoning_effort or extended_thinking: true |
| Model has no thinking support (e.g. Haiku 3.5) | no thinking block at all |
use a thinking-capable model |
Resolved thinking_type is adaptive |
{"type": "adaptive"} -- the API forbids budget_tokens here; your value sizes max_tokens instead |
set thinking_type: enabled |
| Value is outside the model's limits | the clamped value (min 1024, max the model's output ceiling) | pick a value inside the range |
A non-integer value is ignored with a warning and the resolved default is used -- a typo does not raise out of every request.
debug and raw_debug never existed as real keys -- this section documented
them in error for a long time. The real key is raw (boolean, default
false): when true, full request/response payloads are attached to the
llm:request / llm:response events for inspection.
The provider disables SDK built-in retries (max_retries=0) and manages retries itself via amplifier_core.utils.retry.retry_with_backoff(). This gives the provider full control over backoff timing, retry-after header honoring, and per-error-class delay scaling.
SDK exceptions are translated to kernel errors before the retry loop sees them. All translations preserve the original exception as __cause__ for debugging.
| SDK Exception | Condition | Kernel Error | Status | Retryable |
|---|---|---|---|---|
RateLimitError |
429 | RateLimitError |
429 | Yes |
OverloadedError |
529 | ProviderUnavailableError |
529 | Yes (10× backoff) |
InternalServerError |
5xx | ProviderUnavailableError |
5xx | Yes |
AuthenticationError |
401 | AuthenticationError |
401 | No |
BadRequestError |
prompt/context window overflow (e.g. prompt is too long: ... tokens > ... maximum) |
ContextLengthError |
400 | No |
BadRequestError |
safety / content filter / blocked | ContentFilterError |
400 | No |
BadRequestError |
other | InvalidRequestError |
400 | No |
APIStatusError |
403 | AccessDeniedError |
403 | No |
APIStatusError |
404 | NotFoundError |
404 | No |
APIStatusError |
other non-5xx | LLMError |
— | No |
asyncio.TimeoutError |
— | LLMTimeoutError |
— | Yes |
| Other | — | LLMError |
— | Yes |
Each retry delay is computed as follows:
base_delay = min_retry_delay × 2^(attempt - 1)
capped_delay = min(base_delay, max_retry_delay)
scaled_delay = capped_delay × delay_multiplier # 1.0 for most errors, 10.0 for 529
final_delay = max(scaled_delay, retry_after) # server retry-after as floor
sleep = final_delay ± (final_delay × jitter) # randomised ± jitter fraction
Example: 529 Overloaded (10× multiplier, defaults)
| Attempt | base_delay | capped | ×10 | Sleep |
|---|---|---|---|---|
| 1 | 1s | 1s | 10s | 10s |
| 2 | 2s | 2s | 20s | 20s |
| 3 | 4s | 4s | 40s | 40s |
| 4 | 8s | 8s | 80s | 80s |
| 5 | 16s | 16s | 160s | 160s |
Total wait ≈ 310s (~5 min) before the request is abandoned.
providers:
- module: provider-anthropic
config:
max_retries: 5
min_retry_delay: 1.0
max_retry_delay: 60.0
retry_jitter: true
overloaded_delay_multiplier: 10.0| Key | Default | Description |
|---|---|---|
max_retries |
5 |
Maximum retry attempts before giving up |
min_retry_delay |
1.0 |
Base delay in seconds for the first retry |
max_retry_delay |
60.0 |
Cap on the base delay (before multiplier) |
retry_jitter |
true |
Randomise each retry delay to avoid thundering-herd retries. Boolean. A numeric value such as 0.2 is parsed as false and disables jitter -- use true/false |
overloaded_delay_multiplier |
10.0 |
Multiplier applied to delays for 529 Overloaded errors |
A provider:retry event is emitted before each retry sleep with the following fields:
| Field | Description |
|---|---|
provider |
Provider name ("anthropic") |
model |
Model being called |
attempt |
Current retry attempt number |
max_retries |
Configured maximum retries |
delay |
Computed sleep duration in seconds |
retry_after |
Server retry-after value (or null) |
error_type |
Kernel error class name |
error_message |
Error description |
The provider exposes optional asynchronous request_budget() and
recover_context_overflow() methods for a retention-aware context manager.
request_budget() uses Anthropic's count endpoint for the existing shared
assembled request, then adds a fixed 4,096-token safety reserve. It projects
only count-endpoint-compatible fields: the resolved model, messages, system,
tools, tool choice, thinking, output configuration, cache control, and required
beta headers. The dispatch-only max_tokens value is never sent to count.
Count calls have a five-second deadline and no retry. Unknown model windows,
count errors (including 429), timeouts, and malformed count responses return
None; no byte-ratio, character heuristic, cached count, or other estimate is
used. Each unavailable reason emits one safe warning per resolved model, without
request or response data. An explicit ChatRequest.max_output_tokens remains
the wire output cap even when adaptive thinking would otherwise enlarge an
implicit cap.
When this endpoint returns a valid count, request_budget() keeps its existing
budget fields and additively includes:
"measurement": {
"kind": "provider_count",
"source": "anthropic.messages.count_tokens",
"input_tokens": 12345, # raw count-endpoint input_tokens value
}The provider advertises this optional provenance through
request_budget:provider_count. Consumers must negotiate that capability and
still treat an absent or malformed measurement as unavailable; existing
budget-only callers continue to use the unchanged budget fields.
Recovery is narrower still. Only a structured Anthropic
invalid_request_error HTTP 400 whose request id and exact input-only overflow
counts are present can authorize it. A strict documented combined
input/max_tokens grammar is also eligible only when its positive arithmetic
derives a lower input limit. The private feedback is bound to the same unchanged
request, options, and assembled wire payload; it is consumed once and permits
one smaller context retry. Other joint input/output limit errors, malformed
responses, and any stream that has produced its first SDK event are never
eligible. The provider never retries a context error by itself.
The provider places up to four Anthropic cache breakpoints per request: one on the system prompt, one on the last tool definition, and up to two rolling ones inside the conversation. The conversation breakpoints are the ones that matter for a long agent loop, because they are the only ones that grow with the transcript.
Placing them safely requires knowing which trailing messages are regenerated
per request (a <system-reminder> with a live clock, a git-status blob) rather
than appended to history. A cached prefix ending in regenerated content can
never be reproduced, so it is written every turn and never read -- strictly
worse than not caching, because writes are billed at 1.25x.
The provider answers that question two ways, in this order:
- Declared --
Message.metadata = {"ephemeral": True}(and"persisted"for content that is written into canonical history). Orchestrators that populate this get the most precise placement. - Observed --
cache_infer_stability_from_history(defaulttrue). The provider fingerprints each request's message array and, on the next request in the same conversation, takeslen(previous) - longest_common_prefixas the measured unstable-suffix length.
The observation is used as a floor with the declared value, never a replacement: it can only ever make placement more conservative.
providers:
- module: provider-anthropic
config:
enable_prompt_caching: true # default: true
cache_infer_stability_from_history: true # default: trueWhy the default matters. Before observation existed, a deployment whose
orchestrator never populated Message.metadata got no conversation-region
breakpoint at all -- ever. Measured on one coding-agent node visit (164 provider
calls, 60 minutes): cache_read_input_tokens frozen at exactly 10,995 (the
system + tools blocks) on every single call while the prefix grew from 24K to
228K tokens. 1.79M cache_read against 24.7M billed input -- a 6.8% hit
ratio.
Reproduced and fixed under a 12-call tool-using loop against
claude-haiku-4-5 (scripts/prompt_cache_live_probe.py, no metadata anywhere,
regenerated tail):
| calls 2-12 hit ratio | uncached tokens, calls 2-12 | |
|---|---|---|
| before | 71.5% (cache_read frozen at 11,180) | 44,208 |
| after | 95.0% | 411 |
Two things follow from measuring rather than declaring:
- The first request in a conversation places nothing. There is no prior request to compare against, so there is no evidence yet. Caching engages from the second request and the first hit lands on the second or third. That warm-up is the price of not guessing.
- A conversation whose leading message is regenerated per request is detected and reported. Anthropic matches a cached prefix from the start of the request, so nothing downstream of a volatile first message can ever hit. The provider logs a warning naming the likely cause and places no conversation breakpoint, rather than billing a write every turn for a prefix that can never be read.
Set cache_infer_stability_from_history: false to revert to strict
metadata-only behaviour.
Minimum cacheable prefix. Anthropic silently ignores a breakpoint whose prefix is below the model's minimum. Measured for
claude-haiku-4-5(2026-09-06): a 4,369-token system+tools prefix produced no cache write on two consecutive identical requests, while 8,659 tokens cached immediately -- i.e. the effective floor is nearer 4096 than the 2048 the docs list for Haiku. A small system prompt therefore gets no system-block caching at all, in any version of this provider.
By default, prompt-cache breakpoints use Anthropic's standard 5-minute TTL. The
cache_stable_region_ttl_1h config key opts into a 1-hour TTL for the two most
stable cache breakpoints only:
providers:
- module: provider-anthropic
config:
enable_prompt_caching: true # required -- see below
cache_stable_region_ttl_1h: trueWhat it covers: the system prompt and tool-definition breakpoints only.
It deliberately never applies to conversation-region breakpoints, which move
every turn on a rolling basis and would pay the higher write premium (below)
on content that's about to be superseded anyway. Extending the conversation
region to a longer TTL is a separate, currently-unimplemented idea tracked
upstream in microsoft/amplifier#337.
See the design comment above self.cache_stable_region_ttl_1h in
amplifier_module_provider_anthropic/__init__.py for the full rationale.
The economics: Anthropic bills 1-hour TTL cache writes at 2x the base input-token rate, versus 1.25x for the default 5-minute TTL. That's a real up-front cost -- but a longer TTL means the write survives more calls before it needs to be repeated. Once a cache entry is reused across roughly two or more reads inside the 1-hour window that a 5-minute TTL would have missed (because the previous call was more than 5 minutes ago), the 1h TTL comes out ahead. It helps most for:
- Long-running sessions with gaps between calls longer than 5 minutes (e.g. a human pausing between turns, a scheduled/cron-triggered agent).
- Stable system prompts and tool sets that don't change turn-to-turn -- exactly the two regions this knob targets.
If your calls are consistently more frequent than every 5 minutes, the default 5-minute TTL already keeps the cache warm at the cheaper 1.25x write rate, and enabling this knob only adds cost.
Requires prompt caching itself. Because this knob only affects existing
cache breakpoints, it is a no-op when enable_prompt_caching is false (or
left at its false default) -- the provider will not append the
extended-cache-ttl-2025-04-11 beta header in that case, and logs a one-line
notice explaining why. Enable both together.
Left unset by default (rather than defaulting to false): this makes
"no opinion, use the provider's own 5-minute default" a real, distinguishable
third state in the config wizard, separate from an explicit opt-out.
Anthropic provides experimental features through beta headers. Enable these features by adding the beta_headers configuration field.
Single beta header:
providers:
- module: provider-anthropic
config:
default_model: claude-sonnet-4-5
beta_headers: "context-1m-2025-08-07" # Enable 1M token context windowMultiple beta headers:
providers:
- module: provider-anthropic
config:
default_model: claude-sonnet-4-5
beta_headers:
- "context-1m-2025-08-07"
- "future-feature-header"1M context is generally available, on by default, and billed at standard pricing on the previously supported models that have it (Opus 5/4.8/4.7/4.6, Sonnet 5/4.6, Fable 5/5.1, Mythos 5/Preview). No beta header is required, and there is no long-context price premium (Anthropic: Context windows, verified 2026-08-29). Those models cap output at 128K tokens per request regardless of context size.
Haiku 5.5 also has a native 1M window, but has prompt-length pricing tiers; see its per-request pricing boundary above.
The enable_1m_context config key does not change what the API accepts.
It only sets the context window this provider advertises to Amplifier's
context manager (200K vs 1M), which determines how much conversation history
is kept per request -- a cost decision. It defaults to false.
- Beta features are experimental and subject to change
- Check Anthropic's documentation for available beta headers
- Beta headers are optional - existing configurations work unchanged
- Invalid beta headers will cause API errors (fail fast)
- Beta header usage is logged at initialization for observability
House-style key reference. ✅ = wizard-visible ConfigField, ⚙️ = settings-only (fully functional, not asked in the wizard).
| Key | Default | Surface | Description |
|---|---|---|---|
api_key |
(env ANTHROPIC_API_KEY) |
✅ | Anthropic API key |
base_url |
https://api.anthropic.com |
✅ | Custom endpoint |
default_model |
claude-sonnet-5-5 |
(model picker) | Model used when a request does not name one |
reasoning_effort |
(unset) | ✅ | low|medium|high|xhigh|max. Enables extended thinking. Legacy alias: effort (deprecated) |
extended_thinking |
(unset) | ⚙️ | Turn extended thinking on/off without choosing an effort. Overrides the reasoning_effort implication; a per-call kwarg overrides this |
thinking_budget_tokens |
(model default) | ⚙️ | Explicit thinking.budget_tokens. Outranks the effort-implied budget; warns if it can't reach the wire |
thinking_budget_buffer |
8192 |
⚙️ | Headroom added to the budget when sizing max_tokens |
thinking_type |
adaptive |
⚙️ | adaptive|enabled. adaptive lets the model manage its own budget (and forbids budget_tokens); falls back to enabled on models without adaptive support |
thinking_prefix_mismatch_behavior |
auto |
config / per-call | auto, drop_block, error, or null. Selective server recovery for legal prefix edits; see signed-thinking continuation. Does not change explicit thinking modes or native controls |
enable_1m_context |
false |
✅ | Advertise the 1M context window (more history kept = higher cost) |
cache_stable_region_ttl_1h |
(unset) | ✅ | 1h cache TTL for system prompt + tools. 2x write cost, fewer writes |
enable_prompt_caching |
true |
⚙️ | Place cache breakpoints |
cache_infer_stability_from_history |
true |
⚙️ | Measure the regenerated-per-request tail by comparing consecutive requests, when Message.metadata is not populated. Used as a floor with the declared value, never a replacement. false = strict metadata-only |
max_tokens |
(model ceiling) | ⚙️ | Output token cap |
temperature |
0.7 |
⚙️ | Ignored by non-sampling models (Sonnet 5, Opus 4.7+) |
timeout |
null |
⚙️ | Optional request deadline, seconds; default waits for completion |
close_timeout |
5.0 |
⚙️ | Hard bound on closing the HTTP client at session teardown, seconds. httpx's aclose() has no deadline of its own and blocks forever on a half-closed (CLOSE-WAIT) connection; on timeout the client is abandoned with a WARNING and the socket is reclaimed at process exit |
priority |
100 |
⚙️ | Provider selection priority (lower wins) |
raw |
false |
⚙️ | Include full request/response payloads in llm:request/llm:response events |
fallback_on_overload |
false |
⚙️ | Downgrade one ladder rung (fable/mythos → opus → sonnet → haiku) after persistent 529s |
fallback_retry_count |
2 |
⚙️ | 529 retries before downgrading |
fallback_cooldown_seconds |
300 |
⚙️ | How long a downgrade window stays open |
fallback_models |
(unset) | ⚙️ | Per-family target overrides, e.g. {opus: claude-opus-4-8} |
persist_fallback_state |
false |
⚙️ | Share downgrade windows across processes |
refusal_fallback_enabled |
true |
⚙️ | Retry once on finish_reason="refusal", via the same ladder as overload |
extra_request_params |
(unset) | ⚙️ | Escape hatch for Messages API params this provider doesn't model; merged last, user-wins |
An instance selected for Haiku may legitimately inherit fallback_on_overload
from a higher-tier parent. Haiku has no lower tier, so overload downgrade is
unavailable for that request; its normal retry policy is unchanged. This is an
informational diagnostic, not a warning to remove the parent's setting. The
option remains available for higher-tier requests on the same instance.
Removed keys (still recognized, warn with a migration message): fallback_sonnet_model,
fallback_haiku_model (use fallback_models), refusal_fallback_model (refusal fallback
now follows the same ladder as overload -- see Fallback section).
export ANTHROPIC_API_KEY="your-api-key-here"# In amplifier configuration
[provider]
name = "anthropic"
default_model = "claude-sonnet-5-5"- Streaming support
- Tool use (function calling)
- Vision capabilities (on supported models)
- Token counting and management
- Message validation before API calls (defense in depth)
The provider implements automatic repair for incomplete tool call sequences:
The Problem: If tool results are missing from conversation history (due to context compaction bugs, parsing errors, or state corruption), the Anthropic API rejects the entire request, breaking the user's session.
The Solution: The provider automatically detects and repairs missing tool_results by injecting synthetic results:
- Repair before validation - Detects missing tool_results and injects synthetic ones
- Make failures visible - Synthetic results contain
[SYSTEM ERROR: Tool result missing]messages - Maintain conversation validity - API accepts repaired messages, session continues
- Enable recovery - LLM acknowledges error and can ask user to retry
- Provide observability - Emits
provider:tool_sequence_repairedevent with repair details - Validate remaining - After repair, strict validation catches any remaining inconsistencies
Example:
# Anthropic format (after _convert_messages)
messages = [
{
"role": "assistant",
"content": [
{"type": "tool_use", "id": "toolu_123", "name": "get_weather", "input": {...}}
]
},
# MISSING: {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_123", ...}]}
{"role": "user", "content": "Thanks"}
]
# Provider repairs by injecting synthetic result:
# Either appends to existing user message or inserts new one
{
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "[SYSTEM ERROR: Tool result missing]\n\nTool: get_weather\n..."
}]
}Observability: Repairs are logged as warnings and emit provider:tool_sequence_repaired events for monitoring.
Philosophy: This is graceful degradation following kernel philosophy - errors in other modules (context management) don't crash the provider or kill the user's session
amplifier-core>=1.0.0anthropic>=1.0.0,<2.0.0
Note
This project is not currently accepting external contributions, but we're actively working toward opening this up. We value community input and look forward to collaborating in the future. For now, feel free to fork and experiment!
Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit Contributor License Agreements.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
Completion and streaming requests have no elapsed-time or read-idle deadline by default. They wait for completion, explicit cancellation, or a provider/transport error. Set timeout (seconds) to opt into a request deadline; null leaves model work unbounded. Connection and pool acquisition remain bounded to 5 seconds, and existing close_timeout cleanup limits are unchanged.