From 5af2fc3270b55428afd02df58a5107b4e75dd203 Mon Sep 17 00:00:00 2001 From: Cyrus Mobini Date: Wed, 4 Sep 2024 11:49:39 -0400 Subject: [PATCH 1/3] Added a RAG example for AI connector --- examples/llm/openai_chat.yaml | 4 +- examples/llm/openai_chroma_rag.yaml | 183 ++++++++++++++++++ examples/milvus_store.yaml | 4 +- .../langchain_vector_store_embedding_base.py | 6 +- ...langchain_vector_store_embedding_search.py | 3 +- 5 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 examples/llm/openai_chroma_rag.yaml diff --git a/examples/llm/openai_chat.yaml b/examples/llm/openai_chat.yaml index 038903c..71aee14 100644 --- a/examples/llm/openai_chat.yaml +++ b/examples/llm/openai_chat.yaml @@ -16,7 +16,7 @@ # required ENV variables: # - OPENAI_API_KEY # - OPENAI_API_ENDPOINT -# - MODEL_NAME +# - OPENAI_MODEL_NAME # - SOLACE_BROKER_URL # - SOLACE_BROKER_USERNAME # - SOLACE_BROKER_PASSWORD @@ -61,7 +61,7 @@ flows: component_config: api_key: ${OPENAI_API_KEY} base_url: ${OPENAI_API_ENDPOINT} - model: ${MODEL_NAME} + model: ${OPENAI_MODEL_NAME} temperature: 0.01 input_transforms: - type: copy diff --git a/examples/llm/openai_chroma_rag.yaml b/examples/llm/openai_chroma_rag.yaml new file mode 100644 index 0000000..f78bfc0 --- /dev/null +++ b/examples/llm/openai_chroma_rag.yaml @@ -0,0 +1,183 @@ +# OpenAI RAG (Retrieval Augmented Generation) example using ChromaDB +# This will create 2 flows like these: +# +# Solace[topic:demo/rag/data] -> embed and store in ChromaDB +# Solace[topic:demo/rag/query] -> search in ChromaDB -> OpenAI -> Solace[topic:demo/rag/query/response] +# +# Load Data: +# Send data to Solace topic `demo/rag/data` with the following payload format: +# { +# "texts": [. , ...] +# } +# +# RAG Query: +# Send query to Solace topic `demo/rag/query` with the following payload format: +# { +# "query": "" +# } +# The response will be sent to Solace topic `demo/rag/query/response` +# +# Dependencies: +# pip install -U langchain_openai openai chromadb langchain-chroma +# +# Required ENV variables: +# - OPENAI_API_KEY +# - OPENAI_API_ENDPOINT +# - OPENAI_EMBEDDING_MODEL_NAME +# - OPENAI_MODEL_NAME +# - SOLACE_BROKER_URL +# - SOLACE_BROKER_USERNAME +# - SOLACE_BROKER_PASSWORD +# - SOLACE_BROKER_VPN + +--- +log: + stdout_log_level: INFO + log_file_level: INFO + log_file: solace_ai_connector.log + +shared_config: + - broker_config: &broker_connection + broker_type: solace + broker_url: ${SOLACE_BROKER_URL} + broker_username: ${SOLACE_BROKER_USERNAME} + broker_password: ${SOLACE_BROKER_PASSWORD} + broker_vpn: ${SOLACE_BROKER_VPN} + +# Data ingestion and augmented inference flows +flows: + # Data ingestion to chromaDB for RAG + - name: chroma_ingest + components: + # Data Input from a Solace broker for ingestion + - component_name: solace_data_input + component_module: broker_input + component_config: + <<: *broker_connection + broker_queue_name: demo_rag_data + broker_subscriptions: + - topic: demo/rag/data + qos: 1 + payload_encoding: utf-8 + payload_format: json + + # Embedding data & ChromaDB ingest + - component_name: chroma_embed + component_module: langchain_vector_store_embedding_index + component_config: + vector_store_component_path: langchain_chroma + vector_store_component_name: Chroma + vector_store_component_config: + persist_directory: ./chroma_data + collection_name: rag + embedding_component_path: langchain_openai + embedding_component_name: OpenAIEmbeddings + embedding_component_config: + api_key: ${OPENAI_API_KEY} + base_url: ${OPENAI_API_ENDPOINT} + model: ${OPENAI_EMBEDDING_MODEL_NAME} + input_transforms: + - type: copy + source_value: topic:demo/rag/data + dest_expression: user_data.vector_input:metadatas.source + - type: copy + source_expression: input.payload:texts + dest_expression: user_data.vector_input:texts + input_selection: + source_expression: user_data.vector_input + + # RAG Inference flow + - name: OpenAI_RAG + components: + # Inference Input from a Solace broker for completion + - component_name: solace_completion_broker + component_module: broker_input + component_config: + <<: *broker_connection + broker_queue_name: demo_rag_query + broker_subscriptions: + - topic: demo/rag/query + qos: 1 + payload_encoding: utf-8 + payload_format: json + + # Retrieve the top-k documents from ChromaDB + - component_name: chroma_search + component_module: langchain_vector_store_embedding_search + component_config: + vector_store_component_path: langchain_chroma + vector_store_component_name: Chroma + vector_store_component_config: + persist_directory: ./chroma_data + collection_name: rag + embedding_component_path: langchain_openai + embedding_component_name: OpenAIEmbeddings + embedding_component_config: + api_key: ${OPENAI_API_KEY} + base_url: ${OPENAI_API_ENDPOINT} + model: ${OPENAI_EMBEDDING_MODEL_NAME} + max_results: 5 + input_transforms: + - type: copy + source_expression: input.payload:query + dest_expression: user_data.vector_input:text + input_selection: + source_expression: user_data.vector_input + + # Generate response using the retrieved data + - component_name: llm_request + component_module: openai_chat_model + component_config: + api_key: ${OPENAI_API_KEY} + base_url: ${OPENAI_API_ENDPOINT} + model: ${OPENAI_MODEL_NAME} + temperature: 0.01 + input_transforms: + # Extract and format the retrieved data + - type: map + source_list_expression: previous:result + source_expression: | + template:{{text://item:text}}\n\n + dest_list_expression: user_data.retrieved_data + + - type: copy + source_expression: | + template:You are a helpful AI assistant. Using the provided context, help with the user's request below. Refrain to use any knowledge outside from the provided context. If the user query can not be answered using the provided context, reject user's query. + + + {{text://user_data.retrieved_data}} + + + + {{text://input.payload:query}} + + dest_expression: user_data.llm_input:messages.0.content + - type: copy + source_expression: static:user + dest_expression: user_data.llm_input:messages.0.role + input_selection: + source_expression: user_data.llm_input + + # Send response back to broker with completion and retrieved data + - component_name: send_response + component_module: broker_output + component_config: + <<: *broker_connection + payload_encoding: utf-8 + payload_format: json + copy_user_properties: true + input_transforms: + - type: copy + source_expression: previous:content + dest_expression: user_data.output:payload.response + - type: copy + source_expression: input.payload:query + dest_expression: user_data.output:payload.query + - type: copy + source_expression: user_data.retrieved_data + dest_expression: user_data.output:payload.retrieved_data + - type: copy + source_expression: template:{{text://input.topic}}/response + dest_expression: user_data.output:topic + input_selection: + source_expression: user_data.output diff --git a/examples/milvus_store.yaml b/examples/milvus_store.yaml index c1b707c..50c98ad 100644 --- a/examples/milvus_store.yaml +++ b/examples/milvus_store.yaml @@ -51,10 +51,10 @@ flows: invoke: module: platform function: system - dest_expression: user_data.vector_input:metadata.system + dest_expression: user_data.vector_input:metadatas.system - type: copy source_value: username - dest_expression: user_data.vector_input:metadata.user + dest_expression: user_data.vector_input:metadatas.user - type: copy source_expression: input.payload:text dest_expression: user_data.vector_input:texts diff --git a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_base.py b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_base.py index f5543cc..5288fb1 100644 --- a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_base.py +++ b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_base.py @@ -78,8 +78,10 @@ def init(self): self.vector_store_info["config"], vector_store_class ) except Exception: # pylint: disable=broad-except - del self.vector_store_info["config"]["embeddings"] - del self.vector_store_info["config"]["embedding_function"] + if "embeddings" in self.vector_store_info["config"]: + del self.vector_store_info["config"]["embeddings"] + if "embedding_function" in self.vector_store_info["config"]: + del self.vector_store_info["config"]["embedding_function"] self.vector_store = vector_store_class.from_texts( [], self.embedding, **self.vector_store_info["config"] ) diff --git a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_search.py b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_search.py index ef179d9..1b974eb 100644 --- a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_search.py +++ b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_search.py @@ -59,6 +59,7 @@ "name": "max_results", "required": True, "description": "The maximum number of results to return", + "default": 3, }, { "name": "combine_context_from_same_source", @@ -92,7 +93,7 @@ def __init__(self, **kwargs): def invoke(self, message, data): text = data["text"] - k = self.get_config("max_results") + k = self.get_config("max_results", 3) combine_context_from_same_source = self.get_config( "combine_context_from_same_source" ) From 2866536ee9cee2f589acf136851217445eb764cc Mon Sep 17 00:00:00 2001 From: Cyrus Mobini Date: Wed, 4 Sep 2024 12:42:20 -0400 Subject: [PATCH 2/3] Added delete option to vectordb --- .../langchain_vector_store_embedding_index.md | 9 +++- ...langchain_vector_store_embedding_search.md | 2 +- .../langchain_vector_store_embedding_index.py | 41 +++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/docs/components/langchain_vector_store_embedding_index.md b/docs/components/langchain_vector_store_embedding_index.md index a9b3c9d..b6a2eab 100644 --- a/docs/components/langchain_vector_store_embedding_index.md +++ b/docs/components/langchain_vector_store_embedding_index.md @@ -41,13 +41,20 @@ component_config: }, ... - ] + ], + id: [ + , + ... + ], + action: } ``` | Field | Required | Description | | --- | --- | --- | | texts | True | | | metadatas | False | | +| id | False | The ID of the text to add to the index. required for 'delete' action | +| action | False | The action to perform on the index from one of 'add', 'delete' | ## Component Output Schema diff --git a/docs/components/langchain_vector_store_embedding_search.md b/docs/components/langchain_vector_store_embedding_search.md index 7baf102..68c9678 100644 --- a/docs/components/langchain_vector_store_embedding_search.md +++ b/docs/components/langchain_vector_store_embedding_search.md @@ -28,7 +28,7 @@ component_config: | embedding_component_path | True | | The embedding library path - e.g. 'langchain_community.embeddings' | | embedding_component_name | True | | The embedding model to use - e.g. BedrockEmbeddings | | embedding_component_config | True | | Model specific configuration for the embedding model. See documentation for valid parameter names. | -| max_results | True | | The maximum number of results to return | +| max_results | True | 3 | The maximum number of results to return | | combine_context_from_same_source | False | True | Set to False if you don't want to combine all the context from the same source. Default is True | diff --git a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py index 9e41b54..7f47ccb 100644 --- a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py +++ b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py @@ -74,6 +74,18 @@ "type": "object", }, }, + "id": { + "type": "array", + "items": { + "type": "string", + }, + "description": "The ID of the text to add to the index. required for 'delete' action", + }, + "action": { + "type": "string", + "default": "add", + "description": "The action to perform on the index from one of 'add', 'delete'", + }, }, "required": ["texts"], }, @@ -116,12 +128,35 @@ def invoke(self, message, data): # Get the metadatas if they exist metadatas = data.get("metadatas", None) - args = [texts] if metadatas is not None: if not isinstance(metadatas, list): metadatas = [metadatas] - args.append(metadatas) + # Get the ids if they exist + ids = data.get("id", None) + if ids is not None: + if not isinstance(ids, list): + ids = [ids] + + action = data.get("action", "add") + match action: + case "add": + return self.add_data(texts, metadatas, ids) + case "delete": + return self.delete_data(ids) + case _: + raise ValueError("Invalid action: {}".format(action)) + + def add_data(self, texts, metadatas=None, ids=None): # Add the texts to the vector store - self.vector_store.add_texts(*args) + args = [texts] + if metadatas is not None: + args.append(metadatas) + self.vector_store.add_texts(*args, ids=ids) + return {"result": "OK"} + + def delete_data(self, ids): + if not ids: + raise ValueError("No IDs provided to delete") + self.vector_store.delete(ids) return {"result": "OK"} From f9cf4c5c06dfbbce88253e73eccee594d43bc09a Mon Sep 17 00:00:00 2001 From: Cyrus Mobini Date: Wed, 4 Sep 2024 12:47:05 -0400 Subject: [PATCH 3/3] Changed id to ids --- docs/components/langchain_vector_store_embedding_index.md | 4 ++-- .../langchain/langchain_vector_store_embedding_index.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/components/langchain_vector_store_embedding_index.md b/docs/components/langchain_vector_store_embedding_index.md index b6a2eab..5566b8b 100644 --- a/docs/components/langchain_vector_store_embedding_index.md +++ b/docs/components/langchain_vector_store_embedding_index.md @@ -42,7 +42,7 @@ component_config: }, ... ], - id: [ + ids: [ , ... ], @@ -53,7 +53,7 @@ component_config: | --- | --- | --- | | texts | True | | | metadatas | False | | -| id | False | The ID of the text to add to the index. required for 'delete' action | +| ids | False | The ID of the text to add to the index. required for 'delete' action | | action | False | The action to perform on the index from one of 'add', 'delete' | diff --git a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py index 7f47ccb..79064ed 100644 --- a/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py +++ b/src/solace_ai_connector/components/general/langchain/langchain_vector_store_embedding_index.py @@ -74,7 +74,7 @@ "type": "object", }, }, - "id": { + "ids": { "type": "array", "items": { "type": "string", @@ -133,7 +133,7 @@ def invoke(self, message, data): metadatas = [metadatas] # Get the ids if they exist - ids = data.get("id", None) + ids = data.get("ids", None) if ids is not None: if not isinstance(ids, list): ids = [ids]