Giter Club home page Giter Club logo

redis-vl-python's Introduction

🔥 Redis Vector Library

the AI-native Redis Python client

Codecov License: MIT Language Code style: black GitHub last commit GitHub deployments pypi

Home    Documentation    More Projects   

Introduction

The Python Redis Vector Library (RedisVL) is a tailor-made client for AI applications leveraging Redis.

It's specifically designed for:

  • Information retrieval & vector similarity search
  • Real-time RAG pipelines
  • Recommendation engines

Enhance your applications with Redis' speed, flexibility, and reliability, incorporating capabilities like vector-based semantic search, full-text search, and geo-spatial search.

🚀 Why RedisVL?

The emergence of the modern GenAI stack, including vector databases and LLMs, has become increasingly popular due to accelerated innovation & research in information retrieval, the ubiquity of tools & frameworks (e.g. LangChain, LlamaIndex, EmbedChain), and the never-ending stream of business problems addressable by AI.

However, organizations still struggle with delivering reliable solutions quickly (time to value) at scale (beyond a demo).

Redis has been a staple for over a decade in the NoSQL world, and boasts a number of flexible data structures and processing engines to handle realtime application workloads like caching, session management, and search. Most notably, Redis has been used as a vector database for RAG, as an LLM cache, and chat session memory store for conversational AI applications.

The vector library bridges the gap between the emerging AI-native developer ecosystem and the capabilities of Redis by providing a lightweight, elegant, and intuitive interface. Built on the back of the popular Python client, redis-py, it abstracts the features Redis into a grammar that is more aligned to the needs of today's AI/ML Engineers or Data Scientists.

💪 Getting Started

Installation

Install redisvl into your Python (>=3.8) environment using pip:

pip install redisvl

For more instructions, visit the redisvl installation guide.

Setting up Redis

Choose from multiple Redis deployment options:

  1. Redis Cloud: Managed cloud database (free tier available)
  2. Redis Stack: Docker image for development
    docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
  3. Redis Enterprise: Commercial, self-hosted database
  4. Azure Cache for Redis Enterprise: Fully managed Redis Enterprise on Azure

Enhance your experience and observability with the free Redis Insight GUI.

What's included?

🗃️ Redis Index Management

  1. Design an IndexSchema that models your dataset with built-in Redis data structures (Hash or JSON) and indexable fields (e.g. text, tags, numerics, geo, and vectors).

    Load a schema from a YAML file:

    index:
        name: user-index-v1
        prefix: user
        storage_type: json
    
    fields:
      - name: user
        type: tag
      - name: credit_score
        type: tag
      - name: embedding
        type: vector
        attrs:
          algorithm: flat
          dims: 3
          distance_metric: cosine
          datatype: float32
    from redisvl.schema import IndexSchema
    
    schema = IndexSchema.from_yaml("schemas/schema.yaml")

    Or load directly from a Python dictionary:

    schema = IndexSchema.from_dict({
        "index": {
            "name": "user-index-v1",
            "prefix": "user",
            "storage_type": "json"
        },
        "fields": [
            {"name": "user", "type": "tag"},
            {"name": "credit_score", "type": "tag"},
            {
                "name": "embedding",
                "type": "vector",
                "attrs": {
                    "algorithm": "flat",
                    "datatype": "float32",
                    "dims": 4,
                    "distance_metric": "cosine"
                }
            }
        ]
    })
  2. Create a SearchIndex class with an input schema and client connection in order to perform admin and search operations on your index in Redis:

    from redis import Redis
    from redisvl.index import SearchIndex
    
    # Establish Redis connection and define index
    client = Redis.from_url("redis://localhost:6379")
    index = SearchIndex(schema, client)
    
    # Create the index in Redis
    index.create()

    Async compliant search index class also available: AsyncSearchIndex

  3. Load and fetch data to/from your Redis instance:

    data = {"user": "john", "credit_score": "high", "embedding": [0.23, 0.49, -0.18, 0.95]}
    
    # load list of dictionaries, specify the "id" field
    index.load([data], id_field="user")
    
    # fetch by "id"
    john = index.fetch("john")

🔍 Realtime Search

