Skip to main content

langchain-redis

This package contains the LangChain integration with Redis, providing powerful tools for vector storage, semantic caching, and chat history management.

Installation

pip install -U langchain-redis

This will install the package along with its dependencies, including redis, redisvl, and python-ulid.

To use LangCacheSemanticCache, install the optional langcache extra:

pip install -U "langchain-redis[langcache]"

Quickstart

Start Redis, install the package, and run a similarity search:

docker run -d --name redis -p 6379:6379 redis
from langchain_openai import OpenAIEmbeddings  # any LangChain Embeddings works
from langchain_redis import RedisVectorStore

vector_store = RedisVectorStore(
    OpenAIEmbeddings(),
    index_name="quickstart",
    redis_url="redis://localhost:6379",
)

vector_store.add_texts(["Redis is a fast in-memory database"])
docs = vector_store.similarity_search("quick key-value store", k=1)
print(docs[0].page_content)

Redis version compatibility

langchain-redis is tested against Redis 8.2 and newer. Most functionality works on any Redis with the Query Engine, with a few features gated by server version:

Feature Redis 8.2 Redis 8.4+
Vector search (KNN / range / MMR) ✓ ✓
Full-text search ✓ ✓
Hybrid search — aggregate method (FT.AGGREGATE) ✓ ✓
Hybrid search — native method (FT.HYBRID) ✗ ✓
SVS-VAMANA vector indexing (with compression) ✓ ✓

SVS-VAMANA indexing requires Redis 8.2 or newer.

hybrid_search() uses LINEAR fusion by default and detects the server version automatically: it uses FT.HYBRID on Redis 8.4+ and falls back to the aggregate method on older servers. RRF fusion is available explicitly on Redis 8.4+.

Configuration

Every component takes the connection as a redis_url argument (or a pre-existing client via redis_client; see Advanced Configuration for the RedisConfig class). The URL scheme selects the deployment mode:

redis_url = "redis://localhost:6379"                # standard
redis_url = "redis://username:password@host:6379"   # with authentication
redis_url = "rediss://host:6380"                    # SSL/TLS

# Redis Sentinel (high availability):
# redis+sentinel://[username:password@]host1:port1[,host2:port2,...]/service_name
redis_url = "redis+sentinel://sentinel1:26379,sentinel2:26379/mymaster"

The same URL works for every component:

from langchain_redis import RedisCache, RedisChatMessageHistory, RedisVectorStore
from langchain_openai import OpenAIEmbeddings

url = "redis+sentinel://sentinel1:26379,sentinel2:26379/mymaster"

vector_store = RedisVectorStore(
    OpenAIEmbeddings(), index_name="my_index", redis_url=url
)
cache = RedisCache(redis_url=url, ttl=3600)
history = RedisChatMessageHistory(session_id="user_123", redis_url=url)

Features

1. Vector Store

The RedisVectorStore class provides a vector database implementation using Redis.

Usage

from langchain_redis import RedisVectorStore, RedisConfig
from langchain_openai import OpenAIEmbeddings
from redisvl.query.filter import Tag

embeddings = OpenAIEmbeddings()  # any LangChain Embeddings implementation works

config = RedisConfig(
    index_name="my_vectors",
    redis_url="redis://localhost:6379",
    distance_metric="COSINE",  # Options: COSINE, L2, IP
    metadata_schema=[
        {"name": "category", "type": "tag"},
    ],
)

vector_store = RedisVectorStore(embeddings, config=config)

# Adding documents
texts = ["Document 1 content", "Document 2 content"]
metadatas = [{"category": "science"}, {"category": "history"}]
vector_store.add_texts(texts, metadatas=metadatas)

# Adding documents with custom keys
custom_keys = ["doc1", "doc2"]
vector_store.add_texts(texts, metadatas=metadatas, keys=custom_keys)

# Similarity search
query = "Sample query"
docs = vector_store.similarity_search(query, k=2)

# Similarity search with score
docs_and_scores = vector_store.similarity_search_with_score(query, k=2)

