From b9bcb09e639a7eae4566751de10fab9d5938f1d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= <38133825+joaoguilhermeS@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:42:55 -0300 Subject: [PATCH] feat: Add OpenSearch VectorStore Component with Ingest and Search Capabilities (#3799) * update: adding opensearch-py dependency * feat: adding OpenSearch icon * feat: adding OpenSearch VectorStore code * fix: removing unused methods and adding hybrid search capabilities using search method * update: poetry lock with recent hash added * fix: removing unused methods and adding hybrid search capabilities using search method * fix: default value of search_input to an empty string * fix: adapting code to match make format script --- poetry.lock | 91 +++--- pyproject.toml | 1 + .../components/vectorstores/OpenSearch.py | 268 ++++++++++++++++++ .../src/icons/OpenSearch/OpenSearch.jsx | 149 ++++++++++ src/frontend/src/icons/OpenSearch/index.tsx | 9 + .../src/icons/OpenSearch/opensearch.svg | 126 ++++++++ src/frontend/src/utils/styleUtils.ts | 2 + 7 files changed, 590 insertions(+), 56 deletions(-) create mode 100644 src/backend/base/langflow/components/vectorstores/OpenSearch.py create mode 100644 src/frontend/src/icons/OpenSearch/OpenSearch.jsx create mode 100644 src/frontend/src/icons/OpenSearch/index.tsx create mode 100644 src/frontend/src/icons/OpenSearch/opensearch.svg diff --git a/poetry.lock b/poetry.lock index eac01585e..cffc9fd75 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2011,61 +2011,6 @@ qdrant = ["fastembed", "qdrant-client"] snowflake = ["snowflake-snowpark-python"] weaviate = ["weaviate-client (>=4.6.5,<4.7.0)"] -[[package]] -name = "duckdb" -version = "1.1.0" -description = "DuckDB in-process database" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "duckdb-1.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5e4cbc408e6e41146dea89b9044dae7356e353db0c96b183e5583ee02bc6ae5d"}, - {file = "duckdb-1.1.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:6370ae27ec8167ccfbefb94f58ad9fdc7bac142399960549d6d367f233189868"}, - {file = "duckdb-1.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4e1c3414f7fd01f4810dc8b335deffc91933a159282d65fef11c1286bc0ded04"}, - {file = "duckdb-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6bc2a58689adf5520303c5f68b065b9f980bd31f1366c541b8c7490abaf55cd"}, - {file = "duckdb-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d02be208d2885ca085d4c852b911493b8cdac9d6eae893259da32bd72a437c25"}, - {file = "duckdb-1.1.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:655df442ceebfc6f3fd6c8766e04b60d44dddedfa90275d794f9fab2d3180879"}, - {file = "duckdb-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6e183729bb64be7798ccbfda6283ebf423c869268c25af2b56929e48f763be2f"}, - {file = "duckdb-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:61fb838da51e07ceb0222c4406b059b90e10efcc453c19a3650b73c0112138c4"}, - {file = "duckdb-1.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:7807e2f0d3344668e433f0dc1f54bfaddd410589611393e9a7ed56f8dec9514f"}, - {file = "duckdb-1.1.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:3da30b7b466f710d52caa1fdc3ef0bf4176ad7f115953cd9f8b0fbf0f723778f"}, - {file = "duckdb-1.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:b9b6a77ef0183f561b1fc2945fcc762a71570ffd33fea4e3a855d413ed596fe4"}, - {file = "duckdb-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16243e66a9fd0e64ee265f2634d137adc6593f54ddf3ef55cb8a29e1decf6e54"}, - {file = "duckdb-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42b910a149e00f40a1766dc74fa309d4255b912a5d2fdcc387287658048650f6"}, - {file = "duckdb-1.1.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47849d546dc4238c0f20e95fe53b621aa5b08684e68fff91fd84a7092be91a17"}, - {file = "duckdb-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11ec967b67159361ceade34095796a8d19368ea5c30cad988f44896b082b0816"}, - {file = "duckdb-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:510b5885ed6c267b9c0e1e7c6138fdffc2dd6f934a5a95b76da85da127213338"}, - {file = "duckdb-1.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:657bc7ac64d5faf069a782ae73afac51ef30ae2e5d0e09ce6a09d03db84ab35e"}, - {file = "duckdb-1.1.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:89f3de8cba57d19b41cd3c47dd06d979bd2a2ffead115480e37afbe72b02896d"}, - {file = "duckdb-1.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f6486323ab20656d22ffa8f3c6e109dde30d0b327b7c831f22ebcfe747f97fb0"}, - {file = "duckdb-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78a4510f82431ee3f14db689fe8727a4a9062c8f2fbb3bcfe3bfad3c1a198004"}, - {file = "duckdb-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64bf2a6e23840d662bd2ac09206a9bd4fa657418884d69e5c352d4456dc70b3c"}, - {file = "duckdb-1.1.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23fc9aa0af74e3803ed90c8d98280fd5bcac8c940592bf6288e8fd60fb051d00"}, - {file = "duckdb-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f3aea31341ce400640dd522e4399b941f66df17e39884f446638fe958d6117c"}, - {file = "duckdb-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:3db4ab31c20de4edaef152930836b38e7662cd71370748fdf2c38ba9cf854dc4"}, - {file = "duckdb-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3b6b4fe1edfe35f64f403a9f0ab75258cee35abd964356893ee37424174b7e4"}, - {file = "duckdb-1.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad02f50d5a2020822d1638fc1a9bcf082056f11d2e15ccfc1c1ed4d0f85a3be"}, - {file = "duckdb-1.1.0-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb66e9e7391801928ea134dcab12d2e4c97f2ce0391c603a3e480bbb15830bc8"}, - {file = "duckdb-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:069fb7bca459e31edb32a61f0eea95d7a8a766bef7b8318072563abf8e939593"}, - {file = "duckdb-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e39f9b7b62e64e10d421ff04480290a70129c38067d1a4f600e9212b10542c5a"}, - {file = "duckdb-1.1.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:55ef98bcc7ba745752607f1b926e8d9b7ce32c42c423bbad10c44820aefe23a7"}, - {file = "duckdb-1.1.0-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:e2a08175e43b865c1e9611efd18cacd29ddd69093de442b1ebdf312071df7719"}, - {file = "duckdb-1.1.0-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:0e3644b1f034012d82b9baa12a7ea306fe71dc6623731b28c753c4a617ff9499"}, - {file = "duckdb-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:211a33c1ddb5cc609f75eb43772b0b03b45d2fa89bec107e4715267ca907806a"}, - {file = "duckdb-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e74b6f8a5145abbf7e6c1a2a61f0adbcd493c19b358f524ec9a3cebdf362abb"}, - {file = "duckdb-1.1.0-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58f1633dd2c5af5088ae2d119418e200855d0699d84f2fae9d46d30f404bcead"}, - {file = "duckdb-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d18caea926b1e301c29b140418fca697aad728129e269b4f82c2795a184549e1"}, - {file = "duckdb-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:cd9fb1408942411ad360f8414bc3fbf0091c396ca903d947a10f2e31324d5cbd"}, - {file = "duckdb-1.1.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bd11bc899cebf5ff936d1276a2dfb7b7db08aba3bcc42924afeafc2163bddb43"}, - {file = "duckdb-1.1.0-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:53825a63193c582a78c152ea53de8d145744ddbeea18f452625a82ebc33eb14a"}, - {file = "duckdb-1.1.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:29dc18087de47563b3859a6b98bbed96e1c96ce5db829646dc3b16a916997e7d"}, - {file = "duckdb-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb19319883564237a7a03a104dbe7f445e73519bb67108fcab3d19b6b91fe30"}, - {file = "duckdb-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aac2fcabe2d5072c252d0b3087365f431de812d8199705089fb073e4d039d19c"}, - {file = "duckdb-1.1.0-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d89eaaa5df8a57e7d2bc1f4c46493bb1fee319a00155f2015810ad2ace6570ae"}, - {file = "duckdb-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d86a6926313913cd2cc7e08816d3e7f72ba340adf2959279b1a80058be6526d9"}, - {file = "duckdb-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:d8333f3e85fa2a0f1c222b752c2bd42ea875235ff88492f7bcbb6867d0f644eb"}, - {file = "duckdb-1.1.0.tar.gz", hash = "sha256:b4d4c12b1f98732151bd31377753e0da1a20f6423016d2d097d2e31953ec7c23"}, -] - [[package]] name = "duckduckgo-search" version = "6.2.12" @@ -2287,6 +2232,16 @@ django = ["dj-database-url", "dj-email-url", "django-cache-url"] lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] +[[package]] +name = "events" +version = "0.5" +description = "Bringing the elegance of C# EventHandler to Python" +optional = false +python-versions = "*" +files = [ + {file = "Events-0.5-py3-none-any.whl", hash = "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd"}, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -6499,6 +6454,30 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +[[package]] +name = "opensearch-py" +version = "2.7.1" +description = "Python client for OpenSearch" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "opensearch_py-2.7.1-py3-none-any.whl", hash = "sha256:5417650eba98a1c7648e502207cebf3a12beab623ffe0ebbf55f9b1b4b6e44e9"}, + {file = "opensearch_py-2.7.1.tar.gz", hash = "sha256:67ab76e9373669bc71da417096df59827c08369ac3795d5438c9a8be21cbd759"}, +] + +[package.dependencies] +certifi = ">=2024.07.04" +Events = "*" +python-dateutil = "*" +requests = ">=2.32.0,<3.0.0" +urllib3 = {version = ">=1.26.19,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +async = ["aiohttp (>=3.9.4,<4)"] +develop = ["black (>=24.3.0)", "botocore", "coverage (<8.0.0)", "jinja2", "myst-parser", "pytest (>=3.0.0)", "pytest-cov", "pytest-mock (<4.0.0)", "pytz", "pyyaml", "requests (>=2.0.0,<3.0.0)", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"] +docs = ["aiohttp (>=3.9.4,<4)", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"] +kerberos = ["requests-kerberos"] + [[package]] name = "opentelemetry-api" version = "1.25.0" @@ -12171,4 +12150,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "b8b616387b7386f9beab685fcff95ca507f802eb9dc22bd9ae182f0793b11170" +content-hash = "4f9dbbdd9929d50f643790fbae6ecb8e2f052c2a0c399dd79efb6840450bf48d" diff --git a/pyproject.toml b/pyproject.toml index f6aa71bc0..c9947fa1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -229,6 +229,7 @@ langchain-unstructured = "^0.1.2" pydantic-settings = "2.4.0" ragstack-ai-knowledge-store = "^0.2.1" duckduckgo-search = "^6.2.12" +opensearch-py = "^2.7.1" [tool.poetry.group.dev.dependencies] diff --git a/src/backend/base/langflow/components/vectorstores/OpenSearch.py b/src/backend/base/langflow/components/vectorstores/OpenSearch.py new file mode 100644 index 000000000..ca02781e3 --- /dev/null +++ b/src/backend/base/langflow/components/vectorstores/OpenSearch.py @@ -0,0 +1,268 @@ +import json +import traceback +from typing import TYPE_CHECKING, Any + +from langchain_community.vectorstores import OpenSearchVectorSearch +from loguru import logger + +from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store +from langflow.io import ( + BoolInput, + DataInput, + DropdownInput, + FloatInput, + HandleInput, + IntInput, + MultilineInput, + SecretStrInput, + StrInput, +) +from langflow.schema import Data + +if TYPE_CHECKING: + from langchain_community.vectorstores import OpenSearchVectorSearch + + +class OpenSearchVectorStoreComponent(LCVectorStoreComponent): + """ + OpenSearch Vector Store with advanced, customizable search capabilities. + """ + + display_name: str = "OpenSearch" + description: str = "OpenSearch Vector Store with advanced, customizable search capabilities." + documentation = "https://python.langchain.com/docs/integrations/vectorstores/opensearch" + name = "OpenSearch" + icon = "OpenSearch" + + inputs = [ + StrInput( + name="opensearch_url", + display_name="OpenSearch URL", + value="http://localhost:9200", + info="URL for OpenSearch cluster (e.g. https://192.168.1.1:9200).", + ), + StrInput( + name="index_name", + display_name="Index Name", + value="langflow", + info="The index name where the vectors will be stored in OpenSearch cluster.", + ), + MultilineInput( + name="search_input", + display_name="Search Input", + info=( + "Enter a search query. Leave empty to retrieve all documents. " + "If you need a more advanced search consider using Hybrid Search Query instead." + ), + value="", + ), + DataInput( + name="ingest_data", + display_name="Ingest Data", + is_list=True, + ), + HandleInput(name="embedding", display_name="Embedding", input_types=["Embeddings"]), + DropdownInput( + name="search_type", + display_name="Search Type", + options=["similarity", "similarity_score_threshold", "mmr"], + value="similarity", + advanced=True, + ), + IntInput( + name="number_of_results", + display_name="Number of Results", + info="Number of results to return.", + advanced=True, + value=4, + ), + FloatInput( + name="search_score_threshold", + display_name="Search Score Threshold", + info="Minimum similarity score threshold for search results.", + value=0.0, + advanced=True, + ), + StrInput( + name="username", + display_name="Username", + value="admin", + advanced=True, + ), + SecretStrInput( + name="password", + display_name="Password", + value="admin", + advanced=True, + ), + BoolInput( + name="use_ssl", + display_name="Use SSL", + value=True, + advanced=True, + ), + BoolInput( + name="verify_certs", + display_name="Verify Certificates", + value=False, + advanced=True, + ), + MultilineInput( + name="hybrid_search_query", + display_name="Hybrid Search Query", + value="", + advanced=True, + info=( + "Provide a custom hybrid search query in JSON format. This allows you to combine " + "vector similarity and keyword matching." + ), + ), + ] + + @check_cached_vector_store + def build_vector_store(self) -> OpenSearchVectorSearch: + """ + Builds the OpenSearch Vector Store object. + """ + try: + from langchain_community.vectorstores import OpenSearchVectorSearch + except ImportError as e: + error_message = f"Failed to import required modules: {str(e)}" + logger.error(error_message) + raise ImportError(error_message) from e + + try: + opensearch = OpenSearchVectorSearch( + index_name=self.index_name, + embedding_function=self.embedding, + opensearch_url=self.opensearch_url, + http_auth=(self.username, self.password), + use_ssl=self.use_ssl, + verify_certs=self.verify_certs, + ssl_assert_hostname=False, + ssl_show_warn=False, + ) + except Exception as e: + error_message = f"Failed to create OpenSearchVectorSearch instance: {str(e)}" + logger.error(error_message) + raise RuntimeError(error_message) from e + + if self.ingest_data: + self._add_documents_to_vector_store(opensearch) + + return opensearch + + def _add_documents_to_vector_store(self, vector_store: "OpenSearchVectorSearch") -> None: + """ + Adds documents to the Vector Store. + """ + documents = [] + for _input in self.ingest_data or []: + if isinstance(_input, Data): + documents.append(_input.to_lc_document()) + else: + error_message = f"Expected Data object, got {type(_input)}" + logger.error(error_message) + raise ValueError(error_message) + + if documents and self.embedding is not None: + logger.debug(f"Adding {len(documents)} documents to the Vector Store.") + try: + vector_store.add_documents(documents) + except Exception as e: + error_message = f"Error adding documents to Vector Store: {str(e)}" + logger.error(error_message) + logger.error(f"Traceback: {traceback.format_exc()}") + raise RuntimeError(error_message) from e + else: + logger.debug("No documents to add to the Vector Store.") + + def search(self, query: str | None = None) -> list[dict[str, Any]]: + """ + Search for similar documents in the vector store or retrieve all documents if no query is provided. + """ + try: + vector_store = self.build_vector_store() + + query = query or "" + + if self.hybrid_search_query.strip(): + try: + hybrid_query = json.loads(self.hybrid_search_query) + except json.JSONDecodeError as e: + error_message = f"Invalid hybrid search query JSON: {str(e)}" + logger.error(error_message) + raise ValueError(error_message) from e + + results = vector_store.client.search(index=self.index_name, body=hybrid_query) + + processed_results = [] + for hit in results.get("hits", {}).get("hits", []): + source = hit.get("_source", {}) + text = source.get("text", "") + metadata = source.get("metadata", {}) + + if isinstance(text, dict): + text = text.get("text", "") + + processed_results.append( + { + "page_content": text, + "metadata": metadata, + } + ) + return processed_results + + search_kwargs = {"k": self.number_of_results} + search_type = self.search_type.lower() + + if search_type == "similarity": + results = vector_store.similarity_search(query, **search_kwargs) + return [{"page_content": doc.page_content, "metadata": doc.metadata} for doc in results] + if search_type == "similarity_score_threshold": + search_kwargs["score_threshold"] = self.search_score_threshold + results = vector_store.similarity_search_with_relevance_scores(query, **search_kwargs) + return [ + { + "page_content": doc.page_content, + "metadata": doc.metadata, + "score": score, + } + for doc, score in results + ] + if search_type == "mmr": + results = vector_store.max_marginal_relevance_search(query, **search_kwargs) + return [{"page_content": doc.page_content, "metadata": doc.metadata} for doc in results] + + error_message = f"Invalid search type:: {self.search_type}" + logger.error(error_message) + raise ValueError(error_message) + + except Exception as e: + error_message = f"Error during search: {str(e)}" + logger.error(error_message) + logger.error(f"Traceback: {traceback.format_exc()}") + raise RuntimeError(error_message) from e + + def search_documents(self) -> list[Data]: + """ + Search for documents in the vector store based on the search input. + If no search input is provided, retrieve all documents. + """ + try: + query = self.search_input.strip() if self.search_input else None + results = self.search(query) + retrieved_data = [ + Data( + file_path=result["metadata"].get("file_path", ""), + text=result["page_content"], + ) + for result in results + ] + self.status = retrieved_data + return retrieved_data + except Exception as e: + error_message = f"Error during document search: {str(e)}" + logger.error(error_message) + logger.error(f"Traceback: {traceback.format_exc()}") + raise RuntimeError(error_message) from e diff --git a/src/frontend/src/icons/OpenSearch/OpenSearch.jsx b/src/frontend/src/icons/OpenSearch/OpenSearch.jsx new file mode 100644 index 000000000..c68d338f8 --- /dev/null +++ b/src/frontend/src/icons/OpenSearch/OpenSearch.jsx @@ -0,0 +1,149 @@ +const OpenSearchSVG = (props) => ( + + + + + + + + + + + + + +); + +export default OpenSearchSVG; diff --git a/src/frontend/src/icons/OpenSearch/index.tsx b/src/frontend/src/icons/OpenSearch/index.tsx new file mode 100644 index 000000000..0cdb3a980 --- /dev/null +++ b/src/frontend/src/icons/OpenSearch/index.tsx @@ -0,0 +1,9 @@ +import React, { forwardRef } from "react"; +import OpenSearchSVG from "./OpenSearch"; + +export const OpenSearch = forwardRef< + SVGSVGElement, + React.PropsWithChildren<{}> +>((props, ref) => { + return ; +}); diff --git a/src/frontend/src/icons/OpenSearch/opensearch.svg b/src/frontend/src/icons/OpenSearch/opensearch.svg new file mode 100644 index 000000000..8caf21104 --- /dev/null +++ b/src/frontend/src/icons/OpenSearch/opensearch.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 3748d0e3b..556997d3b 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -214,6 +214,7 @@ import { NotionIcon } from "../icons/Notion"; import { NvidiaIcon } from "../icons/Nvidia"; import { OllamaIcon } from "../icons/Ollama"; import { OpenAiIcon } from "../icons/OpenAi"; +import { OpenSearch } from "../icons/OpenSearch"; import { PineconeIcon } from "../icons/Pinecone"; import { PostgresIcon } from "../icons/Postgres"; import { PythonIcon } from "../icons/Python"; @@ -631,4 +632,5 @@ export const nodeIconsLucide: iconsType = { Option: OptionIcon, Perplexity, DuckDuckGo: DuckDuckGoIcon, + OpenSearch, };