Spaces:
Runtime error
Runtime error
A newer version of the Gradio SDK is available: 6.22.0
Medical Predictor Chatbot - Developer Guides
π All Documentation Files
For Project Overview & Planning
- PROJECT_ROADMAP.md β Start here for complete timeline
- ANALYSIS.md β Current status & what's missing
- GUIDES.md β This file (detailed guides for each phase)
π― Phase-by-Phase Implementation Guide
PHASE 1: Environment & Infrastructure β (85% Complete)
What You've Done
- β Created basic project structure
- β Set up Gradio + Groq dependencies
- β Created basic Groq API integration
- β Implemented state management
What You Need to Do (15% Remaining)
1.1 Create .env File
cd /media/zunayed/HDD_code/chatbot\ with\ llm/medical-predictor-chatbot
echo "GROQ_API_KEY=gsk_your_api_key_here" > .env
Get your API key:
- Visit https://console.groq.com/keys
- Sign up (free)
- Generate API key
- Paste into
.env
1.2 Create .gitignore
cat > .gitignore << 'EOF'
.env
*.pkl
__pycache__/
venv/
.DS_Store
*.pyc
.pytest_cache/
.idea/
*.egg-info/
dist/
build/
app_state.json
EOF
1.3 Create Folder Structure
mkdir -p app/services app/utils models
touch app/__init__.py app/services/__init__.py app/utils/__init__.py
1.4 Verify Installation
pip install -r requirements.txt
python test_extractor.py # Should print JSON with features
β Phase 1 Complete When:
.envexists with valid GROQ_API_KEY.gitignorecreated- Folder structure matches
test_extractor.pyruns without errors
PHASE 2: Configuration & Schemas π² (0% Complete)
What to Create
2.1 Create app/config.py
import os
from pathlib import Path
from dotenv import load_dotenv
# Load environment
load_dotenv()
# Paths
BASE_DIR = Path(__file__).parent.parent
MODEL_DIR = BASE_DIR / "models"
MODEL_PATH = MODEL_DIR / "GradientBoosting_model.pkl"
# Model configuration
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
GROQ_MODEL = "llama-3.1-8b-instant"
GROQ_TEMPERATURE = 0
# Medical features (all 16)
DEFAULT_MODEL_FEATURES = [
"LengthOfStay", "Smoking", "Family History", "HbA1c", "Glucose",
"Age", "Diet Score", "Alcohol", "Physical Activity", "Blood Pressure",
"BMI", "Cholesterol", "Sleep Hours", "Stress Level",
"Triglycerides", "Oxygen Saturation"
]
# Feature validation ranges (min, max, expected type)
FEATURE_RANGES = {
"Age": (0, 150, float),
"Glucose": (70, 400, float),
"HbA1c": (3, 15, float),
"BMI": (10, 60, float),
"Cholesterol": (100, 400, float),
"Triglycerides": (20, 500, float),
"Blood Pressure": (60, 200, float), # Systolic only simplified
"Physical Activity": (0, 24, float), # hours per week
"Sleep Hours": (0, 24, float),
"Stress Level": (1, 10, float),
"Diet Score": (1, 10, float),
"Smoking": (0, 1, int), # 0=No, 1=Yes
"Alcohol": (0, 1, int), # 0=No, 1=Yes
"Family History": (0, 1, int), # 0=No, 1=Yes
"LengthOfStay": (0, 365, int), # days
"Oxygen Saturation": (80, 100, float), # percentage
}
# API configuration
GROQ_TIMEOUT = 30
MAX_RETRIES = 3
# App configuration
DEBUG_MODE = True
CONVERSATION_MAX_TURNS = 20
2.2 Create app/schemas.py
from typing import Optional
from pydantic import BaseModel, Field, validator
class MedicalFeatures(BaseModel):
"""All 16 required medical features"""
Age: Optional[float] = None
Glucose: Optional[float] = None
HbA1c: Optional[float] = None
BMI: Optional[float] = None
Cholesterol: Optional[float] = None
Triglycerides: Optional[float] = None
BloodPressure: Optional[float] = None
PhysicalActivity: Optional[float] = None
SleepHours: Optional[float] = None
StressLevel: Optional[float] = None
DietScore: Optional[float] = None
Smoking: Optional[int] = None
Alcohol: Optional[int] = None
FamilyHistory: Optional[int] = None
LengthOfStay: Optional[int] = None
OxygenSaturation: Optional[float] = None
@validator("Age")
def validate_age(cls, v):
if v is not None and not (0 <= v <= 150):
raise ValueError("Age must be between 0 and 150")
return v
# Add similar validators for other fields...
class Config:
use_enum_values = True
arbitrary_types_allowed = True
class PredictionRequest(BaseModel):
"""Request for prediction"""
features: MedicalFeatures
class PredictionResponse(BaseModel):
"""Prediction response"""
prediction: int # 0 or 1 (or disease class)
probability: float # 0.0 to 1.0
risk_level: str # "Low", "Medium", "High"
explanation: str
class ExtractionResponse(BaseModel):
"""LLM extraction response"""
extracted_features: MedicalFeatures
confidence: float
β Phase 2 Complete When:
- Both files exist and have no import errors
- Run:
python -c "from app.config import *; from app.schemas import *" - No errors appear
Time Estimate: 15-20 minutes
PHASE 3: ML Model Preparation π² (0% Complete)
Option A: Create a Synthetic Model (for testing)
Create create_model.py:
import joblib
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from pathlib import Path
# Create synthetic data
np.random.seed(42)
X = np.random.randn(100, 16) # 16 features
y = np.random.randint(0, 2, 100) # Binary classification
# Train model
model = GradientBoostingClassifier(n_estimators=50, random_state=42)
model.fit(X, y)
# Save
Path("models").mkdir(exist_ok=True)
joblib.dump(model, "models/GradientBoosting_model.pkl")
print("β
Model saved to models/GradientBoosting_model.pkl")
print(f"Model expects {model.n_features_in_} features")
Run it:
python create_model.py
Option B: Use Pre-trained Model
If you have an existing model file:
cp /path/to/your/GradientBoosting_model.pkl models/
β Phase 3 Complete When:
models/GradientBoosting_model.pklexists- Test with:
python -c "import joblib; m = joblib.load('models/GradientBoosting_model.pkl'); print(f'Model loaded! Features: {m.n_features_in_}')"
Time Estimate: 20-40 minutes
PHASE 4: Service Layer Implementation π² (50% Complete)
4.1 Implement app/services/feature_builder.py
from typing import List, Dict, Any
from app.config import FEATURE_RANGES, DEFAULT_MODEL_FEATURES
from app.schemas import MedicalFeatures
def validate_feature(name: str, value: Any) -> Any:
"""Validate a single feature"""
if value is None:
return None
if name not in FEATURE_RANGES:
return None
min_val, max_val, expected_type = FEATURE_RANGES[name]
# Convert to expected type
try:
converted = expected_type(value)
except (ValueError, TypeError):
return None
# Check range
if not (min_val <= converted <= max_val):
return None
return converted
def prepare_features_dict(state: Dict[str, Any]) -> MedicalFeatures:
"""Convert state dict to MedicalFeatures model"""
validated = {}
for feature in DEFAULT_MODEL_FEATURES:
value = state.get(feature)
validated[feature] = validate_feature(feature, value)
return MedicalFeatures(**validated)
def prepare_feature_vector(state: Dict[str, Any]) -> List[float]:
"""
Convert state dict to feature vector for ML model.
Returns array in correct feature order.
Handles missing values with sensible defaults.
"""
# Define feature order (MUST match training data order)
feature_order = DEFAULT_MODEL_FEATURES
vector = []
for feature_name in feature_order:
value = state.get(feature_name)
if value is not None:
vector.append(float(value))
else:
# Use feature mean or 0 for missing values
vector.append(0.0) # Simple approach - can be improved
return vector
def is_ready_for_prediction(state: Dict[str, Any]) -> bool:
"""Check if enough features collected for prediction"""
missing = [k for k, v in state.items() if v is None]
# At least 14 out of 16 features
return len(missing) <= 2
4.2 Implement app/services/predictor.py
import joblib
import logging
from pathlib import Path
from app.config import MODEL_PATH
from app.schemas import PredictionResponse
logger = logging.getLogger(__name__)
class Predictor:
"""Load and use ML model for predictions"""
def __init__(self, model_path: str = None):
if model_path is None:
model_path = MODEL_PATH
self.model_path = Path(model_path)
self.model = None
self.load_model()
def load_model(self):
"""Load model from disk"""
if not self.model_path.exists():
raise FileNotFoundError(f"Model not found at {self.model_path}")
try:
self.model = joblib.load(self.model_path)
logger.info(f"β
Model loaded from {self.model_path}")
except Exception as e:
logger.error(f"β Failed to load model: {e}")
raise
def predict(self, feature_vector: list) -> PredictionResponse:
"""
Make prediction on feature vector
Args:
feature_vector: List of 16 floats in correct order
Returns:
PredictionResponse with prediction, probability, risk level
"""
if self.model is None:
raise RuntimeError("Model not loaded")
try:
# Make prediction
prediction = self.model.predict([feature_vector])[0]
# Get probability
proba = self.model.predict_proba([feature_vector])[0]
probability = float(max(proba))
# Determine risk level
if probability >= 0.8:
risk_level = "High"
elif probability >= 0.5:
risk_level = "Medium"
else:
risk_level = "Low"
return PredictionResponse(
prediction=int(prediction),
probability=probability,
risk_level=risk_level,
explanation=f"Model predicts class {prediction} with {probability*100:.1f}% confidence"
)
except Exception as e:
logger.error(f"Prediction failed: {e}")
raise
# Global predictor instance
predictor = None
def get_predictor():
"""Get or create predictor instance"""
global predictor
if predictor is None:
predictor = Predictor()
return predictor
β Phase 4 Complete When:
- Both services import without errors
- Test with:
python -c "from app.services.feature_builder import *; from app.services.predictor import *"
Time Estimate: 30-40 minutes
PHASE 5: Main Application Integration π² (20% Complete)
Update app/main.py
Replace the current main.py with:
import gradio as gr
import logging
from app.services.llm_extractor import extract_features_from_text
from app.services.feature_builder import prepare_feature_vector, is_ready_for_prediction
from app.services.predictor import get_predictor
from app.memory import initialize_state, update_state, get_missing_features
from app.utils.helpers import generate_question
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Global state
state = initialize_state()
conversation_history = []
def chat_fn(message, history):
"""Main chat function for Gradio"""
global state, conversation_history
try:
# Step 1: Extract features from user input
logger.info(f"User: {message}")
extracted = extract_features_from_text(message)
logger.info(f"Extracted: {extracted}")
# Step 2: Update memory with extracted features
state = update_state(state, extracted)
# Step 3: Check for missing features
missing = get_missing_features(state)
# Step 4: If all features collected, make prediction
if not missing or is_ready_for_prediction(state):
logger.info("All features collected! Making prediction...")
# Prepare features for model
feature_vector = prepare_feature_vector(state)
# Get prediction
predictor = get_predictor()
result = predictor.predict(feature_vector)
response = f"""
β
All information collected!
**Prediction Results:**
- **Prediction**: Class {result.prediction}
- **Confidence**: {result.probability*100:.1f}%
- **Risk Level**: {result.risk_level}
- **Details**: {result.explanation}
---
*Note: This is a demonstration. Always consult with a healthcare professional for medical advice.*
"""
return response
else:
# Ask for next missing feature
next_question = generate_question(missing[0])
remaining = len(missing)
response = f"Got it! {next_question}\n\n_(Missing {remaining} more features)_"
return response
except Exception as e:
logger.error(f"Error in chat: {e}")
return f"β Error: {str(e)}. Please try again."
# Create Gradio interface
demo = gr.ChatInterface(
fn=chat_fn,
title="π₯ Medical Predictor Chatbot",
description="Chat about your medical information. I'll ask follow-up questions and predict your health risk.",
examples=[
"I'm 45 years old and my glucose is 150",
"I smoke and my stress level is 8",
"My BMI is 28 and I exercise 5 hours per week"
]
)
if __name__ == "__main__":
demo.launch()
β Phase 5 Complete When:
python app/main.pyruns without errors- Gradio UI launches at http://localhost:7860
- Chat works (asks questions and eventually predicts)
Time Estimate: 25-35 minutes
PHASE 6: Error Handling & Validation π² (0% Complete)
Add Error Handling to app/services/llm_extractor.py
def extract_features_from_text(user_input: str) -> dict:
"""
Uses Groq LLM to extract structured medical features from text.
Returns dictionary with all required keys.
"""
if not user_input or not user_input.strip():
return {feature: None for feature in DEFAULT_MODEL_FEATURES}
prompt = f"""
You are a medical information extraction system.
Extract the following features from the user input.
Return STRICT JSON only. No explanation.
Features:
{DEFAULT_MODEL_FEATURES}
Rules:
- If value is missing, use null
- Convert values to numbers where possible
- Smoking, Alcohol, Family History β 0 or 1
- Blood Pressure β numeric (e.g., 120)
- Output must be valid JSON
User Input:
\"\"\"{user_input}\"\"\"
"""
try:
response = client.chat.completions.create(
model="llama-3.1-8b-instant",
messages=[
{"role": "system", "content": "You are a strict JSON generator."},
{"role": "user", "content": prompt}
],
temperature=0,
timeout=30
)
content = response.choices[0].message.content
try:
data = json.loads(content)
except json.JSONDecodeError:
# Try extracting JSON from markdown code blocks
if "```" in content:
content = content.split("```")[1]
if content.startswith("json"):
content = content[4:]
data = json.loads(content)
else:
logger.warning("Could not parse LLM response as JSON")
data = {feature: None for feature in DEFAULT_MODEL_FEATURES}
return data
except Exception as e:
logger.error(f"LLM extraction error: {e}")
return {feature: None for feature in DEFAULT_MODEL_FEATURES}
β Phase 6 Complete When:
- No unhandled exceptions when running app
- Graceful error messages shown to user
Time Estimate: 15-20 minutes
PHASE 7: Hugging Face Spaces Deployment π² (0% Complete)
7.1 Create HF Space
- Go to https://huggingface.co/spaces/new
- Fill in:
- Space name: medical-predictor-chatbot
- License: MIT
- SDK: Gradio
- Visibility: Public
7.2 Push Code to HF
# Navigate to your project
cd /media/zunayed/HDD_code/chatbot\ with\ llm/medical-predictor-chatbot
# Add HF remote (replace YOUR_USERNAME)
git remote add hf https://huggingface.co/spaces/YOUR_USERNAME/medical-predictor-chatbot
# Push to HF
git push hf main
7.3 Add Secrets in HF Space
- Go to your Space page
- Click "Settings" β "Repository Secrets"
- Add secret:
- Key:
GROQ_API_KEY - Value: Your Groq API key from
console.groq.com/keys
- Key:
7.4 Update README.md
---
title: Medical Predictor Chatbot
emoji: π₯
colorFrom: blue
colorTo: green
sdk: gradio
app_file: app/main.py
pinned: false
---
# Medical Predictor Chatbot
A conversational AI chatbot that collects medical information through natural conversation and predicts health risks using machine learning.
## Features
- π¬ Conversational interface using Groq Llama 3 LLM
- π₯ Extracts 16 medical features from free-form text
- π€ Makes predictions using scikit-learn GradientBoostingClassifier
- π Shows confidence levels and risk assessment
## How It Works
1. Chat naturally about your health
2. The AI extracts medical information from your responses
3. Asks follow-up questions for missing information
4. Makes a health risk prediction once all data is collected
## Example Usage
- "I'm 45 years old and my glucose is 150"
- "I smoke and my stress level is 8"
- "My BMI is 28 and I exercise 5 hours per week"
## Disclaimer
β οΈ This is a demonstration tool. Always consult with healthcare professionals for medical advice.
β Phase 7 Complete When:
- Code deployed to HF Spaces
- App loads and works
- GROQ_API_KEY secret is set
Time Estimate: 15-30 minutes
PHASE 8: Testing & Finalization π² (0% Complete)
Test Scenarios
Test 1: Feature Extraction
python test_extractor.py
# Check if JSON is returned with features
Test 2: Gradio UI (Local)
python app/main.py
# Open http://localhost:7860
# Test conversation flow
Test 3: Full Prediction Flow
Chat sequence:
- "I'm 45 years old"
- "My glucose is 150"
- "I smoke"
- (Continue answering questions for other features)
- (Expect: Prediction with risk level)
Test 4: Error Handling
- Send empty message β Should handle gracefully
- Send gibberish β Should extract what it can
- Invalid values β Should validate and reject
β Phase 8 Complete When:
- All tests pass
- No errors in logs
- App works on HF Spaces
- README is complete
Time Estimate: 30-60 minutes
π Success Criteria Checklist
-
.envfile created with GROQ_API_KEY -
app/config.pycompleted -
app/schemas.pycompleted -
models/GradientBoosting_model.pklexists -
app/services/feature_builder.pycompleted -
app/services/predictor.pycompleted -
app/main.pyupdated with prediction - Error handling added
- Logging configured
- Local testing successful
- HF Space created
- Code pushed to HF
- GROQ_API_KEY secret added to HF
- HF deployment successful
- README updated with documentation
π Start Here!
Next step: Follow PHASE 2 in this guide to create config.py and schemas.py
When ready, type: "I'm ready for Phase 2"