
    3fi                       d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	m
Z
mZmZmZmZmZmZmZmZ ddlZddlZddlmZ ddlmZmZ ddlmZ ddlmZ dd	lm Z  dd
l!m"Z"m#Z# ddl$m%Z% ddl&m'Z'm(Z(m)Z)m*Z* ddl+m,Z,m-Z- ddl.m/Z/  ej`                  e1      Z2ee
e3e3f      Z4erddl5m6Z7 ddl8m9Z9 ddl:m;Z; ddl<m=Z= ddZ>d dZ? eddd       G d de"             Z6d!dZ@d"dZA G d de#      ZBy)#z%Wrapper around Redis vector database.    )annotationsN)TYPE_CHECKINGAnyCallableDictIterableListMappingOptionalTupleTypeUnioncast)
deprecated)#AsyncCallbackManagerForRetrieverRunCallbackManagerForRetrieverRun)Document)
Embeddings)get_from_dict_or_env)VectorStoreVectorStoreRetriever)
ConfigDict)_array_to_buffer_buffer_to_arraycheck_redis_module_exist
get_client)REDIS_REQUIRED_MODULESREDIS_TAG_SEPARATOR)maximal_marginal_relevance)RedisQuery)RedisFilterExpression)
RedisModelc                    d| z
  S )N    )vals    i/var/www/auto_recruiter/arenv/lib/python3.12/site-packages/langchain_community/vectorstores/redis/base.py_default_relevance_scorer*   ;   s    s7N    c                    	 | j                  |      j                          t        j                  d       y#  t        j                  d       Y yxY w)zCheck if Redis index exists.zIndex does not existFzIndex already existsT)ftinfologgerdebug)client
index_names     r)   check_index_existsr3   ?   sE    		*""$ LL'(	+,s	   7 Az0.3.13z1.0z langchain_redis.RedisVectorStore)sinceremovalalternative_importc                  "   e Zd ZdZddddddZ	 	 	 	 d%	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d&d	Zed'd
       Ze	 	 	 	 d%	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d(d       Z	e	 	 	 	 d%	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d)d       Z
e	 d*	 	 	 	 	 	 	 	 	 	 	 d+d       Zed,d       Zd-dZ	 d*	 	 	 	 	 d.dZe	 	 	 	 	 	 	 	 d/d       Z	 	 	 	 d0	 	 	 	 	 	 	 	 	 	 	 	 	 d1dZd2dZ edd      	 d3	 	 	 	 	 	 	 	 	 d4d       Z	 	 	 d5	 	 	 	 	 	 	 	 	 	 	 d6dZ	 	 	 	 d7	 	 	 	 	 	 	 	 	 	 	 	 	 d8dZ	 	 	 	 d7	 	 	 	 	 	 	 	 	 	 	 	 	 d9dZ	 	 	 	 	 	 d:	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d;dZd<dZ	 	 	 	 	 d=	 	 	 	 	 	 	 	 	 	 	 	 	 d>dZ	 	 d?	 	 	 	 	 	 	 d@dZ	 	 d?	 	 	 	 	 	 	 d@dZ	 	 d?	 	 	 	 	 dAd ZdBdCd!ZdDd"ZdEd#Z dFd$Z!y)Gr    u  Redis vector database.

    Deployment Options:
        Below, we will use a local deployment as an example. However, Redis can be deployed in all of the following ways:

        - [Redis Cloud](https://redis.com/redis-enterprise-cloud/overview/)
        - [Docker (Redis Stack)](https://hub.docker.com/r/redis/redis-stack)
        - Cloud marketplaces: [AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-e6y7ork67pjwg?sr=0-2&ref_=beagle&applicationId=AWSMPContessa), [Google Marketplace](https://console.cloud.google.com/marketplace/details/redislabs-public/redis-enterprise?pli=1), or [Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/garantiadata.redis_enterprise_1sp_public_preview?tab=Overview)
        - On-premise: [Redis Enterprise Software](https://redis.com/redis-enterprise-software/overview/)
        - Kubernetes: [Redis Enterprise Software on Kubernetes](https://docs.redis.com/latest/kubernetes/)

    Setup:
        Install ``redis``, ``redisvl``, and ``langchain-community`` and run Redis locally.

        .. code-block:: bash

            pip install -qU redis redisvl langchain-community
            docker run -d -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

    Key init args — indexing params:
        index_name: str
            Name of the index.
        index_schema: Optional[Union[Dict[str, ListOfDict], str, os.PathLike]]
            Schema of the index and the vector schema. Can be a dict, or path to yaml file.
        embedding: Embeddings
            Embedding function to use.

    Key init args — client params:
        redis_url: str
            Redis connection url.

    Instantiate:
        .. code-block:: python

            from langchain_community.vectorstores.redis import Redis
            from langchain_openai import OpenAIEmbeddings

            vector_store = Redis(
                redis_url="redis://localhost:6379",
                embedding=OpenAIEmbeddings(),
                index_name="users",
            )

    Add Documents:
        .. code-block:: python

            from langchain_core.documents import Document

            document_1 = Document(page_content="foo", metadata={"baz": "bar"})
            document_2 = Document(page_content="thud", metadata={"bar": "baz"})
            document_3 = Document(page_content="i will be deleted :(")

            documents = [document_1, document_2, document_3]
            ids = ["1", "2", "3"]
            vector_store.add_documents(documents=documents, ids=ids)

    Delete Documents:
        .. code-block:: python

            vector_store.delete(ids=["3"])

    Search:
        .. code-block:: python

            results = vector_store.similarity_search(query="thud",k=1)
            for doc in results:
                print(f"* {doc.page_content} [{doc.metadata}]")

        .. code-block:: python

            * thud [{'id': 'doc:users:2'}]

    Search with filter:
        .. code-block:: python

            from langchain_community.vectorstores.redis import RedisTag

            results = vector_store.similarity_search(query="thud",k=1,filter=(RedisTag("baz") != "bar"))
            for doc in results:
                print(f"* {doc.page_content} [{doc.metadata}]")

        .. code-block:: python

            * thud [{'id': 'doc:users:2'}]

    Search with score:
        .. code-block:: python

            results = vector_store.similarity_search_with_score(query="qux",k=1)
            for doc, score in results:
                print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]")

        .. code-block:: python

            * [SIM=0.167700] foo [{'id': 'doc:users:1'}]

    Async:
        .. code-block:: python

            # add documents
            # await vector_store.aadd_documents(documents=documents, ids=ids)

            # delete documents
            # await vector_store.adelete(ids=["3"])

            # search
            # results = vector_store.asimilarity_search(query="thud",k=1)

            # search with score
            results = await vector_store.asimilarity_search_with_score(query="qux",k=1)
            for doc,score in results:
                print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]")

        .. code-block:: python

            * [SIM=0.167700] foo [{'id': 'doc:users:1'}]

    Use as Retriever:
        .. code-block:: python

            retriever = vector_store.as_retriever(
                search_type="mmr",
                search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5},
            )
            retriever.invoke("thud")

        .. code-block:: python

            [Document(metadata={'id': 'doc:users:2'}, page_content='thud')]

    **Advanced examples:**

    Custom vector schema can be supplied to change the way that
    Redis creates the underlying vector schema. This is useful
    for production use cases where you want to optimize the
    vector schema for your use case. ex. using HNSW instead of
    FLAT (knn) which is the default

        .. code-block:: python

            vector_schema = {
                "algorithm": "HNSW"
            }

            rds = Redis.from_texts(
                texts, # a list of strings
                metadata, # a list of metadata dicts
                embeddings, # an Embeddings object
                vector_schema=vector_schema,
                redis_url="redis://localhost:6379",
            )

    Custom index schema can be supplied to change the way that the
    metadata is indexed. This is useful for you would like to use the
    hybrid querying (filtering) capability of Redis.

    By default, this implementation will automatically generate the index
    schema according to the following rules:
        - All strings are indexed as text fields
        - All numbers are indexed as numeric fields
        - All lists of strings are indexed as tag fields (joined by
            langchain_community.vectorstores.redis.constants.REDIS_TAG_SEPARATOR)
        - All None values are not indexed but still stored in Redis these are
            not retrievable through the interface here, but the raw Redis client
            can be used to retrieve them.
        - All other types are not indexed

    To override these rules, you can pass in a custom index schema like the following

        .. code-block:: yaml

            tag:
                - name: credit_score
            text:
                - name: user
                - name: job

    Typically, the ``credit_score`` field would be a text field since it's a string,
    however, we can override this behavior by specifying the field type as shown with
    the yaml config (can also be a dictionary) above and the code below.

        .. code-block:: python

            rds = Redis.from_texts(
                texts, # a list of strings
                metadata, # a list of metadata dicts
                embeddings, # an Embeddings object
                index_schema="path/to/index_schema.yaml", # can also be a dictionary
                redis_url="redis://localhost:6379",
            )

    When connecting to an existing index where a custom schema has been applied, it's
    important to pass in the same schema to the ``from_existing_index`` method.
    Otherwise, the schema for newly added samples will be incorrect and metadata
    will not be returned.

    content_vectorFLAT   COSINEFLOAT32)name	algorithmdimsdistance_metricdatatypeNc                r   | j                  |       	 ddl}	|| _        || _        	 t        dd|i|}t        |t               || _	        || _
        | j                  ||      | _        ||| _        yd| | _        y# t        $ r}
