From cda890438297ac133df56a93d69e0e3eac65340b Mon Sep 17 00:00:00 2001 From: Yujong Lee Date: Mon, 21 Sep 2026 12:03:13 -0700 Subject: [PATCH] fix(cache): keep native wheel within size budget --- .../crates/python-bridge/src/cache/config.rs | 84 ++++++++----------- 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/litellm-rust/crates/python-bridge/src/cache/config.rs b/litellm-rust/crates/python-bridge/src/cache/config.rs index 637bdab4055..37eee2048e0 100644 --- a/litellm-rust/crates/python-bridge/src/cache/config.rs +++ b/litellm-rust/crates/python-bridge/src/cache/config.rs @@ -1,7 +1,7 @@ use std::time::Duration; use pyo3::{ - exceptions::{PyOverflowError, PyTypeError, PyValueError}, + exceptions::{PyTypeError, PyValueError}, prelude::*, types::{PyAny, PyDict}, }; @@ -43,7 +43,7 @@ pub(super) struct RedisTlsConfig { pub(super) certificate_requirement: CertificateRequirement, pub(super) check_hostname: bool, pub(super) ca_certificate: Option, - pub(super) ca_data: Option>, + pub(super) ca_data: Option, pub(super) client_certificate: Option, pub(super) client_key: Option, } @@ -86,21 +86,21 @@ pub(super) struct NativeCacheConfig { } pub(super) enum UnsupportedCacheConfig { - Backend(String), - RedisMode(&'static str), - RedisOption(String), + Backend, + RedisTopology, + RedisCredentials, + RedisConnection, + RedisOption, } impl UnsupportedCacheConfig { - pub(super) fn message(&self) -> String { + pub(super) fn message(&self) -> &'static str { match self { - Self::Backend(backend) => { - format!("native cache backend {backend:?} is not implemented") - } - Self::RedisMode(mode) => format!("native Redis {mode} mode is not implemented"), - Self::RedisOption(option) => { - format!("native Redis option {option:?} is not implemented") - } + Self::Backend => "native cache backend is not implemented", + Self::RedisTopology => "native Redis topology is not implemented", + Self::RedisCredentials => "native Redis credentials require Python", + Self::RedisConnection => "native Redis connection type is not implemented", + Self::RedisOption => "native Redis configuration requires Python", } } } @@ -143,7 +143,7 @@ impl NativeCacheConfig { Err(reason) => Ok(CacheConfigProjection::Unsupported(reason)), }, _ => Ok(CacheConfigProjection::Unsupported( - UnsupportedCacheConfig::Backend(backend_name), + UnsupportedCacheConfig::Backend, )), } } @@ -187,7 +187,7 @@ fn project_memory(backend: &Bound<'_, PyAny>) -> PyResult { capacity: backend.getattr("max_size_in_memory")?.extract::()?, max_entry_bytes: max_size_kib .checked_mul(1024) - .ok_or_else(|| PyOverflowError::new_err("memory cache item limit is too large"))?, + .ok_or_else(|| PyValueError::new_err("memory cache item limit is too large"))?, }) } @@ -196,20 +196,19 @@ fn project_redis( ) -> PyResult> { let source = backend.getattr("redis_kwargs")?.cast_into::()?; if has_value(&source, "startup_nodes")? { - return Ok(Err(UnsupportedCacheConfig::RedisMode("cluster"))); + return Ok(Err(UnsupportedCacheConfig::RedisTopology)); } if has_value(&source, "sentinel_nodes")? { - return Ok(Err(UnsupportedCacheConfig::RedisMode("sentinel"))); + return Ok(Err(UnsupportedCacheConfig::RedisTopology)); } - for key in [ - "credential_provider", - "redis_connect_func", - "connection_pool", - ] { + for key in ["credential_provider", "redis_connect_func"] { if has_value(&source, key)? { - return Ok(Err(UnsupportedCacheConfig::RedisOption(key.to_owned()))); + return Ok(Err(UnsupportedCacheConfig::RedisCredentials)); } } + if has_value(&source, "connection_pool")? { + return Ok(Err(UnsupportedCacheConfig::RedisConnection)); + } for key in [ "retry", "retry_on_error", @@ -228,12 +227,12 @@ fn project_redis( "ssl_ocsp_expected_cert", ] { if has_value(&source, key)? { - return Ok(Err(UnsupportedCacheConfig::RedisOption(key.to_owned()))); + return Ok(Err(UnsupportedCacheConfig::RedisOption)); } } for key in ["retry_on_timeout", "single_connection_client"] { if optional_coerced_bool(&source, key)?.unwrap_or(false) { - return Ok(Err(UnsupportedCacheConfig::RedisOption(key.to_owned()))); + return Ok(Err(UnsupportedCacheConfig::RedisOption)); } } @@ -241,12 +240,12 @@ fn project_redis( let pool = client.getattr("connection_pool")?; let pool_class = class_identity(&pool)?; if pool_class != ("redis.connection".to_owned(), "ConnectionPool".to_owned()) { - return Ok(Err(UnsupportedCacheConfig::RedisMode("custom pool"))); + return Ok(Err(UnsupportedCacheConfig::RedisConnection)); } let resolved = pool.getattr("connection_kwargs")?.cast_into::()?; for key in ["credential_provider", "redis_connect_func"] { if has_value(&resolved, key)? { - return Ok(Err(UnsupportedCacheConfig::RedisOption(key.to_owned()))); + return Ok(Err(UnsupportedCacheConfig::RedisCredentials)); } } let connection_class = resolved @@ -265,17 +264,13 @@ fn project_redis( (module, name) if module == "redis.connection" && name == "SSLConnection" => { Some(project_tls(&resolved)?) } - _ => return Ok(Err(UnsupportedCacheConfig::RedisMode("custom connection"))), + _ => return Ok(Err(UnsupportedCacheConfig::RedisConnection)), }; let protocol = match optional_u8(&resolved, "protocol")?.unwrap_or(2) { 2 => RedisProtocol::Resp2, 3 => RedisProtocol::Resp3, - value => { - return Err(PyValueError::new_err(format!( - "unsupported Redis protocol version {value}" - ))); - } + _ => return Err(PyValueError::new_err("unsupported Redis protocol version")), }; let health_check_interval = duration(optional_f64(&resolved, "health_check_interval")?.unwrap_or(0.0))?; @@ -306,7 +301,7 @@ fn project_tls(values: &Bound<'_, PyDict>) -> PyResult { certificate_requirement: certificate_requirement(values)?, check_hostname: optional_bool(values, "ssl_check_hostname")?.unwrap_or(false), ca_certificate: optional_dict_string(values, "ssl_ca_certs")?, - ca_data: optional_bytes(values, "ssl_ca_data")?, + ca_data: optional_dict_string(values, "ssl_ca_data")?, client_certificate: optional_dict_string(values, "ssl_certfile")?, client_key: optional_dict_string(values, "ssl_keyfile")?, }) @@ -374,14 +369,14 @@ fn has_value(values: &Bound<'_, PyDict>, key: &str) -> PyResult { fn required_string(values: &Bound<'_, PyDict>, key: &str) -> PyResult { values .get_item(key)? - .ok_or_else(|| PyTypeError::new_err(format!("Redis connection is missing {key:?}")))? + .ok_or_else(|| PyTypeError::new_err("Redis connection is incomplete"))? .extract::() } fn required_u16(values: &Bound<'_, PyDict>, key: &str) -> PyResult { values .get_item(key)? - .ok_or_else(|| PyTypeError::new_err(format!("Redis connection is missing {key:?}")))? + .ok_or_else(|| PyTypeError::new_err("Redis connection is incomplete"))? .extract::() } @@ -392,19 +387,6 @@ fn optional_dict_string(values: &Bound<'_, PyDict>, key: &str) -> PyResult, key: &str) -> PyResult>> { - let Some(value) = values.get_item(key)? else { - return Ok(None); - }; - if value.is_none() { - return Ok(None); - } - if let Ok(bytes) = value.extract::>() { - return Ok(Some(bytes)); - } - Ok(Some(value.extract::()?.into_bytes())) -} - fn optional_f64(values: &Bound<'_, PyDict>, key: &str) -> PyResult> { match values.get_item(key)? { Some(value) => value.extract::>(), @@ -560,7 +542,7 @@ mod tests { ); assert!(tls.check_hostname); assert_eq!(tls.ca_certificate.as_deref(), Some("/ca.pem")); - assert_eq!(tls.ca_data.as_deref(), Some(b"CA DATA".as_slice())); + assert_eq!(tls.ca_data.as_deref(), Some("CA DATA")); assert_eq!(tls.client_certificate.as_deref(), Some("/client.pem")); assert_eq!(tls.client_key.as_deref(), Some("/client.key")); }); @@ -580,7 +562,7 @@ mod tests { else { panic!("dynamic authentication must stay on Python"); }; - assert!(reason.message().contains("credential_provider")); + assert_eq!(reason.message(), "native Redis credentials require Python"); }); } }