# Relevance scores (higher is more relevant) and score-threshold retrieval
docs_and_relevance = vector_store.similarity_search_with_relevance_scores(
    query, k=2, score_threshold=0.5
)
retriever = vector_store.as_retriever(
    search_type="similarity_score_threshold",
    search_kwargs={"k": 4, "score_threshold": 0.5},
)

# Similarity search with filtering
from redisvl.query.filter import Tag

filter_expr = Tag("category") == "science"
filtered_docs = vector_store.similarity_search(query, k=2, filter=filter_expr)

# Tag filters support wildcard patterns
filtered_docs = vector_store.similarity_search(
    query, k=2, filter=Tag("category") % "scien*"
)

# Maximum marginal relevance search
docs = vector_store.max_marginal_relevance_search(query, k=2, fetch_k=10)

Metadata is always stored and returned with documents, but only fields declared in metadata_schema are indexed and filterable. Filtering on an undeclared field raises an Unknown field error.

similarity_search_with_score returns the raw Redis distance, where lower is closer. The relevance scores above use RedisVL's normalization instead, so they fall in [0, 1] and higher is closer: (2 - d) / 2 for COSINE and 1 / (1 + d) for L2. The two scale differently, so pick score_threshold per index (with COSINE, unrelated vectors score about 0.5). IP indexes raise a ValueError, since RedisVL has no normalization for them. Use COSINE, which is normalized inner product. distance_threshold stays a raw distance when you combine it with these methods.

hybrid_search combines BM25 text scoring with vector similarity in a single query, and full_text_search exposes BM25-only retrieval. The examples below share this product catalog:

from redisvl.query.filter import Num, Tag

store = RedisVectorStore(
    embeddings,
    index_name="products",
    redis_url="redis://localhost:6379",
    metadata_schema=[
        {"name": "category", "type": "tag"},
        {"name": "title", "type": "text"},
        {"name": "price", "type": "numeric"},
    ],
)

store.add_texts(
    [
        "Lightweight running shoes with cushioned soles",
        "Waterproof hiking boots for rough mountain trails",
        "Ergonomic office chair with lumbar support",
    ],
    metadatas=[
        {"category": "footwear", "title": "running shoes", "price": 89},
        {"category": "footwear", "title": "hiking boots", "price": 129},
        {"category": "furniture", "title": "office chair", "price": 249},
    ],
)
# Hybrid search: text relevance + vector similarity, one ranked list.
# Uses LINEAR fusion with FT.HYBRID on Redis 8.4+ and transparently falls
# back to the equivalent FT.AGGREGATE-based combination on older servers.
docs = store.hybrid_search("running shoes", k=2)

# With scores, an explicit engine, and linear weighting:
# score = alpha * vector_score + (1 - alpha) * text_score
results = store.hybrid_search_with_score(
    "running shoes",
    k=2,
    method="aggregate",       # "auto" (default), "ft_hybrid", or "aggregate"
    alpha=0.5,
    filter=Tag("category") == "footwear",
)

# Full-text (BM25) search, weighting title matches higher than body text
docs = store.full_text_search(
    "running shoes", k=2, text_fields={"title": 5.0, "text": 1.0}
)

Hybrid scores are combined scores (rank-based when RRF is selected), not comparable with the cosine distances returned by similarity_search_with_score.

Filter-based deletion

Documents can be deleted by what they are, not just by id, using the same FilterExpression type as search. This is useful for re-syncing a changed source, tenant offboarding, or retention policies. Continuing with the product catalog above:

# Delete every document matching a filter
result = store.delete_by_filter(Tag("category") == "furniture")
print(result.processed, result.completed)

# Preview a purge before running it
preview = store.delete_by_filter(Num("price") > 100, dry_run=True)
print(preview.matched)

delete_by_filter() returns RedisVL's BulkResult. Check completed as well as processed: RedisVL can stop a bulk deletion early under concurrent writes, in which case matching documents may remain.

