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 and full-text search
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 invector_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_sizeonadd_texts,add_documents,from_textsorfrom_documents: larger batches mean fewer round trips to Redis and faster writes; smaller batches keep each write lighter. - Adjust the
kandfetch_kparameters 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.