Chatbot / app.py
Ommodi07's picture
Update app.py
ce79822 verified
from flask import Flask, request, jsonify, render_template
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_google_genai import ChatGoogleGenerativeAI
import os
import json
app = Flask(__name__)
# Create templates directory if it doesn't exist
os.makedirs('templates', exist_ok=True)
# Create HTML template for the chatbot interface
with open('templates/index.html', 'w') as f:
f.write('''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cirrhosis Toolkit Chatbot</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.header {
background-color: #2c3e50;
color: white;
text-align: center;
padding: 1rem;
}
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
max-width: 800px;
margin: 0 auto;
padding: 1rem;
width: 100%;
box-sizing: border-box;
}
.messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 1rem;
}
.message {
margin-bottom: 1rem;
padding: 0.8rem;
border-radius: 8px;
max-width: 80%;
}
.user-message {
background-color: #3498db;
color: white;
align-self: flex-end;
margin-left: auto;
}
.bot-message {
background-color: #ecf0f1;
color: #333;
align-self: flex-start;
}
.input-area {
display: flex;
gap: 0.5rem;
}
#user-input {
flex: 1;
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
button {
padding: 0.8rem 1.5rem;
background-color: #2c3e50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
button:hover {
background-color: #1a252f;
}
.loading {
display: none;
text-align: center;
margin: 1rem 0;
}
.loading-dots {
display: inline-block;
}
.loading-dots::after {
content: '.';
animation: dots 1.5s steps(5, end) infinite;
}
@keyframes dots {
0%, 20% { content: '.'; }
40% { content: '..'; }
60% { content: '...'; }
80%, 100% { content: ''; }
}
</style>
</head>
<body>
<div class="header">
<h1>Cirrhosis Toolkit Assistant</h1>
</div>
<div class="chat-container">
<div class="messages" id="chat-messages">
<div class="message bot-message">
Hello! I'm your Cirrhosis Toolkit assistant. Ask me any questions about cirrhosis management and treatment.
</div>
</div>
<div class="loading" id="loading">
Thinking<span class="loading-dots"></span>
</div>
<div class="input-area">
<input type="text" id="user-input" placeholder="Ask a question..." autocomplete="off">
<button id="send-btn">Send</button>
</div>
</div>
<script>
const chatMessages = document.getElementById('chat-messages');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const loadingIndicator = document.getElementById('loading');
// Function to add a message to the chat
function addMessage(message, isUser = false) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${isUser ? 'user-message' : 'bot-message'}`;
messageDiv.textContent = message;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Function to send user query to backend
async function sendQuery(query) {
loadingIndicator.style.display = 'block';
try {
const response = await fetch('/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ query }),
});
const data = await response.json();
if (response.ok) {
addMessage(data.response);
} else {
addMessage(`Error: ${data.error || 'Something went wrong'}`);
}
} catch (error) {
addMessage(`Error: ${error.message}`);
} finally {
loadingIndicator.style.display = 'none';
}
}
// Event listener for send button
sendBtn.addEventListener('click', () => {
const query = userInput.value.trim();
if (query) {
addMessage(query, true);
userInput.value = '';
sendQuery(query);
}
});
// Event listener for Enter key
userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const query = userInput.value.trim();
if (query) {
addMessage(query, true);
userInput.value = '';
sendQuery(query);
}
}
});
// Focus input on page load
userInput.focus();
</script>
</body>
</html>
''')
# Set your Google API key
os.environ["GOOGLE_API_KEY"] = "AIzaSyCOsco3wW-yHA074FTp-Mbz8NgUptGUY_8" # Replace with your actual API key
# Use a lightweight embedding model to reduce memory usage
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
# Google Gemini LLM setup
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash-lite", temperature=0.7)
# FAISS index path (stored on disk)
FAISS_INDEX_PATH = "faiss_index"
# PDF path
PDF_PATH = "CirrhosisToolkit.pdf"
def process_pdf(pdf_path):
"""Processes the PDF and stores FAISS index on disk to save memory."""
loader = PyPDFLoader(pdf_path)
documents = loader.lazy_load() # Lazy load to avoid high memory usage
# Reduce chunk size to save memory
text_splitter = RecursiveCharacterTextSplitter(chunk_size=150, chunk_overlap=30)
texts = text_splitter.split_documents(documents)
# Store FAISS on disk to avoid keeping everything in RAM
vectordb = FAISS.from_documents(texts, embeddings)
vectordb.save_local(FAISS_INDEX_PATH)
# Load FAISS index if available, else process the PDF
if os.path.exists(FAISS_INDEX_PATH):
vectordb = FAISS.load_local(FAISS_INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
else:
process_pdf(PDF_PATH)
vectordb = FAISS.load_local(FAISS_INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
@app.route('/')
def index():
"""Renders the chatbot frontend."""
return render_template('index.html')
@app.route('/query', methods=['POST'])
def query_pdf():
"""Handles user queries and retrieves relevant document context."""
if vectordb is None:
return jsonify({'error': 'No PDF processed yet'}), 400
data = request.get_json()
query_text = data.get("query", "")
if not query_text:
return jsonify({'error': 'No query provided'}), 400
# Perform similarity search with reduced results (k=2) to save memory
results = vectordb.similarity_search(query_text, k=2)
context = "\n".join([res.page_content for res in results])
# Generate response with Gemini
prompt = f"Using the following document context, answer the query concisely.\n\nContext:\n{context}\n\nQuery: {query_text}"
gemini_response = llm.invoke(prompt)
return jsonify({'response': gemini_response.content})
if __name__ == '__main__':
app.run(debug=True)