pub mod config;
pub mod types;
#[cfg(feature = "heygen-avatar")]
pub mod heygen;
#[cfg(feature = "did-avatar")]
pub mod did;
pub use config::{AvatarConfig, AvatarProviderKind, LipSyncConfig, RenderingConfig};
pub use types::{AvatarSessionInfo, IceServer, VideoStreamInfo};
#[cfg(feature = "heygen-avatar")]
pub use heygen::{HeyGenConfig, HeyGenProvider, HeyGenQuality};
#[cfg(feature = "did-avatar")]
pub use did::{DIDConfig, DIDLlmConfig, DIDProvider};
use std::sync::Arc;
use async_trait::async_trait;
use crate::error::RealtimeError;
pub type AvatarResult<T> = Result<T, RealtimeError>;
#[async_trait]
pub trait AvatarProvider: Send + Sync + std::fmt::Debug {
fn name(&self) -> &str;
async fn start_session(&self, config: &AvatarConfig) -> AvatarResult<AvatarSessionInfo>;
async fn send_audio(&self, session_id: &str, audio: &[u8]) -> AvatarResult<()>;
async fn keep_alive(&self, session_id: &str) -> AvatarResult<()>;
async fn stop_session(&self, session_id: &str) -> AvatarResult<()>;
async fn is_active(&self, session_id: &str) -> bool;
}
const _: () = {
fn _assert_object_safe(_: &dyn AvatarProvider) {}
fn _assert_arc_compatible(_: Arc<dyn AvatarProvider>) {}
};
pub fn spawn_keep_alive(
provider: Arc<dyn AvatarProvider>,
session_id: String,
interval: std::time::Duration,
) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
let mut ticker = tokio::time::interval(interval);
ticker.tick().await; loop {
ticker.tick().await;
if !provider.is_active(&session_id).await {
tracing::debug!(session_id = %session_id, "avatar keep-alive: session no longer active, stopping");
break;
}
if let Err(e) = provider.keep_alive(&session_id).await {
tracing::warn!(session_id = %session_id, error = %e, "avatar keep-alive failed, stopping");
break;
}
tracing::debug!(session_id = %session_id, "avatar keep-alive sent");
}
})
}