Filter deletion is automatically scoped to the store's own index, so indexes sharing a key_prefix cannot delete each other's documents. Generated schemas include the required exact _index_name TAG marker; existing or custom schemas without that marker must be migrated or recreated before searching, filter deletion, or direct-ID access. Existing custom TAG fields that store the raw index name must also be migrated or reindexed: current schemas store a hashed index-ownership marker, so changing only the field type is not sufficient. Until migration, direct-ID operations remain available for legacy TEXT markers, but searches and filter deletion are refused because TEXT tokenization cannot provide exact index isolation.

Migrating legacy ownership markers

Use migrate_index_ownership() to preview and upgrade a legacy index. Supply the original, fully populated RedisConfig for a generated schema, or the original IndexSchema for a custom schema. Every call, including previews and reruns, requires exactly one source, validated against the live index.

TEMPORARY indexes are unsupported: rebuilding them deletes their documents.

Use a separate migration client with protocol=2, connected to the application's Redis deployment. Explicit protocol=3 clients are rejected before any Redis operations because migration does not yet support their query and cursor replies.

from redis import Redis

from langchain_redis import migrate_index_ownership

migration_client = Redis.from_url(redis_url, protocol=2)

# Use the same definition that originally created the index.
migration_source = {"source_config": original_config}
# For a custom schema, use this instead:
# migration_source = {"source_schema": original_schema}

aliases = ["products-search"]  # Use [] when the index has no aliases.
preview = migrate_index_ownership(
    "products",
    redis_client=migration_client,
    aliases=aliases,
    **migration_source,
)
print(preview.schema_action, preview.would_update)

# Stop all writers and back up the index before applying.
result = migrate_index_ownership(
    "products",
    redis_client=migration_client,
    aliases=aliases,
    dry_run=False,
    **migration_source,
)
print(result.updated, result.completed)
# Restart writers and recreate existing RedisVectorStore instances afterwards.

Generated configurations must include embedding_dimensions. Validation checks reported core vector settings and vector INDEXMISSING. All vector tuning is taken from the supplied source without comparison; preserve the original tuning and phonetic settings there. Indexes with overlapping prefixes of the same storage type cannot be migrated automatically because ownership of legacy documents is ambiguous. Recreate those indexes with disjoint prefixes instead.

Vector index tuning

vector_attrs exposes algorithm-specific index attributes without writing a full custom schema:

# HNSW with tuned build/query parameters
config = RedisConfig(
    index_name="tuned_hnsw",
    indexing_algorithm="HNSW",
    vector_attrs={"m": 16, "ef_construction": 200, "ef_runtime": 20},
)

# SVS-VAMANA (Redis 8.2+) with vector compression for reduced memory
config = RedisConfig(
    index_name="compressed",
    indexing_algorithm="SVS-VAMANA",
    vector_attrs={"compression": "LeanVec4x8", "reduce": 256},
)

Using an existing index or schema

To connect to an index that already exists in Redis (created by another process, or by redisvl directly):

vector_store = RedisVectorStore.from_existing_index(
    index_name="products",
    embedding=embeddings,
    redis_url="redis://localhost:6379",
)

A redisvl IndexSchema or schema YAML file can also define the index instead of the individual config fields:

from redisvl.schema import IndexSchema

schema = IndexSchema.from_yaml("index.yaml")   # or IndexSchema.from_dict(...)
config = RedisConfig(schema=schema, redis_url="redis://localhost:6379")
vector_store = RedisVectorStore(embeddings, config=config)

Searches are always scoped by the internal _index_name ownership field. Generated schemas add this exact TAG field automatically. Existing and custom schemas must define _index_name as TAG before they can be searched; missing, legacy TEXT, and unsupported marker fields are refused instead of running without exact index isolation. Build metadata filters with RedisVL FilterExpression helpers such as Tag and Num when values come from user input. Non-empty raw Redis query strings are rejected because they cannot be safely combined with the index ownership filter.