t        d      |
d}
~
ww xY w# t        $ r}
t        d|
       d}
~
ww xY w)z8Initialize Redis vector store with necessary components.r   NRCould not import redis python package. Please install it with `pip install redis`.	redis_urlzRedis failed to connect: zdoc:r'   )_check_deprecated_kwargsredisImportErrorr2   _embeddingsr   r   r   
ValueErrorr1   relevance_score_fn_get_schema_with_defaults_schema
key_prefix)selfrD   r2   	embeddingindex_schemavector_schemarJ   rM   kwargsrF   eredis_clients               r)   __init__zRedis.__init__  s     	%%f-	 %$	>%D	DVDL$\3IJ #"455lMR(2(>*d:,DW%  	> 	  	>8<==	>s.   A= B =	BBB	B6#B11B6c                    | j                   S )z/Access the query embedding object if available.)rH   rN   s    r)   
embeddingszRedis.embeddingsA  s     r+   c                   	 ddl }ddlm}	 t	        |dd      }d|v r|j                  d       d|v r|j                  d       d}d|v r|j                  d      }|st        j                         j                  }|rt        |t              r"t        |      t        |      k7  rt        d	      t        |t              rt        |d   t              st        d
      t        |d         }|r1 |	|      }||k7  r&t        j!                  dd| dz   d| dz          n|} | |||f||d|}|j#                  |||      }||fS # t        $ r}
t        d      |
d}
~
ww xY w)a
  Create a Redis vectorstore from raw documents.

        This is a user-friendly interface that:
            1. Embeds documents.
            2. Creates a new Redis index if it doesn't already exist
            3. Adds the documents to the newly created Redis index.
            4. Returns the keys of the newly created documents once stored.

        This method will generate schema based on the metadata passed in
        if the `index_schema` is not defined. If the `index_schema` is defined,
        it will compare against the generated schema and warn if there are
        differences. If you are purposefully defining the schema for the
        metadata, then you can ignore that warning.

        To examine the schema options, initialize an instance of this class
        and print out the schema using the `Redis.schema`` property. This
        will include the content and content_vector classes which are
        always present in the langchain schema.

        Example:
            .. code-block:: python

                from langchain_community.vectorstores import Redis
                from langchain_community.embeddings import OpenAIEmbeddings
                embeddings = OpenAIEmbeddings()
                redis, keys = Redis.from_texts_return_keys(
                    texts,
                    embeddings,
                    redis_url="redis://localhost:6379"
                )

        Args:
            texts (List[str]): List of texts to add to the vectorstore.
            embedding (Embeddings): Embeddings to use for the vectorstore.
            metadatas (Optional[List[dict]], optional): Optional list of metadata
                dicts to add to the vectorstore. Defaults to None.
            index_name (Optional[str], optional): Optional name of the index to
                create or add to. Defaults to None.
            index_schema (Optional[Union[Dict[str, ListOfDict], str, os.PathLike]],
                optional):
                Optional fields to index within the metadata. Overrides generated
                schema. Defaults to None.
            vector_schema (Optional[Dict[str, Union[str, int]]], optional): Optional
                vector schema to use. Defaults to None.
            **kwargs (Any): Additional keyword arguments to pass to the Redis client.

        Returns:
            Tuple[Redis, List[str]]: Tuple of the Redis instance and the keys of
                the newly created documents.

        Raises:
            ValueError: If the number of metadatas does not match the number of texts.
        r   N)read_schemarC   rD   	REDIS_URLgeneratekeys.Number of metadatas must match number of texts!Metadatas must be a list of dictsz`index_schema` does not match generated metadata schema.
If you meant to manually override the schema, please ignore this message.
zindex_schema: 
zgenerated_schema: )rP   rQ   )r]   )rF   -langchain_community.vectorstores.redis.schemarZ   rG   r   popuuiduuid4hex
isinstancelistlenrI   dict_generate_field_schemar/   warning	add_texts)clstextsrO   	metadatasr2   rP   rQ   rR   rF   rZ   rS   rD   r]   generated_schemauser_schemainstances                   r)   from_texts_return_keyszRedis.from_texts_return_keysF  s   @
	Q )kJ	& JJ{# JJz" V::f%D ))J )T*s9~U/K !QRRy$/Jy|T4R !DEE5ilC),7
 "22NN3 +;-r:; //?.@C	D  0 
 &'
 
 !!%!>~A  	> 	s   
E 	EEEc                >     | j                   ||f||||d|\  }}	|S )a	  Create a Redis vectorstore from a list of texts.

        This is a user-friendly interface that:
            1. Embeds documents.
            2. Creates a new Redis index if it doesn't already exist
            3. Adds the documents to the newly created Redis index.

        This method will generate schema based on the metadata passed in
        if the `index_schema` is not defined. If the `index_schema` is defined,
        it will compare against the generated schema and warn if there are
        differences. If you are purposefully defining the schema for the
        metadata, then you can ignore that warning.

        To examine the schema options, initialize an instance of this class
        and print out the schema using the `Redis.schema`` property. This
        will include the content and content_vector classes which are
        always present in the langchain schema.


        Example:
            .. code-block:: python

                from langchain_community.vectorstores import Redis
                from langchain_community.embeddings import OpenAIEmbeddings
                embeddings = OpenAIEmbeddings()
                redisearch = RediSearch.from_texts(
                    texts,
                    embeddings,
                    redis_url="redis://username:password@localhost:6379"
                )

        Args:
            texts (List[str]): List of texts to add to the vectorstore.
            embedding (Embeddings): Embedding model class (i.e. OpenAIEmbeddings)
                for embedding queries.
            metadatas (Optional[List[dict]], optional): Optional list of metadata dicts
                to add to the vectorstore. Defaults to None.
            index_name (Optional[str], optional): Optional name of the index to create
                or add to. Defaults to None.
            index_schema (Optional[Union[Dict[str, ListOfDict], str, os.PathLike]],
                optional):
                Optional fields to index within the metadata. Overrides generated
                schema. Defaults to None.
            vector_schema (Optional[Dict[str, Union[str, int]]], optional): Optional
                vector schema to use. Defaults to None.
            **kwargs (Any): Additional keyword arguments to pass to the Redis client.

        Returns:
            Redis: Redis VectorStore instance.

        Raises:
            ValueError: If the number of metadatas does not match the number of texts.
            ImportError: If the redis python package is not installed.
        )ro   r2   rP   rQ   )rs   )
rm   rn   rO   ro   r2   rP   rQ   rR   rr   _s
             r)   
from_textszRedis.from_texts  sE    B 1c00
  !%'
 
! r+   c                    t        |dd      }d|v r|j                  d        | |||f||d|}t        |j                  |      st	        d| d      |S )a  Connect to an existing Redis index.

        Example:
            .. code-block:: python

                from langchain_community.vectorstores import Redis
                from langchain_community.embeddings import OpenAIEmbeddings

                embeddings = OpenAIEmbeddings()

                # must pass in schema and key_prefix from another index
                existing_rds = Redis.from_existing_index(
                    embeddings,
                    index_name="my-index",
                    schema=rds.schema, # schema dumped from another index
                    key_prefix=rds.key_prefix, # key prefix from another index
                    redis_url="redis://username:password@localhost:6379",
                )

        Args:
            embedding (Embeddings): Embedding model class (i.e. OpenAIEmbeddings)
                for embedding queries.
            index_name (str): Name of the index to connect to.
            schema (Union[Dict[str, str], str, os.PathLike, Dict[str, ListOfDict]]):
                Schema of the index and the vector schema. Can be a dict, or path to
                yaml file.
            key_prefix (Optional[str]): Prefix to use for all keys in Redis associated
                with this index.
            **kwargs (Any): Additional keyword arguments to pass to the Redis client.

        Returns:
            Redis: Redis VectorStore instance.

        Raises:
            ValueError: If the index does not exist.
            ImportError: If the redis python package is not installed.
        rD   r[   )rP   rM   zRedis failed to connect: Index z does not exist.)r   rb   r3   r1   rI   )rm   rO   r2   schemarM   rR   rD   rr   s           r)   from_existing_indexzRedis.from_existing_index  s    \ )kJ	 & JJ{# 
  !
 
 "(//:> 1*=MN  r+   c                6    | j                   j                         S )zReturn the schema of the index.)rL   as_dictrW   s    r)   rx   zRedis.schemac  s     ||##%%r+   c                    t        |d      5 }t        j                  | j                  |       ddd       y# 1 sw Y   yxY w)z Write the schema to a yaml file.zw+N)openyamldumprx   )rN   pathfs      r)   write_schemazRedis.write_schemah  s5    $ 	&IIdkk1%	& 	& 	&s	   !7A c                x    | j                   }	 |r$ |j                  |  t        j                  d       y#  Y yxY w)a  
        Delete a Redis entry.

        Args:
            ids: List of ids (keys in redis) to delete.
            redis_url: Redis connection url. This should be passed in the kwargs
                or set as an environment variable: REDIS_URL.

        Returns:
            bool: Whether or not the deletions were successful.

        Raises:
            ValueError: If the redis python package is not installed.
            ValueError: If the ids (keys in redis) are not provided
        zEntries deletedTF)r1   deleter/   r.   )rN   idsrR   r1   s       r)   r   zRedis.deletem  s=    ( 	s#-.	s   &5 9c                V   t        |dd      }	 ddl}	 d|v r|j                  d       t	        d
d|i|}	 |j                  |       j                  |       t        j                  d       y# t        $ r t        d      w xY w# t
        $ r}t        d|       d}~ww xY w#  Y y	xY w)a  
        Drop a Redis search index.

        Args:
            index_name (str): Name of the index to drop.
            delete_documents (bool): Whether to drop the associated documents.

        Returns:
            bool: Whether or not the drop was successful.
        rD   r[   r   NrC   zYour redis connected error: z
Drop indexTFr'   )
r   rF   rG   rb   r   rI   r-   	dropindexr/   r.   )r2   delete_documentsrR   rD   rF   r1   rS   s          r)   
drop_indexzRedis.drop_index  s      )kJ			A f$

;'>)>v>F	IIj!++,<=KK%#  	> 	  	A;A3?@@	A	s-   A- "B 5B$ -B	B!BB!$B(c                "   g }|j                  d|j                  d            }|r`t        |t              r"t        |      t        |      k7  rt	        d      t        |t              rt        |d   t
              st	        d      |xs$ | j                  j                  t        |            }| j                  t        |d                | j                  j                  d      }	t        |      D ]  \  }
}|r||
   n&t        t        j                         j                        }|j!                  | j"                  d	z         s| j"                  d	z   |z   }|r||
   ni }|rt%        |      n|}|	j'                  || j(                  j*                  || j(                  j,                  t/        ||
   | j(                  j0                        i|
       |j3                  |       |
|z  dk(  s|	j5                           |	j5                          |S )a  Add more texts to the vectorstore.

        Args:
            texts (Iterable[str]): Iterable of strings/text to add to the vectorstore.
            metadatas (Optional[List[dict]], optional): Optional list of metadatas.
                Defaults to None.
            embeddings (Optional[List[List[float]]], optional): Optional pre-generated
                embeddings. Defaults to None.
            keys (List[str]) or ids (List[str]): Identifiers of entries.
                Defaults to None.
            batch_size (int, optional): Batch size to use for writes. Defaults to 1000.

        Returns:
            List[str]: List of ids added to the vectorstore
        r]   r   r^   r   r_   )dimF)transaction:)mapping)getrf   rg   rh   rI   ri   rH   embed_documents_create_index_if_not_existr1   pipeline	enumeratestrrc   rd   re   
startswithrM   _prepare_metadatahsetrL   content_keycontent_vector_keyr   vector_dtypeappendexecute)rN   rn   ro   rX   
batch_sizeclean_metadatarR   r   keys_or_idsr   itextkeymetadatas                 r)   rl   zRedis.add_texts  s   0  jjE):; )T*s9~U/K !QRRy$/Jy|T4R !DEEP4#3#3#C#CDK#P
''C
1,>'? ;;''E': ' 	#GAt$/+a.S9I9I5JC>>$//C"78oo+c1'0y|bH6D(2(HMMLL,,dLL335E"1t||'@'@6
   	 JJsO :~"  "+	#0 	
r+   c                    |j                  dd       xs g }|j                  | j                                t        dd| i|d|iS )Ntagsvectorstorer'   )rb   extend_get_retriever_tagsRedisVectorStoreRetriever)rN   rR   r   s      r)   as_retrieverzRedis.as_retriever  sD    zz&$'-2D,,./(OTOVO$OOr+   z0.0.1z)similarity_search(distance_threshold=0.1))alternativec                .     | j                   |f||d|S )a*  
        Returns the most similar indexed documents to the query text within the
        score_threshold range.

        Deprecated: Use similarity_search with distance_threshold instead.

        Args:
            query (str): The query text for which to find similar documents.
            k (int): The number of documents to return. Default is 4.
            score_threshold (float): The minimum matching *distance* required
                for a document to be considered a match. Defaults to 0.2.

        Returns:
            List[Document]: A list of documents that are most similar to the query text
                including the match score for each document.

        Note:
            If there are no documents that satisfy the score_threshold value,
            an empty list is returned.

        )kdistance_threshold)similarity_search)rN   queryr   score_thresholdrR   s        r)   similarity_search_limit_scorez#Redis.similarity_search_limit_score  s.    2 &t%%
?
>D
 	
r+   c                2   	 ddl }d|v rt        j                  d       | j                  j                  |      } | j                  |f|||dd|\  }	}
	 | j                  j                  | j                        j                  |	|
      }g }|j                   D ]  }i }|r.d|j"                  i}|j%                  | j'                  |             | j(                  j*                  }t-        t/        ||      |      }| j1                  |j2                        }|j5                  ||f        |S # t        $ r}t        d      |d}~ww xY w# |j                  j                  $ r3}t        |      j                  d      d   d	k(  rt        d
      ||d}~ww xY w)a  Run similarity search with **vector distance**.

        The "scores" returned from this function are the raw vector
        distances from the query vector. For similarity scores, use
        ``similarity_search_with_relevance_scores``.

        Args:
            query (str): The query text for which to find similar documents.
            k (int): The number of documents to return. Default is 4.
            filter (RedisFilterExpression, optional): Optional metadata filter.
                Defaults to None.
            return_metadata (bool, optional): Whether to return metadata.
                Defaults to True.

        Returns:
            List[Tuple[Document, float]]: A list of documents that are
                most similar to the query with the distance for each document.
        r   NrC   r   score_threshold is deprecated. Use distance_threshold instead.score_threshold should only be used in similarity_search_with_relevance_scores.score_threshold will be removed in a future release.T)r   filterwith_metadatawith_distance SyntaxgQuery failed with syntax error. This is likely due to malformation of filter, vector, or query argumentidpage_contentr   )rF   rG   r/   rk   rH   embed_query_prepare_queryr1   r-   r2   search
exceptionsResponseErrorr   splitrI   docsr   update_collect_metadatarL   r   r   getattr_calculate_fp_distancedistancer   )rN   r   r   r   return_metadatarR   rF   rS   query_embeddingredis_queryparams_dictresultsdocs_with_scoresresultr   r   docr   s                     r)   similarity_search_with_scorez"Redis.similarity_search_with_score  s   4	 &NNI **66u=#64#6#6$
)$
 $
 [
	kknnT__5<<[+VG :<ll 		5FH &)), 6 6v >?,,22K(DxXC226??CH##S(O4		5  i  	> 	: -- 	1v||C #x/ : 	
 G	s/   D- 5E
 -	E6EE
F#.FFc                h    | j                   j                  |      } | j                  |f||||d|S )a  Run similarity search

        Args:
            query (str): The query text for which to find similar documents.
            k (int): The number of documents to return. Default is 4.
            filter (RedisFilterExpression, optional): Optional metadata filter.
                Defaults to None.
            return_metadata (bool, optional): Whether to return metadata.
                Defaults to True.
            distance_threshold (Optional[float], optional): Maximum vector distance
                between selected documents and the query vector. Defaults to None.

        Returns:
            List[Document]: A list of documents that are most similar to the query
                text.
        r   r   r   r   )rH   r   similarity_search_by_vector)rN   r   r   r   r   r   rR   r   s           r)   r   zRedis.similarity_searcho  sM    2 **66u=/t//
+1
 
 	
r+   c           	        	 ddl }d|v rt        j                  d       | j	                  |||||d      \  }	}
	 | j
                  j                  | j                        j                  |	|
      }g }|j                  D ]p  }i }|r.d|j                  i}|j!                  | j#                  |             | j$                  j&                  }|j)                  t+        t-        ||      |             r |S # t        $ r}t        d      |d}~ww xY w# |j                  j                  $ r3}t        |      j                  d      d   d	k(  rt        d
      ||d}~ww xY w)a  Run similarity search between a query vector and the indexed vectors.

        Args:
            embedding (List[float]): The query vector for which to find similar
                documents.
            k (int): The number of documents to return. Default is 4.
            filter (RedisFilterExpression, optional): Optional metadata filter.
                Defaults to None.
            return_metadata (bool, optional): Whether to return metadata.
                Defaults to True.
            distance_threshold (Optional[float], optional): Maximum vector distance
                between selected documents and the query vector. Defaults to None.

        Returns:
            List[Document]: A list of documents that are most similar to the query
                text.
        r   NrC   r   r   F)r   r   r   r   r   r   r   r   r   r   )rF   rG   r/   rk   r   r1   r-   r2   r   r   r   r   r   rI   r   r   r   r   rL   r   r   r   r   )rN   rO   r   r   r   r   rR   rF   rS   r   r   r   r   r   r   r   s                   r)   r   z!Redis.similarity_search_by_vector  su   4	 &NNI $(#6#61) $7 $
 [
	kknnT__5<<[+VG ll 		FH &)), 6 6v >?,,22KKKgfk&BXV		 c  	> 	6 -- 	1v||C #x/ : 	
 G	s.   C2 5D 2	D;DDE(.EEc                   | j                   j                  |      }	 | j                  |	f||||d|}
|
D cg c]  }|j                  d    }}|D cg c]_  }t	        t        t        | j                  j                  || j                  j                              | j                  j                        a }}t        t        j                  |	      |||      }|D cg c]  }|
|   	 }}|S c c}w c c}w c c}w )a  Return docs selected using the maximal marginal relevance.

        Maximal marginal relevance optimizes for similarity to query AND diversity
            among selected documents.

        Args:
            query (str): Text to look up documents similar to.
            k (int): Number of Documents to return. Defaults to 4.
            fetch_k (int): Number of Documents to fetch to pass to MMR algorithm.
            lambda_mult (float): Number between 0 and 1 that determines the degree
                of diversity among the results with 0 corresponding
                to maximum diversity and 1 to minimum diversity.
                Defaults to 0.5.
            filter (RedisFilterExpression, optional): Optional metadata filter.
                Defaults to None.
            return_metadata (bool, optional): Whether to return metadata.
                Defaults to True.
            distance_threshold (Optional[float], optional): Maximum vector distance
                between selected documents and the query vector. Defaults to None.

        Returns:
            List[Document]: A list of Documents selected by maximal marginal relevance.
        r   r   )dtype)lambda_multr   )rH   r   r   r   r   r   bytesr1   hgetrL   r   r   r   nparray)rN   r   r   fetch_kr   r   r   r   rR   r   prefetch_docsr   prefetch_idsprefetch_idprefetch_embeddingsselected_indicesr   selected_docss                     r)   max_marginal_relevance_searchz#Redis.max_marginal_relevance_search  s   F **66u= 988
+1
 
 7DDsT*DD  ,	
  KK$$[$,,2Q2QR ll//	
 	
 6HH_%':WX
 4DDaq)DD) E	
 Es   C0A$C5 C:c                    i }| j                   j                  D ]  }	 t        ||      ||<    |S # t        $ r? t        j                  d| ddz   dz   | j                   j                   z          d||<   Y ^w xY w)a  Collect metadata from Redis.

        Method ensures that there isn't a mismatch between the metadata
        and the index schema passed to this class by the user or generated
        by this class.

        Args:
            result (Document): redis.commands.search.Document object returned
                from Redis.

        Returns:
            Dict[str, Any]: Collected metadata.
        zMetadata key z not found in metadata. zSetting to None. 
z+Metadata fields defined for this instance: N)rL   metadata_keysr   AttributeErrorr/   rk   )rN   r   metar   s       r)   r   zRedis._collect_metadata&  s     <<-- 	!C
!#FC0S		!  " !#C5(@A+,CD 3346 !S	!s   0AA87A8c                N   dt        || j                  j                        i}| j                  j                  g}|r|j	                  d       |r%|j                  | j                  j                         |r||d<   | j                  |||      |fS | j                  |||      |fS )Nvectorr   r   )r   return_fields)	r   rL   r   r   r   r   r   _prepare_range_query_prepare_vector_query)	rN   r   r   r   r   r   r   r   r   s	            r)   r   zRedis._prepare_queryD  s     &8Q8QR<

 112  ,  !;!;<0BK,-))fM *  	  &&q}&U
 	
r+   c                D   	 ddl m} |xs g }| j                  j                  }d| d}|rt        |      dz   |z   }|dz   }  ||      j                  | j                  d      j                  d|      j                  d	      S # t        $ r}t        d      |d }~ww xY w)
Nr   r!   rC   @z+:[VECTOR_RANGE $distance_threshold $vector]r   z =>{$yield_distance_as: distance}r      )
redis.commands.search.queryr"   rG   rL   r   r   r   sort_bypagingdialect)	rN   r   r   r   r"   rS   
vector_key
base_queryquery_strings	            r)   r   zRedis._prepare_range_queryf  s    	9 &+\\44
$OP
Vs*Z7J!$FFE,]M+WZ VAq\WQZ	
  	> 	   B 	BBBc                D   	 ddl m} |xs g }d}|rt        |       }| j                  j
                  }d| d| d| d	}  ||      j                  | j                  d
      j                  d|      j                  d      }	|	S # t        $ r}t        d      |d}~ww xY w)zPrepare query for vector search.

        Args:
            k: Number of results to return.
            filter: Optional metadata filter.

        Returns:
            query: Query object.
        r   r!   rC   N*(z)=>[KNN z @z $vector AS distance]r   r   )
r   r"   rG   r   rL   r   r   r   r   r   )
rN   r   r   r   r"   rS   query_prefixr   r   r   s
             r)   r   zRedis._prepare_vector_query  s    	9 &+!&k]L\\44
hqcJ<?TU
E*]M+WZ VAq\WQZ 	 '  	> 	r   c                L   ddl m}m}  |       }|r  ||      } |di |}|j                          	 |j                   |rt
        j                  d       |S # t        $ rB | j                  j                         }|r|j                  |       |j                  |       Y |S w xY w)Nr   )r$   rZ   zP`vector_schema` is ignored since content_vector is overridden in `index_schema`.r'   )ra   r$   rZ   add_content_fieldr8   r/   rk   rI   DEFAULT_VECTOR_SCHEMAcopyr   add_vector_field)rN   rP   rQ   r$   rZ   rx   schema_valuesvector_fields           r)   rK   zRedis._get_schema_with_defaults  s    	

  '5M0-0F $$&
	2!! 6   	2  55::<L##M2 ##L1	2s   #A AB#"B#c                   	 ddl m}m} || j                  j
                  _        t        | j                  | j                        sl| j                  j                  | j                        j                  | j                  j                          || j                  g|j                               y y # t        $ r t        d      w xY w)Nr   )IndexDefinition	IndexTyperC   )prefix
index_type)fields
definition)%redis.commands.search.indexDefinitionr  r  rG   rL   r8   r?   r3   r1   r2   r-   create_index
get_fieldsrM   HASH)rN   r   r  r  s       r)   r   z Redis._create_index_if_not_exist  s    
	 ,/##( "$++t?KKNN4??+88||..0* OO, 9  @  	> 	s   B2 2Cc                    | j                   j                  j                  dk(  rt        t	        |      d      S t        t	        |      d      S )zCalculate the distance based on the vector datatype

        Two datatypes supported:
        - FLOAT32
        - FLOAT64

        if it's FLOAT32, we need to round the distance to 4 decimal places
        otherwise, round to 7 decimal places.
        r<         )rL   r8   rA   roundfloat)rN   r   s     r)   r   zRedis._calculate_fp_distance  s>     <<&&//9<x!,,U8_a((r+   c                x    ddddddd}|j                         D ]  \  }}||v st        d| d||    d       y)	zCheck for deprecated kwargs.rD   rP   rQ   )
redis_host
redis_portredis_passwordr   r   r@   zKeyword argument 'z' is deprecated. Please use 'z
' instead.N)itemsrI   )rN   rR   deprecated_kwargsr   values        r)   rE   zRedis._check_deprecated_kwargs  sn     &%))).
 !,,. 	JC'' ( .##4S#9":*F 	r+   c                    | j                   r| j                   S | j                  | j                  | j                  d}	 || j                  j
                  j                     S # t        $ r	 t        cY S w xY w)N)r;   IPL2)	rJ   _cosine_relevance_score_fn%_max_inner_product_relevance_score_fn_euclidean_relevance_score_fnrL   r8   r@   KeyErrorr*   )rN   
metric_maps     r)   _select_relevance_score_fnz Redis._select_relevance_score_fn  sp    ""*** 55<<44


	,dll99IIJJ 	,++	,s   "A! !A32A3)NNNN)rD   r   r2   r   rO   r   rP   8Optional[Union[Dict[str, ListOfDict], str, os.PathLike]]rQ   $Optional[Dict[str, Union[str, int]]]rJ   z"Optional[Callable[[float], float]]rM   Optional[str]rR   r   )returnzOptional[Embeddings])rn   	List[str]rO   r   ro   Optional[List[dict]]r2   r'  rP   r%  rQ   r&  rR   r   r(  zTuple[Redis, List[str]])rm   zType[Redis]rn   r)  rO   r   ro   r*  r2   r'  rP   r%  rQ   r&  rR   r   r(  r    )N)rO   r   r2   r   rx   zEUnion[Dict[str, ListOfDict], str, os.PathLike, Dict[str, ListOfDict]]rM   r'  rR   r   r(  r    )r(  zDict[str, List[Any]])r   zUnion[str, os.PathLike]r(  None)r   Optional[List[str]]rR   r   r(  bool)r2   r   r   r-  rR   r   r(  r-  )NNi  T)rn   zIterable[str]ro   r*  rX   zOptional[List[List[float]]]r   intr   r-  rR   r   r(  r)  )rR   r   r(  r   )r  g?)
r   r   r   r.  r   r  rR   r   r(  List[Document])r  NT)r   r   r   r.  r   Optional[RedisFilterExpression]r   r-  rR   r   r(  zList[Tuple[Document, float]])r  NTN)r   r   r   r.  r   r0  r   r-  r   Optional[float]rR   r   r(  r/  )rO   List[float]r   r.  r   r0  r   r-  r   r1  rR   r   r(  r/  )r     g      ?NTN)r   r   r   r.  r   r.  r   r  r   r0  r   r-  r   r1  rR   r   r(  r/  )r   z
'Document'r(  Dict[str, Any])r  NNTF)r   r2  r   r.  r   r0  r   r1  r   r-  r   r-  r(  zTuple['Query', Dict[str, Any]])NN)r   r.  r   r0  r   r,  r(  z'Query')rP   r%  rQ   r&  r(  z'RedisModel')r:   )r   r.  r(  r+  )r   r   r(  r  )rR   zMapping[str, Any]r(  r+  )r(  zCallable[[float], float])"__name__
__module____qualname____doc__r   rU   propertyrX   classmethodrs   rv   ry   rx   r   r   staticmethodr   rl   r   r   r   r   r   r   r   r   r   r   r   rK   r   r   rE   r$  r'   r+   r)   r    r    J   s   DN !# RV>BAE$(#X#X #X 	#X
 O#X <#X ?#X "#X #XJ     
 +/$(QU>BEE E (	E
 "E OE <E E 
!E EN 
 +/$(QU>BIII I (	I
 "I OI <I I 
I IV  %)FF F V	F
 "F F 
F FP & && $(   
	> &&& & 
	& &V +/26#CC (C 0	C
 C C C 
CJP
 %PQ?B

 
7<
NQ
	
 R
> 26 $Q Q  Q  0	Q 
 Q  Q  
&Q l 26 $.2!
!
 !
 0	!

 !
 ,!
 !
 
!
L 26 $.2NN N 0	N
 N ,N N 
Nf  26 $.2BB B 	B
 B 0B B ,B B 
BHB 26.2"# 
$ 
  
 0	 

 , 
  
  
 
( 
J 37-1	

 0
 +	

 

B 37-1	$$ 0$ +	$
 
$P RV>B/N/ </ 
	/b8)$,r+   r    c                .   g g g d}| j                         D ]'  \  }}	 t        |       |d   j                  d|i       ) |S # t        t        f$ r Y nw xY w|Dt        |t        t        f      rV|rt        |d   t              r|d   j                  d|i       n)t        |d         j                  }t        d| d|       t        |t              r|d   j                  d|i       t        |      j                  }t        d	d
| d| z         )a1  
    Generate a schema for the search index in Redis based on the input metadata.

    Given a dictionary of metadata, this function categorizes each metadata
        field into one of the three categories:
    - text: The field contains textual data.
    - numeric: The field contains numeric data (either integer or float).
    - tag: The field contains list of tags (strings).

    Args
        data (Dict[str, Any]): A dictionary where keys are metadata field names
            and values are the metadata values.

    Returns:
        Dict[str, Any]: A dictionary with three keys "text", "numeric", and "tag".
            Each key maps to a list of fields that belong to that category.

    Raises:
        ValueError: If a metadata field cannot be categorized into any of
            the three known types.
    )r   numerictagr=  r=   r   r>  z+List/tuple values should contain strings: 'z': r   z2Could not generate Redis index field type mapping zfor metadata: ')r  r.  r   rI   	TypeErrorrf   rg   tupler   typer5  )datar   r   r  r=   s        r)   rj   rj   &  sF   . F jjl "

U	J9$$fc]3"
H M= I& 		 = edE]+JuQx5u$$fc]3E!H~.. A#c$P   eS!6N!!63-0 E{##@uCv./
 	
