From 0ef910987669aaeb28e5e156eeca1e9c6def54d3 Mon Sep 17 00:00:00 2001 From: Brummel Date: Tue, 14 Apr 2026 11:00:52 +0200 Subject: [PATCH] Add Whisper user settings to config Introduce a new struct `WhisperUserSettings` to hold per-user transcription settings. This allows users to customize Whisper transcription for their specific needs directly in the `users.toml` configuration file. The changes include: - Defining `WhisperUserSettings` with optional fields for `language`, `hotwords`, and `initial_prompt`. - Integrating `WhisperUserSettings` into the `User` struct, with `#[serde(default)]` to ensure it defaults to `WhisperUserSettings::default()` if not present in the TOML. - Adding new unit tests to verify that users can be parsed correctly with and without a `[user.whisper]` block. - Updating existing tests in `auth_test.rs`, `upload_test.rs`, and `web_test.rs` to include the new `whisper` field, ensuring backward compatibility. --- server/src/config.rs | 61 +++++++++++++++++++++++++++++++++++++ server/tests/auth_test.rs | 1 + server/tests/upload_test.rs | 1 + server/tests/web_test.rs | 1 + 4 files changed, 64 insertions(+) diff --git a/server/src/config.rs b/server/src/config.rs index c604f73..3e43b1d 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -3,6 +3,15 @@ use std::path::PathBuf; use serde::Deserialize; +/// Per-user Whisper transcription settings from users.toml. +/// All fields optional — missing `[user.whisper]` block yields defaults (None). +#[derive(Debug, Default, Clone, Deserialize)] +pub struct WhisperUserSettings { + pub language: Option, + pub hotwords: Option, + pub initial_prompt: Option, +} + /// A single user entry from users.toml. #[derive(Debug, Deserialize)] pub struct User { @@ -10,6 +19,8 @@ pub struct User { pub api_key: String, pub web_password: String, pub role: String, + #[serde(default)] + pub whisper: WhisperUserSettings, } /// Wrapper for deserializing the [[user]] array from TOML. @@ -158,9 +169,59 @@ mod tests { api_key: api_key.into(), web_password: String::new(), role: "doctor".into(), + whisper: WhisperUserSettings::default(), } } + #[test] + fn parses_user_without_whisper_block() { + let toml_str = r#" + [[user]] + slug = "a" + api_key = "k1" + web_password = "" + role = "doctor" + "#; + let parsed: UsersFile = toml::from_str(toml_str).unwrap(); + let u = &parsed.user[0]; + assert!(u.whisper.language.is_none()); + assert!(u.whisper.hotwords.is_none()); + assert!(u.whisper.initial_prompt.is_none()); + } + + #[test] + fn parses_user_with_full_whisper_block() { + let toml_str = r#" + [[user]] + slug = "a" + api_key = "k1" + web_password = "" + role = "doctor" + [user.whisper] + language = "de" + hotwords = "HOCM Valsalva" + initial_prompt = "Kardiologie" + "#; + let parsed: UsersFile = toml::from_str(toml_str).unwrap(); + let w = &parsed.user[0].whisper; + assert_eq!(w.language.as_deref(), Some("de")); + assert_eq!(w.hotwords.as_deref(), Some("HOCM Valsalva")); + assert_eq!(w.initial_prompt.as_deref(), Some("Kardiologie")); + } + + #[test] + fn accepts_admin_role() { + let toml_str = r#" + [[user]] + slug = "root" + api_key = "k1" + web_password = "" + role = "admin" + "#; + let parsed: UsersFile = toml::from_str(toml_str).unwrap(); + assert_eq!(parsed.user[0].role, "admin"); + } + #[test] fn accepts_distinct_users() { let users = vec![u("a", "k1"), u("b", "k2")]; diff --git a/server/tests/auth_test.rs b/server/tests/auth_test.rs index ea4ae6a..2a4cba3 100644 --- a/server/tests/auth_test.rs +++ b/server/tests/auth_test.rs @@ -19,6 +19,7 @@ fn test_config() -> Arc { api_key: "test-key-123".into(), web_password: "unused".into(), role: "doctor".into(), + whisper: Default::default(), }], api_keys: HashMap::from([("test-key-123".into(), "dr_test".into())]), retention_audio_days: 30, diff --git a/server/tests/upload_test.rs b/server/tests/upload_test.rs index 7c834ea..afa0664 100644 --- a/server/tests/upload_test.rs +++ b/server/tests/upload_test.rs @@ -24,6 +24,7 @@ fn test_config() -> Arc { api_key: "test-key-123".into(), web_password: "unused".into(), role: "doctor".into(), + whisper: Default::default(), }], api_keys: HashMap::from([("test-key-123".into(), "dr_test".into())]), retention_audio_days: 30, diff --git a/server/tests/web_test.rs b/server/tests/web_test.rs index dab070b..a9a36bd 100644 --- a/server/tests/web_test.rs +++ b/server/tests/web_test.rs @@ -24,6 +24,7 @@ fn test_config() -> Arc { api_key: "test-key".into(), web_password: "unused".into(), role: "doctor".into(), + whisper: Default::default(), }], api_keys: HashMap::from([("test-key".into(), "dr_test".into())]), retention_audio_days: 30,