use super::traits::TranscriptionError;
use super::{BackendCapabilities, BackendConfig, BackendType, QuantizationLevel};
use ct2rs::{ComputeType, Config, Device, Whisper};
use std::path::Path;
pub struct CT2Backend {
whisper: Whisper,
config: BackendConfig,
}
impl CT2Backend {
pub fn new(
model_path: impl AsRef<Path>,
backend_config: &BackendConfig,
) -> Result<Self, TranscriptionError> {
let ct2_config = Self::map_config(backend_config);
let whisper = Whisper::new(model_path.as_ref(), ct2_config)
.map_err(|e| TranscriptionError::ModelNotAvailable(e.to_string()))?;
Ok(Self {
whisper,
config: backend_config.clone(),
})
}
fn map_config(backend_config: &BackendConfig) -> Config {
let device = if backend_config.gpu_enabled {
Device::CUDA
} else {
Device::CPU
};
let compute_type = match backend_config.quantization_level {
QuantizationLevel::High => ComputeType::FLOAT16,
QuantizationLevel::Medium => ComputeType::INT8,
QuantizationLevel::Low => ComputeType::INT8,
};
Config {
device,
device_indices: vec![0], compute_type,
tensor_parallel: false,
num_threads_per_replica: backend_config.threads,
max_queued_batches: 0, cpu_core_offset: -1, }
}
pub fn capabilities(&self) -> BackendCapabilities {
BackendCapabilities {
name: "CTranslate2",
max_audio_duration: Some(60.0), supported_languages: None, supports_streaming: false, gpu_available: self.config.gpu_enabled,
}
}
pub fn transcribe(
&self,
samples: &[f32],
language: &str,
common_options: &crate::config::CommonTranscriptionOptions,
options: &crate::config::CT2Options,
_sample_rate: usize,
) -> Result<String, TranscriptionError> {
let ct2_options = options.to_whisper_options(common_options);
let result = self
.whisper
.generate(samples, Some(language), false, &ct2_options)?;
let transcription = result
.first()
.map(|s| s.to_string())
.unwrap_or_else(String::new);
Ok(transcription)
}
}
pub fn migrate_legacy_config(
compute_type_str: &str,
device_str: &str,
threads: Option<usize>,
) -> BackendConfig {
let quantization_level = match compute_type_str.to_uppercase().as_str() {
"FLOAT32" | "FLOAT16" | "AUTO" => QuantizationLevel::High,
"INT8" => QuantizationLevel::Medium,
"INT16" => QuantizationLevel::Low,
_ => QuantizationLevel::Medium, };
let gpu_enabled =
device_str.to_uppercase().contains("CUDA") || device_str.to_uppercase().contains("GPU");
BackendConfig {
backend: BackendType::CTranslate2,
threads: threads.unwrap_or_else(|| num_cpus::get().min(4)),
gpu_enabled,
quantization_level,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_mapping() {
let backend_config = BackendConfig {
backend: BackendType::CTranslate2,
threads: 4,
gpu_enabled: false,
quantization_level: QuantizationLevel::Medium,
};
let ct2_config = CT2Backend::map_config(&backend_config);
assert_eq!(ct2_config.device, Device::CPU);
assert_eq!(ct2_config.compute_type, ComputeType::INT8);
assert_eq!(ct2_config.num_threads_per_replica, 4);
}
#[test]
fn test_legacy_migration() {
let backend_config = migrate_legacy_config("INT8", "CPU", Some(4));
assert!(!backend_config.gpu_enabled);
assert_eq!(backend_config.quantization_level, QuantizationLevel::Medium);
assert_eq!(backend_config.threads, 4);
}
#[test]
fn test_gpu_config() {
let backend_config = BackendConfig {
backend: BackendType::CTranslate2,
threads: 4,
gpu_enabled: true,
quantization_level: QuantizationLevel::High,
};
let ct2_config = CT2Backend::map_config(&backend_config);
assert_eq!(ct2_config.device, Device::CUDA);
assert_eq!(ct2_config.compute_type, ComputeType::FLOAT16);
}
}