Skip to content

Bedrock Converse path never injects cachePoint — Claude prompt caching silently disabled #11970

Description

@aws-yz

Summary

Bedrock Converse API path never sends cachePoint markers, so prompt caching is silently disabled for Claude on Bedrock. Users pay full input-token cost on every turn even though Bedrock has supported Claude prompt caching via cachePoint since early 2026 (5-minute default, and 1-hour extended TTL for Opus 4.5+ / Sonnet 4.5+ / Haiku 4.5+ / Opus 4.6 / Opus 4.7).

On a typical Hermes session with ~50k static prefix tokens, this is roughly a 10x input-cost overspend on every follow-up turn.

Root cause

In agent/bedrock_adapter.py, build_converse_kwargs() constructs the Converse request but never injects any cachePoint content blocks into system, toolConfig.tools, or the last user message.

Meanwhile in run_agent.py the gate for prompt caching is:

# run_agent.py:876
self._use_prompt_caching = (is_openrouter and is_claude) or is_native_anthropic

Bedrock is excluded entirely, and even if it weren't, apply_anthropic_cache_control() emits Anthropic-native cache_control markers, which the Converse API does not accept — the Converse format is a distinct cachePoint content block per AWS docs.

Repro

Run any Hermes session against global.anthropic.claude-opus-4-7 (or any Claude on Bedrock). Inspect the Converse response usage — you will never see cacheReadInputTokens / cacheWriteInputTokens fields.

Expected

For Claude models on Bedrock Converse, the agent should inject cachePoint checkpoints at stable breakpoints (system prompt, tools, last user message prefix), producing cacheReadInputTokens > 0 on follow-up turns.

Proposed fix

Inject up to 3 cachePoint blocks inside build_converse_kwargs() in agent/bedrock_adapter.py when the model is a Claude family model. Expose TTL via env var (default 5m, 1h for long sessions).

Minimal patch (verified working on Opus 4.7 in us-east-1):

# agent/bedrock_adapter.py, inside build_converse_kwargs()
_cache_ttl_env = (os.environ.get("HERMES_BEDROCK_CACHE_TTL") or "5m").strip().lower()
if _cache_ttl_env not in ("5m", "1h"):
    _cache_ttl_env = "5m"
_cache_enabled = (
    os.environ.get("HERMES_BEDROCK_PROMPT_CACHE", "1") not in ("0", "false", "False")
    and "claude" in (model or "").lower()
)
_cache_point = {"cachePoint": {"type": "default", "ttl": _cache_ttl_env}}

# after building system_prompt
if _cache_enabled and system_prompt:
    system_prompt = list(system_prompt) + [_cache_point]

# after building tool_list (inside the tool branch)
if _cache_enabled:
    tool_list = list(converse_tools) + [_cache_point]
kwargs["toolConfig"] = {"tools": tool_list}

# after converse_messages is finalized
if _cache_enabled and converse_messages:
    last = converse_messages[-1]
    if last.get("role") == "user" and isinstance(last.get("content"), list):
        last["content"] = list(last["content"]) + [_cache_point]

Verification output

Running against global.anthropic.claude-opus-4-7 in us-east-1 with a ~10k-token static prefix:

=== TTL = 5m ===
Call #1:  cacheWriteInputTokens=10870  cacheReadInputTokens=0   cacheDetails.ttl="5m"
Call #2:  cacheWriteInputTokens=17     cacheReadInputTokens=10853
→ ✅ CACHE WORKING

=== TTL = 1h ===
Call #1:  cacheWriteInputTokens=10870  cacheReadInputTokens=0   cacheDetails.ttl="1h"
Call #2:  cacheWriteInputTokens=17     cacheReadInputTokens=10853
→ ✅ CACHE WORKING

Supported models (per AWS docs)

Model Min tokens/checkpoint TTLs
Claude Opus 4.5 / 4.6 / 4.7 4,096 5m, 1h
Claude Sonnet 4.5 1,024 5m, 1h
Claude Haiku 4.5 4,096 5m, 1h
Claude Opus 4 / 4.1, Sonnet 4, 3.7 Sonnet, 3.5 Haiku, 3.5 Sonnet v2 1,024–2,048 5m

Max 4 cache checkpoints per request; the proposed patch uses 3.

Environment

  • Hermes 4.9
  • Provider: bedrock (api_mode=bedrock_converse)
  • Model: global.anthropic.claude-opus-4-7
  • Region: us-east-1

Happy to submit a PR if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundcomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointprovider/anthropicAnthropic native Messages APItype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions