GFMM / app.py
neuroEngage's picture
Update app.py
c833ddd verified
Raw
History Blame Contribute Delete
7.57 kB
# app.py — Heart‑Disease Predictor + CardioConsult‑Bot (Gemini 1.5 Flash)
# ----------------------------------------------------------------------------------
import os, streamlit as st, pandas as pd, joblib
from sklearn.impute import SimpleImputer
from google import genai
from google.genai import types
from google.api_core.exceptions import GoogleAPIError
# ----------------------------------------------------------------------------------
# 1. Gemini client (google‑genai)
# ----------------------------------------------------------------------------------
API_KEY = "AIzaSyBjyI-6GuU9bWAHBgO3_GbCTuBRr2SBczo"
if not API_KEY:
st.stop("❌ GEMINI_API_KEY not found — set env var or .streamlit/secrets.toml")
client = genai.Client(api_key=API_KEY)
MODEL = "gemini-1.5-flash-8b" # change to gemini-pro if you prefer
# ----------------------------------------------------------------------------------
# 2. Load ML model & scaler
# ----------------------------------------------------------------------------------
best_model = joblib.load("best_model.pkl")
scaler = joblib.load("scaler.pkl")
# ----------------------------------------------------------------------------------
# 3. Lookup tables
# ----------------------------------------------------------------------------------
sex_map = {"Male": 1, "Female": 0}
cp_map = {"atypical angina": 0, "asymptomatic": 1, "non-anginal": 2, "typical angina": 3}
fbs_map = {"TRUE": 1, "FALSE": 0}
rest_map = {"left ventricular hypertrophy": 0, "normal": 1, "ST-T wave abnormality": 2}
exang_map = {"TRUE": 1, "FALSE": 0}
slope_map = {"downsloping": 0, "flat": 1, "upsloping": 2}
thal_map = {"fixed defect": 0, "normal": 1, "reversable defect": 2}
pred_map = {0:"No Heart Disease",1:"Mild Heart Disease",
2:"Moderate Heart Disease",3:"Severe Heart Disease",4:"Critical Heart Disease"}
# ----------------------------------------------------------------------------------
# 4. Streamlit UI
# ----------------------------------------------------------------------------------
st.set_page_config(page_title="Heart‑Disease Predictor + CardioConsult‑Bot", page_icon="🫀")
st.title("🫀 Heart‑Disease Risk Predictor  +  CardioConsult‑Bot")
st.caption("Machine‑learning screening + live cardiovascular chat • *Educational only — not medical advice*")
# ---------------------- Prediction form ------------------------------------------
with st.form("prediction"):
age = st.number_input("Age", 1, 120, 50)
trestbps = st.number_input("Resting Blood Pressure (mmHg)", 50, 250, 130)
chol = st.number_input("Serum Cholesterol (mg/dL)", 100, 600, 200)
thalch = st.number_input("Max Heart Rate Achieved (bpm)", 50, 250, 150)
oldpeak = st.number_input("ST Depression", 0.0, 10.0, 1.0, format="%.1f")
ca = st.number_input("Number of Major Vessels (0–4)", 0, 4, 0)
sex = st.selectbox("Sex", list(sex_map))
cp = st.selectbox("Chest‑Pain Type", list(cp_map))
fbs = st.selectbox("Fasting Blood Sugar >120 mg/dL", list(fbs_map))
restecg = st.selectbox("Resting ECG", list(rest_map))
exang = st.selectbox("Exercise‑Induced Angina", list(exang_map))
slope = st.selectbox("Slope of Peak Exercise ST", list(slope_map))
thal = st.selectbox("Thalassemia", list(thal_map))
submitted = st.form_submit_button("Predict")
# ---------------------- Make prediction ------------------------------------------
if submitted:
X = pd.DataFrame({
"age":[age],"sex":[sex_map[sex]],"cp":[cp_map[cp]],"trestbps":[trestbps],
"chol":[chol],"fbs":[fbs_map[fbs]],"restecg":[rest_map[restecg]],
"thalch":[thalch],"exang":[exang_map[exang]],"oldpeak":[oldpeak],
"slope":[slope_map[slope]],"ca":[ca],"thal":[thal_map[thal]]
})
if X.shape[1] != scaler.mean_.shape[0]:
X = X.drop(columns=["thal"]) # scaler trained on 13 features
X_scaled = scaler.transform(SimpleImputer(strategy="median").fit_transform(X))
y_pred = int(best_model.predict(X_scaled)[0])
pred_txt = pred_map.get(y_pred, "Unknown")
st.subheader("🔎 Prediction Result")
st.success(f"**Predicted Heart‑Disease Level:** {pred_txt}")
st.session_state.prediction_ctx = dict(X.iloc[0], model_prediction=pred_txt)
# ----------------------------------------------------------------------------------
# 5. Chat memory initialisation
# ----------------------------------------------------------------------------------
if "messages" not in st.session_state:
st.session_state.messages = [
("assistant",
"👋 Hi — I'm **CardioConsult‑Bot**. Ask me about your heart‑disease screening result or cardiovascular health. "
"_I am not a substitute for a doctor; for urgent or non‑cardiac issues, seek professional care._")
]
# Display last messages
for role, msg in st.session_state.messages[-8:]:
st.chat_message(role).markdown(msg)
# ----------------------------------------------------------------------------------
# 6. Chat input -> Gemini stream
# ----------------------------------------------------------------------------------
# ----------------------------------------------------------------------------------
# 6. Chat input -> Gemini (show full response once)
# ----------------------------------------------------------------------------------
if prompt := st.chat_input("Ask about your cardiovascular health…"):
# 1️⃣ Save user message and show chat history
st.session_state.messages.append(("user", prompt))
# Show previous messages (chat history)
if "messages" in st.session_state:
for message in st.session_state.messages:
if message[0] == "user":
st.chat_message("user").markdown(message[1])
else:
st.chat_message("assistant").markdown(message[1])
# 2️⃣ Build single user prompt (instructions + context + question)
instructions = (
"You are CardioConsult‑Bot, an educational assistant who discusses cardiovascular "
"screening results, risk factors, and heart‑healthy lifestyle. "
"Do **not** prescribe medications or diagnose conditions outside cardiology. "
"Urge professional medical advice for emergencies.\n\n"
)
context_txt = ""
if "prediction_ctx" in st.session_state:
ctx = st.session_state.prediction_ctx
context_txt = "\n".join(f"{k}: {v}" for k, v in ctx.items()) + "\n\n"
req_content = [
types.Content(
role="user",
parts=[types.Part(text=instructions + context_txt + prompt)]
)
]
# Debugging output for `req_content`
print("Request content:", req_content)
gen_cfg = types.GenerateContentConfig(
thinking_config=types.ThinkingConfig(thinking_budget=0),
response_mime_type="text/plain",
)
# 3️⃣ Call Gemini and buffer chunks
full_reply = ""
try:
with st.spinner("Consulting Gemini…"):
for chunk in client.models.generate_content_stream(
model=MODEL, contents=req_content, config=gen_cfg
):
if chunk.text:
full_reply += chunk.text
except (GoogleAPIError, Exception) as e:
full_reply = f"⚠️ Service error: {getattr(e,'message',e)}"
# 4️⃣ Display the complete answer exactly once
st.chat_message("assistant").markdown(full_reply)
st.session_state.messages.append(("assistant", full_reply))