Define queries and perform advanced searches over your indices, including the combination of vectors, metadata filters, and more.

  • VectorQuery - Flexible vector queries with customizable filters enabling semantic search:

    from redisvl.query import VectorQuery
    
    query = VectorQuery(
      vector=[0.16, -0.34, 0.98, 0.23],
      vector_field_name="embedding",
      num_results=3
    )
    # run the vector search query against the embedding field
    results = index.query(query)

    Incorporate complex metadata filters on your queries:

    from redisvl.query.filter import Tag
    
    # define a tag match filter
    tag_filter = Tag("user") == "john"
    
    # update query definition
    query.set_filter(tag_filter)
    
    # execute query
    results = index.query(query)
  • RangeQuery - Vector search within a defined range paired with customizable filters

  • FilterQuery - Standard search using filters and the full-text search

  • CountQuery - Count the number of indexed records given attributes

Read more about building advanced Redis queries here.

🖥️ Command Line Interface

Create, destroy, and manage Redis index configurations from a purpose-built CLI interface: rvl.

$ rvl -h

usage: rvl <command> [<args>]

Commands:
        index       Index manipulation (create, delete, etc.)
        version     Obtain the version of RedisVL
        stats       Obtain statistics about an index

Read more about using the redisvl CLI here.

⚡ Community Integrations

Integrate with popular embedding models and providers to greatly simplify the process of vectorizing unstructured data for your index and queries:

from redisvl.utils.vectorize import CohereTextVectorizer

# set COHERE_API_KEY in your environment
co = CohereTextVectorizer()

embedding = co.embed(
    text="What is the capital city of France?",
    input_type="search_query"
)

embeddings = co.embed_many(
    texts=["my document chunk content", "my other document chunk content"],
    input_type="search_document"
)

Learn more about using redisvl Vectorizers in your workflows here.

💫 Beyond Vector Search

In order to perform well in production, modern GenAI applications require much more than vector search for retrieval. redisvl provides some common extensions that aim to improve applications working with LLMs:

  • LLM Semantic Caching is designed to increase application throughput and reduce the cost of using LLM models in production by leveraging previously generated knowledge.

    from redisvl.extensions.llmcache import SemanticCache
    
    # init cache with TTL (expiration) policy and semantic distance threshhold
    llmcache = SemanticCache(
        name="llmcache",
        ttl=360,
        redis_url="redis://localhost:6379"
    )
    llmcache.set_threshold(0.2) # can be changed on-demand
    
    # store user queries and LLM responses in the semantic cache
    llmcache.store(
        prompt="What is the capital city of France?",
        response="Paris",
        metadata={}
    )
    
    # quickly check the cache with a slightly different prompt (before invoking an LLM)
    response = llmcache.check(prompt="What is France's capital city?")
    print(response[0]["response"])
    >>> "Paris"
    

    Learn more about Semantic Caching here.

  • LLM Session Management (COMING SOON) aims to improve personalization and accuracy of the LLM application by providing user chat session information and conversational memory.

  • LLM Contextual Access Control (COMING SOON) aims to improve security concerns by preventing malicious, irrelevant, or problematic user input from reaching LLMs and infrastructure.

Helpful Links

To get started, check out the following guides:

🫱🏼‍🫲🏽 Contributing

Please help us by contributing PRs, opening GitHub issues for bugs or new feature ideas, improving documentation, or increasing test coverage. Read more about how to contribute!

🚧 Maintenance

This project is supported by Redis, Inc on a good faith effort basis. To report bugs, request features, or receive assistance, please file an issue.

redis-vl-python's People

Contributors

tylerhutcherson avatar justin-cechmanek avatar bsbodden avatar chayim avatar ajac-zero avatar eltociear avatar msfteegarden avatar mankerious avatar rbs333 avatar joshrotenberg avatar nabsabraham avatar

Stargazers

DsHan avatar  avatar Luigi Mazzon avatar Zachary Taylor avatar Steve avatar Fahd Labba avatar Randall Puterbaugh avatar alleniver avatar hitrust avatar  avatar Yaozhi Wang avatar Allen S, bookman avatar Pierre Lambert avatar Vu Anh Duc avatar George Kontridze avatar Lemuel Okoli avatar Chih-Yu Yeh avatar  avatar Leon avatar  avatar  avatar  avatar Mardix avatar Kenneth Goh avatar  avatar Daniel Saad avatar Michael XU avatar Shane Utt avatar György Kiss avatar Dan Goodman avatar Burden Haze  avatar  avatar  avatar  avatar  avatar Matheus José Geraldini dos Santos avatar Pete Tanski avatar tom zhou avatar Hannu Ylitalo avatar Coco avatar  avatar Zollty Tsou avatar Max You avatar  avatar  avatar Matteo avatar Justin Bowen avatar Rafael Pierre avatar Neet Tejani avatar Muhibbudin Suretno avatar Night-Quiet avatar Abdellah avatar Karol Burdziński avatar vanguard_space avatar  avatar Noah Gardner avatar Tarran Benson-West avatar vvanglro avatar  avatar Alon Valadji avatar matt burnett avatar Federico Zaiter avatar Puru Jaiswal avatar Harish Ranganathan avatar Anton Umnikov avatar Leslie-Alexandre DENIS avatar Aritro Pal Choudhury avatar  avatar  avatar  avatar  avatar  avatar Ishaan Jaff avatar Dongwon Kim avatar SHOOBI avatar Varun Talwar avatar Caleb John avatar Otoonat avatar Diego Francisco Valenzuela Iturra avatar HeYun avatar Jeev B avatar Anton Litvinov avatar  avatar Alejandro Rojas avatar  avatar Chuan Lim avatar Thomas Mickley-Doyle avatar Zander avatar Dhruv Anand avatar DylanXu avatar Oswald avatar  avatar Jeremy Hutterer avatar Touchumind avatar Patrick Lurch avatar Jeremy Tregunna avatar  avatar Conor Sinclair avatar  avatar  avatar

