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('''
Cirrhosis Toolkit Chatbot
Cirrhosis Toolkit Assistant
Hello! I'm your Cirrhosis Toolkit assistant. Ask me any questions about cirrhosis management and treatment.
Thinking
''')
# 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)