1use serde::{Deserialize, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5use crate::keys::StatusResponse;
6
7#[derive(Debug, Clone, Deserialize)]
9pub struct ComputeTemplate {
10 pub id: String,
12
13 #[serde(default)]
15 pub name: Option<String>,
16
17 #[serde(default)]
19 pub gpu: Option<String>,
20
21 #[serde(default)]
23 pub gpu_count: Option<i32>,
24
25 #[serde(default)]
27 pub vram_gb: Option<i32>,
28
29 #[serde(default)]
31 pub vcpus: Option<i32>,
32
33 #[serde(default)]
35 pub ram_gb: Option<i32>,
36
37 #[serde(default)]
39 pub price_per_hour_usd: Option<f64>,
40
41 #[serde(default)]
43 pub zones: Option<Vec<String>>,
44}
45
46#[derive(Debug, Clone, Deserialize)]
48pub struct TemplatesResponse {
49 pub templates: Vec<ComputeTemplate>,
51}
52
53#[derive(Debug, Clone, Serialize, Default)]
55pub struct ProvisionRequest {
56 pub template: String,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub zone: Option<String>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub spot: Option<bool>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub auto_teardown_minutes: Option<i32>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub ssh_public_key: Option<String>,
74}
75
76#[derive(Debug, Clone, Deserialize)]
78pub struct ProvisionResponse {
79 pub instance_id: String,
81
82 pub status: String,
84
85 #[serde(default)]
87 pub template: Option<String>,
88
89 #[serde(default)]
91 pub zone: Option<String>,
92
93 #[serde(default)]
95 pub ssh_address: Option<String>,
96
97 #[serde(default)]
99 pub price_per_hour_usd: Option<f64>,
100}
101
102#[derive(Debug, Clone, Deserialize)]
104pub struct ComputeInstance {
105 pub id: String,
107
108 pub status: String,
110
111 #[serde(default)]
113 pub template: Option<String>,
114
115 #[serde(default)]
117 pub zone: Option<String>,
118
119 #[serde(default)]
121 pub ssh_address: Option<String>,
122
123 #[serde(default)]
125 pub created_at: Option<String>,
126
127 #[serde(default)]
129 pub price_per_hour_usd: Option<f64>,
130
131 #[serde(default)]
133 pub auto_teardown_minutes: Option<i32>,
134}
135
136#[derive(Debug, Clone, Deserialize)]
138pub struct InstancesResponse {
139 pub instances: Vec<ComputeInstance>,
141}
142
143#[derive(Debug, Clone, Deserialize)]
145pub struct InstanceResponse {
146 pub instance: ComputeInstance,
148}
149
150#[derive(Debug, Clone, Deserialize)]
152pub struct DeleteResponse {
153 pub status: String,
155
156 #[serde(default)]
158 pub instance_id: Option<String>,
159}
160
161#[derive(Debug, Clone, Serialize, Default)]
163pub struct SSHKeyRequest {
164 pub ssh_public_key: String,
166}
167
168#[derive(Debug, Clone, Serialize, Default)]
170pub struct BillingRequest {
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub instance_id: Option<String>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub start_date: Option<String>,
178
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub end_date: Option<String>,
182}
183
184#[derive(Debug, Clone, Deserialize)]
186pub struct BillingEntry {
187 pub instance_id: String,
189
190 #[serde(default)]
192 pub instance_name: Option<String>,
193
194 pub cost_usd: f64,
196
197 #[serde(default)]
199 pub usage_hours: Option<f64>,
200
201 #[serde(default)]
203 pub sku_description: Option<String>,
204
205 #[serde(default)]
207 pub start_time: Option<String>,
208
209 #[serde(default)]
211 pub end_time: Option<String>,
212}
213
214#[derive(Debug, Clone, Deserialize)]
216pub struct BillingResponse {
217 pub entries: Vec<BillingEntry>,
219
220 pub total_cost_usd: f64,
222}
223
224impl Client {
225 pub async fn compute_templates(&self) -> Result<TemplatesResponse> {
227 let (resp, _meta) = self
228 .get_json::<TemplatesResponse>("/qai/v1/compute/templates")
229 .await?;
230 Ok(resp)
231 }
232
233 pub async fn compute_provision(&self, req: &ProvisionRequest) -> Result<ProvisionResponse> {
235 let (resp, _meta) = self
236 .post_json::<ProvisionRequest, ProvisionResponse>("/qai/v1/compute/provision", req)
237 .await?;
238 Ok(resp)
239 }
240
241 pub async fn compute_instances(&self) -> Result<InstancesResponse> {
243 let (resp, _meta) = self
244 .get_json::<InstancesResponse>("/qai/v1/compute/instances")
245 .await?;
246 Ok(resp)
247 }
248
249 pub async fn compute_instance(&self, id: &str) -> Result<InstanceResponse> {
251 let path = format!("/qai/v1/compute/instance/{id}");
252 let (resp, _meta) = self.get_json::<InstanceResponse>(&path).await?;
253 Ok(resp)
254 }
255
256 pub async fn compute_delete(&self, id: &str) -> Result<DeleteResponse> {
258 let path = format!("/qai/v1/compute/instance/{id}");
259 let (resp, _meta) = self.delete_json::<DeleteResponse>(&path).await?;
260 Ok(resp)
261 }
262
263 pub async fn compute_ssh_key(&self, id: &str, req: &SSHKeyRequest) -> Result<StatusResponse> {
265 let path = format!("/qai/v1/compute/instance/{id}/ssh-key");
266 let (resp, _meta) = self
267 .post_json::<SSHKeyRequest, StatusResponse>(&path, req)
268 .await?;
269 Ok(resp)
270 }
271
272 pub async fn compute_keepalive(&self, id: &str) -> Result<StatusResponse> {
274 let path = format!("/qai/v1/compute/instance/{id}/keepalive");
275 let (resp, _meta) = self
276 .post_json::<serde_json::Value, StatusResponse>(&path, &serde_json::json!({}))
277 .await?;
278 Ok(resp)
279 }
280
281 pub async fn compute_billing(&self, req: &BillingRequest) -> Result<BillingResponse> {
283 let (resp, _meta) = self
284 .post_json::<BillingRequest, BillingResponse>("/qai/v1/compute/billing", req)
285 .await?;
286 Ok(resp)
287 }
288}