diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-18 23:18:37 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-18 23:18:37 +0200 |
| commit | b252c39517b9fce181d1a0fe4761404c7d981397 (patch) | |
| tree | 564cf01aed601f1974e0de8801d4452215ec05f5 /src/config.rs | |
| parent | fir auto completion and send file in the response body upon grajcuj file (diff) | |
| download | senator-sperma-b252c39517b9fce181d1a0fe4761404c7d981397.tar.gz senator-sperma-b252c39517b9fce181d1a0fe4761404c7d981397.zip | |
feat: local TTS via piper subprocess with Google fallback
- New src/tts.rs: centralize TTS in synthesize(config, http_client, text)
Piper subprocess primary, Google Translate fallback on error/missing
- TTS_MODEL_PATH/TTS_VOICE env config (defaults to pl_PL-bass-high)
- Fix Unknown interaction errors: spawn play_tts_in_channel in background
so voice-state TTS no longer blocks the shard event loop (3s Discord limit)
- Best-effort confirmation replies in /tts to prevent error cascade
- Custom on_error handler: graceful interaction-expired error handling
- Comprehensive tracing logging (engine, model, duration, stderr)
Diffstat (limited to '')
| -rw-r--r-- | src/config.rs | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/src/config.rs b/src/config.rs index 989a060..3b92dd8 100644 --- a/src/config.rs +++ b/src/config.rs | |||
| @@ -18,6 +18,12 @@ pub struct Config { | |||
| 18 | pub llm_model: Option<String>, | 18 | pub llm_model: Option<String>, |
| 19 | pub llm_api_url: String, | 19 | pub llm_api_url: String, |
| 20 | pub database_path: String, | 20 | pub database_path: String, |
| 21 | /// Optional path to the Piper TTS `.onnx` model (or a directory of models). | ||
| 22 | /// When unset, TTS falls back to Google Translate TTS. | ||
| 23 | pub tts_model_path: Option<String>, | ||
| 24 | /// Piper voice name, used to resolve the model file when `tts_model_path` | ||
| 25 | /// points at a directory. Ignored when it points at a `.onnx` file directly. | ||
| 26 | pub tts_voice: String, | ||
| 21 | } | 27 | } |
| 22 | 28 | ||
| 23 | struct EnvVar { | 29 | struct EnvVar { |
| @@ -101,6 +107,18 @@ const ENV_VARS: &[EnvVar] = &[ | |||
| 101 | description: "SQLite database path", | 107 | description: "SQLite database path", |
| 102 | }, | 108 | }, |
| 103 | EnvVar { | 109 | EnvVar { |
| 110 | name: "TTS_MODEL_PATH", | ||
| 111 | required: false, | ||
| 112 | default: None, | ||
| 113 | description: "Path to a Piper TTS .onnx model (or a models directory). Unset => Google TTS fallback", | ||
| 114 | }, | ||
| 115 | EnvVar { | ||
| 116 | name: "TTS_VOICE", | ||
| 117 | required: false, | ||
| 118 | default: Some("pl_PL-bass-high"), | ||
| 119 | description: "Piper TTS voice name (used when TTS_MODEL_PATH points at a directory)", | ||
| 120 | }, | ||
| 121 | EnvVar { | ||
| 104 | name: "RUST_LOG", | 122 | name: "RUST_LOG", |
| 105 | required: false, | 123 | required: false, |
| 106 | default: Some("info"), | 124 | default: Some("info"), |
| @@ -180,6 +198,23 @@ impl Config { | |||
| 180 | llm_api_url: env::var("LLM_API_URL") | 198 | llm_api_url: env::var("LLM_API_URL") |
| 181 | .unwrap_or_else(|_| "https://openrouter.ai/api/v1/chat/completions".into()), | 199 | .unwrap_or_else(|_| "https://openrouter.ai/api/v1/chat/completions".into()), |
| 182 | database_path: env::var("SENATOR_DB_PATH").unwrap_or_else(|_| "data/senator.db".into()), | 200 | database_path: env::var("SENATOR_DB_PATH").unwrap_or_else(|_| "data/senator.db".into()), |
| 201 | tts_model_path: env::var("TTS_MODEL_PATH").ok(), | ||
| 202 | tts_voice: env::var("TTS_VOICE").unwrap_or_else(|_| "pl_PL-bass-high".into()), | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Resolve the Piper model path to pass to the `piper` CLI. | ||
| 207 | /// | ||
| 208 | /// - `TTS_MODEL_PATH` set to a `.onnx` file => used verbatim. | ||
| 209 | /// - `TTS_MODEL_PATH` set to a directory => `{dir}/{TTS_VOICE}.onnx`. | ||
| 210 | /// - `TTS_MODEL_PATH` unset => `None` (caller falls back to Google TTS). | ||
| 211 | pub fn piper_model_path(&self) -> Option<std::path::PathBuf> { | ||
| 212 | let configured = self.tts_model_path.as_ref()?; | ||
| 213 | let path = std::path::PathBuf::from(configured); | ||
| 214 | if path.is_dir() { | ||
| 215 | Some(path.join(format!("{}.onnx", self.tts_voice))) | ||
| 216 | } else { | ||
| 217 | Some(path) | ||
| 183 | } | 218 | } |
| 184 | } | 219 | } |
| 185 | 220 | ||
