summarylogtreecommitdiffstats
path: root/feature-local-stt.patch
blob: 46db929b0664136024ac51c25716bcbb4ff982f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
diff --git a/crates/librefang-runtime-media/src/media_understanding.rs b/crates/librefang-runtime-media/src/media_understanding.rs
index da809fd77..887275d9a 100644
--- a/crates/librefang-runtime-media/src/media_understanding.rs
+++ b/crates/librefang-runtime-media/src/media_understanding.rs
@@ -257,7 +257,8 @@ impl MediaEngine {
         let transcription = match provider {
             // Whisper-compatible providers (OpenAI multipart protocol)
             "groq" | "openai" | "minimax" | "fireworks" | "together" | "siliconflow" => {
-                let (api_url, api_key) = whisper_provider_config(provider)?;
+                let custom_url = self.config.audio_base_url.as_deref();
+                let (api_url, api_key) = whisper_provider_config(provider, custom_url)?;
                 whisper_transcribe(&api_url, &api_key, model, audio_bytes, &filename, &mime).await?
             }
             // Gemini — multimodal content generation with audio input
@@ -626,32 +627,44 @@ async fn gemini_describe_image(
 // ── STT provider helpers ──────────────────────────────────────────────
 
 /// Resolve Whisper-compatible API URL and key for a provider.
-fn whisper_provider_config(provider: &str) -> Result<(String, String), String> {
+fn whisper_provider_config(provider: &str, custom_base_url: Option<&str>) -> Result<(String, String), String> {
     match provider {
         "groq" => Ok((
-            "https://api.groq.com/openai/v1/audio/transcriptions".into(),
+            custom_base_url
+                .map(|u| format!("{}/audio/transcriptions", u.trim_end_matches('/')))
+                .unwrap_or_else(|| "https://api.groq.com/openai/v1/audio/transcriptions".into()),
             std::env::var("GROQ_API_KEY").map_err(|_| "GROQ_API_KEY not set")?,
         )),
         "openai" => Ok((
-            "https://api.openai.com/v1/audio/transcriptions".into(),
+            custom_base_url
+                .map(|u| format!("{}/audio/transcriptions", u.trim_end_matches('/')))
+                .unwrap_or_else(|| "https://api.openai.com/v1/audio/transcriptions".into()),
             std::env::var("OPENAI_API_KEY").map_err(|_| "OPENAI_API_KEY not set")?,
         )),
         "minimax" => Ok((
-            "https://api.minimax.io/v1/audio/transcriptions".into(),
+            custom_base_url
+                .map(|u| format!("{}/audio/transcriptions", u.trim_end_matches('/')))
+                .unwrap_or_else(|| "https://api.minimax.io/v1/audio/transcriptions".into()),
             std::env::var("MINIMAX_API_KEY")
                 .or_else(|_| std::env::var("MINIMAX_CN_API_KEY"))
                 .map_err(|_| "MINIMAX_API_KEY not set")?,
         )),
         "fireworks" => Ok((
-            "https://api.fireworks.ai/inference/v1/audio/transcriptions".into(),
+            custom_base_url
+                .map(|u| format!("{}/audio/transcriptions", u.trim_end_matches('/')))
+                .unwrap_or_else(|| "https://api.fireworks.ai/inference/v1/audio/transcriptions".into()),
             std::env::var("FIREWORKS_API_KEY").map_err(|_| "FIREWORKS_API_KEY not set")?,
         )),
         "together" => Ok((
-            "https://api.together.xyz/v1/audio/transcriptions".into(),
+            custom_base_url
+                .map(|u| format!("{}/audio/transcriptions", u.trim_end_matches('/')))
+                .unwrap_or_else(|| "https://api.together.xyz/v1/audio/transcriptions".into()),
             std::env::var("TOGETHER_API_KEY").map_err(|_| "TOGETHER_API_KEY not set")?,
         )),
         "siliconflow" => Ok((
-            "https://api.siliconflow.cn/v1/audio/transcriptions".into(),
+            custom_base_url
+                .map(|u| format!("{}/audio/transcriptions", u.trim_end_matches('/')))
+                .unwrap_or_else(|| "https://api.siliconflow.cn/v1/audio/transcriptions".into()),
             std::env::var("SILICONFLOW_API_KEY").map_err(|_| "SILICONFLOW_API_KEY not set")?,
         )),
         other => Err(format!("Unknown Whisper-compatible provider: {other}")),
diff --git a/crates/librefang-types/src/media.rs b/crates/librefang-types/src/media.rs
index db7fb8411..4223a6058 100644
--- a/crates/librefang-types/src/media.rs
+++ b/crates/librefang-types/src/media.rs
@@ -124,6 +124,8 @@ pub struct MediaConfig {
     pub audio_provider: Option<String>,
     /// Preferred audio transcription model (provider default if None).
     pub audio_model: Option<String>,
+    /// Custom base URL override for transcription.
+    pub audio_base_url: Option<String>,
     /// Custom / self-hosted STT endpoint configuration.
     ///
     /// When `audio_provider` is set to a name that is not one of the
@@ -146,6 +148,7 @@ impl Default for MediaConfig {
             image_model: None,
             audio_provider: None,
             audio_model: None,
+            audio_base_url: None,
             custom_stt: CustomSttConfig::default(),
         }
     }