1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Bedrock Converse `cachePoint` prompt-caching helpers (#2260, ported #2407).
//!
//! Why: a caller that marks the byte-stable tools+system-prompt prefix with
//! `cache_control` (#2156, e.g. tcode's `agent_loop::build_request`) emits a
//! marker every provider's adapter is free to interpret however its wire format
//! requires. OpenRouter's `anthropic/*` passthrough serialises that marker as
//! Anthropic's `cache_control` content-block field (see
//! [`crate::inference::types::ChatMessage`]'s hand-written `Serialize`);
//! Bedrock's Converse API has NO such field — it expresses the identical
//! concept as a distinct `cachePoint` content block interleaved into the
//! `system`/`tools`/`messages` arrays (`ContentBlock::CachePoint` /
//! `SystemContentBlock::CachePoint` / `Tool::CachePoint`, all wrapping the same
//! `CachePointBlock` struct). This module is the translation: it reads the SAME
//! markers [`super::convert`] already receives on the [`crate::inference::ChatRequest`]
//! and emits the Bedrock-native block shape instead.
//! What: [`cache_point_block`] builds the one `CachePointBlock` shape ever sent
//! (`type: default`, no explicit TTL). [`system_cacheable`] and
//! [`tools_cacheable`] are the minimum-size guards: Anthropic models on Bedrock
//! only cache a prefix at or above ~1024 tokens (the Sonnet/Opus floor; Haiku's
//! is 2048, but per-model minimums are not tracked, so the conservative Sonnet
//! floor is applied universally) — marking a smaller prefix is not an error, but
//! it wastes a checkpoint slot (Bedrock allows at most 4 per request) for zero
//! benefit, so [`super::convert`] skips emission below the floor rather than
//! relying on Bedrock to silently no-op it. Token counts use the same cheap
//! chars/4 heuristic tcode's `agent_loop::estimate_tokens` uses, ported here so
//! the commons adapter is byte-for-byte behaviour-identical (the M3 bake-off
//! no-regression gate).
//! Test: `super::tests::{cache_point_block_is_default_type,
//! system_cacheable_respects_floor, tools_cacheable_respects_floor}`.
use ;
use crateToolDefinition;
/// Minimum estimated-token size of a prefix worth a Bedrock cachePoint
/// breakpoint (the Claude Sonnet/Opus floor; see module doc).
pub const MIN_CACHEABLE_TOKENS: usize = 1024;
/// Estimate the token count of a text span using a cheap chars/4 heuristic.
///
/// Why: the cache guards only need to answer "is this prefix large enough to
/// ever produce a cache hit"; a tokenizer dependency would be disproportionate.
/// This is the exact heuristic tcode's `agent_loop::estimate_tokens` applies, so
/// the ported adapter's cachePoint emission is byte-identical (bake-off gate).
/// What: returns `text.chars().count() / 4`, floor-divided.
/// Test: `super::tests::system_cacheable_respects_floor`.
/// Build the one `CachePointBlock` shape ever sent.
///
/// Why: every breakpoint emitted is the same "cache everything up to here,
/// ephemeral" marker — there is no per-call variation (no custom TTL) today, so
/// a single constructor keeps [`super::convert`]'s call sites a one-liner
/// instead of repeating the builder chain.
/// What: returns `CachePointBlock { r#type: Default, ttl: None }`. The builder
/// can only fail if `r#type` is left unset, which never happens here — `expect`
/// documents that as a genuine invariant, not a call for `?` plumbing a caller
/// could ever meaningfully recover from.
/// Test: `super::tests::cache_point_block_is_default_type`.
pub
/// Whether a system-prompt text is large enough to justify a cachePoint
/// breakpoint.
///
/// Why: guards [`super::convert::build_converse_messages`] against spending one
/// of Bedrock's 4-per-request checkpoint slots on a system prompt too small for
/// Anthropic's minimum-cacheable-prefix floor to ever produce a cache hit.
/// What: `true` when `estimate_tokens(text) >= MIN_CACHEABLE_TOKENS`.
/// Test: `super::tests::system_cacheable_respects_floor`.
pub
/// Whether a set of tool definitions is large enough (their combined
/// JSON-Schema bodies) to justify a cachePoint breakpoint.
///
/// Why: same floor as [`system_cacheable`], applied to the tools array — guards
/// [`super::convert::build_tool_config`] against a wasted checkpoint on a small
/// tool set (e.g. a single-tool test fixture).
/// What: serialises each `ToolDefinition.function` (name + description +
/// parameters — the JSON Schema actually sent on the wire) to a JSON string,
/// sums `estimate_tokens` over those strings, and compares against
/// `MIN_CACHEABLE_TOKENS`. A serialisation failure (should be unreachable —
/// `FunctionDefinition` is always representable as JSON) contributes zero rather
/// than aborting the guard.
/// Test: `super::tests::tools_cacheable_respects_floor`.
pub