use crate::cache::Cache;
use crate::cli::Cli;
use crate::config::Config;
use crate::db::Database;
use crate::llm::LlmClient;
use crate::ui::Ui;
use anyhow::Result;
use std::path::Path;
pub async fn handle_models(args: &Cli, as_json: bool) -> Result<()> {
let llm = crate::commands::init_llm_or_exit(args);
let pb = Ui::spinner("Querying LLM server for available models...");
let models_result = llm.list_models().await;
pb.finish_and_clear();
match models_result {
Ok(models) => {
if as_json {
println!("{}", serde_json::to_string_pretty(&models)?);
return Ok(());
}
if models.is_empty() {
Ui::info(&format!(
"Backend at {} reported 0 loaded models.",
llm.base_url()
));
return Ok(());
}
Ui::header(&format!(
"Available Models at {} ({} found)",
llm.base_url(),
models.len()
));
for m in &models {
let is_active = m.id == args.model;
let active_badge = if is_active { " [ACTIVE]" } else { "" };
let safe_id = crate::ui::sanitize_title(&m.id);
let owner = m
.owned_by
.as_deref()
.map(|o| format!(" ({})", crate::ui::sanitize_title(o)))
.unwrap_or_default();
println!(" • {}{}{}", safe_id, owner, active_badge);
}
println!();
Ok(())
}
Err(err) => {
let safe_err = crate::ui::sanitize_terminal_text(&format!("{:#}", err));
Ui::error(&format!("Could not fetch models: {}", safe_err));
Ui::info("Tip: Run `srz doctor` to diagnose server connectivity and ports.");
std::process::exit(1);
}
}
}
pub async fn handle_doctor(
args: &Cli,
config: &Config,
config_path: Option<&Path>,
cache: &Cache,
as_json: bool,
) -> Result<()> {
let llm_init = LlmClient::from_cli(args);
let mut endpoint_ok = false;
let mut latency_ms = None;
let mut model_found = false;
let mut available_models = Vec::new();
let mut endpoint_err = None;
let mut server_caps = None;
if let Ok(llm) = &llm_init {
let start = std::time::Instant::now();
match llm.list_models().await {
Ok(models) => {
endpoint_ok = true;
latency_ms = Some(start.elapsed().as_millis() as u64);
available_models = models.iter().map(|m| m.id.clone()).collect();
model_found = available_models.contains(&args.model);
server_caps = Some(llm.probe_server_capabilities().await);
}
Err(e) => {
endpoint_err = Some(e.to_string());
}
}
}
let hw = crate::system::hardware::HardwareProfile::detect();
let db_path = Database::default_path();
let (db_ok, db_entries_count) = match Database::open_default() {
Ok(db) => match db.get_stats() {
Ok(stats) => (true, stats.total_summaries),
Err(_) => (false, 0),
},
Err(_) => (false, 0),
};
let cache_dir = cache.dir().to_path_buf();
let cache_ok = cache_dir.exists() || crate::fs_util::create_private_dir_all(&cache_dir).is_ok();
let cache_stats = cache.stats();
if as_json {
let report = serde_json::json!({
"hardware": {
"device": hw.chip_name,
"total_memory_bytes": hw.total_memory_bytes,
"total_memory_gb": hw.total_memory_gb,
"tier": hw.tier.display_name(),
"recommended_family": "Gemma 4",
"recommended_model": hw.recommended_model,
"recommended_quant": hw.recommended_quant,
"recommended_context": hw.recommended_context,
"recommended_launch_command": hw.recommended_launch_command,
"ollama_launch_command": hw.ollama_launch_command,
"ram_footprint": hw.ram_footprint,
},
"llm": {
"endpoint": args.base_url,
"reachable": endpoint_ok,
"latency_ms": latency_ms,
"configured_model": args.model,
"model_available": model_found,
"available_models": available_models,
"capabilities": server_caps,
"error": endpoint_err,
},
"database": {
"path": db_path,
"healthy": db_ok,
"total_summaries": db_entries_count,
},
"cache": {
"dir": cache_dir,
"healthy": cache_ok,
"files_count": cache_stats.total_files,
"size_bytes": cache_stats.total_bytes,
"policy": {
"pages_ttl_days": cache.policy().pages_ttl.as_secs() / 86400,
"search_ttl_days": cache.policy().search_ttl.as_secs() / 86400,
"summaries_ttl_days": cache.policy().summaries_ttl.as_secs() / 86400,
"views_ttl_days": cache.policy().views_ttl.as_secs() / 86400,
"max_budget_bytes": cache.policy().max_bytes,
},
},
"config": {
"path": config_path,
"active_profile": args.profile,
"profiles_configured": config.profiles.keys().collect::<Vec<_>>(),
}
});
println!("{}", serde_json::to_string_pretty(&report)?);
return Ok(());
}
Ui::header("srz System & LLM Health Diagnostics");
println!("Hardware Profile & Recommendations:");
println!(
" • Host Device: {} ({:.1} GB RAM / {})",
hw.chip_name,
hw.total_memory_gb,
hw.tier.display_name()
);
println!(
" • Recommended Family: Google Gemma 4 (High fidelity Q8_0, laptop battery & memory optimized)"
);
println!(
" • Recommended Model: {} ({})",
hw.recommended_model, hw.recommended_quant
);
println!(
" • Recommended Context: {} tokens ({})",
hw.recommended_context, hw.ram_footprint
);
println!(" • Recommended Command: {}", hw.recommended_launch_command);
println!(" • Ollama Command: {}", hw.ollama_launch_command);
println!();
println!("LLM Backend:");
if endpoint_ok {
println!(
" ✓ Reachable: {} (latency: {}ms)",
args.base_url,
latency_ms.unwrap_or(0)
);
if model_found {
println!(
" ✓ Configured Model: \"{}\" is loaded and ready",
args.model
);
} else {
println!(
" ! Configured Model: \"{}\" was not reported by /models endpoint.",
args.model
);
if !available_models.is_empty() {
let safe_models: Vec<String> = available_models
.iter()
.map(|m| crate::ui::sanitize_title(m))
.collect();
println!(
" Available models on backend: {}",
safe_models.join(", ")
);
}
}
if let Some(caps) = &server_caps {
println!(" ✓ Context Window (n_ctx): {} tokens", caps.n_ctx);
println!(
" ✓ Server Concurrency (total_slots): {} slot(s)",
caps.total_slots
);
println!(
" ✓ Native Tokenizer: {}",
if caps.supports_tokenize {
"Supported (POST /tokenize enabled, 0% token drift)"
} else {
"Fallback (tiktoken-rs)"
}
);
println!(" ✓ Backend Kind: {}", caps.backend_kind);
if caps.n_ctx >= hw.recommended_context {
println!(
" ✓ Optimization Status: EXCELLENT (Fully optimized for single-pass summaries)"
);
} else {
println!(
" ! Optimization Status: SUBOPTIMAL (Server context is {} vs {} recommended for your machine; run: {})",
caps.n_ctx, hw.recommended_context, hw.recommended_launch_command
);
}
}
} else {
println!(" ✗ Endpoint Unreachable: {}", args.base_url);
if let Some(err) = endpoint_err {
let safe_err = crate::ui::sanitize_terminal_text(&err);
println!(" Reason: {}", safe_err);
}
println!(" Check that llama.cpp / Ollama / LM Studio is running and listening.");
}
println!();
println!("DuckDB Embedded Database:");
if db_ok {
println!(
" ✓ Healthy at: {} (tracking {} past summaries)",
db_path.display(),
db_entries_count
);
} else {
println!(" ✗ Could not open database at: {}", db_path.display());
}
println!();
println!("Cache & Storage:");
if cache_ok {
println!(
" ✓ Storage directory: {} ({} in {} files)",
cache_dir.display(),
Cache::format_bytes(cache_stats.total_bytes),
cache_stats.total_files
);
println!(
" ✓ Retention policy: {}d pages, {}d search, {}d summaries, {} max budget",
cache.policy().pages_ttl.as_secs() / 86400,
cache.policy().search_ttl.as_secs() / 86400,
cache.policy().summaries_ttl.as_secs() / 86400,
Cache::format_bytes(cache.policy().max_bytes)
);
} else {
println!(
" ✗ Unable to access or create cache directory at: {}",
cache_dir.display()
);
}
println!();
println!("Configuration:");
if let Some(cp) = config_path {
println!(" ✓ Active config file: {}", cp.display());
} else {
println!(
" • No config file found (using defaults at {})",
Config::default_config_path().display()
);
}
if let Some(p) = &args.profile {
println!(" • Active profile: {}", p);
}
println!();
Ok(())
}
pub fn handle_recommend(as_json: bool) -> Result<()> {
let hw = crate::system::hardware::HardwareProfile::detect();
let one_pass_tokens = hw.one_pass_token_ceiling();
let one_pass_pages = (one_pass_tokens / 250).max(1);
if as_json {
let report = serde_json::json!({
"hardware": {
"device": hw.chip_name,
"total_memory_bytes": hw.total_memory_bytes,
"total_memory_gb": hw.total_memory_gb,
"tier": hw.tier.display_name(),
},
"recommendations": {
"family": "Google Gemma 4",
"model": hw.recommended_model,
"quantization": hw.recommended_quant,
"context_window": hw.recommended_context,
"ram_footprint": hw.ram_footprint,
"one_pass_token_ceiling": one_pass_tokens,
},
"launch_commands": {
"llama_server": hw.recommended_launch_command,
"ollama": hw.ollama_launch_command,
}
});
println!("{}", serde_json::to_string_pretty(&report)?);
return Ok(());
}
Ui::header("srz Recommended Model & Context Length");
println!("Host Machine:");
println!(" • Device: {}", hw.chip_name);
println!(
" • Memory: {:.1} GB RAM ({})",
hw.total_memory_gb,
hw.tier.display_name()
);
println!();
println!("Recommended Local LLM Setup:");
println!(" • Family: Google Gemma 4 (High-fidelity Q8_0, laptop battery & memory optimized)");
println!(
" • Model: {} ({})",
hw.recommended_model, hw.recommended_quant
);
println!(
" • Context: {} tokens ({})",
hw.recommended_context, hw.ram_footprint
);
println!();
println!("Ready-to-Run Launch Commands:");
println!(" • llama.cpp / Llama-macOS / Llama-Windows (default port 9931):");
println!(" {}", hw.recommended_launch_command);
println!(" • Ollama:");
println!(" {}", hw.ollama_launch_command);
println!();
println!("Summary Synthesis Speed:");
println!(
" 💡 Fast 1-Pass Summaries: Documents up to ~{}k tokens (~{} pages) synthesize in a single pass.",
one_pass_tokens / 1000,
one_pass_pages
);
println!(
" Larger documents automatically use parallel map-reduce chunking without OOM crashes."
);
println!();
println!("Next Steps:");
println!(" 1. Start your local LLM runner using one of the commands above.");
println!(" 2. Run `srz config init` to generate a config tailored to your machine.");
println!(" 3. Summarize any target: srz \"<URL, file, or book>\"");
println!();
Ok(())
}