Skip to main content

mesh_llm_native_runtime/
flavor.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use std::{fmt, str::FromStr};
3
4#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
5pub enum NativeRuntimeBackendKind {
6    Cpu,
7    Metal,
8    Cuda,
9    Rocm,
10    Vulkan,
11    Other(String),
12}
13
14pub type NativeRuntimeFlavor = NativeRuntimeBackendKind;
15
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct NativeRuntimeFlavorParseError {
18    value: String,
19}
20
21#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
22pub struct NativeRuntimeBackend {
23    pub kind: NativeRuntimeBackendKind,
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub cuda: Option<CudaRuntimeRequirements>,
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub rocm: Option<RocmRuntimeRequirements>,
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub vulkan: Option<VulkanRuntimeRequirements>,
30}
31
32#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
33pub struct CudaRuntimeRequirements {
34    pub toolkit_major: u32,
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    pub min_driver: Option<String>,
37    #[serde(default, skip_serializing_if = "Vec::is_empty")]
38    pub gpu_arches: Vec<String>,
39}
40
41#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
42pub struct RocmRuntimeRequirements {
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub version: Option<String>,
45    #[serde(default, skip_serializing_if = "Vec::is_empty")]
46    pub gpu_arches: Vec<String>,
47}
48
49#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
50pub struct VulkanRuntimeRequirements {
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub min_api_version: Option<String>,
53}
54
55impl fmt::Display for NativeRuntimeFlavorParseError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "invalid native runtime backend '{}'", self.value)
58    }
59}
60
61impl std::error::Error for NativeRuntimeFlavorParseError {}
62
63impl NativeRuntimeBackendKind {
64    pub fn as_str(&self) -> &str {
65        match self {
66            Self::Cpu => "cpu",
67            Self::Metal => "metal",
68            Self::Cuda => "cuda",
69            Self::Rocm => "rocm",
70            Self::Vulkan => "vulkan",
71            Self::Other(value) => value.as_str(),
72        }
73    }
74
75    pub fn default_rank(&self) -> i64 {
76        match self {
77            Self::Cuda => 650,
78            Self::Rocm => 600,
79            Self::Metal => 600,
80            Self::Vulkan => 350,
81            Self::Cpu => 100,
82            Self::Other(_) => 0,
83        }
84    }
85}
86
87impl NativeRuntimeBackend {
88    pub fn cpu() -> Self {
89        Self {
90            kind: NativeRuntimeBackendKind::Cpu,
91            cuda: None,
92            rocm: None,
93            vulkan: None,
94        }
95    }
96
97    pub fn metal() -> Self {
98        Self {
99            kind: NativeRuntimeBackendKind::Metal,
100            cuda: None,
101            rocm: None,
102            vulkan: None,
103        }
104    }
105
106    pub fn cuda(toolkit_major: u32, gpu_arches: Vec<String>) -> Self {
107        Self {
108            kind: NativeRuntimeBackendKind::Cuda,
109            cuda: Some(CudaRuntimeRequirements {
110                toolkit_major,
111                min_driver: None,
112                gpu_arches,
113            }),
114            rocm: None,
115            vulkan: None,
116        }
117    }
118
119    pub fn rocm(gpu_arches: Vec<String>) -> Self {
120        Self {
121            kind: NativeRuntimeBackendKind::Rocm,
122            cuda: None,
123            rocm: Some(RocmRuntimeRequirements {
124                version: None,
125                gpu_arches,
126            }),
127            vulkan: None,
128        }
129    }
130
131    pub fn vulkan() -> Self {
132        Self {
133            kind: NativeRuntimeBackendKind::Vulkan,
134            cuda: None,
135            rocm: None,
136            vulkan: Some(VulkanRuntimeRequirements {
137                min_api_version: None,
138            }),
139        }
140    }
141}
142
143impl fmt::Display for NativeRuntimeBackendKind {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        f.write_str(self.as_str())
146    }
147}
148
149impl Serialize for NativeRuntimeBackendKind {
150    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
151    where
152        S: Serializer,
153    {
154        serializer.serialize_str(self.as_str())
155    }
156}
157
158impl<'de> Deserialize<'de> for NativeRuntimeBackendKind {
159    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160    where
161        D: Deserializer<'de>,
162    {
163        let value = String::deserialize(deserializer)?;
164        Ok(Self::from(value.as_str()))
165    }
166}
167
168impl FromStr for NativeRuntimeBackendKind {
169    type Err = NativeRuntimeFlavorParseError;
170
171    fn from_str(value: &str) -> Result<Self, Self::Err> {
172        let normalized = value.trim().to_ascii_lowercase();
173        if normalized.is_empty() {
174            return Err(NativeRuntimeFlavorParseError {
175                value: value.to_string(),
176            });
177        }
178        Ok(match normalized.as_str() {
179            "cpu" => Self::Cpu,
180            "metal" => Self::Metal,
181            "cuda" | "cuda-blackwell" | "blackwell" => Self::Cuda,
182            "rocm" | "hip" => Self::Rocm,
183            "vulkan" => Self::Vulkan,
184            _ => Self::Other(normalized),
185        })
186    }
187}
188
189impl From<&str> for NativeRuntimeBackendKind {
190    fn from(value: &str) -> Self {
191        value
192            .parse()
193            .unwrap_or_else(|_| Self::Other(value.trim().to_ascii_lowercase()))
194    }
195}