vBot-1.5 / gradio_app.py
iajitpanday's picture
Create gradio_app.py
f18e345 verified
"""
Gradio interface for the RAG-based response system.
This will be deployed to your Hugging Face Space.
"""
import gradio as gr
import os
import tempfile
import json
from pathlib import Path
import torch
from transformers import AutoTokenizer, AutoModel, pipeline
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Define paths
DOCUMENTS_DIR = Path("documents")
DOCUMENTS_DIR.mkdir(exist_ok=True)
VECTOR_DB_PATH = Path("vector_db")
# Initialize models
model_name = "sentence-transformers/all-MiniLM-L6-v2"
embeddings = HuggingFaceEmbeddings(model_name=model_name)
# Initialize vector store
if VECTOR_DB_PATH.exists():
try:
vector_db = FAISS.load_local(str(VECTOR_DB_PATH), embeddings)
print("Loaded existing vector database.")
except Exception as e:
print(f"Error loading vector database: {e}")
vector_db = None
else:
vector_db = None
# Define possible intents
POSSIBLE_INTENTS = [
"product_inquiry",
"technical_support",
"billing_question",
"general_information",
"appointment_scheduling",
"complaint",
"other"
]
# Default responses for when RAG fails or no documents are available
DEFAULT_RESPONSES = {
"product_inquiry": "Thank you for your interest in our products. I'll gather the information and have someone contact you with more details.",
"technical_support": "I understand you're experiencing technical issues. Let me find the right person to help you resolve this.",
"billing_question": "Thank you for your billing inquiry. I'll connect you with our billing department for assistance.",
"general_information": "Thank you for reaching out. I'll make sure you get the information you need.",
"appointment_scheduling": "I'd be happy to help schedule an appointment for you. Let me find the next available slot.",
"complaint": "I'm sorry to hear about your experience. Your feedback is important to us, and we'll address this promptly.",
"other": "Thank you for your call. I'll make sure your message gets to the right person."
}
def load_pdf(file):
"""Load a PDF document into the vector store"""
global vector_db
try:
# Save the uploaded file temporarily
temp_dir = tempfile.mkdtemp()
temp_path = os.path.join(temp_dir, file.name)
with open(temp_path, "wb") as f:
f.write(file.read())
# Save a copy to the documents directory
target_path = os.path.join(DOCUMENTS_DIR, file.name)
with open(target_path, "wb") as f:
with open(temp_path, "rb") as src:
f.write(src.read())
# Load and process the PDF
loader = PyPDFLoader(temp_path)
documents = loader.load()
# Split the documents
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(documents)
# Update or create vector store
if vector_db is None:
vector_db = FAISS.from_documents(chunks, embeddings)
vector_db.save_local(str(VECTOR_DB_PATH))
else:
vector_db.add_documents(chunks)
vector_db.save_local(str(VECTOR_DB_PATH))
return f"Successfully added {file.name} to the knowledge base with {len(chunks)} chunks."
except Exception as e:
return f"Error processing PDF: {str(e)}"
def load_website(url):
"""Load a website into the vector store"""
global vector_db
try:
# Load content from website
loader = WebBaseLoader(url)
documents = loader.load()
# Save the URL reference
with open(os.path.join(DOCUMENTS_DIR, "websites.txt"), "a") as f:
f.write(f"{url}\n")
# Split the documents
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = text_splitter.split_documents(documents)
# Update or create vector store
if vector_db is None:
vector_db = FAISS.from_documents(chunks, embeddings)
vector_db.save_local(str(VECTOR_DB_PATH))
else:
vector_db.add_documents(chunks)
vector_db.save_local(str(VECTOR_DB_PATH))
return f"Successfully added {url} to the knowledge base with {len(chunks)} chunks."
except Exception as e:
return f"Error processing website: {str(e)}"
def generate_response(query, intent=None):
"""Generate a response based on the query and intent"""
global vector_db
# If no intent provided, use a default
if not intent or intent not in POSSIBLE_INTENTS:
intent = "general_information"
# If no vector database, return default response
if vector_db is None:
return DEFAULT_RESPONSES.get(intent, DEFAULT_RESPONSES["other"])
try:
# Query the vector database
retrieved_docs = vector_db.similarity_search(query, k=3)
if not retrieved_docs:
return DEFAULT_RESPONSES.get(intent, DEFAULT_RESPONSES["other"])
# Combine retrieved document chunks
context = "\n\n".join([doc.page_content for doc in retrieved_docs])
# Create prompt for the model
prompt = f"""
Based on the following context information and the user's query,
provide a helpful, professional, and concise response.
Context:
{context}
User query: {query}
Intent: {intent}
Response:
"""
# Generate response using a pre-trained model
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-base")
model = AutoModel.from_pretrained("google/flan-t5-base")
inputs = tokenizer(prompt, return_tensors="pt", max_length=512, truncation=True)
outputs = model.generate(
inputs["input_ids"],
max_length=200,
num_beams=4,
early_stopping=True
)
response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
# If model output is empty or too short, use retrieved text with a prefix
if len(response_text) < 20:
response_text = f"Based on the information I have: {context[:200]}..."
return response_text
except Exception as e:
print(f"Error generating response: {e}")
return DEFAULT_RESPONSES.get(intent, DEFAULT_RESPONSES["other"])
def list_documents():
"""List all documents in the knowledge base"""
files = list(DOCUMENTS_DIR.glob("*.pdf"))
# Add websites if available
website_file = DOCUMENTS_DIR / "websites.txt"
websites = []
if website_file.exists():
with open(website_file, "r") as f:
websites = [line.strip() for line in f if line.strip()]
return {
"PDFs": [f.name for f in files],
"Websites": websites
}
# Create the Gradio interface
with gr.Blocks(title="Call Assistant RAG System") as demo:
gr.Markdown("# Call Assistant RAG System")
gr.Markdown("Add documents and websites to the knowledge base, and test the response generation.")
with gr.Tab("Add Knowledge"):
with gr.Row():
with gr.Column():
pdf_input = gr.File(label="Upload PDF Document")
pdf_button = gr.Button("Add PDF to Knowledge Base")
pdf_output = gr.Textbox(label="PDF Upload Status")
pdf_button.click(
load_pdf,
inputs=[pdf_input],
outputs=[pdf_output]
)
with gr.Column():
url_input = gr.Textbox(label="Website URL")
url_button = gr.Button("Add Website to Knowledge Base")
url_output = gr.Textbox(label="Website Status")
url_button.click(
load_website,
inputs=[url_input],
outputs=[url_output]
)
with gr.Tab("Knowledge Base"):
list_button = gr.Button("List Documents in Knowledge Base")
knowledge_output = gr.JSON(label="Documents")
list_button.click(
list_documents,
inputs=[],
outputs=[knowledge_output]
)
with gr.Tab("Test Response Generation"):
with gr.Row():
query_input = gr.Textbox(label="Query / Transcription")
intent_input = gr.Dropdown(
choices=POSSIBLE_INTENTS,
label="Intent",
value="general_information"
)
test_button = gr.Button("Generate Response")
response_output = gr.Textbox(label="Generated Response")
test_button.click(
generate_response,
inputs=[query_input, intent_input],
outputs=[response_output]
)
with gr.Tab("API"):
gr.Markdown("""
## API Documentation
This Gradio app exposes an API endpoint that can be used by your Twilio application.
### Endpoint: `/api/predict`
**Method:** POST
**Input:**
```json
{
"data": [
"user's transcribed query",
"detected intent"
]
}
```
**Output:**
```json
{
"data": [
"generated response"
],
"duration": 1.2345
}
```
**Example Python code to call the API:**
```python
import requests
response = requests.post(
"https://huggingface.co/spaces/iajitpanday/vBot-1.5/api/predict",
json={"data": ["How do I reset my password?", "technical_support"]}
)
result = response.json()
generated_response = result["data"][0]
```
""")
# Define API function for Gradio Spaces
def api_response(query, intent=None):
"""API function for Gradio Spaces"""
response = generate_response(query, intent)
return [response]
# Define the API
demo.queue()
demo.launch()