Features

  • Efficient vector storage and retrieval
  • Hybrid search combining BM25 text scoring and vector similarity (RRF or linear fusion), with automatic engine selection by server version
  • Full-text (BM25) search with per-field weighting
  • Support for metadata filtering, including wildcard tag patterns
  • Filter-based bulk deletion with dry-run support
  • Multiple distance metrics: Cosine similarity, L2, and Inner Product
  • FLAT, HNSW, and SVS-VAMANA indexing algorithms with tunable algorithm-specific attributes (including SVS-VAMANA vector compression)
  • Maximum marginal relevance search
  • Custom key support for document indexing
  • Multi-prefix indexes: one searchable index spanning several key namespaces
  • Index-level stopwords configuration

2. Cache

The RedisCache, RedisSemanticCache, and LangCacheSemanticCache classes provide caching mechanisms for LLM calls.

Usage

from langchain_redis import RedisCache, RedisSemanticCache, LangCacheSemanticCache
from langchain_openai import OpenAI, OpenAIEmbeddings
from langchain_core.outputs import Generation

# Standard cache
cache = RedisCache(redis_url="redis://localhost:6379", ttl=3600)

# Semantic cache
embeddings = OpenAIEmbeddings()  # any LangChain Embeddings implementation works
semantic_cache = RedisSemanticCache(
    redis_url="redis://localhost:6379",
    embeddings=embeddings,
    distance_threshold=0.1
)

# LangCache semantic cache - manages embeddings for you
langcache_cache = LangCacheSemanticCache(
    cache_id="your-cache-id",
    api_key="your-api-key",
    distance_threshold=0.1
)

# Using cache with an LLM
llm = OpenAI(cache=cache)  # or semantic_cache / langcache_cache

# Async cache operations
await cache.aupdate("prompt", "llm_string", [Generation(text="cached_response")])
cached_result = await cache.alookup("prompt", "llm_string")

Features

  • Efficient caching of LLM responses
  • TTL support for automatic cache expiration
  • Semantic caching for similarity-based retrieval
  • Asynchronous cache operations

Upgrading an existing semantic cache

RedisSemanticCache scopes entries by LLM with a tag field in its index. An index created by an earlier version raises a ValueError until it is upgraded. Stop the applications using the cache, then run:

RedisSemanticCache.migrate_index(
    redis_url="redis://localhost:6379",
    name="llmcache",
    backfill=False,
)

This keeps the existing keys, but entries cached before the upgrade are no longer returned. Set backfill=True to also migrate those entries and keep the cache warm; this scans the whole cache and is safe to rerun if it gets interrupted.

What is Redis LangCache?

  • LangCache is a fully managed, cloud-based service that provides a semantic cache for LLM applications.
  • It manages embeddings and vector search for you, allowing you to focus on your application logic.
  • See our docs to learn more, or try LangCache on Redis Cloud today.

3. Chat History

The RedisChatMessageHistory class provides a Redis-based storage for chat message history with efficient search capabilities.

Usage

from langchain_redis import RedisChatMessageHistory
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

# Initialize with optional TTL (time-to-live) in seconds
history = RedisChatMessageHistory(
    session_id="user_123",
    redis_url="redis://localhost:6379",
    ttl=3600,  # Messages will expire after 1 hour
)

# Adding messages
history.add_message(HumanMessage(content="Hello, AI!"))
history.add_message(AIMessage(content="Hello, human! How can I assist you today?"))
history.add_message(SystemMessage(content="This is a system message"))

# Retrieving all messages in chronological order
messages = history.messages

# Searching messages with full-text search
results = history.search_messages("assist", limit=5)  # Returns matching messages

# Get message count
message_count = len(history)

# Clear history for current session
history.clear()

# Delete all sessions and index (use with caution)
history.delete()

Features

  • Fast storage of chat messages with automatic expiration (TTL)
  • Support for different message types (Human, AI, System)
  • Full-text search capabilities across message content
  • Chronological message retrieval
  • Session-based message organization
  • Customizable key prefixing
  • Thread-safe operations
  • Efficient RedisVL-based indexing and querying

Advanced Configuration

The RedisConfig class allows for detailed configuration of the Redis integration:

