anamjafar6's picture
Update app.py
a000ef8 verified
# --- Imports ---
import streamlit as st
import PyPDF2
import requests
import json
from sentence_transformers import SentenceTransformer
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from datetime import datetime
from groq import Groq
# --- Page config ---
st.set_page_config(
page_title="🚨 First Aid Emergency Assistant",
page_icon="🚨",
layout="wide",
initial_sidebar_state="expanded"
)
# --- Enhanced Professional UI CSS ---
st.markdown("""
<style>
/* Main App Background */
.stApp {
background: linear-gradient(135deg, #0F1419 0%, #1a1d29 50%, #2d3748 100%);
color: #ffffff;
}
/* Header Styling - Made more compact */
.main-header {
text-align: center;
padding: 1.5rem 0;
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, rgba(0, 242, 254, 0.1) 100%);
border-radius: 15px;
margin-bottom: 1rem;
backdrop-filter: blur(20px);
border: 1px solid rgba(79, 172, 254, 0.2);
box-shadow: 0 8px 40px rgba(79, 172, 254, 0.1);
}
.main-header h1 {
font-size: 2rem;
font-weight: 700;
margin: 0;
background: linear-gradient(135deg, #4FACFE 0%, #00F2FE 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.main-header p {
margin: 0.3rem 0 0 0;
font-size: 1rem;
opacity: 0.8;
color: #a0aec0;
}
/* Fixed Height Chat Container */
.chat-container {
background: rgba(26, 32, 46, 0.8);
border-radius: 20px;
padding: 1.5rem;
margin: 1rem 0;
height: 450px;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(79, 172, 254, 0.1);
backdrop-filter: blur(20px);
}
/* Enhanced Scrollbar Styling */
.chat-container::-webkit-scrollbar {
width: 8px;
}
.chat-container::-webkit-scrollbar-track {
background: rgba(160, 174, 192, 0.1);
border-radius: 10px;
}
.chat-container::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #4FACFE 0%, #00F2FE 100%);
border-radius: 10px;
}
.chat-container::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, #00F2FE 0%, #4FACFE 100%);
}
/* User Message */
.user-message {
background: linear-gradient(135deg, #4FACFE 0%, #00F2FE 100%);
color: white;
padding: 12px 16px;
border-radius: 18px 18px 6px 18px;
margin: 12px 0;
max-width: 70%;
margin-left: auto;
box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3);
font-size: 0.95rem;
line-height: 1.4;
word-wrap: break-word;
display: block;
}
/* Bot Message */
.bot-message {
background: rgba(45, 55, 72, 0.95);
color: #f7fafc;
padding: 14px 18px;
border-radius: 18px 18px 18px 6px;
margin: 12px 0;
max-width: 85%;
margin-right: auto;
border: 1px solid rgba(79, 172, 254, 0.2);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
font-size: 0.95rem;
line-height: 1.5;
word-wrap: break-word;
display: block;
}
/* Enhanced readability for bot messages */
.bot-message strong {
color: #63b3ed !important;
font-weight: 600;
}
.bot-message h1, .bot-message h2, .bot-message h3 {
color: #4FACFE !important;
margin: 0.8rem 0 0.4rem 0 !important;
font-weight: 600;
}
.bot-message ul, .bot-message ol {
margin: 0.5rem 0;
padding-left: 1.2rem;
}
.bot-message li {
margin: 0.3rem 0;
color: #e2e8f0;
}
/* Input Container */
.input-container {
background: rgba(26, 32, 46, 0.95);
padding: 1.5rem 2rem;
border-radius: 20px;
margin: 1.5rem 0;
box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(79, 172, 254, 0.2);
backdrop-filter: blur(20px);
}
/* Text Input Styling */
.stTextInput > div > div > input {
background: rgba(45, 55, 72, 0.9) !important;
border: 2px solid rgba(79, 172, 254, 0.3) !important;
border-radius: 25px !important;
padding: 14px 20px !important;
font-size: 16px !important;
color: #f7fafc !important;
transition: all 0.3s ease !important;
height: 50px !important;
}
.stTextInput > div > div > input:focus {
border-color: #4FACFE !important;
box-shadow: 0 0 0 0.3rem rgba(79, 172, 254, 0.3) !important;
background: rgba(45, 55, 72, 1) !important;
}
.stTextInput > div > div > input::placeholder {
color: #cbd5e0 !important;
opacity: 0.8 !important;
}
/* Button Styling */
.stButton > button, .stForm > div > div > button {
background: linear-gradient(135deg, #4FACFE 0%, #00F2FE 100%) !important;
color: white !important;
border: none !important;
border-radius: 25px !important;
padding: 14px 28px !important;
font-weight: 600 !important;
font-size: 16px !important;
transition: all 0.3s ease !important;
box-shadow: 0 4px 20px rgba(79, 172, 254, 0.3) !important;
width: 100% !important;
height: 50px !important;
}
.stButton > button:hover, .stForm > div > div > button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 8px 30px rgba(79, 172, 254, 0.5) !important;
background: linear-gradient(135deg, #00F2FE 0%, #4FACFE 100%) !important;
}
/* Clear Chat Button Styling */
.clear-button {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%) !important;
margin-top: 0.5rem !important;
}
.clear-button:hover {
background: linear-gradient(135deg, #ee5a24 0%, #ff6b6b 100%) !important;
}
/* Sidebar Styling */
.sidebar-content {
background: rgba(45, 55, 72, 0.8);
padding: 1rem;
border-radius: 15px;
margin: 0.5rem 0;
border: 1px solid rgba(79, 172, 254, 0.2);
color: #e2e8f0;
}
/* Feedback Section */
.feedback-container {
background: rgba(34, 197, 94, 0.1);
border: 1px solid rgba(34, 197, 94, 0.3);
border-radius: 15px;
padding: 1rem;
margin: 1rem 0;
}
.feedback-container h4 {
color: #22c55e !important;
margin: 0 0 0.5rem 0;
}
/* Hide Streamlit default elements */
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
header {visibility: hidden;}
/* Mobile responsiveness */
@media (max-width: 768px) {
.main-header h1 {
font-size: 1.5rem;
}
.user-message, .bot-message {
max-width: 90%;
font-size: 0.9rem;
}
.input-container {
padding: 1rem;
}
.chat-container {
height: 350px;
padding: 1rem;
}
}
</style>
""", unsafe_allow_html=True)
# --- GROQ API setup ---
@st.cache_resource
def setup_groq():
try:
return st.secrets["GROQ_API_KEY"]
except:
st.error("❌ Missing GROQ_API_KEY in `.streamlit/secrets.toml`")
st.stop()
client = Groq(api_key=setup_groq())
# --- PDF Processing ---
@st.cache_resource
def load_pdf():
try:
with open("First-Aid.pdf", "rb") as f:
reader = PyPDF2.PdfReader(f)
text = "".join(page.extract_text() + "\n" for page in reader.pages)
return text
except:
st.error("❌ Failed to load 'First-Aid.pdf'. Upload it to the app root.")
st.stop()
# --- Knowledge Base Setup ---
@st.cache_resource
def setup_knowledge_base():
text = load_pdf()
chunks, current_chunk = [], ""
for sentence in text.split('\n'):
if len(current_chunk + sentence) < 1000:
current_chunk += sentence + "\n"
else:
if current_chunk.strip(): chunks.append(current_chunk.strip())
current_chunk = sentence + "\n"
if current_chunk.strip(): chunks.append(current_chunk.strip())
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(chunks)
return chunks, embeddings, model
# --- Context Matching ---
def find_relevant_context(query, chunks, embeddings, model, top_k=3):
query_embedding = model.encode([query])
similarities = cosine_similarity(query_embedding, embeddings)[0]
top_indices = np.argsort(similarities)[-top_k:][::-1]
return "\n\n".join([chunks[i] for i in top_indices])
# --- Query GROQ ---
def query_groq(prompt, api_key):
url = "https://api.groq.com/openai/v1/chat/completions"
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
data = {
"model": "llama3-70b-8192",
"messages": [
{
"role": "system",
"content": """You are a First Aid Emergency Assistant. Answer only first aid-related queries. Be clear, concise, and step-by-step. For serious emergencies, advise calling 911. If asked off-topic, respond with: "🚨 I'm specialized in First Aid emergencies only!"."""
},
{"role": "user", "content": prompt}
],
"temperature": 0.3,
"max_tokens": 1000
}
try:
r = requests.post(url, headers=headers, json=data, timeout=30)
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"]
except Exception as e:
return f"❌ GROQ API Error: {str(e)}"
# --- Session State ---
if "messages" not in st.session_state:
st.session_state.messages = [{
"role": "assistant",
"content": "🚨 **Hello! I'm your First Aid Emergency Assistant.**\n\nAsk me about CPR, bleeding, choking, burns, or any first aid emergency. I'm here to help you with step-by-step guidance!"
}]
if "knowledge_base" not in st.session_state:
with st.spinner("πŸ”„ Loading knowledge base..."):
chunks, embeddings, model = setup_knowledge_base()
st.session_state.knowledge_base = {
"chunks": chunks, "embeddings": embeddings, "model": model
}
if "feedback_shown" not in st.session_state:
st.session_state.feedback_shown = False
if "response_count" not in st.session_state:
st.session_state.response_count = 0
# --- Main App Layout ---
# Compact Header
st.markdown('''
<div class="main-header">
<h1>🚨 First Aid Emergency Assistant</h1>
<p>Your AI-powered emergency response guide</p>
</div>
''', unsafe_allow_html=True)
# Main content area
col1, col2 = st.columns([3, 1])
with col1:
# Create a container with fixed height for chat messages
chat_container = st.container()
with chat_container:
# Create the scrollable chat area using st.container with height
st.markdown('<div class="chat-container">', unsafe_allow_html=True)
# Display chat messages
for i, msg in enumerate(st.session_state.messages):
if msg["role"] == "user":
st.markdown(f'<div class="user-message">{msg["content"]}</div>', unsafe_allow_html=True)
else:
st.markdown(f'<div class="bot-message"><strong>πŸ€– First Aid Assistant</strong><br>{msg["content"]}</div>', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
# Show feedback request after first response
if st.session_state.response_count >= 1 and not st.session_state.feedback_shown:
st.markdown('''
<div class="feedback-container">
<h4>πŸ’¬ How was this assistance?</h4>
<p style="margin: 0; color: #a0aec0;">Your feedback helps us improve emergency response guidance.</p>
</div>
''', unsafe_allow_html=True)
feedback_col1, feedback_col2, feedback_col3 = st.columns(3)
with feedback_col1:
if st.button("πŸ‘ Helpful"):
st.success("Thank you for your feedback!")
st.session_state.feedback_shown = True
st.rerun()
with feedback_col2:
if st.button("πŸ‘Ž Not helpful"):
st.info("Thanks for letting us know. We'll work to improve!")
st.session_state.feedback_shown = True
st.rerun()
with feedback_col3:
if st.button("⏭️ Skip"):
st.session_state.feedback_shown = True
st.rerun()
# Enhanced Input Container with better spacing
st.markdown('<div class="input-container">', unsafe_allow_html=True)
# Form for Enter key support and auto-clear
with st.form(key="chat_form", clear_on_submit=True):
input_col1, input_col2 = st.columns([4, 1])
with input_col1:
user_input = st.text_input(
"",
placeholder="What do to in case of heart pain?",
key="user_input_form",
label_visibility="collapsed"
)
with input_col2:
send = st.form_submit_button("Send πŸš€")
# Clear Chat Button
if st.button("πŸ—‘οΈ Clear Chat", key="clear_chat", help="Clear all messages and start fresh"):
st.session_state.messages = [{
"role": "assistant",
"content": "🚨 **Hello! I'm your First Aid Emergency Assistant.**\n\nAsk me about CPR, bleeding, choking, burns, or any first aid emergency. I'm here to help you with step-by-step guidance!"
}]
st.session_state.feedback_shown = False
st.session_state.response_count = 0
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
# Sidebar with medical disclaimer and info
with col2:
st.markdown("### 🩺 Medical Disclaimer")
with st.expander("⚠️ Important - Click to read", expanded=False):
st.markdown("""
**MEDICAL DISCLAIMER:**
This tool provides general first aid guidance only.
🚨 **In real emergencies:**
- Call emergency services immediately
- This is NOT a substitute for professional medical care
- Always seek professional help for serious injuries
Use this tool for educational purposes and basic guidance only.
""")
st.markdown("### 🎯 What I Can Help With")
st.markdown('''
<div class="sidebar-content">
β€’ <strong>CPR</strong> and rescue breathing<br>
β€’ <strong>Wounds</strong> and bleeding control<br>
β€’ <strong>Burns</strong> and scalds<br>
β€’ <strong>Fractures</strong> and sprains<br>
β€’ <strong>Choking</strong> procedures<br>
β€’ <strong>Poisoning</strong> emergencies<br>
β€’ <strong>Heart attack</strong> signs<br>
β€’ <strong>Snake bites</strong> and stings<br>
β€’ And much more!
</div>
''', unsafe_allow_html=True)
# Process Input (works with both Enter key and button click)
if send and user_input and user_input.strip():
# Add user message
st.session_state.messages.append({"role": "user", "content": user_input})
# Get AI response
try:
kb = st.session_state.knowledge_base
context = find_relevant_context(user_input, kb["chunks"], kb["embeddings"], kb["model"])
full_prompt = f"""
Based on this first aid manual content, answer the question: {user_input}
Context:
{context}
Please provide clear, step-by-step guidance. Use bullet points or numbered lists when appropriate.
"""
response = query_groq(full_prompt, setup_groq())
# Add emergency warning for serious cases
if any(x in user_input.lower() for x in ["heart attack", "stroke", "not breathing", "unconscious", "severe bleeding", "choking"]):
response = f"🚨 **CALL EMERGENCY SERVICES IMMEDIATELY!**\n\n{response}"
# Add bot response
st.session_state.messages.append({"role": "assistant", "content": response})
st.session_state.response_count += 1
except Exception as e:
st.session_state.messages.append({
"role": "assistant",
"content": f"❌ Sorry, I encountered an error: {str(e)}. Please try again or rephrase your question."
})
# Rerun to show updated chat
st.rerun()
# Footer
st.markdown("---")
st.markdown('''
<div style="text-align: center; opacity: 0.6; padding: 0.5rem; font-size: 0.9rem;">
πŸ€– First Aid Emergency Assistant | Always consult medical professionals for serious emergencies
</div>
''', unsafe_allow_html=True)