Watchers

Kyle Banker avatar  avatar vanguard_space avatar Mirko Ortensi avatar Sam Partee avatar Dhruv Kumar Jha avatar Arun Sathiya avatar  avatar Kostas Georgiou avatar  avatar  avatar

redis-vl-python's Issues

Publish on PyPI through actions

Description

Github action workflow to publish the redisvl package to GitHub Actions.

Acceptance Criteria

  • Github action to publish to pypi
  • Manually test with test.pypi

Implement redisvl index command in CLI

Description

Implement the index management command for creating, reading, and deleting an index.

Interface

usage: redisvl index <command> [<args>]

Index creation and manipulation commands

Commands:
        list        List available indices
        create      Create a new index
        delete      delete an existing index


positional arguments:
  command     Subcommand to run

optional arguments:
  -h, --help  show this help message and exit
  -s SCHEMA, --schema SCHEMA
                        Path to schema file
  --host HOST           Redis host
  -p PORT, --port PORT  Redis port
  -a PASSWORD, --password PASSWORD

Will have to wait until SearchIndex class in the library is completed in #7

Acceptance Criteria

  • redisvl index list
  • redisvl index create
  • redisvl index delete

Support for OpenAI API >= 1.0.0

RedisVL currently only supports the OpenAI API for versions < 1.0.0. This prevents the use of features in more current versions of Langchain, e.g. langchain_openai.

How to apply filter on a semantic cache check() function

Here is how i am using semantic cache

SemanticCacheObject = SemanticCache(
    name=cache_index,                               # underlying search index name
    prefix=cache_prefix,                            # redis key prefix for hash entries
    redis_url=redis_url,                            # redis connection url string
    distance_threshold=distance_threshold,          # semantic cache distance threshold
    ttl=ttl                                         # time to live each key in cache
)
query_embedding = <embedding of my query>
SemanticCacheObject.check(vector=query_embedding, return_fields=["prompt", "response", "metadata"]):

Now I want to filter the responses based on a specific column in the index, before applying the vector search.
Then how to apply filter condition on this semantic cache index in check() function.
Eg: I am expecting something like this

SemanticCacheObject.check(vector=query_embedding, return_fields=["prompt", "response", "metadata"], 
filter = <column name> )

Is this supported now in redis vl?

Improve & Enrich the LLM client implementation for TextVectorizer

  1. There is no direct way today to use another service than the OpenAI.
    It would be nice to be able to use any service with an API compatible with the OpenAI one.
    The target host seems to be hardcoded towards the Open AI service, I may want to use an on-prem/local LLM service, but as for today this is not possible.
  2. Add support for mistral.ai equivalent

Build better conditional filter expression interface

Currently, using redisvl there is a pattern emerging from the FilterExpression usage. Functions like the following are commonly being written by end users.

def build_filter_expression(years: List[int], categories: List[str]) -> FilterExpression:
    """
    Construct a filter expression based on the provided years and categories.
    Args:
        years (list): A list of years (integers or strings) to be included in the filter
                      expression. An empty list means there's no filter applied based on years.
        categories (list): A list of category strings to be included in the filter
                           expression. An empty list means there's no filter applied based
                           on categories.
    Returns:
        FilterExpression: A FilterExpression object representing the combined filter for both years and categories.
    """
    # Build filters
    year_filter = Tag("year") == [str(year) for year in years] if years else None
    category_filter = Tag("categories") == [str(category) for category in categories] if categories else None
    # Parse and create filter expression
    if not year_filter and not category_filter:
        return FilterExpression("*")
    if year_filter and category_filter:
        return year_filter & category_filter
    return year_filter or category_filter

