Skip to main content

quantum_sdk/
compute.rs

1use serde::{Deserialize, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5use crate::keys::StatusResponse;
6
7/// A compute instance template describing available GPU configurations.
8#[derive(Debug, Clone, Deserialize)]
9pub struct ComputeTemplate {
10    /// Template identifier (e.g. "a100-80gb", "h100-sxm").
11    pub id: String,
12
13    /// Human-readable name.
14    #[serde(default)]
15    pub name: Option<String>,
16
17    /// GPU type description.
18    #[serde(default)]
19    pub gpu: Option<String>,
20
21    /// Number of GPUs.
22    #[serde(default)]
23    pub gpu_count: Option<i32>,
24
25    /// VRAM per GPU in GB.
26    #[serde(default)]
27    pub vram_gb: Option<i32>,
28
29    /// CPU cores.
30    #[serde(default)]
31    pub vcpus: Option<i32>,
32
33    /// RAM in GB.
34    #[serde(default)]
35    pub ram_gb: Option<i32>,
36
37    /// Price per hour in USD.
38    #[serde(default)]
39    pub price_per_hour_usd: Option<f64>,
40
41    /// Available zones.
42    #[serde(default)]
43    pub zones: Option<Vec<String>>,
44}
45
46/// Response from listing compute templates.
47#[derive(Debug, Clone, Deserialize)]
48pub struct TemplatesResponse {
49    /// Available compute templates.
50    pub templates: Vec<ComputeTemplate>,
51}
52
53/// Request body for provisioning a compute instance.
54#[derive(Debug, Clone, Serialize, Default)]
55pub struct ProvisionRequest {
56    /// Template ID to provision.
57    pub template: String,
58
59    /// Preferred zone (e.g. "us-central1-a").
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub zone: Option<String>,
62
63    /// Use spot/preemptible pricing.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub spot: Option<bool>,
66
67    /// Auto-teardown after N minutes of inactivity.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub auto_teardown_minutes: Option<i32>,
70
71    /// SSH public key for access.
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub ssh_public_key: Option<String>,
74}
75
76/// Response from provisioning a compute instance.
77#[derive(Debug, Clone, Deserialize)]
78pub struct ProvisionResponse {
79    /// Instance identifier.
80    pub instance_id: String,
81
82    /// Current instance status.
83    pub status: String,
84
85    /// Template that was provisioned.
86    #[serde(default)]
87    pub template: Option<String>,
88
89    /// Zone the instance was placed in.
90    #[serde(default)]
91    pub zone: Option<String>,
92
93    /// SSH connection address.
94    #[serde(default)]
95    pub ssh_address: Option<String>,
96
97    /// Estimated price per hour.
98    #[serde(default)]
99    pub price_per_hour_usd: Option<f64>,
100}
101
102/// A running compute instance.
103#[derive(Debug, Clone, Deserialize)]
104pub struct ComputeInstance {
105    /// Instance identifier.
106    pub id: String,
107
108    /// Current status (e.g. "running", "provisioning", "stopped").
109    pub status: String,
110
111    /// Template used.
112    #[serde(default)]
113    pub template: Option<String>,
114
115    /// Zone.
116    #[serde(default)]
117    pub zone: Option<String>,
118
119    /// SSH connection address.
120    #[serde(default)]
121    pub ssh_address: Option<String>,
122
123    /// Creation timestamp.
124    #[serde(default)]
125    pub created_at: Option<String>,
126
127    /// Price per hour.
128    #[serde(default)]
129    pub price_per_hour_usd: Option<f64>,
130
131    /// Auto-teardown setting in minutes.
132    #[serde(default)]
133    pub auto_teardown_minutes: Option<i32>,
134}
135
136/// Detailed compute instance info with GPU, cost, and uptime details.
137#[derive(Debug, Clone, Deserialize)]
138pub struct ComputeInstanceInfo {
139    /// Unique instance identifier.
140    pub instance_id: String,
141
142    /// Template that was used.
143    pub template: String,
144
145    /// Current instance status ("provisioning", "running", "stopping", "terminated", "failed").
146    pub status: String,
147
148    /// Live GCE instance status.
149    #[serde(default)]
150    pub gcp_status: Option<String>,
151
152    /// GCP zone.
153    pub zone: String,
154
155    /// GCE machine type.
156    #[serde(default)]
157    pub machine_type: Option<String>,
158
159    /// Public IP address (available once running).
160    #[serde(default)]
161    pub external_ip: Option<String>,
162
163    /// GPU accelerator type.
164    #[serde(default)]
165    pub gpu_type: Option<String>,
166
167    /// Number of GPUs.
168    #[serde(default)]
169    pub gpu_count: Option<i32>,
170
171    /// Whether this is a spot/preemptible instance.
172    #[serde(default)]
173    pub spot: bool,
174
175    /// Hourly rate in USD.
176    #[serde(default)]
177    pub hourly_usd: f64,
178
179    /// Total cost so far in USD.
180    #[serde(default)]
181    pub cost_usd: f64,
182
183    /// Total uptime in minutes.
184    #[serde(default)]
185    pub uptime_minutes: i32,
186
187    /// Inactivity timeout in minutes.
188    #[serde(default)]
189    pub auto_teardown_minutes: i32,
190
191    /// SSH username for the instance.
192    #[serde(default)]
193    pub ssh_username: Option<String>,
194
195    /// ISO 8601 timestamp of last activity.
196    #[serde(default)]
197    pub last_active_at: Option<String>,
198
199    /// ISO 8601 creation timestamp.
200    #[serde(default)]
201    pub created_at: Option<String>,
202
203    /// ISO 8601 termination timestamp (if terminated).
204    #[serde(default)]
205    pub terminated_at: Option<String>,
206
207    /// Error message if the instance failed.
208    #[serde(default)]
209    pub error_message: Option<String>,
210}
211
212/// Response from listing compute instances.
213#[derive(Debug, Clone, Deserialize)]
214pub struct InstancesResponse {
215    /// Running compute instances.
216    pub instances: Vec<ComputeInstance>,
217}
218
219/// Response from getting a single compute instance.
220#[derive(Debug, Clone, Deserialize)]
221pub struct InstanceResponse {
222    /// The compute instance details.
223    pub instance: ComputeInstance,
224}
225
226/// Response from deleting a compute instance.
227#[derive(Debug, Clone, Deserialize)]
228pub struct DeleteResponse {
229    /// Status message.
230    pub status: String,
231
232    /// Instance that was deleted.
233    #[serde(default)]
234    pub instance_id: Option<String>,
235}
236
237/// Request body for adding an SSH key to an instance.
238#[derive(Debug, Clone, Serialize, Default)]
239pub struct SSHKeyRequest {
240    /// SSH public key to add.
241    pub ssh_public_key: String,
242}
243
244/// Request for querying compute billing from BigQuery.
245#[derive(Debug, Clone, Serialize, Default)]
246pub struct BillingRequest {
247    /// Filter by instance ID.
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub instance_id: Option<String>,
250
251    /// Start date for billing period (ISO 8601).
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub start_date: Option<String>,
254
255    /// End date for billing period (ISO 8601).
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub end_date: Option<String>,
258}
259
260/// A single billing line item from BigQuery.
261#[derive(Debug, Clone, Deserialize)]
262pub struct BillingEntry {
263    /// Instance identifier.
264    pub instance_id: String,
265
266    /// Instance name.
267    #[serde(default)]
268    pub instance_name: Option<String>,
269
270    /// Total cost in USD.
271    pub cost_usd: f64,
272
273    /// Usage duration in hours.
274    #[serde(default)]
275    pub usage_hours: Option<f64>,
276
277    /// SKU description (e.g. "N1 Predefined Instance Core").
278    #[serde(default)]
279    pub sku_description: Option<String>,
280
281    /// Billing period start.
282    #[serde(default)]
283    pub start_time: Option<String>,
284
285    /// Billing period end.
286    #[serde(default)]
287    pub end_time: Option<String>,
288}
289
290/// Response from billing query.
291#[derive(Debug, Clone, Deserialize)]
292pub struct BillingResponse {
293    /// Individual billing entries.
294    pub entries: Vec<BillingEntry>,
295
296    /// Total cost across all entries.
297    pub total_cost_usd: f64,
298}
299
300impl Client {
301    /// Lists available compute templates (GPU configurations and pricing).
302    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    /// Provisions a new GPU compute instance.
310    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    /// Lists all compute instances for the account.
318    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    /// Gets details for a specific compute instance.
326    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    /// Deletes (tears down) a compute instance.
333    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    /// Adds an SSH public key to a running compute instance.
340    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    /// Sends a keepalive to prevent auto-teardown of a compute instance.
349    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    /// Queries compute billing from BigQuery via the QAI backend.
358    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}