ayushsahu45 commited on
Commit
cd8677c
Β·
verified Β·
1 Parent(s): 8932d46

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +1323 -0
  2. config.py +53 -0
  3. 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 &nbsp;Β·&nbsp; Deep Learning &nbsp;Β·&nbsp; NLP &nbsp;Β·&nbsp; Generative AI &nbsp;Β·&nbsp; 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"&nbsp;&nbsp;{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 &nbsp;Β·&nbsp; Clean Edition &nbsp;Β·&nbsp;
1320
+ ML Β· DL Β· NLP Β· GenAI Β· PowerBI &nbsp;&nbsp;
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