1use serde::{Deserialize, Serialize};
16use serde_json::Value;
17
18#[derive(Debug, Clone, Default, Serialize)]
20pub struct UsageSnapshot {
21 #[serde(skip_serializing_if = "Option::is_none")]
24 pub plan: Option<String>,
25 pub windows: Vec<UsageWindow>,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub credits: Option<UsageCredits>,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub rate_limit_reset_credits: Option<RateLimitResetCredits>,
34 pub raw: Value,
36}
37
38#[derive(Debug, Clone, Default, Serialize)]
43pub struct UsageWindow {
44 pub name: String,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub label: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub used_percent: Option<f64>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub used: Option<f64>,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub limit: Option<f64>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub resets_at: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub resets_at_unix: Option<i64>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub window_seconds: Option<i64>,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct UsageWindowDescriptor {
73 pub key: String,
76 pub scope: UsageWindowScope,
78 pub meter: UsageWindowMeter,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub period_start_unix: Option<i64>,
83 pub boundary_source: UsageWindowBoundarySource,
85 pub boundary_confidence: UsageWindowBoundaryConfidence,
87}
88
89impl UsageWindowDescriptor {
90 pub fn from_window(window: &UsageWindow) -> Self {
95 let period_start_unix = window
96 .resets_at_unix
97 .zip(window.window_seconds)
98 .filter(|(_, seconds)| *seconds > 0)
99 .map(|(reset, seconds)| reset.saturating_sub(seconds));
100 let (boundary_source, boundary_confidence) = if period_start_unix.is_some() {
101 (
102 UsageWindowBoundarySource::ResetAndDuration,
103 UsageWindowBoundaryConfidence::Exact,
104 )
105 } else if window.resets_at_unix.is_some() || window.resets_at.is_some() {
106 (
107 UsageWindowBoundarySource::ResetOnly,
108 UsageWindowBoundaryConfidence::Partial,
109 )
110 } else {
111 (
112 UsageWindowBoundarySource::Unknown,
113 UsageWindowBoundaryConfidence::Unknown,
114 )
115 };
116 Self {
117 key: window.name.clone(),
118 scope: UsageWindowScope::Unknown,
119 meter: UsageWindowMeter::Opaque,
120 period_start_unix,
121 boundary_source,
122 boundary_confidence,
123 }
124 }
125
126 pub fn scope(mut self, scope: UsageWindowScope) -> Self {
127 self.scope = scope;
128 self
129 }
130
131 pub fn meter(mut self, meter: UsageWindowMeter) -> Self {
132 self.meter = meter;
133 self
134 }
135
136 pub fn period_start(
137 mut self,
138 unix: i64,
139 source: UsageWindowBoundarySource,
140 confidence: UsageWindowBoundaryConfidence,
141 ) -> Self {
142 self.period_start_unix = Some(unix);
143 self.boundary_source = source;
144 self.boundary_confidence = confidence;
145 self
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151#[serde(tag = "kind", rename_all = "snake_case")]
152pub enum UsageWindowScope {
153 All,
154 Models { models: Vec<String> },
155 Feature { feature: String },
156 Unknown,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
161#[serde(rename_all = "snake_case")]
162pub enum UsageWindowMeter {
163 Tokens,
164 Requests,
165 Credits,
166 Usd,
167 Opaque,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
172#[serde(rename_all = "snake_case")]
173pub enum UsageWindowBoundarySource {
174 Upstream,
176 ResetAndDuration,
178 KnownWindow,
180 ResetOnly,
182 Unknown,
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
187#[serde(rename_all = "snake_case")]
188pub enum UsageWindowBoundaryConfidence {
189 Exact,
190 Derived,
191 Partial,
192 Unknown,
193}
194
195impl UsageWindow {
196 pub fn percent(name: impl Into<String>, used_percent: f64) -> Self {
198 Self {
199 name: name.into(),
200 used_percent: Some(used_percent),
201 ..Default::default()
202 }
203 }
204
205 pub fn amounts(name: impl Into<String>, used: f64, limit: f64) -> Self {
207 Self {
208 name: name.into(),
209 used: Some(used),
210 limit: Some(limit),
211 ..Default::default()
212 }
213 }
214
215 pub fn resets_iso(mut self, iso: impl Into<String>) -> Self {
217 self.resets_at = Some(iso.into());
218 self
219 }
220
221 pub fn resets_unix(mut self, unix: i64) -> Self {
223 self.resets_at_unix = Some(unix);
224 self
225 }
226
227 pub fn window_secs(mut self, seconds: i64) -> Self {
229 self.window_seconds = Some(seconds);
230 self
231 }
232
233 pub fn label(mut self, label: impl Into<String>) -> Self {
235 self.label = Some(label.into());
236 self
237 }
238}
239
240#[derive(Debug, Clone, Default, Serialize)]
243pub struct UsageCredits {
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub has_credits: Option<bool>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub unlimited: Option<bool>,
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub balance: Option<String>,
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub used_credits: Option<f64>,
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub monthly_limit: Option<f64>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub currency: Option<String>,
259}
260
261#[derive(Debug, Clone, Default, Serialize)]
262pub struct RateLimitResetCredits {
263 pub available_count: i64,
264}
265
266#[derive(Debug, Clone, Serialize)]
267pub struct RateLimitResetCreditConsumeResponse {
268 pub outcome: RateLimitResetCreditConsumeOutcome,
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub windows_reset: Option<i64>,
271 pub raw: Value,
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
275#[serde(rename_all = "snake_case")]
276pub enum RateLimitResetCreditConsumeOutcome {
277 Reset,
278 NothingToReset,
279 NoCredit,
280 AlreadyRedeemed,
281}