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