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.
This commit is contained in:
2026-04-14 11:00:52 +02:00
parent c3d5e2b0ad
commit 0ef9109876
4 changed files with 64 additions and 0 deletions
+61
View File
@@ -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<String>,
pub hotwords: Option<String>,
pub initial_prompt: Option<String>,
}
/// 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")];
+1
View File
@@ -19,6 +19,7 @@ fn test_config() -> Arc<Config> {
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,
+1
View File
@@ -24,6 +24,7 @@ fn test_config() -> Arc<Config> {
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,
+1
View File
@@ -24,6 +24,7 @@ fn test_config() -> Arc<Config> {
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,