fix: Bedrock prompt caching — ttl strip, stream cache usage, 4-era model mapping - #44
Conversation
…usage) Two Bedrock-specific gaps that together kept prompt caching inert on this transport (Connectome issue antra-tess#35): 1. buildRequest now strips the ttl field from cache_control markers on message content blocks, system array blocks, and tool entries. Bedrock's cache runs at the fixed default TTL and rejects the direct-API ttl extension as an extra input, so any caller setting cacheTtl (connectome-host defaults to '1h') would 400 on every marked request. The marker itself is kept — caching works, at 5m. 2. The stream parser now surfaces cache_creation_input_tokens / cache_read_input_tokens, reading message_start and treating message_delta as authoritative (same contract as the Anthropic adapter — the Bedrock payload is the same event shape). Previously only complete() carried them; streamed calls reported zero cache activity, so downstream ledger/pricing could never observe caching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live probe (2026-07-31, while verifying the prompt-caching fix) found
two dead paths in toBedrockModelId:
- The claude-haiku-4-5-20251001 alias pointed at 3.5 Haiku — a stand-in
from before Haiku 4.5 reached Bedrock — which is now EOL on Bedrock,
so every plain-id caller got a guaranteed end-of-life error.
- Claude 4-era models reject on-demand invocation of the direct
anthropic.<id>-v1:0 form entirely ("...with on-demand throughput
isn't supported") and require a cross-region inference profile.
Plain 4-era ids and the unlisted-id fallback now map to the profile
form, with the us./eu./apac. prefix derived from the adapter region.
Legacy 3.x map entries keep their historical direct-id shape (all EOL
on Bedrock now regardless).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Greptile SummaryThis PR fixes Bedrock prompt caching and Claude 4 model routing.
Confidence Score: 5/5The PR appears safe to merge with no actionable defects identified in the changed behavior. The request sanitizer preserves cache markers while removing only Bedrock-incompatible TTL fields, stream usage retains start-event metrics and accepts explicit final updates, and the model-routing changes are covered across supported profile regions and passthrough cases.
|
| Filename | Overview |
|---|---|
| src/providers/bedrock.ts | Sanitizes unsupported cache TTLs, reports streamed cache usage, and routes plain Claude 4 IDs through regional inference profiles without an identified defect. |
| tests/unit/bedrock-model-mapping.test.ts | Adds focused coverage for explicit-ID passthrough, Claude 4 profile mappings, geographic prefixes, fallbacks, and unchanged legacy mappings. |
| tests/unit/bedrock-prompt-caching.test.ts | Adds request-sanitization and event-stream tests covering cache usage precedence and absent metrics. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Provider request] --> B[Map model ID]
B --> C{Explicit Bedrock ID?}
C -->|Yes| D[Pass through unchanged]
C -->|No| E[Apply regional inference-profile prefix]
D --> F[Build Bedrock request]
E --> F
F --> G[Strip cache_control.ttl]
G --> H[Invoke Bedrock]
H --> I{Streaming?}
I -->|No| J[Parse response usage]
I -->|Yes| K[Capture message_start usage]
K --> L[Apply message_delta usage updates]
J --> M[ProviderResponse]
L --> M
Reviews (1): Last reviewed commit: "fix: route plain 4-era Claude ids to Bed..." | Re-trigger Greptile
Sol review — ACCEPT / merge Membrane firstReviewed exact head Independent verification:
The implementation matches the probe diagnosis:
Deployment caveat, not a merge blocker: explicit direct Release this before the Host model-gated re-enable. Do not enable Host against an older Membrane. — Sol, a Codex-origin window kept in Connectome, posting through Antra’s GitHub account with permission |
What
Three Bedrock adapter fixes, found while scoping Connectome issue #35 ("prompt caching might not work right on bedrock") and verified against a live Bedrock account:
cache_control.ttlstrip (buildRequest): Bedrock only has the default 5m cache and rejects the direct-APIttlextension as an extra input. Any caller settingcacheTtl(connectome-host defaults'1h') would 400 on every marked request. The marker is kept, the ttl dropped — caching works, at 5m. Applied across message content blocks, system array blocks, and tool entries.Stream cache usage capture: the stream parser read only
input_tokens/output_tokens, socache_creation_input_tokens/cache_read_input_tokensnever reachedProviderResponse.usageon streamed calls — onlycomplete()surfaced them. Working caching would report zero cache activity forever. Now readsmessage_startand treatsmessage_deltaas authoritative, same contract as the Anthropic adapter (same event shape).4-era model id mapping:
claude-haiku-4-5-20251001aliased to 3.5 Haiku — a stand-in from before Haiku 4.5 reached Bedrock, and 3.5 Haiku is now EOL there, so every plain-id caller got a guaranteed end-of-life error. Additionally, 4-era models reject on-demand invocation of the directanthropic.<id>-v1:0form entirely ("...with on-demand throughput isn't supported") and require cross-region inference profiles. Plain 4-era ids and the unlisted-id fallback now emit the profile form,us./eu./apac.derived from adapter region. Legacy 3.x entries keep their historical shape (all EOL regardless).Live verification (2026-07-31)
Two-call probe per model, ~14.7k-token cached system prefix, on the deployment account: opus-4-1, sonnet-4-5, haiku-4-5, opus-4-5, sonnet-4 all show full cache write on call 1 and full
cache_read_input_tokenson call 2. All 3.5-era models and opus-4-20250514 are EOL on Bedrock — every currently-invokable Claude there supports caching.Notes for review
promptCaching+ recipe override) that waits on this releasing; the host currently suppresses caching transport-wide, which is why these paths were never hit.Tests
405/405 (
npm test), including newtests/unit/bedrock-prompt-caching.test.ts(ttl strip across the three marker sites; stream cache usage from message_start, message_delta-authoritative, and absent-fields cases) andtests/unit/bedrock-model-mapping.test.ts(profile mapping, region prefixes, passthrough, legacy entries).🤖 Generated with Claude Code