This function is verbose and there are two specific problems

the presence of conditional values i.e. None
Chaining given the presence of conditionals

Deliverable:
Design and implement and approach that allows for a conditional value to be passed
Ensure that if None is passed, the following chaining operation is not effected.

example, the function should be able to look like.

def build_filter_expression(years: List[int], categories: List[str]) -> FilterExpression:
    """
    Construct a filter expression based on the provided years and categories.
    Args:
        years (list): A list of years (integers or strings) to be included in the filter
                      expression. An empty list means there's no filter applied based on years.
        categories (list): A list of category strings to be included in the filter
                           expression. An empty list means there's no filter applied based
                           on categories.
    Returns:
        FilterExpression: A FilterExpression object representing the combined filter for both years and categories.
    """
    # Build filters
    year_filter = Tag("year") == [str(year) for year in years]
    category_filter = Tag("categories") == [str(category) for category in categories]
    return year_filter & category_filter

AC:
Implement this capability with tests and examples in the notebooks.

Unable to pass redis client when loading existing index

When loading an existing index, get_redis_connection method in connection.py should allow passing of an existing redis client instance as well

for example :
index = SearchIndex.from_dict(schema)
index.set_client(redis)

works

But, this won’t work for SerachIndex.from_existing

This prevents users from passing redis connection objects created outside to be passed into while loading an existing index

Getting started example

Description

Jupyter Notebook that takes one of the current simple test examples and walks through using redisvl as a python library and a cli.

Acceptance Criteria

  • Well explained notebook with example usage for simple data in a jupyter notebook.

how does num_results work ?

If i set num_results = 1, will it return the top match ?

query = VectorQuery(
            vector=[0.1, 0.1, 0.5],
            vector_field_name="litellm_embedding",
            return_fields=["response", "prompt", "vector_distance"],
            num_results=1
        )

[Bug] Async Caching - object list can't be used in 'await' expression

Hello, I'm trying to use async mode - seeing this bug, can I get help ? cc @tylerhutcherson

Code to repro

Create Index

def __init__(
        self,
        host=None,
        port=None,
        password=None,
        redis_url=None,
        similarity_threshold=None,
        use_async = False,
        **kwargs,
    ):
        from redisvl.index import SearchIndex
        from redisvl.query import VectorQuery

        print_verbose(
            "redis semantic-cache initializing INDEX - litellm_semantic_cache_index"
        )
        if similarity_threshold is None:
            raise Exception("similarity_threshold must be provided, passed None")
        self.similarity_threshold = similarity_threshold
        schema = {
            "index": {
                "name": "litellm_semantic_cache_index",
                "prefix": "litellm",
                "storage_type": "hash",
            },
            "fields": {
                "text": [{"name": "response"}],
                "text": [{"name": "prompt"}],
                "vector": [
                    {
                        "name": "litellm_embedding",
                        "dims": 1536,
                        "distance_metric": "cosine",
                        "algorithm": "flat",
                        "datatype": "float32",
                    }
                ],
            },
        }
        if redis_url is None:
            # if no url passed, check if host, port and password are passed, if not raise an Exception
            if host is None or port is None or password is None:
                raise Exception(f"Redis host, port, and password must be provided")
            redis_url = "redis://:" + password + "@" + host + ":" + port
        
        print_verbose(f"redis semantic-cache redis_url: {redis_url}")
        if use_async == False:
            self.index = SearchIndex.from_dict(schema)
            self.index.connect(redis_url=redis_url)
            try:
                self.index.create(overwrite=False)  # don't overwrite existing index
            except Exception as e:
                print_verbose(f"Got exception creating semantic cache index: {str(e)}")
        elif use_async == True:
            print("creating async semantic cache index")
            schema["index"]["name"] = "litellm_semantic_cache_index_async"
            self.index = SearchIndex.from_dict(schema, use_async=True)
            self.index.connect(redis_url=redis_url)
            try:
                self.index.create(overwrite=False)  # don't overwrite existing index
            except Exception as e:
                print_verbose(f"Got exception creating semantic cache index: {str(e)}")

