Source code for redisvl.extensions.cache.llm.schema

from typing import Any

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

from redisvl.extensions.constants import (
    CACHE_VECTOR_FIELD_NAME,
    INSERTED_AT_FIELD_NAME,
    PROMPT_FIELD_NAME,
    RESPONSE_FIELD_NAME,
    UPDATED_AT_FIELD_NAME,
)
from redisvl.redis.utils import array_to_buffer, hashify
from redisvl.schema import IndexSchema
from redisvl.utils.utils import current_timestamp, deserialize, serialize


[docs] class CacheEntry(BaseModel): """A single cache entry in Redis""" entry_id: str | None = Field(default=None) """Cache entry identifier""" prompt: str """Input prompt or question cached in Redis""" response: str """Response or answer to the question, cached in Redis""" prompt_vector: list[float] """Text embedding representation of the prompt""" inserted_at: float = Field(default_factory=current_timestamp) """Timestamp of when the entry was added to the cache""" updated_at: float = Field(default_factory=current_timestamp) """Timestamp of when the entry was updated in the cache""" metadata: dict[str, Any] | None = Field(default=None) """Optional metadata stored on the cache entry""" filters: dict[str, Any] | None = Field(default=None) """Optional filter data stored on the cache entry for customizing retrieval""" @model_validator(mode="before") @classmethod def generate_id(cls, values): # Ensure entry_id is set if not values.get("entry_id"): values["entry_id"] = hashify(values["prompt"], values.get("filters")) return values @field_validator("metadata") @classmethod def non_empty_metadata(cls, v): if v is not None and not isinstance(v, dict): raise TypeError("Metadata must be a dictionary.") return v def to_dict(self, dtype: str) -> dict[str, Any]: data = self.model_dump(exclude_none=True) data["prompt_vector"] = array_to_buffer(self.prompt_vector, dtype) if self.metadata is not None: data["metadata"] = serialize(self.metadata) if self.filters is not None: data.update(self.filters) del data["filters"] return data
[docs] class CacheHit(BaseModel): """A cache hit based on some input query""" entry_id: str """Cache entry identifier""" prompt: str """Input prompt or question cached in Redis""" response: str """Response or answer to the question, cached in Redis""" vector_distance: float """The semantic distance between the query vector and the stored prompt vector""" inserted_at: float """Timestamp of when the entry was added to the cache""" updated_at: float """Timestamp of when the entry was updated in the cache""" metadata: dict[str, Any] | None = Field(default=None) """Optional metadata stored on the cache entry""" filters: dict[str, Any] | None = Field(default=None) """Optional filter data stored on the cache entry for customizing retrieval""" # Allow extra fields to simplify handling filters model_config = ConfigDict(extra="allow") @model_validator(mode="before") @classmethod def validate_cache_hit(cls, values: dict[str, Any]) -> dict[str, Any]: # Deserialize metadata if necessary if "metadata" in values and isinstance(values["metadata"], str): values["metadata"] = deserialize(values["metadata"]) # Collect any extra fields and store them as filters extra_data = values.pop("__pydantic_extra__", {}) or {} if extra_data: current_filters = values.get("filters") or {} if not isinstance(current_filters, dict): current_filters = {} current_filters.update(extra_data) values["filters"] = current_filters return values
[docs] def to_dict(self) -> dict[str, Any]: """Convert this model to a dictionary, merging filters into the result.""" data = self.model_dump(exclude_none=True) if data.get("filters"): data.update(data["filters"]) del data["filters"] return data
class SemanticCacheIndexSchema(IndexSchema): @classmethod def from_params(cls, name: str, prefix: str, vector_dims: int, dtype: str): return cls( index={"name": name, "prefix": prefix}, # type: ignore fields=[ # type: ignore {"name": PROMPT_FIELD_NAME, "type": "text"}, {"name": RESPONSE_FIELD_NAME, "type": "text"}, {"name": INSERTED_AT_FIELD_NAME, "type": "numeric"}, {"name": UPDATED_AT_FIELD_NAME, "type": "numeric"}, { "name": CACHE_VECTOR_FIELD_NAME, "type": "vector", "attrs": { "dims": vector_dims, "datatype": dtype, "distance_metric": "cosine", "algorithm": "flat", }, }, ], )