cognee_llm/
transcriber.rs1use async_trait::async_trait;
7
8use crate::error::{LlmError, LlmResult};
9
10const SUPPORTED_AUDIO_FORMATS: &[&str] = &["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"];
12
13pub fn validate_audio_format(format: &str) -> LlmResult<()> {
18 let lower = format.to_ascii_lowercase();
19 if SUPPORTED_AUDIO_FORMATS.contains(&lower.as_str()) {
20 Ok(())
21 } else {
22 Err(LlmError::InvalidAudioFormat(format.to_string()))
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct TranscriptionOutput {
29 pub text: String,
31 pub language: Option<String>,
33 pub duration: Option<f32>,
35}
36
37#[async_trait]
42pub trait Transcriber: Send + Sync {
43 async fn transcribe_audio(
51 &self,
52 audio: &[u8],
53 format: &str,
54 language_hint: Option<&str>,
55 prompt_hint: Option<&str>,
56 ) -> LlmResult<TranscriptionOutput>;
57
58 fn transcription_model(&self) -> &str;
60}
61
62#[cfg(test)]
63mod tests {
64 #![allow(
65 clippy::unwrap_used,
66 clippy::expect_used,
67 reason = "test code — panics are acceptable"
68 )]
69 use super::*;
70
71 #[test]
72 fn test_valid_formats() {
73 for fmt in &["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"] {
74 assert!(
75 validate_audio_format(fmt).is_ok(),
76 "Expected {fmt} to be valid"
77 );
78 }
79 }
80
81 #[test]
82 fn test_invalid_formats() {
83 for fmt in &["mid", "aiff", "amr", "ogg", "flac", "aac", "wma"] {
84 let result = validate_audio_format(fmt);
85 assert!(result.is_err(), "Expected {fmt} to be invalid");
86 assert!(
87 matches!(result.unwrap_err(), LlmError::InvalidAudioFormat(_)),
88 "Expected InvalidAudioFormat for {fmt}"
89 );
90 }
91 }
92
93 #[test]
94 fn test_format_case_insensitive() {
95 assert!(validate_audio_format("MP3").is_ok());
96 assert!(validate_audio_format("Mp3").is_ok());
97 assert!(validate_audio_format("WAV").is_ok());
98 assert!(validate_audio_format("WebM").is_ok());
99 }
100}