From 7f77febb934a983922740de7cdeea1c8bc55b2c7 Mon Sep 17 00:00:00 2001 From: will wade Date: Sun, 16 Aug 2026 10:29:05 +0000 Subject: [PATCH 1/2] fix(sherpaonnx): accept numeric values in credentials JSON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The strict HashMap parse silently discarded the entire credentials object when any value was a JSON number (e.g. {"numThreads": 2}), leaving every option at its default with no error — VoiceGarden-SPD hit this shipping numThreads as a number. Coerce numbers to their string form instead. --- src/sherpaonnx_engine.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/sherpaonnx_engine.rs b/src/sherpaonnx_engine.rs index dc36516..5c036bb 100644 --- a/src/sherpaonnx_engine.rs +++ b/src/sherpaonnx_engine.rs @@ -128,7 +128,21 @@ impl SherpaOnnxEngine { let mut provider: Option = None; if !credentials_json.is_empty() { - if let Ok(creds) = serde_json::from_str::>(credentials_json) { + // Numeric values are coerced to strings so callers can write + // `{"numThreads": 2}` (JSON number) or `"2"` interchangeably — + // a strict HashMap parse would silently drop + // the whole object on the first number and leave every option + // at its default with no error. + if let Ok(creds) = + serde_json::from_str::>(credentials_json) + { + let creds: HashMap = creds + .into_iter() + .filter_map(|(k, v)| match v { + serde_json::Value::String(s) => Some((k, s)), + other => Some((k, other.to_string())), + }) + .collect(); if let Some(dir) = creds.get("modelPath") { model_dir = PathBuf::from(dir); } @@ -1739,6 +1753,18 @@ mod tests { assert_eq!(engine.loaded_model_id, "piper-en_US-amy-low"); } + #[test] + fn test_engine_construction_accepts_numeric_credential_values() { + // JSON numbers must be coerced to strings, not silently dropped + // (a strict HashMap parse rejects the whole object). + let engine = SherpaOnnxEngine::new( + r#"{"modelId":"piper-en_US-amy-low","numThreads":4,"numSteps":10}"#, + ); + assert_eq!(engine.loaded_model_id, "piper-en_US-amy-low"); + assert_eq!(engine.num_threads, 4); + assert_eq!(engine.num_steps, 10); + } + #[test] fn test_engine_speak_without_model_id_errors_clearly() { let engine = SherpaOnnxEngine::new(""); From a6fd7fcf5099670a6d619dffa9db33bc3f9ee260 Mon Sep 17 00:00:00 2001 From: will wade Date: Sun, 16 Aug 2026 10:29:44 +0000 Subject: [PATCH 2/2] style: appease clippy unnecessary_filter_map --- src/sherpaonnx_engine.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sherpaonnx_engine.rs b/src/sherpaonnx_engine.rs index 5c036bb..d3b0e82 100644 --- a/src/sherpaonnx_engine.rs +++ b/src/sherpaonnx_engine.rs @@ -138,9 +138,9 @@ impl SherpaOnnxEngine { { let creds: HashMap = creds .into_iter() - .filter_map(|(k, v)| match v { - serde_json::Value::String(s) => Some((k, s)), - other => Some((k, other.to_string())), + .map(|(k, v)| match v { + serde_json::Value::String(s) => (k, s), + other => (k, other.to_string()), }) .collect(); if let Some(dir) = creds.get("modelPath") {