From 2691d5d6ad5b17cfbc0cd67323c4cfc5777abe46 Mon Sep 17 00:00:00 2001 From: Brian Sam-Bodden Date: Wed, 31 Jul 2024 14:53:40 -0700 Subject: [PATCH] Release v0.0.1 for LangChain-Redis Partner Package (#4) * refactor: remove commented out fixture * fix: respect index configuration when using a RedisVL IndexSchema * git: add MacOS artifacts to .gitignore * feature: report library name via RedisVL->redis-py * release: v0.0.1 --- .gitignore | 37 ++++++++++++++++++- libs/redis/langchain_redis/__init__.py | 3 ++ libs/redis/langchain_redis/config.py | 23 +++++++++++- libs/redis/langchain_redis/vectorstores.py | 21 ++++++----- libs/redis/langchain_redis/version.py | 2 + libs/redis/pyproject.toml | 2 +- .../integration_tests/test_vectorstores.py | 6 --- libs/redis/tests/unit_tests/test_imports.py | 2 + .../tests/unit_tests/test_vectorstores.py | 5 ++- 9 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 libs/redis/langchain_redis/version.py diff --git a/.gitignore b/.gitignore index 3eb4e57..31b936a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,38 @@ -# Created by https://www.toptal.com/developers/gitignore/api/python,venv -# Edit at https://www.toptal.com/developers/gitignore?templates=python,venv +# Created by https://www.toptal.com/developers/gitignore/api/python,venv,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=python,venv,macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud ### Python ### # Byte-compiled / optimized / DLL files diff --git a/libs/redis/langchain_redis/__init__.py b/libs/redis/langchain_redis/__init__.py index 7336796..4794efe 100644 --- a/libs/redis/langchain_redis/__init__.py +++ b/libs/redis/langchain_redis/__init__.py @@ -2,8 +2,11 @@ from langchain_redis.chat_message_history import RedisChatMessageHistory from langchain_redis.config import RedisConfig from langchain_redis.vectorstores import RedisVectorStore +from langchain_redis.version import __lib_name__, __version__ __all__ = [ + "__version__", + "__lib_name__", "RedisVectorStore", "RedisConfig", "RedisCache", diff --git a/libs/redis/langchain_redis/config.py b/libs/redis/langchain_redis/config.py index f580b8e..8137b54 100644 --- a/libs/redis/langchain_redis/config.py +++ b/libs/redis/langchain_redis/config.py @@ -2,7 +2,7 @@ from pydantic.v1 import BaseModel, Field, validator from redis import Redis -from redisvl.schema import IndexSchema # type: ignore[import] +from redisvl.schema import IndexSchema, StorageType # type: ignore[import] from ulid import ULID @@ -35,6 +35,15 @@ class RedisConfig(BaseModel): class Config: arbitrary_types_allowed = True + def __init__(self, **data: Any): + super().__init__(**data) + if "schema" in data: + schema = data["schema"] + self.index_name = schema.index.name + self.key_prefix = schema.index.prefix + self.storage_type = "hash" + self.index_schema = schema + @validator("key_prefix", always=True) def set_key_prefix(cls, v: Optional[str], values: Dict[str, str]) -> str: if v is None: @@ -84,7 +93,17 @@ def from_kwargs(cls: Type["RedisConfig"], **kwargs: Any) -> "RedisConfig": @classmethod def from_schema(cls, schema: IndexSchema, **kwargs: Any) -> "RedisConfig": - return cls(schema=schema, **kwargs) + if schema.index.storage_type == StorageType.HASH: + storage_type = "hash" + else: + storage_type = "json" + return cls( + schema=schema, + index_name=schema.index.name, + key_prefix=schema.index.prefix, + storage_type=storage_type, + **kwargs, + ) @classmethod def from_yaml(cls, schema_path: str, **kwargs: Any) -> "RedisConfig": diff --git a/libs/redis/langchain_redis/vectorstores.py b/libs/redis/langchain_redis/vectorstores.py index 6c52ded..87c36ec 100644 --- a/libs/redis/langchain_redis/vectorstores.py +++ b/libs/redis/langchain_redis/vectorstores.py @@ -14,6 +14,7 @@ from redisvl.redis.utils import buffer_to_array, convert_bytes # type: ignore[import] from langchain_redis.config import RedisConfig +from langchain_redis.version import __lib_name__ Matrix = Union[List[List[float]], List[np.ndarray], np.ndarray] @@ -103,16 +104,20 @@ def __init__( ) if self.config.index_schema: - self._index = SearchIndex(self.config.index_schema, self.config.redis()) + self._index = SearchIndex( + self.config.index_schema, self.config.redis(), lib_name=__lib_name__ + ) self._index.create(overwrite=False) elif self.config.schema_path: - self._index = SearchIndex.from_yaml(self.config.schema_path) + self._index = SearchIndex.from_yaml( + self.config.schema_path, lib_name=__lib_name__ + ) self._index.set_client(self.config.redis()) self._index.create(overwrite=False) elif self.config.from_existing and self.config.index_name: self._index = SearchIndex.from_existing( - self.config.index_name, self.config.redis() + self.config.index_name, self.config.redis(), lib_name=__lib_name__ ) self._index.create(overwrite=False) else: @@ -157,7 +162,8 @@ def __init__( }, *modified_metadata_schema, ], - } + }, + lib_name=__lib_name__, ) self._index.set_client(self.config.redis()) self._index.create(overwrite=False) @@ -234,10 +240,7 @@ def from_texts( if metadatas is None: metadatas = [{} for _ in range(len(texts))] - vector_store = cls( - embeddings=embedding, - config=config, - ) + vector_store = cls(embeddings=embedding, config=config, **kwargs) out_keys = vector_store.add_texts(texts, metadatas, keys) # type: ignore if return_keys: @@ -281,7 +284,7 @@ def from_existing_index( config.index_name = index_name config.from_existing = True - return RedisVectorStore(embedding, config=config) + return RedisVectorStore(embedding, config=config, **kwargs) def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]: """Delete keys from the vector store.""" diff --git a/libs/redis/langchain_redis/version.py b/libs/redis/langchain_redis/version.py new file mode 100644 index 0000000..0cc0eb3 --- /dev/null +++ b/libs/redis/langchain_redis/version.py @@ -0,0 +1,2 @@ +__version__ = "0.0.1" +__lib_name__ = f"langchain-redis_v{__version__}" diff --git a/libs/redis/pyproject.toml b/libs/redis/pyproject.toml index c291e53..ad56988 100644 --- a/libs/redis/pyproject.toml +++ b/libs/redis/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langchain-redis" -version = "0.0.1rc1" +version = "0.0.1" description = "An integration package connecting Redis and LangChain" authors = ["Brian Sam-Bodden "] readme = "README.md" diff --git a/libs/redis/tests/integration_tests/test_vectorstores.py b/libs/redis/tests/integration_tests/test_vectorstores.py index 880009a..3c68cc1 100644 --- a/libs/redis/tests/integration_tests/test_vectorstores.py +++ b/libs/redis/tests/integration_tests/test_vectorstores.py @@ -46,17 +46,11 @@ def embed_query(self, text: str) -> List[float]: return [0.0, 0.0] -# @pytest.fixture -# def redis_url() -> str: -# return "redis://localhost:6379" - - @pytest.fixture def texts() -> List[str]: return ["foo", "bar", "baz"] -# @pytest.mark.usefixtures("redis_container") def test_with_redis_url(texts: List[str], redis_url: str) -> None: """Test end to end construction and search.""" # Create a unique index name for testing diff --git a/libs/redis/tests/unit_tests/test_imports.py b/libs/redis/tests/unit_tests/test_imports.py index 7d9052d..22ee46c 100644 --- a/libs/redis/tests/unit_tests/test_imports.py +++ b/libs/redis/tests/unit_tests/test_imports.py @@ -1,6 +1,8 @@ from langchain_redis import __all__ EXPECTED_ALL = [ + "__version__", + "__lib_name__", "RedisVectorStore", "RedisConfig", "RedisCache", diff --git a/libs/redis/tests/unit_tests/test_vectorstores.py b/libs/redis/tests/unit_tests/test_vectorstores.py index 36d366c..40d8d14 100644 --- a/libs/redis/tests/unit_tests/test_vectorstores.py +++ b/libs/redis/tests/unit_tests/test_vectorstores.py @@ -46,7 +46,10 @@ def get(self, client: Any, doc_ids: List[str]) -> List[Dict[str, Any]]: class MockSearchIndex: def __init__( - self, schema: Optional[Dict[str, Any]] = None, client: Optional[Any] = None + self, + schema: Optional[Dict[str, Any]] = None, + client: Optional[Any] = None, + lib_name: Optional[str] = None, ) -> None: self.data: List[Dict[str, Any]] = [] default_schema = {