Skip to main content

anthropic_async/types/
common.rs

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