Spaces:
Paused
Paused
Ashvanth.S
commited on
Commit
β’
dbb2933
1
Parent(s):
c5d127d
Add initial files
Browse files- .gitignore +9 -0
- README.md +124 -11
- chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/data_level0.bin +3 -0
- chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/header.bin +3 -0
- chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/length.bin +3 -0
- chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/link_lists.bin +0 -0
- chroma_langchain_db/chroma.sqlite3 +0 -0
- main_app.py +72 -0
- requirements.txt +9 -0
- utils/__init__.py +0 -0
- utils/agent.py +114 -0
- utils/document_loader.py +14 -0
- utils/embeddings.py +8 -0
- utils/gradio_interface.py +46 -0
- utils/rag_chain.py +62 -0
- utils/vector_store.py +36 -0
.gitignore
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.venv/
|
2 |
+
|
3 |
+
.env
|
4 |
+
|
5 |
+
|
6 |
+
__pycache__/
|
7 |
+
|
8 |
+
# Jupyter Notebook checkpoints
|
9 |
+
.ipynb_checkpoints/
|
README.md
CHANGED
@@ -1,11 +1,124 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# RAG and Agent-based Q&A System
|
2 |
+
|
3 |
+
## Table of Contents
|
4 |
+
- [Introduction](#introduction)
|
5 |
+
- [Features](#features)
|
6 |
+
- [System Architecture](#system-architecture)
|
7 |
+
- [Prerequisites](#prerequisites)
|
8 |
+
- [Installation](#installation)
|
9 |
+
- [Usage](#usage)
|
10 |
+
- [API Endpoints](#api-endpoints)
|
11 |
+
- [Frontend Interface](#frontend-interface)
|
12 |
+
|
13 |
+
This project implements a sophisticated Question-Answering system that combines Retrieval-Augmented Generation (RAG) with an intelligent agent. The system is designed to answer queries related to NCERT textbooks chapters, specifically focusing on the Sound chapter, while also handling general queries using web search capabilities. Do not: **Don't forget to specify your open-ai key in the .env file**
|
14 |
+
|
15 |
+
The application serves two main functionalities:
|
16 |
+
|
17 |
+
- A RAG system that retrieves relevant information from a vector database containing NCERT textbook content.
|
18 |
+
- An agent-based system that can perform smart actions based on the user's query, including invoking the RAG system when appropriate and using additional tools like web search.
|
19 |
+
|
20 |
+
## Introduction
|
21 |
+
|
22 |
+
This project implements a sophisticated Question-Answering system that combines Retrieval-Augmented Generation (RAG) with an intelligent agent. The system is designed to answer queries related to NCERT textbooks, specifically focusing on the Sound chapter, while also handling general queries using web search capabilities.
|
23 |
+
|
24 |
+
The application serves two main functionalities:
|
25 |
+
1. A RAG system that retrieves relevant information from a vector database containing NCERT textbook content.
|
26 |
+
2. An agent-based system that can perform smart actions based on the user's query, including invoking the RAG system when appropriate and using additional tools like web search.
|
27 |
+
|
28 |
+
## Features
|
29 |
+
|
30 |
+
- **RAG System**:
|
31 |
+
- Utilizes a vector database to store and retrieve relevant information from NCERT textbooks.
|
32 |
+
- Provides accurate and concise answers to questions related to the Sound chapter.
|
33 |
+
|
34 |
+
- **Intelligent Agent**:
|
35 |
+
- Determines when to use the RAG system based on the query content.
|
36 |
+
- Incorporates additional tools, including web search for non-textbook related queries.
|
37 |
+
- Calculates word count of responses when requested.
|
38 |
+
|
39 |
+
- **FastAPI Backend**:
|
40 |
+
- Serves both RAG and Agent functionalities via separate endpoints.
|
41 |
+
- Ensures efficient and scalable handling of requests.
|
42 |
+
|
43 |
+
- **Gradio Frontend**:
|
44 |
+
- Provides an intuitive user interface for interacting with both the RAG and Agent systems.
|
45 |
+
- Allows easy testing and demonstration of the system's capabilities.
|
46 |
+
|
47 |
+
|
48 |
+
## System Architecture
|
49 |
+
|
50 |
+
The system is built using the following key components:
|
51 |
+
|
52 |
+
1. **Vector Store**: Stores embeddings of NCERT textbook content for efficient retrieval.
|
53 |
+
2. **LangChain**: Facilitates the creation of the RAG chain and the agent.
|
54 |
+
3. **OpenAI's ChatGPT**: Powers the language model for generating responses.
|
55 |
+
4. **DuckDuckGo Search API**: Enables web search capabilities for the agent.
|
56 |
+
5. **FastAPI**: Provides the backend API framework.
|
57 |
+
6. **Gradio**: Creates the frontend user interface.
|
58 |
+
|
59 |
+
## Installation
|
60 |
+
|
61 |
+
1. Clone the repository and create a virtual env
|
62 |
+
```bash
|
63 |
+
python -m venv venv
|
64 |
+
source venv/bin/activate
|
65 |
+
```
|
66 |
+
|
67 |
+
2.Install the required packages:
|
68 |
+
```bash
|
69 |
+
pip install -r requirements.txt
|
70 |
+
```
|
71 |
+
|
72 |
+
3. Setup up .env with all the environment variables
|
73 |
+
```bash
|
74 |
+
OPEN_API_KEY=your_openai_api_key
|
75 |
+
UVICORN_HOST = 127.0.0.1
|
76 |
+
UVICORN_PORT = 7860
|
77 |
+
SOURCE_DATA = "../pdf_data"
|
78 |
+
VECTOR_STORE = "../chroma_langchain_db"
|
79 |
+
```
|
80 |
+
|
81 |
+
## Usage
|
82 |
+
|
83 |
+
To start the application run:
|
84 |
+
```python
|
85 |
+
python3 main_app.py
|
86 |
+
```
|
87 |
+
|
88 |
+
This will start the FastAPI server and launch the Gradio interface. You can access the Gradio interface by navigating to `http://localhost:8000` in your web browser.
|
89 |
+
|
90 |
+
## API Endpoints
|
91 |
+
|
92 |
+
The application exposes two main endpoints:
|
93 |
+
|
94 |
+
1. `/rag` (POST): For querying the RAG system
|
95 |
+
- Request body: `{ "question": "Your question here" }`
|
96 |
+
- Response: `{ "answer": "Generated answer" }`
|
97 |
+
|
98 |
+
2. `/agent` (POST): For interacting with the intelligent agent
|
99 |
+
- Request body: `{ "question": "Your question here" }`
|
100 |
+
- Response: `{ "answer": "Agent's response" }`
|
101 |
+
|
102 |
+
|
103 |
+
## Frontend Interface
|
104 |
+
|
105 |
+
The Gradio interface provides two tabs:
|
106 |
+
|
107 |
+
1. **RAG System**: For asking questions related to the NCERT Sound chapter.
|
108 |
+
2. **Agent**: For general queries, including those that may require web search or other tools.
|
109 |
+
|
110 |
+
Users can type their questions in the input box and receive answers in real-time.
|
111 |
+
|
112 |
+
### RAG System Interface
|
113 |
+
|
114 |
+
![RAG System Interface](images/RAG_app.png)
|
115 |
+
|
116 |
+
### Agent Interface
|
117 |
+
|
118 |
+
![Agent Interface](images/Agent_app.png)
|
119 |
+
|
120 |
+
### API Documentation
|
121 |
+
|
122 |
+
The FastAPI automatic interactive API documentation is available at `/docs` endpoint:
|
123 |
+
|
124 |
+
![API Documentation](images/API_docs.png)
|
chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/data_level0.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f18abd8c514282db82706e52b0a33ed659cd534e925a6f149deb7af9ce34bd8e
|
3 |
+
size 6284000
|
chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/header.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:effaa959ce2b30070fdafc2fe82096fc46e4ee7561b75920dd3ce43d09679b21
|
3 |
+
size 100
|
chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/length.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f7e5e2d7ceaf351ff89d12523776511335dcc308f06544d4cbbc32efa59828a2
|
3 |
+
size 4000
|
chroma_langchain_db/237d30d4-0d4f-4fa7-b4ff-2981bcd6b160/link_lists.bin
ADDED
File without changes
|
chroma_langchain_db/chroma.sqlite3
ADDED
Binary file (643 kB). View file
|
|
main_app.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from fastapi import FastAPI
|
3 |
+
from pydantic import BaseModel
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
from utils.document_loader import load_pdf, create_unique_ids
|
6 |
+
from utils.embeddings import get_embeddings
|
7 |
+
from utils.vector_store import create_vector_store, get_retriever, load_vector_store
|
8 |
+
from utils.rag_chain import get_model, create_rag_chain, get_conversational_rag_chain
|
9 |
+
from utils.gradio_interface import create_gradio_interface
|
10 |
+
from utils.agent import init_agent, get_agent_response
|
11 |
+
import gradio as gr
|
12 |
+
|
13 |
+
load_dotenv()
|
14 |
+
|
15 |
+
app = FastAPI()
|
16 |
+
|
17 |
+
class QuestionRequest(BaseModel):
|
18 |
+
question: str
|
19 |
+
|
20 |
+
class AnswerResponse(BaseModel):
|
21 |
+
answer: str
|
22 |
+
|
23 |
+
def init_rag_system():
|
24 |
+
pdf_path = os.getenv("SOURCE_DATA")
|
25 |
+
vector_store_path = os.getenv("VECTOR_STORE")
|
26 |
+
# Load embeddings
|
27 |
+
embeddings = get_embeddings()
|
28 |
+
if os.path.exists(vector_store_path) and os.listdir(vector_store_path):
|
29 |
+
print("Loading existing vector store...")
|
30 |
+
vector_store = load_vector_store(embeddings)
|
31 |
+
else:
|
32 |
+
print("Creating new vector store...")
|
33 |
+
documents = load_pdf(pdf_path)
|
34 |
+
unique_ids = create_unique_ids(documents)
|
35 |
+
vector_store = create_vector_store(documents, unique_ids, embeddings)
|
36 |
+
retriever = get_retriever(vector_store)
|
37 |
+
model = get_model()
|
38 |
+
rag_chain = create_rag_chain(model, retriever)
|
39 |
+
return get_conversational_rag_chain(rag_chain)
|
40 |
+
|
41 |
+
# Initialize conversational RAG chain
|
42 |
+
conversational_rag_chain = init_rag_system()
|
43 |
+
|
44 |
+
# Initialize agent
|
45 |
+
agent = init_agent()
|
46 |
+
|
47 |
+
@app.post("/rag", response_model=AnswerResponse)
|
48 |
+
async def ask_rag_question(request: QuestionRequest):
|
49 |
+
print(f"RAG Question: {request.question}")
|
50 |
+
response = conversational_rag_chain.invoke(
|
51 |
+
{"input": request.question},
|
52 |
+
config={"configurable": {"session_id": "default_session"}}
|
53 |
+
)
|
54 |
+
return AnswerResponse(answer=response["answer"])
|
55 |
+
|
56 |
+
@app.post("/agent", response_model=AnswerResponse)
|
57 |
+
async def ask_agent_question(request: QuestionRequest):
|
58 |
+
print(f"Agent Question: {request.question}")
|
59 |
+
response = get_agent_response(agent, request.question)
|
60 |
+
return AnswerResponse(answer=response)
|
61 |
+
|
62 |
+
interface = create_gradio_interface(app, conversational_rag_chain, agent)
|
63 |
+
app = gr.mount_gradio_app(app, interface, path="/")
|
64 |
+
|
65 |
+
if __name__ == "__main__":
|
66 |
+
import uvicorn
|
67 |
+
uvicorn.run(
|
68 |
+
app,
|
69 |
+
host=os.getenv("UVICORN_HOST"),
|
70 |
+
port=int(os.getenv("UVICORN_PORT")),
|
71 |
+
# reload=True
|
72 |
+
)
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
chromadb
|
2 |
+
langchain
|
3 |
+
langchain-community
|
4 |
+
langchain-openai
|
5 |
+
langchain-chroma
|
6 |
+
gradio
|
7 |
+
openai
|
8 |
+
pydantic
|
9 |
+
duckduckgo-search
|
utils/__init__.py
ADDED
File without changes
|
utils/agent.py
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
from utils.embeddings import get_embeddings
|
4 |
+
from utils.vector_store import load_vector_store
|
5 |
+
from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
|
6 |
+
from langchain_community.tools import DuckDuckGoSearchResults
|
7 |
+
from langchain.chains import create_retrieval_chain
|
8 |
+
from langchain.chains.combine_documents import create_stuff_documents_chain
|
9 |
+
from langchain.tools import tool
|
10 |
+
from langchain_openai import ChatOpenAI
|
11 |
+
from langchain.agents import create_openai_functions_agent, AgentExecutor
|
12 |
+
from langchain_community.chat_message_histories import ChatMessageHistory
|
13 |
+
from langchain_core.messages import AIMessage
|
14 |
+
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
15 |
+
from langchain_core.runnables import RunnableWithMessageHistory
|
16 |
+
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
wrapper = DuckDuckGoSearchAPIWrapper(max_results=2)
|
20 |
+
search_web = DuckDuckGoSearchResults(api_wrapper=wrapper, source="news")
|
21 |
+
|
22 |
+
@tool
|
23 |
+
def rag_tool(query:str)->str:
|
24 |
+
"""
|
25 |
+
The function queries the vector db and retrieves the answer
|
26 |
+
"""
|
27 |
+
embeddings = get_embeddings()
|
28 |
+
vector_store = load_vector_store(embeddings)
|
29 |
+
retriver = vector_store.as_retriever(search_type='similarity',search_kwargs={"k": 2})
|
30 |
+
|
31 |
+
system_prompt = (
|
32 |
+
"You are an assistant for question-answering tasks. "
|
33 |
+
"Use the following pieces of retrieved context to answer "
|
34 |
+
"the question. If you don't know the answer, say that you "
|
35 |
+
"don't know. Use three sentences maximum and keep the "
|
36 |
+
"answer concise."
|
37 |
+
"\n\n"
|
38 |
+
"{context}"
|
39 |
+
)
|
40 |
+
|
41 |
+
prompt = ChatPromptTemplate.from_messages(
|
42 |
+
[
|
43 |
+
("system", system_prompt),
|
44 |
+
("human", "{input}"),
|
45 |
+
]
|
46 |
+
)
|
47 |
+
|
48 |
+
question_answer_chain = create_stuff_documents_chain(get_model_use(), prompt)
|
49 |
+
rag_chain = create_retrieval_chain(retriver, question_answer_chain)
|
50 |
+
response = rag_chain.invoke({"input":query})
|
51 |
+
return (response["answer"])
|
52 |
+
|
53 |
+
|
54 |
+
@tool
|
55 |
+
def calculate_word_count(words: str) -> int:
|
56 |
+
"""
|
57 |
+
The function helps in calculating the number of words present in the responses
|
58 |
+
"""
|
59 |
+
response = words.split()
|
60 |
+
return len(response)
|
61 |
+
|
62 |
+
tools = [rag_tool,search_web, calculate_word_count]
|
63 |
+
|
64 |
+
prompt = ChatPromptTemplate.from_messages([
|
65 |
+
("system", """
|
66 |
+
You are an assistant helping the user with queries related to the NCERT Sound chapter and real-time events or factual information. Follow these instructions:
|
67 |
+
|
68 |
+
1. **NCERT Sound Chapter Queries**:
|
69 |
+
- Use your rag tool provide to answer any query regarding NCERT Sound chapter to answer questions such as:
|
70 |
+
- What is an echo?
|
71 |
+
- How is sound propagated?
|
72 |
+
- What are the applications of ultrasound?
|
73 |
+
- STRICT RULE: Do not use external tools after using rag_tool
|
74 |
+
|
75 |
+
2. **Non-Sound Chapter Queries**:
|
76 |
+
- For any questions unrelated to the Sound chapter, such as real-time events, news, or factual information not covered in the Sound chapter, use the search tool to provide the latest and most accurate information.
|
77 |
+
|
78 |
+
3. **Counting Words in a Response**:
|
79 |
+
- If the query involves counting the number of words in a response, use the `calculate_word_count` tool to determine the word count.
|
80 |
+
|
81 |
+
4. **Clarification**:
|
82 |
+
- If the query is unclear or ambiguous, clarify the user's intent before selecting the appropriate tool or providing a response.
|
83 |
+
|
84 |
+
Be concise, accurate, and use the appropriate tool or knowledge based on the query type. Do not confuse the tools or mix the instructions for different query types.
|
85 |
+
"""),
|
86 |
+
MessagesPlaceholder(variable_name="chat_history"),
|
87 |
+
("user", "Form input details: {input}"),
|
88 |
+
MessagesPlaceholder(variable_name="agent_scratchpad"),
|
89 |
+
])
|
90 |
+
|
91 |
+
def get_model_use():
|
92 |
+
return ChatOpenAI(api_key=os.getenv("OPEN_API_KEY"),temperature=0)
|
93 |
+
|
94 |
+
def init_agent():
|
95 |
+
llm = get_model_use()
|
96 |
+
agent = create_openai_functions_agent(llm, tools, prompt)
|
97 |
+
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
98 |
+
message_history = ChatMessageHistory()
|
99 |
+
agent_with_chat_history = RunnableWithMessageHistory(
|
100 |
+
agent_executor,
|
101 |
+
lambda session_id: message_history,
|
102 |
+
input_messages_key="input",
|
103 |
+
history_messages_key="chat_history",
|
104 |
+
)
|
105 |
+
return agent_with_chat_history
|
106 |
+
|
107 |
+
def get_agent_response(agent, user_input, session_id="agentic_trial"):
|
108 |
+
response = agent.invoke(
|
109 |
+
{
|
110 |
+
"input": user_input
|
111 |
+
},
|
112 |
+
config={"configurable": {"session_id": session_id}}
|
113 |
+
)
|
114 |
+
return response['output']
|
utils/document_loader.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from langchain_community.document_loaders import PDFPlumberLoader
|
3 |
+
|
4 |
+
def load_pdf(directory):
|
5 |
+
|
6 |
+
for file in os.listdir(directory):
|
7 |
+
file_path = os.path.join(directory,file)
|
8 |
+
loader = PDFPlumberLoader(file_path)
|
9 |
+
document = loader.load()
|
10 |
+
|
11 |
+
return document
|
12 |
+
|
13 |
+
def create_unique_ids(documents):
|
14 |
+
return [f"{doc.metadata['source']}_page_{doc.metadata['page']}" for doc in documents]
|
utils/embeddings.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from langchain_openai import OpenAIEmbeddings
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
|
7 |
+
def get_embeddings():
|
8 |
+
return OpenAIEmbeddings(model='text-embedding-3-small', api_key=os.getenv("OPEN_API_KEY"))
|
utils/gradio_interface.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from fastapi import FastAPI
|
3 |
+
from fastapi.responses import JSONResponse
|
4 |
+
|
5 |
+
def create_gradio_interface(app: FastAPI, conversational_rag_chain, agent):
|
6 |
+
def qa_function(message, history, system):
|
7 |
+
if system == "RAG":
|
8 |
+
response = conversational_rag_chain.invoke(
|
9 |
+
{"input": message},
|
10 |
+
config={"configurable": {"session_id": "abc123"}}
|
11 |
+
)
|
12 |
+
return response["answer"]
|
13 |
+
elif system == "Agent":
|
14 |
+
response = agent.invoke(
|
15 |
+
{"input": message},
|
16 |
+
config={"configurable": {"session_id": "agent_session"}}
|
17 |
+
)
|
18 |
+
return response['output']
|
19 |
+
|
20 |
+
gr_app = gr.Blocks()
|
21 |
+
|
22 |
+
with gr_app:
|
23 |
+
gr.Markdown("# NCERT Q&A System")
|
24 |
+
gr.Markdown("Ask questions based on the NCERT Sound chapter or use the Agent for broader queries.")
|
25 |
+
|
26 |
+
chatbot = gr.Chatbot()
|
27 |
+
msg = gr.Textbox()
|
28 |
+
clear = gr.Button("Clear")
|
29 |
+
|
30 |
+
system_choice = gr.Radio(["RAG", "Agent"], label="Choose System", value="RAG")
|
31 |
+
|
32 |
+
def user(user_message, history, system):
|
33 |
+
return "", history + [[user_message, None]]
|
34 |
+
|
35 |
+
def bot(history, system):
|
36 |
+
user_message = history[-1][0]
|
37 |
+
bot_message = qa_function(user_message, history, system)
|
38 |
+
history[-1][1] = bot_message
|
39 |
+
return history
|
40 |
+
|
41 |
+
msg.submit(user, [msg, chatbot, system_choice], [msg, chatbot], queue=False).then(
|
42 |
+
bot, [chatbot, system_choice], chatbot
|
43 |
+
)
|
44 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
45 |
+
|
46 |
+
return gr_app
|
utils/rag_chain.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from langchain_openai import ChatOpenAI
|
3 |
+
from langchain.chains import create_retrieval_chain, create_history_aware_retriever
|
4 |
+
from langchain.chains.combine_documents import create_stuff_documents_chain
|
5 |
+
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
6 |
+
from langchain_core.runnables.history import RunnableWithMessageHistory
|
7 |
+
from langchain_community.chat_message_histories import ChatMessageHistory
|
8 |
+
|
9 |
+
def get_model():
|
10 |
+
return ChatOpenAI(api_key=os.getenv("OPEN_API_KEY"))
|
11 |
+
|
12 |
+
def create_contextualize_q_prompt():
|
13 |
+
contextualize_q_system_prompt = (
|
14 |
+
"Given a chat history and the latest user question "
|
15 |
+
"which might reference context in the chat history, "
|
16 |
+
"formulate a standalone question which can be understood "
|
17 |
+
"without the chat history. Do NOT answer the question, "
|
18 |
+
"just reformulate it if needed and otherwise return it as is."
|
19 |
+
)
|
20 |
+
return ChatPromptTemplate.from_messages([
|
21 |
+
("system", contextualize_q_system_prompt),
|
22 |
+
MessagesPlaceholder("chat_history"),
|
23 |
+
("human", "{input}"),
|
24 |
+
])
|
25 |
+
|
26 |
+
def create_qa_prompt():
|
27 |
+
qa_system_prompt = """You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. \
|
28 |
+
The retrieved content belongs to subject textbooks present. You will receive different chunks, each of which belongs to a single page of a textbook. \
|
29 |
+
Using the chunk given, think logically and answer the questions from the user. \
|
30 |
+
If you are not able to identify the relevant information regarding to the user's question in the retrieved chunks, then just return 'No data found'.\
|
31 |
+
Use three sentences maximum and keep the answer concise. \
|
32 |
+
|
33 |
+
{context}"""
|
34 |
+
return ChatPromptTemplate.from_messages([
|
35 |
+
("system", qa_system_prompt),
|
36 |
+
MessagesPlaceholder("chat_history"),
|
37 |
+
("human", "{input}"),
|
38 |
+
])
|
39 |
+
|
40 |
+
def create_rag_chain(model, retriever):
|
41 |
+
contextualize_q_prompt = create_contextualize_q_prompt()
|
42 |
+
qa_prompt = create_qa_prompt()
|
43 |
+
|
44 |
+
history_aware_retriever = create_history_aware_retriever(model, retriever, contextualize_q_prompt)
|
45 |
+
question_answer_chain = create_stuff_documents_chain(model, qa_prompt)
|
46 |
+
return create_retrieval_chain(history_aware_retriever, question_answer_chain)
|
47 |
+
|
48 |
+
def get_conversational_rag_chain(rag_chain):
|
49 |
+
store = {}
|
50 |
+
|
51 |
+
def get_session_history(session_id: str):
|
52 |
+
if session_id not in store:
|
53 |
+
store[session_id] = ChatMessageHistory()
|
54 |
+
return store[session_id]
|
55 |
+
|
56 |
+
return RunnableWithMessageHistory(
|
57 |
+
rag_chain,
|
58 |
+
get_session_history,
|
59 |
+
input_messages_key="input",
|
60 |
+
history_messages_key="chat_history",
|
61 |
+
output_messages_key="answer",
|
62 |
+
)
|
utils/vector_store.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
from langchain_chroma import Chroma
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
|
7 |
+
persist_directory = os.getenv("VECTOR_STORE")
|
8 |
+
|
9 |
+
def create_vector_store(documents, unique_ids, embeddings):
|
10 |
+
"""
|
11 |
+
Creates a new vector store with the given documents, unique IDs, and embeddings.
|
12 |
+
"""
|
13 |
+
vector_store = Chroma(
|
14 |
+
collection_name="NCERT-Chapters",
|
15 |
+
embedding_function=embeddings,
|
16 |
+
persist_directory=persist_directory
|
17 |
+
)
|
18 |
+
vector_store.add_documents(documents=documents, ids=unique_ids)
|
19 |
+
vector_store.persist()
|
20 |
+
return vector_store
|
21 |
+
|
22 |
+
def load_vector_store(embeddings):
|
23 |
+
"""
|
24 |
+
Loads an existing vector store using the embeddings provided.
|
25 |
+
"""
|
26 |
+
return Chroma(
|
27 |
+
collection_name="NCERT-Chapters",
|
28 |
+
persist_directory=persist_directory,
|
29 |
+
embedding_function=embeddings
|
30 |
+
)
|
31 |
+
|
32 |
+
def get_retriever(vector_store, k=5):
|
33 |
+
"""
|
34 |
+
Returns a retriever object to search through the vector store.
|
35 |
+
"""
|
36 |
+
return vector_store.as_retriever(search_kwargs={"k": k})
|