mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
fix(cache): keep native Redis semantic binding and Qdrant batch writes after merge
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
cc9970efbf
commit
397d0b4824
3 changed files with 100 additions and 7 deletions
|
|
@ -410,7 +410,8 @@ impl NativeCacheConfig {
|
|||
Some("facade and native backend index names must match")
|
||||
}
|
||||
CacheBackendConfig::RedisSemantic(config)
|
||||
if service.similarity_threshold() != Some(config.similarity_threshold) =>
|
||||
if service.similarity_threshold()
|
||||
!= Some(f64::from(config.similarity_threshold as f32)) =>
|
||||
{
|
||||
Some("facade and native backend similarity thresholds must match")
|
||||
}
|
||||
|
|
@ -1177,12 +1178,13 @@ mod tests {
|
|||
use pyo3::{prelude::*, types::PyDict};
|
||||
|
||||
use litellm_cache_redis::{RedisNode, RedisTopology};
|
||||
use litellm_cache_redis_semantic::RedisSemanticConfig;
|
||||
|
||||
use super::{
|
||||
CacheBackendConfig, CacheConfigProjection, CertificateRequirement, GcsCacheConfig,
|
||||
NativeCacheConfig, RedisProtocol, UnsupportedCacheConfig,
|
||||
CacheBackendConfig, CacheConfigProjection, CachePolicy, CertificateRequirement,
|
||||
GcsCacheConfig, NativeCacheConfig, RedisProtocol, UnsupportedCacheConfig,
|
||||
};
|
||||
use crate::cache::native::NativeResponseCache;
|
||||
use crate::cache::{embedder::PythonEmbedder, native::NativeResponseCache};
|
||||
|
||||
fn cluster_facade<'py>(py: Python<'py>, startup_nodes: &str, hook: &str) -> Bound<'py, PyAny> {
|
||||
facade(
|
||||
|
|
@ -1255,6 +1257,49 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redis_semantic_service_mismatch_accepts_backend_precision_threshold() {
|
||||
Python::initialize();
|
||||
Python::attach(|py| {
|
||||
let facade = facade(
|
||||
py,
|
||||
"backend = SimpleNamespace(_redis_url='redis://127.0.0.1/', _index_name='semantic_idx', similarity_threshold=0.8, embedding_model='text-embedding-3-small', embedding_max_input_tokens=None, embedding_timeout=None)\n\
|
||||
facade = SimpleNamespace(type='redis-semantic', mode='default-on', ttl=None, namespace=None, supported_call_types=None, redis_flush_size=None, semantic_cache_scope='key', cache=backend)",
|
||||
);
|
||||
let backend = facade.getattr("cache").unwrap();
|
||||
let embedder = PythonEmbedder::new(backend.clone().unbind());
|
||||
let CacheConfigProjection::Native(config) =
|
||||
NativeCacheConfig::project(&facade).unwrap()
|
||||
else {
|
||||
panic!("Redis semantic cache should be supported");
|
||||
};
|
||||
let CacheBackendConfig::RedisSemantic(config) = config.backend else {
|
||||
panic!("expected Redis semantic configuration");
|
||||
};
|
||||
let service = NativeResponseCache::redis_semantic(
|
||||
&config.redis_url,
|
||||
embedder,
|
||||
RedisSemanticConfig {
|
||||
index_name: config.index_name.clone(),
|
||||
similarity_threshold: config.similarity_threshold as f32,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let matching_config = NativeCacheConfig {
|
||||
policy: CachePolicy {
|
||||
mode: "default-on".into(),
|
||||
ttl: None,
|
||||
namespace: None,
|
||||
supported_call_types: None,
|
||||
redis_flush_size: None,
|
||||
semantic_cache_scope: "key".into(),
|
||||
},
|
||||
backend: CacheBackendConfig::RedisSemantic(config),
|
||||
};
|
||||
assert_eq!(matching_config.service_mismatch(&service), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projects_resolved_redis_tls_configuration() {
|
||||
Python::initialize();
|
||||
|
|
|
|||
|
|
@ -849,8 +849,13 @@ impl NativeResponseCache {
|
|||
.collect();
|
||||
cache.async_store_batch(entries, now).await
|
||||
}
|
||||
Self::RedisSemantic { .. } | Self::QdrantSemantic(_) => {
|
||||
Err(Error::UnsupportedOperation)
|
||||
Self::RedisSemantic { .. } => Err(Error::UnsupportedOperation),
|
||||
Self::QdrantSemantic(cache) => {
|
||||
let entries = entries
|
||||
.into_iter()
|
||||
.map(|(request, value)| (Self::semantic_request(&request), value))
|
||||
.collect();
|
||||
cache.async_store_batch(entries, now).await
|
||||
}
|
||||
Self::Gcs(cache) => {
|
||||
let entries = entries
|
||||
|
|
@ -922,7 +927,18 @@ impl NativeResponseCache {
|
|||
py,
|
||||
SemanticBody::new(self.clone(), SemanticOperation::StoreBatch(entries.into())),
|
||||
),
|
||||
Self::QdrantSemantic(_) => Err(super::cache_error(Error::UnsupportedOperation)),
|
||||
Self::QdrantSemantic(_) => {
|
||||
let service = self.clone();
|
||||
litellm_host_python::run_async(
|
||||
py,
|
||||
async move {
|
||||
service
|
||||
.async_store_batch(entries, super::request::now())
|
||||
.await
|
||||
},
|
||||
super::cache_error,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1830,6 +1830,38 @@ async def test_qdrant_semantic_async_parity(
|
|||
assert python_value["response"] == {"id": "native"}
|
||||
|
||||
|
||||
async def test_qdrant_semantic_async_store_batch_shares_entries(
|
||||
qdrant_url: str, fake_embedding_endpoint: str
|
||||
) -> None:
|
||||
del fake_embedding_endpoint
|
||||
collection: Final = f"cache_{uuid4().hex}"
|
||||
facade: Final = qdrant_facade(qdrant_url, collection)
|
||||
handle: Final = _native._CacheTestHandle.qdrant_semantic(
|
||||
qdrant_url,
|
||||
collection_name=collection,
|
||||
similarity_threshold=0.99,
|
||||
vector_size=8,
|
||||
)
|
||||
handle._bind_facade(facade)
|
||||
binding: Final = _native._CacheTestResolver(SimpleNamespace(cache=facade)).resolve()
|
||||
entries: Final = [
|
||||
qdrant_request("batch-one", [{"role": "user", "content": "first batch prompt"}]),
|
||||
qdrant_request("batch-two", [{"role": "user", "content": "second batch prompt"}]),
|
||||
]
|
||||
await binding.async_store_batch(entries, [{"id": "one"}, {"id": "two"}])
|
||||
|
||||
assert binding.lookup(entries[0]) == {"id": "one"}
|
||||
assert binding.lookup(entries[1]) == {"id": "two"}
|
||||
assert (
|
||||
(await facade.cache.async_get_cache("batch-one", messages=entries[0]["messages"]))["response"]
|
||||
== {"id": "one"}
|
||||
)
|
||||
assert (
|
||||
(await facade.cache.async_get_cache("batch-two", messages=entries[1]["messages"]))["response"]
|
||||
== {"id": "two"}
|
||||
)
|
||||
|
||||
|
||||
async def test_qdrant_semantic_malformed_entries_and_unsupported_operations(
|
||||
qdrant_url: str, fake_embedding_endpoint: str
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue