use crate::stream::StreamEvent;
use crate::tasks::generate::GenerateRequest;
use crate::{InferenceError, InferenceResult};
use std::sync::{Arc, OnceLock, RwLock};
#[async_trait::async_trait]
pub trait LocalGenerationOffload: Send + Sync {
async fn generate(&self, request: GenerateRequest) -> Result<InferenceResult, InferenceError>;
async fn stream(
&self,
request: GenerateRequest,
) -> Result<tokio::sync::mpsc::Receiver<StreamEvent>, InferenceError>;
}
fn offload_slot() -> &'static RwLock<Option<Arc<dyn LocalGenerationOffload>>> {
static SLOT: OnceLock<RwLock<Option<Arc<dyn LocalGenerationOffload>>>> = OnceLock::new();
SLOT.get_or_init(|| RwLock::new(None))
}
pub fn set_local_offload(offload: Option<Arc<dyn LocalGenerationOffload>>) {
let mut guard = offload_slot().write().expect("local offload slot poisoned");
*guard = offload;
}
pub fn current_local_offload() -> Option<Arc<dyn LocalGenerationOffload>> {
if is_offload_worker() {
return None;
}
offload_slot()
.read()
.expect("local offload slot poisoned")
.clone()
}
pub fn is_offload_worker() -> bool {
std::env::var_os("CAR_INFERENCE_WORKER").is_some()
}
#[cfg(test)]
mod tests {
use super::*;
struct NoopOffload;
#[async_trait::async_trait]
impl LocalGenerationOffload for NoopOffload {
async fn generate(
&self,
_request: GenerateRequest,
) -> Result<InferenceResult, InferenceError> {
Err(InferenceError::InferenceFailed("noop".into()))
}
async fn stream(
&self,
_request: GenerateRequest,
) -> Result<tokio::sync::mpsc::Receiver<StreamEvent>, InferenceError> {
Err(InferenceError::InferenceFailed("noop".into()))
}
}
#[test]
fn slot_set_and_clear() {
assert!(current_local_offload().is_none());
set_local_offload(Some(Arc::new(NoopOffload)));
assert!(current_local_offload().is_some());
set_local_offload(None);
assert!(current_local_offload().is_none());
}
}