Query index

        from redisvl.query import VectorQuery
        import numpy as np

        # query

        # get the messages
        messages = kwargs["messages"]
        prompt = ""
        for message in messages:
            prompt += message["content"]

        # convert to embedding
        embedding_response = await litellm.aembedding(
            model="text-embedding-ada-002",
            input=prompt,
            cache={"no-store": True, "no-cache": True},
        )

        print("async got embedding_response", embedding_response)

        # get the embedding
        embedding = embedding_response["data"][0]["embedding"]

        query = VectorQuery(
            vector=embedding,
            vector_field_name="litellm_embedding",
            return_fields=["response", "prompt", "vector_distance"],
        )
        print("async querying semantic cache", query)
        results = await self.index.aquery(query)
        print("async got results", results)

stacktrace

An exception occurred: Traceback (most recent call last):
  File "/Users/ishaanjaffer/Github/litellm/litellm/caching.py", line 972, in async_get_cache
    cached_result = await self.cache.async_get_cache(cache_key, messages=messages)
  File "/Users/ishaanjaffer/Github/litellm/litellm/caching.py", line 472, in async_get_cache
    results = await self.index.aquery(query)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/redisvl/index.py", line 106, in wrapper
    await check_async_redis_modules_exist(_redis_conn.client)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/redisvl/utils/utils.py", line 60, in check_async_redis_modules_exist
    installed_modules = await client.module_list()
TypeError: object list can't be used in 'await' expression

How to hold / cache a SemanticCache instance?

Hi, is there a way to hold a pre defined redis cache instance somewhere? I currently define everytime a request comes in a new one what takes way too loong.
So there must be the apprich to hold it somehow? Everything i can think about is pickling it and save it but that sounds not right. What are your approaches?

[Feat] Add index.ping()

After initializing an index() I need to know if it was created successfully, Can you expose something like this ?

Implement Reader API and Pandas support

Description

redisvl.readers will be a module of classes named after the method used to read data from specific formats. For example, the pandas reader will be implemented first with support with csv, and pickled dataframe formats.

Interface

class Reader:

    support_map = {
        "pickle": self._from_pickle,
        "json": self._from_json,
        "parquet": self._from_parquet,
        "csv": self._from_csv
    }

    def __init__(data_format="pickle"):
        self._data_format = data_format

    @staticmethod
    def _from_pickle() -> t.Iterable[t.Dict[str, t.Any]]:

    @staticmethod
    def _from_json() -> t.Iterable[t.Dict[str, t.Any]]:

    @staticmethod
    def _from_parquet() -> t.Iterable[t.Dict[str, t.Any]]:
    
    @staticmethod
    def _from_csv() -> t.Iterable[t.Dict[str, t.Any]]:

    def __iter__():
        self.support_map[self._data_format]()

The Reader will be imported by the user and passed to the SearchIndex class. The SearchIndex will use the Reader to get data prior to loading it into redis.

The interface isn't complete stable, but should be more flushed out after the pandas reader is adapted to it.

Out of scope here

out of scope items, but worth thinking about while developing.

  • multifile reader
  • distributed reader (i.e. Dask)
  • larger than memory reader
  • conversion of vectors to bytes

Acceptance Criteria

  • Implemented the redisvl.readers.pandas module adhering to the above interface.
  • tests confirming support for
    • Pickled dataframe
    • csv
  • Use in CLI load command

Enforce uniqueness of fieldnames

An easy fix... but this is something we should address to prevent any footguns:

index = SearchIndex.from_dict({"index": {"name": "tyler"}, "fields": {"text": [{"name": "test"}], "vector": [{"name": "test", "dims": 3, "algorithm": "flat"}]}})

for example yields an error from Redis because there is a duplicate field name.

One possibility for fixing this is to flip the schema spec on it's head and make the field name the "key" by which we organize the schema:

index:
    name: hash-test
    prefix: hash
    storage_type: hash

fields:
    sentence:
        - type: text
    embedding:
        - type: vector
          dims: 768
          algorithm: flat
          distance_metric: cosine

How to access aws bed rock embedding models for vectorizer?

I want to use Amazon bedrock models as vectorizer but there is no such vectorizer. How can i use such embedding models?

I have created a custom vectorizer:

