use serde::{Deserialize, Serialize, Serializer};
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Model {
Known(KnownModel),
Custom(String),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum KnownModel {
ClaudeOpus4520251101,
ClaudeOpus45,
ClaudeOpus46,
Claude37SonnetLatest,
Claude37Sonnet20250219,
ClaudeHaiku45,
ClaudeHaiku4520251001,
ClaudeSonnet420250514,
ClaudeSonnet40,
Claude4Sonnet20250514,
ClaudeSonnet45,
ClaudeSonnet4520250929,
ClaudeOpus40,
ClaudeOpus420250514,
Claude4Opus20250514,
ClaudeOpus4120250805,
ClaudeOpus47,
Claude3OpusLatest,
Claude3Opus20240229,
Claude3Haiku20240307,
}
impl fmt::Display for Model {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Model::Known(known_model) => write!(f, "{known_model}"),
Model::Custom(custom) => write!(f, "{custom}"),
}
}
}
impl fmt::Display for KnownModel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
KnownModel::ClaudeOpus4520251101 => write!(f, "claude-opus-4-5-20251101"),
KnownModel::ClaudeOpus45 => write!(f, "claude-opus-4-5"),
KnownModel::ClaudeOpus46 => write!(f, "claude-opus-4-6"),
KnownModel::Claude37SonnetLatest => write!(f, "claude-3-7-sonnet-latest"),
KnownModel::Claude37Sonnet20250219 => write!(f, "claude-3-7-sonnet-20250219"),
KnownModel::ClaudeHaiku45 => write!(f, "claude-haiku-4-5"),
KnownModel::ClaudeHaiku4520251001 => write!(f, "claude-haiku-4-5-20251001"),
KnownModel::ClaudeSonnet420250514 => write!(f, "claude-sonnet-4-20250514"),
KnownModel::ClaudeSonnet40 => write!(f, "claude-sonnet-4-0"),
KnownModel::Claude4Sonnet20250514 => write!(f, "claude-4-sonnet-20250514"),
KnownModel::ClaudeSonnet45 => write!(f, "claude-sonnet-4-5"),
KnownModel::ClaudeSonnet4520250929 => write!(f, "claude-sonnet-4-5-20250929"),
KnownModel::ClaudeOpus40 => write!(f, "claude-opus-4-0"),
KnownModel::ClaudeOpus420250514 => write!(f, "claude-opus-4-20250514"),
KnownModel::Claude4Opus20250514 => write!(f, "claude-4-opus-20250514"),
KnownModel::ClaudeOpus4120250805 => write!(f, "claude-opus-4-1-20250805"),
KnownModel::ClaudeOpus47 => write!(f, "claude-opus-4-7"),
KnownModel::Claude3OpusLatest => write!(f, "claude-3-opus-latest"),
KnownModel::Claude3Opus20240229 => write!(f, "claude-3-opus-20240229"),
KnownModel::Claude3Haiku20240307 => write!(f, "claude-3-haiku-20240307"),
}
}
}
impl Serialize for Model {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Model {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"claude-opus-4-5-20251101" => Ok(Model::Known(KnownModel::ClaudeOpus4520251101)),
"claude-opus-4-5" => Ok(Model::Known(KnownModel::ClaudeOpus45)),
"claude-opus-4-6" => Ok(Model::Known(KnownModel::ClaudeOpus46)),
"claude-3-7-sonnet-latest" => Ok(Model::Known(KnownModel::Claude37SonnetLatest)),
"claude-3-7-sonnet-20250219" => Ok(Model::Known(KnownModel::Claude37Sonnet20250219)),
"claude-haiku-4-5" => Ok(Model::Known(KnownModel::ClaudeHaiku45)),
"claude-haiku-4-5-20251001" => Ok(Model::Known(KnownModel::ClaudeHaiku4520251001)),
"claude-sonnet-4-20250514" => Ok(Model::Known(KnownModel::ClaudeSonnet420250514)),
"claude-sonnet-4-0" => Ok(Model::Known(KnownModel::ClaudeSonnet40)),
"claude-4-sonnet-20250514" => Ok(Model::Known(KnownModel::Claude4Sonnet20250514)),
"claude-sonnet-4-5" => Ok(Model::Known(KnownModel::ClaudeSonnet45)),
"claude-sonnet-4-5-20250929" => Ok(Model::Known(KnownModel::ClaudeSonnet4520250929)),
"claude-opus-4-0" => Ok(Model::Known(KnownModel::ClaudeOpus40)),
"claude-opus-4-20250514" => Ok(Model::Known(KnownModel::ClaudeOpus420250514)),
"claude-4-opus-20250514" => Ok(Model::Known(KnownModel::Claude4Opus20250514)),
"claude-opus-4-1-20250805" => Ok(Model::Known(KnownModel::ClaudeOpus4120250805)),
"claude-opus-4-7" => Ok(Model::Known(KnownModel::ClaudeOpus47)),
"claude-3-opus-latest" => Ok(Model::Known(KnownModel::Claude3OpusLatest)),
"claude-3-opus-20240229" => Ok(Model::Known(KnownModel::Claude3Opus20240229)),
"claude-3-haiku-20240307" => Ok(Model::Known(KnownModel::Claude3Haiku20240307)),
_ => Ok(Model::Custom(s)),
}
}
}
impl From<KnownModel> for Model {
fn from(model: KnownModel) -> Self {
Model::Known(model)
}
}
impl FromStr for KnownModel {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"claude-opus-4-5-20251101" => Ok(KnownModel::ClaudeOpus4520251101),
"claude-opus-4-5" => Ok(KnownModel::ClaudeOpus45),
"claude-opus-4-6" => Ok(KnownModel::ClaudeOpus46),
"claude-3-7-sonnet-latest" => Ok(KnownModel::Claude37SonnetLatest),
"claude-3-7-sonnet-20250219" => Ok(KnownModel::Claude37Sonnet20250219),
"claude-haiku-4-5" => Ok(KnownModel::ClaudeHaiku45),
"claude-haiku-4-5-20251001" => Ok(KnownModel::ClaudeHaiku4520251001),
"claude-sonnet-4-20250514" => Ok(KnownModel::ClaudeSonnet420250514),
"claude-sonnet-4-0" => Ok(KnownModel::ClaudeSonnet40),
"claude-4-sonnet-20250514" => Ok(KnownModel::Claude4Sonnet20250514),
"claude-sonnet-4-5" => Ok(KnownModel::ClaudeSonnet45),
"claude-sonnet-4-5-20250929" => Ok(KnownModel::ClaudeSonnet4520250929),
"claude-opus-4-0" => Ok(KnownModel::ClaudeOpus40),
"claude-opus-4-20250514" => Ok(KnownModel::ClaudeOpus420250514),
"claude-4-opus-20250514" => Ok(KnownModel::Claude4Opus20250514),
"claude-opus-4-1-20250805" => Ok(KnownModel::ClaudeOpus4120250805),
"claude-opus-4-7" => Ok(KnownModel::ClaudeOpus47),
"claude-3-opus-latest" => Ok(KnownModel::Claude3OpusLatest),
"claude-3-opus-20240229" => Ok(KnownModel::Claude3Opus20240229),
"claude-3-haiku-20240307" => Ok(KnownModel::Claude3Haiku20240307),
_ => Err(()),
}
}
}
impl FromStr for Model {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match KnownModel::from_str(s) {
Ok(known_model) => Ok(Model::Known(known_model)),
Err(_) => Ok(Model::Custom(s.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_model_serialization() {
let model = Model::Known(KnownModel::Claude37SonnetLatest);
let json = serde_json::to_string(&model).unwrap();
assert_eq!(json, r#""claude-3-7-sonnet-latest""#);
let model = Model::Known(KnownModel::ClaudeSonnet4520250929);
let json = serde_json::to_string(&model).unwrap();
assert_eq!(json, r#""claude-sonnet-4-5-20250929""#);
}
#[test]
fn custom_model_serialization() {
let model = Model::Custom("claude-4-custom".to_string());
let json = serde_json::to_string(&model).unwrap();
assert_eq!(json, r#""claude-4-custom""#);
}
#[test]
fn model_deserialization() {
let json = r#""claude-3-7-sonnet-latest""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::Claude37SonnetLatest));
let json = r#""claude-4-custom""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Custom("claude-4-custom".to_string()));
}
#[test]
fn claude_4_models() {
let model = Model::Known(KnownModel::ClaudeSonnet420250514);
assert_eq!(model.to_string(), "claude-sonnet-4-20250514");
let json = serde_json::to_string(&model).unwrap();
assert_eq!(json, r#""claude-sonnet-4-20250514""#);
let model = Model::Known(KnownModel::Claude4Sonnet20250514);
assert_eq!(model.to_string(), "claude-4-sonnet-20250514");
let model = Model::Known(KnownModel::ClaudeOpus40);
assert_eq!(model.to_string(), "claude-opus-4-0");
let model = Model::Known(KnownModel::Claude4Opus20250514);
assert_eq!(model.to_string(), "claude-4-opus-20250514");
let model = Model::Known(KnownModel::ClaudeOpus4120250805);
assert_eq!(model.to_string(), "claude-opus-4-1-20250805");
let model = Model::Known(KnownModel::ClaudeOpus47);
assert_eq!(model.to_string(), "claude-opus-4-7");
let json = r#""claude-sonnet-4-20250514""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeSonnet420250514));
let json = r#""claude-4-opus-20250514""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::Claude4Opus20250514));
let json = r#""claude-opus-4-1-20250805""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeOpus4120250805));
let json = r#""claude-opus-4-7""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeOpus47));
}
#[test]
fn claude_45_models() {
let model = Model::Known(KnownModel::ClaudeOpus4520251101);
assert_eq!(model.to_string(), "claude-opus-4-5-20251101");
let json = serde_json::to_string(&model).unwrap();
assert_eq!(json, r#""claude-opus-4-5-20251101""#);
let model = Model::Known(KnownModel::ClaudeOpus45);
assert_eq!(model.to_string(), "claude-opus-4-5");
let model = Model::Known(KnownModel::ClaudeHaiku45);
assert_eq!(model.to_string(), "claude-haiku-4-5");
let model = Model::Known(KnownModel::ClaudeHaiku4520251001);
assert_eq!(model.to_string(), "claude-haiku-4-5-20251001");
let model = Model::Known(KnownModel::ClaudeSonnet45);
assert_eq!(model.to_string(), "claude-sonnet-4-5");
let model = Model::Known(KnownModel::ClaudeSonnet4520250929);
assert_eq!(model.to_string(), "claude-sonnet-4-5-20250929");
let json = r#""claude-opus-4-5-20251101""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeOpus4520251101));
let json = r#""claude-opus-4-5""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeOpus45));
let json = r#""claude-haiku-4-5""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeHaiku45));
let json = r#""claude-haiku-4-5-20251001""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeHaiku4520251001));
let json = r#""claude-sonnet-4-5""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeSonnet45));
let json = r#""claude-sonnet-4-5-20250929""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeSonnet4520250929));
}
#[test]
fn claude_46_models() {
let model = Model::Known(KnownModel::ClaudeOpus46);
assert_eq!(model.to_string(), "claude-opus-4-6");
let json = serde_json::to_string(&model).unwrap();
assert_eq!(json, r#""claude-opus-4-6""#);
let json = r#""claude-opus-4-6""#;
let model: Model = serde_json::from_str(json).unwrap();
assert_eq!(model, Model::Known(KnownModel::ClaudeOpus46));
}
#[test]
fn display() {
let model = Model::Known(KnownModel::Claude37SonnetLatest);
assert_eq!(model.to_string(), "claude-3-7-sonnet-latest");
let model = Model::Custom("claude-4-custom".to_string());
assert_eq!(model.to_string(), "claude-4-custom");
}
}