Skip to main content

llm_manager/config/
model_config.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use serde::{Deserialize, Serialize};
5
6use crate::config::ModelOverride;
7use crate::config::config_base_dir;
8use crate::config::store::{load_all_from_dir, move_to_unused, save_yaml};
9
10/// Directory for per-model YAML configs.
11pub fn models_config_dir() -> PathBuf {
12    config_base_dir().join("llm-manager").join("models")
13}
14
15/// Directory for unused (deleted) model configs.
16pub fn unused_config_dir() -> PathBuf {
17    config_base_dir().join("llm-manager").join("unused")
18}
19
20/// Convert a display_name (relative path) to a filesystem-safe key.
21/// Replaces path separators with "__" to create unique keys.
22/// E.g. "qwen/model-v2" → "qwen__model-v2"
23pub fn key_from_display(display_name: &str) -> String {
24    display_name.replace(std::path::MAIN_SEPARATOR, "__")
25}
26
27/// Convert a filesystem-safe key back to a display_name.
28/// Reverses the key_from_display transformation.
29/// E.g. "qwen__model-v2" → "qwen/model-v2"
30pub fn display_from_key(key: &str) -> String {
31    key.replace("__", std::path::MAIN_SEPARATOR_STR)
32}
33
34/// Per-model configuration store.
35///
36/// Each model config is a YAML file stored in `~/.config/llm-manager/models/`.
37/// Files are named `<key>.yaml` where `key` is derived from the model's
38/// display_name (path relative to its model directory) with path separators
39/// replaced by "__".
40///
41/// Example: model at "models/qwen/model-v2.gguf" has display_name "qwen/model-v2"
42/// and config file "qwen__model-v2.yaml".
43///
44/// Deleting a model moves its config file to `~/.config/llm-manager/unused/`
45/// instead of removing it, allowing recovery.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct ModelConfigStore {
48    models_dir: PathBuf,
49    unused_dir: PathBuf,
50    cache: HashMap<String, ModelOverride>,
51}
52
53impl ModelConfigStore {
54    pub fn new() -> Self {
55        let models_dir = models_config_dir();
56        let unused_dir = unused_config_dir();
57        let cache = load_all_from_dir(&models_dir);
58
59        Self {
60            models_dir,
61            unused_dir,
62            cache,
63        }
64    }
65
66    /// Get the config for a model by its display_name.
67    pub fn get(&self, display_name: &str) -> Option<&ModelOverride> {
68        let key = key_from_display(display_name);
69        self.cache.get(&key)
70    }
71
72    /// Save (or update) a model config by its display_name.
73    pub fn save(&mut self, display_name: &str, config: &ModelOverride) {
74        let key = key_from_display(display_name);
75        save_yaml(&key, config, &self.models_dir, &self.unused_dir);
76        self.cache.insert(key, config.clone());
77    }
78
79    /// Delete a model config by its display_name.
80    pub fn delete(&mut self, display_name: &str) {
81        let key = key_from_display(display_name);
82        move_to_unused(&key, &self.models_dir, &self.unused_dir);
83        self.cache.remove(&key);
84    }
85
86    /// Get all model config display names (keys transformed back to display form).
87    pub fn keys(&self) -> Vec<String> {
88        let mut keys: Vec<String> = self.cache.keys().map(|k| display_from_key(k)).collect();
89        keys.sort();
90        keys
91    }
92}
93
94impl Default for ModelConfigStore {
95    fn default() -> Self {
96        Self::new()
97    }
98}