nika_engine/core/
models.rs1pub use nika_core::catalogs::models::*;
13
14use std::path::PathBuf;
15
16#[derive(Debug, Clone)]
18pub struct ResolvedModel {
19 pub model: &'static KnownModel,
21 pub quantization: Quantization,
23 pub path: PathBuf,
25}
26
27#[must_use]
35pub fn detect_available_ram_gb() -> u32 {
36 crate::util::system::get_available_ram_gb()
37}
38
39pub fn resolve_model(
53 id: &str,
54 quantization: Option<Quantization>,
55) -> Result<ResolvedModel, ModelResolveError> {
56 let model =
57 find_model(id).ok_or_else(|| ModelResolveError::UnknownModel { id: id.to_string() })?;
58
59 let quant =
60 quantization.unwrap_or_else(|| auto_select_quantization(model, detect_available_ram_gb()));
61
62 let filename = model
64 .quantizations
65 .iter()
66 .find(|(q, _)| *q == quant)
67 .map(|(_, f)| *f)
68 .ok_or_else(|| ModelResolveError::QuantizationNotAvailable {
69 quantization: quant,
70 model_id: id.to_string(),
71 })?;
72
73 let cache_dir = dirs::home_dir()
75 .ok_or(ModelResolveError::HomeDirectoryNotFound)?
76 .join(".cache/huggingface/hub");
77
78 let repo_dir_name = format!("models--{}", model.hf_repo.replace('/', "--"));
80 let snapshots_dir = cache_dir.join(&repo_dir_name).join("snapshots");
81
82 if !snapshots_dir.exists() {
84 return Err(ModelResolveError::ModelNotDownloaded {
85 model_id: id.to_string(),
86 });
87 }
88
89 let snapshot = std::fs::read_dir(&snapshots_dir)
91 .map_err(|e| ModelResolveError::SnapshotsDirReadError {
92 path: snapshots_dir.clone(),
93 message: e.to_string(),
94 })?
95 .filter_map(|e| e.ok())
96 .filter(|e| e.file_type().map(|t| t.is_dir()).unwrap_or(false))
97 .max_by_key(|e| e.metadata().and_then(|m| m.modified()).ok());
98
99 let snapshot_dir = snapshot.ok_or_else(|| ModelResolveError::NoSnapshotsFound {
100 model_id: id.to_string(),
101 })?;
102 let model_path = snapshot_dir.path().join(filename);
103
104 if !model_path.exists() {
105 return Err(ModelResolveError::ModelFileNotFound {
106 path: model_path,
107 model_id: id.to_string(),
108 });
109 }
110
111 Ok(ResolvedModel {
112 model,
113 quantization: quant,
114 path: model_path,
115 })
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
128 fn test_resolve_model_unknown_id() {
129 let err = resolve_model("nonexistent:model", None).unwrap_err();
130 match err {
131 ModelResolveError::UnknownModel { id } => {
132 assert_eq!(id, "nonexistent:model");
133 }
134 other => panic!("Expected UnknownModel, got: {:?}", other),
135 }
136 }
137
138 #[test]
139 fn test_resolve_model_unavailable_quantization() {
140 let err = resolve_model("qwen3:1.7b", Some(Quantization::F16)).unwrap_err();
142 match err {
143 ModelResolveError::QuantizationNotAvailable {
144 quantization,
145 model_id,
146 } => {
147 assert_eq!(quantization, Quantization::F16);
148 assert_eq!(model_id, "qwen3:1.7b");
149 }
150 other => panic!("Expected QuantizationNotAvailable, got: {:?}", other),
151 }
152 }
153}