openai_protocol/
model_card.rs1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use super::{
11 model_type::{Endpoint, ModelType},
12 worker::ProviderType,
13};
14
15fn is_zero(n: &u32) -> bool {
16 *n == 0
17}
18
19fn default_model_type() -> ModelType {
20 ModelType::LLM
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ModelCard {
43 pub id: String,
46
47 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub display_name: Option<String>,
50
51 #[serde(default, skip_serializing_if = "Vec::is_empty")]
53 pub aliases: Vec<String>,
54
55 #[serde(default = "default_model_type")]
58 pub model_type: ModelType,
59
60 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub hf_model_type: Option<String>,
63
64 #[serde(default, skip_serializing_if = "Vec::is_empty")]
66 pub architectures: Vec<String>,
67
68 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub provider: Option<ProviderType>,
72
73 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub context_length: Option<u32>,
76
77 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub tokenizer_path: Option<String>,
81
82 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub chat_template: Option<String>,
85
86 #[serde(default, skip_serializing_if = "Option::is_none")]
88 pub reasoning_parser: Option<String>,
89
90 #[serde(default, skip_serializing_if = "Option::is_none")]
92 pub tool_parser: Option<String>,
93
94 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub metadata: Option<serde_json::Value>,
97
98 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
102 pub id2label: HashMap<u32, String>,
103
104 #[serde(default, skip_serializing_if = "is_zero")]
106 pub num_labels: u32,
107}
108
109impl ModelCard {
110 pub fn new(id: impl Into<String>) -> Self {
114 Self {
115 id: id.into(),
116 display_name: None,
117 aliases: Vec::new(),
118 model_type: ModelType::LLM,
119 hf_model_type: None,
120 architectures: Vec::new(),
121 provider: None,
122 context_length: None,
123 tokenizer_path: None,
124 chat_template: None,
125 reasoning_parser: None,
126 tool_parser: None,
127 metadata: None,
128 id2label: HashMap::new(),
129 num_labels: 0,
130 }
131 }
132
133 pub fn with_display_name(mut self, name: impl Into<String>) -> Self {
137 self.display_name = Some(name.into());
138 self
139 }
140
141 pub fn with_alias(mut self, alias: impl Into<String>) -> Self {
143 self.aliases.push(alias.into());
144 self
145 }
146
147 pub fn with_aliases(mut self, aliases: impl IntoIterator<Item = impl Into<String>>) -> Self {
149 self.aliases.extend(aliases.into_iter().map(|a| a.into()));
150 self
151 }
152
153 pub fn with_model_type(mut self, model_type: ModelType) -> Self {
155 self.model_type = model_type;
156 self
157 }
158
159 pub fn with_hf_model_type(mut self, hf_model_type: impl Into<String>) -> Self {
161 self.hf_model_type = Some(hf_model_type.into());
162 self
163 }
164
165 pub fn with_architectures(mut self, architectures: Vec<String>) -> Self {
167 self.architectures = architectures;
168 self
169 }
170
171 pub fn with_provider(mut self, provider: ProviderType) -> Self {
173 self.provider = Some(provider);
174 self
175 }
176
177 pub fn with_context_length(mut self, length: u32) -> Self {
179 self.context_length = Some(length);
180 self
181 }
182
183 pub fn with_tokenizer_path(mut self, path: impl Into<String>) -> Self {
185 self.tokenizer_path = Some(path.into());
186 self
187 }
188
189 pub fn with_chat_template(mut self, template: impl Into<String>) -> Self {
191 self.chat_template = Some(template.into());
192 self
193 }
194
195 pub fn with_reasoning_parser(mut self, parser: impl Into<String>) -> Self {
197 self.reasoning_parser = Some(parser.into());
198 self
199 }
200
201 pub fn with_tool_parser(mut self, parser: impl Into<String>) -> Self {
203 self.tool_parser = Some(parser.into());
204 self
205 }
206
207 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
209 self.metadata = Some(metadata);
210 self
211 }
212
213 pub fn with_id2label(mut self, id2label: HashMap<u32, String>) -> Self {
215 self.num_labels = id2label.len() as u32;
216 self.id2label = id2label;
217 self
218 }
219
220 pub fn with_num_labels(mut self, num_labels: u32) -> Self {
222 self.num_labels = num_labels;
223 self
224 }
225
226 pub fn matches(&self, model_id: &str) -> bool {
230 self.id == model_id || self.aliases.iter().any(|a| a == model_id)
231 }
232
233 pub fn supports_endpoint(&self, endpoint: Endpoint) -> bool {
235 self.model_type.supports_endpoint(endpoint)
236 }
237
238 pub fn name(&self) -> &str {
240 self.display_name.as_deref().unwrap_or(&self.id)
241 }
242
243 #[inline]
245 pub fn is_native(&self) -> bool {
246 self.provider.is_none()
247 }
248
249 #[inline]
251 pub fn has_external_provider(&self) -> bool {
252 self.provider.is_some()
253 }
254
255 #[inline]
257 pub fn is_llm(&self) -> bool {
258 self.model_type.is_llm()
259 }
260
261 #[inline]
263 pub fn is_embedding_model(&self) -> bool {
264 self.model_type.is_embedding_model()
265 }
266
267 #[inline]
269 pub fn supports_vision(&self) -> bool {
270 self.model_type.supports_vision()
271 }
272
273 #[inline]
275 pub fn supports_tools(&self) -> bool {
276 self.model_type.supports_tools()
277 }
278
279 #[inline]
281 pub fn supports_reasoning(&self) -> bool {
282 self.model_type.supports_reasoning()
283 }
284
285 #[inline]
287 pub fn is_classifier(&self) -> bool {
288 self.num_labels > 0
289 }
290
291 pub fn get_label(&self, class_idx: u32) -> String {
293 self.id2label
294 .get(&class_idx)
295 .cloned()
296 .unwrap_or_else(|| format!("LABEL_{}", class_idx))
297 }
298}
299
300impl Default for ModelCard {
301 fn default() -> Self {
302 Self::new(super::UNKNOWN_MODEL_ID)
303 }
304}
305
306impl std::fmt::Display for ModelCard {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 write!(f, "{}", self.name())
309 }
310}