`import os
import boto3
from typing import Callable, Dict, List, Optional

from tenacity import retry, stop_after_attempt, wait_random_exponential
from langchain_community.embeddings.bedrock import BedrockEmbeddings
from tenacity.retry import retry_if_not_exception_type

from redisvl.utils.vectorize.base import BaseVectorizer

class AmazonBedrockTextVectorizer(BaseVectorizer):

def __init__(
    self, model_id: str = "amazon.titan-embed-text-v1", api_config: Optional[Dict] = None
):
    # Set up AWS credentials
    aws_access_key_id = api_config.get("aws_access_key_id") if api_config else os.getenv("AWS_ACCESS_KEY_ID")
    aws_secret_access_key = api_config.get("aws_secret_access_key") if api_config else os.getenv("AWS_SECRET_ACCESS_KEY")
    region_name = api_config.get("region_name", "us-east-1") if api_config else os.getenv("AWS_REGION", "us-east-1")
    
    if not aws_access_key_id or not aws_secret_access_key:
        raise ValueError("AWS access key and secret key are required.")
    
    # Initialize Bedrock client
    client = boto3.client(
        'bedrock-runtime',
        aws_access_key_id=aws_access_key_id,
        aws_secret_access_key=aws_secret_access_key,
        region_name=region_name
    )

    # print(f"Client --> {client}")

    dims = self._set_model_dims(model=model_id, client=client)
    super().__init__(model=model_id, dims=dims, client=client)

@staticmethod
def _set_model_dims(client, model) -> int:
    try:
        bedrock_em_model = BedrockEmbeddings(model_id=model,client=client)
        embedding = bedrock_em_model.embed_query("dimension test")
    except (KeyError, IndexError) as ke:
        raise ValueError(f"Unexpected response from the Cohere API: {str(ke)}")
    except Exception as e:  # pylint: disable=broad-except
        # fall back (TODO get more specific)
        raise ValueError(f"Error setting embedding model dimensions: {str(e)}")
    return len(embedding)

@retry(
    wait=wait_random_exponential(min=1, max=60),
    stop=stop_after_attempt(6),
    retry=retry_if_not_exception_type(TypeError),
)
def embed(
    self,
    text: str,
    preprocess: Optional[Callable] = None,
    as_buffer: bool = False,
    **kwargs,
) -> List[float]:
    if not isinstance(text, str):
        raise TypeError("Must pass in a str value to embed.")

    if preprocess:
        text = preprocess(text)
    
    bedrock_em_model = BedrockEmbeddings(model_id=self.model,client=self.client)

    embedding = bedrock_em_model.embed_query(text)

    # print(f"\n\n **** Embedding: {embedding} ****")

    return self._process_embedding(embedding, as_buffer)

@retry(
    wait=wait_random_exponential(min=1, max=60),
    stop=stop_after_attempt(6),
    retry=retry_if_not_exception_type(TypeError),
)
def embed_many(
    self,
    texts: List[str],
    preprocess: Optional[Callable] = None,
    batch_size: int = 10,
    as_buffer: bool = False,
    **kwargs,
) -> List[List[float]]:
    input_type = kwargs.get("input_type")

    if not isinstance(texts, list):
        raise TypeError("Must pass in a list of str values to embed.")
    if len(texts) > 0 and not isinstance(texts[0], str):
        raise TypeError("Must pass in a list of str values to embed.")
    if not isinstance(input_type, str):
        raise TypeError(
            "Must pass in a str value for cohere embedding input_type.\
                See AWS" 
        )

    embeddings: List = []

    bedrock_em_model = BedrockEmbeddings(model_id=self.model,client=self.client)

    for batch in self.batchify(texts, batch_size, preprocess):


        response = bedrock_em_model.embed_query(texts=batch)

        embeddings += [
            self._process_embedding(embedding, as_buffer)
            for embedding in response.embeddings
        ]

    # print(f"\n\n **** Embeddings: {embeddings} ****")
    return embeddings`

It is working perfectly and creating vectors.

But when I check the vectors dimensions for Huggingface, OpenAI, AmazonTitan Models

Hf : 768 AZ : 1536 OpenAI : 1536

Here I found one similarity that amazon titan and OpenAI models have same dimensions. and its greater than HFace.

I guess that why it giving this error:

ResponseError: Error parsing vector similarity query: query vector blob size (6144) does not match index's expected size (3072).

While using Hugging face default model its working great.

Simplify the chaining of queries

In some cases we need to perform multiple queries to be able to extract the document we need.

First case:
Query1 without return fields: returns the id of keys (with with vector_distance or other) => The SubsetA

=> If I need to perform a second query, Query2 taking SubsetA as input to perform a new VectorQuery limited to this subset:
Search query language permits me to use INKEYS keyword to limit the scope of a query to a subset of keys

Second case:
Query1 without return an indexed id field (with with vector_distance or other) => The SubsetB
=> If I need to perform a second query, Query2 taking SubsetB as input to perform a new VectorQuery limited to this subset:
Search query language permits me to use a TAG filter on the id field to limit the scope of a query to a subset of id

in both case we may imagine N steps.

It would be nice to provide helper method , enrich query object to be able to chain the Queries.

