feat(cli): add delete subcommand to remove downloaded models
This commit is contained in:
+31
@@ -223,6 +223,37 @@ aha download -m qwen3vl-2b --download-retries 5
|
||||
aha download -m minicpm4-0.5b -s models
|
||||
```
|
||||
|
||||
### delete - Delete downloaded model
|
||||
|
||||
Delete a downloaded model from the default location (`~/.aha/{model_id}`).
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
aha delete [OPTIONS] --model <MODEL>
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `-m, --model <MODEL>` | Model type (required) | - |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Delete RMBG2.0 model from default location
|
||||
aha delete -m rmbg2.0
|
||||
|
||||
# Delete Qwen3-VL-2B model
|
||||
aha delete --model qwen3vl-2b
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Displays model information (ID, location, size) before deletion
|
||||
- Requires confirmation (y/N) before proceeding
|
||||
- Shows "Model not found" message if the model directory doesn't exist
|
||||
- Shows "Model deleted successfully" message after completion
|
||||
|
||||
## Supported Models
|
||||
|
||||
| Model ID | Model Name | Description |
|
||||
|
||||
@@ -223,6 +223,37 @@ aha download -m qwen3vl-2b --download-retries 5
|
||||
aha download -m minicpm4-0.5b -s models
|
||||
```
|
||||
|
||||
### delete - 删除已下载的模型
|
||||
|
||||
删除默认位置(`~/.aha/{model_id}`)的已下载模型。
|
||||
|
||||
**语法:**
|
||||
```bash
|
||||
aha delete [OPTIONS] --model <MODEL>
|
||||
```
|
||||
|
||||
**选项:**
|
||||
|
||||
| 选项 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `-m, --model <MODEL>` | 模型类型(必选) | - |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 删除 RMBG2.0 模型
|
||||
aha delete -m rmbg2.0
|
||||
|
||||
# 删除 Qwen3-VL-2B 模型
|
||||
aha delete --model qwen3vl-2b
|
||||
```
|
||||
|
||||
**行为说明:**
|
||||
- 删除前会显示模型信息(ID、位置、大小)
|
||||
- 需要用户确认(y/N)才会执行删除
|
||||
- 如果模型目录不存在,显示"模型未找到"消息
|
||||
- 删除完成后显示"删除成功"消息
|
||||
|
||||
## 支持的模型
|
||||
|
||||
| 模型标识 | 模型名称 | 说明 |
|
||||
|
||||
+99
@@ -56,6 +56,8 @@ enum Commands {
|
||||
Serv(ServArgs),
|
||||
/// List all running aha services
|
||||
Ps(ServListArgs),
|
||||
/// Delete a downloaded model from the default location (~/.aha/{model_id})
|
||||
Delete(DeleteArgs),
|
||||
/// Download model only
|
||||
Download(DownloadArgs),
|
||||
/// Run model inference directly
|
||||
@@ -158,6 +160,14 @@ struct RunArgs {
|
||||
weight_path: Option<String>,
|
||||
}
|
||||
|
||||
/// Arguments for the 'delete' subcommand (delete model from default location)
|
||||
#[derive(Args, Debug)]
|
||||
struct DeleteArgs {
|
||||
/// Model type (required)
|
||||
#[arg(short, long)]
|
||||
model: WhichModel,
|
||||
}
|
||||
|
||||
/// Get the default weight path for a given model
|
||||
/// Returns ~/.aha/{model_id} e.g., ~/.aha/OpenBMB/VoxCPM1.5
|
||||
fn get_default_weight_path(model: WhichModel) -> String {
|
||||
@@ -408,6 +418,94 @@ fn run_run(args: RunArgs) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run the 'delete' subcommand: delete model from default location
|
||||
fn run_delete(args: DeleteArgs) -> anyhow::Result<()> {
|
||||
let DeleteArgs { model } = args;
|
||||
let model_id = model.model_id();
|
||||
let save_dir = get_default_save_dir().expect("Failed to get home directory");
|
||||
let model_path = format!("{}/{}", save_dir, model_id);
|
||||
|
||||
let path = std::path::Path::new(&model_path);
|
||||
|
||||
if !path.exists() {
|
||||
println!("Model not found: {} does not exist", model_path);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Show model info
|
||||
println!("Model ID: {}", model_id);
|
||||
println!("Location: {}", model_path);
|
||||
|
||||
// Calculate size if possible
|
||||
if let Ok(metadata) = std::fs::metadata(path) {
|
||||
if metadata.is_dir() {
|
||||
if let Ok(total_size) = dir_size(path) {
|
||||
println!("Size: {}", bytes_to_human(total_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm deletion
|
||||
print!("Are you sure you want to delete this model? (y/N): ");
|
||||
use std::io::Write;
|
||||
std::io::stdout().flush()?;
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input)?;
|
||||
|
||||
let input = input.trim().to_lowercase();
|
||||
if input != "y" && input != "yes" {
|
||||
println!("Deletion cancelled.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Delete the directory
|
||||
std::fs::remove_dir_all(path)?;
|
||||
|
||||
println!("Model deleted successfully: {}", model_path);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Calculate total size of a directory recursively
|
||||
fn dir_size(path: &std::path::Path) -> anyhow::Result<u64> {
|
||||
let mut total = 0;
|
||||
if path.is_dir() {
|
||||
for entry in std::fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let entry_path = entry.path();
|
||||
if entry_path.is_dir() {
|
||||
total += dir_size(&entry_path)?;
|
||||
} else {
|
||||
total += entry.metadata()?.len();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
total = std::fs::metadata(path)?.len();
|
||||
}
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
/// Convert bytes to human readable format
|
||||
fn bytes_to_human(bytes: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = KB * 1024;
|
||||
const GB: u64 = MB * 1024;
|
||||
const TB: u64 = GB * 1024;
|
||||
|
||||
if bytes >= TB {
|
||||
format!("{:.2} TB", bytes as f64 / TB as f64)
|
||||
} else if bytes >= GB {
|
||||
format!("{:.2} GB", bytes as f64 / GB as f64)
|
||||
} else if bytes >= MB {
|
||||
format!("{:.2} MB", bytes as f64 / MB as f64)
|
||||
} else if bytes >= KB {
|
||||
format!("{:.2} KB", bytes as f64 / KB as f64)
|
||||
} else {
|
||||
format!("{} B", bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
@@ -416,6 +514,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
Some(Commands::Cli(args)) => run_cli(args).await,
|
||||
Some(Commands::Serv(args)) => run_serv(args).await,
|
||||
Some(Commands::Ps(args)) => run_ps(args),
|
||||
Some(Commands::Delete(args)) => run_delete(args),
|
||||
Some(Commands::Download(args)) => run_download(args).await,
|
||||
Some(Commands::Run(args)) => run_run(args),
|
||||
Some(Commands::List) => run_list(),
|
||||
|
||||
Reference in New Issue
Block a user