Skip to content

Commit

Permalink
Add RAG example for AI connector + delete action for vector index (#31)
Browse files Browse the repository at this point in the history
* Added a RAG example for AI connector

* Added delete option to vectordb

* Changed id to ids
  • Loading branch information
cyrus2281 authored Sep 4, 2024
1 parent 3f33985 commit 8c9434f
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 12 deletions.
9 changes: 8 additions & 1 deletion docs/components/langchain_vector_store_embedding_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,20 @@ component_config:
<freeform-object>
},
...
]
],
ids: [
<string>,
...
],
action: <string>
}
```
| Field | Required | Description |
| --- | --- | --- |
| texts | True | |
| metadatas | False | |
| 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' |


## Component Output Schema
Expand Down
2 changes: 1 addition & 1 deletion docs/components/langchain_vector_store_embedding_search.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
4 changes: 2 additions & 2 deletions examples/llm/openai_chat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
183 changes: 183 additions & 0 deletions examples/llm/openai_chroma_rag.yaml
Original file line number Diff line number Diff line change
@@ -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": [<text data 1>. <text data 2>, ...]
# }
#
# RAG Query:
# Send query to Solace topic `demo/rag/query` with the following payload format:
# {
# "query": "<question or request as text>"
# }
# 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.
<context>
{{text://user_data.retrieved_data}}
</context>
<user-question>
{{text://input.payload:query}}
</user-question>
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
4 changes: 2 additions & 2 deletions examples/milvus_store.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
)
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@
"type": "object",
},
},
"ids": {
"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"],
},
Expand Down Expand Up @@ -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("ids", 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"}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
)
Expand Down

0 comments on commit 8c9434f

Please sign in to comment.