fix aha run multiple inputs bug

This commit is contained in:
jhqxxx
2026-01-31 18:37:28 +08:00
parent fe299c3401
commit 16e3aefaa7
16 changed files with 308 additions and 123 deletions
+24 -15
View File
@@ -65,7 +65,7 @@ aha -m qwen3vl-2b
**语法:** **语法:**
```bash ```bash
aha run [OPTIONS] --model <MODEL> --input <INPUT> --weight-path <WEIGHT_PATH> aha run [OPTIONS] --model <MODEL> --input <INPUT> [--input <INPUT2>] --weight-path <WEIGHT_PATH>
``` ```
**选项:** **选项:**
@@ -73,30 +73,39 @@ aha run [OPTIONS] --model <MODEL> --input <INPUT> --weight-path <WEIGHT_PATH>
| 选项 | 说明 | 默认值 | | 选项 | 说明 | 默认值 |
|------|------|--------| |------|------|--------|
| `-m, --model <MODEL>` | 模型类型(必选) | - | | `-m, --model <MODEL>` | 模型类型(必选) | - |
| `-in, --input <INPUT>` | 输入文本或文件路径(模型特定解释) | - | | `-i, --input <INPUT>` | 输入文本或文件路径(模型特定解释,支持1-2个参数, input1 提示文本, input2: 文件地址 | - |
| `-out, --output <OUTPUT>` | 输出文件路径(可选,未指定则自动生成) | - | | `-o, --output <OUTPUT>` | 输出文件路径(可选,未指定则自动生成) | - |
| `--weight-path <WEIGHT_PATH>` | 本地模型权重路径(必选) | - | | `--weight-path <WEIGHT_PATH>` | 本地模型权重路径(必选) | - |
**示例:** **示例:**
```bash ```bash
# VoxCPM1.5 文字转语音 # VoxCPM1.5 文字转语音(单个输入)
aha run -m voxcpm1.5 -in "太阳当空照" -out output.wav --weight-path /path/to/model aha run -m voxcpm1.5 -i "太阳当空照" -o output.wav --weight-path /path/to/model
# VoxCPM1.5 从文件读取输入 # VoxCPM1.5 从文件读取输入(单个输入)
aha run -m voxcpm1.5 -in "file://./input.txt" --weight-path /path/to/model aha run -m voxcpm1.5 -i "file://./input.txt" --weight-path /path/to/model
# MiniCPM4 文本生成 # MiniCPM4 文本生成(单个输入)
aha run -m minicpm4-0.5b -in "你好" --weight-path /path/to/model aha run -m minicpm4-0.5b -i "你好" --weight-path /path/to/model
# DeepSeek OCR 图片识别 # DeepSeek OCR 图片识别(单个输入)
aha run -m deepseek-ocr -in "image.jpg" --weight-path /path/to/model aha run -m deepseek-ocr -i "image.jpg" --weight-path /path/to/model
# RMBG2.0 背景移除 # RMBG2.0 背景移除(单个输入)
aha run -m RMBG2.0 -in "photo.png" -out "no_bg.png" --weight-path /path/to/model aha run -m RMBG2.0 -i "photo.png" -o "no_bg.png" --weight-path /path/to/model
# GLM-ASR 语音识别 # GLM-ASR 语音识别(两个输入:提示文本 + 音频文件)
aha run -m glm-asr-nano-2512 -in "audio.wav" -in "请转写这段音频" --weight-path /path/to/model aha run -m glm-asr-nano-2512 -i "请转写这段音频" -i "audio.wav" --weight-path /path/to/model
# Fun-ASR 语音识别(两个输入:提示文本 + 音频文件)
aha run -m fun-asr-nano-2512 -i "语音转写:" -i "audio.wav" --weight-path /path/to/model
# qwen3 文本生成(单个输入)
aha run -m qwen3-0.6b -i "你好" --weight-path /path/to/model
# qwen2.5vl 图像理解(两个输入:提示文本 + 图片文件)
aha run -m qwen2.5vl-3b -i "请分析图片并提取所有可见文本内容,按从左到右、从上到下的布局,返回纯文本" -i "image.jpg" --weight-path /path/to/model
``` ```
### serv - 启动服务 ### serv - 启动服务
+17 -7
View File
@@ -1,18 +1,24 @@
//! DeepSeek-OCR exec implementation for CLI `run` subcommand //! DeepSeek-OCR exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, deepseek_ocr::generate::DeepseekOCRGenerateModel}; use crate::models::{GenerateModel, deepseek_ocr::generate::DeepseekOCRGenerateModel};
use anyhow::{Ok, Result};
use std::time::Instant;
pub struct DeepSeekORExec; pub struct DeepSeekORExec;
impl ExecModel for DeepSeekORExec { impl ExecModel for DeepSeekORExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let input_path = if input.starts_with("file://") { let url = &input[0];
input.to_string() let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else { } else {
format!("file://{}", input) format!("file://{}", url)
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -32,12 +38,16 @@ impl ExecModel for DeepSeekORExec {
"image_url": {{ "image_url": {{
"url": "{}" "url": "{}"
}} }}
}},
{{
"type": "text",
"text": "<image>\nConvert the document to markdown. "
}} }}
] ]
}} }}
] ]
}}"#, }}"#,
input_path input_url
); );
let mes = serde_json::from_str(&message)?; let mes = serde_json::from_str(&message)?;
+18 -9
View File
@@ -1,19 +1,24 @@
//! Fun-ASR-Nano-2512 exec implementation for CLI `run` subcommand //! Fun-ASR-Nano-2512 exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, fun_asr_nano::generate::FunAsrNanoGenerateModel}; use crate::models::{GenerateModel, fun_asr_nano::generate::FunAsrNanoGenerateModel};
use anyhow::{Ok, Result}; use crate::utils::get_file_path;
use std::time::Instant;
pub struct FunASRNanoExec; pub struct FunASRNanoExec;
impl ExecModel for FunASRNanoExec { impl ExecModel for FunASRNanoExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
// let path = &input[7..];
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -22,10 +27,14 @@ impl ExecModel for FunASRNanoExec {
println!("Time elapsed in load model is: {:?}", i_duration); println!("Time elapsed in load model is: {:?}", i_duration);
// Create ChatCompletionParameters for ASR // Create ChatCompletionParameters for ASR
let input_url = if input.starts_with("http://") || input.starts_with("https://") || input.starts_with("file://") { let url = &input[1];
input.to_string() let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else { } else {
format!("file://{}", input) format!("file://{}", url)
}; };
let message = format!( let message = format!(
+18 -9
View File
@@ -1,19 +1,24 @@
//! GLM-ASR-Nano-2512 exec implementation for CLI `run` subcommand //! GLM-ASR-Nano-2512 exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, glm_asr_nano::generate::GlmAsrNanoGenerateModel}; use crate::models::{GenerateModel, glm_asr_nano::generate::GlmAsrNanoGenerateModel};
use anyhow::{Ok, Result}; use crate::utils::get_file_path;
use std::time::Instant;
pub struct GlmASRNanoExec; pub struct GlmASRNanoExec;
impl ExecModel for GlmASRNanoExec { impl ExecModel for GlmASRNanoExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
// let path = &input[7..];
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -23,10 +28,14 @@ impl ExecModel for GlmASRNanoExec {
// Create ChatCompletionParameters for ASR // Create ChatCompletionParameters for ASR
// Input should be an audio file path // Input should be an audio file path
let input_url = if input.starts_with("http://") || input.starts_with("https://") || input.starts_with("file://") { let url = &input[1];
input.to_string() let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else { } else {
format!("file://{}", input) format!("file://{}", url)
}; };
let message = format!( let message = format!(
+17 -7
View File
@@ -1,18 +1,24 @@
//! Hunyuan-OCR exec implementation for CLI `run` subcommand //! Hunyuan-OCR exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, hunyuan_ocr::generate::HunyuanOCRGenerateModel}; use crate::models::{GenerateModel, hunyuan_ocr::generate::HunyuanOCRGenerateModel};
use anyhow::{Ok, Result};
use std::time::Instant;
pub struct HunyuanORExec; pub struct HunyuanORExec;
impl ExecModel for HunyuanORExec { impl ExecModel for HunyuanORExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let input_path = if input.starts_with("file://") { let url = &input[0];
input.to_string() let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else { } else {
format!("file://{}", input) format!("file://{}", url)
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -32,12 +38,16 @@ impl ExecModel for HunyuanORExec {
"image_url": {{ "image_url": {{
"url": "{}" "url": "{}"
}} }}
}},
{{
"type": "text",
"text": "检测并识别图片中的文字,将文本坐标格式化输出。"
}} }}
] ]
}} }}
] ]
}}"#, }}"#,
input_path input_url
); );
let mes = serde_json::from_str(&message)?; let mes = serde_json::from_str(&message)?;
+11 -6
View File
@@ -1,19 +1,24 @@
//! MiniCPM4-0.5B exec implementation for CLI `run` subcommand //! MiniCPM4-0.5B exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, minicpm4::generate::MiniCPMGenerateModel}; use crate::models::{GenerateModel, minicpm4::generate::MiniCPMGenerateModel};
use anyhow::{Ok, Result}; use crate::utils::get_file_path;
use std::time::Instant;
pub struct MiniCPM4Exec; pub struct MiniCPM4Exec;
impl ExecModel for MiniCPM4Exec { impl ExecModel for MiniCPM4Exec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
// let path = &input[7..];
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.to_string()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
+1 -1
View File
@@ -33,5 +33,5 @@ pub trait ExecModel {
/// # Returns /// # Returns
/// * `Ok(())` on success /// * `Ok(())` on success
/// * `Err(anyhow::Error)` on failure /// * `Err(anyhow::Error)` on failure
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()>; fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()>;
} }
+17 -7
View File
@@ -1,18 +1,24 @@
//! PaddleOCR-VL exec implementation for CLI `run` subcommand //! PaddleOCR-VL exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, paddleocr_vl::generate::PaddleOCRVLGenerateModel}; use crate::models::{GenerateModel, paddleocr_vl::generate::PaddleOCRVLGenerateModel};
use anyhow::{Ok, Result};
use std::time::Instant;
pub struct PaddleOVLExec; pub struct PaddleOVLExec;
impl ExecModel for PaddleOVLExec { impl ExecModel for PaddleOVLExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let input_path = if input.starts_with("file://") { let url = &input[0];
input.to_string() let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else { } else {
format!("file://{}", input) format!("file://{}", url)
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -32,12 +38,16 @@ impl ExecModel for PaddleOVLExec {
"image_url": {{ "image_url": {{
"url": "{}" "url": "{}"
}} }}
}},
{{
"type": "text",
"text": "OCR:"
}} }}
] ]
}} }}
] ]
}}"#, }}"#,
input_path input_url
); );
let mes = serde_json::from_str(&message)?; let mes = serde_json::from_str(&message)?;
+32 -9
View File
@@ -1,21 +1,33 @@
//! Qwen2.5VL-3B exec implementation for CLI `run` subcommand //! Qwen2.5VL-3B exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, qwen2_5vl::generate::Qwen2_5VLGenerateModel}; use crate::models::{GenerateModel, qwen2_5vl::generate::Qwen2_5VLGenerateModel};
use anyhow::{Ok, Result}; use crate::utils::get_file_path;
use std::time::Instant;
pub struct Qwen2_5vlExec; pub struct Qwen2_5vlExec;
impl ExecModel for Qwen2_5vlExec { impl ExecModel for Qwen2_5vlExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
};
let url = &input[1];
let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else {
format!("file://{}", url)
}; };
let i_start = Instant::now(); let i_start = Instant::now();
let mut model = Qwen2_5VLGenerateModel::init(weight_path, None, None)?; let mut model = Qwen2_5VLGenerateModel::init(weight_path, None, None)?;
let i_duration = i_start.elapsed(); let i_duration = i_start.elapsed();
@@ -27,11 +39,22 @@ impl ExecModel for Qwen2_5vlExec {
"messages": [ "messages": [
{{ {{
"role": "user", "role": "user",
"content": "{}" "content": [
{{
"type": "image",
"image_url": {{
"url": "{}"
}}
}},
{{
"type": "text",
"text": "{}"
}}
]
}} }}
] ]
}}"#, }}"#,
target_text.replace('"', "\\\"") input_url, target_text
); );
let mes = serde_json::from_str(&message)?; let mes = serde_json::from_str(&message)?;
+11 -6
View File
@@ -1,19 +1,24 @@
//! Qwen3-0.6B exec implementation for CLI `run` subcommand //! Qwen3-0.6B exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, qwen3::generate::Qwen3GenerateModel}; use crate::models::{GenerateModel, qwen3::generate::Qwen3GenerateModel};
use anyhow::{Ok, Result}; use crate::utils::get_file_path;
use std::time::Instant;
pub struct Qwen3Exec; pub struct Qwen3Exec;
impl ExecModel for Qwen3Exec { impl ExecModel for Qwen3Exec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
// let path = &input[7..];
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
+62 -12
View File
@@ -1,38 +1,88 @@
//! Qwen3VL-2B exec implementation for CLI `run` subcommand //! Qwen3VL-2B exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::{GenerateModel, qwen3vl::generate::Qwen3VLGenerateModel}; use crate::models::{GenerateModel, qwen3vl::generate::Qwen3VLGenerateModel};
use anyhow::{Ok, Result}; use crate::utils::get_file_path;
use std::time::Instant;
pub struct Qwen3vlExec; pub struct Qwen3vlExec;
impl ExecModel for Qwen3vlExec { impl ExecModel for Qwen3vlExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
let mut model = Qwen3VLGenerateModel::init(weight_path, None, None)?; let mut model = Qwen3VLGenerateModel::init(weight_path, None, None)?;
let i_duration = i_start.elapsed(); let i_duration = i_start.elapsed();
println!("Time elapsed in load model is: {:?}", i_duration); println!("Time elapsed in load model is: {:?}", i_duration);
let url = &input[1];
let message = format!( let input_url = if url.starts_with("http://")
r#"{{ || url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else {
format!("file://{}", url)
};
let message = if input_url.ends_with("mp4") {
format!(
r#"{{
"model": "qwen3vl", "model": "qwen3vl",
"messages": [ "messages": [
{{ {{
"role": "user", "role": "user",
"content": "{}" "content": [
{{
"type": "video",
"video_url":
{{
"url": "{}"
}}
}},
{{
"type": "text",
"text": "{}"
}}
]
}} }}
] ]
}}"#, }}"#,
target_text.replace('"', "\\\"") input_url, target_text
); )
} else {
format!(
r#"{{
"model": "qwen2.5vl",
"messages": [
{{
"role": "user",
"content": [
{{
"type": "image",
"image_url": {{
"url": "{}"
}}
}},
{{
"type": "text",
"text": "{}"
}}
]
}}
]
}}"#,
input_url, target_text
)
};
let mes = serde_json::from_str(&message)?; let mes = serde_json::from_str(&message)?;
let i_start = Instant::now(); let i_start = Instant::now();
+13 -7
View File
@@ -1,18 +1,24 @@
//! RMBG2.0 exec implementation for CLI `run` subcommand //! RMBG2.0 exec implementation for CLI `run` subcommand
use std::time::Instant;
use anyhow::{Ok, Result};
use crate::exec::ExecModel; use crate::exec::ExecModel;
use crate::models::rmbg2_0::generate::RMBG2_0Model; use crate::models::rmbg2_0::generate::RMBG2_0Model;
use anyhow::{Ok, Result};
use std::time::Instant;
pub struct RMBG2_0Exec; pub struct RMBG2_0Exec;
impl ExecModel for RMBG2_0Exec { impl ExecModel for RMBG2_0Exec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let input_path = if input.starts_with("file://") { let url = &input[0];
input.to_string() let input_url = if url.starts_with("http://")
|| url.starts_with("https://")
|| url.starts_with("file://")
{
url.clone()
} else { } else {
format!("file://{}", input) format!("file://{}", url)
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -38,7 +44,7 @@ impl ExecModel for RMBG2_0Exec {
}} }}
] ]
}}"#, }}"#,
input_path input_url
); );
let mes = serde_json::from_str(&message)?; let mes = serde_json::from_str(&message)?;
+14 -10
View File
@@ -1,19 +1,23 @@
//! VoxCPM exec implementation for CLI `run` subcommand //! VoxCPM exec implementation for CLI `run` subcommand
use crate::exec::ExecModel;
use crate::models::voxcpm::generate::VoxCPMGenerate;
use anyhow::{Ok, Result};
use std::time::Instant; use std::time::Instant;
use anyhow::{Ok, Result};
use crate::models::voxcpm::generate::VoxCPMGenerate;
use crate::{exec::ExecModel, utils::get_file_path};
pub struct VoxCPMExec; pub struct VoxCPMExec;
impl ExecModel for VoxCPMExec { impl ExecModel for VoxCPMExec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
// let path = &input[7..];
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -24,10 +28,10 @@ impl ExecModel for VoxCPMExec {
let i_start = Instant::now(); let i_start = Instant::now();
let audio = voxcpm_generate.inference( let audio = voxcpm_generate.inference(
target_text, target_text,
Some("啥子小师叔,打狗还要看主人,你再要继续,我就是你的对手".to_string()), //todo args Some("啥子小师叔,打狗还要看主人,你再要继续,我就是你的对手".to_string()), // todo args
Some("file://./assets/audio/voice_01.wav".to_string()), //todo args Some("file://./assets/audio/voice_01.wav".to_string()), // todo args
2, 2,
100, // max_len (voxcpm uses 100 vs voxcpm1.5's 4096) 100, // max_len (voxcpm uses 100 vs voxcpm1.5's 4096)
10, 10,
2.0, 2.0,
6.0, 6.0,
+13 -9
View File
@@ -5,20 +5,24 @@
//! - Input can be text content or a file path (with `file://` prefix) //! - Input can be text content or a file path (with `file://` prefix)
//! - Output can be a file path or will be auto-generated if not specified //! - Output can be a file path or will be auto-generated if not specified
use crate::exec::ExecModel;
use crate::models::voxcpm::generate::VoxCPMGenerate;
use anyhow::{Ok, Result};
use std::time::Instant; use std::time::Instant;
use anyhow::{Ok, Result};
use crate::models::voxcpm::generate::VoxCPMGenerate;
use crate::{exec::ExecModel, utils::get_file_path};
pub struct VoxCPM1_5Exec; pub struct VoxCPM1_5Exec;
impl ExecModel for VoxCPM1_5Exec { impl ExecModel for VoxCPM1_5Exec {
fn run(input: &str, output: Option<&str>, weight_path: &str) -> Result<()> { fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
let target_text = if input.starts_with("file://") { let input_text = &input[0];
let path = &input[7..]; let target_text = if input_text.starts_with("file://") {
// let path = &input[7..];
let path = get_file_path(input_text)?;
std::fs::read_to_string(path)? std::fs::read_to_string(path)?
} else { } else {
input.to_string() input_text.clone()
}; };
let i_start = Instant::now(); let i_start = Instant::now();
@@ -29,8 +33,8 @@ impl ExecModel for VoxCPM1_5Exec {
let i_start = Instant::now(); let i_start = Instant::now();
let audio = voxcpm_generate.inference( let audio = voxcpm_generate.inference(
target_text, target_text,
Some("啥子小师叔,打狗还要看主人,你再要继续,我就是你的对手".to_string()), //todo args Some("啥子小师叔,打狗还要看主人,你再要继续,我就是你的对手".to_string()), // todo args
Some("file://./assets/audio/voice_01.wav".to_string()), //todo args Some("file://./assets/audio/voice_01.wav".to_string()), // todo args
2, 2,
4096, 4096,
10, 10,
+25 -8
View File
@@ -129,8 +129,8 @@ struct RunArgs {
model: WhichModel, model: WhichModel,
/// Input text or file path /// Input text or file path
#[arg(short, long)] #[arg(short, long, num_args = 1..=2, value_delimiter = ' ')]
input: String, input: Vec<String>,
/// Output file path (optional) /// Output file path (optional)
#[arg(short, long)] #[arg(short, long)]
@@ -219,7 +219,7 @@ fn run_list() -> anyhow::Result<()> {
println!("Available models:"); println!("Available models:");
println!(); println!();
println!("{:<30} {}", "Model Name", "ModelScope ID"); println!("{:<30} ModelScope ID", "Model Name");
println!("{}", "-".repeat(80)); println!("{}", "-".repeat(80));
for model in models { for model in models {
let possible_value = model.to_possible_value().unwrap(); let possible_value = model.to_possible_value().unwrap();
@@ -233,7 +233,12 @@ fn run_list() -> anyhow::Result<()> {
/// Run the 'cli' subcommand: download model (if needed) and start service /// Run the 'cli' subcommand: download model (if needed) and start service
async fn run_cli(args: CliArgs) -> anyhow::Result<()> { async fn run_cli(args: CliArgs) -> anyhow::Result<()> {
let CliArgs { common, weight_path, save_dir, download_retries } = args; let CliArgs {
common,
weight_path,
save_dir,
download_retries,
} = args;
let model_id = get_model_id(common.model); let model_id = get_model_id(common.model);
let model_path = match weight_path { let model_path = match weight_path {
@@ -257,7 +262,10 @@ async fn run_cli(args: CliArgs) -> anyhow::Result<()> {
/// Run the 'serv' subcommand: start service only (no download) /// Run the 'serv' subcommand: start service only (no download)
async fn run_serv(args: ServArgs) -> anyhow::Result<()> { async fn run_serv(args: ServArgs) -> anyhow::Result<()> {
let ServArgs { common, weight_path } = args; let ServArgs {
common,
weight_path,
} = args;
init(common.model, weight_path)?; init(common.model, weight_path)?;
start_http_server(common.address, common.port).await?; start_http_server(common.address, common.port).await?;
@@ -267,7 +275,11 @@ async fn run_serv(args: ServArgs) -> anyhow::Result<()> {
/// Run the 'download' subcommand: download model only (no server) /// Run the 'download' subcommand: download model only (no server)
async fn run_download(args: DownloadArgs) -> anyhow::Result<()> { async fn run_download(args: DownloadArgs) -> anyhow::Result<()> {
let DownloadArgs { model, save_dir, download_retries } = args; let DownloadArgs {
model,
save_dir,
download_retries,
} = args;
let model_id = get_model_id(model); let model_id = get_model_id(model);
let save_dir = match save_dir { let save_dir = match save_dir {
@@ -285,7 +297,12 @@ async fn run_download(args: DownloadArgs) -> anyhow::Result<()> {
fn run_run(args: RunArgs) -> anyhow::Result<()> { fn run_run(args: RunArgs) -> anyhow::Result<()> {
use aha::exec::ExecModel; use aha::exec::ExecModel;
let RunArgs { model, input, output, weight_path } = args; let RunArgs {
model,
input,
output,
weight_path,
} = args;
match model { match model {
WhichModel::MiniCPM4_0_5B => { WhichModel::MiniCPM4_0_5B => {
@@ -405,4 +422,4 @@ pub(crate) async fn start_http_server(address: String, port: u16) -> anyhow::Res
builder.launch().await?; builder.launch().await?;
Ok(()) Ok(())
} }
+15 -1
View File
@@ -3,7 +3,7 @@ pub mod img_utils;
pub mod tensor_utils; pub mod tensor_utils;
pub mod video_utils; pub mod video_utils;
use std::{fs, process::Command}; use std::{fs, path::PathBuf, process::Command};
use aha_openai_dive::v1::resources::{ use aha_openai_dive::v1::resources::{
chat::{ chat::{
@@ -490,3 +490,17 @@ pub fn get_default_save_dir() -> Option<String> {
path.to_string_lossy().to_string() path.to_string_lossy().to_string()
}) })
} }
pub fn get_file_path(file: &str) -> Result<PathBuf> {
let path = url::Url::parse(file)?;
let path = path.to_file_path();
let path = match path {
Ok(path) => path,
Err(_) => {
let mut path = file.to_owned();
path = path.split_off(7);
PathBuf::from(path)
}
};
Ok(path)
}