Skip to main content

capo_agent/
model.rs

1//! `ModelId` — a thin newtype over the provider's model-name string,
2//! used by `App::switch_model` and (Phase D2) the `/model` picker.
3
4/// An LLM model identifier, e.g. `claude-sonnet-4-6`. Just a string;
5/// the newtype keeps `switch_model` call sites self-documenting.
6#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
7pub struct ModelId(pub String);
8
9impl ModelId {
10    pub fn as_str(&self) -> &str {
11        &self.0
12    }
13}
14
15impl From<&str> for ModelId {
16    fn from(s: &str) -> Self {
17        Self(s.to_string())
18    }
19}
20
21impl From<String> for ModelId {
22    fn from(s: String) -> Self {
23        Self(s)
24    }
25}
26
27impl std::fmt::Display for ModelId {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.write_str(&self.0)
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn round_trips_through_str_and_string() {
39        assert_eq!(
40            ModelId::from("claude-sonnet-4-6").as_str(),
41            "claude-sonnet-4-6"
42        );
43        assert_eq!(ModelId::from("x".to_string()).to_string(), "x");
44    }
45
46    #[test]
47    fn equality_is_by_value() {
48        assert_eq!(ModelId::from("a"), ModelId::from("a"));
49        assert_ne!(ModelId::from("a"), ModelId::from("b"));
50    }
51}