claude_codex/providers/kimi/translate/
model_allowlist.rs1use std::collections::HashMap;
2
3pub const KIMI_DEFAULT_MODEL: &str = "kimi-for-coding";
4
5static ALIAS_TARGETS: once_cell::sync::Lazy<HashMap<&'static str, &'static str>> =
6 once_cell::sync::Lazy::new(|| {
7 let mut m = HashMap::new();
8 m.insert("haiku", KIMI_DEFAULT_MODEL);
9 m.insert("claude-haiku-4-5", KIMI_DEFAULT_MODEL);
10 m.insert("claude-haiku-4-5-20251001", KIMI_DEFAULT_MODEL);
11 m.insert("sonnet", KIMI_DEFAULT_MODEL);
12 m.insert("claude-sonnet-4-6", KIMI_DEFAULT_MODEL);
13 m.insert("claude-sonnet-5", KIMI_DEFAULT_MODEL);
14 m.insert("opus", KIMI_DEFAULT_MODEL);
15 m.insert("claude-opus-4-7", KIMI_DEFAULT_MODEL);
16 m.insert("claude-opus-4-8", KIMI_DEFAULT_MODEL);
17 m.insert("claude-opus-5", KIMI_DEFAULT_MODEL);
18 m.insert("fable", KIMI_DEFAULT_MODEL);
19 m.insert("claude-fable-5", KIMI_DEFAULT_MODEL);
20 m.insert("kimi-for-coding", KIMI_DEFAULT_MODEL);
21 m.insert("kimi-k3", "k3");
22 m.insert("k3", "k3");
23 m
24 });
25
26const ALLOWED_MODELS: &[&str] = &["kimi-for-coding", "k3"];
27
28pub fn resolve_model(model: &str) -> String {
29 ALIAS_TARGETS
30 .get(model)
31 .copied()
32 .unwrap_or(KIMI_DEFAULT_MODEL)
33 .to_string()
34}
35
36pub fn is_k3(model: &str) -> bool {
37 model == "k3"
38}
39
40pub fn assert_allowed_model(model: &str) -> Result<(), ModelNotAllowedError> {
41 if ALLOWED_MODELS.contains(&model) {
42 Ok(())
43 } else {
44 Err(ModelNotAllowedError {
45 model: model.to_string(),
46 })
47 }
48}
49
50#[derive(Debug)]
51pub struct ModelNotAllowedError {
52 pub model: String,
53}
54
55impl std::fmt::Display for ModelNotAllowedError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "Model not allowed: {}", self.model)
58 }
59}
60
61impl std::error::Error for ModelNotAllowedError {}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn resolve_haiku_to_default() {
69 assert_eq!(resolve_model("haiku"), KIMI_DEFAULT_MODEL);
70 }
71
72 #[test]
73 fn resolve_opus_4_8_to_default() {
74 assert_eq!(resolve_model("claude-opus-4-8"), KIMI_DEFAULT_MODEL);
75 }
76
77 #[test]
78 fn resolve_claude_5_aliases_to_default() {
79 for model in [
80 "claude-sonnet-5",
81 "claude-opus-5",
82 "fable",
83 "claude-fable-5",
84 ] {
85 assert_eq!(resolve_model(model), KIMI_DEFAULT_MODEL);
86 }
87 }
88
89 #[test]
90 fn resolve_unknown_to_default() {
91 assert_eq!(resolve_model("unknown-model"), KIMI_DEFAULT_MODEL);
92 }
93
94 #[test]
95 fn resolve_kimi_for_coding() {
96 assert_eq!(resolve_model("kimi-for-coding"), KIMI_DEFAULT_MODEL);
97 }
98
99 #[test]
100 fn resolve_kimi_k3_to_k3() {
101 assert_eq!(resolve_model("kimi-k3"), "k3");
102 }
103
104 #[test]
105 fn resolve_k3_to_k3() {
106 assert_eq!(resolve_model("k3"), "k3");
107 }
108
109 #[test]
110 fn k3_detected() {
111 assert!(is_k3("k3"));
112 assert!(!is_k3("kimi-for-coding"));
113 }
114
115 #[test]
116 fn assert_allowed_accepts_default() {
117 assert!(assert_allowed_model(KIMI_DEFAULT_MODEL).is_ok());
118 }
119
120 #[test]
121 fn assert_allowed_accepts_k3() {
122 assert!(assert_allowed_model("k3").is_ok());
123 }
124
125 #[test]
126 fn assert_allowed_rejects_other() {
127 assert!(assert_allowed_model("kimi-k2.6").is_err());
128 }
129}