Skip to content

Commit d465fb9

Browse files
authored
Merge pull request #131 from saiteja007-mv/feat/runtime-hf-update
feat: add runtime model database updates from HuggingFace
2 parents ef974ee + c0ed6b4 commit d465fb9

File tree

5 files changed

+914
-38
lines changed

5 files changed

+914
-38
lines changed

llmfit-core/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod hardware;
33
pub mod models;
44
pub mod plan;
55
pub mod providers;
6+
pub mod update;
67

78
pub use fit::{FitLevel, InferenceRuntime, ModelFit, RunMode, ScoreComponents, SortColumn};
89
pub use hardware::{GpuBackend, SystemSpecs};
@@ -14,3 +15,6 @@ pub use plan::{
1415
pub use providers::{
1516
LlamaCppProvider, LmStudioProvider, MlxProvider, ModelProvider, OllamaProvider,
1617
};
18+
pub use update::{
19+
UpdateOptions, cache_file, clear_cache, load_cache, save_cache, update_model_cache,
20+
};

llmfit-core/src/models.rs

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -455,40 +455,79 @@ impl Default for ModelDatabase {
455455
}
456456
}
457457

458+
/// Normalize a model name/ID to a canonical slug for deduplication.
459+
///
460+
/// Strips the `org/` prefix, lowercases, and collapses `-`/`_`/`.` so that
461+
/// `meta-llama/Llama-3.1-8B` and `meta-llama/llama-3.1-8b` compare equal.
462+
pub(crate) fn canonical_slug(name: &str) -> String {
463+
let slug = name.split('/').last().unwrap_or(name);
464+
slug.to_lowercase().replace(['-', '_', '.'], "")
465+
}
466+
467+
/// Parse the compile-time embedded JSON into a flat `Vec<LlmModel>`.
468+
fn load_embedded() -> Vec<LlmModel> {
469+
let entries: Vec<HfModelEntry> =
470+
serde_json::from_str(HF_MODELS_JSON).expect("Failed to parse embedded hf_models.json");
471+
entries
472+
.into_iter()
473+
.map(|e| {
474+
let mut model = LlmModel {
475+
name: e.name,
476+
provider: e.provider,
477+
parameter_count: e.parameter_count,
478+
parameters_raw: e.parameters_raw,
479+
min_ram_gb: e.min_ram_gb,
480+
recommended_ram_gb: e.recommended_ram_gb,
481+
min_vram_gb: e.min_vram_gb,
482+
quantization: e.quantization,
483+
context_length: e.context_length,
484+
use_case: e.use_case,
485+
is_moe: e.is_moe,
486+
num_experts: e.num_experts,
487+
active_experts: e.active_experts,
488+
active_parameters: e.active_parameters,
489+
release_date: e.release_date,
490+
gguf_sources: e.gguf_sources,
491+
capabilities: e.capabilities,
492+
format: e.format,
493+
num_attention_heads: None,
494+
num_key_value_heads: None,
495+
};
496+
model.capabilities = Capability::infer(&model);
497+
model
498+
})
499+
.collect()
500+
}
501+
458502
impl ModelDatabase {
503+
/// Load only the compile-time embedded model list (no cache).
504+
/// Used internally by the updater to determine which models are already known.
505+
pub fn embedded() -> Self {
506+
ModelDatabase {
507+
models: load_embedded(),
508+
}
509+
}
510+
511+
/// Load the embedded model list **and** merge any locally cached models.
512+
///
513+
/// Cached models are appended after the embedded ones; if an ID already
514+
/// exists in the embedded list it is skipped to avoid duplication.
515+
/// Silently ignores a missing or corrupt cache file.
459516
pub fn new() -> Self {
460-
let entries: Vec<HfModelEntry> =
461-
serde_json::from_str(HF_MODELS_JSON).expect("Failed to parse embedded hf_models.json");
462-
463-
let models = entries
464-
.into_iter()
465-
.map(|e| {
466-
let mut model = LlmModel {
467-
name: e.name,
468-
provider: e.provider,
469-
parameter_count: e.parameter_count,
470-
parameters_raw: e.parameters_raw,
471-
min_ram_gb: e.min_ram_gb,
472-
recommended_ram_gb: e.recommended_ram_gb,
473-
min_vram_gb: e.min_vram_gb,
474-
quantization: e.quantization,
475-
context_length: e.context_length,
476-
use_case: e.use_case,
477-
is_moe: e.is_moe,
478-
num_experts: e.num_experts,
479-
active_experts: e.active_experts,
480-
active_parameters: e.active_parameters,
481-
release_date: e.release_date,
482-
gguf_sources: e.gguf_sources,
483-
capabilities: e.capabilities,
484-
format: e.format,
485-
num_attention_heads: None,
486-
num_key_value_heads: None,
487-
};
488-
model.capabilities = Capability::infer(&model);
489-
model
490-
})
491-
.collect();
517+
let mut models = load_embedded();
518+
519+
// Merge cached models (from `llmfit update`) without duplicating.
520+
// canonical_slug normalizes org/ prefix, case, and separators so that
521+
// e.g. `meta-llama/Llama-3.1-8B` and `meta-llama/llama-3.1-8b` are
522+
// treated as the same model.
523+
let embedded_keys: std::collections::HashSet<String> =
524+
models.iter().map(|m| canonical_slug(&m.name)).collect();
525+
526+
for cached in crate::update::load_cache() {
527+
if !embedded_keys.contains(&canonical_slug(&cached.name)) {
528+
models.push(cached);
529+
}
530+
}
492531

493532
ModelDatabase { models }
494533
}

0 commit comments

Comments
 (0)