Skip to content

Commit 51b19d3

Browse files
committed
feat: add context grounding vectorstore
update comment revert version increment
1 parent 2b8cf4d commit 51b19d3

File tree

6 files changed

+488
-1
lines changed

6 files changed

+488
-1
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
UIPATH_BASE_URL="https://alpha.uipath.com"
2+
UIPATH_URL="https://alpha.uipath.com/goldenagents/DefaultTenant"
3+
UIPATH_ORGANIZATION_ID="xxx"
4+
UIPATH_TENANT_ID="xxx"
5+
UIPATH_REQUESTING_PRODUCT="xxx"
6+
UIPATH_REQUESTING_FEATURE="xxx"
7+
UIPATH_ACCESS_TOKEN="xxx"
8+
UIPATH_FOLDER_PATH=""
9+
UIPATH_FOLDER_KEY=""
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Retrieval chain and Context Grounding vectorstore example
2+
3+
Use the UiPath Context Grounding vectorstore to retrieve relevant documents for a query, and integrate this into a Langchain retrieval chain to answer that query.
4+
5+
## Debug
6+
7+
1. Clone the repository:
8+
```bash
9+
git clone
10+
cd samples\uipath_retrieval_chain
11+
```
12+
13+
2. Install dependencies:
14+
```bash
15+
pip install uv
16+
uv venv -p 3.11 .venv
17+
.venv\Scripts\activate
18+
uv sync
19+
```
20+
21+
3. Create a `.env` file in the project root using the template `.env.example`.
22+
23+
### Run
24+
25+
To check the vectorstore and retrieval chain outputs, you should run:
26+
27+
```bash
28+
python main.py --index_name $INDEX_NAME --query $QUERY --k $NUM_RESULTS
29+
```
30+
31+
### Input Format
32+
33+
The CLI parameters for the sample script are follows:
34+
$INDEX_NAME -> The name of the index to use (string)
35+
#QUERY -> The query for which documents will be retrieved (string)
36+
$NUM_RESULTS -> The number of documents to retrieve
37+
38+
39+
### Output Format
40+
41+
The script first outputs the result of retrieving the most relevant K documents, first with the distance score, then with the relevance score.
42+
Finally, it outputs the result of running the retrieval chain on the query, mentioning the sources alongside the answer.
43+
```
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""Example demonstrating how to use the ContextGroundingVectorStore class with LangChain."""
2+
3+
import argparse
4+
import asyncio
5+
from pprint import pprint
6+
from typing import Any
7+
8+
from dotenv import find_dotenv, load_dotenv
9+
from langchain_core.language_models.chat_models import BaseChatModel
10+
from langchain_core.output_parsers import StrOutputParser
11+
from langchain_core.prompts import ChatPromptTemplate
12+
from langchain_core.runnables import RunnablePassthrough
13+
from langchain_core.vectorstores import VectorStore
14+
from uipath_langchain.chat.models import UiPathAzureChatOpenAI
15+
from uipath_langchain.vectorstores.context_grounding_vectorstore import (
16+
ContextGroundingVectorStore,
17+
)
18+
19+
20+
def create_retrieval_chain(vectorstore: VectorStore, model: BaseChatModel, k: int = 3):
21+
"""Create a retrieval chain using a vector store.
22+
23+
Args:
24+
vectorstore: Vector store to use for the chain
25+
model: LangChain language model to use for the chain
26+
27+
Returns:
28+
A retrieval chain ready to answer questions
29+
"""
30+
# Create a retriever from the vector store
31+
retriever = vectorstore.as_retriever(
32+
search_kwargs={"k": k},
33+
)
34+
35+
# Create a prompt template
36+
template = """Answer the question based on the following context:
37+
{context}
38+
Question: {question}
39+
"""
40+
prompt = ChatPromptTemplate.from_template(template)
41+
42+
# Create the retrieval chain
43+
chain = (
44+
{"context": retriever, "question": RunnablePassthrough()}
45+
| prompt
46+
| model
47+
| StrOutputParser()
48+
)
49+
50+
# Return a function that will run the chain and include source documents
51+
def retrieval_chain(query: str) -> dict[str, Any]:
52+
# Get documents separately to include them in the result
53+
docs = retriever.invoke(query)
54+
# Run the chain
55+
answer = chain.invoke(query)
56+
# Return combined result
57+
return {"result": answer, "source_documents": docs}
58+
59+
return retrieval_chain
60+
61+
62+
async def main(index_name: str, query: str, k: int = 3):
63+
load_dotenv(find_dotenv())
64+
65+
"""Run a simple example of ContextGroundingVectorStore."""
66+
vectorstore = ContextGroundingVectorStore(
67+
index_name=index_name,
68+
)
69+
70+
# Example query
71+
query = "What is the ECCN for a laptop?"
72+
73+
# Perform semantic searches with distance scores
74+
docs_with_scores = await vectorstore.asimilarity_search_with_score(query=query, k=5)
75+
print("==== Docs with distance scores ====")
76+
pprint(
77+
[
78+
{"page_content": doc.page_content, "distance_score": distance_score}
79+
for doc, distance_score in docs_with_scores
80+
]
81+
)
82+
83+
# Perform a similarity search with relevance scores
84+
docs_with_relevance_scores = (
85+
await vectorstore.asimilarity_search_with_relevance_scores(query=query, k=5)
86+
)
87+
print("==== Docs with relevance scores ====")
88+
pprint(
89+
[
90+
{"page_content": doc.page_content, "relevance_score": relevance_score}
91+
for doc, relevance_score in docs_with_relevance_scores
92+
]
93+
)
94+
95+
# Run a retrieval chain
96+
model = UiPathAzureChatOpenAI(
97+
model="gpt-4o-2024-08-06",
98+
max_retries=3,
99+
)
100+
101+
retrieval_chain = create_retrieval_chain(
102+
vectorstore=vectorstore,
103+
model=model,
104+
)
105+
106+
# Run a retrieval chain
107+
result = retrieval_chain(query)
108+
print("==== Retrieval chain result ====")
109+
print(f"Query: {query}")
110+
print(f"Answer: {result['result']}")
111+
print("\nSource Documents:")
112+
for i, doc in enumerate(result["source_documents"]):
113+
print(f"\nDocument {i + 1}:")
114+
print(f"Content: {doc.page_content[:100]}...")
115+
print(
116+
f"Source: {doc.metadata.get('source', 'N/A')}, Page Number: {doc.metadata.get('page_number', '0')}"
117+
)
118+
119+
120+
if __name__ == "__main__":
121+
parser = argparse.ArgumentParser()
122+
parser.add_argument(
123+
"--index_name", type=str, default="ECCN", help="The name of the index to use"
124+
)
125+
parser.add_argument(
126+
"--query",
127+
type=str,
128+
default="What is the ECCN for a laptop?",
129+
help="The query for which documents will be retrieved",
130+
)
131+
parser.add_argument(
132+
"--k", type=int, default=3, help="The number of documents to retrieve"
133+
)
134+
args = parser.parse_args()
135+
asyncio.run(main(args.index_name, args.query, args.k))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[project]
2+
name = "uipath_retrieval_chain"
3+
version = "0.0.1"
4+
description = "Sample retrieval chain using UiPath Context Grounding API"
5+
authors = [{ name = "Andrei Rusu", email = "andrei.rusu@uipath.com" }]
6+
dependencies = [
7+
"uipath-langchain>=0.0.85",
8+
]
9+
requires-python = ">=3.10"

src/uipath_langchain/retrievers/context_grounding_retriever.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from typing import List, Optional
22

3-
from langchain_core.callbacks import CallbackManagerForRetrieverRun
3+
from langchain_core.callbacks import (
4+
AsyncCallbackManagerForRetrieverRun,
5+
CallbackManagerForRetrieverRun,
6+
)
47
from langchain_core.documents import Document
58
from langchain_core.retrievers import BaseRetriever
69
from uipath_sdk import UiPathSDK
@@ -33,3 +36,26 @@ def _get_relevant_documents(
3336
)
3437
for x in results
3538
]
39+
40+
async def _aget_relevant_documents(
41+
self, query: str, *, run_manager: AsyncCallbackManagerForRetrieverRun
42+
) -> List[Document]:
43+
"""Async implementations for retriever calls context_grounding API to search the requested index."""
44+
45+
sdk = self.uipath_sdk if self.uipath_sdk is not None else UiPathSDK()
46+
results = await sdk.context_grounding.search_async(
47+
self.index_name,
48+
query,
49+
self.number_of_results if self.number_of_results is not None else 10,
50+
)
51+
52+
return [
53+
Document(
54+
page_content=x.content,
55+
metadata={
56+
"source": x.source,
57+
"page_number": x.page_number,
58+
},
59+
)
60+
for x in results
61+
]

0 commit comments

Comments
 (0)