use std::sync::Arc;
use async_trait::async_trait;
use crate::error::FlowcatError;
use crate::processor::frame::AudioFrame;
use crate::realtime::{RealtimeKickoff, RealtimeLlm};
use crate::service::RealtimeLlmService;
use crate::types::{AudioChunk, RealtimeEvent, RealtimeSetup, ToolDecl};
pub struct ServiceRealtimeAdapter<S: RealtimeLlmService> {
inner: S,
}
impl<S: RealtimeLlmService> ServiceRealtimeAdapter<S> {
pub fn new(inner: S) -> Self {
Self { inner }
}
pub fn into_inner(self) -> S {
self.inner
}
}
#[async_trait]
impl<S: RealtimeLlmService> RealtimeLlm for ServiceRealtimeAdapter<S> {
async fn connect(&mut self, setup: RealtimeSetup) -> Result<(), FlowcatError> {
self.inner.connect(setup).await
}
async fn send_audio(&mut self, chunk: AudioChunk) -> Result<(), FlowcatError> {
self.inner
.send_audio(Arc::new(AudioFrame::from(chunk)))
.await
}
async fn update_system(
&mut self,
prompt: String,
tools: Vec<ToolDecl>,
) -> Result<(), FlowcatError> {
self.inner.update_system(prompt, tools).await
}
async fn send_tool_result(
&mut self,
id: String,
result: serde_json::Value,
) -> Result<(), FlowcatError> {
self.inner.send_tool_result(id, result).await
}
async fn next_event(&mut self) -> Option<RealtimeEvent> {
self.inner.next_event().await
}
fn input_sample_rate(&self) -> u32 {
self.inner.input_sample_rate()
}
fn event_notify(&self) -> Option<std::sync::Arc<tokio::sync::Notify>> {
self.inner.event_notify()
}
async fn poll_event(&mut self) -> crate::realtime::PollEvent {
self.inner.poll_event().await
}
}
#[async_trait]
impl<S: RealtimeLlmService> RealtimeKickoff for ServiceRealtimeAdapter<S> {
async fn kickoff(&mut self) -> Result<(), FlowcatError> {
self.inner.kickoff().await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct SpyService {
connected: bool,
last_audio_rate: Option<u32>,
kicked: bool,
}
#[async_trait]
impl RealtimeLlmService for SpyService {
async fn connect(&mut self, _setup: RealtimeSetup) -> Result<(), FlowcatError> {
self.connected = true;
Ok(())
}
async fn send_audio(&mut self, chunk: Arc<AudioFrame>) -> Result<(), FlowcatError> {
self.last_audio_rate = Some(chunk.sample_rate);
Ok(())
}
async fn update_system(
&mut self,
_p: String,
_t: Vec<ToolDecl>,
) -> Result<(), FlowcatError> {
Ok(())
}
async fn send_tool_result(
&mut self,
_id: String,
_r: serde_json::Value,
) -> Result<(), FlowcatError> {
Ok(())
}
async fn next_event(&mut self) -> Option<RealtimeEvent> {
None
}
fn input_sample_rate(&self) -> u32 {
24_000
}
async fn kickoff(&mut self) -> Result<(), FlowcatError> {
self.kicked = true;
Ok(())
}
}
#[tokio::test]
async fn adapter_delegates_and_converts_audio() {
let mut a = ServiceRealtimeAdapter::new(SpyService::default());
assert_eq!(a.input_sample_rate(), 24_000);
a.send_audio(AudioChunk::new(vec![0i16; 10], 24_000))
.await
.unwrap();
assert_eq!(a.inner.last_audio_rate, Some(24_000));
RealtimeKickoff::kickoff(&mut a).await.unwrap();
assert!(a.inner.kicked);
}
}