feat(cache): add SemanticCacheContext and semantic error variants

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Yujong Lee 2026-09-21 20:21:24 +00:00
parent ebb4d230f8
commit 0c72a94a84
3 changed files with 78 additions and 0 deletions

View file

@ -6,4 +6,8 @@ pub enum Error {
InvalidEntry,
#[error("flushing Redis requires an explicit namespace")]
UnscopedFlush,
#[error("cache backend does not support this operation")]
UnsupportedOperation,
#[error("semantic cache requires request messages")]
MissingPrompt,
}

View file

@ -5,6 +5,7 @@ mod capabilities;
mod codec;
mod dual;
mod error;
mod semantic;
pub use base_cache::{
BaseCache, BatchEntry, CacheConnectionResult, CacheConnectionStatus, CacheContext,
@ -19,3 +20,4 @@ pub use capabilities::{
pub use codec::{CacheCodec, JsonCodec};
pub use dual::{DualCache, ReadPolicy, RemoteFailurePolicy, WritePolicy};
pub use error::Error;
pub use semantic::{SemanticCacheContext, SemanticCacheScope};

View file

@ -0,0 +1,72 @@
use std::time::Duration;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::CacheContext;
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SemanticCacheScope {
#[default]
Key,
EndUser,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SemanticCacheContext {
pub input: Option<String>,
pub messages: Vec<Value>,
pub metadata: Map<String, Value>,
pub scope: SemanticCacheScope,
pub ttl: Option<Duration>,
}
impl CacheContext for SemanticCacheContext {
fn ttl(&self) -> Option<Duration> {
self.ttl
}
fn with_ttl(&self, ttl: Option<Duration>) -> Self {
Self {
ttl,
..self.clone()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_ttl_keeps_request_fields() {
let context = SemanticCacheContext {
input: Some("query".to_owned()),
messages: vec![serde_json::json!({"role": "user", "content": "hi"})],
metadata: Map::from_iter([("user".to_owned(), Value::from("u1"))]),
scope: SemanticCacheScope::EndUser,
ttl: None,
};
let updated = context.with_ttl(Some(Duration::from_secs(5)));
assert_eq!(updated.ttl(), Some(Duration::from_secs(5)));
assert_eq!(updated.input, context.input);
assert_eq!(updated.messages, context.messages);
assert_eq!(updated.metadata, context.metadata);
assert_eq!(updated.scope, SemanticCacheScope::EndUser);
}
#[test]
fn scope_serializes_like_python_cache_scope() {
assert_eq!(
serde_json::to_value(SemanticCacheScope::EndUser).unwrap(),
Value::from("end_user")
);
assert_eq!(
serde_json::from_value::<SemanticCacheScope>(Value::from("key")).unwrap(),
SemanticCacheScope::Key
);
}
}