Skip to main content

apr_cli/commands/
pull_list.rs

1
2/// List cached models
3// serde_json::json!() macro uses infallible unwrap internally
4#[allow(clippy::disallowed_methods)]
5pub fn list(json: bool) -> Result<()> {
6    let fetcher = ModelFetcher::new().map_err(|e| {
7        CliError::ValidationFailed(format!("Failed to initialize model fetcher: {e}"))
8    })?;
9
10    let models = fetcher.list();
11
12    // GH-248: JSON output mode
13    if json {
14        let models_json: Vec<serde_json::Value> = models
15            .iter()
16            .map(|m| {
17                serde_json::json!({
18                    "name": m.name,
19                    "size_bytes": m.size_bytes,
20                    "format": m.format.name(),
21                    "path": m.path.display().to_string(),
22                })
23            })
24            .collect();
25        let stats = fetcher.stats();
26        let output = serde_json::json!({
27            "models": models_json,
28            "total": models.len(),
29            "total_size_bytes": stats.total_size_bytes,
30        });
31        println!(
32            "{}",
33            serde_json::to_string_pretty(&output).unwrap_or_default()
34        );
35        return Ok(());
36    }
37
38    println!("{}", "=== Cached Models ===".cyan().bold());
39    println!();
40
41    if models.is_empty() {
42        println!("{}", "No cached models found.".dimmed());
43        println!();
44        println!("Pull a model with:");
45        println!("  apr pull hf://Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf");
46        println!();
47        println!("Or run directly (auto-downloads):");
48        println!("  apr run hf://Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf");
49        return Ok(());
50    }
51
52    // Print header
53    println!(
54        "{:<40} {:<12} {:<12} {}",
55        "NAME".dimmed(),
56        "SIZE".dimmed(),
57        "FORMAT".dimmed(),
58        "PATH".dimmed()
59    );
60    println!("{}", "-".repeat(104).dimmed());
61
62    for model in &models {
63        let size = format_bytes(model.size_bytes);
64        let format = model.format.name();
65        let name = if model.name.len() > 38 {
66            format!("{}...", &model.name[..35])
67        } else {
68            model.name.clone()
69        };
70
71        println!(
72            "{:<40} {:<12} {:<12} {}",
73            name.cyan(),
74            size.yellow(),
75            format,
76            model.path.display().to_string().dimmed()
77        );
78    }
79
80    println!();
81
82    // Print stats
83    let stats = fetcher.stats();
84    println!(
85        "Total: {} models, {} used",
86        models.len(),
87        format_bytes(stats.total_size_bytes)
88    );
89
90    Ok(())
91}