ValueError: Required Redis database module searchlight with version >= 20600 not installed. Refer to Redis Stack documentation:

Tried the given code

from redisvl.extensions.llmcache import SemanticCache
llmcache = SemanticCache(
    name="llmcache",                     # underlying search index name
    prefix="llmcache",                   # redis key prefix for hash entries
    redis_url="redis://localhost:6379",  # redis connection url string
    distance_threshold=0.1               # semantic cache distance threshold
)

and getting given error

using python 3.9 and redisvl==0.2.1
ValueError: Required Redis database module searchlight with version >= 20600 not installed. Refer to Redis Stack documentation:

Adding a filter_expression to an existing VectorQuery is not always working

Executing:

from redisvl.query.filter import Text, Tag, Num, FilterExpression
from redisvl.query import VectorQuery

unionTag = Tag("id_texte") == {"VA2", "VA4", "VA1"}

query1 = VectorQuery(
vector = vector1f,
vector_field_name = "texte_vec",
return_fields = ["titre", "questions", "keyword", "texte", "id_texte"],
num_results = 5
)
queryf = VectorQuery(
vector = vector1f,
vector_field_name = "texte_vec",
return_fields = ["titre", "questions", "keyword", "texte", "id_texte"],
num_results = 5,
filter_expression=unionTag
)

print(query1)
print(unionTag)
print(queryf)
filter_query_2 = query1.set_filter(unionTag)
print(filter_query_2)
=====================Results=======
*=>[KNN 5 @texte_vec $vector AS vector_distance] RETURN 6 titre questions keyword texte id_texte vector_distance SORTBY vector_distance ASC DIALECT 2 LIMIT 0 5
@id_texte:{A2|A4|A1}
@id_texte:{A2|A4|A1}=>[KNN 5 @texte_vec $vector AS vector_distance] RETURN 6 titre questions keyword texte id_texte vector_distance SORTBY vector_distance ASC DIALECT 2 LIMIT 0 5
None

I would expect to be able to update a VectorQuery with a filter_expression

GitHub Actions Tests for pytest suite

Description

Github action to run the pytest suite while a Redis container is running with multiple versions of RediSearch

Acceptance Criteria

  • GA workflow to run pytest suite
  • add running redis instance in container to action
  • enable tests on push and pull request
  • require tests to pass to merge PRs

Update to Pydantic >2.0.0

Update to Pydantic 2.0+

I took a look at patching it.
This project's @validator is easily updated to @field_validator.
There is a breaking unit test: tests/integration/test_llmcache.py that I was not able to resolve.

Redis semantic cache working with custom embeddings

I am following the official documentation scrips for semantic cache. In the following code

from redisvl.extensions.llmcache import SemanticCache
llmcache = SemanticCache(
    name="llmcache",                     # underlying search index name
    redis_url="redis://localhost:6379",  # redis connection url string
    distance_threshold=0.2               # semantic cache distance threshold
)
llmcache.store(
    prompt="What is the capital city of France?",
    response="Paris",
    metadata={"city": "Paris", "country": "france"}
)
llmcache.check(prompt=question)[0]['response']

I have these questions,

1 - In llmcache.store() hope I can store the custom vector of the prompt directly rather than prompt. That custom vector can be generated by any embedding model like sentence transformer, minilm-l12-v2, openai embeddings, huggingface embeddings...etc
2 - Is there any embedding length limitation to store using llmcache.store()? Shall I use a vector with any length?
3 - In llmcache.check() hope we can pass the vector( from sentence transformer, minilm-l12-v2, openai embeddings, huggingface embeddings,..etc) directly for the semantic matching purpose,rather than the query
4 - inside llmcache.check() which distance measure using for finding the semantic similarity ? is it cosine similarity or any other? do we have the privilege to configure that?

Add Session Management Feat.

Has progress begun on llm session management? It would be an awesome feature.

Currently, our apps pull past conversation messages straight from our db upon each request, identified by a conversation id.

However, saving on-going conversations in-cache would be much faster and off-load some work from the db.

Is there an idea on how this would be implemented? Or should I pr something from scratch?

Docstring error on schema `distance_metrics` -> `distance_metric`

See https://github.com/RedisVentures/redisvl/blob/8d388011c22fea168c9ed3f40c2543614d655304/redisvl/schema/schema.py#L117

        # From Dict
        schema = IndexSchema.from_dict({
            "index": {
                "name": "user-index",
                "prefix": "user",
                "storage_type": "json",
            },
            "fields": [
                {"name": "user", "type": "tag"},
                {"name": "credit_score", "type": "tag"},
                {
                    "name": "embedding",
                    "type": "vector",
                    "attrs": {
                        "algorithm": "flat",
                        "dims": 3,
                        **"distance_metrics": "cosine",**
                        "datatype": "float32"
                    }
                }
            ]
        })

