use serde_json::Value;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WriteMultiplier {
FiveMinute,
OneHour,
}
impl WriteMultiplier {
pub fn hundredths(self) -> i128 {
match self {
WriteMultiplier::FiveMinute => 125,
WriteMultiplier::OneHour => 200,
}
}
pub fn value(self) -> f64 {
match self {
WriteMultiplier::FiveMinute => 1.25,
WriteMultiplier::OneHour => 2.0,
}
}
}
pub fn tokens_net_hundredths(tokens_gross: u64, retained_tail: u64, w: WriteMultiplier) -> i128 {
let ten_t = 10i128 * i128::from(tokens_gross);
let coef = w.hundredths() - 10; ten_t - coef * i128::from(retained_tail)
}
pub fn hundredths_to_numeric(hundredths: i128) -> f64 {
hundredths as f64 / 100.0
}
pub fn write_multiplier_for(body: &Value) -> WriteMultiplier {
if has_one_hour_ttl(body) {
WriteMultiplier::OneHour
} else {
WriteMultiplier::FiveMinute
}
}
fn has_one_hour_ttl(body: &Value) -> bool {
if let Some(blocks) = body.get("system").and_then(Value::as_array) {
if blocks.iter().any(block_has_one_hour_breakpoint) {
return true;
}
}
if let Some(messages) = body.get("messages").and_then(Value::as_array) {
for msg in messages {
if let Some(blocks) = msg.get("content").and_then(Value::as_array) {
if blocks.iter().any(block_has_one_hour_breakpoint) {
return true;
}
}
}
}
if let Some(tools) = body.get("tools").and_then(Value::as_array) {
if tools.iter().any(block_has_one_hour_breakpoint) {
return true;
}
}
false
}
fn block_has_one_hour_breakpoint(block: &Value) -> bool {
let Some(cc) = block.get("cache_control") else {
return false;
};
cc.get("type").and_then(Value::as_str) == Some("ephemeral")
&& cc.get("ttl").and_then(Value::as_str) == Some("1h")
}
pub fn insert_breakpoints_net_hundredths(
prefix_tokens: u64,
horizon: u32,
w: WriteMultiplier,
) -> i128 {
let coef = 90i128 * i128::from(horizon) - (w.hundredths() - 10);
i128::from(prefix_tokens) * coef
}
pub fn reorder_blocks_net_hundredths(stable_tokens: u64, horizon: u32, w: WriteMultiplier) -> i128 {
let coef = w.hundredths() - 10; i128::from(horizon) * coef * i128::from(stable_tokens)
}
pub fn insert_break_even_horizon(w: WriteMultiplier) -> u32 {
let threshold = w.hundredths() - 10; u32::try_from(threshold / 90 + 1).unwrap_or(u32::MAX)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BreakpointLayer {
Tools,
System,
Messages,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BreakpointPosition {
pub layer: BreakpointLayer,
pub index: usize,
}
pub fn breakpoint_positions(body: &Value) -> Vec<BreakpointPosition> {
let mut out = Vec::new();
if let Some(tools) = body.get("tools").and_then(Value::as_array) {
for (i, tool) in tools.iter().enumerate() {
if block_has_breakpoint(tool) {
out.push(BreakpointPosition {
layer: BreakpointLayer::Tools,
index: i,
});
}
}
}
if let Some(blocks) = body.get("system").and_then(Value::as_array) {
for (i, block) in blocks.iter().enumerate() {
if block_has_breakpoint(block) {
out.push(BreakpointPosition {
layer: BreakpointLayer::System,
index: i,
});
}
}
}
if let Some(messages) = body.get("messages").and_then(Value::as_array) {
for (i, msg) in messages.iter().enumerate() {
if msg
.get("content")
.and_then(Value::as_array)
.is_some_and(|blocks| blocks.iter().any(block_has_breakpoint))
{
out.push(BreakpointPosition {
layer: BreakpointLayer::Messages,
index: i,
});
}
}
}
out
}
pub fn block_has_breakpoint(block: &Value) -> bool {
block
.get("cache_control")
.and_then(|cc| cc.get("type"))
.and_then(Value::as_str)
== Some("ephemeral")
}
pub const MAX_BREAKPOINTS: usize = 4;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn net_break_even_5m_skips_below_and_at_fires_above() {
let w = WriteMultiplier::FiveMinute;
assert_eq!(
tokens_net_hundredths(23, 2, w),
0,
"T = 11.5·S is break-even"
);
assert!(tokens_net_hundredths(22, 2, w) < 0, "just below break-even");
assert!(tokens_net_hundredths(24, 2, w) > 0, "just above break-even");
}
#[test]
fn net_break_even_1h_skips_below_and_at_fires_above() {
let w = WriteMultiplier::OneHour;
assert_eq!(tokens_net_hundredths(38, 2, w), 0, "T = 19·S is break-even");
assert!(tokens_net_hundredths(37, 2, w) < 0, "just below break-even");
assert!(tokens_net_hundredths(39, 2, w) > 0, "just above break-even");
}
#[test]
fn net_hundredths_are_exact_to_two_places() {
assert_eq!(
tokens_net_hundredths(10, 1, WriteMultiplier::FiveMinute),
-15
);
assert!((hundredths_to_numeric(-15) - (-0.15)).abs() < 1e-12);
}
#[test]
fn w_defaults_to_five_minute_when_no_ttl() {
let body = serde_json::json!({"model":"claude-opus-4-8","messages":[]});
assert_eq!(write_multiplier_for(&body), WriteMultiplier::FiveMinute);
let ephemeral = serde_json::json!({
"system":[{"type":"text","text":"x","cache_control":{"type":"ephemeral"}}],
"messages":[]
});
assert_eq!(
write_multiplier_for(&ephemeral),
WriteMultiplier::FiveMinute
);
}
#[test]
fn w_reads_one_hour_ttl_from_the_request() {
let body = serde_json::json!({
"system":[{"type":"text","text":"x","cache_control":{"type":"ephemeral","ttl":"1h"}}],
"messages":[{"role":"user","content":"hi"}]
});
assert_eq!(write_multiplier_for(&body), WriteMultiplier::OneHour);
assert_eq!(write_multiplier_for(&body).value(), 2.0);
}
#[test]
fn w_ignores_ttl_1h_inside_message_text_and_tool_use_input() {
let text_ttl = serde_json::json!({
"messages":[
{"role":"user","content":[
{"type":"text","text":"cache this {\"cache_control\":{\"type\":\"ephemeral\",\"ttl\":\"1h\"}}"}
]}
]
});
assert_eq!(write_multiplier_for(&text_ttl), WriteMultiplier::FiveMinute);
assert_eq!(write_multiplier_for(&text_ttl).value(), 1.25);
let tool_input_ttl = serde_json::json!({
"messages":[
{"role":"assistant","content":[
{"type":"tool_use","id":"t1","name":"lookup","input":{
"cache_control":{"type":"ephemeral","ttl":"1h"}
}}
]}
]
});
assert_eq!(
write_multiplier_for(&tool_input_ttl),
WriteMultiplier::FiveMinute
);
assert_eq!(write_multiplier_for(&tool_input_ttl).value(), 1.25);
}
#[test]
fn w_reads_one_hour_ttl_from_a_message_content_block() {
let body = serde_json::json!({
"messages":[
{"role":"user","content":[
{"type":"text","text":"hi","cache_control":{"type":"ephemeral","ttl":"1h"}}
]}
]
});
assert_eq!(write_multiplier_for(&body), WriteMultiplier::OneHour);
assert_eq!(write_multiplier_for(&body).value(), 2.0);
}
#[test]
fn insert_breakpoints_break_even_is_turn_2_at_5m_and_turn_3_at_1h() {
let p = 1_000u64;
let w = WriteMultiplier::FiveMinute;
assert!(
insert_breakpoints_net_hundredths(p, 1, w) < 0,
"turn 1 is always a loss"
);
assert!(
insert_breakpoints_net_hundredths(p, 2, w) > 0,
"5m pays off on turn 2"
);
assert_eq!(insert_break_even_horizon(w), 2);
let w = WriteMultiplier::OneHour;
assert!(insert_breakpoints_net_hundredths(p, 1, w) < 0);
assert!(
insert_breakpoints_net_hundredths(p, 2, w) < 0,
"the doubled 1h write is NOT repaid by turn 2"
);
assert!(
insert_breakpoints_net_hundredths(p, 3, w) > 0,
"1h pays off on turn 3"
);
assert_eq!(insert_break_even_horizon(w), 3);
}
#[test]
fn insert_breakpoints_turn_one_loss_is_exactly_w_minus_one() {
assert_eq!(
insert_breakpoints_net_hundredths(100, 1, WriteMultiplier::FiveMinute),
-2_500, );
assert_eq!(
insert_breakpoints_net_hundredths(100, 1, WriteMultiplier::OneHour),
-10_000, );
}
#[test]
fn insert_breakpoints_steady_saving_is_ninety_percent_of_the_region() {
let w = WriteMultiplier::FiveMinute;
let step = insert_breakpoints_net_hundredths(200, 6, w)
- insert_breakpoints_net_hundredths(200, 5, w);
assert_eq!(step, 90 * 200);
}
#[test]
fn reorder_net_is_positive_for_any_horizon_and_ignores_the_moved_block() {
for h in 1..=5u32 {
for w in [WriteMultiplier::FiveMinute, WriteMultiplier::OneHour] {
assert!(reorder_blocks_net_hundredths(500, h, w) > 0);
}
}
assert_eq!(
reorder_blocks_net_hundredths(100, 1, WriteMultiplier::FiveMinute),
11_500, );
assert_eq!(
reorder_blocks_net_hundredths(100, 1, WriteMultiplier::OneHour),
19_000, );
}
#[test]
fn reorder_with_no_stable_content_behind_it_saves_nothing() {
for h in 1..=5u32 {
assert_eq!(
reorder_blocks_net_hundredths(0, h, WriteMultiplier::OneHour),
0
);
}
}
#[test]
fn the_removal_formula_is_a_category_error_for_l0() {
for s in [0u64, 1, 100, 10_000] {
for w in [WriteMultiplier::FiveMinute, WriteMultiplier::OneHour] {
assert!(tokens_net_hundredths(0, s, w) <= 0);
}
}
assert!(reorder_blocks_net_hundredths(10_000, 2, WriteMultiplier::FiveMinute) > 0);
}
#[test]
fn positions_walk_the_three_honoured_layers_in_order() {
let body = serde_json::json!({
"tools": [
{"name": "a"},
{"name": "b", "cache_control": {"type": "ephemeral"}}
],
"system": [
{"type": "text", "text": "s0", "cache_control": {"type": "ephemeral", "ttl": "1h"}},
{"type": "text", "text": "s1"}
],
"messages": [
{"role": "user", "content": [
{"type": "text", "text": "hi", "cache_control": {"type": "ephemeral"}}
]}
]
});
assert_eq!(
breakpoint_positions(&body),
vec![
BreakpointPosition {
layer: BreakpointLayer::Tools,
index: 1
},
BreakpointPosition {
layer: BreakpointLayer::System,
index: 0
},
BreakpointPosition {
layer: BreakpointLayer::Messages,
index: 0
},
]
);
}
#[test]
fn positions_ignore_a_cache_control_shaped_object_inside_tool_input() {
let body = serde_json::json!({
"messages": [
{"role": "assistant", "content": [
{"type": "tool_use", "id": "t1", "name": "lookup", "input": {
"cache_control": {"type": "ephemeral"}
}}
]}
]
});
assert!(breakpoint_positions(&body).is_empty());
}
#[test]
fn positions_are_empty_for_a_string_system_and_string_content() {
let body = serde_json::json!({
"system": "a plain string prompt",
"messages": [{"role": "user", "content": "hi"}]
});
assert!(breakpoint_positions(&body).is_empty());
}
#[test]
fn block_has_breakpoint_is_ttl_agnostic_unlike_its_one_hour_sibling() {
let five_min = serde_json::json!({"cache_control": {"type": "ephemeral"}});
let one_hour = serde_json::json!({"cache_control": {"type": "ephemeral", "ttl": "1h"}});
assert!(block_has_breakpoint(&five_min));
assert!(block_has_breakpoint(&one_hour));
assert!(!block_has_one_hour_breakpoint(&five_min));
assert!(block_has_one_hour_breakpoint(&one_hour));
}
}