Skip to main content

anthropic_async/types/
common.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4/// Cache time-to-live for prompt caching
5///
6/// See the [Anthropic prompt caching documentation](https://docs.anthropic.com/en/docs/prompt-caching)
7/// for details on caching behavior.
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
9#[serde(rename_all = "snake_case")]
10pub enum CacheTtl {
11    /// 5-minute cache TTL
12    #[serde(rename = "5m")]
13    FiveMinutes,
14    /// 1-hour cache TTL
15    #[serde(rename = "1h")]
16    OneHour,
17}
18
19/// Cache control configuration for content blocks
20///
21/// Used to enable prompt caching on specific content blocks. When mixing TTLs in a single request,
22/// 1-hour cache entries must appear before 5-minute entries.
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24pub struct CacheControl {
25    /// Cache type (currently only "ephemeral" is supported)
26    #[serde(rename = "type")]
27    pub kind: String,
28    /// Time-to-live for the cache entry
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub ttl: Option<CacheTtl>,
31}
32
33impl CacheControl {
34    /// Creates an ephemeral cache control with 5-minute TTL
35    #[must_use]
36    pub fn ephemeral_5m() -> Self {
37        Self {
38            kind: "ephemeral".into(),
39            ttl: Some(CacheTtl::FiveMinutes),
40        }
41    }
42
43    /// Creates an ephemeral cache control with 1-hour TTL
44    #[must_use]
45    pub fn ephemeral_1h() -> Self {
46        Self {
47            kind: "ephemeral".into(),
48            ttl: Some(CacheTtl::OneHour),
49        }
50    }
51
52    /// Creates an ephemeral cache control with default TTL (5 minutes)
53    #[must_use]
54    pub fn ephemeral() -> Self {
55        Self {
56            kind: "ephemeral".into(),
57            ttl: None,
58        }
59    }
60}
61
62/// Validate that when mixing TTLs, `OneHour` entries appear before `FiveMinutes`.
63#[must_use]
64pub fn validate_mixed_ttl_order(block_ttls: impl IntoIterator<Item = CacheTtl>) -> bool {
65    let mut seen_5m = false;
66    for ttl in block_ttls {
67        match ttl {
68            CacheTtl::OneHour if seen_5m => return false, // 1h after 5m → invalid
69            CacheTtl::FiveMinutes => seen_5m = true,
70            CacheTtl::OneHour => {}
71        }
72    }
73    true
74}
75
76/// Metadata for requests
77///
78/// Used to pass additional information about the request.
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
80pub struct Metadata {
81    /// User ID for tracking purposes
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub user_id: Option<String>,
84}
85
86/// Token usage information for a request/response
87///
88/// Includes cache-related token counts when prompt caching is used.
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
90pub struct Usage {
91    /// Number of input tokens processed
92    pub input_tokens: Option<u64>,
93    /// Number of output tokens generated
94    pub output_tokens: Option<u64>,
95    /// Number of tokens used to create cache entries
96    pub cache_creation_input_tokens: Option<u64>,
97    /// Number of tokens read from cache
98    pub cache_read_input_tokens: Option<u64>,
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn ttl_ser_de() {
107        let s = serde_json::to_string(&CacheTtl::FiveMinutes).unwrap();
108        assert_eq!(s, r#""5m""#);
109        let t: CacheTtl = serde_json::from_str(r#""1h""#).unwrap();
110        assert_eq!(t, CacheTtl::OneHour);
111    }
112
113    #[test]
114    fn cache_control_ser() {
115        let cc = CacheControl::ephemeral_5m();
116        let s = serde_json::to_string(&cc).unwrap();
117        assert!(s.contains(r#""type":"ephemeral""#));
118        assert!(s.contains(r#""ttl":"5m""#));
119    }
120
121    #[test]
122    fn ordering_valid() {
123        assert!(validate_mixed_ttl_order([
124            CacheTtl::OneHour,
125            CacheTtl::FiveMinutes
126        ]));
127        assert!(validate_mixed_ttl_order([CacheTtl::FiveMinutes]));
128        assert!(validate_mixed_ttl_order([CacheTtl::OneHour]));
129        assert!(!validate_mixed_ttl_order([
130            CacheTtl::FiveMinutes,
131            CacheTtl::OneHour
132        ]));
133    }
134}