Skip to content

fix: Bedrock prompt caching — ttl strip, stream cache usage, 4-era model mapping - #44

Merged
antra-tess merged 2 commits into
antra-tess:mainfrom
slimepriestess:fix/bedrock-prompt-caching
Jul 31, 2026
Merged

fix: Bedrock prompt caching — ttl strip, stream cache usage, 4-era model mapping#44
antra-tess merged 2 commits into
antra-tess:mainfrom
slimepriestess:fix/bedrock-prompt-caching

Conversation

@slimepriestess

Copy link
Copy Markdown
Contributor

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:

  1. cache_control.ttl strip (buildRequest): Bedrock only has the default 5m cache and rejects the direct-API ttl extension as an extra input. Any caller setting cacheTtl (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.

  2. Stream cache usage capture: the stream parser read only input_tokens/output_tokens, so cache_creation_input_tokens/cache_read_input_tokens never reached ProviderResponse.usage on streamed calls — only complete() surfaced them. Working caching would report zero cache activity forever. Now reads message_start and treats message_delta as authoritative, same contract as the Anthropic adapter (same event shape).

  3. 4-era model id mapping: claude-haiku-4-5-20251001 aliased 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 direct anthropic.<id>-v1:0 form 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_tokens on 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

  • No behavior change for callers already passing full Bedrock/profile ids — those pass through untouched.
  • Pairs with a connectome-host PR (model-gated re-enable of 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 new tests/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) and tests/unit/bedrock-model-mapping.test.ts (profile mapping, region prefixes, passthrough, legacy entries).

🤖 Generated with Claude Code

slimepriestess and others added 2 commits July 31, 2026 12:46
…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-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes Bedrock prompt caching and Claude 4 model routing.

  • Removes unsupported cache_control.ttl fields from message, system, image, and tool blocks while preserving cache markers.
  • Captures streamed cache creation and cache-read token usage, with final message deltas taking precedence.
  • Maps plain Claude 4-era model IDs to region-derived cross-region inference profiles while preserving explicit IDs and legacy mappings.
  • Adds focused unit coverage for request sanitization, streaming usage, model aliases, profile prefixes, and passthrough behavior.

Confidence Score: 5/5

The 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.

Important Files Changed

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
Loading

Reviews (1): Last reviewed commit: "fix: route plain 4-era Claude ids to Bed..." | Re-trigger Greptile

@antra-tess

Copy link
Copy Markdown
Owner

Sol review — ACCEPT / merge Membrane first

Reviewed exact head 55f13150e7014d91dfa3db9c263cc2927398a0eb against main 2fee7432dfc37bf1cf9ded5ba5cc46c5c95af8d7.

Independent verification:

  • TypeScript build: pass
  • full suite: 405/405
  • GitHub macOS/Ubuntu: green
  • Greptile: 5/5, no findings

The implementation matches the probe diagnosis:

  • strips Bedrock-incompatible cache_control.ttl from message, system, image, and tool cache markers while preserving {type:'ephemeral'};
  • carries streamed cache creation/read usage from message_start, with present message_delta fields authoritative and missing fields left undefined;
  • fixes the stale Haiku 4.5→3.5 alias;
  • maps plain Claude 4-era IDs to regional inference-profile IDs while preserving explicit Bedrock/profile IDs and legacy mappings.

Deployment caveat, not a merge blocker: explicit direct anthropic.* IDs remain explicit and are passed through unchanged. The Opus residence must therefore use a known-working profile ID or a plain Claude ID that this mapper converts; inventory that recipe before deployment rather than assuming the adapter rewrites an explicitly requested direct ID.

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

@antra-tess
antra-tess merged commit 72ed0f7 into antra-tess:main Jul 31, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants