| 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__) |
|
|
| |
| os.makedirs('templates', exist_ok=True) |
|
|
| |
| 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> |
| ''') |
|
|
| |
| os.environ["GOOGLE_API_KEY"] = "AIzaSyCOsco3wW-yHA074FTp-Mbz8NgUptGUY_8" |
|
|
| |
| embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") |
|
|
| |
| llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash-lite", temperature=0.7) |
|
|
| |
| FAISS_INDEX_PATH = "faiss_index" |
|
|
| |
| 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() |
| |
| |
| text_splitter = RecursiveCharacterTextSplitter(chunk_size=150, chunk_overlap=30) |
| texts = text_splitter.split_documents(documents) |
| |
| |
| vectordb = FAISS.from_documents(texts, embeddings) |
| vectordb.save_local(FAISS_INDEX_PATH) |
|
|
| |
| 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 |
| |
| |
| results = vectordb.similarity_search(query_text, k=2) |
| context = "\n".join([res.page_content for res in results]) |
| |
| |
| 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) |