fix(local-inference): preserve featured model size on delete and backfill missing sizes (#10422)

This commit is contained in:
Harnoor Singh
2026-07-21 08:20:27 +05:30
committed by GitHub
parent 076da90e89
commit 18740b095f
5 changed files with 76 additions and 6 deletions
Generated
+1
View File
@@ -5128,6 +5128,7 @@ dependencies = [
"base64 0.22.1",
"chrono",
"encoding_rs",
"env-lock",
"etcetera 0.11.0",
"fs2",
"futures",
+3
View File
@@ -51,3 +51,6 @@ safemlx-lm-utils = { optional = true, version = "0.1.2" }
[target.'cfg(target_os = "macos")'.dependencies]
llama-cpp-2 = { workspace = true, features = ["sampler", "metal", "mtmd"] }
[dev-dependencies]
env-lock.workspace = true
+3 -3
View File
@@ -314,13 +314,13 @@ pub fn recommend_local_model(runtime: &InferenceRuntime) -> String {
let mut models: Vec<_> = registry
.list_models()
.iter()
.filter(|m| is_featured_model(&m.id) && m.size_bytes > 0)
.filter(|m| is_featured_model(&m.id) && m.file_size() > 0)
.collect();
models.sort_by_key(|model| std::cmp::Reverse(model.size_bytes));
models.sort_by_key(|model| std::cmp::Reverse(model.file_size()));
// Return largest that fits in available memory
for model in &models {
if available_memory >= model.size_bytes {
if available_memory >= model.file_size() {
return model.id.clone();
}
}
@@ -516,7 +516,12 @@ impl LocalModelRegistry {
let mut changed = false;
for mut entry in featured_entries {
if !self.models.iter().any(|m| m.id == entry.id) {
if let Some(existing) = self.models.iter_mut().find(|m| m.id == entry.id) {
if existing.size_bytes == 0 && entry.size_bytes > 0 {
existing.size_bytes = entry.size_bytes;
changed = true;
}
} else {
entry.enrich_with_featured_mmproj();
self.models.push(entry);
changed = true;
@@ -559,7 +564,6 @@ impl LocalModelRegistry {
if let Some(entry) = self.models.iter_mut().find(|m| m.id == id) {
entry.local_path = Paths::in_data_dir("models").join(&entry.filename);
entry.storage = LocalModelStorage::GooseManaged;
entry.size_bytes = 0;
entry.shard_files.clear();
}
self.save()
@@ -726,6 +730,67 @@ mod tests {
get_download_manager().clear_completed(&download_id);
}
#[test]
fn delete_featured_model_preserves_size_bytes() {
let root = tempfile::tempdir().unwrap();
let _guard = env_lock::lock_env([("GOOSE_PATH_ROOT", Some(root.path().to_str().unwrap()))]);
let mut entry = test_entry("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M");
entry.local_path = root.path().join("model.gguf");
entry.size_bytes = 12345;
let mut registry = LocalModelRegistry {
models: vec![entry],
};
registry
.delete_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M")
.unwrap();
let kept = registry
.get_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M")
.expect("featured model entry should remain after deletion");
assert_eq!(kept.size_bytes, 12345);
assert!(!kept.is_downloaded());
}
#[test]
fn sync_with_featured_backfills_size_without_touching_settings() {
let root = tempfile::tempdir().unwrap();
let _guard = env_lock::lock_env([("GOOSE_PATH_ROOT", Some(root.path().to_str().unwrap()))]);
let mut existing = test_entry("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M");
existing.settings.enable_thinking = true;
existing.storage = LocalModelStorage::HuggingFaceCache;
let mut resolved = test_entry("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M");
resolved.size_bytes = 999;
resolved.settings.enable_thinking = false;
let mut registry = LocalModelRegistry {
models: vec![existing],
};
registry.sync_with_featured(vec![resolved]);
let updated = registry
.get_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M")
.unwrap();
assert_eq!(updated.size_bytes, 999);
assert!(updated.settings.enable_thinking);
assert_eq!(updated.storage, LocalModelStorage::HuggingFaceCache);
let mut refreshed = test_entry("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M");
refreshed.size_bytes = 777;
registry.sync_with_featured(vec![refreshed]);
assert_eq!(
registry
.get_model("unsloth/gemma-4-E4B-it-GGUF:Q4_K_M")
.unwrap()
.size_bytes,
999,
"non-zero size should not be overwritten"
);
}
#[test]
fn enrich_with_featured_mmproj_preserves_existing_downloaded_path() {
let existing_path = std::env::temp_dir().join(format!(
@@ -294,6 +294,7 @@ pub async fn ensure_featured_models_current() -> Result<()> {
.map_err(|_| anyhow!("Failed to acquire registry lock"))?;
if let Some(existing) = registry.get_model(&model_id) {
let needs_backfill = existing.mmproj_path.is_none() && featured.mmproj.is_some();
let needs_size = existing.size_bytes == 0 && !existing.is_downloaded();
let needs_download = existing.is_downloaded()
&& featured.mmproj.is_some()
&& !existing.mmproj_path.as_ref().is_some_and(|p| p.exists());
@@ -309,7 +310,7 @@ pub async fn ensure_featured_models_current() -> Result<()> {
}
}
if !needs_backfill {
if !needs_backfill && !needs_size {
continue;
}
}