fix: piper 二进制+依赖库+config 修正 (符号链接指向正确二进制,加 LD_LIBRARY_PATH 和 -c 参数)
This commit is contained in:
@@ -384,6 +384,12 @@ pub struct AiConfig {
|
||||
/// whisper-cli 依赖库路径 (libggml*.so 所在,部署时标定)
|
||||
#[serde(default)]
|
||||
pub whisper_lib_dir: Option<PathBuf>,
|
||||
/// piper 依赖库路径 (libespeak-ng.so / libpiper_phonemize.so 所在)
|
||||
#[serde(default)]
|
||||
pub piper_lib_dir: Option<PathBuf>,
|
||||
/// piper 模型 config json 路径 (.onnx.json)
|
||||
#[serde(default)]
|
||||
pub piper_config: Option<PathBuf>,
|
||||
/// 临时文件目录
|
||||
#[serde(default = "default_tmp_dir")]
|
||||
pub tmp_dir: PathBuf,
|
||||
|
||||
@@ -50,6 +50,8 @@ fn main() -> Result<()> {
|
||||
let ai_model_store = config.ai.model_store.clone();
|
||||
let ai_tools_dir = config.ai.tools_dir.clone();
|
||||
let ai_whisper_lib_dir = config.ai.whisper_lib_dir.clone();
|
||||
let ai_piper_lib_dir = config.ai.piper_lib_dir.clone();
|
||||
let ai_piper_config = config.ai.piper_config.clone();
|
||||
let ai_tmp_dir = config.ai.tmp_dir.clone();
|
||||
let ai_context_window = config.ai.context_window;
|
||||
|
||||
@@ -81,6 +83,8 @@ fn main() -> Result<()> {
|
||||
ai_model_store,
|
||||
ai_tools_dir,
|
||||
ai_whisper_lib_dir,
|
||||
ai_piper_lib_dir,
|
||||
ai_piper_config,
|
||||
ai_tmp_dir,
|
||||
ai_context_window,
|
||||
);
|
||||
|
||||
@@ -48,6 +48,10 @@ pub struct LocalCliBackend {
|
||||
tools_dir: PathBuf,
|
||||
/// whisper-cli 依赖的库路径(libggml*.so 所在)
|
||||
whisper_lib_dir: Option<PathBuf>,
|
||||
/// piper 依赖的库路径(libespeak-ng.so / libpiper_phonemize.so 所在)
|
||||
piper_lib_dir: Option<PathBuf>,
|
||||
/// piper 模型 config json 路径(.onnx.json)
|
||||
piper_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl LocalCliBackend {
|
||||
@@ -55,6 +59,8 @@ impl LocalCliBackend {
|
||||
Self {
|
||||
tools_dir,
|
||||
whisper_lib_dir: None,
|
||||
piper_lib_dir: None,
|
||||
piper_config: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +70,13 @@ impl LocalCliBackend {
|
||||
self
|
||||
}
|
||||
|
||||
/// 设置 piper 依赖库路径和 config(部署时由 deploy_ai.sh 标定)
|
||||
pub fn with_piper_deps(mut self, lib_dir: PathBuf, config: PathBuf) -> Self {
|
||||
self.piper_lib_dir = Some(lib_dir);
|
||||
self.piper_config = Some(config);
|
||||
self
|
||||
}
|
||||
|
||||
fn whisper_cli(&self) -> PathBuf {
|
||||
self.tools_dir.join("whisper-cli")
|
||||
}
|
||||
@@ -76,21 +89,26 @@ impl LocalCliBackend {
|
||||
self.tools_dir.join("piper")
|
||||
}
|
||||
|
||||
/// 给命令设置 LD_LIBRARY_PATH(whisper-cli 依赖 libggml*.so)
|
||||
fn with_env(&self, cmd: &mut Command) {
|
||||
if let Some(lib_dir) = &self.whisper_lib_dir {
|
||||
cmd.env("LD_LIBRARY_PATH", lib_dir);
|
||||
}
|
||||
/// 给命令设置 LD_LIBRARY_PATH(合并所有库路径)
|
||||
fn with_lib_path(&self, cmd: &mut Command, lib_dir: &Path) {
|
||||
let existing = std::env::var("LD_LIBRARY_PATH").unwrap_or_default();
|
||||
let new_path = if existing.is_empty() {
|
||||
lib_dir.to_string_lossy().into_owned()
|
||||
} else {
|
||||
format!("{}:{}", lib_dir.to_string_lossy(), existing)
|
||||
};
|
||||
cmd.env("LD_LIBRARY_PATH", new_path);
|
||||
}
|
||||
}
|
||||
|
||||
impl AiBackend for LocalCliBackend {
|
||||
fn asr(&self, audio_path: &Path, model_path: &Path) -> Result<String> {
|
||||
// whisper-cli -m <model> -f <audio> -l zh --no-timestamps -otxt -of <tmp>
|
||||
// 输出到 <tmp>.txt,读取后返回
|
||||
let tmp_out = audio_path.with_extension("asr.txt");
|
||||
let mut cmd = Command::new(self.whisper_cli());
|
||||
self.with_env(&mut cmd);
|
||||
if let Some(lib_dir) = &self.whisper_lib_dir {
|
||||
self.with_lib_path(&mut cmd, lib_dir);
|
||||
}
|
||||
let output = cmd
|
||||
.arg("-m")
|
||||
.arg(model_path)
|
||||
@@ -178,15 +196,25 @@ impl AiBackend for LocalCliBackend {
|
||||
}
|
||||
|
||||
fn tts(&self, text: &str, model_path: &Path, output_path: &Path) -> Result<()> {
|
||||
// echo <text> | piper -m <model> -f <output> --output_format wav
|
||||
// echo <text> | piper -m <model> [-c <config>] -f <output> --output_format wav
|
||||
// piper 依赖 libespeak-ng.so / libpiper_phonemize.so,需设 LD_LIBRARY_PATH
|
||||
let mut cmd = Command::new(self.piper());
|
||||
if let Some(lib_dir) = &self.piper_lib_dir {
|
||||
self.with_lib_path(&mut cmd, lib_dir);
|
||||
}
|
||||
cmd.arg("-m")
|
||||
.arg(model_path)
|
||||
.arg("-f")
|
||||
.arg(output_path)
|
||||
.arg("--output_format")
|
||||
.arg("wav")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.arg("wav");
|
||||
|
||||
// piper 需要 -c 指定 .onnx.json config 文件
|
||||
if let Some(config) = &self.piper_config {
|
||||
cmd.arg("-c").arg(config);
|
||||
}
|
||||
|
||||
cmd.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::piped());
|
||||
|
||||
@@ -199,7 +227,8 @@ impl AiBackend for LocalCliBackend {
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"piper 失败: {}",
|
||||
"piper 失败 (exit={}): {}",
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ impl AiPlugin {
|
||||
model_store: PathBuf,
|
||||
tools_dir: PathBuf,
|
||||
whisper_lib_dir: Option<PathBuf>,
|
||||
piper_lib_dir: Option<PathBuf>,
|
||||
piper_config: Option<PathBuf>,
|
||||
tmp_dir: PathBuf,
|
||||
context_window: usize,
|
||||
) -> Self {
|
||||
@@ -55,6 +57,9 @@ impl AiPlugin {
|
||||
if let Some(lib_dir) = whisper_lib_dir {
|
||||
backend_builder = backend_builder.with_whisper_lib_dir(lib_dir);
|
||||
}
|
||||
if let (Some(lib_dir), Some(config)) = (piper_lib_dir, piper_config) {
|
||||
backend_builder = backend_builder.with_piper_deps(lib_dir, config);
|
||||
}
|
||||
let backend = Box::new(backend_builder);
|
||||
let models = model_manager::ModelManager::new(model_store);
|
||||
let pipeline = ChatPipeline::new(backend, models, tmp_dir, context_window);
|
||||
|
||||
Reference in New Issue
Block a user