# System Architecture & Data Flow ## πŸ—οΈ System Architecture Diagram ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HUGGING FACE SPACES β”‚ β”‚ (Free CPU Tier) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ GRADIO UI (Chat Interface) β”‚ β”‚ β”‚ β”‚ - Display chat messages β”‚ β”‚ β”‚ β”‚ - Take user input β”‚ β”‚ β”‚ β”‚ - Show predictions β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ app/main.py β”‚ β”‚ β”‚ β”‚ (Chat Function & Orchestration) β”‚ β”‚ β”‚ β”‚ - Coordinates all services β”‚ β”‚ β”‚ β”‚ - Manages conversation flow β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ LLM β”‚ β”‚ Memory β”‚ β”‚ Predictor β”‚ β”‚ β”‚ β”‚ Extractor β”‚ β”‚ Manager β”‚ β”‚ Service β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Input: β”‚ β”‚ Input: β”‚ β”‚ Input: β”‚ β”‚ β”‚ β”‚ "I'm 45..." β”‚ β”‚ Extracted β”‚ β”‚ Feature β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ features β”‚ β”‚ vector β”‚ β”‚ β”‚ β”‚ Output: β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ JSON with β”‚ β”‚ Output: β”‚ β”‚ Output: β”‚ β”‚ β”‚ β”‚ features β”‚ β”‚ Full state β”‚ β”‚ Prediction β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ (16 features) β”‚ β”‚ + confidence β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β” β”‚ β”‚ β”‚ GROQ β”‚ β”‚ app/ β”‚ β”‚ Feature β”‚ β”‚ β”‚ β”‚ Llama 3 β”‚ β”‚ memory.py β”‚ β”‚ Builder β”‚ β”‚ β”‚ β”‚ (Free API) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ State: β”‚ β”‚ Validates & β”‚ β”‚ β”‚ β”‚ Cloud- β”‚ β”‚ { β”‚ β”‚ prepares β”‚ β”‚ β”‚ β”‚ based β”‚ β”‚ Age: 45, β”‚ β”‚ feature β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Glucose: β”‚ β”‚ vector for β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ 150, β”‚ β”‚ ML model β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ... β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ... β”‚ β”‚ (Validates β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ } β”‚ β”‚ ranges) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Predictor Service β”‚ β”‚ β”‚ β”‚ (app/services/ β”‚ β”‚ β”‚ β”‚ predictor.py) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Loads: β”‚ β”‚ β”‚ β”‚ GradientBoosting_ β”‚ β”‚ β”‚ β”‚ model.pkl β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Inputs: [16 floats] β”‚ β”‚ β”‚ β”‚ Outputs: class + prob β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ scikit-learn β”‚ β”‚ β”‚ β”‚ GradientBoosting β”‚ β”‚ β”‚ β”‚ Classifier β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ (Runs locally on CPU) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ“Š Data Flow Sequence ### Step 1: User Inputs Text ``` User: "I'm 45 years old and my glucose is 150" ↓ [Sent to main.py] ``` ### Step 2: LLM Extraction (Groq API) ``` app/main.py ↓ call: extract_features_from_text(message) ↓ llm_extractor.py ↓ Groq API (Cloud) β”‚ └─→ llama-3.1-8b-instant (Processes: "I'm 45... glucose 150...") ↓ Returns JSON: { "Age": 45, "Glucose": 150, "Smoking": null, "HbA1c": null, ... (rest null) } ↓ [Returns to main.py] ``` ### Step 3: Update Memory ``` current_state = { "Age": null, "Glucose": null, ... (all null) } ↓ extracted = {"Age": 45, "Glucose": 150, ...} ↓ memory.update_state(current_state, extracted) ↓ new_state = { "Age": 45, "Glucose": 150, "Smoking": null, ... (rest null) } ``` ### Step 4: Check Missing Features ``` missing_features = get_missing_features(state) ↓ Result: ["Smoking", "Family History", "HbA1c", ... (12 more)] ↓ len(missing) = 14 features still needed ↓ DECISION: Not ready for prediction yet ``` ### Step 5: Generate Question ``` next_missing = missing_features[0] # "Smoking" ↓ question = generate_question("Smoking") ↓ Result: "Do you smoke? (yes/no)" ↓ Send to user in chat ``` ### Step 6: User Answers (Loop Back to Step 1) ``` User: "No, I don't smoke" ↓ [Loop back to Step 1] ↓ (Repeat until all 16 features collected) ``` ### Step 7: All Features Collected - Ready for Prediction ``` state = { "Age": 45, "Glucose": 150, "Smoking": 0, "Family History": 1, ... (all 16 features filled) } ↓ missing_features = [] # Empty! ↓ DECISION: Ready for prediction! ``` ### Step 8: Feature Builder - Prepare for ML Model ``` feature_builder.prepare_feature_vector(state) ↓ Validation: - Check each value in valid range - Convert types to float - Handle missing with defaults ↓ Output: [45.0, 150.0, 0.0, 1.0, ... (16 floats total)] ↓ This vector is ready for ML model ``` ### Step 9: Prediction ``` feature_vector = [45.0, 150.0, 0.0, 1.0, ...] ↓ predictor = get_predictor() # Loads model.pkl ↓ result = predictor.predict(feature_vector) ↓ Model processes: - Input: 16 features - Runs through GradientBoosting - Output: class (0 or 1) + probability ↓ Returns: PredictionResponse { "prediction": 1, "probability": 0.85, "risk_level": "High", "explanation": "Model predicts class 1 with 85% confidence" } ``` ### Step 10: Display Results to User ``` Chatbot: "βœ… All information collected! Prediction Results: - Prediction: Class 1 - Confidence: 85.0% - Risk Level: High - Details: Model predicts class 1 with 85% confidence" ``` --- ## πŸ”„ Complete Conversation Example ``` USER: "I'm 45 years old, my glucose is 150, I smoke, and my stress is high" STEP 1 (Extract): Groq extracts: {Age: 45, Glucose: 150, Smoking: 1, StressLevel: null} STEP 2 (Update Memory): state = {Age: 45, Glucose: 150, Smoking: 1, StressLevel: null, ...} STEP 3 (Check Missing): missing = ["Family History", "HbA1c", "StressLevel", ... (12 more)] STEP 4 (Ask Question): BOT: "Do you have a family history of disease? (yes/no)" USER: "Yes" STEP 1 (Extract): Groq extracts: {FamilyHistory: 1} STEP 2 (Update Memory): state = {Age: 45, Glucose: 150, Smoking: 1, FamilyHistory: 1, ...} STEP 3 (Check Missing): missing = ["HbA1c", "StressLevel", ... (12 more)] STEP 4 (Ask Question): BOT: "What is your HbA1c level?" ... (repeat until all 16 features) STEP 7 (All Collected): state = {Age: 45, Glucose: 150, Smoking: 1, FamilyHistory: 1, HbA1c: 7.2, BMI: 28, ... (all 16 filled)} STEP 8 (Prepare): feature_vector = [45.0, 150.0, 1.0, 1.0, 7.2, ... (16 values)] STEP 9 (Predict): ML Model processes vector Returns: {prediction: 1, probability: 0.82, risk_level: "High"} STEP 10 (Display): BOT: "βœ… Prediction: Class 1 (82% confidence)" ``` --- ## πŸ“ File Dependencies & Data Flow ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ app/main.py β”‚ ← ORCHESTRATOR (coordinates everything) β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β–Ό β–Ό β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚memory β”‚ β”‚llm_ β”‚ β”‚feature_ β”‚ β”‚predictor β”‚ β”‚.py β”‚ β”‚extractor β”‚ β”‚builder.py β”‚ β”‚.py β”‚ β”‚ β”‚ β”‚.py β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Groq β”‚ β”‚ β”‚ β”‚Validate β”‚ β”‚ β”‚ β”‚Load β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚API β”‚ β”‚ β”‚ β”‚Features β”‚ β”‚ β”‚ β”‚Model β”‚ β”‚ β”‚ β””β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ pkl β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Prepare β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Tracks β”‚ β”‚Extracts β”‚ β”‚ β”‚Vector β”‚ β”‚ β”‚ β”‚Predict β”‚ β”‚ β”‚State β”‚ β”‚Features β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Result β”‚ β”‚ β”‚ β”‚ β”‚(JSON) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜ β”‚ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ └────┼──┐ β”‚ β””β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β–Ό β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚app/schemas.pyβ”‚ β”‚models/ β”‚ β”‚(Validation) β”‚ β”‚GradientBoosting β”‚ β”‚ β”‚ β”‚_model.pkl β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚Pydantic β”‚ β”‚ β”‚ (scikit-learn) β”‚ β”‚ β”‚Models β”‚ β”‚ β”‚ Binary Classifier β”‚ β”‚(Types) β”‚ β”‚ β”‚ 16 Features β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Input β†’ Output β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚app/config.py β”‚ β”‚(Constants & Configuration) β”‚ β”‚ β€’ Paths β”‚ β”‚ β€’ API settings β”‚ β”‚ β€’ Feature ranges β”‚ β”‚ β€’ Feature list β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚app/utils/helpers.py β”‚ β”‚(Question Mapping) β”‚ β”‚ Feature β†’ Question lookup β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ”Œ Integration Points ### 1. **Groq API ↔ LLM Extractor** ``` Input: User text (string) Process: HTTP request to Groq cloud Output: JSON with features Error: Timeout, invalid JSON, API errors ``` ### 2. **LLM Extractor ↔ Memory** ``` Input: JSON from Groq Process: Merge into state dict Output: Updated state with new values Error: Type mismatch, null values (OK) ``` ### 3. **Memory ↔ Feature Builder** ``` Input: State dict with all features Process: Validate ranges, convert types Output: Prepared feature vector Error: Out of range, type errors ``` ### 4. **Feature Builder ↔ Predictor** ``` Input: Feature vector [16 floats] Process: Load model, make prediction Output: Prediction class + probability Error: Model not found, predict error ``` ### 5. **Predictor ↔ Main App** ``` Input: Request for prediction Process: Get result from model Output: PredictionResponse object Error: Model errors, input errors ``` --- ## 🎯 Key Design Decisions ### Why Separate Services? - **llm_extractor.py**: Handles all Groq API logic - **feature_builder.py**: Handles all validation logic - **predictor.py**: Handles all ML model logic - **memory.py**: Handles state management - **helpers.py**: Handles UI text generation **Benefits**: - Easy to test each independently - Easy to modify without breaking others - Clear separation of concerns - Reusable components ### Why Pydantic Schemas? - Type validation - Automatic conversion - Error messages - Documentation - IDE autocomplete ### Why Groq Instead of Local LLM? - Free tier (very generous) - Fast inference (cloud-based) - No GPU needed - No local setup required - Easy to deploy on CPU-only Spaces ### Why scikit-learn Model? - Lightweight (fast on CPU) - Works on HF Spaces free tier - Easy to load/save (joblib) - No deep learning overhead - Deterministic results --- ## πŸš€ Performance Considerations ### Typical Response Times | Step | Time | Notes | |------|------|-------| | Groq API call | 1-3s | Cloud-based, depends on load | | Feature extraction | <0.1s | JSON parsing | | Memory update | <0.01s | Dict operations | | Feature validation | <0.01s | Simple checks | | Prediction | <0.1s | scikit-learn inference | | **Total** | **1-3s** | User sees response in 1-3 seconds | ### Scalability - **Concurrent Users**: HF Spaces free CPU can handle ~10-20 concurrent users - **API Rate**: Groq free tier: very generous (1000s of calls/day) - **Model Size**: GradientBoosting small (<5MB) - **Memory Usage**: ~200MB for app + model --- ## πŸ”’ Security Considerations ### Secrets Handling - GROQ_API_KEY: Stored in .env locally, HF Spaces secrets in production - Model file: Public (no sensitive info) - User data: In-memory only (not persisted) ### Input Validation - All user inputs validated via Pydantic - Feature ranges checked - Type conversion safe ### Privacy - No data logged - No external APIs called except Groq - No user data persisted --- ## ⚑ Optimization Opportunities (Future) 1. **Caching**: Cache similar predictions 2. **Batching**: Process multiple users' requests together 3. **Model**: Use faster model variant 4. **LLM**: Use smaller Groq model for faster extraction 5. **Storage**: Add database for history (optional) --- This architecture is designed for: - βœ… Clarity & maintainability - βœ… Testability - βœ… Deployability on free Spaces - βœ… Easy debugging - βœ… Extensibility Ready to implement? Follow the GUIDES.md file step-by-step!