entrenar/ecosystem/batuta/
client.rs1use std::time::Duration;
4
5use super::error::BatutaError;
6use super::pricing::{FallbackPricing, GpuPricing};
7use super::queue::QueueState;
8
9#[derive(Debug, Clone)]
11pub struct BatutaClient {
12 base_url: Option<String>,
14 fallback: FallbackPricing,
16 timeout: Duration,
18 service_available: bool,
20}
21
22impl Default for BatutaClient {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl BatutaClient {
29 pub fn new() -> Self {
31 Self {
32 base_url: None,
33 fallback: FallbackPricing::new(),
34 timeout: Duration::from_secs(5),
35 service_available: false,
36 }
37 }
38
39 pub fn with_url(url: impl Into<String>) -> Self {
41 Self {
42 base_url: Some(url.into()),
43 fallback: FallbackPricing::new(),
44 timeout: Duration::from_secs(5),
45 service_available: true,
46 }
47 }
48
49 pub fn with_timeout(mut self, timeout: Duration) -> Self {
51 self.timeout = timeout;
52 self
53 }
54
55 pub fn with_fallback(mut self, fallback: FallbackPricing) -> Self {
57 self.fallback = fallback;
58 self
59 }
60
61 pub fn is_connected(&self) -> bool {
63 self.base_url.is_some() && self.service_available
64 }
65
66 pub fn get_hourly_rate(&self, gpu_type: &str) -> Result<GpuPricing, BatutaError> {
70 if let Some(_url) = &self.base_url {
72 }
76
77 self.fallback
79 .get_rate(gpu_type)
80 .cloned()
81 .ok_or_else(|| BatutaError::UnknownGpuType(gpu_type.to_string()))
82 }
83
84 pub fn get_queue_depth(&self, gpu_type: &str) -> Result<QueueState, BatutaError> {
89 if self.fallback.get_rate(gpu_type).is_none() {
91 return Err(BatutaError::UnknownGpuType(gpu_type.to_string()));
92 }
93
94 if let Some(_url) = &self.base_url {
96 }
99
100 Ok(QueueState::new(0, 4, 4))
102 }
103
104 pub fn get_status(&self, gpu_type: &str) -> Result<(GpuPricing, QueueState), BatutaError> {
106 let pricing = self.get_hourly_rate(gpu_type)?;
107 let queue = self.get_queue_depth(gpu_type)?;
108 Ok((pricing, queue))
109 }
110
111 pub fn estimate_cost(&self, gpu_type: &str, hours: f64) -> Result<f64, BatutaError> {
113 let pricing = self.get_hourly_rate(gpu_type)?;
114 Ok(pricing.hourly_rate * hours)
115 }
116
117 pub fn cheapest_gpu(&self, min_memory_gb: u32) -> Option<&GpuPricing> {
119 self.fallback.all_pricing().iter().filter(|p| p.memory_gb >= min_memory_gb).min_by(
120 |a, b| a.hourly_rate.partial_cmp(&b.hourly_rate).unwrap_or(std::cmp::Ordering::Equal),
121 )
122 }
123}