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 ComputeInstanceInfo {
139 pub instance_id: String,
141
142 pub template: String,
144
145 pub status: String,
147
148 #[serde(default)]
150 pub gcp_status: Option<String>,
151
152 pub zone: String,
154
155 #[serde(default)]
157 pub machine_type: Option<String>,
158
159 #[serde(default)]
161 pub external_ip: Option<String>,
162
163 #[serde(default)]
165 pub gpu_type: Option<String>,
166
167 #[serde(default)]
169 pub gpu_count: Option<i32>,
170
171 #[serde(default)]
173 pub spot: bool,
174
175 #[serde(default)]
177 pub hourly_usd: f64,
178
179 #[serde(default)]
181 pub cost_usd: f64,
182
183 #[serde(default)]
185 pub uptime_minutes: i32,
186
187 #[serde(default)]
189 pub auto_teardown_minutes: i32,
190
191 #[serde(default)]
193 pub ssh_username: Option<String>,
194
195 #[serde(default)]
197 pub last_active_at: Option<String>,
198
199 #[serde(default)]
201 pub created_at: Option<String>,
202
203 #[serde(default)]
205 pub terminated_at: Option<String>,
206
207 #[serde(default)]
209 pub error_message: Option<String>,
210}
211
212#[derive(Debug, Clone, Deserialize)]
214pub struct InstancesResponse {
215 pub instances: Vec<ComputeInstance>,
217}
218
219#[derive(Debug, Clone, Deserialize)]
221pub struct InstanceResponse {
222 pub instance: ComputeInstance,
224}
225
226#[derive(Debug, Clone, Deserialize)]
228pub struct DeleteResponse {
229 pub status: String,
231
232 #[serde(default)]
234 pub instance_id: Option<String>,
235}
236
237#[derive(Debug, Clone, Serialize, Default)]
239pub struct SSHKeyRequest {
240 pub ssh_public_key: String,
242}
243
244#[derive(Debug, Clone, Serialize, Default)]
246pub struct BillingRequest {
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub instance_id: Option<String>,
250
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub start_date: Option<String>,
254
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub end_date: Option<String>,
258}
259
260#[derive(Debug, Clone, Deserialize)]
262pub struct BillingEntry {
263 pub instance_id: String,
265
266 #[serde(default)]
268 pub instance_name: Option<String>,
269
270 pub cost_usd: f64,
272
273 #[serde(default)]
275 pub usage_hours: Option<f64>,
276
277 #[serde(default)]
279 pub sku_description: Option<String>,
280
281 #[serde(default)]
283 pub start_time: Option<String>,
284
285 #[serde(default)]
287 pub end_time: Option<String>,
288}
289
290#[derive(Debug, Clone, Deserialize)]
292pub struct BillingResponse {
293 pub entries: Vec<BillingEntry>,
295
296 pub total_cost_usd: f64,
298}
299
300impl Client {
301 pub async fn compute_templates(&self) -> Result<TemplatesResponse> {
303 let (resp, _meta) = self
304 .get_json::<TemplatesResponse>("/qai/v1/compute/templates")
305 .await?;
306 Ok(resp)
307 }
308
309 pub async fn compute_provision(&self, req: &ProvisionRequest) -> Result<ProvisionResponse> {
311 let (resp, _meta) = self
312 .post_json::<ProvisionRequest, ProvisionResponse>("/qai/v1/compute/provision", req)
313 .await?;
314 Ok(resp)
315 }
316
317 pub async fn compute_instances(&self) -> Result<InstancesResponse> {
319 let (resp, _meta) = self
320 .get_json::<InstancesResponse>("/qai/v1/compute/instances")
321 .await?;
322 Ok(resp)
323 }
324
325 pub async fn compute_instance(&self, id: &str) -> Result<InstanceResponse> {
327 let path = format!("/qai/v1/compute/instance/{id}");
328 let (resp, _meta) = self.get_json::<InstanceResponse>(&path).await?;
329 Ok(resp)
330 }
331
332 pub async fn compute_delete(&self, id: &str) -> Result<DeleteResponse> {
334 let path = format!("/qai/v1/compute/instance/{id}");
335 let (resp, _meta) = self.delete_json::<DeleteResponse>(&path).await?;
336 Ok(resp)
337 }
338
339 pub async fn compute_ssh_key(&self, id: &str, req: &SSHKeyRequest) -> Result<StatusResponse> {
341 let path = format!("/qai/v1/compute/instance/{id}/ssh-key");
342 let (resp, _meta) = self
343 .post_json::<SSHKeyRequest, StatusResponse>(&path, req)
344 .await?;
345 Ok(resp)
346 }
347
348 pub async fn compute_keepalive(&self, id: &str) -> Result<StatusResponse> {
350 let path = format!("/qai/v1/compute/instance/{id}/keepalive");
351 let (resp, _meta) = self
352 .post_json::<serde_json::Value, StatusResponse>(&path, &serde_json::json!({}))
353 .await?;
354 Ok(resp)
355 }
356
357 pub async fn compute_billing(&self, req: &BillingRequest) -> Result<BillingResponse> {
359 let (resp, _meta) = self
360 .post_json::<BillingRequest, BillingResponse>("/qai/v1/compute/billing", req)
361 .await?;
362 Ok(resp)
363 }
364}