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.
Summary
Bedrock Converse API path never sends
cachePointmarkers, 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 viacachePointsince 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 anycachePointcontent blocks intosystem,toolConfig.tools, or the last user message.Meanwhile in
run_agent.pythe gate for prompt caching is:Bedrock is excluded entirely, and even if it weren't,
apply_anthropic_cache_control()emits Anthropic-nativecache_controlmarkers, which the Converse API does not accept — the Converse format is a distinctcachePointcontent block per AWS docs.Repro
Run any Hermes session against
global.anthropic.claude-opus-4-7(or any Claude on Bedrock). Inspect the Converse responseusage— you will never seecacheReadInputTokens/cacheWriteInputTokensfields.Expected
For Claude models on Bedrock Converse, the agent should inject
cachePointcheckpoints at stable breakpoints (system prompt, tools, last user message prefix), producingcacheReadInputTokens> 0 on follow-up turns.Proposed fix
Inject up to 3
cachePointblocks insidebuild_converse_kwargs()inagent/bedrock_adapter.pywhen the model is a Claude family model. Expose TTL via env var (default5m,1hfor long sessions).Minimal patch (verified working on Opus 4.7 in
us-east-1):Verification output
Running against
global.anthropic.claude-opus-4-7inus-east-1with a ~10k-token static prefix:Supported models (per AWS docs)
Max 4 cache checkpoints per request; the proposed patch uses 3.
Environment
bedrock_converse)global.anthropic.claude-opus-4-7us-east-1Happy to submit a PR if useful.