gproxy_protocol/protocol/claude/common/
usage.rs1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use super::{ClaudeModel, InferenceGeo, IterationUsageType, JsonObject, Speed, UsageServiceTier};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
8#[non_exhaustive]
9pub struct Usage {
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub input_tokens: Option<u64>,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub output_tokens: Option<u64>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub cache_creation_input_tokens: Option<u64>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub cache_read_input_tokens: Option<u64>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub cache_creation: Option<CacheCreation>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub output_tokens_details: Option<OutputTokensDetails>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub server_tool_use: Option<ServerToolUsage>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub iterations: Option<Vec<IterationUsage>>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub inference_geo: Option<InferenceGeo>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub service_tier: Option<UsageServiceTier>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub speed: Option<Speed>,
32 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
33 pub extra: JsonObject,
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
37#[non_exhaustive]
38pub struct CacheCreation {
39 pub ephemeral_1h_input_tokens: u64,
40 pub ephemeral_5m_input_tokens: u64,
41 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
42 pub extra: JsonObject,
43}
44
45impl Usage {
46 pub fn cache_creation_total(&self) -> Option<u64> {
49 self.cache_creation_input_tokens.or_else(|| {
50 self.cache_creation
51 .as_ref()
52 .map(|c| c.ephemeral_5m_input_tokens + c.ephemeral_1h_input_tokens)
53 })
54 }
55}
56
57#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
58#[non_exhaustive]
59pub struct OutputTokensDetails {
60 pub thinking_tokens: u64,
61 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
62 pub extra: JsonObject,
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
66#[non_exhaustive]
67pub struct ServerToolUsage {
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub web_fetch_requests: Option<u64>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub web_search_requests: Option<u64>,
72 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
73 pub extra: JsonObject,
74}
75
76#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
77#[non_exhaustive]
78pub struct IterationUsage {
79 #[serde(rename = "type")]
80 pub type_: IterationUsageType,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub model: Option<ClaudeModel>,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub input_tokens: Option<u64>,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub output_tokens: Option<u64>,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub cache_creation_input_tokens: Option<u64>,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub cache_read_input_tokens: Option<u64>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub cache_creation: Option<CacheCreation>,
93 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
94 pub extra: JsonObject,
95}