feat: add transcription backend contracts

This commit is contained in:
Evandro committed 2026-06-10 20:55:10 -03:00
1 parent 3b05cb22a0
commit 54e8b42330
9 files changed
+461

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::error::{AppError, AppResult};
use super::{Transcriber, TranscriptionInput, TranscriptionResult};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct CrispAsrConfig {
pub sidecar_name: Option<String>,
pub model_path: Option<PathBuf>,
}
#[derive(Debug, Clone)]
pub struct CrispAsrTranscriber {
config: CrispAsrConfig,
}
impl CrispAsrTranscriber {
pub fn new(config: CrispAsrConfig) -> Self {
Self { config }
}
pub fn config(&self) -> &CrispAsrConfig {
&self.config
}
}
impl Transcriber for CrispAsrTranscriber {
fn transcribe(&self, input: TranscriptionInput) -> AppResult<TranscriptionResult> {
let Some(model_path) = &self.config.model_path else {
return Err(AppError::ModelNotInstalled(input.options.model_id));
};
if self.config.sidecar_name.is_none() {
return Err(AppError::SidecarNotConfigured);
}
Ok(TranscriptionResult {
text: format!(
"Transcrição local pendente via CrispASR para {} usando {}.",
input.audio_path.display(),
model_path.display()
),
backend: "crispasr".to_string(),
model_id: input.options.model_id,
detected_language: input.options.language,
})
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use crate::error::AppError;
use crate::models::DEFAULT_MODEL_ID;
use crate::transcription::{Transcriber, TranscriptionInput, TranscriptionOptions};
use super::{CrispAsrConfig, CrispAsrTranscriber};
#[test]
fn missing_model_returns_clear_error() {
let transcriber = CrispAsrTranscriber::new(CrispAsrConfig {
sidecar_name: Some("crispasr".to_string()),
model_path: None,
});
let err = transcriber
.transcribe(TranscriptionInput {
audio_path: PathBuf::from("sample.wav"),
options: TranscriptionOptions::default(),
})
.expect_err("modelo ausente deve falhar");
assert_eq!(err, AppError::ModelNotInstalled(DEFAULT_MODEL_ID.to_string()));
}
}
+47
View File
@@ -0,0 +1,47 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::error::AppResult;
use crate::models::DEFAULT_MODEL_ID;
pub mod crispasr;
pub mod openai;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct TranscriptionInput {
pub audio_path: PathBuf,
pub options: TranscriptionOptions,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct TranscriptionOptions {
pub model_id: String,
pub language: Option<String>,
pub timestamps: bool,
}
impl Default for TranscriptionOptions {
fn default() -> Self {
Self {
model_id: DEFAULT_MODEL_ID.to_string(),
language: None,
timestamps: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct TranscriptionResult {
pub text: String,
pub backend: String,
pub model_id: String,
pub detected_language: Option<String>,
}
pub trait Transcriber {
fn transcribe(&self, input: TranscriptionInput) -> AppResult<TranscriptionResult>;
}
+19
View File
@@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct OpenAiTranscriptionConfig {
pub endpoint: String,
pub model: String,
pub language: Option<String>,
}
impl Default for OpenAiTranscriptionConfig {
fn default() -> Self {
Self {
endpoint: "https://api.openai.com/v1/audio/transcriptions".to_string(),
model: "whisper-1".to_string(),
language: None,
}
}
}