1use serde::{Deserialize, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5
6#[derive(Debug, Clone, Deserialize)]
8pub struct BalanceResponse {
9 #[serde(default)]
10 pub user_id: Option<String>,
11 pub balance_ticks: i64,
12 pub balance_usd: f64,
13 #[serde(default)]
14 pub ticks_per_usd: i64,
15}
16
17#[derive(Debug, Clone, Deserialize)]
19pub struct UsageEntry {
20 pub id: String,
21 #[serde(default)]
22 pub request_id: Option<String>,
23 #[serde(default)]
24 pub model: Option<String>,
25 #[serde(default)]
26 pub provider: Option<String>,
27 #[serde(default)]
28 pub endpoint: Option<String>,
29 #[serde(default)]
30 pub delta_ticks: Option<i64>,
31 #[serde(default)]
32 pub balance_after: Option<i64>,
33 #[serde(default)]
34 pub input_tokens: Option<i64>,
35 #[serde(default)]
36 pub output_tokens: Option<i64>,
37 #[serde(default)]
38 pub created_at: Option<String>,
39}
40
41#[derive(Debug, Clone, Deserialize)]
43pub struct UsageResponse {
44 pub entries: Vec<UsageEntry>,
45 pub has_more: bool,
46 #[serde(default)]
47 pub next_cursor: Option<String>,
48}
49
50#[derive(Debug, Clone, Deserialize)]
52pub struct UsageSummaryMonth {
53 pub month: String,
54 pub total_requests: i64,
55 pub total_input_tokens: i64,
56 pub total_output_tokens: i64,
57 pub total_cost_usd: f64,
58 pub total_margin_usd: f64,
59 #[serde(default)]
60 pub by_provider: Vec<serde_json::Value>,
61}
62
63#[derive(Debug, Clone, Deserialize)]
65pub struct UsageSummaryResponse {
66 pub months: Vec<UsageSummaryMonth>,
67}
68
69#[derive(Debug, Clone, Serialize, Default)]
71pub struct UsageQuery {
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub limit: Option<i32>,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub start_after: Option<String>,
79}
80
81#[derive(Debug, Clone, Deserialize)]
83pub struct PricingEntry {
84 #[serde(default)]
85 pub provider: String,
86 #[serde(default)]
87 pub model: String,
88 #[serde(default)]
89 pub display_name: String,
90 #[serde(default)]
91 pub category: Option<String>,
92 #[serde(default)]
93 pub context_window: Option<String>,
94 #[serde(default)]
95 pub input_per_million: f64,
96 #[serde(default)]
97 pub output_per_million: f64,
98 #[serde(default)]
99 pub cached_per_million: f64,
100 #[serde(default)]
101 pub per_unit_price: Option<f64>,
102 #[serde(default)]
103 pub price_unit: Option<String>,
104}
105
106#[derive(Debug, Clone, Deserialize)]
108pub struct PricingResponse {
109 pub pricing: std::collections::HashMap<String, PricingEntry>,
110}
111
112impl Client {
113 pub async fn account_balance(&self) -> Result<BalanceResponse> {
115 let (resp, _meta) = self
116 .get_json::<BalanceResponse>("/qai/v1/account/balance")
117 .await?;
118 Ok(resp)
119 }
120
121 pub async fn account_usage(&self, query: &UsageQuery) -> Result<UsageResponse> {
123 let mut path = "/qai/v1/account/usage".to_string();
124 let mut params = Vec::new();
125 if let Some(limit) = query.limit {
126 params.push(format!("limit={limit}"));
127 }
128 if let Some(ref cursor) = query.start_after {
129 params.push(format!("start_after={cursor}"));
130 }
131 if !params.is_empty() {
132 path.push('?');
133 path.push_str(¶ms.join("&"));
134 }
135 let (resp, _meta) = self.get_json::<UsageResponse>(&path).await?;
136 Ok(resp)
137 }
138
139 pub async fn account_usage_summary(&self, months: Option<i32>) -> Result<UsageSummaryResponse> {
141 let path = if let Some(m) = months {
142 format!("/qai/v1/account/usage/summary?months={m}")
143 } else {
144 "/qai/v1/account/usage/summary".to_string()
145 };
146 let (resp, _meta) = self.get_json::<UsageSummaryResponse>(&path).await?;
147 Ok(resp)
148 }
149
150 pub async fn account_pricing(&self) -> Result<PricingResponse> {
152 let (resp, _meta) = self
153 .get_json::<PricingResponse>("/qai/v1/pricing")
154 .await?;
155 Ok(resp)
156 }
157}