Skip to main content

mur_common/
model_resolve.rs

1//! First-run model-resolution decision tree (spec §7.3). Pure logic shared
2//! by the CLI (Plan 1b) and the Hub GUI wizard (Plan 2). Hardware detection
3//! and the actual pull/key-entry actions live in the surface layers; this
4//! module only decides the recommended default given a hint + hardware.
5
6use crate::muragent::manifest::ModelHint;
7
8/// Recipient hardware snapshot. `apple_silicon` and `ollama_present` inform
9/// which options a surface offers; the recommendation itself keys off RAM
10/// and the hint's `local_capable` flag.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Hardware {
13    pub total_ram_gb: u32,
14    pub apple_silicon: bool,
15    pub ollama_present: bool,
16}
17
18/// The highlighted default the wizard should pre-select. All escape hatches
19/// (local pull / paste key / endpoint) remain available regardless (§7.3).
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Recommendation {
22    Local,
23    Cloud,
24    CloudOrSmallerLocal,
25    NeutralMenu,
26}
27
28pub fn recommend(hint: Option<&ModelHint>, hw: &Hardware) -> Recommendation {
29    match hint {
30        None => Recommendation::NeutralMenu,
31        Some(h) if !h.local_capable => Recommendation::Cloud,
32        Some(h) if hw.total_ram_gb >= h.min_ram_gb => Recommendation::Local,
33        Some(_) => Recommendation::CloudOrSmallerLocal,
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use crate::muragent::manifest::ModelTier;
41
42    fn hint(local: bool, min_ram: u32) -> ModelHint {
43        ModelHint {
44            provider: "p".into(),
45            name: "n".into(),
46            tier: if local {
47                ModelTier::Small
48            } else {
49                ModelTier::Frontier
50            },
51            min_ram_gb: min_ram,
52            local_capable: local,
53        }
54    }
55    fn hw(ram: u32) -> Hardware {
56        Hardware {
57            total_ram_gb: ram,
58            apple_silicon: true,
59            ollama_present: true,
60        }
61    }
62
63    #[test]
64    fn no_hint_is_neutral() {
65        assert_eq!(recommend(None, &hw(16)), Recommendation::NeutralMenu);
66    }
67    #[test]
68    fn frontier_is_cloud() {
69        assert_eq!(
70            recommend(Some(&hint(false, 0)), &hw(16)),
71            Recommendation::Cloud
72        );
73    }
74    #[test]
75    fn local_with_enough_ram_is_local() {
76        assert_eq!(
77            recommend(Some(&hint(true, 8)), &hw(16)),
78            Recommendation::Local
79        );
80    }
81    #[test]
82    fn local_without_ram_is_cloud_or_smaller() {
83        assert_eq!(
84            recommend(Some(&hint(true, 16)), &hw(8)),
85            Recommendation::CloudOrSmallerLocal
86        );
87    }
88}