-
Notifications
You must be signed in to change notification settings - Fork 3.8k
feat(channel): Add Mistral Voxtral support for voice transcription #2778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
305c40f
1d6bcd1
5031547
5b05f2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -42,6 +42,7 @@ const SUPPORTED_PROXY_SERVICE_KEYS: &[&str] = &[ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "memory.embeddings", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "tunnel.custom", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "transcription.groq", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "transcription.mistral", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const SUPPORTED_PROXY_SERVICE_SELECTORS: &[&str] = &[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -538,22 +539,50 @@ fn default_transcription_max_duration_secs() -> u64 { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 120 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Voice transcription configuration (Whisper API via Groq). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Voice transcription configuration (Groq Whisper default; also supports Mistral Voxtral endpoints). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// # Defaults | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// - `enabled`: `false` — transcription is opt-in. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// - `api_url`: `https://api.groq.com/openai/v1/audio/transcriptions` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// - `model`: `whisper-large-v3-turbo` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// # Compatibility | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Additive and backward-compatible for existing Groq configurations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// New providers may differ in supported models, encoding, or rate limits. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// # Migration / Rollback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// To switch to Mistral: set `api_url` to the Mistral endpoint, `model` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// to e.g. `voxtral-mini-latest`, and provide `api_key` or `MISTRAL_API_KEY`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// To revert: restore `api_url`/`model` to the Groq defaults above (or | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// remove the keys to use serde defaults). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Clone, Serialize, Deserialize, JsonSchema)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub struct TranscriptionConfig { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Enable voice transcription for channels that support it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Default: `false`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[serde(default)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub enabled: bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Whisper API endpoint URL. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// API key used for transcription requests. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// If unset, runtime falls back to `MISTRAL_API_KEY` (for Mistral | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// endpoints) or `GROQ_API_KEY` (all others). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[serde(default)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub api_key: Option<String>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Transcription API endpoint URL. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Default: `https://api.groq.com/openai/v1/audio/transcriptions`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[serde(default = "default_transcription_api_url")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub api_url: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Whisper model name. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Whisper or Voxtral model name (e.g. `whisper-large-v3-turbo`, `voxtral-mini-latest`). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Default: `whisper-large-v3-turbo`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[serde(default = "default_transcription_model")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub model: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Optional language hint (ISO-639-1, e.g. "en", "ru"). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[serde(default)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub language: Option<String>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Maximum voice duration in seconds (messages longer than this are skipped). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Default: `120`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[serde(default = "default_transcription_max_duration_secs")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub max_duration_secs: u64, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -562,6 +591,7 @@ impl Default for TranscriptionConfig { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn default() -> Self { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Self { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enabled: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_url: default_transcription_api_url(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: default_transcription_model(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| language: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -570,6 +600,26 @@ impl Default for TranscriptionConfig { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl std::fmt::Debug for TranscriptionConfig { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f.debug_struct("TranscriptionConfig") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .field("enabled", &self.enabled) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .field( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "api_key", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &if self.api_key.is_some() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some("<redacted>") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None::<&str> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .field("api_url", &self.api_url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .field("model", &self.model) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .field("language", &self.language) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .field("max_duration_secs", &self.max_duration_secs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .finish() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+603
to
+621
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redact or sanitize
🔒 Proposed fix impl std::fmt::Debug for TranscriptionConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TranscriptionConfig")
.field("enabled", &self.enabled)
.field(
"api_key",
&if self.api_key.is_some() {
Some("<redacted>")
} else {
None::<&str>
},
)
- .field("api_url", &self.api_url)
+ .field("api_url_configured", &!self.api_url.trim().is_empty())
.field("model", &self.model)
.field("language", &self.language)
.field("max_duration_secs", &self.max_duration_secs)
.finish()
}
}Based on learnings: "Never log secrets, raw tokens, or sensitive payloads". 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ── Agents IPC ────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn default_agents_ipc_db_path() -> String { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -5870,6 +5920,11 @@ impl Config { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut config.storage.provider.config.db_url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "config.storage.provider.config.db_url", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| decrypt_optional_secret( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &store, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut config.transcription.api_key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "config.transcription.api_key", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| decrypt_vec_secrets( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &store, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut config.reliability.api_keys, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -6739,6 +6794,11 @@ impl Config { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut config_to_save.storage.provider.config.db_url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "config.storage.provider.config.db_url", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encrypt_optional_secret( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &store, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut config_to_save.transcription.api_key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "config.transcription.api_key", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| encrypt_vec_secrets( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &store, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut config_to_save.reliability.api_keys, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -7781,6 +7841,8 @@ tool_dispatcher = "xml" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| config.transcription.api_key = Some("transcription-credential".into()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| config.save().await.unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let contents = tokio::fs::read_to_string(config.config_path.clone()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -7888,6 +7950,10 @@ tool_dispatcher = "xml" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "telegram-credential" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let transcription_key = stored.transcription.api_key.as_deref().unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert!(crate::security::SecretStore::is_encrypted(transcription_key)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(store.decrypt(transcription_key).unwrap(), "transcription-credential"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let _ = fs::remove_dir_all(&dir).await; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.