use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModelFamily {
OpenAiGpt,
AnthropicClaude,
GoogleGemini,
XaiGrok,
DeepSeek,
CohereCommand,
Llama,
Qwen,
Mistral,
Other(String),
}
impl ModelFamily {
pub fn from_model_name(name: &str) -> Self {
let n = name.to_ascii_lowercase();
if n.contains("claude") {
ModelFamily::AnthropicClaude
} else if n.contains("gemini") || n.contains("gemma") {
ModelFamily::GoogleGemini
} else if n.contains("grok") {
ModelFamily::XaiGrok
} else if n.contains("deepseek") {
ModelFamily::DeepSeek
} else if n.contains("command") && !n.contains("gpt") {
ModelFamily::CohereCommand
} else if n.contains("llama") {
ModelFamily::Llama
} else if n.contains("qwen") {
ModelFamily::Qwen
} else if n.contains("mistral") || n.contains("mixtral") || n.contains("codestral") {
ModelFamily::Mistral
} else if n.contains("gpt") || n.starts_with("o1") || n.starts_with("o3") {
ModelFamily::OpenAiGpt
} else {
ModelFamily::Other(name.to_string())
}
}
pub fn is_distinct_from(&self, other: &ModelFamily) -> bool {
self != other
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classifies_by_model_name_not_host() {
assert_eq!(
ModelFamily::from_model_name("llama-3.3-70b-versatile"),
ModelFamily::Llama
);
assert_eq!(
ModelFamily::from_model_name("claude-opus-5"),
ModelFamily::AnthropicClaude
);
assert_eq!(
ModelFamily::from_model_name("gemini-3.5-flash"),
ModelFamily::GoogleGemini
);
assert_eq!(
ModelFamily::from_model_name("gpt-5.5"),
ModelFamily::OpenAiGpt
);
assert_eq!(
ModelFamily::from_model_name("qwen2.5-coder"),
ModelFamily::Qwen
);
}
#[test]
fn unknown_names_stay_other_instead_of_being_guessed() {
assert_eq!(
ModelFamily::from_model_name("sonnetina-9000"),
ModelFamily::Other("sonnetina-9000".to_string())
);
}
#[test]
fn same_lineage_on_two_hosts_is_not_distinct() {
let groq = ModelFamily::from_model_name("llama-3.3-70b-versatile");
let ollama = ModelFamily::from_model_name("llama3.3:70b");
assert!(!groq.is_distinct_from(&ollama));
}
}