Skip to main content

Semantic caching for LLM calls via vector similarity search

Project description

semantic-cache-lib

PyPI version CI Python License: MIT

Semantic caching for LLM calls via vector similarity search.

Instead of exact-match caching, semantic-cache-lib embeds your queries and finds semantically similar past answers — so "What is the capital of France?" and "Tell me the capital city of France" both hit the same cache entry.

Works as a decorator on any function that calls an LLM. Fully configurable: choose your embedder, backend, similarity threshold, and TTL.


Installation

# Core (in-memory backend only)
pip install semantic-cache-lib

# With specific extras
pip install semantic-cache-lib[openai]        # OpenAI embedder
pip install semantic-cache-lib[huggingface]   # HuggingFace Inference API embedder
pip install semantic-cache-lib[transformers]  # Local SentenceTransformers
pip install semantic-cache-lib[redis]         # Redis backend
pip install semantic-cache-lib[faiss]         # FAISS backend
pip install semantic-cache-lib[chroma]        # ChromaDB backend
pip install semantic-cache-lib[all]           # Everything

Quick Start

from semantic_cache import SemanticCache
from semantic_cache.embedders import HuggingFaceEmbedder

cache = SemanticCache(
    embedder=HuggingFaceEmbedder(
        token="hf_...",                              # or set HF_TOKEN env var
        model="sentence-transformers/all-MiniLM-L6-v2",
    ),
    threshold=0.90,   # 0.0–1.0 cosine similarity cutoff
    ttl=3600,         # cache entries expire after 1 hour (optional)
)

@cache
def call_llm(prompt: str) -> str:
    # your actual LLM call here
    return openai_client.chat.completions.create(...).choices[0].message.content

# First call → hits the LLM
response = call_llm("What is the capital of France?")

# Second call (semantically similar) → served from cache, LLM never called
response = call_llm("Tell me the capital city of France")

Async support

@cache
async def call_llm_async(prompt: str) -> str:
    return await async_openai_client.chat.completions.create(...)

response = await call_llm_async("What is the capital of France?")

Programmatic API

result = cache.get_or_set("my query", lambda: llm("my query"))
result = await cache.aget_or_set("my query", async_llm_call)

Embedders

HuggingFace Inference API

No GPU needed — calls the remote HF API.

from semantic_cache.embedders import HuggingFaceEmbedder

embedder = HuggingFaceEmbedder(
    token="hf_...",        # or HF_TOKEN env var
    model="sentence-transformers/all-MiniLM-L6-v2",
)

Local SentenceTransformers

Runs fully on-device. Downloads model from HF Hub on first use.

from semantic_cache.embedders import SentenceTransformerEmbedder

embedder = SentenceTransformerEmbedder(
    model="all-MiniLM-L6-v2",   # any sentence-transformers model
    device="cpu",                 # or "cuda"
)

OpenAI

from semantic_cache.embedders import OpenAIEmbedder

embedder = OpenAIEmbedder(
    api_key="sk-...",             # or OPENAI_API_KEY env var
    model="text-embedding-3-small",
)

Backends

In-Memory (default)

from semantic_cache.backends import MemoryBackend

backend = MemoryBackend(
    save_path="./cache.pkl",   # optional: persist across restarts
)

Redis

Requires Redis Stack for vector search.

from semantic_cache.backends import RedisBackend

backend = RedisBackend(
    url="redis://localhost:6379",
    vector_dim=384,              # must match your embedder's output dimension
)

FAISS

from semantic_cache.backends import FAISSBackend

backend = FAISSBackend(
    vector_dim=384,
    save_path="./faiss_cache",   # optional: persist across restarts
)

ChromaDB

from semantic_cache.backends import ChromaBackend

backend = ChromaBackend(
    url="http://localhost:8000",   # omit for in-process ephemeral client
    collection="llm_cache",
)

Full Configuration

from semantic_cache import SemanticCache
from semantic_cache.embedders import OpenAIEmbedder
from semantic_cache.backends import RedisBackend

cache = SemanticCache(
    embedder=OpenAIEmbedder(api_key="sk-..."),
    backend=RedisBackend(url="redis://localhost:6379", vector_dim=1536),
    threshold=0.92,                # similarity cutoff (default: 0.85)
    ttl=86400,                     # 24h TTL (default: None = no expiry)
    top_k=1,                       # candidates to retrieve (default: 1)
    on_hit=lambda q, r, s: print(f"HIT  sim={s:.3f} | {q[:60]}"),
    on_miss=lambda q: print(f"MISS | {q[:60]}"),
)
Parameter Type Default Description
embedder AbstractEmbedder required Embedding model
backend AbstractBackend MemoryBackend() Storage backend
threshold float 0.85 Min cosine similarity for a cache hit
ttl int | None None Entry expiry in seconds
top_k int 1 Number of candidates to retrieve before thresholding
on_hit Callable None Called with (query, response, similarity) on hit
on_miss Callable None Called with (query,) on miss

Observability

stats = cache.stats()
# CacheStats(hits=42, misses=8, total=50, hit_rate=84.00%, avg_similarity=0.9541)

cache.reset_stats()
cache.clear()          # remove all cache entries

Logging

Enable structured logging at any level:

import logging
logging.getLogger("semantic_cache").setLevel(logging.DEBUG)

Bring Your Own Embedder / Backend

from semantic_cache.embedders import AbstractEmbedder
from semantic_cache.backends import AbstractBackend

class MyEmbedder(AbstractEmbedder):
    def embed(self, text: str) -> list[float]:
        return my_model.encode(text)

class MyBackend(AbstractBackend):
    def store(self, key, vector, response, ttl=None): ...
    def search(self, vector, top_k=1) -> list[tuple[str, float]]: ...
    def delete(self, key): ...
    def clear(self): ...

Publishing / PyPI

# Install build tools
pip install hatch

# Build
hatch build

# Publish (or push a git tag to trigger GitHub Actions)
hatch publish

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

semantic_cache_lib-0.1.0.tar.gz (17.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

semantic_cache_lib-0.1.0-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

File details

Details for the file semantic_cache_lib-0.1.0.tar.gz.

File metadata

  • Download URL: semantic_cache_lib-0.1.0.tar.gz
  • Upload date:
  • Size: 17.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for semantic_cache_lib-0.1.0.tar.gz
Algorithm Hash digest
SHA256 844a98b2e0236c4b0d67aefa7e55eae98acf40d95f252663f93e1ebd083b7ea2
MD5 eccd36c50bdfa97539e529515f0b3003
BLAKE2b-256 5b9bf179b6c593c17dc48a8a63a6578282652116c05125833f6ae3aa75132a6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for semantic_cache_lib-0.1.0.tar.gz:

Publisher: publish.yml on iamsuryansh/semantic-cache-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file semantic_cache_lib-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for semantic_cache_lib-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6a30fadf819137ab4098c6896288e58961851436f41510797d9b5dbfba10bf61
MD5 7e7e8e2427e79769849680b3709d3bf4
BLAKE2b-256 4e40a57aab4109e5ed82476017bee057a0c666e7889f7133fcc19c8d0138924d

See more details on using hashes here.

Provenance

The following attestation bundles were made for semantic_cache_lib-0.1.0-py3-none-any.whl:

Publisher: publish.yml on iamsuryansh/semantic-cache-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page