1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
14pub enum Provider {
15 OpenAi,
17 Anthropic,
19 Cohere,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
27pub enum Model {
28 Gpt4o,
30 Gpt4,
32 Gpt35Turbo,
34 Claude3Opus,
36 Claude3Sonnet,
38 Claude3Haiku,
40 CommandRPlus,
42 CommandR,
44}
45
46impl Model {
47 pub fn infer_from_name(name: &str) -> Option<(Provider, Self)> {
61 let n = name.to_ascii_lowercase();
62 if n.contains("gpt-4o") || n.contains("gpt4o") {
64 Some((Provider::OpenAi, Model::Gpt4o))
65 } else if n.contains("gpt-3.5") || n.contains("gpt35") || n.contains("gpt-35") {
66 Some((Provider::OpenAi, Model::Gpt35Turbo))
67 } else if n.contains("gpt-4") || n.contains("gpt4") {
68 Some((Provider::OpenAi, Model::Gpt4))
69 } else if n.contains("opus") {
70 Some((Provider::Anthropic, Model::Claude3Opus))
71 } else if n.contains("sonnet") {
72 Some((Provider::Anthropic, Model::Claude3Sonnet))
73 } else if n.contains("haiku") {
74 Some((Provider::Anthropic, Model::Claude3Haiku))
75 } else if n.contains("command-r-plus") || n.contains("command-r+") {
76 Some((Provider::Cohere, Model::CommandRPlus))
77 } else if n.contains("command-r") {
78 Some((Provider::Cohere, Model::CommandR))
79 } else {
80 None
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn provider_variants_are_distinct() {
91 assert_eq!(Provider::OpenAi, Provider::OpenAi);
92 assert_ne!(Provider::OpenAi, Provider::Anthropic);
93 assert_ne!(Provider::OpenAi, Provider::Cohere);
94 assert_ne!(Provider::Anthropic, Provider::Cohere);
95 }
96
97 #[test]
98 fn model_variants_are_distinct() {
99 assert_eq!(Model::Gpt4o, Model::Gpt4o);
100 assert_ne!(Model::Gpt4o, Model::Gpt4);
101 assert_ne!(Model::Claude3Opus, Model::Claude3Haiku);
102 assert_ne!(Model::CommandRPlus, Model::CommandR);
103 }
104
105 #[test]
106 fn infer_openai_gpt4o_most_specific() {
107 assert_eq!(
108 Model::infer_from_name("gpt-4o-2024-08-06"),
109 Some((Provider::OpenAi, Model::Gpt4o))
110 );
111 assert_eq!(Model::infer_from_name("GPT4O"), Some((Provider::OpenAi, Model::Gpt4o)));
112 }
113
114 #[test]
115 fn infer_openai_gpt35_before_gpt4() {
116 assert_eq!(
117 Model::infer_from_name("gpt-3.5-turbo"),
118 Some((Provider::OpenAi, Model::Gpt35Turbo))
119 );
120 }
121
122 #[test]
123 fn infer_openai_gpt4() {
124 assert_eq!(Model::infer_from_name("gpt-4"), Some((Provider::OpenAi, Model::Gpt4)));
125 }
126
127 #[test]
128 fn infer_anthropic_family() {
129 assert_eq!(
130 Model::infer_from_name("claude-3-opus-20240229"),
131 Some((Provider::Anthropic, Model::Claude3Opus))
132 );
133 assert_eq!(
134 Model::infer_from_name("claude-3-5-sonnet"),
135 Some((Provider::Anthropic, Model::Claude3Sonnet))
136 );
137 assert_eq!(
138 Model::infer_from_name("claude-3-haiku"),
139 Some((Provider::Anthropic, Model::Claude3Haiku))
140 );
141 }
142
143 #[test]
144 fn infer_cohere_command_r_plus_before_command_r() {
145 assert_eq!(
146 Model::infer_from_name("command-r-plus"),
147 Some((Provider::Cohere, Model::CommandRPlus))
148 );
149 assert_eq!(
150 Model::infer_from_name("command-r"),
151 Some((Provider::Cohere, Model::CommandR))
152 );
153 }
154
155 #[test]
156 fn infer_unknown_returns_none() {
157 assert_eq!(Model::infer_from_name("mystery-model-v9"), None);
158 assert_eq!(Model::infer_from_name(""), None);
159 }
160}