use crate::types::{
CacheControlEphemeral, ContentBlock, MessageParam, MessageParamContent, MessageRole,
SystemPrompt, TextBlock,
};
pub const MAX_CACHE_BREAKPOINTS: usize = 4;
pub fn count_system_cache_controls(system: &Option<SystemPrompt>) -> usize {
match system {
Some(SystemPrompt::Blocks(blocks)) => {
blocks.iter().filter(|block| block.block.cache_control.is_some()).count()
}
_ => 0,
}
}
pub fn prune_cache_controls_in_messages(messages: &mut [MessageParam], keep_latest: usize) {
if keep_latest == 0 {
for message in messages.iter_mut() {
clear_cache_control_from_message(message);
}
return;
}
let mut cached_positions = Vec::new();
for (msg_idx, message) in messages.iter().enumerate() {
if let MessageParamContent::Array(blocks) = &message.content {
for (block_idx, block) in blocks.iter().enumerate() {
if block_has_cache_control(block) {
cached_positions.push((msg_idx, block_idx));
}
}
}
}
if cached_positions.len() <= keep_latest {
return;
}
let drop_count = cached_positions.len() - keep_latest;
for (msg_idx, block_idx) in cached_positions.into_iter().take(drop_count) {
if let MessageParamContent::Array(blocks) = &mut messages[msg_idx].content
&& let Some(block) = blocks.get_mut(block_idx)
{
clear_cache_control_on_block(block);
}
}
}
pub fn apply_cache_control_to_messages(messages: &mut [MessageParam]) {
for msg in messages.iter_mut() {
clear_cache_control_from_message(msg);
}
let user_indices: Vec<usize> = messages
.iter()
.enumerate()
.filter(|(_, msg)| msg.role == MessageRole::User)
.map(|(idx, _)| idx)
.rev()
.take(MAX_CACHE_BREAKPOINTS - 1) .collect();
for idx in user_indices {
apply_cache_control_to_message(&mut messages[idx]);
}
}
fn clear_cache_control_from_message(message: &mut MessageParam) {
if let MessageParamContent::Array(blocks) = &mut message.content {
for block in blocks.iter_mut() {
clear_cache_control_on_block(block);
}
}
}
fn clear_cache_control_on_block(block: &mut ContentBlock) {
match block {
ContentBlock::Text(text_block) => {
text_block.cache_control = None;
}
ContentBlock::ToolResult(tool_result) => {
tool_result.cache_control = None;
}
ContentBlock::ToolUse(tool_use) => {
tool_use.cache_control = None;
}
ContentBlock::Image(image_block) => {
image_block.cache_control = None;
}
ContentBlock::Document(document_block) => {
document_block.cache_control = None;
}
ContentBlock::ServerToolUse(server_tool_use) => {
server_tool_use.cache_control = None;
}
ContentBlock::WebSearchToolResult(web_search_result) => {
web_search_result.cache_control = None;
}
ContentBlock::Thinking(_)
| ContentBlock::RedactedThinking(_)
| ContentBlock::CodeExecutionResult(_)
| ContentBlock::ProgrammaticToolUse(_) => {}
}
}
pub(crate) fn apply_cache_control_to_message(message: &mut MessageParam) {
match &mut message.content {
MessageParamContent::String(text) => {
let block = ContentBlock::Text(
TextBlock::new(text.clone()).with_cache_control(CacheControlEphemeral::new()),
);
message.content = MessageParamContent::Array(vec![block]);
}
MessageParamContent::Array(blocks) => {
if let Some(last_block) = blocks.last_mut() {
set_cache_control_on_block(last_block);
}
}
}
}
fn set_cache_control_on_block(block: &mut ContentBlock) {
match block {
ContentBlock::Text(text_block) => {
text_block.cache_control = Some(CacheControlEphemeral::new());
}
ContentBlock::ToolResult(tool_result) => {
tool_result.cache_control = Some(CacheControlEphemeral::new());
}
ContentBlock::ToolUse(tool_use) => {
tool_use.cache_control = Some(CacheControlEphemeral::new());
}
ContentBlock::Image(_)
| ContentBlock::Document(_)
| ContentBlock::ServerToolUse(_)
| ContentBlock::WebSearchToolResult(_)
| ContentBlock::Thinking(_)
| ContentBlock::RedactedThinking(_)
| ContentBlock::CodeExecutionResult(_)
| ContentBlock::ProgrammaticToolUse(_) => {}
}
}
fn block_has_cache_control(block: &ContentBlock) -> bool {
match block {
ContentBlock::Text(text_block) => text_block.cache_control.is_some(),
ContentBlock::ToolResult(tool_result) => tool_result.cache_control.is_some(),
ContentBlock::ToolUse(tool_use) => tool_use.cache_control.is_some(),
ContentBlock::Image(image_block) => image_block.cache_control.is_some(),
ContentBlock::Document(document_block) => document_block.cache_control.is_some(),
ContentBlock::ServerToolUse(server_tool_use) => server_tool_use.cache_control.is_some(),
ContentBlock::WebSearchToolResult(web_search_result) => {
web_search_result.cache_control.is_some()
}
ContentBlock::Thinking(_) | ContentBlock::RedactedThinking(_) => false,
ContentBlock::CodeExecutionResult(_) | ContentBlock::ProgrammaticToolUse(_) => false,
}
}