use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CacheStrategy {
#[default]
Auto,
Anthropic(AnthropicCacheConfig),
None,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AnthropicCacheConfig {
#[serde(default = "default_true")]
pub cache_tools: bool,
#[serde(default = "default_true")]
pub cache_system: bool,
#[serde(default = "default_tail_count")]
pub tail_message_count: usize,
}
fn default_true() -> bool {
true
}
fn default_tail_count() -> usize {
2
}
impl Default for AnthropicCacheConfig {
fn default() -> Self {
Self {
cache_tools: true,
cache_system: true,
tail_message_count: 2,
}
}
}
impl CacheStrategy {
pub fn auto() -> Self {
Self::Auto
}
pub fn anthropic(cache_tools: bool, cache_system: bool, tail_count: usize) -> Self {
Self::Anthropic(AnthropicCacheConfig {
cache_tools,
cache_system,
tail_message_count: tail_count,
})
}
pub fn none() -> Self {
Self::None
}
pub fn is_enabled(&self) -> bool {
!matches!(self, Self::None)
}
pub fn to_anthropic_config(&self) -> Option<AnthropicCacheConfig> {
match self {
Self::Auto => Some(AnthropicCacheConfig::default()),
Self::Anthropic(config) => Some(config.clone()),
Self::None => None,
}
}
pub fn max_breakpoint_count(&self, has_tools: bool, has_system: bool) -> usize {
match self.to_anthropic_config() {
Some(config) => {
let mut count = 0;
if config.cache_tools && has_tools {
count += 1;
}
if config.cache_system && has_system {
count += 1;
}
count += config.tail_message_count;
count.min(4) }
None => 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_strategy_is_auto() {
assert_eq!(CacheStrategy::default(), CacheStrategy::Auto);
}
#[test]
fn test_auto_returns_default_anthropic_config() {
let config = CacheStrategy::Auto.to_anthropic_config().unwrap();
assert!(config.cache_tools);
assert!(config.cache_system);
assert_eq!(config.tail_message_count, 2);
}
#[test]
fn test_none_returns_no_config() {
assert!(CacheStrategy::None.to_anthropic_config().is_none());
}
#[test]
fn test_custom_anthropic_config() {
let strategy = CacheStrategy::anthropic(false, true, 3);
let config = strategy.to_anthropic_config().unwrap();
assert!(!config.cache_tools);
assert!(config.cache_system);
assert_eq!(config.tail_message_count, 3);
}
#[test]
fn test_max_breakpoint_count() {
assert_eq!(CacheStrategy::Auto.max_breakpoint_count(true, true), 4);
assert_eq!(CacheStrategy::Auto.max_breakpoint_count(false, true), 3);
assert_eq!(CacheStrategy::None.max_breakpoint_count(true, true), 0);
let custom = CacheStrategy::anthropic(false, true, 5);
assert_eq!(custom.max_breakpoint_count(true, true), 4);
}
#[test]
fn test_is_enabled() {
assert!(CacheStrategy::Auto.is_enabled());
assert!(CacheStrategy::anthropic(true, true, 2).is_enabled());
assert!(!CacheStrategy::None.is_enabled());
}
#[test]
fn test_serialization_auto() {
let strategy = CacheStrategy::Auto;
let json = serde_json::to_string(&strategy).unwrap();
assert!(json.contains("auto"));
let deserialized: CacheStrategy = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, CacheStrategy::Auto);
}
#[test]
fn test_serialization_anthropic() {
let strategy = CacheStrategy::anthropic(true, false, 1);
let json = serde_json::to_string(&strategy).unwrap();
assert!(json.contains("anthropic"));
assert!(json.contains("cache_tools"));
let deserialized: CacheStrategy = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, strategy);
}
#[test]
fn test_serialization_none() {
let strategy = CacheStrategy::None;
let json = serde_json::to_string(&strategy).unwrap();
assert!(json.contains("none"));
let deserialized: CacheStrategy = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, CacheStrategy::None);
}
#[test]
fn test_anthropic_cache_config_default() {
let config = AnthropicCacheConfig::default();
assert!(config.cache_tools);
assert!(config.cache_system);
assert_eq!(config.tail_message_count, 2);
}
}