Implement SearchIndex class

Description

Implement the SearchIndex class in the redisvl.index module.

SearchIndex will contain most of the methods required to interact with the VSS capabilities of Redis.

There will be three types of intializers that can be used:

  1. from_yaml for file-based schema
  2. from_dict for python dictionary based schema (library usage)
  3. from_existing for communicating with existing schema.

Interface

class SearchIndex:

    def __init__(self, schema, sync=False):
        # initialize redis client

    @classmethod
    def from_yaml():
        pass

    @classmethod
    def from_dict():
        pass

    @classmethod
    def from_existing():
        pass

    # create index
    def create(self):
        pass

    def delete(self):
        pass

    def info(self):
        # if init, pull from redis, otherwise, return schema
        pass

    # load vectors
    async def load_concurrent(self, reader, concurrency=5):
        pass

    def load(self, reader):
        pass

Acceptance Criteria

  • SearchIndex class
  • Pytests confirming functionality
  • Update library examples

Provide the utility to count the token saved using Semantic Cache

It would be nice to have an optional mode permitting to monitor the number of token the Semantic Cache permits to save.
To do so, today we can store the token as metadata item.

from langchain_community.callbacks import get_openai_callback

with get_openai_callback() as cb:
        response = agent.run(input=user_query, callbacks=[retrieval_handler])
        red.set("query_token_total",cb.total_tokens)
        red.incr("session_token",cb.total_tokens)
    return response

(...)

metadata=dict()
                if int(red.get("query_token_total").decode('utf-8'))!=0:
                    metadata["token"]= int(red.get("query_token_total").decode('utf-8'))
                    llmcache.store(user_query, response, metadata=metadata)
                else:
                    llmcache.store(user_query, response)

I am wondering if it would make sense to make such easier as it permits to highlight the Semantic Cache value.

Add Index Alias management capabilities

The Alias capability of Indexes with ft.aliasadd, ft.aliasdel, FT.ALIASUPDATE permits agility in making index and apps evolving.
Would be nice to be able to handle this at SearchIndex level directly.

Improve redis connection handling

  • Support for both Sync and Async client connections
  • Proper checks and handling
  • Ability to pass in external redis connections to use instead of creating new ones

Also mentioned here: #79

Implement SearchEngine Class

Description

Interface

The theme of this interface is simplicity. We don't want to cover every possible use case, but we want to simplify the string construction methods that are currently used to build query strings in Redis.

To accomplish this, SearchEngine will be primarily geared towards vector search. We will allow the query to be returned and then passed to the vector_search command such that redis-py arguments and fluent modifiers can be chained to the result of create_vector_query.

class SearchEngine:

    def __init__(self, index):
        self._index = index
        pass

    def create_vector_query(self, topk, return_fields, sort_by, ) -> Query:
        pass

    def vector_search(self, query, query_vector, **kwargs):
        pass

Acceptance Criteria

  • Implement the above interface in the query module
  • Update existing tests and examples
  • Add new tests to confirm functionality

Issue with search dialect 3 and JSON

The dialect 3 api changes the outputs for JSON:

q = VectorQuery(
    vector=[0.23, 0.12, -0.03, 0.98],
    vector_field_name="embedding",
    filter_expression=filter_expression,
    return_fields=["name", "description", "price"],
    dialect=3
)
index.query(q)

returns:

[{'id': 'product:36edbfd1372144759975f01fe6968bbf',
  'vector_distance': '1.08839428425',
  'name': '["Wireless earbuds"]',
  'description': '["Wireless Bluetooth in-ear headphones"]',
  'price': '[64.99]'}]

BUT dialect 2 returns:

[{'id': 'product:36edbfd1372144759975f01fe6968bbf',
  'vector_distance': '1.08839428425',
  'name': 'Wireless earbuds',
  'description': 'Wireless Bluetooth in-ear headphones',
  'price': '64.99'}]

(which is the correct and user expected format.

Additionally FilterQuery types break when using dialect=3...

Need to fix the parsing layer in RedisVL.

Documentation for 0.1.0 Release

Description

API documentation for the python library for redisvl. Autogenerate this using sphinx or similar tooling.

Acceptance Criteria

  • Method to create barebones API documentation using a tool like sphinx
  • Add documentation dependencies to [dev] extras require
  • add instructions for generating documentation to README.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.