1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//! `rqmd pull` — download configured GGUF models from HuggingFace.
//!
//! Maps to qmd's `pullModels` CLI handler (`src/cli/qmd.ts` lines 3569–3587)
//! and `pullModels` in `src/llm.ts` (lines 377–435). ETag-based incremental
//! download is handled by `rqmd_core::llm::pull::pull_models`.
use anyhow::{Context, Result};
use rqmd_core::llm::pull::pull_models;
use rqmd_core::llm::types::PullOptions;
use crate::cli::PullArgs;
use crate::color::Palette;
use crate::format_helpers::format_bytes;
use crate::state::IndexState;
pub async fn run(args: PullArgs, state: &mut IndexState, p: &Palette) -> Result<()> {
let uris = state.resolved_model_uris()?;
let models = vec![uris.embed, uris.generate, uris.rerank];
eprintln!("{}Pulling models{}", p.bold(), p.reset());
let opts = PullOptions {
refresh: args.refresh,
cache_dir: None,
};
// `pull_models` is sync (hf-hub uses blocking ureq); offload to a blocking
// task so we don't park a tokio worker thread for the full download.
// Mirrors the pattern in `LlamaCpp::load_model`.
//
// We preserve the underlying error chain via `?` + `.context()` so that
// `main.rs`'s `{err:#}` formatting can surface specific guidance like
// `Error::InvalidGguf { looks_like_html, .. }` from `rqmd-core/src/llm/pull.rs`.
let results = tokio::task::spawn_blocking(move || pull_models(&models, &opts))
.await
.context("pull task panicked")?
.context("pull failed")?;
for r in results {
let note = if r.refreshed {
"refreshed"
} else {
"cached/checked"
};
println!(
"{}-{} {} -> {} ({}, {})",
p.dim(),
p.reset(),
r.model,
r.path.display(),
format_bytes(r.size_bytes),
note,
);
}
Ok(())
}