Upload 3 files
Browse files- app.py +1323 -0
- config.py +53 -0
- requirements.txt +46 -0
app.py
ADDED
|
@@ -0,0 +1,1323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
app.py β Multi-AI Analytics Platform v2.1 (Clean Edition)
|
| 3 |
+
Run: streamlit run app.py
|
| 4 |
+
|
| 5 |
+
Requirements (install before running):
|
| 6 |
+
pip install streamlit pandas numpy pillow plotly scikit-learn xgboost lightgbm
|
| 7 |
+
pip install opencv-python-headless transformers torch torchvision
|
| 8 |
+
pip install openai google-generativeai anthropic # optional, for API providers
|
| 9 |
+
pip install spacy && python -m spacy download en_core_web_sm # optional NER
|
| 10 |
+
|
| 11 |
+
Place these files in the SAME folder as app.py:
|
| 12 |
+
generative_ai.py
|
| 13 |
+
nlp_module.py
|
| 14 |
+
ml_models.py (your existing file β unchanged)
|
| 15 |
+
dl_module.py (your existing file β unchanged)
|
| 16 |
+
data/data_loader.py
|
| 17 |
+
data/powerbi_export.py
|
| 18 |
+
config.py
|
| 19 |
+
utils/helpers.py
|
| 20 |
+
"""
|
| 21 |
+
import warnings
|
| 22 |
+
warnings.filterwarnings("ignore")
|
| 23 |
+
|
| 24 |
+
import os
|
| 25 |
+
import sys
|
| 26 |
+
import json
|
| 27 |
+
from pathlib import Path
|
| 28 |
+
from typing import Any, Optional
|
| 29 |
+
|
| 30 |
+
import streamlit as st
|
| 31 |
+
import pandas as pd
|
| 32 |
+
import numpy as np
|
| 33 |
+
from PIL import Image
|
| 34 |
+
|
| 35 |
+
# ββ Root path setup βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 36 |
+
ROOT = Path(__file__).parent
|
| 37 |
+
sys.path.insert(0, str(ROOT))
|
| 38 |
+
|
| 39 |
+
# ββ Core imports ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 40 |
+
try:
|
| 41 |
+
from config import Config, OUTPUT_DIR
|
| 42 |
+
except ImportError:
|
| 43 |
+
OUTPUT_DIR = ROOT / "outputs"
|
| 44 |
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
| 45 |
+
|
| 46 |
+
from data.data_loader import DataLoader
|
| 47 |
+
from data.powerbi_export import PowerBIExporter
|
| 48 |
+
from models.ml_models import MLPipeline, XGBoostPipeline, EnsemblePipeline
|
| 49 |
+
from models.generative_ai import GenerativeAI, OPENAI_OK, GOOGLE_OK, ANTHROPIC_OK
|
| 50 |
+
from utils.helpers import (
|
| 51 |
+
create_feature_importance_chart, create_metrics_dashboard,
|
| 52 |
+
create_confusion_matrix, create_correlation_heatmap,
|
| 53 |
+
create_class_distribution, create_actual_vs_predicted,
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
try:
|
| 57 |
+
from models.ml_models import LightGBMPipeline
|
| 58 |
+
LGB_OK = True
|
| 59 |
+
except Exception:
|
| 60 |
+
LightGBMPipeline = None
|
| 61 |
+
LGB_OK = False
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# ββ Page config βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 65 |
+
st.set_page_config(
|
| 66 |
+
page_title="Multi-AI Analytics Platform",
|
| 67 |
+
page_icon="β‘",
|
| 68 |
+
layout="wide",
|
| 69 |
+
initial_sidebar_state="expanded",
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 74 |
+
# Global CSS β Aurora Dark theme
|
| 75 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 76 |
+
st.markdown("""
|
| 77 |
+
<style>
|
| 78 |
+
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
| 79 |
+
|
| 80 |
+
*, *::before, *::after { box-sizing: border-box; }
|
| 81 |
+
html, body, [class*="css"] { font-family: 'Outfit', sans-serif !important; }
|
| 82 |
+
|
| 83 |
+
.stApp {
|
| 84 |
+
background: #080b14;
|
| 85 |
+
background-image:
|
| 86 |
+
radial-gradient(ellipse 80% 50% at 20% -10%, rgba(99,102,241,0.18) 0%, transparent 60%),
|
| 87 |
+
radial-gradient(ellipse 60% 40% at 80% 110%, rgba(20,184,166,0.14) 0%, transparent 55%),
|
| 88 |
+
radial-gradient(ellipse 50% 60% at 50% 50%, rgba(139,92,246,0.05) 0%, transparent 70%);
|
| 89 |
+
color: #e2e8f0;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
section[data-testid="stSidebar"] {
|
| 93 |
+
background: linear-gradient(180deg, #0c0f1d 0%, #0e1120 60%, #0a0d18 100%) !important;
|
| 94 |
+
border-right: 1px solid rgba(99,102,241,0.2);
|
| 95 |
+
}
|
| 96 |
+
section[data-testid="stSidebar"] * { color: #cbd5e1 !important; }
|
| 97 |
+
|
| 98 |
+
.hero-wrap {
|
| 99 |
+
background: linear-gradient(135deg,
|
| 100 |
+
rgba(99,102,241,0.12) 0%, rgba(139,92,246,0.08) 40%, rgba(20,184,166,0.1) 100%);
|
| 101 |
+
border: 1px solid rgba(99,102,241,0.25);
|
| 102 |
+
border-radius: 20px;
|
| 103 |
+
padding: 2.4rem 2rem 2rem;
|
| 104 |
+
margin-bottom: 1.8rem;
|
| 105 |
+
position: relative; overflow: hidden;
|
| 106 |
+
}
|
| 107 |
+
.hero-title {
|
| 108 |
+
font-size: 2.6rem; font-weight: 800; line-height: 1.2;
|
| 109 |
+
background: linear-gradient(135deg, #a78bfa 0%, #60a5fa 50%, #2dd4bf 100%);
|
| 110 |
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 111 |
+
background-clip: text; margin-bottom: 0.5rem;
|
| 112 |
+
}
|
| 113 |
+
.hero-sub { font-size: 1rem; color: #94a3b8; letter-spacing: 0.06em; }
|
| 114 |
+
.hero-badges { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 1.2rem; }
|
| 115 |
+
.badge {
|
| 116 |
+
background: rgba(99,102,241,0.15); border: 1px solid rgba(99,102,241,0.3);
|
| 117 |
+
color: #a78bfa; padding: 4px 14px; border-radius: 999px;
|
| 118 |
+
font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em;
|
| 119 |
+
font-family: 'JetBrains Mono', monospace;
|
| 120 |
+
}
|
| 121 |
+
.badge.teal { background: rgba(20,184,166,0.12); border-color: rgba(20,184,166,0.3); color: #2dd4bf; }
|
| 122 |
+
.badge.blue { background: rgba(59,130,246,0.12); border-color: rgba(59,130,246,0.3); color: #60a5fa; }
|
| 123 |
+
|
| 124 |
+
.mod-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin: 1.2rem 0; }
|
| 125 |
+
.mod-card {
|
| 126 |
+
background: linear-gradient(135deg, rgba(15,18,35,0.9) 0%, rgba(20,24,42,0.9) 100%);
|
| 127 |
+
border: 1px solid rgba(99,102,241,0.2); border-radius: 16px;
|
| 128 |
+
padding: 1.4rem 1.2rem; transition: border-color 0.25s, transform 0.2s;
|
| 129 |
+
position: relative; overflow: hidden;
|
| 130 |
+
}
|
| 131 |
+
.mod-card:hover { border-color: rgba(139,92,246,0.55); transform: translateY(-2px); }
|
| 132 |
+
.mod-card .icon { font-size: 2.2rem; margin-bottom: 0.7rem; }
|
| 133 |
+
.mod-card .title { font-size: 1.05rem; font-weight: 700; color: #f1f5f9; margin-bottom: 0.3rem; }
|
| 134 |
+
.mod-card .desc { font-size: 0.82rem; color: #64748b; line-height: 1.5; }
|
| 135 |
+
.mod-card .glow {
|
| 136 |
+
position: absolute; top: -40px; right: -40px;
|
| 137 |
+
width: 100px; height: 100px; border-radius: 50%;
|
| 138 |
+
background: radial-gradient(circle, var(--gc) 0%, transparent 70%); opacity: 0.35;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
.stat-row { display: flex; gap: 1rem; margin: 1rem 0; }
|
| 142 |
+
.stat-card {
|
| 143 |
+
flex: 1;
|
| 144 |
+
background: linear-gradient(135deg, rgba(15,18,35,0.95) 0%, rgba(20,24,45,0.95) 100%);
|
| 145 |
+
border: 1px solid rgba(99,102,241,0.18); border-radius: 14px;
|
| 146 |
+
padding: 1.1rem 1rem; text-align: center;
|
| 147 |
+
}
|
| 148 |
+
.stat-val { font-size: 1.7rem; font-weight: 700; color: #a78bfa; font-family: 'JetBrains Mono', monospace; }
|
| 149 |
+
.stat-lbl { font-size: 0.75rem; color: #64748b; margin-top: 2px; text-transform: uppercase; letter-spacing: 0.07em; }
|
| 150 |
+
|
| 151 |
+
.sec-head {
|
| 152 |
+
font-size: 1.35rem; font-weight: 700;
|
| 153 |
+
background: linear-gradient(90deg, #a78bfa, #60a5fa);
|
| 154 |
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 155 |
+
background-clip: text; margin: 1rem 0 0.6rem;
|
| 156 |
+
display: flex; align-items: center; gap: 0.5rem;
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
.info-box {
|
| 160 |
+
background: rgba(99,102,241,0.08); border-left: 4px solid #6366f1;
|
| 161 |
+
border-radius: 0 12px 12px 0; padding: 0.9rem 1.1rem; margin: 0.7rem 0;
|
| 162 |
+
color: #cbd5e1; line-height: 1.6;
|
| 163 |
+
}
|
| 164 |
+
.success-box {
|
| 165 |
+
background: rgba(20,184,166,0.08); border-left: 4px solid #14b8a6;
|
| 166 |
+
border-radius: 0 12px 12px 0; padding: 0.9rem 1.1rem; margin: 0.7rem 0;
|
| 167 |
+
color: #99f6e4;
|
| 168 |
+
}
|
| 169 |
+
.result-box {
|
| 170 |
+
background: linear-gradient(135deg, rgba(15,18,35,0.98) 0%, rgba(20,24,48,0.98) 100%);
|
| 171 |
+
border: 1px solid rgba(99,102,241,0.22); border-radius: 14px;
|
| 172 |
+
padding: 1.3rem 1.4rem; margin: 0.8rem 0; color: #e2e8f0; line-height: 1.75;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
.stButton > button {
|
| 176 |
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important;
|
| 177 |
+
color: white !important; border: none !important; border-radius: 10px !important;
|
| 178 |
+
font-weight: 600 !important; font-family: 'Outfit', sans-serif !important;
|
| 179 |
+
letter-spacing: 0.02em; transition: opacity 0.2s, transform 0.15s !important;
|
| 180 |
+
padding: 0.55rem 1.4rem !important;
|
| 181 |
+
}
|
| 182 |
+
.stButton > button:hover { opacity: 0.88 !important; transform: translateY(-1px) !important; }
|
| 183 |
+
|
| 184 |
+
.stTabs [data-baseweb="tab-list"] {
|
| 185 |
+
gap: 6px; background: rgba(8,11,20,0.6); border-radius: 12px; padding: 4px;
|
| 186 |
+
border: 1px solid rgba(99,102,241,0.15);
|
| 187 |
+
}
|
| 188 |
+
.stTabs [data-baseweb="tab"] {
|
| 189 |
+
border-radius: 9px; padding: 0.5rem 1.2rem; font-weight: 600; color: #64748b;
|
| 190 |
+
}
|
| 191 |
+
.stTabs [aria-selected="true"] {
|
| 192 |
+
background: linear-gradient(135deg, rgba(99,102,241,0.3), rgba(139,92,246,0.3)) !important;
|
| 193 |
+
color: #a78bfa !important;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
.stTextArea textarea, .stTextInput input, .stSelectbox select {
|
| 197 |
+
background: rgba(15,18,35,0.9) !important; border: 1px solid rgba(99,102,241,0.2) !important;
|
| 198 |
+
border-radius: 10px !important; color: #e2e8f0 !important;
|
| 199 |
+
}
|
| 200 |
+
.stDataFrame { border: 1px solid rgba(99,102,241,0.2) !important; border-radius: 12px !important; }
|
| 201 |
+
[data-testid="stMetricValue"] { color: #a78bfa !important; font-weight: 700; }
|
| 202 |
+
[data-testid="stMetricLabel"] { color: #64748b !important; }
|
| 203 |
+
[data-testid="stChatMessage"] {
|
| 204 |
+
background: rgba(15,18,35,0.7) !important;
|
| 205 |
+
border: 1px solid rgba(99,102,241,0.15) !important; border-radius: 14px !important;
|
| 206 |
+
}
|
| 207 |
+
.footer {
|
| 208 |
+
text-align: center; color: #334155; font-size: 0.78rem;
|
| 209 |
+
margin-top: 2.5rem; padding: 1rem;
|
| 210 |
+
border-top: 1px solid rgba(99,102,241,0.1); letter-spacing: 0.06em;
|
| 211 |
+
}
|
| 212 |
+
.footer span { color: #6366f1; font-family: 'JetBrains Mono', monospace; font-weight: 600; }
|
| 213 |
+
.stSpinner > div { border-top-color: #6366f1 !important; }
|
| 214 |
+
hr { border-color: rgba(99,102,241,0.15) !important; }
|
| 215 |
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
| 216 |
+
::-webkit-scrollbar-track { background: #080b14; }
|
| 217 |
+
::-webkit-scrollbar-thumb { background: rgba(99,102,241,0.4); border-radius: 3px; }
|
| 218 |
+
</style>
|
| 219 |
+
""", unsafe_allow_html=True)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
# βββββββββββββββββββββοΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 223 |
+
# Sidebar
|
| 224 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 225 |
+
with st.sidebar:
|
| 226 |
+
st.markdown("""
|
| 227 |
+
<div style="text-align:center;padding:1rem 0 0.5rem">
|
| 228 |
+
<div style="font-size:2.4rem">β‘</div>
|
| 229 |
+
<div style="font-size:1.1rem;font-weight:800;
|
| 230 |
+
background:linear-gradient(135deg,#a78bfa,#2dd4bf);
|
| 231 |
+
-webkit-background-clip:text;-webkit-text-fill-color:transparent">
|
| 232 |
+
AI Platform
|
| 233 |
+
</div>
|
| 234 |
+
<div style="font-size:0.72rem;color:#475569;letter-spacing:0.08em;margin-top:2px">
|
| 235 |
+
v2.1 Β· CLEAN EDITION
|
| 236 |
+
</div>
|
| 237 |
+
</div>
|
| 238 |
+
""", unsafe_allow_html=True)
|
| 239 |
+
st.divider()
|
| 240 |
+
|
| 241 |
+
st.markdown("### π Generative AI")
|
| 242 |
+
provider_choice = st.selectbox(
|
| 243 |
+
"Provider",
|
| 244 |
+
["smart", "openai", "google", "anthropic"],
|
| 245 |
+
format_func=lambda x: {
|
| 246 |
+
"smart": "β‘ Smart AI (Instant Β· No API Key)",
|
| 247 |
+
"openai": "π’ OpenAI GPT-4o",
|
| 248 |
+
"google": "π΅ Google Gemini",
|
| 249 |
+
"anthropic": "π£ Anthropic Claude",
|
| 250 |
+
}[x],
|
| 251 |
+
)
|
| 252 |
+
if provider_choice == "smart":
|
| 253 |
+
st.caption("β
Instant responses β no API key, no downloads")
|
| 254 |
+
api_key_input = ""
|
| 255 |
+
else:
|
| 256 |
+
api_key_input = st.text_input("API Key", type="password", placeholder="Paste key hereβ¦")
|
| 257 |
+
st.divider()
|
| 258 |
+
|
| 259 |
+
st.markdown("### βοΈ System Status")
|
| 260 |
+
for lib, label, emoji in [
|
| 261 |
+
("torch", "PyTorch", "π₯"),
|
| 262 |
+
("sklearn", "sklearn", "π€"),
|
| 263 |
+
("xgboost", "XGBoost", "β‘"),
|
| 264 |
+
("transformers","Transformers", "π€"),
|
| 265 |
+
("cv2", "OpenCV", "π·"),
|
| 266 |
+
("lightgbm", "LightGBM", "πΏ"),
|
| 267 |
+
]:
|
| 268 |
+
try:
|
| 269 |
+
mod = __import__(lib)
|
| 270 |
+
ver = getattr(mod, "__version__", "β")
|
| 271 |
+
st.markdown(f"{emoji} {label} `{ver}`")
|
| 272 |
+
except ImportError:
|
| 273 |
+
st.markdown(f"{emoji} {label} β")
|
| 274 |
+
st.divider()
|
| 275 |
+
|
| 276 |
+
if st.button("π Reset Session", use_container_width=True):
|
| 277 |
+
for k in list(st.session_state.keys()):
|
| 278 |
+
del st.session_state[k]
|
| 279 |
+
st.rerun()
|
| 280 |
+
|
| 281 |
+
st.markdown("""
|
| 282 |
+
<div style="text-align:center;margin-top:1rem;color:#334155;font-size:0.7rem">
|
| 283 |
+
Multi-AI Analytics Platform<br>
|
| 284 |
+
<span style="color:#6366f1;font-family:'JetBrains Mono',monospace">KYOTO-Z</span>
|
| 285 |
+
</div>""", unsafe_allow_html=True)
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 289 |
+
# Session state initialisation
|
| 290 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 291 |
+
def _init():
|
| 292 |
+
defaults = {
|
| 293 |
+
"data_loader": DataLoader(),
|
| 294 |
+
"powerbi_exp": PowerBIExporter(OUTPUT_DIR),
|
| 295 |
+
"df": None,
|
| 296 |
+
"data_summary": None,
|
| 297 |
+
"ml_pipeline": None,
|
| 298 |
+
"ml_results": None,
|
| 299 |
+
"ml_metrics": None,
|
| 300 |
+
"gen_ai": GenerativeAI(provider="smart"),
|
| 301 |
+
"uploaded_image": None,
|
| 302 |
+
"target_column": None,
|
| 303 |
+
}
|
| 304 |
+
for k, v in defaults.items():
|
| 305 |
+
if k not in st.session_state:
|
| 306 |
+
st.session_state[k] = v
|
| 307 |
+
|
| 308 |
+
_init()
|
| 309 |
+
|
| 310 |
+
# Always rebuild gen_ai when provider/key change
|
| 311 |
+
st.session_state.gen_ai = GenerativeAI(api_key=api_key_input, provider=provider_choice)
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 315 |
+
# Hero Banner
|
| 316 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 317 |
+
st.markdown("""
|
| 318 |
+
<div class="hero-wrap">
|
| 319 |
+
<div class="hero-title">β‘ Multi-AI Analytics Platform</div>
|
| 320 |
+
<div class="hero-sub">Machine Learning Β· Deep Learning Β· NLP Β· Generative AI Β· Power BI Export</div>
|
| 321 |
+
<div class="hero-badges">
|
| 322 |
+
<span class="badge">scikit-learn</span>
|
| 323 |
+
<span class="badge">XGBoost</span>
|
| 324 |
+
<span class="badge teal">HuggingFace</span>
|
| 325 |
+
<span class="badge blue">OpenCV</span>
|
| 326 |
+
<span class="badge">PyTorch / TF</span>
|
| 327 |
+
<span class="badge teal">Plotly</span>
|
| 328 |
+
<span class="badge blue">Power BI</span>
|
| 329 |
+
</div>
|
| 330 |
+
</div>
|
| 331 |
+
""", unsafe_allow_html=True)
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 335 |
+
# Main tabs
|
| 336 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 337 |
+
tab_home, tab_data, tab_ml, tab_dl, tab_nlp, tab_genai, tab_pbi = st.tabs([
|
| 338 |
+
"π Home", "π Data", "π€ ML Pipeline",
|
| 339 |
+
"π§ Deep Learning", "π NLP", "π‘ Generative AI", "π€ Power BI",
|
| 340 |
+
])
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 344 |
+
# TAB 0 Β· HOME
|
| 345 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 346 |
+
with tab_home:
|
| 347 |
+
st.markdown("""
|
| 348 |
+
<div class="mod-grid">
|
| 349 |
+
<div class="mod-card">
|
| 350 |
+
<div class="glow" style="--gc:rgba(99,102,241,0.5)"></div>
|
| 351 |
+
<div class="icon">π€</div>
|
| 352 |
+
<div class="title">ML Pipeline</div>
|
| 353 |
+
<div class="desc">9+ models β Random Forest, XGBoost, LightGBM, Ensemble. Full metrics, ROC, feature importance.</div>
|
| 354 |
+
</div>
|
| 355 |
+
<div class="mod-card">
|
| 356 |
+
<div class="glow" style="--gc:rgba(20,184,166,0.5)"></div>
|
| 357 |
+
<div class="icon">π</div>
|
| 358 |
+
<div class="title">Data Explorer</div>
|
| 359 |
+
<div class="desc">Upload CSV/Excel/JSON. Auto EDA, correlation heatmaps, distributions, scatter builder.</div>
|
| 360 |
+
</div>
|
| 361 |
+
<div class="mod-card">
|
| 362 |
+
<div class="glow" style="--gc:rgba(139,92,246,0.5)"></div>
|
| 363 |
+
<div class="icon">π§ </div>
|
| 364 |
+
<div class="title">Deep Learning</div>
|
| 365 |
+
<div class="desc">MobileNetV2, ResNet50, VGG16 classification. Grad-CAM, face/edge detection, image filters.</div>
|
| 366 |
+
</div>
|
| 367 |
+
<div class="mod-card">
|
| 368 |
+
<div class="glow" style="--gc:rgba(59,130,246,0.5)"></div>
|
| 369 |
+
<div class="icon">π</div>
|
| 370 |
+
<div class="title">NLP Suite</div>
|
| 371 |
+
<div class="desc">Sentiment analysis, NER, zero-shot classification, summarization β all via HuggingFace.</div>
|
| 372 |
+
</div>
|
| 373 |
+
<div class="mod-card">
|
| 374 |
+
<div class="glow" style="--gc:rgba(236,72,153,0.5)"></div>
|
| 375 |
+
<div class="icon">π‘</div>
|
| 376 |
+
<div class="title">Generative AI</div>
|
| 377 |
+
<div class="desc">GPT-4 Β· Gemini Β· Claude. Context-aware chatbot, Q&A, code gen, image generation, reports.</div>
|
| 378 |
+
</div>
|
| 379 |
+
<div class="mod-card">
|
| 380 |
+
<div class="glow" style="--gc:rgba(245,158,11,0.5)"></div>
|
| 381 |
+
<div class="icon">π€</div>
|
| 382 |
+
<div class="title">Power BI Export</div>
|
| 383 |
+
<div class="desc">Export datasets, feature importance tables, and predictions as CSV/Parquet for Power BI.</div>
|
| 384 |
+
</div>
|
| 385 |
+
</div>
|
| 386 |
+
""", unsafe_allow_html=True)
|
| 387 |
+
|
| 388 |
+
st.markdown("---")
|
| 389 |
+
c1, c2, c3, c4 = st.columns(4)
|
| 390 |
+
c1.markdown('<div class="stat-card"><div class="stat-val">9+</div><div class="stat-lbl">ML Models</div></div>', unsafe_allow_html=True)
|
| 391 |
+
c2.markdown('<div class="stat-card"><div class="stat-val">4</div><div class="stat-lbl">CNN Backbones</div></div>', unsafe_allow_html=True)
|
| 392 |
+
c3.markdown('<div class="stat-card"><div class="stat-val">5</div><div class="stat-lbl">NLP Tasks</div></div>', unsafe_allow_html=True)
|
| 393 |
+
c4.markdown('<div class="stat-card"><div class="stat-val">3</div><div class="stat-lbl">Gen AI Providers</div></div>', unsafe_allow_html=True)
|
| 394 |
+
|
| 395 |
+
st.markdown("""
|
| 396 |
+
<div class="info-box">
|
| 397 |
+
π‘ <strong>Quick Start:</strong> Head to <em>π Data</em> to upload your dataset,
|
| 398 |
+
then use <em>π€ ML Pipeline</em> to train models, or jump to <em>π§ Deep Learning</em>
|
| 399 |
+
for image analysis. Use <em>π‘ Generative AI</em> for automated insights.
|
| 400 |
+
</div>""", unsafe_allow_html=True)
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 404 |
+
# TAB 1 Β· DATA
|
| 405 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 406 |
+
with tab_data:
|
| 407 |
+
st.markdown('<div class="sec-head">π Data Loading & Exploration</div>', unsafe_allow_html=True)
|
| 408 |
+
|
| 409 |
+
col_up, col_sum = st.columns([2, 1])
|
| 410 |
+
with col_up:
|
| 411 |
+
uploaded_file = st.file_uploader(
|
| 412 |
+
"Upload CSV, Excel, JSON or Image",
|
| 413 |
+
type=["csv", "xlsx", "xls", "json", "png", "jpg", "jpeg", "bmp", "webp"],
|
| 414 |
+
)
|
| 415 |
+
if uploaded_file:
|
| 416 |
+
try:
|
| 417 |
+
if uploaded_file.type.startswith("image"):
|
| 418 |
+
img = Image.open(uploaded_file).convert("RGB")
|
| 419 |
+
st.image(img, caption=uploaded_file.name, width=420)
|
| 420 |
+
st.session_state.uploaded_image = img
|
| 421 |
+
else:
|
| 422 |
+
ext = Path(uploaded_file.name).suffix.lower()
|
| 423 |
+
if ext == ".csv":
|
| 424 |
+
df = pd.read_csv(uploaded_file)
|
| 425 |
+
elif ext in [".xlsx", ".xls"]:
|
| 426 |
+
df = pd.read_excel(uploaded_file)
|
| 427 |
+
elif ext == ".json":
|
| 428 |
+
df = pd.read_json(uploaded_file)
|
| 429 |
+
else:
|
| 430 |
+
df = pd.read_csv(uploaded_file)
|
| 431 |
+
|
| 432 |
+
st.session_state.df = df
|
| 433 |
+
st.session_state.data_summary = st.session_state.data_loader.get_data_summary(df)
|
| 434 |
+
st.success(f"β
Loaded **{uploaded_file.name}** β {df.shape[0]:,} rows Γ {df.shape[1]} cols")
|
| 435 |
+
except Exception as e:
|
| 436 |
+
st.error(f"β {e}")
|
| 437 |
+
|
| 438 |
+
if st.session_state.df is not None:
|
| 439 |
+
df = st.session_state.df
|
| 440 |
+
with col_sum:
|
| 441 |
+
st.markdown(f'<div class="stat-card" style="margin-bottom:8px"><div class="stat-val">{df.shape[0]:,}</div><div class="stat-lbl">Rows</div></div>', unsafe_allow_html=True)
|
| 442 |
+
st.markdown(f'<div class="stat-card" style="margin-bottom:8px"><div class="stat-val">{df.shape[1]}</div><div class="stat-lbl">Columns</div></div>', unsafe_allow_html=True)
|
| 443 |
+
st.markdown(f'<div class="stat-card"><div class="stat-val">{df.isnull().sum().sum()}</div><div class="stat-lbl">Missing</div></div>', unsafe_allow_html=True)
|
| 444 |
+
|
| 445 |
+
dtabs = st.tabs(["π Preview", "π Statistics", "π Charts", "π‘οΈ Correlations"])
|
| 446 |
+
|
| 447 |
+
with dtabs[0]:
|
| 448 |
+
st.dataframe(df.head(50), use_container_width=True)
|
| 449 |
+
|
| 450 |
+
with dtabs[1]:
|
| 451 |
+
st.dataframe(df.describe(include="all").T, use_container_width=True)
|
| 452 |
+
col_a, col_b = st.columns(2)
|
| 453 |
+
with col_a:
|
| 454 |
+
st.markdown("**Data Types**")
|
| 455 |
+
st.dataframe(df.dtypes.rename("Type").reset_index().rename(columns={"index": "Column"}), use_container_width=True)
|
| 456 |
+
with col_b:
|
| 457 |
+
st.markdown("**Missing Values**")
|
| 458 |
+
miss = df.isnull().sum().reset_index()
|
| 459 |
+
miss.columns = ["Column", "Missing"]
|
| 460 |
+
miss["Pct"] = (miss["Missing"] / len(df) * 100).round(2)
|
| 461 |
+
st.dataframe(miss[miss["Missing"] > 0], use_container_width=True)
|
| 462 |
+
|
| 463 |
+
with dtabs[2]:
|
| 464 |
+
import plotly.express as px
|
| 465 |
+
num_cols = df.select_dtypes(include=np.number).columns.tolist()
|
| 466 |
+
cat_cols = df.select_dtypes(include="object").columns.tolist()
|
| 467 |
+
chart_type = st.selectbox("Chart Type", ["Histogram", "Bar Chart", "Scatter", "Box Plot", "Line"])
|
| 468 |
+
|
| 469 |
+
if chart_type == "Histogram" and num_cols:
|
| 470 |
+
col = st.selectbox("Column", num_cols)
|
| 471 |
+
fig = px.histogram(df, x=col, template="plotly_dark", color_discrete_sequence=["#6366f1"])
|
| 472 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(15,18,35,0.6)")
|
| 473 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 474 |
+
elif chart_type == "Bar Chart" and cat_cols and num_cols:
|
| 475 |
+
xc = st.selectbox("X (categorical)", cat_cols)
|
| 476 |
+
yc = st.selectbox("Y (numeric)", num_cols)
|
| 477 |
+
fig = px.bar(df.groupby(xc)[yc].mean().reset_index(), x=xc, y=yc,
|
| 478 |
+
template="plotly_dark", color=yc, color_continuous_scale="Viridis")
|
| 479 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)")
|
| 480 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 481 |
+
elif chart_type == "Scatter" and len(num_cols) >= 2:
|
| 482 |
+
xc = st.selectbox("X", num_cols, key="sc_x")
|
| 483 |
+
yc = st.selectbox("Y", num_cols, index=1, key="sc_y")
|
| 484 |
+
cc = st.selectbox("Color", ["None"] + cat_cols)
|
| 485 |
+
fig = px.scatter(df, x=xc, y=yc, color=None if cc == "None" else cc,
|
| 486 |
+
template="plotly_dark", opacity=0.7)
|
| 487 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)")
|
| 488 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 489 |
+
elif chart_type == "Box Plot" and num_cols:
|
| 490 |
+
yc = st.selectbox("Value", num_cols, key="bp_y")
|
| 491 |
+
gc = st.selectbox("Group", ["None"] + cat_cols)
|
| 492 |
+
fig = px.box(df, y=yc, x=None if gc == "None" else gc,
|
| 493 |
+
template="plotly_dark", color_discrete_sequence=["#8b5cf6"])
|
| 494 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)")
|
| 495 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 496 |
+
elif chart_type == "Line" and num_cols:
|
| 497 |
+
yc = st.selectbox("Column", num_cols, key="lc_y")
|
| 498 |
+
fig = px.line(df.reset_index(), x="index", y=yc,
|
| 499 |
+
template="plotly_dark", color_discrete_sequence=["#2dd4bf"])
|
| 500 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)")
|
| 501 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 502 |
+
|
| 503 |
+
with dtabs[3]:
|
| 504 |
+
num_cols = df.select_dtypes(include=np.number).columns.tolist()
|
| 505 |
+
cat_cols = df.select_dtypes(include="object").columns.tolist()
|
| 506 |
+
if len(num_cols) >= 2:
|
| 507 |
+
fig = create_correlation_heatmap(df)
|
| 508 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 509 |
+
if cat_cols:
|
| 510 |
+
tc = st.selectbox("Class column for distribution", cat_cols)
|
| 511 |
+
fig2 = create_class_distribution(df[tc], f"{tc} Distribution")
|
| 512 |
+
st.plotly_chart(fig2, use_container_width=True)
|
| 513 |
+
else:
|
| 514 |
+
st.info("Need at least 2 numeric columns for correlation.")
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 518 |
+
# TAB 2 Β· ML PIPELINE
|
| 519 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 520 |
+
with tab_ml:
|
| 521 |
+
st.markdown('<div class="sec-head">π€ Machine Learning Pipeline</div>', unsafe_allow_html=True)
|
| 522 |
+
|
| 523 |
+
if st.session_state.df is None:
|
| 524 |
+
st.markdown('<div class="info-box">π Load a dataset in the <strong>π Data</strong> tab first.</div>', unsafe_allow_html=True)
|
| 525 |
+
else:
|
| 526 |
+
df = st.session_state.df
|
| 527 |
+
|
| 528 |
+
mc1, mc2, mc3 = st.columns(3)
|
| 529 |
+
with mc1:
|
| 530 |
+
target_col = st.selectbox("π― Target Column", df.columns.tolist())
|
| 531 |
+
st.session_state.target_column = target_col
|
| 532 |
+
with mc2:
|
| 533 |
+
from data.data_loader import DataLoader as _DL
|
| 534 |
+
task_type = _DL().detect_task_type(df[target_col])
|
| 535 |
+
st.markdown(f"**Detected Task:** `{task_type}`")
|
| 536 |
+
model_options = {
|
| 537 |
+
"classification": ["Random Forest", "Gradient Boosting", "Logistic Regression", "SVM", "XGBoost", "LightGBM", "Ensemble"],
|
| 538 |
+
"regression": ["Random Forest", "Gradient Boosting", "Ridge Regression", "Lasso Regression", "SVM", "XGBoost", "LightGBM", "Ensemble"],
|
| 539 |
+
}
|
| 540 |
+
model_name = st.selectbox("π§ Algorithm", model_options[task_type])
|
| 541 |
+
with mc3:
|
| 542 |
+
test_size = st.slider("Test Split %", 10, 40, 20) / 100
|
| 543 |
+
cv_folds = st.slider("CV Folds", 2, 10, 5)
|
| 544 |
+
|
| 545 |
+
if st.button("π Train Model", type="primary", use_container_width=True):
|
| 546 |
+
with st.spinner("Trainingβ¦"):
|
| 547 |
+
try:
|
| 548 |
+
if model_name == "XGBoost":
|
| 549 |
+
pipe = XGBoostPipeline(task_type=task_type)
|
| 550 |
+
elif model_name == "LightGBM" and LGB_OK and LightGBMPipeline is not None:
|
| 551 |
+
pipe = LightGBMPipeline(task_type=task_type)
|
| 552 |
+
elif model_name == "Ensemble":
|
| 553 |
+
pipe = EnsemblePipeline(task_type=task_type)
|
| 554 |
+
else:
|
| 555 |
+
pipe = MLPipeline(task_type=task_type, model_name=model_name)
|
| 556 |
+
|
| 557 |
+
result = pipe.preprocess(df, target_col=target_col)
|
| 558 |
+
if result is None:
|
| 559 |
+
st.error("β Preprocessing returned None. Check your target column.")
|
| 560 |
+
st.stop()
|
| 561 |
+
X, y = result
|
| 562 |
+
if y is None:
|
| 563 |
+
st.error("β Target column could not be extracted.")
|
| 564 |
+
st.stop()
|
| 565 |
+
metrics = pipe.train(X, y, test_size=test_size)
|
| 566 |
+
|
| 567 |
+
st.session_state.ml_pipeline = pipe
|
| 568 |
+
st.session_state.ml_metrics = metrics
|
| 569 |
+
st.session_state.ml_results = {
|
| 570 |
+
"model_name": model_name,
|
| 571 |
+
"task_type": task_type,
|
| 572 |
+
"feature_importance": pipe.get_feature_importance().to_dict("records")
|
| 573 |
+
if hasattr(pipe, "get_feature_importance") else [],
|
| 574 |
+
}
|
| 575 |
+
st.success(f"β
**{model_name}** trained successfully!")
|
| 576 |
+
except Exception as e:
|
| 577 |
+
st.error(f"β Training failed: {e}")
|
| 578 |
+
|
| 579 |
+
if st.session_state.ml_metrics:
|
| 580 |
+
metrics = st.session_state.ml_metrics
|
| 581 |
+
st.markdown("---")
|
| 582 |
+
st.markdown('<div class="sec-head">π Results</div>', unsafe_allow_html=True)
|
| 583 |
+
|
| 584 |
+
num_m = {k: v for k, v in metrics.items() if isinstance(v, (int, float)) and not isinstance(v, bool)}
|
| 585 |
+
cols_m = st.columns(min(len(num_m), 4))
|
| 586 |
+
for i, (k, v) in enumerate(list(num_m.items())[:4]):
|
| 587 |
+
cols_m[i].metric(k.replace("_", " ").title(), f"{v:.4f}" if isinstance(v, float) else str(v))
|
| 588 |
+
|
| 589 |
+
fig_dash = create_metrics_dashboard(metrics)
|
| 590 |
+
st.plotly_chart(fig_dash, use_container_width=True)
|
| 591 |
+
|
| 592 |
+
pipe = st.session_state.ml_pipeline
|
| 593 |
+
if pipe and pipe.is_fitted:
|
| 594 |
+
result_tabs = st.tabs(["π Confusion Matrix / Scatter", "π Feature Importance", "π Report"])
|
| 595 |
+
|
| 596 |
+
with result_tabs[0]:
|
| 597 |
+
task_type = st.session_state.ml_results["task_type"]
|
| 598 |
+
if task_type == "classification" and pipe.y_pred is not None:
|
| 599 |
+
labels = [str(c) for c in pipe.classes_] if pipe.classes_ is not None else None
|
| 600 |
+
fig_cm = create_confusion_matrix(
|
| 601 |
+
list(pipe.y_test), # type: ignore[arg-type]
|
| 602 |
+
list(pipe.y_pred), # type: ignore[arg-type]
|
| 603 |
+
labels,
|
| 604 |
+
)
|
| 605 |
+
st.plotly_chart(fig_cm, use_container_width=True)
|
| 606 |
+
elif task_type == "regression" and pipe.y_pred is not None:
|
| 607 |
+
fig_avp = create_actual_vs_predicted(
|
| 608 |
+
pipe.y_test, pipe.y_pred,
|
| 609 |
+
f"{st.session_state.ml_results['model_name']} β Actual vs Predicted"
|
| 610 |
+
)
|
| 611 |
+
st.plotly_chart(fig_avp, use_container_width=True)
|
| 612 |
+
|
| 613 |
+
with result_tabs[1]:
|
| 614 |
+
if hasattr(pipe, "get_feature_importance"):
|
| 615 |
+
fi_df = pipe.get_feature_importance()
|
| 616 |
+
if not fi_df.empty:
|
| 617 |
+
fig_fi = create_feature_importance_chart(fi_df, top_n=20)
|
| 618 |
+
st.plotly_chart(fig_fi, use_container_width=True)
|
| 619 |
+
st.dataframe(fi_df.head(20), use_container_width=True)
|
| 620 |
+
|
| 621 |
+
with result_tabs[2]:
|
| 622 |
+
if "classification_report" in metrics:
|
| 623 |
+
st.code(metrics["classification_report"], language="text")
|
| 624 |
+
else:
|
| 625 |
+
for k, v in num_m.items():
|
| 626 |
+
st.markdown(f"**{k.replace('_', ' ').title()}:** `{v:.4f}`")
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 630 |
+
# TAB 3 Β· DEEP LEARNING
|
| 631 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 632 |
+
with tab_dl:
|
| 633 |
+
st.markdown('<div class="sec-head">π§ Deep Learning & Computer Vision</div>', unsafe_allow_html=True)
|
| 634 |
+
|
| 635 |
+
from models.dl_module import (
|
| 636 |
+
_classify_image_tf, _classify_image_torch,
|
| 637 |
+
detect_edges_opencv, detect_faces_opencv, apply_image_filters,
|
| 638 |
+
)
|
| 639 |
+
|
| 640 |
+
dl_uploaded = st.file_uploader("π· Upload Image (JPG / PNG)", type=["jpg", "jpeg", "png"], key="dl_up")
|
| 641 |
+
|
| 642 |
+
if dl_uploaded is None:
|
| 643 |
+
st.markdown('<div class="info-box">π Upload any image β try animals, faces, objects, or landscapes.</div>', unsafe_allow_html=True)
|
| 644 |
+
else:
|
| 645 |
+
pil_img = Image.open(dl_uploaded)
|
| 646 |
+
c1, c2 = st.columns([2, 1])
|
| 647 |
+
with c1:
|
| 648 |
+
st.image(pil_img, caption="Uploaded Image", use_column_width=True)
|
| 649 |
+
with c2:
|
| 650 |
+
w, h = pil_img.size
|
| 651 |
+
arr_np = np.array(pil_img.convert("RGB"))
|
| 652 |
+
for lbl, val in [
|
| 653 |
+
("Resolution", f"{w}Γ{h}"),
|
| 654 |
+
("Mode", pil_img.mode),
|
| 655 |
+
("Brightness", f"{arr_np.mean():.1f}"),
|
| 656 |
+
("File size", f"{dl_uploaded.size / 1024:.1f} KB"),
|
| 657 |
+
]:
|
| 658 |
+
st.markdown(
|
| 659 |
+
f'<div class="stat-card" style="margin-bottom:6px">'
|
| 660 |
+
f'<div class="stat-val" style="font-size:1rem">{val}</div>'
|
| 661 |
+
f'<div class="stat-lbl">{lbl}</div></div>',
|
| 662 |
+
unsafe_allow_html=True,
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
st.markdown("---")
|
| 666 |
+
dl_tabs = st.tabs(["π·οΈ Classification", "π₯ Grad-CAM", "ποΈ Detection", "π¨ Filters"])
|
| 667 |
+
|
| 668 |
+
with dl_tabs[0]:
|
| 669 |
+
st.subheader("Image Classification β ImageNet 1K")
|
| 670 |
+
backend = st.radio("Backend", ["TensorFlow/Keras", "PyTorch"], horizontal=True)
|
| 671 |
+
if backend == "TensorFlow/Keras":
|
| 672 |
+
model_choice = st.selectbox("Model", ["MobileNetV2", "ResNet50", "VGG16"])
|
| 673 |
+
else:
|
| 674 |
+
model_choice = st.selectbox("Model", ["MobileNetV2", "ResNet50"])
|
| 675 |
+
|
| 676 |
+
if st.button("π Classify Image", type="primary", key="cls_btn"):
|
| 677 |
+
with st.spinner(f"Running {model_choice}β¦"):
|
| 678 |
+
try:
|
| 679 |
+
# Verify TF is importable before calling classify
|
| 680 |
+
if backend == "TensorFlow/Keras":
|
| 681 |
+
try:
|
| 682 |
+
import tensorflow as _tf_check # type: ignore[import-untyped]
|
| 683 |
+
except ImportError:
|
| 684 |
+
raise ImportError("TensorFlow not installed. Run: pip install tensorflow")
|
| 685 |
+
results = _classify_image_tf(pil_img, model_choice)
|
| 686 |
+
else:
|
| 687 |
+
results = _classify_image_torch(pil_img, model_choice)
|
| 688 |
+
|
| 689 |
+
import plotly.graph_objects as go
|
| 690 |
+
st.markdown(
|
| 691 |
+
f'<div class="success-box">π <strong>{results[0]["Label"]}</strong> β {results[0]["Confidence"]}</div>',
|
| 692 |
+
unsafe_allow_html=True,
|
| 693 |
+
)
|
| 694 |
+
st.dataframe(pd.DataFrame(results), use_container_width=True)
|
| 695 |
+
|
| 696 |
+
labels_list = [r["Label"][:28] for r in results]
|
| 697 |
+
scores_list = [r["Score"] for r in results]
|
| 698 |
+
colors_list = ["#a78bfa" if i == 0 else "#334155" for i in range(len(scores_list))]
|
| 699 |
+
fig = go.Figure(go.Bar(
|
| 700 |
+
x=scores_list[::-1], y=labels_list[::-1], orientation="h",
|
| 701 |
+
marker=dict(color=colors_list[::-1]),
|
| 702 |
+
text=[f"{s * 100:.1f}%" for s in scores_list[::-1]],
|
| 703 |
+
textposition="outside",
|
| 704 |
+
))
|
| 705 |
+
fig.update_layout(title="Top-5 Confidence Scores", template="plotly_dark",
|
| 706 |
+
paper_bgcolor="rgba(0,0,0,0)", height=280)
|
| 707 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 708 |
+
except Exception as e:
|
| 709 |
+
st.error(f"Classification failed: {e}")
|
| 710 |
+
st.info("Make sure TensorFlow or PyTorch is installed.")
|
| 711 |
+
|
| 712 |
+
with dl_tabs[1]:
|
| 713 |
+
st.subheader("Grad-CAM β Class Activation Heatmap")
|
| 714 |
+
st.markdown("Highlights **which image regions** the model focused on for its prediction.")
|
| 715 |
+
|
| 716 |
+
_tf_ok, _pt_ok = False, False
|
| 717 |
+
try:
|
| 718 |
+
import tensorflow; _tf_ok = True
|
| 719 |
+
except ImportError:
|
| 720 |
+
pass
|
| 721 |
+
try:
|
| 722 |
+
import torch; _pt_ok = True
|
| 723 |
+
except ImportError:
|
| 724 |
+
pass
|
| 725 |
+
|
| 726 |
+
if not _tf_ok and not _pt_ok:
|
| 727 |
+
st.markdown("""
|
| 728 |
+
<div class="info-box">
|
| 729 |
+
β οΈ Grad-CAM requires TensorFlow or PyTorch.<br>
|
| 730 |
+
Install: <code>pip install tensorflow</code> or <code>pip install torch torchvision</code>
|
| 731 |
+
</div>""", unsafe_allow_html=True)
|
| 732 |
+
else:
|
| 733 |
+
backend_opts = (["TensorFlow/Keras"] if _tf_ok else []) + (["PyTorch"] if _pt_ok else [])
|
| 734 |
+
gc_backend = st.radio("Backend", backend_opts, horizontal=True, key="gc_back")
|
| 735 |
+
gc_model = st.selectbox(
|
| 736 |
+
"Model",
|
| 737 |
+
["MobileNetV2", "ResNet50"] if gc_backend == "PyTorch" else ["MobileNetV2", "ResNet50", "VGG16"],
|
| 738 |
+
key="gc_m",
|
| 739 |
+
)
|
| 740 |
+
|
| 741 |
+
if st.button("π₯ Generate Grad-CAM", type="primary"):
|
| 742 |
+
with st.spinner("Computing Grad-CAMβ¦"):
|
| 743 |
+
try:
|
| 744 |
+
import cv2 as _cv
|
| 745 |
+
import numpy as _np
|
| 746 |
+
|
| 747 |
+
orig_224 = _np.array(pil_img.convert("RGB").resize((224, 224)))
|
| 748 |
+
|
| 749 |
+
if gc_backend == "TensorFlow/Keras":
|
| 750 |
+
# Robust TensorFlow import β handles tf2, tf-cpu, standalone keras
|
| 751 |
+
try:
|
| 752 |
+
import tensorflow as tf # type: ignore[import-untyped]
|
| 753 |
+
except ImportError:
|
| 754 |
+
st.error("β TensorFlow not installed. Run: pip install tensorflow")
|
| 755 |
+
st.stop()
|
| 756 |
+
try:
|
| 757 |
+
from tensorflow.keras.preprocessing.image import img_to_array # type: ignore[import-untyped]
|
| 758 |
+
except ImportError:
|
| 759 |
+
try:
|
| 760 |
+
from tensorflow.keras.utils import img_to_array # type: ignore[import-untyped]
|
| 761 |
+
except ImportError:
|
| 762 |
+
from keras.utils import img_to_array # type: ignore[import-untyped]
|
| 763 |
+
try:
|
| 764 |
+
from models.dl_module import _load_tf_model
|
| 765 |
+
except ImportError:
|
| 766 |
+
from dl_module import _load_tf_model # type: ignore[import]
|
| 767 |
+
|
| 768 |
+
model, preprocess, decode, (ih, iw) = _load_tf_model(gc_model)
|
| 769 |
+
arr = preprocess(_np.expand_dims(img_to_array(
|
| 770 |
+
pil_img.convert("RGB").resize((iw, ih))), 0))
|
| 771 |
+
|
| 772 |
+
layer_name = None
|
| 773 |
+
try:
|
| 774 |
+
_conv2d_cls = tf.keras.layers.Conv2D # type: ignore[attr-defined]
|
| 775 |
+
except AttributeError:
|
| 776 |
+
import keras
|
| 777 |
+
_conv2d_cls = keras.layers.Conv2D # type: ignore[attr-defined]
|
| 778 |
+
for layer in reversed(model.layers):
|
| 779 |
+
if isinstance(layer, _conv2d_cls):
|
| 780 |
+
layer_name = layer.name
|
| 781 |
+
break
|
| 782 |
+
|
| 783 |
+
if not layer_name:
|
| 784 |
+
st.warning("No Conv2D layer found in model.")
|
| 785 |
+
st.stop()
|
| 786 |
+
|
| 787 |
+
grad_model = tf.keras.models.Model(
|
| 788 |
+
inputs=model.inputs,
|
| 789 |
+
outputs=[model.get_layer(layer_name).output, model.output],
|
| 790 |
+
)
|
| 791 |
+
with tf.GradientTape() as tape:
|
| 792 |
+
conv_out, preds = grad_model(arr)
|
| 793 |
+
top_cls = int(tf.argmax(preds[0]).numpy()) # type: ignore[union-attr]
|
| 794 |
+
loss = preds[:, top_cls] # type: ignore[index]
|
| 795 |
+
grads = tape.gradient(loss, conv_out)
|
| 796 |
+
pooled = tf.reduce_mean(grads, axis=(0, 1, 2))
|
| 797 |
+
heatmap = conv_out[0] @ pooled[..., tf.newaxis]
|
| 798 |
+
heatmap = tf.squeeze(heatmap).numpy()
|
| 799 |
+
top_label = decode(model.predict(arr, verbose=0), top=1)[0][0][1].replace("_", " ").title()
|
| 800 |
+
|
| 801 |
+
else: # PyTorch
|
| 802 |
+
import torch
|
| 803 |
+
import torchvision.models as M
|
| 804 |
+
import torchvision.transforms as T
|
| 805 |
+
import json, urllib.request
|
| 806 |
+
|
| 807 |
+
pt_map = {"MobileNetV2": M.mobilenet_v2, "ResNet50": M.resnet50}
|
| 808 |
+
pt_model = pt_map.get(gc_model, M.mobilenet_v2)(weights="DEFAULT")
|
| 809 |
+
pt_model.eval()
|
| 810 |
+
|
| 811 |
+
last_conv = None
|
| 812 |
+
for name, m in pt_model.named_modules():
|
| 813 |
+
if isinstance(m, torch.nn.Conv2d):
|
| 814 |
+
last_conv = (name, m)
|
| 815 |
+
|
| 816 |
+
if last_conv is None:
|
| 817 |
+
st.warning("No Conv2d layer found.")
|
| 818 |
+
st.stop()
|
| 819 |
+
|
| 820 |
+
activations, gradients = [], []
|
| 821 |
+
|
| 822 |
+
def _fwd_hook(mod, inp, out):
|
| 823 |
+
activations.clear(); activations.append(out.detach())
|
| 824 |
+
|
| 825 |
+
def _bwd_hook(mod, grad_in, grad_out):
|
| 826 |
+
gradients.clear(); gradients.append(grad_out[0].detach())
|
| 827 |
+
|
| 828 |
+
h1 = last_conv[1].register_forward_hook(_fwd_hook)
|
| 829 |
+
h2 = last_conv[1].register_full_backward_hook(_bwd_hook)
|
| 830 |
+
|
| 831 |
+
_transform = T.Compose([
|
| 832 |
+
T.Resize(256), T.CenterCrop(224), T.ToTensor(),
|
| 833 |
+
T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
|
| 834 |
+
])
|
| 835 |
+
_tf_raw = _transform(pil_img.convert("RGB"))
|
| 836 |
+
tf_img = _tf_raw.unsqueeze(0) # torch Tensor β ignore PIL Image complaint # type: ignore[union-attr]
|
| 837 |
+
tf_img.requires_grad_(True)
|
| 838 |
+
|
| 839 |
+
output = pt_model(tf_img)
|
| 840 |
+
top_cls_pt = output.argmax(dim=1).item()
|
| 841 |
+
pt_model.zero_grad()
|
| 842 |
+
output[0, top_cls_pt].backward()
|
| 843 |
+
h1.remove(); h2.remove()
|
| 844 |
+
|
| 845 |
+
act = activations[0].squeeze()
|
| 846 |
+
grad = gradients[0].squeeze()
|
| 847 |
+
weights = grad.mean(dim=(1, 2))
|
| 848 |
+
heatmap = (weights[:, None, None] * act).sum(dim=0).numpy()
|
| 849 |
+
heatmap = _np.maximum(heatmap, 0)
|
| 850 |
+
|
| 851 |
+
try:
|
| 852 |
+
url = "https://raw.githubusercontent.com/anishathalye/imagenet-simple-labels/master/imagenet-simple-labels.json"
|
| 853 |
+
with urllib.request.urlopen(url, timeout=5) as r:
|
| 854 |
+
class_labels = json.load(r)
|
| 855 |
+
top_label = class_labels[top_cls_pt].replace("_", " ").title()
|
| 856 |
+
except Exception:
|
| 857 |
+
top_label = f"Class {top_cls_pt}"
|
| 858 |
+
|
| 859 |
+
# ββ Render heatmap ββ
|
| 860 |
+
heatmap = heatmap / (heatmap.max() + 1e-8)
|
| 861 |
+
h_res = _cv.resize(heatmap, (224, 224))
|
| 862 |
+
_h_uint8 = _np.array(255 * h_res, dtype=_np.uint8)
|
| 863 |
+
h_col = _cv.cvtColor(
|
| 864 |
+
_cv.applyColorMap(_h_uint8, _cv.COLORMAP_JET), # type: ignore[call-overload]
|
| 865 |
+
_cv.COLOR_BGR2RGB,
|
| 866 |
+
)
|
| 867 |
+
overlay = _np.array(orig_224 * 0.6 + h_col * 0.4, dtype=_np.uint8)
|
| 868 |
+
|
| 869 |
+
gc1, gc2, gc3 = st.columns(3)
|
| 870 |
+
gc1.image(orig_224, caption="Original", use_column_width=True)
|
| 871 |
+
gc2.image(h_col, caption="Heatmap", use_column_width=True)
|
| 872 |
+
gc3.image(overlay, caption="Overlay", use_column_width=True)
|
| 873 |
+
st.markdown(
|
| 874 |
+
f'<div class="success-box">π Top prediction: <strong>{top_label}</strong> '
|
| 875 |
+
f'β red/yellow regions = highest model attention</div>',
|
| 876 |
+
unsafe_allow_html=True,
|
| 877 |
+
)
|
| 878 |
+
|
| 879 |
+
except Exception as e:
|
| 880 |
+
st.error(f"Grad-CAM failed: {e}")
|
| 881 |
+
|
| 882 |
+
with dl_tabs[2]:
|
| 883 |
+
st.subheader("OpenCV Detection")
|
| 884 |
+
cv_task = st.selectbox("Task", ["Face Detection", "Edge Detection"])
|
| 885 |
+
t1 = t2 = None
|
| 886 |
+
if cv_task == "Edge Detection":
|
| 887 |
+
t1 = st.slider("Threshold 1", 10, 200, 50)
|
| 888 |
+
t2 = st.slider("Threshold 2", 50, 400, 150)
|
| 889 |
+
|
| 890 |
+
if st.button("βΆ Run Detection", type="primary", key="det_btn"):
|
| 891 |
+
with st.spinner("Running OpenCVβ¦"):
|
| 892 |
+
if cv_task == "Edge Detection":
|
| 893 |
+
import cv2 as _cv
|
| 894 |
+
_t1: float = float(t1) if t1 is not None else 50.0
|
| 895 |
+
_t2: float = float(t2) if t2 is not None else 150.0
|
| 896 |
+
gray = _cv.cvtColor(np.array(pil_img.convert("RGB")), _cv.COLOR_RGB2GRAY)
|
| 897 |
+
edges = _cv.Canny(_cv.GaussianBlur(gray, (5, 5), 0), _t1, _t2)
|
| 898 |
+
dc1, dc2 = st.columns(2)
|
| 899 |
+
dc1.image(pil_img, caption="Original", use_column_width=True)
|
| 900 |
+
dc2.image(edges, caption="Edges", use_column_width=True, clamp=True)
|
| 901 |
+
st.info(f"Edge pixels: **{np.sum(edges > 0):,}**")
|
| 902 |
+
else:
|
| 903 |
+
result_img, face_count = detect_faces_opencv(pil_img)
|
| 904 |
+
dc1, dc2 = st.columns(2)
|
| 905 |
+
dc1.image(pil_img, caption="Original", use_column_width=True)
|
| 906 |
+
dc2.image(result_img, caption="Detections", use_column_width=True)
|
| 907 |
+
if face_count > 0:
|
| 908 |
+
st.markdown(f'<div class="success-box">β
Detected <strong>{face_count}</strong> face(s).</div>', unsafe_allow_html=True)
|
| 909 |
+
else:
|
| 910 |
+
st.warning("No faces detected. Try a clear frontal portrait.")
|
| 911 |
+
|
| 912 |
+
with dl_tabs[3]:
|
| 913 |
+
st.subheader("Image Filters Gallery")
|
| 914 |
+
if st.button("π¨ Apply All Filters", type="primary", key="flt_btn"):
|
| 915 |
+
with st.spinner("Applying filtersβ¦"):
|
| 916 |
+
filters = apply_image_filters(pil_img)
|
| 917 |
+
cols_f = st.columns(3)
|
| 918 |
+
for i, (name, img) in enumerate(filters.items()):
|
| 919 |
+
with cols_f[i % 3]:
|
| 920 |
+
st.image(img, caption=name, use_column_width=True, clamp=True)
|
| 921 |
+
|
| 922 |
+
|
| 923 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 924 |
+
# TAB 4 Β· NLP
|
| 925 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 926 |
+
with tab_nlp:
|
| 927 |
+
st.markdown('<div class="sec-head">π NLP Suite</div>', unsafe_allow_html=True)
|
| 928 |
+
|
| 929 |
+
try:
|
| 930 |
+
from models.nlp_module import ( # type: ignore[import]
|
| 931 |
+
run_sentiment, run_ner, run_text_classification,
|
| 932 |
+
run_summarization, chat_with_model,
|
| 933 |
+
)
|
| 934 |
+
except ImportError:
|
| 935 |
+
from nlp_module import ( # type: ignore[import]
|
| 936 |
+
run_sentiment, run_ner, run_text_classification,
|
| 937 |
+
run_summarization, chat_with_model,
|
| 938 |
+
)
|
| 939 |
+
|
| 940 |
+
nlp_tabs = st.tabs(["π Sentiment", "π·οΈ NER", "π Classification", "π° Summarization", "π¬ Chatbot"])
|
| 941 |
+
|
| 942 |
+
# ββ Sentiment ββ
|
| 943 |
+
with nlp_tabs[0]:
|
| 944 |
+
st.subheader("Sentiment Analysis β DistilBERT")
|
| 945 |
+
mode = st.radio("Mode", ["Single", "Batch"], horizontal=True)
|
| 946 |
+
if mode == "Single":
|
| 947 |
+
txt = st.text_area("Text to analyze:", height=110,
|
| 948 |
+
placeholder="The product quality is amazing and delivery was super fast!")
|
| 949 |
+
if st.button("π Analyze", type="primary", key="sa_btn"):
|
| 950 |
+
if not txt.strip():
|
| 951 |
+
st.warning("Enter some text.")
|
| 952 |
+
else:
|
| 953 |
+
with st.spinner("Loading DistilBERT and analyzingβ¦"):
|
| 954 |
+
r = run_sentiment([txt])
|
| 955 |
+
if r:
|
| 956 |
+
color = "#22c55e" if r[0]["Sentiment"] == "POSITIVE" else "#ef4444"
|
| 957 |
+
icon = "π" if r[0]["Sentiment"] == "POSITIVE" else "π"
|
| 958 |
+
st.markdown(f"""
|
| 959 |
+
<div class="result-box" style="border-left:5px solid {color}">
|
| 960 |
+
<div style="font-size:2rem">{icon}</div>
|
| 961 |
+
<div style="font-size:1.4rem;font-weight:700;color:{color}">{r[0]["Sentiment"]}</div>
|
| 962 |
+
<div style="color:#94a3b8;margin-top:6px">Confidence: <strong style="color:#e2e8f0">{r[0]["Confidence"]}</strong></div>
|
| 963 |
+
</div>""", unsafe_allow_html=True)
|
| 964 |
+
else:
|
| 965 |
+
batch = st.text_area("One sentence per line:", height=180,
|
| 966 |
+
placeholder="Great product!\nTerrible experience.\nIt was okay.")
|
| 967 |
+
if st.button("π Analyze All", type="primary", key="sa_batch"):
|
| 968 |
+
lines = [ln.strip() for ln in batch.split("\n") if ln.strip()]
|
| 969 |
+
if lines:
|
| 970 |
+
with st.spinner("Analyzingβ¦"):
|
| 971 |
+
results = run_sentiment(lines)
|
| 972 |
+
df_s = pd.DataFrame(results)
|
| 973 |
+
st.dataframe(df_s, use_container_width=True)
|
| 974 |
+
import plotly.express as px
|
| 975 |
+
pos = sum(1 for r in results if r["Sentiment"] == "POSITIVE")
|
| 976 |
+
fig = px.pie(
|
| 977 |
+
values=[pos, len(results) - pos],
|
| 978 |
+
names=["Positive", "Negative"],
|
| 979 |
+
color_discrete_sequence=["#22c55e", "#ef4444"],
|
| 980 |
+
hole=0.4, template="plotly_dark",
|
| 981 |
+
)
|
| 982 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)")
|
| 983 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 984 |
+
|
| 985 |
+
# ββ NER ββ
|
| 986 |
+
with nlp_tabs[1]:
|
| 987 |
+
st.subheader("Named Entity Recognition")
|
| 988 |
+
ner_txt = st.text_area("Text for NER:", height=130,
|
| 989 |
+
value="Apple Inc. was founded by Steve Jobs in Cupertino, California in 1976.")
|
| 990 |
+
if st.button("π·οΈ Extract Entities", type="primary", key="ner_btn"):
|
| 991 |
+
with st.spinner("Running NERβ¦"):
|
| 992 |
+
ents = run_ner(ner_txt)
|
| 993 |
+
if ents:
|
| 994 |
+
df_ner = pd.DataFrame(ents)
|
| 995 |
+
st.dataframe(df_ner, use_container_width=True)
|
| 996 |
+
import plotly.express as px
|
| 997 |
+
vc = df_ner["Type"].value_counts().reset_index()
|
| 998 |
+
vc.columns = ["Type", "count"]
|
| 999 |
+
fig = px.bar(vc, x="Type", y="count", template="plotly_dark",
|
| 1000 |
+
color="Type", color_discrete_sequence=["#a78bfa", "#60a5fa", "#34d399", "#f472b6"])
|
| 1001 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)", showlegend=False)
|
| 1002 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 1003 |
+
type_icons = {"PER": "π§", "ORG": "π’", "LOC": "π", "MISC": "π", "GPE": "π"}
|
| 1004 |
+
for _, row in df_ner.iterrows():
|
| 1005 |
+
st.markdown(f"- **{row['Entity']}** β {type_icons.get(row['Type'], '')} `{row['Type']}` ({row['Score']})")
|
| 1006 |
+
else:
|
| 1007 |
+
st.info("No entities found.")
|
| 1008 |
+
|
| 1009 |
+
# ββ Zero-Shot Classification ββ
|
| 1010 |
+
with nlp_tabs[2]:
|
| 1011 |
+
st.subheader("Zero-Shot Text Classification")
|
| 1012 |
+
cl_txt = st.text_area("Text to classify:", height=110,
|
| 1013 |
+
value="The new iPhone features an upgraded camera and faster processor.")
|
| 1014 |
+
cl_labels = st.text_input("Candidate labels (comma-separated):",
|
| 1015 |
+
value="technology, sports, politics, business, health, entertainment")
|
| 1016 |
+
if st.button("π Classify", type="primary", key="zs_btn"):
|
| 1017 |
+
lbls = [lb.strip() for lb in cl_labels.split(",") if lb.strip()]
|
| 1018 |
+
if cl_txt.strip() and lbls:
|
| 1019 |
+
with st.spinner("Running zero-shot classificationβ¦"):
|
| 1020 |
+
results = run_text_classification(cl_txt, lbls)
|
| 1021 |
+
st.markdown(
|
| 1022 |
+
f'<div class="success-box">π Best: <strong>{results[0]["Label"]}</strong> ({results[0]["Confidence"]})</div>',
|
| 1023 |
+
unsafe_allow_html=True,
|
| 1024 |
+
)
|
| 1025 |
+
import plotly.express as px
|
| 1026 |
+
df_zs = pd.DataFrame(results)
|
| 1027 |
+
fig = px.bar(df_zs, x="Label", y="Score", template="plotly_dark",
|
| 1028 |
+
color="Score", color_continuous_scale="Purples")
|
| 1029 |
+
fig.update_layout(paper_bgcolor="rgba(0,0,0,0)")
|
| 1030 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 1031 |
+
|
| 1032 |
+
# ββ Summarization ββ
|
| 1033 |
+
with nlp_tabs[3]:
|
| 1034 |
+
st.subheader("Text Summarization β DistilBART")
|
| 1035 |
+
long_txt = st.text_area("Long text to summarize:", height=220,
|
| 1036 |
+
value=(
|
| 1037 |
+
"Artificial intelligence (AI) is intelligence demonstrated by machines, as opposed to "
|
| 1038 |
+
"the natural intelligence displayed by animals including humans. AI research has been defined "
|
| 1039 |
+
"as the field of study of intelligent agents, which refers to any system that perceives its "
|
| 1040 |
+
"environment and takes actions that maximize its chance of achieving its goals. AI applications "
|
| 1041 |
+
"include advanced web search engines, recommendation systems, understanding human speech, "
|
| 1042 |
+
"self-driving cars, generative or creative tools, automated decision-making, and competing "
|
| 1043 |
+
"at the highest level in strategic game systems."
|
| 1044 |
+
))
|
| 1045 |
+
if st.button("π° Summarize", type="primary", key="sum_btn"):
|
| 1046 |
+
if len(long_txt.split()) < 30:
|
| 1047 |
+
st.warning("Need at least 30 words.")
|
| 1048 |
+
else:
|
| 1049 |
+
with st.spinner("Loading DistilBART and summarizingβ¦"):
|
| 1050 |
+
summary = run_summarization(long_txt)
|
| 1051 |
+
st.markdown(f'<div class="result-box">{summary}</div>', unsafe_allow_html=True)
|
| 1052 |
+
sc1, sc2 = st.columns(2)
|
| 1053 |
+
sc1.metric("Original Words", len(long_txt.split()))
|
| 1054 |
+
sc2.metric("Summary Words", len(summary.split()))
|
| 1055 |
+
|
| 1056 |
+
# ββ Chatbot ββ
|
| 1057 |
+
with nlp_tabs[4]:
|
| 1058 |
+
st.subheader("π¬ AI Chatbot")
|
| 1059 |
+
if "chat_pairs" not in st.session_state:
|
| 1060 |
+
st.session_state.chat_pairs = []
|
| 1061 |
+
|
| 1062 |
+
with st.expander("βοΈ Settings"):
|
| 1063 |
+
sys_hint = st.text_input("System hint:", value="You are a helpful AI assistant. Be concise.")
|
| 1064 |
+
if st.button("ποΈ Clear Chat"):
|
| 1065 |
+
st.session_state.chat_pairs = []
|
| 1066 |
+
st.rerun()
|
| 1067 |
+
|
| 1068 |
+
for um, bm in st.session_state.chat_pairs:
|
| 1069 |
+
with st.chat_message("user"):
|
| 1070 |
+
st.markdown(um)
|
| 1071 |
+
with st.chat_message("assistant"):
|
| 1072 |
+
st.markdown(bm)
|
| 1073 |
+
|
| 1074 |
+
user_input = st.chat_input("Ask anythingβ¦")
|
| 1075 |
+
if user_input:
|
| 1076 |
+
with st.chat_message("user"):
|
| 1077 |
+
st.markdown(user_input)
|
| 1078 |
+
with st.chat_message("assistant"):
|
| 1079 |
+
with st.spinner("Thinkingβ¦"):
|
| 1080 |
+
try:
|
| 1081 |
+
prompt = f"{sys_hint}\n\n{user_input}" if sys_hint else user_input
|
| 1082 |
+
resp = chat_with_model(prompt, st.session_state.chat_pairs)
|
| 1083 |
+
except Exception as e:
|
| 1084 |
+
resp = f"β οΈ {e}"
|
| 1085 |
+
st.markdown(resp)
|
| 1086 |
+
st.session_state.chat_pairs.append((user_input, resp))
|
| 1087 |
+
|
| 1088 |
+
if not st.session_state.chat_pairs:
|
| 1089 |
+
examples = [
|
| 1090 |
+
"What is machine learning?", "Explain neural networks simply.",
|
| 1091 |
+
"Top 3 AI programming languages?", "Benefits of deep learning?",
|
| 1092 |
+
]
|
| 1093 |
+
ecols = st.columns(2)
|
| 1094 |
+
for i, ex in enumerate(examples):
|
| 1095 |
+
with ecols[i % 2]:
|
| 1096 |
+
if st.button(ex, key=f"ex_{i}", use_container_width=True):
|
| 1097 |
+
with st.spinner("Thinkingβ¦"):
|
| 1098 |
+
try:
|
| 1099 |
+
resp = chat_with_model(ex, [])
|
| 1100 |
+
except Exception as e:
|
| 1101 |
+
resp = f"β οΈ {e}"
|
| 1102 |
+
st.session_state.chat_pairs.append((ex, resp))
|
| 1103 |
+
st.rerun()
|
| 1104 |
+
|
| 1105 |
+
|
| 1106 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1107 |
+
# TAB 5 Β· GENERATIVE AI
|
| 1108 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1109 |
+
with tab_genai:
|
| 1110 |
+
st.markdown('<div class="sec-head">π‘ Generative AI Suite</div>', unsafe_allow_html=True)
|
| 1111 |
+
gen_ai = st.session_state.gen_ai
|
| 1112 |
+
|
| 1113 |
+
# ββ Status banner ββ
|
| 1114 |
+
if gen_ai._provider == "smart":
|
| 1115 |
+
st.markdown("""
|
| 1116 |
+
<div class="success-box">
|
| 1117 |
+
β‘ <strong>Smart AI active</strong> β instant built-in responses, zero downloads, no API key needed.<br>
|
| 1118 |
+
Knows: ML algorithms, deep learning, NLP, Python, data science, and more.
|
| 1119 |
+
For open-ended GPT-4 quality responses, add an API key in the sidebar.
|
| 1120 |
+
</div>""", unsafe_allow_html=True)
|
| 1121 |
+
elif gen_ai.is_available():
|
| 1122 |
+
st.markdown(f'<div class="success-box">β
<strong>{gen_ai._provider_config["name"]}</strong> connected and ready.</div>', unsafe_allow_html=True)
|
| 1123 |
+
elif not gen_ai.pkg_installed():
|
| 1124 |
+
st.markdown(f"""
|
| 1125 |
+
<div class="info-box">
|
| 1126 |
+
β οΈ <strong>{gen_ai._provider_config["name"]}</strong> package not installed.<br>
|
| 1127 |
+
Run: <code>{gen_ai.install_cmd()}</code> β then restart Streamlit.
|
| 1128 |
+
</div>""", unsafe_allow_html=True)
|
| 1129 |
+
else:
|
| 1130 |
+
pkg_status = {
|
| 1131 |
+
"π’ OpenAI": "β
installed" if OPENAI_OK else "β pip install openai",
|
| 1132 |
+
"π΅ Google": "β
installed" if GOOGLE_OK else "β pip install google-generativeai",
|
| 1133 |
+
"π£ Anthropic": "β
installed" if ANTHROPIC_OK else "β pip install anthropic",
|
| 1134 |
+
}
|
| 1135 |
+
rows = "<br>".join(f" {k}: <code>{v}</code>" for k, v in pkg_status.items())
|
| 1136 |
+
st.markdown(f"""
|
| 1137 |
+
<div class="info-box">
|
| 1138 |
+
β οΈ No API key entered β using Smart AI mode.<br>
|
| 1139 |
+
To enable full LLM responses, install a package and add your key:<br><br>{rows}
|
| 1140 |
+
</div>""", unsafe_allow_html=True)
|
| 1141 |
+
|
| 1142 |
+
gen_tabs = st.tabs(["π¬ Chatbot", "β Data Q&A", "π» Code Gen", "π¨ Image Gen", "π Report"])
|
| 1143 |
+
|
| 1144 |
+
# ββ Chatbot ββ
|
| 1145 |
+
with gen_tabs[0]:
|
| 1146 |
+
st.subheader("Context-Aware Chatbot")
|
| 1147 |
+
if "gen_chat" not in st.session_state:
|
| 1148 |
+
st.session_state.gen_chat = []
|
| 1149 |
+
st.session_state.gen_history = []
|
| 1150 |
+
|
| 1151 |
+
for role, msg in st.session_state.gen_chat:
|
| 1152 |
+
with st.chat_message(role):
|
| 1153 |
+
st.markdown(msg)
|
| 1154 |
+
|
| 1155 |
+
user_q = st.chat_input("Chat with AIβ¦", key="gen_chat_input")
|
| 1156 |
+
if user_q:
|
| 1157 |
+
st.session_state.gen_chat.append(("user", user_q))
|
| 1158 |
+
st.session_state.gen_history.append({"role": "user", "content": user_q})
|
| 1159 |
+
with st.chat_message("user"):
|
| 1160 |
+
st.markdown(user_q)
|
| 1161 |
+
with st.chat_message("assistant"):
|
| 1162 |
+
with st.spinner("Generatingβ¦"):
|
| 1163 |
+
try:
|
| 1164 |
+
resp = gen_ai.chat(st.session_state.gen_history)
|
| 1165 |
+
except Exception as e:
|
| 1166 |
+
resp = f"β οΈ {e}"
|
| 1167 |
+
st.markdown(resp)
|
| 1168 |
+
st.session_state.gen_chat.append(("assistant", resp))
|
| 1169 |
+
st.session_state.gen_history.append({"role": "assistant", "content": resp})
|
| 1170 |
+
|
| 1171 |
+
if st.button("ποΈ Clear Conversation", key="clr_gen"):
|
| 1172 |
+
st.session_state.gen_chat = []
|
| 1173 |
+
st.session_state.gen_history = []
|
| 1174 |
+
st.rerun()
|
| 1175 |
+
|
| 1176 |
+
# ββ Data Q&A ββ
|
| 1177 |
+
with gen_tabs[1]:
|
| 1178 |
+
st.subheader("Prompt-Based Data Q&A")
|
| 1179 |
+
st.markdown("Ask questions about your loaded dataset and get AI-generated insights.")
|
| 1180 |
+
gen_option = st.selectbox("Analysis Type", [
|
| 1181 |
+
"General Insights", "Trends Analysis", "Anomaly Detection", "Recommendations", "Custom Question",
|
| 1182 |
+
])
|
| 1183 |
+
analysis_map = {
|
| 1184 |
+
"General Insights": "general",
|
| 1185 |
+
"Trends Analysis": "trends_analysis",
|
| 1186 |
+
"Anomaly Detection": "anomaly_detection",
|
| 1187 |
+
"Recommendations": "recommendations",
|
| 1188 |
+
}
|
| 1189 |
+
if gen_option != "Custom Question":
|
| 1190 |
+
if st.button("β¨ Generate Insights", type="primary", key="gen_ins"):
|
| 1191 |
+
if st.session_state.data_summary:
|
| 1192 |
+
with st.spinner("Generatingβ¦"):
|
| 1193 |
+
insights = gen_ai.generate_insights(
|
| 1194 |
+
st.session_state.data_summary, analysis_map[gen_option]
|
| 1195 |
+
)
|
| 1196 |
+
st.markdown(f'<div class="result-box">{insights}</div>', unsafe_allow_html=True)
|
| 1197 |
+
else:
|
| 1198 |
+
st.warning("Load data in the π Data tab first.")
|
| 1199 |
+
else:
|
| 1200 |
+
question = st.text_input("Your question:", placeholder="What features are most correlated with the target?")
|
| 1201 |
+
if st.button("π Ask", type="primary", key="gen_qa") and question:
|
| 1202 |
+
ctx = json.dumps(st.session_state.data_summary, default=str) if st.session_state.data_summary else None
|
| 1203 |
+
with st.spinner("Thinkingβ¦"):
|
| 1204 |
+
answer = gen_ai.answer_question(question, ctx)
|
| 1205 |
+
st.markdown(f'<div class="result-box">{answer}</div>', unsafe_allow_html=True)
|
| 1206 |
+
|
| 1207 |
+
# ββ Code Generation ββ
|
| 1208 |
+
with gen_tabs[2]:
|
| 1209 |
+
st.subheader("π» Code Generation")
|
| 1210 |
+
code_lang = st.selectbox("Language", ["Python", "JavaScript", "SQL", "Bash", "R", "TypeScript"])
|
| 1211 |
+
code_prompt = st.text_area(
|
| 1212 |
+
"Describe what you want to code:", height=110,
|
| 1213 |
+
placeholder="Write a Python function to clean a pandas dataframe by removing duplicates and filling nulls with median values.",
|
| 1214 |
+
)
|
| 1215 |
+
if st.button("β‘ Generate Code", type="primary", key="code_gen"):
|
| 1216 |
+
if code_prompt.strip():
|
| 1217 |
+
with st.spinner("Generating codeβ¦"):
|
| 1218 |
+
code_result = gen_ai.generate_code(code_prompt, language=code_lang)
|
| 1219 |
+
st.code(code_result, language=code_lang.lower())
|
| 1220 |
+
st.download_button(
|
| 1221 |
+
"β¬οΈ Download", code_result.encode(),
|
| 1222 |
+
file_name=f"generated.{code_lang.lower()[:2]}",
|
| 1223 |
+
mime="text/plain",
|
| 1224 |
+
)
|
| 1225 |
+
else:
|
| 1226 |
+
st.warning("Enter a code description.")
|
| 1227 |
+
|
| 1228 |
+
# ββ Image Generation ββ
|
| 1229 |
+
with gen_tabs[3]:
|
| 1230 |
+
st.subheader("π¨ Image Generation from Text")
|
| 1231 |
+
img_prompt = st.text_area(
|
| 1232 |
+
"Image description:", height=100,
|
| 1233 |
+
placeholder="A futuristic city at night with neon lights and flying cars, digital art style",
|
| 1234 |
+
)
|
| 1235 |
+
img_style = st.selectbox("Style", ["Photorealistic", "Digital Art", "Oil Painting", "Anime", "Sketch", "Cyberpunk"])
|
| 1236 |
+
img_size = st.selectbox("Size", ["1024x1024", "512x512", "1792x1024"])
|
| 1237 |
+
|
| 1238 |
+
if st.button("π¨ Generate Image", type="primary", key="img_gen"):
|
| 1239 |
+
if img_prompt.strip():
|
| 1240 |
+
full_prompt = f"{img_prompt}, {img_style} style"
|
| 1241 |
+
with st.spinner("Generating imageβ¦"):
|
| 1242 |
+
result = gen_ai.generate_image(full_prompt, size=img_size)
|
| 1243 |
+
if result.get("url"):
|
| 1244 |
+
st.image(result["url"], caption=f"Generated: {img_prompt[:60]}β¦", use_column_width=True)
|
| 1245 |
+
st.markdown(f"[π Open full size]({result['url']})")
|
| 1246 |
+
elif result.get("error"):
|
| 1247 |
+
st.markdown(f'<div class="info-box">β οΈ {result["error"]}</div>', unsafe_allow_html=True)
|
| 1248 |
+
else:
|
| 1249 |
+
st.warning("Enter an image description.")
|
| 1250 |
+
|
| 1251 |
+
# ββ Report ββ
|
| 1252 |
+
with gen_tabs[4]:
|
| 1253 |
+
st.subheader("π Auto-Generated Analysis Report")
|
| 1254 |
+
if st.button("π Generate Report", type="primary", key="gen_rep"):
|
| 1255 |
+
payload = {
|
| 1256 |
+
"ml_metrics": st.session_state.ml_metrics or {},
|
| 1257 |
+
"data_summary": st.session_state.data_summary or {},
|
| 1258 |
+
"model": st.session_state.ml_results.get("model_name", "N/A") if st.session_state.ml_results else "N/A",
|
| 1259 |
+
}
|
| 1260 |
+
with st.spinner("Writing reportβ¦"):
|
| 1261 |
+
report = gen_ai.generate_report(payload)
|
| 1262 |
+
st.text_area("Report", report, height=420)
|
| 1263 |
+
st.download_button("β¬οΈ Download Report", report.encode(), file_name="ai_report.txt", mime="text/plain")
|
| 1264 |
+
|
| 1265 |
+
|
| 1266 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1267 |
+
# TAB 6 Β· POWER BI
|
| 1268 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1269 |
+
with tab_pbi:
|
| 1270 |
+
st.markdown('<div class="sec-head">π€ Power BI Export</div>', unsafe_allow_html=True)
|
| 1271 |
+
exporter = st.session_state.powerbi_exp
|
| 1272 |
+
|
| 1273 |
+
if st.session_state.df is None:
|
| 1274 |
+
st.markdown('<div class="info-box">π Load data in the <strong>π Data</strong> tab first.</div>', unsafe_allow_html=True)
|
| 1275 |
+
else:
|
| 1276 |
+
df = st.session_state.df
|
| 1277 |
+
ec1, ec2 = st.columns(2)
|
| 1278 |
+
with ec1:
|
| 1279 |
+
include_parquet = st.checkbox("Include Parquet files", value=True)
|
| 1280 |
+
export_name = st.text_input("Dataset name", value="main_data")
|
| 1281 |
+
with ec2:
|
| 1282 |
+
st.markdown("**Available datasets:**")
|
| 1283 |
+
available = {"Main Data": df}
|
| 1284 |
+
if st.session_state.ml_results and st.session_state.ml_results.get("feature_importance"):
|
| 1285 |
+
available["Feature Importance"] = pd.DataFrame(st.session_state.ml_results["feature_importance"])
|
| 1286 |
+
for name in available:
|
| 1287 |
+
st.markdown(f"β’ {name}")
|
| 1288 |
+
|
| 1289 |
+
if st.button("π Export All for Power BI", type="primary", use_container_width=True):
|
| 1290 |
+
with st.spinner("Exportingβ¦"):
|
| 1291 |
+
try:
|
| 1292 |
+
named = {export_name: df}
|
| 1293 |
+
if "Feature Importance" in available:
|
| 1294 |
+
named["feature_importance"] = available["Feature Importance"]
|
| 1295 |
+
paths = exporter.export_all(named, include_parquet=include_parquet)
|
| 1296 |
+
st.success(f"β
Exported **{len(paths)}** files to `{OUTPUT_DIR}`")
|
| 1297 |
+
for p in paths:
|
| 1298 |
+
st.markdown(f" β’ `{p.name}`")
|
| 1299 |
+
except Exception as e:
|
| 1300 |
+
st.error(f"β {e}")
|
| 1301 |
+
|
| 1302 |
+
st.divider()
|
| 1303 |
+
st.info(exporter.generate_powerbi_instructions())
|
| 1304 |
+
|
| 1305 |
+
dl1, dl2 = st.columns(2)
|
| 1306 |
+
with dl1:
|
| 1307 |
+
st.download_button("β¬οΈ Download CSV", df.to_csv(index=False).encode(),
|
| 1308 |
+
file_name=f"{export_name}.csv", mime="text/csv", use_container_width=True)
|
| 1309 |
+
with dl2:
|
| 1310 |
+
if st.session_state.ml_results and st.session_state.ml_results.get("feature_importance"):
|
| 1311 |
+
fi_df = pd.DataFrame(st.session_state.ml_results["feature_importance"])
|
| 1312 |
+
st.download_button("β¬οΈ Feature Importance CSV", fi_df.to_csv(index=False).encode(),
|
| 1313 |
+
file_name="feature_importance.csv", mime="text/csv", use_container_width=True)
|
| 1314 |
+
|
| 1315 |
+
|
| 1316 |
+
# ββ Footer ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1317 |
+
st.markdown("""
|
| 1318 |
+
<div class="footer">
|
| 1319 |
+
Multi-AI Analytics Platform v2.1 Β· Clean Edition Β·
|
| 1320 |
+
ML Β· DL Β· NLP Β· GenAI Β· PowerBI
|
| 1321 |
+
<span>| KYOTO-Z |</span>
|
| 1322 |
+
</div>
|
| 1323 |
+
""", unsafe_allow_html=True)
|
config.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# import os
|
| 2 |
+
# from pathlib import Path
|
| 3 |
+
|
| 4 |
+
# BASE_DIR = Path(__file__).parent
|
| 5 |
+
# OUTPUT_DIR = BASE_DIR / "output"
|
| 6 |
+
# OUTPUT_DIR.mkdir(exist_ok=True)
|
| 7 |
+
|
| 8 |
+
# class Config:
|
| 9 |
+
# OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
| 10 |
+
# GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
|
| 11 |
+
# MODEL_CACHE_DIR = BASE_DIR / "model_cache"
|
| 12 |
+
# MAX_IMAGE_SIZE = (1024, 1024)
|
| 13 |
+
# BATCH_SIZE = 16
|
| 14 |
+
# DEFAULT_LLM_MODEL = "gemini-pro"
|
| 15 |
+
# DEFAULT_VISION_MODEL = "facebook/deit-base-patch16-224"
|
| 16 |
+
# DEFAULT_NLP_MODEL = "distilbert-base-uncased"
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
import os
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
|
| 22 |
+
BASE_DIR = Path(__file__).parent
|
| 23 |
+
OUTPUT_DIR = BASE_DIR / "output"
|
| 24 |
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
| 25 |
+
|
| 26 |
+
MODEL_CACHE_DIR = BASE_DIR / "model_cache"
|
| 27 |
+
MODEL_CACHE_DIR.mkdir(exist_ok=True)
|
| 28 |
+
|
| 29 |
+
class Config:
|
| 30 |
+
# API Keys
|
| 31 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
| 32 |
+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
|
| 33 |
+
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "")
|
| 34 |
+
|
| 35 |
+
# Paths
|
| 36 |
+
BASE_DIR = BASE_DIR
|
| 37 |
+
OUTPUT_DIR = OUTPUT_DIR
|
| 38 |
+
MODEL_CACHE_DIR = MODEL_CACHE_DIR
|
| 39 |
+
|
| 40 |
+
# Image settings
|
| 41 |
+
MAX_IMAGE_SIZE = (1024, 1024)
|
| 42 |
+
BATCH_SIZE = 16
|
| 43 |
+
|
| 44 |
+
# Model defaults
|
| 45 |
+
DEFAULT_LLM_MODEL = "gpt-4"
|
| 46 |
+
DEFAULT_VISION_MODEL = "resnet50"
|
| 47 |
+
DEFAULT_NLP_MODEL = "distilbert-base-uncased"
|
| 48 |
+
|
| 49 |
+
# ML defaults
|
| 50 |
+
N_ESTIMATORS = 150
|
| 51 |
+
MAX_DEPTH = 6
|
| 52 |
+
RANDOM_STATE = 42
|
| 53 |
+
CV_FOLDS = 5
|
requirements.txt
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2 |
+
# Multi-AI Analytics Platform v2.0
|
| 3 |
+
# Merged Requirements
|
| 4 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 5 |
+
|
| 6 |
+
# ββ Core ββββββββββββββββββββββββββββββββββββββ
|
| 7 |
+
streamlit>=1.32.0
|
| 8 |
+
pandas>=2.0.0
|
| 9 |
+
numpy>=1.24.0
|
| 10 |
+
pillow>=10.0.0
|
| 11 |
+
python-dotenv>=1.0.0
|
| 12 |
+
requests>=2.31.0
|
| 13 |
+
|
| 14 |
+
# ββ Machine Learning ββββββββββββββββββββββββββ
|
| 15 |
+
scikit-learn>=1.4.0
|
| 16 |
+
xgboost>=2.0.0
|
| 17 |
+
lightgbm>=4.0.0
|
| 18 |
+
|
| 19 |
+
# ββ Deep Learning (choose one or both) βββββββ
|
| 20 |
+
torch>=2.1.0
|
| 21 |
+
torchvision>=0.16.0
|
| 22 |
+
tensorflow>=2.13.0 # Optional β comment out if using PyTorch only
|
| 23 |
+
|
| 24 |
+
# ββ NLP / HuggingFace βββββββββββββββββββββββββ
|
| 25 |
+
transformers>=4.38.0
|
| 26 |
+
sentencepiece>=0.1.99 # Required by some HuggingFace tokenizers
|
| 27 |
+
|
| 28 |
+
# ββ Computer Vision βββββββββββββββββββββββββββ
|
| 29 |
+
opencv-python-headless>=4.8.0
|
| 30 |
+
|
| 31 |
+
# ββ Visualization βββββββββββββββββββββββββββββ
|
| 32 |
+
plotly>=5.18.0
|
| 33 |
+
matplotlib>=3.7.0
|
| 34 |
+
seaborn>=0.12.0
|
| 35 |
+
|
| 36 |
+
# ββ Generative AI Providers βββββββββββββββββββ
|
| 37 |
+
openai>=1.12.0 # For GPT-4o + DALL-E 3 image generation
|
| 38 |
+
google-generativeai>=0.4.0 # For Gemini
|
| 39 |
+
anthropic>=0.20.0 # For Claude
|
| 40 |
+
|
| 41 |
+
# ββ Export ββββββββββββββββββββββββββββββββββββ
|
| 42 |
+
pyarrow>=14.0.0 # For Parquet export (Power BI)
|
| 43 |
+
|
| 44 |
+
# ββ Optional ββββββββββββββββββββββββββββββββββ
|
| 45 |
+
# accelerate>=0.24.0 # Faster HuggingFace inference
|
| 46 |
+
# kaleido>=0.2.1 # Save Plotly charts as images
|