from langchain_redis import RedisConfig

config = RedisConfig(
    index_name="my_index",
    redis_url="redis://localhost:6379",
    distance_metric="COSINE",
    key_prefix="my_prefix",
    vector_datatype="FLOAT32",
    storage_type="hash",
    metadata_schema=[
        {"name": "category", "type": "tag"},
        {"name": "price", "type": "numeric"}
    ]
)

Available options include:

  • key_prefix: a single prefix, or a list of prefixes (e.g. key_prefix=["kb:team_acme", "kb:global"]). With a list, searches cover every namespace and new documents are written under the first prefix.
  • stopwords: index-level stopwords. None (default) uses the Redis defaults, [] disables them (STOPWORDS 0), and a list replaces them.
  • indexing_algorithm: "FLAT", "HNSW", or "SVS-VAMANA". Algorithm-specific parameters go in vector_attrs (see Vector index tuning above).

Refer to the inline documentation for detailed information on these configuration options.

Error Handling and Logging

The package uses Python's standard logging module. You can configure logging to get more information about the package's operations:

import logging
logging.basicConfig(level=logging.INFO)

The package doesn’t define custom exceptions. It raises standard Python exceptions (mainly ValueError for invalid input) and lets Redis and RedisVL errors propagate.

Performance Considerations

  • When loading large datasets, tune batch_size on add_texts, add_documents, from_texts or from_documents: larger batches mean fewer round trips to Redis and faster writes; smaller batches keep each write lighter.
  • Adjust the k and fetch_k parameters in similarity searches to balance between accuracy and performance.
  • Use appropriate indexing algorithms (FLAT, HNSW, SVS-VAMANA) based on your dataset size and query requirements.

Examples

For more detailed examples and use cases, please refer to the docs/ directory in this repository.

Contributing / Development

The library is rooted at libs/redis. For all the commands below, cd to libs/redis:

Unit Tests

To install dependencies for unit tests:

poetry install --with test

To run unit tests:

make test

To run a specific test:

TEST_FILE=tests/unit_tests/test_imports.py make test

Integration Tests

You would need an OpenAI API Key to run the integration tests:

export OPENAI_API_KEY=sk-J3nnYJ3nnYWh0Can1Turnt0Ug1VeMe50mth1n1cAnH0ld0n2

To install dependencies for integration tests:

poetry install --with test,test_integration

To run integration tests:

make integration_tests

Local Development

Install langchain-redis development requirements (for running langchain, running examples, linting, formatting, spell checking, tests, and coverage):

poetry install --with lint,typing,test,test_integration,codespell

Then verify dependency installation:

make lint

License

This project is licensed under the MIT License (LICENSE).

Metadata

Release files for langchain-redis 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langchain-redis 0.3.0
File Size Uploaded Provenance Actions
langchain_redis-0.3.0.tar.gz 63.8 kB Without provenance Details for langchain_redis-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for langchain-redis 0.3.0
File Interpreter ABI Platform Actions
langchain_redis-0.3.0-py3-none-any.whl Python 3 none any Details for langchain_redis-0.3.0-py3-none-any.whl

Total release size: 124.7 kB

Release files / langchain_redis-0.3.0.tar.gz

Download URL langchain_redis-0.3.0.tar.gz
Size 63.8 kB
Tags Source
SHA-256 checksum
How to use checksums
643e3be1e77d91099de187820a4e29e24832218e99f875ac1ec574f93796b04e
BLAKE2b-256 checksum
How to use checksums
5008235eea6677cf8d5a0db2b1dd5f5f63982714e57fc55986a0399ff14a60f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / langchain_redis-0.3.0-py3-none-any.whl

Download URL langchain_redis-0.3.0-py3-none-any.whl
Size 60.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e76109e16df7ca44f700d878ae637ebcb0063a8c145c5c98ee25e6baa02a1ef5
BLAKE2b-256 checksum
How to use checksums
1a673c80694aee74eec373cbb8b75294589fcaea0c20474defacc3f7711d0c44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Security for langchain-redis 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page