s   !AAAc                B   dd}i }| j                         D ]  \  }}|d||<   t        |t        t        t        f      r|||<   /t        |t
        t        f      r8|rt        |d   t              rt        j                  |      ||<   s |||       } |||        |S )a  
    Prepare metadata for indexing in Redis by sanitizing its values.

    - String, integer, and float values remain unchanged.
    - None or empty values are replaced with empty strings.
    - Lists/tuples of strings are joined into a single string with a comma separator.

    Args:
        metadata (Dict[str, Any]): A dictionary where keys are metadata
            field names and values are the metadata values.

    Returns:
        Dict[str, Any]: A sanitized dictionary ready for indexing in Redis.

    Raises:
        ValueError: If any metadata value is not one of the known
            types (string, int, float, or list of strings).
    c                R    t        d|  ddt        |      j                   z         )NzMetadata value for key 'z' must be a string, int, zfloat, or list of strings. Got )rI   rA  r5  )r   r  s     r)   raise_errorz&_prepare_metadata.<locals>.raise_error}  s7    &se+DE/U0D0D/EFG
 	
r+    r   )r   r   r  r   r(  r+  )	r  rf   r   r.  r  rg   r@  r   join)r   rE  
clean_metar   r  s        r)   r   r   i  s    (
 57Jnn& $
U= JsO ec3./#JsO e}-JuQx5"5":":5"A
3C'U#!$" r+   c                      e Zd ZU dZded<   	 dZded<   	 ddd	d
Zded<   	 g dZ	  ed      Z		 	 	 	 	 	 	 	 ddZ
	 	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 ddZy	)r   z Retriever for Redis VectorStore.r    r   
similarityr   search_typer  g?N)r   r   r   r4  search_kwargs)rJ  similarity_distance_thresholdsimilarity_score_thresholdmmrT)arbitrary_types_allowedc                  | j                   |z  }| j                  dk(  r | j                  j                  |fi |}|S | j                  dk(  r/|d   t	        d       | j                  j                  |fi |}|S | j                  dk(  r3 | j                  j
                  |fi |}|D cg c]  \  }}|	 }}}|S | j                  dk(  r | j                  j                  |fi |}|S t	        d| j                   d      c c}}w 	NrJ  rM  r   zOdistance_threshold must be provided for similarity_distance_threshold retrieverrN  rO  zsearch_type of z not allowed.)rL  rK  r   r   rI   'similarity_search_with_relevance_scoresr   	rN   r   run_managerrR   _kwargsr   docs_and_similaritiesr   ru   s	            r)   _get_relevant_documentsz1RedisVectorStoreRetriever._get_relevant_documents  sI    $$v-|+54##55eGwGD( ' !@@+,4 @  64##55eGwGD  !==H  HH$ "
 '<<FCC<D<
 	 &A4##AA%S7SD  t/?/?.@NOO	 =s   -Dc               n  K   | j                   |z  }| j                  dk(  r' | j                  j                  |fi | d {   }|S | j                  dk(  r7|d   t	        d       | j                  j                  |fi | d {   }|S | j                  dk(  r; | j                  j
                  |fi | d {   }|D cg c]  \  }}|	 }}}|S | j                  dk(  r' | j                  j                  |fi | d {   }|S t	        d| j                   d      7 7 7 mc c}}w 7 +wrR  )rL  rK  r   asimilarity_searchrI   (asimilarity_search_with_relevance_scoresamax_marginal_relevance_searchrT  s	            r)   _aget_relevant_documentsz2RedisVectorStoreRetriever._aget_relevant_documents  s{     $$v-|+<))<<UNgNND* ) !@@+,4 @  =))<<UNgNND  !==Od&&OO$  "
 '<<FCC<D<  &H))HH  D
  t/?/?.@NOO) O O =sT   ?D5D'AD5D)5D5=D+>	D5D-4D5D3 D5)D5+D5-D5c                <     | j                   j                  |fi |S )Add documents to vectorstore.)r   add_documentsrN   	documentsrR   s      r)   r`  z'RedisVectorStoreRetriever.add_documents  s     -t--iB6BBr+   c                X   K    | j                   j                  |fi | d{   S 7 w)r_  N)r   aadd_documentsra  s      r)   rd  z(RedisVectorStoreRetriever.aadd_documents  s,      5T%%44YI&IIIIs   !*(*)r   r   rU  r   rR   r   r(  r/  )r   r   rU  r   rR   r   r(  r/  )rb  r/  rR   r   r(  r)  )r5  r6  r7  r8  __annotations__rK  rL  allowed_search_typesr   model_configrX  r]  r`  rd  r'   r+   r)   r   r     s    *#K# "	%M>  !   $L*HTW	6 9	
  
@CJ'J36J	Jr+   r   )r(   r  r(  r  )r1   	RedisTyper2   r   r(  r-  )rB  r4  r(  r4  )r   r4  r(  r4  )Cr8  
__future__r   loggingosrc   typingr   r   r   r   r   r	   r
   r   r   r   r   r   numpyr   r~   langchain_core._apir   langchain_core.callbacksr   r   langchain_core.documentsr   langchain_core.embeddingsr   langchain_core.utilsr   langchain_core.vectorstoresr   r   pydanticr   #langchain_community.utilities.redisr   r   r   r   0langchain_community.vectorstores.redis.constantsr   r   &langchain_community.vectorstores.utilsr   	getLoggerr5  r/   r   
ListOfDictredis.clientr    rh  r   r"   .langchain_community.vectorstores.redis.filtersr#   ra   r$   r*   r3   rj   r   r   r'   r+   r)   <module>r|     s    + "  	       * . 0 5 I   N			8	$$sCx.!
/1TH 
E6XV,K V,V,r&@F,^cJ 4 cJr+   