nikhilkomakula
commited on
Commit
·
27b774b
1
Parent(s):
4d13c26
Gradio + Streamlit
Browse files- Dockerfile +3 -4
- README.md +8 -0
- app.py +23 -17
- eval.py +21 -0
- gradio_ui.py +24 -0
- requirements.txt +2 -1
- src/generation/generate_response.py +53 -10
- src/indexing/build_indexes.py +41 -16
- src/retrieval/retriever_chain.py +3 -2
- src/test/eval_rag.py +6 -0
- src/test/eval_results_20240325111437.csv +86 -0
- src/ui/{chat_interface.py → gradio_chat_interface.py} +1 -1
- src/ui/streamlit_chat_interface.py +47 -0
- streamlit_ui.py +24 -0
Dockerfile
CHANGED
@@ -28,9 +28,8 @@ ENV HOME=/home/user \
|
|
28 |
# Set the working directory to the user's home directory
|
29 |
WORKDIR $HOME/app
|
30 |
|
31 |
-
#
|
32 |
-
RUN --
|
33 |
-
git clone --depth 1 https://github.com/nikhilkomakula/llm-rag-op-chatbot.git $HOME/app
|
34 |
|
35 |
# Use ENTRYPOINT to specify the command to run when the container starts
|
36 |
-
ENTRYPOINT ["python", "
|
|
|
28 |
# Set the working directory to the user's home directory
|
29 |
WORKDIR $HOME/app
|
30 |
|
31 |
+
# Clone the Git repo
|
32 |
+
RUN git clone --depth 1 https://github.com/nikhilkomakula/llm-rag-op-chatbot.git $HOME/app
|
|
|
33 |
|
34 |
# Use ENTRYPOINT to specify the command to run when the container starts
|
35 |
+
ENTRYPOINT ["python", "gradio_ui.py"]
|
README.md
CHANGED
@@ -113,6 +113,14 @@ docker ps -a
|
|
113 |
docker logs -f llm-rag-op-chatbot
|
114 |
```
|
115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
## Technologies Used:
|
117 |
|
118 |
* **PDF Parser :** PyMuPDFLoader
|
|
|
113 |
docker logs -f llm-rag-op-chatbot
|
114 |
```
|
115 |
|
116 |
+
## REST API:
|
117 |
+
|
118 |
+
**Note:** Navigate to the chat interface UI in the browser and locate `Use via API` and click on it. A fly over opens on the right hand side. Capture the URL under the title named `API documentation`.
|
119 |
+
|
120 |
+
* **URL:** `<http|https>`://`<hostname`:`<port>`/run/chat
|
121 |
+
* **METHOD:** POST
|
122 |
+
* **BODY:** { "data": ["`<query>`", ""] }
|
123 |
+
|
124 |
## Technologies Used:
|
125 |
|
126 |
* **PDF Parser :** PyMuPDFLoader
|
app.py
CHANGED
@@ -1,23 +1,29 @@
|
|
1 |
# import libraries
|
2 |
import sys
|
3 |
-
|
4 |
|
5 |
-
#
|
6 |
-
|
7 |
-
|
8 |
-
from src.generation.generate_response import get_qa_chain, set_global_qa_chain, generate_response
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
21 |
else:
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
|
|
|
1 |
# import libraries
|
2 |
import sys
|
3 |
+
import subprocess
|
4 |
|
5 |
+
# Define functions for Gradio and Streamlit interfaces
|
6 |
+
def run_gradio_interface():
|
7 |
+
subprocess.run(["python", "gradio_ui.py"])
|
|
|
8 |
|
9 |
+
def run_streamlit_interface():
|
10 |
+
subprocess.run(["streamlit", "run", "streamlit_ui.py"])
|
11 |
+
|
12 |
+
def run_rag_evaluate():
|
13 |
+
subprocess.run(["python", "eval.py"])
|
14 |
|
15 |
+
# Main function to determine which interface to run
|
16 |
+
def main():
|
17 |
+
if "-gradio" in sys.argv:
|
18 |
+
print("Initializing Gradio Interface!")
|
19 |
+
run_gradio_interface()
|
20 |
+
elif "-streamlit" in sys.argv:
|
21 |
+
print("Initializing Streamlit Interface!")
|
22 |
+
run_streamlit_interface()
|
23 |
else:
|
24 |
+
print("Running RAG Evaluation!")
|
25 |
+
run_rag_evaluate()
|
26 |
+
|
27 |
+
# Run the main function
|
28 |
+
if __name__ == "__main__":
|
29 |
+
main()
|
eval.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import libraries
|
2 |
+
from dotenv import find_dotenv, load_dotenv
|
3 |
+
|
4 |
+
# import functions
|
5 |
+
from src.test.eval_rag import evaluate_rag
|
6 |
+
from src.generation.generate_response import get_qa_chain
|
7 |
+
|
8 |
+
def main():
|
9 |
+
|
10 |
+
# find .env automatically by walking up directories until it's found, then
|
11 |
+
# load up the .env entries as environment variables
|
12 |
+
load_dotenv(find_dotenv())
|
13 |
+
|
14 |
+
# get the qa chain
|
15 |
+
qa_chain = get_qa_chain()
|
16 |
+
|
17 |
+
# evaluate the qa chain
|
18 |
+
evaluate_rag("qa_chain", qa_chain)
|
19 |
+
|
20 |
+
if __name__ == "__main__":
|
21 |
+
main()
|
gradio_ui.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import libraries
|
2 |
+
from dotenv import find_dotenv, load_dotenv
|
3 |
+
|
4 |
+
# import functions
|
5 |
+
from src.ui.gradio_chat_interface import create_gradio_chatinterface
|
6 |
+
from src.generation.generate_response import get_qa_chain, set_global_qa_chain, generate_response_gradio
|
7 |
+
|
8 |
+
def main():
|
9 |
+
|
10 |
+
# find .env automatically by walking up directories until it's found, then
|
11 |
+
# load up the .env entries as environment variables
|
12 |
+
load_dotenv(find_dotenv())
|
13 |
+
|
14 |
+
# get the qa chain
|
15 |
+
qa_chain = get_qa_chain()
|
16 |
+
|
17 |
+
# set the global qa chain
|
18 |
+
set_global_qa_chain(qa_chain)
|
19 |
+
|
20 |
+
# initiate the chat interface
|
21 |
+
create_gradio_chatinterface(generate_response_gradio).launch(server_name="0.0.0.0", server_port=7860)
|
22 |
+
|
23 |
+
if __name__ == "__main__":
|
24 |
+
main()
|
requirements.txt
CHANGED
@@ -7,4 +7,5 @@ pymupdf==1.23.26
|
|
7 |
sentence-transformers==2.5.1
|
8 |
tensorflow==2.16.1
|
9 |
gradio==4.21.0
|
10 |
-
pandas==2.2.1
|
|
|
|
7 |
sentence-transformers==2.5.1
|
8 |
tensorflow==2.16.1
|
9 |
gradio==4.21.0
|
10 |
+
pandas==2.2.1
|
11 |
+
streamlit==1.32.2
|
src/generation/generate_response.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1 |
# import libraries
|
2 |
import time
|
|
|
3 |
from src.retrieval.retriever_chain import get_base_retriever, load_hf_llm, create_qa_chain
|
4 |
|
5 |
# constants
|
6 |
-
HF_MODEL
|
|
|
7 |
|
8 |
|
9 |
# get the qa chain
|
@@ -16,7 +18,7 @@ def get_qa_chain():
|
|
16 |
"""
|
17 |
|
18 |
# get retriever
|
19 |
-
retriever = get_base_retriever(k=4, search_type="mmr")
|
20 |
|
21 |
# instantiate llm
|
22 |
llm = load_hf_llm(repo_id=HF_MODEL, max_new_tokens=512, temperature=0.4)
|
@@ -28,17 +30,22 @@ def get_qa_chain():
|
|
28 |
|
29 |
|
30 |
def set_global_qa_chain(local_qa_chain):
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
global global_qa_chain
|
32 |
global_qa_chain = local_qa_chain
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
def generate_response(message, history):
|
37 |
"""
|
38 |
Generates response based on the question being asked.
|
39 |
|
40 |
Args:
|
41 |
-
|
42 |
history (dict): Chat history. NOT USED FOR NOW.
|
43 |
|
44 |
Returns:
|
@@ -46,12 +53,48 @@ def generate_response(message, history):
|
|
46 |
"""
|
47 |
|
48 |
# invoke chain
|
49 |
-
print("
|
|
|
50 |
start_time = time.time()
|
51 |
-
query = message
|
52 |
response = global_qa_chain.invoke(query)
|
53 |
print("Answer:", response)
|
54 |
end_time = time.time()
|
55 |
print("Response Time:", "{:.2f}".format(round(end_time - start_time, 2)))
|
56 |
-
|
57 |
return response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# import libraries
|
2 |
import time
|
3 |
+
from typing import Optional
|
4 |
from src.retrieval.retriever_chain import get_base_retriever, load_hf_llm, create_qa_chain
|
5 |
|
6 |
# constants
|
7 |
+
HF_MODEL = "huggingfaceh4/zephyr-7b-beta" # "mistralai/Mistral-7B-Instruct-v0.2" # "google/gemma-7b"
|
8 |
+
EMBEDDING_MODEL = "BAAI/bge-large-en-v1.5"
|
9 |
|
10 |
|
11 |
# get the qa chain
|
|
|
18 |
"""
|
19 |
|
20 |
# get retriever
|
21 |
+
retriever = get_base_retriever(embedding_model=EMBEDDING_MODEL, k=4, search_type="mmr")
|
22 |
|
23 |
# instantiate llm
|
24 |
llm = load_hf_llm(repo_id=HF_MODEL, max_new_tokens=512, temperature=0.4)
|
|
|
30 |
|
31 |
|
32 |
def set_global_qa_chain(local_qa_chain):
|
33 |
+
"""
|
34 |
+
Sets the Global QA Chain.
|
35 |
+
|
36 |
+
Args:
|
37 |
+
local_qa_chain: Local QA Chain.
|
38 |
+
"""
|
39 |
global global_qa_chain
|
40 |
global_qa_chain = local_qa_chain
|
41 |
+
|
42 |
+
# function to generate streamlit response
|
43 |
+
def generate_response(query: str):
|
|
|
44 |
"""
|
45 |
Generates response based on the question being asked.
|
46 |
|
47 |
Args:
|
48 |
+
query (str): Question asked by the user.
|
49 |
history (dict): Chat history. NOT USED FOR NOW.
|
50 |
|
51 |
Returns:
|
|
|
53 |
"""
|
54 |
|
55 |
# invoke chain
|
56 |
+
print("*" * 100)
|
57 |
+
print("Question:", query)
|
58 |
start_time = time.time()
|
|
|
59 |
response = global_qa_chain.invoke(query)
|
60 |
print("Answer:", response)
|
61 |
end_time = time.time()
|
62 |
print("Response Time:", "{:.2f}".format(round(end_time - start_time, 2)))
|
63 |
+
print("*" * 100)
|
64 |
return response
|
65 |
+
|
66 |
+
# function to generate streamlit response
|
67 |
+
def generate_response_streamlit(message: str, history: Optional[dict]):
|
68 |
+
"""
|
69 |
+
Generates response based on the question being asked.
|
70 |
+
|
71 |
+
Args:
|
72 |
+
message (str): Question asked by the user.
|
73 |
+
history (dict): Chat history. NOT USED FOR NOW.
|
74 |
+
|
75 |
+
Returns:
|
76 |
+
str: Returns the generated response.
|
77 |
+
"""
|
78 |
+
|
79 |
+
response = generate_response(message)
|
80 |
+
for word in response.split():
|
81 |
+
yield word + " "
|
82 |
+
time.sleep(0.05)
|
83 |
+
|
84 |
+
# function to generate response
|
85 |
+
def generate_response_gradio(message: str, history: Optional[dict]):
|
86 |
+
"""
|
87 |
+
Generates response based on the question being asked.
|
88 |
+
|
89 |
+
Args:
|
90 |
+
message (str): Question asked by the user.
|
91 |
+
history (dict): Chat history. NOT USED FOR NOW.
|
92 |
+
|
93 |
+
Returns:
|
94 |
+
str: Returns the generated response.
|
95 |
+
"""
|
96 |
+
|
97 |
+
response = generate_response(message)
|
98 |
+
for i in range(len(response)):
|
99 |
+
time.sleep(0.01)
|
100 |
+
yield response[: i+1]
|
src/indexing/build_indexes.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
# import libraries
|
2 |
import os
|
3 |
-
from typing import List
|
4 |
from transformers import AutoTokenizer
|
5 |
from langchain_community.vectorstores import Chroma
|
6 |
from sentence_transformers import SentenceTransformer
|
7 |
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
8 |
-
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
|
9 |
from langchain.docstore.document import Document
|
10 |
|
11 |
# import functions
|
@@ -13,13 +13,15 @@ from ..data.load_dataset import load_documents
|
|
13 |
|
14 |
# constants
|
15 |
INDEX_DIR = "indexes/"
|
16 |
-
EMBEDDING_MODEL = "BAAI/bge-large-en-v1.5"
|
17 |
|
18 |
|
19 |
# instantiate embedding model
|
20 |
-
def load_embedding_model():
|
21 |
"""
|
22 |
Load the embedding model.
|
|
|
|
|
|
|
23 |
|
24 |
Returns:
|
25 |
HuggingFaceBgeEmbeddings: Returns the embedding model.
|
@@ -32,7 +34,7 @@ def load_embedding_model():
|
|
32 |
print("device:", device)
|
33 |
|
34 |
hf_bge_embeddings = HuggingFaceBgeEmbeddings(
|
35 |
-
model_name=
|
36 |
model_kwargs={"device": device},
|
37 |
encode_kwargs={
|
38 |
"normalize_embeddings": True # set True to compute cosine similarity
|
@@ -41,17 +43,31 @@ def load_embedding_model():
|
|
41 |
|
42 |
# To get the value of the max sequence_length, we will query the underlying `SentenceTransformer` object used in the RecursiveCharacterTextSplitter.
|
43 |
print(
|
44 |
-
f"Model's maximum sequence length: {SentenceTransformer(
|
45 |
)
|
46 |
|
47 |
return hf_bge_embeddings
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
# split documents
|
51 |
def chunk_documents(
|
52 |
chunk_size: int,
|
53 |
knowledge_base: List[Document],
|
54 |
-
tokenizer_name:
|
55 |
) -> List[Document]:
|
56 |
"""
|
57 |
Split documents into chunks of maximum size `chunk_size` tokens and return a list of documents.
|
@@ -90,9 +106,12 @@ def chunk_documents(
|
|
90 |
|
91 |
|
92 |
# generate indexes
|
93 |
-
def generate_indexes():
|
94 |
"""
|
95 |
Generates indexes.
|
|
|
|
|
|
|
96 |
|
97 |
Returns:
|
98 |
ChromaCollection: Returns vector store.
|
@@ -104,16 +123,16 @@ def generate_indexes():
|
|
104 |
# chunk documents to honor the context length
|
105 |
chunked_documents = chunk_documents(
|
106 |
SentenceTransformer(
|
107 |
-
|
108 |
).max_seq_length, # We choose a chunk size adapted to our model
|
109 |
documents,
|
110 |
-
tokenizer_name=
|
111 |
)
|
112 |
|
113 |
# save indexes to disk
|
114 |
vector_store = Chroma.from_documents(
|
115 |
documents=chunked_documents,
|
116 |
-
embedding=load_embedding_model(),
|
117 |
collection_metadata={"hnsw:space": "cosine"},
|
118 |
persist_directory=INDEX_DIR,
|
119 |
)
|
@@ -122,24 +141,30 @@ def generate_indexes():
|
|
122 |
|
123 |
|
124 |
# load indexes from disk
|
125 |
-
def load_indexes():
|
126 |
"""
|
127 |
Loads indexes into memory.
|
|
|
|
|
|
|
128 |
|
129 |
Returns:
|
130 |
ChromaCollection: Returns vector store.
|
131 |
"""
|
132 |
|
133 |
vector_store = Chroma(
|
134 |
-
persist_directory=INDEX_DIR, embedding_function=
|
135 |
)
|
136 |
return vector_store
|
137 |
|
138 |
|
139 |
# retrieve vector store
|
140 |
-
def retrieve_indexes():
|
141 |
"""
|
142 |
Retrieves indexes.
|
|
|
|
|
|
|
143 |
|
144 |
Returns:
|
145 |
ChromaCollection: Returns vector store.
|
@@ -147,7 +172,7 @@ def retrieve_indexes():
|
|
147 |
|
148 |
if [f for f in os.listdir(INDEX_DIR) if not f.startswith(".")] == []:
|
149 |
print("Generating indexes...")
|
150 |
-
return generate_indexes()
|
151 |
else:
|
152 |
print("Loading existing indexes!")
|
153 |
-
return load_indexes()
|
|
|
1 |
# import libraries
|
2 |
import os
|
3 |
+
from typing import List
|
4 |
from transformers import AutoTokenizer
|
5 |
from langchain_community.vectorstores import Chroma
|
6 |
from sentence_transformers import SentenceTransformer
|
7 |
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
8 |
+
from langchain_community.embeddings import HuggingFaceBgeEmbeddings, HuggingFaceInferenceAPIEmbeddings
|
9 |
from langchain.docstore.document import Document
|
10 |
|
11 |
# import functions
|
|
|
13 |
|
14 |
# constants
|
15 |
INDEX_DIR = "indexes/"
|
|
|
16 |
|
17 |
|
18 |
# instantiate embedding model
|
19 |
+
def load_embedding_model(embedding_model):
|
20 |
"""
|
21 |
Load the embedding model.
|
22 |
+
|
23 |
+
Args:
|
24 |
+
embedding_model(str): Hugging Face Embedding Model name.
|
25 |
|
26 |
Returns:
|
27 |
HuggingFaceBgeEmbeddings: Returns the embedding model.
|
|
|
34 |
print("device:", device)
|
35 |
|
36 |
hf_bge_embeddings = HuggingFaceBgeEmbeddings(
|
37 |
+
model_name=embedding_model,
|
38 |
model_kwargs={"device": device},
|
39 |
encode_kwargs={
|
40 |
"normalize_embeddings": True # set True to compute cosine similarity
|
|
|
43 |
|
44 |
# To get the value of the max sequence_length, we will query the underlying `SentenceTransformer` object used in the RecursiveCharacterTextSplitter.
|
45 |
print(
|
46 |
+
f"Model's maximum sequence length: {SentenceTransformer(embedding_model).max_seq_length}"
|
47 |
)
|
48 |
|
49 |
return hf_bge_embeddings
|
50 |
|
51 |
+
# instantiate embedding model
|
52 |
+
def load_inference_embedding_model(embedding_model):
|
53 |
+
"""
|
54 |
+
Load the inference embedding model.
|
55 |
+
|
56 |
+
Args:
|
57 |
+
embedding_model(str): Hugging Face Embedding Model name.
|
58 |
+
|
59 |
+
Returns:
|
60 |
+
HuggingFaceInferenceAPIEmbeddings: Returns the inference embedding model.
|
61 |
+
"""
|
62 |
+
hf_inference_bge_embeddings = HuggingFaceInferenceAPIEmbeddings(model_name=embedding_model, api_key=os.environ["HUGGINGFACEHUB_API_TOKEN"])
|
63 |
+
return hf_inference_bge_embeddings
|
64 |
+
|
65 |
|
66 |
# split documents
|
67 |
def chunk_documents(
|
68 |
chunk_size: int,
|
69 |
knowledge_base: List[Document],
|
70 |
+
tokenizer_name: str,
|
71 |
) -> List[Document]:
|
72 |
"""
|
73 |
Split documents into chunks of maximum size `chunk_size` tokens and return a list of documents.
|
|
|
106 |
|
107 |
|
108 |
# generate indexes
|
109 |
+
def generate_indexes(embedding_model):
|
110 |
"""
|
111 |
Generates indexes.
|
112 |
+
|
113 |
+
Args:
|
114 |
+
embedding_model(str): Hugging Face Embedding Model name.
|
115 |
|
116 |
Returns:
|
117 |
ChromaCollection: Returns vector store.
|
|
|
123 |
# chunk documents to honor the context length
|
124 |
chunked_documents = chunk_documents(
|
125 |
SentenceTransformer(
|
126 |
+
embedding_model
|
127 |
).max_seq_length, # We choose a chunk size adapted to our model
|
128 |
documents,
|
129 |
+
tokenizer_name=embedding_model,
|
130 |
)
|
131 |
|
132 |
# save indexes to disk
|
133 |
vector_store = Chroma.from_documents(
|
134 |
documents=chunked_documents,
|
135 |
+
embedding=load_embedding_model(embedding_model),
|
136 |
collection_metadata={"hnsw:space": "cosine"},
|
137 |
persist_directory=INDEX_DIR,
|
138 |
)
|
|
|
141 |
|
142 |
|
143 |
# load indexes from disk
|
144 |
+
def load_indexes(embedding_model):
|
145 |
"""
|
146 |
Loads indexes into memory.
|
147 |
+
|
148 |
+
Args:
|
149 |
+
embedding_model(str): Hugging Face Embedding Model name.
|
150 |
|
151 |
Returns:
|
152 |
ChromaCollection: Returns vector store.
|
153 |
"""
|
154 |
|
155 |
vector_store = Chroma(
|
156 |
+
persist_directory=INDEX_DIR, embedding_function=load_inference_embedding_model(embedding_model)
|
157 |
)
|
158 |
return vector_store
|
159 |
|
160 |
|
161 |
# retrieve vector store
|
162 |
+
def retrieve_indexes(embedding_model):
|
163 |
"""
|
164 |
Retrieves indexes.
|
165 |
+
|
166 |
+
Args:
|
167 |
+
embedding_model(str): Hugging Face Embedding Model name.
|
168 |
|
169 |
Returns:
|
170 |
ChromaCollection: Returns vector store.
|
|
|
172 |
|
173 |
if [f for f in os.listdir(INDEX_DIR) if not f.startswith(".")] == []:
|
174 |
print("Generating indexes...")
|
175 |
+
return generate_indexes(embedding_model)
|
176 |
else:
|
177 |
print("Loading existing indexes!")
|
178 |
+
return load_indexes(embedding_model)
|
src/retrieval/retriever_chain.py
CHANGED
@@ -10,11 +10,12 @@ from ..indexing.build_indexes import retrieve_indexes
|
|
10 |
|
11 |
|
12 |
# instantiate base retriever
|
13 |
-
def get_base_retriever(k=4, search_type="mmr"):
|
14 |
"""
|
15 |
Instantiates base retriever.
|
16 |
|
17 |
Args:
|
|
|
18 |
k (int, optional): Top k results to retrieve. Defaults to 4.
|
19 |
search_type (str, optional): Search type (mmr or similarity). Defaults to 'mmr'.
|
20 |
|
@@ -23,7 +24,7 @@ def get_base_retriever(k=4, search_type="mmr"):
|
|
23 |
"""
|
24 |
|
25 |
# get the vector store of indexes
|
26 |
-
vector_store = retrieve_indexes()
|
27 |
|
28 |
base_retriever = vector_store.as_retriever(
|
29 |
search_type=search_type, search_kwargs={"k": k}
|
|
|
10 |
|
11 |
|
12 |
# instantiate base retriever
|
13 |
+
def get_base_retriever(embedding_model, k=4, search_type="mmr"):
|
14 |
"""
|
15 |
Instantiates base retriever.
|
16 |
|
17 |
Args:
|
18 |
+
embedding_model(str): Hugging Face Embedding Model name.
|
19 |
k (int, optional): Top k results to retrieve. Defaults to 4.
|
20 |
search_type (str, optional): Search type (mmr or similarity). Defaults to 'mmr'.
|
21 |
|
|
|
24 |
"""
|
25 |
|
26 |
# get the vector store of indexes
|
27 |
+
vector_store = retrieve_indexes(embedding_model)
|
28 |
|
29 |
base_retriever = vector_store.as_retriever(
|
30 |
search_type=search_type, search_kwargs={"k": k}
|
src/test/eval_rag.py
CHANGED
@@ -56,6 +56,12 @@ def evaluate_rag(chain_name, rag_chain):
|
|
56 |
"Response": answer,
|
57 |
"Time": "{:.2f}".format(round(end_time - start_time, 2)),
|
58 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
df = pd.concat([df, pd.DataFrame.from_records([row])])
|
61 |
|
|
|
56 |
"Response": answer,
|
57 |
"Time": "{:.2f}".format(round(end_time - start_time, 2)),
|
58 |
}
|
59 |
+
|
60 |
+
print("*" * 100)
|
61 |
+
print("Question:", question)
|
62 |
+
print("Answer:", answer)
|
63 |
+
print("Response Time:", "{:.2f}".format(round(end_time - start_time, 2)))
|
64 |
+
print("*" * 100)
|
65 |
|
66 |
df = pd.concat([df, pd.DataFrame.from_records([row])])
|
67 |
|
src/test/eval_results_20240325111437.csv
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Chain,Question,Response,Time
|
2 |
+
qa_chain,What is FastMap?," FastMap is a productivity tool that integrates with IBM OpenPages with Watson's export feature and automates the process of importing and batch processing object data into OpenPages with Watson. It uses a data load template, typically in Microsoft Excel format, to capture data for import. When importing data into OpenPages with Watson, FastMap validates the data and, if no errors are found, populates the repository with the new or updated records. It supports the import of various types of objects, but not File and Signature objects or the Comment field's system field. Some general rules apply when entering object data into a FastMap data load template, such as associating child objects using specific columns and not importing certain fields like read-only fields or those not in the Admin view for the object. Additionally, FastMap import does not evaluate field dependency rules, allowing users to stage data requiring them to enter required data during subsequent updates. Overall, FastMap helps streamline the process of importing large amounts of data into OpenPages with Watson, reducing manual input and increasing efficiency.",4.01
|
3 |
+
qa_chain,What is a Role Template?," A Role Template in IBM OpenPages with Watson solutions is a predefined set of permissions and access controls that determine the level of access and functionality granted to a user in the system. It includes application permissions, feature access, and Object Access Controls (OACs) for specific object types. Role Templates help simplify the process of managing user access and ensure consistent security policies across the organization. By assigning a Role Template to a user, you can quickly and easily grant them the required permissions and access levels for their job function.",1.85
|
4 |
+
qa_chain,What is the purpose of Object Reset?," The purpose of Object Reset in IBM OpenPages with Watson is to allow users to efficiently update multiple objects at once based on predefined rules. This feature helps to simplify repetitive data entry tasks and reduce errors associated with manually updating large numbers of objects. It also provides flexibility in terms of what objects are affected by the reset, as users can choose specific business entities and define rulesets to control the behavior of the reset. Overall, Object Reset helps to streamline data management processes and improve efficiency in IBM OpenPages with Watson.",2.56
|
5 |
+
qa_chain,What is the purpose of Reporting Periods?," Reporting periods in IBM OpenPages with Watson serve as ""snapshots"" of the current state of the repository during specific points in time, typically at the end of a quarter or year. These periods allow organizations to archive and report on historical data while maintaining the ability to view and analyze current data. Reporting periods facilitate compliance and regulatory requirements, provide insights into trends and patterns, and enable comparisons between different periods. Additionally, they can be used to automate object resets, which modify object properties based on predefined rulesets. Overall, reporting periods help organizations manage and analyze their data more efficiently and effectively.",2.05
|
6 |
+
qa_chain,List the system variables used in Expressions.,"1. $APPLICATION_URL$: A URL for OpenPages.
|
7 |
+
2. $COGNOS_URL$: A URL for IBM Cognos Analytics.
|
8 |
+
3. $ApplicationText/application text key$): Application text content.
|
9 |
+
4. $TASK_VIEW_URL$): A URL to an object task view.
|
10 |
+
5. $System Fields:Task View URL$): Deprecated. Use [$TASK_VIEW_URL$].
|
11 |
+
6. $Setting/OpenPages/...$): A registry setting value.
|
12 |
+
7. $END_USER$): The user name of the signed on user.
|
13 |
+
8. $TODAY$): Today's date.
|
14 |
+
9. $DaysFromNow/field group:field name$): A day count from today to a given date.
|
15 |
+
10. ${asset.id}$: Inserts the identifier of the underlying asset.
|
16 |
+
11. ${asset.name}$: Inserts the asset name of the underlying asset.
|
17 |
+
12. ${asset.description}$: Inserts the asset description of the underlying asset.",2.46
|
18 |
+
qa_chain,Provide the steps to configure Watson Assistant in OpenPages?," Unfortunately, I don't have access to your specific environment or installation details. However, here are the general steps to configure Watson Assistant in OpenPages:
|
19 |
+
|
20 |
+
1. Ensure that you have a Watson Assistant service instance created and configured with the necessary intents and entities.
|
21 |
+
|
22 |
+
2. In OpenPages, go to the Administration Console > Integrations > Watson Assistant.
|
23 |
+
|
24 |
+
3. Click on the ""Add"" button to create a new Watson Assistant integration.
|
25 |
+
|
26 |
+
4. Enter a name and description for the integration and select the appropriate Watson Assistant service instance from the list.
|
27 |
+
|
28 |
+
5. Configure the authentication settings by providing the API key and URL of your Watson Assistant service.
|
29 |
+
|
30 |
+
6. Select the appropriate language and version of Watson Assistant.
|
31 |
+
|
32 |
+
7. Map the Watson Assistant intents and entities to the corresponding OpenPages business objects and fields.
|
33 |
+
|
34 |
+
8. Save the configuration and test the integration by running a sample request through the Watson Assistant REST API.
|
35 |
+
|
36 |
+
9. Verify that the response is correctly mapped to the appropriate OpenPages business object and field values.
|
37 |
+
|
38 |
+
10. Deploy the integration to make it available to users in the OpenPages application.
|
39 |
+
|
40 |
+
Note: These steps may vary depending on your specific implementation and version of OpenPages and Watson Assistant. It's recommended to refer to the official documentation for detailed instructions and best practices.",1.94
|
41 |
+
qa_chain,What is the difference between PRE and POST position in Triggers?,"
|
42 |
+
In Triggers, the position ""PRE"" refers to events that occur prior to the actual execution of an operation by the system. These triggers allow for additional processing of business logic before the operation is carried out. For example, during the creation of a GRC object, a PRE trigger has access to all the necessary information about the object to be created, but the system has not yet taken action to create the object and persist its values. PRE triggers are mandatory for deletions, associations, and disassociations.
|
43 |
+
|
44 |
+
On the other hand, the position ""POST"" refers to events that occur after an operation has been performed by the system but before the transaction is committed. These triggers provide further processing of additional business logic after the operation has been completed. POST triggers are mandatory for creating and updating operations.
|
45 |
+
|
46 |
+
In summary, PRE triggers execute before the operation takes place, while POST triggers execute after the operation has been completed.",2.56
|
47 |
+
qa_chain,What are the features of Operational Risk Management in OpenPages?,"1. Loss Events: This feature allows organizations to track, assess, and manage both internal and external events that could lead to operational losses. Multiple impact events and recoveries associated with operational losses can also be managed through this feature.
|
48 |
+
|
49 |
+
2. Risk and Control Self Assessments (RCSA): This feature helps in identifying, measuring, and mitigating risks, as well as testing and documenting internal controls.
|
50 |
+
|
51 |
+
3. Key Risk Indicators (KRIs) and Key Performance Indicators (KPIs): These features enable tracking of performance metrics that may indicate the presence or state of a risk condition or trend.
|
52 |
+
|
53 |
+
4. Scenario Analysis: This feature is used to identify and measure specific types of risks, particularly low-frequency, high-severity events.
|
54 |
+
|
55 |
+
5. External Loss Events: This feature allows for the import of loss data from various sources such as IBM FIRST Risk Case Studies, ORX, and ORIC loss databases for scenario analysis, benchmarking, and report generation.
|
56 |
+
|
57 |
+
6. Issue Management and Remediation (IMR): This feature includes issue creation and assignment, action creation and assignment, remediation performance, issue closure, and reporting.
|
58 |
+
|
59 |
+
7. Reporting, Monitoring, and Analytics: This feature provides reporting, monitoring, and analytics capabilities for operational risk management.
|
60 |
+
|
61 |
+
Note: The above features are part of IBM OpenPages Operational Risk Management. Other related features like Regulatory Compliance Management, Third Party Risk Management, and IT Governance with RiskLens are also available in IBM OpenPages.",2.56
|
62 |
+
qa_chain,What are the different permissions that can be delegated to a user group administrator?," There are six security management permissions that can be delegated to a user group administrator: Manage, Lock, Unlock, Reset Password, Assign Role, and Browse. These permissions allow the administrator to perform various user-provisioning functions such as creating, modifying, and associating users and groups, locking and unlocking user accounts, resetting passwords, assigning roles to users and groups, and browsing users and groups within their respective groups. The specific permissions required for each user-provisioning function are listed in Table 38 provided in the text material. It's important to note that these permissions should be granted carefully and only to trusted individuals as they have significant impact on the overall security and functionality of the system.",8.57
|
63 |
+
qa_chain,What are the different access controls available for non-participants for a standard stage within a workflow?," Table 142 in the text provides the different access controls available for non-participants for a standard stage within a workflow:
|
64 |
+
|
65 |
+
Access control for the stage
|
66 |
+
Can view the object when it’s at this stage
|
67 |
+
Can edit the object when it’s at this stage
|
68 |
+
Can see the Actions button in views
|
69 |
+
Strict
|
70 |
+
No
|
71 |
+
No
|
72 |
+
No
|
73 |
+
Read
|
74 |
+
Yes
|
75 |
+
No
|
76 |
+
No
|
77 |
+
Open
|
78 |
+
Depends on standard access controls
|
79 |
+
Depends on standard access controls
|
80 |
+
No
|
81 |
+
No Override
|
82 |
+
Depends on standard access controls
|
83 |
+
Depends on standard access controls
|
84 |
+
Yes
|
85 |
+
|
86 |
+
Note: For workflow participants (assignees, oversight users, and subscribers), the standard access controls apply, and they only know their part of the process. Non-participants may require additional access controls based on their roles and responsibilities within the organization. The ""Override"" option allows you to define whether to override these standard access controls for the workflow stage for non-participants.",41.15
|
src/ui/{chat_interface.py → gradio_chat_interface.py}
RENAMED
@@ -6,7 +6,7 @@ from src.test.eval_rag import load_eval_questions
|
|
6 |
|
7 |
|
8 |
# create chatbot interface
|
9 |
-
def
|
10 |
"""
|
11 |
Instantiates the gradio chat interface.
|
12 |
|
|
|
6 |
|
7 |
|
8 |
# create chatbot interface
|
9 |
+
def create_gradio_chatinterface(generate_response):
|
10 |
"""
|
11 |
Instantiates the gradio chat interface.
|
12 |
|
src/ui/streamlit_chat_interface.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import libraries
|
2 |
+
import streamlit as st
|
3 |
+
|
4 |
+
# import functions
|
5 |
+
from src.test.eval_rag import load_eval_questions
|
6 |
+
|
7 |
+
# initialize streamlit
|
8 |
+
def create_streamlit_chatinterface(generate_response):
|
9 |
+
"""
|
10 |
+
Instantiates the streamlit chat interface.
|
11 |
+
|
12 |
+
Args:
|
13 |
+
generate_response (callable): Function that generates the response.
|
14 |
+
"""
|
15 |
+
|
16 |
+
st.title("OpenPages IntelliBot")
|
17 |
+
st.caption("Ask me about OpenPages (v9.0), its features, solutions / modules it offers and the trigger framework. Authored by Nikhil Komakula ([email protected]).")
|
18 |
+
st.divider()
|
19 |
+
|
20 |
+
# Initialize chat history
|
21 |
+
if "messages" not in st.session_state:
|
22 |
+
st.session_state.messages = []
|
23 |
+
|
24 |
+
# Display chat messages from history on app rerun
|
25 |
+
for message in st.session_state.messages:
|
26 |
+
with st.chat_message(message["role"]):
|
27 |
+
st.markdown(message["content"])
|
28 |
+
|
29 |
+
# Accept user input
|
30 |
+
if prompt := st.chat_input("Type your question here!"):
|
31 |
+
# Add user message to chat history
|
32 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
33 |
+
# Display user message in chat message container
|
34 |
+
with st.chat_message("user"):
|
35 |
+
st.markdown(prompt)
|
36 |
+
|
37 |
+
# Display assistant response in chat message container
|
38 |
+
with st.chat_message("assistant"):
|
39 |
+
with st.spinner(""):
|
40 |
+
response = st.write_stream(generate_response(prompt, st.session_state.messages))
|
41 |
+
|
42 |
+
# Add assistant response to chat history
|
43 |
+
st.session_state.messages.append({"role": "assistant", "content": response})
|
44 |
+
|
45 |
+
# option = st.selectbox("Examples:", tuple(load_eval_questions()))
|
46 |
+
# if option:
|
47 |
+
# st.chat_message(option)
|
streamlit_ui.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import libraries
|
2 |
+
from dotenv import find_dotenv, load_dotenv
|
3 |
+
|
4 |
+
# import functions
|
5 |
+
from src.ui.streamlit_chat_interface import create_streamlit_chatinterface
|
6 |
+
from src.generation.generate_response import get_qa_chain, set_global_qa_chain, generate_response_streamlit
|
7 |
+
|
8 |
+
def main():
|
9 |
+
|
10 |
+
# find .env automatically by walking up directories until it's found, then
|
11 |
+
# load up the .env entries as environment variables
|
12 |
+
load_dotenv(find_dotenv())
|
13 |
+
|
14 |
+
# get the qa chain
|
15 |
+
qa_chain = get_qa_chain()
|
16 |
+
|
17 |
+
# set the global qa chain
|
18 |
+
set_global_qa_chain(qa_chain)
|
19 |
+
|
20 |
+
# initiate the chat interface
|
21 |
+
create_streamlit_chatinterface(generate_response_streamlit)
|
22 |
+
|
23 |
+
if __name__ == "__main__":
|
24 |
+
main()
|