Skip to main content

apollo/providers/
defaults.rs

1//! Default model per provider.
2//!
3//! A provider configured with no model has to land somewhere. Picking the
4//! wrong entry is a billing decision, not a cosmetic one, so these track the
5//! defaults published by NousResearch/hermes-agent — the first entry of each
6//! provider's curated list, which is what its
7//! `get_default_model_for_provider()` returns.
8//!
9//! Only providers apollo can actually construct appear here. An unknown
10//! provider yields `None` and the caller keeps whatever the config already
11//! had, rather than being sent to a model that may not exist on it.
12
13/// Provider name (and common aliases) to the model it defaults to.
14const DEFAULTS: &[(&str, &str)] = &[
15    ("chatgpt", "gpt-5.5"),
16    ("openai", "gpt-5.4"),
17    ("copilot", "gpt-5.4"),
18    ("github-copilot", "gpt-5.4"),
19    ("xai", "grok-build-0.1"),
20    ("grok", "grok-build-0.1"),
21    ("gemini", "gemini-3.1-pro-preview"),
22    ("deepseek", "deepseek-v4-pro"),
23    ("moonshot", "kimi-k3"),
24    ("kimi", "kimi-k3"),
25    ("minimax", "MiniMax-M3"),
26    ("huggingface", "moonshotai/Kimi-K2.5"),
27    // Metered aggregator: hermes routes this one through its catalog-labeled
28    // default rather than the head of the list, because that list is ordered
29    // most-capable-first and defaulting to the head would silently pick the
30    // most expensive model.
31    ("openrouter", "z-ai/glm-5.2"),
32];
33
34/// The model a provider falls back to when none was configured.
35pub fn default_model_for_provider(provider: &str) -> Option<&'static str> {
36    let key = provider.trim();
37    DEFAULTS
38        .iter()
39        .find(|(name, _)| name.eq_ignore_ascii_case(key))
40        .map(|(_, model)| *model)
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn oauth_providers_get_their_own_defaults() {
49        assert_eq!(default_model_for_provider("chatgpt"), Some("gpt-5.5"));
50        assert_eq!(default_model_for_provider("xai"), Some("grok-build-0.1"));
51    }
52
53    #[test]
54    fn aliases_resolve_to_the_same_model() {
55        for (a, b) in [("grok", "xai"), ("kimi", "moonshot")] {
56            assert_eq!(
57                default_model_for_provider(a),
58                default_model_for_provider(b),
59                "{a} and {b} should agree"
60            );
61        }
62    }
63
64    #[test]
65    fn lookup_ignores_case_and_padding() {
66        assert_eq!(default_model_for_provider("  ChatGpt "), Some("gpt-5.5"));
67    }
68
69    #[test]
70    fn an_unknown_provider_picks_nothing() {
71        assert_eq!(default_model_for_provider("not-a-provider"), None);
72        assert_eq!(default_model_for_provider(""), None);
73    }
74}