use std::time::{Duration, SystemTime, UNIX_EPOCH}; use litellm_cache::{ExactCacheContext, SemanticCacheContext}; use litellm_cache_response::{CacheControls, CacheKeyInput, ResponseCacheRequest}; use litellm_host_python::from_py; use pyo3::{exceptions::PyValueError, prelude::*}; use serde::Deserialize; use serde_json::{Map, Value}; #[derive(Deserialize)] #[serde(deny_unknown_fields)] struct RequestInput { key: CacheKeyInput, controls: Option, ttl_seconds: Option, max_age_seconds: Option, input: Option, messages: Option>, metadata: Option>, scope: Option, } pub(super) struct CacheRequest { key: CacheKeyInput, controls: CacheControls, ttl: Option, max_age: Option, input: Option, messages: Vec, metadata: Map, scope: Option, } impl CacheRequest { pub(super) fn exact(&self) -> ResponseCacheRequest { let mut request = ResponseCacheRequest::new(self.key.clone()); request.controls = self.controls; request.context.ttl = self.ttl; request.max_age = self.max_age; request } pub(super) fn semantic(&self) -> ResponseCacheRequest { ResponseCacheRequest { key: self.key.clone(), controls: self.controls, context: SemanticCacheContext { input: self.input.clone(), messages: self.messages.clone(), metadata: self.metadata.clone(), scope: self.scope.clone(), ttl: self.ttl, }, max_age: self.max_age, } } } pub(super) fn request(value: &Bound<'_, PyAny>) -> PyResult { let input: RequestInput = from_py(value)?; request_input(input) } fn request_input(input: RequestInput) -> PyResult { let defaults = ResponseCacheRequest::::new(input.key.clone()); Ok(CacheRequest { key: input.key, controls: input.controls.unwrap_or(defaults.controls), ttl: input.ttl_seconds.map(duration).transpose()?, max_age: input.max_age_seconds.map(duration).transpose()?, input: input.input, messages: input.messages.unwrap_or_default(), metadata: input.metadata.unwrap_or_default(), scope: input.scope, }) } pub(super) fn requests(value: &Bound<'_, PyAny>) -> PyResult> { from_py::>(value)? .into_iter() .map(request_input) .collect() } pub(super) fn duration(seconds: f64) -> PyResult { Duration::try_from_secs_f64(seconds) .map_err(|_| PyValueError::new_err("cache durations must be finite and nonnegative")) } pub(super) fn now() -> Duration { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() }