spec_ai_core/agent/transcription_providers/
mock.rs1use crate::agent::transcription::{
4 TranscriptionConfig, TranscriptionEvent, TranscriptionProvider, TranscriptionProviderKind,
5 TranscriptionProviderMetadata,
6};
7use anyhow::Result;
8use async_stream::stream;
9use async_trait::async_trait;
10use futures::Stream;
11use std::pin::Pin;
12
13#[derive(Debug, Clone)]
15pub struct MockTranscriptionProvider {
16 transcriptions: Vec<String>,
18 name: String,
20}
21
22impl MockTranscriptionProvider {
23 pub fn new() -> Self {
25 Self {
26 transcriptions: vec![
27 "Hello, this is a test transcription.".to_string(),
28 "The audio is being transcribed in real-time.".to_string(),
29 "This is a mock provider for testing purposes.".to_string(),
30 ],
31 name: "Mock Transcription Provider".to_string(),
32 }
33 }
34
35 pub fn with_transcriptions(transcriptions: Vec<String>) -> Self {
37 Self {
38 transcriptions,
39 name: "Mock Transcription Provider".to_string(),
40 }
41 }
42
43 pub fn with_name(mut self, name: impl Into<String>) -> Self {
45 self.name = name.into();
46 self
47 }
48}
49
50impl Default for MockTranscriptionProvider {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56#[async_trait]
57impl TranscriptionProvider for MockTranscriptionProvider {
58 async fn start_transcription(
59 &self,
60 config: &TranscriptionConfig,
61 ) -> Result<Pin<Box<dyn Stream<Item = Result<TranscriptionEvent>> + Send>>> {
62 let transcriptions = self.transcriptions.clone();
63 let chunk_duration = config.chunk_duration_secs;
64 let total_duration = config.duration_secs.unwrap_or(30);
65
66 let stream = stream! {
67 yield Ok(TranscriptionEvent::Started {
69 timestamp: std::time::SystemTime::now(),
70 });
71
72 let mut chunk_id = 0;
73 let num_chunks = (total_duration as f64 / chunk_duration).ceil() as usize;
74 let chunk_duration_ms = (chunk_duration * 1000.0) as u64;
75
76 for i in 0..num_chunks {
77 tokio::time::sleep(tokio::time::Duration::from_millis(chunk_duration_ms)).await;
79
80 let text = transcriptions[i % transcriptions.len()].clone();
82
83 yield Ok(TranscriptionEvent::Transcription {
85 chunk_id,
86 text,
87 timestamp: std::time::SystemTime::now(),
88 });
89
90 chunk_id += 1;
91 }
92
93 yield Ok(TranscriptionEvent::Completed {
95 timestamp: std::time::SystemTime::now(),
96 total_chunks: chunk_id,
97 });
98 };
99
100 Ok(Box::pin(stream))
101 }
102
103 fn metadata(&self) -> TranscriptionProviderMetadata {
104 TranscriptionProviderMetadata {
105 name: self.name.clone(),
106 supported_models: vec!["mock-model".to_string()],
107 supports_streaming: true,
108 supported_languages: vec!["en".to_string(), "es".to_string(), "fr".to_string()],
109 }
110 }
111
112 fn kind(&self) -> TranscriptionProviderKind {
113 TranscriptionProviderKind::Mock
114 }
115
116 async fn health_check(&self) -> Result<bool> {
117 Ok(true)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use futures::StreamExt;
125
126 #[tokio::test]
127 async fn test_mock_provider() {
128 let provider = MockTranscriptionProvider::new();
129 let config = TranscriptionConfig {
130 duration_secs: Some(1),
131 chunk_duration_secs: 0.1,
132 ..Default::default()
133 };
134
135 let mut stream = provider.start_transcription(&config).await.unwrap();
136
137 let mut events = Vec::new();
138 while let Some(event) = stream.next().await {
139 events.push(event.unwrap());
140 }
141
142 assert!(!events.is_empty());
144 assert!(matches!(events[0], TranscriptionEvent::Started { .. }));
145 assert!(matches!(
146 events[events.len() - 1],
147 TranscriptionEvent::Completed { .. }
148 ));
149 }
150
151 #[test]
152 fn test_mock_provider_metadata() {
153 let provider = MockTranscriptionProvider::new();
154 let metadata = provider.metadata();
155
156 assert_eq!(metadata.name, "Mock Transcription Provider");
157 assert!(metadata.supports_streaming);
158 assert!(!metadata.supported_models.is_empty());
159 }
160}