Add per-user whisper settings for hotwords and initial prompt
This commit introduces the capability to configure user-specific Whisper settings, including language, hotwords, and initial prompts. These settings are stored in `users.toml` and are passed to the Whisper service for more tailored transcription results. New unit tests have been added to verify that the `transcribe` function correctly forwards these optional settings to the Whisper client and omits them when they are not provided. Additionally, a new directory `tests/fixtures/dictations` has been created to store M4A audio files, their expected transcripts, and associated hotword files. This serves as a regression corpus for the Whisper pipeline. A README file explains the structure and conventions for adding new fixtures. Several new fixtures have been added, covering various medical domains and transcription scenarios.
This commit is contained in:
@@ -135,6 +135,76 @@ async fn whisper_client_times_out() {
|
||||
assert!(matches!(err, WhisperError::Http(_)), "expected Http error, got {err:?}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn whisper_client_forwards_hotwords_and_prompt_when_set() {
|
||||
let server = MockServer::start().await;
|
||||
|
||||
// Accept any POST; inspect captured body below. language query param is
|
||||
// still matched strictly to verify override from settings.
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/asr"))
|
||||
.and(query_param("language", "en"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_string("ok"))
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let settings = WhisperUserSettings {
|
||||
language: Some("en".into()),
|
||||
hotwords: Some("HOCM Valsalva".into()),
|
||||
initial_prompt: Some("Kardiologie".into()),
|
||||
};
|
||||
transcribe(
|
||||
&client,
|
||||
&server.uri(),
|
||||
&fixture("sample.m4a"),
|
||||
Duration::from_secs(10),
|
||||
&settings,
|
||||
)
|
||||
.await
|
||||
.expect("transcribe failed");
|
||||
|
||||
let received = server.received_requests().await.unwrap();
|
||||
let body = String::from_utf8_lossy(&received[0].body);
|
||||
assert!(body.contains("name=\"hotwords\""), "hotwords part missing");
|
||||
assert!(body.contains("HOCM Valsalva"), "hotwords value missing: {body}");
|
||||
assert!(body.contains("name=\"initial_prompt\""), "initial_prompt part missing");
|
||||
assert!(body.contains("Kardiologie"), "initial_prompt value missing");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn whisper_client_omits_empty_optional_fields() {
|
||||
let server = MockServer::start().await;
|
||||
|
||||
// Default settings = all None → only `audio_file` part, language defaults
|
||||
// to `de`. Assert that neither optional field appears in the body.
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/asr"))
|
||||
.and(query_param("language", "de"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_string("ok"))
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
transcribe(
|
||||
&client,
|
||||
&server.uri(),
|
||||
&fixture("sample.m4a"),
|
||||
Duration::from_secs(10),
|
||||
&WhisperUserSettings::default(),
|
||||
)
|
||||
.await
|
||||
.expect("transcribe failed");
|
||||
|
||||
// Inspect the captured request to confirm absence of the optional parts.
|
||||
let received = server.received_requests().await.unwrap();
|
||||
let body = String::from_utf8_lossy(&received[0].body);
|
||||
assert!(!body.contains("name=\"hotwords\""), "hotwords part leaked: {body}");
|
||||
assert!(!body.contains("name=\"initial_prompt\""), "initial_prompt part leaked: {body}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn recovery_enqueues_only_pending_recordings() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user