fix: piper 二进制+依赖库+config 修正 (符号链接指向正确二进制,加 LD_LIBRARY_PATH 和 -c 参数)

This commit is contained in:
2026-07-04 16:58:41 +08:00
parent 706b1375ce
commit 4fd7e7997e
7 changed files with 62 additions and 13 deletions

1
.chat_req.json Normal file
View File

@@ -0,0 +1 @@
{"text":"hello","session_id":"e2e"}

View File

@@ -1167,6 +1167,8 @@
}, },
"ai": { "ai": {
"backend": "local", "backend": "local",
"whisper_lib_dir": "/home/showen/ai_spike/whisper.cpp/build/bin" "whisper_lib_dir": "/home/showen/ai_spike/whisper.cpp/build/bin",
"piper_lib_dir": "/home/showen/ai_spike/piper",
"piper_config": "/home/showen/ai_spike/piper/zh_CN-huayan-medium.onnx.json"
} }
} }

View File

@@ -1179,6 +1179,8 @@
}, },
"ai": { "ai": {
"backend": "local", "backend": "local",
"whisper_lib_dir": "/home/showen/ai_spike/whisper.cpp/build/bin" "whisper_lib_dir": "/home/showen/ai_spike/whisper.cpp/build/bin",
"piper_lib_dir": "/home/showen/ai_spike/piper",
"piper_config": "/home/showen/ai_spike/piper/zh_CN-huayan-medium.onnx.json"
} }
} }

View File

@@ -384,6 +384,12 @@ pub struct AiConfig {
/// whisper-cli 依赖库路径 (libggml*.so 所在,部署时标定) /// whisper-cli 依赖库路径 (libggml*.so 所在,部署时标定)
#[serde(default)] #[serde(default)]
pub whisper_lib_dir: Option<PathBuf>, 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")] #[serde(default = "default_tmp_dir")]
pub tmp_dir: PathBuf, pub tmp_dir: PathBuf,

View File

@@ -50,6 +50,8 @@ fn main() -> Result<()> {
let ai_model_store = config.ai.model_store.clone(); let ai_model_store = config.ai.model_store.clone();
let ai_tools_dir = config.ai.tools_dir.clone(); let ai_tools_dir = config.ai.tools_dir.clone();
let ai_whisper_lib_dir = config.ai.whisper_lib_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_tmp_dir = config.ai.tmp_dir.clone();
let ai_context_window = config.ai.context_window; let ai_context_window = config.ai.context_window;
@@ -81,6 +83,8 @@ fn main() -> Result<()> {
ai_model_store, ai_model_store,
ai_tools_dir, ai_tools_dir,
ai_whisper_lib_dir, ai_whisper_lib_dir,
ai_piper_lib_dir,
ai_piper_config,
ai_tmp_dir, ai_tmp_dir,
ai_context_window, ai_context_window,
); );

View File

@@ -48,6 +48,10 @@ pub struct LocalCliBackend {
tools_dir: PathBuf, tools_dir: PathBuf,
/// whisper-cli 依赖的库路径libggml*.so 所在) /// whisper-cli 依赖的库路径libggml*.so 所在)
whisper_lib_dir: Option<PathBuf>, 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 { impl LocalCliBackend {
@@ -55,6 +59,8 @@ impl LocalCliBackend {
Self { Self {
tools_dir, tools_dir,
whisper_lib_dir: None, whisper_lib_dir: None,
piper_lib_dir: None,
piper_config: None,
} }
} }
@@ -64,6 +70,13 @@ impl LocalCliBackend {
self 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 { fn whisper_cli(&self) -> PathBuf {
self.tools_dir.join("whisper-cli") self.tools_dir.join("whisper-cli")
} }
@@ -76,21 +89,26 @@ impl LocalCliBackend {
self.tools_dir.join("piper") self.tools_dir.join("piper")
} }
/// 给命令设置 LD_LIBRARY_PATHwhisper-cli 依赖 libggml*.so /// 给命令设置 LD_LIBRARY_PATH合并所有库路径
fn with_env(&self, cmd: &mut Command) { fn with_lib_path(&self, cmd: &mut Command, lib_dir: &Path) {
if let Some(lib_dir) = &self.whisper_lib_dir { let existing = std::env::var("LD_LIBRARY_PATH").unwrap_or_default();
cmd.env("LD_LIBRARY_PATH", lib_dir); 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 { impl AiBackend for LocalCliBackend {
fn asr(&self, audio_path: &Path, model_path: &Path) -> Result<String> { fn asr(&self, audio_path: &Path, model_path: &Path) -> Result<String> {
// whisper-cli -m <model> -f <audio> -l zh --no-timestamps -otxt -of <tmp> // 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 tmp_out = audio_path.with_extension("asr.txt");
let mut cmd = Command::new(self.whisper_cli()); 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 let output = cmd
.arg("-m") .arg("-m")
.arg(model_path) .arg(model_path)
@@ -178,15 +196,25 @@ impl AiBackend for LocalCliBackend {
} }
fn tts(&self, text: &str, model_path: &Path, output_path: &Path) -> Result<()> { 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()); 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") cmd.arg("-m")
.arg(model_path) .arg(model_path)
.arg("-f") .arg("-f")
.arg(output_path) .arg(output_path)
.arg("--output_format") .arg("--output_format")
.arg("wav") .arg("wav");
.stdin(std::process::Stdio::piped())
// 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()) .stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped()); .stderr(std::process::Stdio::piped());
@@ -199,7 +227,8 @@ impl AiBackend for LocalCliBackend {
if !output.status.success() { if !output.status.success() {
bail!( bail!(
"piper 失败: {}", "piper 失败 (exit={}): {}",
output.status,
String::from_utf8_lossy(&output.stderr) String::from_utf8_lossy(&output.stderr)
); );
} }

View File

@@ -48,6 +48,8 @@ impl AiPlugin {
model_store: PathBuf, model_store: PathBuf,
tools_dir: PathBuf, tools_dir: PathBuf,
whisper_lib_dir: Option<PathBuf>, whisper_lib_dir: Option<PathBuf>,
piper_lib_dir: Option<PathBuf>,
piper_config: Option<PathBuf>,
tmp_dir: PathBuf, tmp_dir: PathBuf,
context_window: usize, context_window: usize,
) -> Self { ) -> Self {
@@ -55,6 +57,9 @@ impl AiPlugin {
if let Some(lib_dir) = whisper_lib_dir { if let Some(lib_dir) = whisper_lib_dir {
backend_builder = backend_builder.with_whisper_lib_dir(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 backend = Box::new(backend_builder);
let models = model_manager::ModelManager::new(model_store); let models = model_manager::ModelManager::new(model_store);
let pipeline = ChatPipeline::new(backend, models, tmp_dir, context_window); let pipeline = ChatPipeline::new(backend, models, tmp_dir, context_window);