translate app.py into english

#5
by granamaa - opened
Files changed (1) hide show
  1. app.py +214 -116
app.py CHANGED
@@ -8,113 +8,144 @@ import pickle
8
  import os
9
  from typing import Dict, List, Any
10
 
11
- os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
12
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
13
 
14
- print("🚀 Iniciando Eco Finder API...")
15
 
16
- # Configuración
17
  try:
18
  import tensorflow as tf
 
19
  print(f"✅ TensorFlow version: {tf.__version__}")
20
  from tensorflow.keras.models import load_model
 
21
  TENSORFLOW_AVAILABLE = True
22
  except ImportError as e:
23
- print(f"❌ TensorFlow no disponible: {e}")
24
  TENSORFLOW_AVAILABLE = False
25
 
26
- # Cargar recursos
 
27
  def load_resources():
28
  try:
29
  with open("feature_stats.json", "r") as f:
30
  feature_stats = json.load(f)
31
- print("✅ Feature stats cargado")
32
-
33
  with open("scaler.pkl", "rb") as f:
34
  scaler = pickle.load(f)
35
- print("✅ Scaler cargado")
36
-
37
  with open("label_encoder.pkl", "rb") as f:
38
  label_encoder = pickle.load(f)
39
- print("✅ Label encoder cargado")
40
-
41
  model = None
42
  if TENSORFLOW_AVAILABLE:
43
  model = load_model("modulo_tabular.h5")
44
- print("✅ Modelo cargado")
45
-
46
  return model, scaler, label_encoder, feature_stats
47
-
48
  except Exception as e:
49
- print(f"❌ Error cargando recursos: {str(e)}")
50
  feature_stats = {
51
  "feature_columns": [
52
- "koi_period", "koi_duration", "koi_depth", "koi_prad",
53
- "koi_srad", "koi_teq", "koi_steff", "koi_slogg",
54
- "koi_smet", "koi_kepmag", "koi_model_snr", "koi_num_transits"
 
 
 
 
 
 
 
 
 
55
  ],
56
  "train_medians": {
57
- "koi_period": 10.0, "koi_duration": 5.0, "koi_depth": 1000.0,
58
- "koi_prad": 2.0, "koi_srad": 1.0, "koi_teq": 1000.0,
59
- "koi_steff": 6000.0, "koi_slogg": 4.5, "koi_smet": 0.0,
60
- "koi_kepmag": 12.0, "koi_model_snr": 10.0, "koi_num_transits": 3.0
61
- }
 
 
 
 
 
 
 
 
62
  }
63
  return None, None, None, feature_stats
64
 
65
- # Cargar recursos
 
66
  model, scaler, label_encoder, feature_stats = load_resources()
67
  feature_columns = feature_stats.get("feature_columns", [])
68
  train_medians = feature_stats.get("train_medians", {})
69
 
70
  BASE = "https://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI"
71
 
72
- # ==================== FUNCIONES PARA GRADIO ====================
 
73
 
74
  def predict_single(features: Dict) -> Dict:
75
- """Función para predecir un solo objeto - USADA POR GRADIO"""
76
  try:
77
  if model is None or scaler is None or label_encoder is None:
78
- return {"error": "Modelo no disponible"}
79
-
80
- # Crear array de características
81
  input_features = []
82
  for feature in feature_columns:
83
  value = features.get(feature, train_medians.get(feature, 0))
84
  input_features.append(float(value))
85
-
86
- # Predecir
87
  input_array = np.array([input_features])
88
  X_input = scaler.transform(input_array)
89
-
90
  if TENSORFLOW_AVAILABLE:
91
  probs = model.predict(X_input, verbose=0)[0]
92
  else:
93
  probs = np.random.dirichlet(np.ones(3), size=1)[0]
94
-
95
  pred_idx = np.argmax(probs)
96
  pred_label = label_encoder.inverse_transform([pred_idx])[0]
97
-
98
  return {
99
  "prediction": pred_label,
100
  "probabilities": {
101
  "CONFIRMED": float(probs[0]),
102
  "CANDIDATE": float(probs[1]),
103
- "FALSE_POSITIVE": float(probs[2])
104
  },
105
- "input_features": dict(zip(feature_columns, input_features))
106
  }
107
-
108
  except Exception as e:
109
  return {"error": str(e)}
110
 
 
111
  def predict_from_dict(
112
- koi_period: float, koi_duration: float, koi_depth: float,
113
- koi_prad: float, koi_srad: float, koi_teq: float,
114
- koi_steff: float, koi_slogg: float, koi_smet: float,
115
- koi_kepmag: float, koi_model_snr: float, koi_num_transits: float
 
 
 
 
 
 
 
 
116
  ) -> Dict:
117
- """Wrapper que toma parámetros individuales y los convierte a dict"""
118
  features = {
119
  "koi_period": koi_period,
120
  "koi_duration": koi_duration,
@@ -127,36 +158,46 @@ def predict_from_dict(
127
  "koi_smet": koi_smet,
128
  "koi_kepmag": koi_kepmag,
129
  "koi_model_snr": koi_model_snr,
130
- "koi_num_transits": koi_num_transits
131
  }
132
  return predict_single(features)
133
 
 
134
  def predict_toi_realtime():
135
- """Función para TOI en tiempo real"""
136
  try:
137
  if model is None or scaler is None or label_encoder is None:
138
- return "❌ Modelo no disponible"
139
-
140
- # Consultar API de exoplanetas
141
- where = ("(tfopwg_disp like 'PC' or tfopwg_disp like 'APC') "
142
- "and (pl_orbper is not null or tce_period is not null)")
143
-
 
 
144
  params = {"table": "toi", "where": where, "format": "csv"}
145
  resp = requests.get(BASE, params=params, timeout=60)
146
  resp.raise_for_status()
147
  toi_df = pd.read_csv(io.StringIO(resp.text))
148
-
149
  if toi_df.empty:
150
- return "❌ No se encontraron objetos TOI"
151
-
152
- # Tomar muestra
153
  toi_sample = toi_df.sample(min(3, len(toi_df)), random_state=7)
154
  toi_sample.columns = [c.strip().lower() for c in toi_sample.columns]
155
-
156
- # Mapeo de sinónimos
157
  candidates_map = {
158
  "koi_period": ["pl_orbper", "tce_period", "orbper", "period"],
159
- "koi_duration": ["pl_trandurh", "tce_duration", "tran_dur", "trandur", "duration", "dur"],
 
 
 
 
 
 
 
160
  "koi_depth": ["pl_trandep", "tce_depth", "depth", "trandep"],
161
  "koi_prad": ["pl_rade", "prad", "rade", "planet_radius"],
162
  "koi_srad": ["st_rad", "srad", "stellar_radius", "star_radius"],
@@ -166,9 +207,14 @@ def predict_toi_realtime():
166
  "koi_smet": ["st_met", "feh", "metallicity", "smet"],
167
  "koi_kepmag": ["st_tmag", "tmag", "kepmag", "koi_kepmag"],
168
  "koi_model_snr": ["tce_model_snr", "model_snr", "snr"],
169
- "koi_num_transits": ["tce_num_transits", "num_transits", "ntransits", "tran_count"]
 
 
 
 
 
170
  }
171
-
172
  def first_present(candidates, cols_set):
173
  for name in candidates:
174
  if name in cols_set:
@@ -178,12 +224,12 @@ def predict_toi_realtime():
178
  if found:
179
  return found[0]
180
  return None
181
-
182
  cols_set = set(toi_sample.columns)
183
  results = []
184
-
185
  for idx, row in toi_sample.iterrows():
186
- # Preparar características
187
  features = {}
188
  for feat in feature_columns:
189
  src = first_present(candidates_map.get(feat, []), cols_set)
@@ -191,65 +237,91 @@ def predict_toi_realtime():
191
  features[feat] = float(row[src])
192
  else:
193
  features[feat] = train_medians.get(feat, 0)
194
-
195
- # Predecir
196
  result = predict_single(features)
197
-
198
  if "error" not in result:
199
- results.append({
200
- "TOI": row.get('toi', f"TOI-{idx}"),
201
- "Disposición": row.get('tfopwg_disp', 'Unknown'),
202
- "Predicción": result['prediction'],
203
- "P(Confirmado)": f"{result['probabilities']['CONFIRMED']:.3f}",
204
- "P(Candidato)": f"{result['probabilities']['CANDIDATE']:.3f}",
205
- "P(Falso Positivo)": f"{result['probabilities']['FALSE_POSITIVE']:.3f}"
206
- })
207
-
 
 
208
  if not results:
209
- return "❌ No se pudieron generar predicciones"
210
-
211
  result_df = pd.DataFrame(results)
212
- return f"**Predicciones TOI:**\n\n{result_df.to_markdown(index=False)}"
213
-
214
  except Exception as e:
215
  return f"❌ Error: {str(e)}"
216
 
 
217
  def predict_manual(
218
- period, duration, depth, prad, srad, teq, steff, slogg, smet, kepmag, snr, num_transits
 
 
 
 
 
 
 
 
 
 
 
219
  ):
220
- """Función para predicción manual en Gradio"""
221
  try:
222
  result = predict_from_dict(
223
- period, duration, depth, prad, srad, teq, steff, slogg, smet, kepmag, snr, num_transits
 
 
 
 
 
 
 
 
 
 
 
224
  )
225
-
226
  if "error" in result:
227
  return f"❌ {result['error']}"
228
-
229
- output = f"**Predicción:** {result['prediction']}\n\n**Probabilidades:**\n"
230
- for clase, prob in result['probabilities'].items():
231
  output += f"- {clase}: {prob:.3f}\n"
232
-
233
  return output
234
-
235
  except Exception as e:
236
  return f"❌ Error: {str(e)}"
237
 
238
- # ==================== INTERFAZ GRADIO ====================
 
239
 
240
  with gr.Blocks(theme=gr.themes.Soft(), title="Eco Finder API") as demo:
241
  gr.Markdown("# 🌌 Eco Finder API")
242
- gr.Markdown("Clasificador de exoplanetas")
243
-
244
- with gr.Tab("🎯 Predicción API"):
245
- gr.Markdown("### Endpoint para consumo desde frontend")
246
  gr.Markdown("""
247
  **URL:** `https://jarpalucas-echo-finder-api.hf.space/api/predict`
248
 
249
- **Método:** POST
250
  **Content-Type:** application/json
251
 
252
- **Ejemplo de uso con curl:**
253
  ```bash
254
  curl -X POST "https://jarpalucas-echo-finder-api.hf.space/api/predict" \\
255
  -H "Content-Type: application/json" \\
@@ -269,8 +341,8 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Eco Finder API") as demo:
269
  }'
270
  ```
271
  """)
272
-
273
- # Inputs para probar la API localmente
274
  with gr.Row():
275
  with gr.Column():
276
  period = gr.Number(label="koi_period", value=10.0)
@@ -287,35 +359,61 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Eco Finder API") as demo:
287
  kepmag = gr.Number(label="koi_kepmag", value=12.0)
288
  snr = gr.Number(label="koi_model_snr", value=10.0)
289
  num_transits = gr.Number(label="koi_num_transits", value=3.0)
290
-
291
- api_btn = gr.Button("🚀 Probar Predicción")
292
  api_output = gr.JSON()
293
-
294
  api_btn.click(
295
  fn=predict_from_dict,
296
- inputs=[period, duration, depth, prad, srad, teq, steff, slogg, smet, kepmag, snr, num_transits],
297
- outputs=api_output
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  )
299
-
300
- with gr.Tab("🔭 TOI Tiempo Real"):
301
- gr.Markdown("Predicciones de objetos TOI en tiempo real")
302
- toi_btn = gr.Button("🔍 Analizar TOI")
303
  toi_output = gr.Markdown()
304
  toi_btn.click(predict_toi_realtime, outputs=toi_output)
305
-
306
- with gr.Tab("📊 Interfaz Manual"):
307
- gr.Markdown("Interfaz manual para predicciones")
308
- manual_btn = gr.Button("🎯 Predecir")
309
  manual_output = gr.Markdown()
310
  manual_btn.click(
311
  fn=predict_manual,
312
- inputs=[period, duration, depth, prad, srad, teq, steff, slogg, smet, kepmag, snr, num_transits],
313
- outputs=manual_output
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  )
315
 
316
- print("🎉 Aplicación iniciada correctamente!")
317
- print("🌐 Interfaz disponible en: /")
318
- print("🔗 Endpoint API disponible en: /api/predict")
319
 
320
  if __name__ == "__main__":
321
- demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
 
8
  import os
9
  from typing import Dict, List, Any
10
 
11
+ os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0"
12
+ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
13
 
14
+ print("🚀 Starting Eco Finder API...")
15
 
16
+ # Configuration
17
  try:
18
  import tensorflow as tf
19
+
20
  print(f"✅ TensorFlow version: {tf.__version__}")
21
  from tensorflow.keras.models import load_model
22
+
23
  TENSORFLOW_AVAILABLE = True
24
  except ImportError as e:
25
+ print(f"❌ TensorFlow not available: {e}")
26
  TENSORFLOW_AVAILABLE = False
27
 
28
+
29
+ # Load resources
30
  def load_resources():
31
  try:
32
  with open("feature_stats.json", "r") as f:
33
  feature_stats = json.load(f)
34
+ print("✅ Feature stats loaded")
35
+
36
  with open("scaler.pkl", "rb") as f:
37
  scaler = pickle.load(f)
38
+ print("✅ Scaler loaded")
39
+
40
  with open("label_encoder.pkl", "rb") as f:
41
  label_encoder = pickle.load(f)
42
+ print("✅ Label encoder loaded")
43
+
44
  model = None
45
  if TENSORFLOW_AVAILABLE:
46
  model = load_model("modulo_tabular.h5")
47
+ print("✅ Model loaded")
48
+
49
  return model, scaler, label_encoder, feature_stats
50
+
51
  except Exception as e:
52
+ print(f"❌ Error loading resources: {str(e)}")
53
  feature_stats = {
54
  "feature_columns": [
55
+ "koi_period",
56
+ "koi_duration",
57
+ "koi_depth",
58
+ "koi_prad",
59
+ "koi_srad",
60
+ "koi_teq",
61
+ "koi_steff",
62
+ "koi_slogg",
63
+ "koi_smet",
64
+ "koi_kepmag",
65
+ "koi_model_snr",
66
+ "koi_num_transits",
67
  ],
68
  "train_medians": {
69
+ "koi_period": 10.0,
70
+ "koi_duration": 5.0,
71
+ "koi_depth": 1000.0,
72
+ "koi_prad": 2.0,
73
+ "koi_srad": 1.0,
74
+ "koi_teq": 1000.0,
75
+ "koi_steff": 6000.0,
76
+ "koi_slogg": 4.5,
77
+ "koi_smet": 0.0,
78
+ "koi_kepmag": 12.0,
79
+ "koi_model_snr": 10.0,
80
+ "koi_num_transits": 3.0,
81
+ },
82
  }
83
  return None, None, None, feature_stats
84
 
85
+
86
+ # Load resources
87
  model, scaler, label_encoder, feature_stats = load_resources()
88
  feature_columns = feature_stats.get("feature_columns", [])
89
  train_medians = feature_stats.get("train_medians", {})
90
 
91
  BASE = "https://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI"
92
 
93
+ # ==================== FUNCTIONS FOR GRADIO ====================
94
+
95
 
96
  def predict_single(features: Dict) -> Dict:
97
+ """Function to predict a single object - USED BY GRADIO"""
98
  try:
99
  if model is None or scaler is None or label_encoder is None:
100
+ return {"error": "Model not available"}
101
+
102
+ # Create feature array
103
  input_features = []
104
  for feature in feature_columns:
105
  value = features.get(feature, train_medians.get(feature, 0))
106
  input_features.append(float(value))
107
+
108
+ # Predict
109
  input_array = np.array([input_features])
110
  X_input = scaler.transform(input_array)
111
+
112
  if TENSORFLOW_AVAILABLE:
113
  probs = model.predict(X_input, verbose=0)[0]
114
  else:
115
  probs = np.random.dirichlet(np.ones(3), size=1)[0]
116
+
117
  pred_idx = np.argmax(probs)
118
  pred_label = label_encoder.inverse_transform([pred_idx])[0]
119
+
120
  return {
121
  "prediction": pred_label,
122
  "probabilities": {
123
  "CONFIRMED": float(probs[0]),
124
  "CANDIDATE": float(probs[1]),
125
+ "FALSE_POSITIVE": float(probs[2]),
126
  },
127
+ "input_features": dict(zip(feature_columns, input_features)),
128
  }
129
+
130
  except Exception as e:
131
  return {"error": str(e)}
132
 
133
+
134
  def predict_from_dict(
135
+ koi_period: float,
136
+ koi_duration: float,
137
+ koi_depth: float,
138
+ koi_prad: float,
139
+ koi_srad: float,
140
+ koi_teq: float,
141
+ koi_steff: float,
142
+ koi_slogg: float,
143
+ koi_smet: float,
144
+ koi_kepmag: float,
145
+ koi_model_snr: float,
146
+ koi_num_transits: float,
147
  ) -> Dict:
148
+ """Wrapper that takes individual parameters and converts them to dict"""
149
  features = {
150
  "koi_period": koi_period,
151
  "koi_duration": koi_duration,
 
158
  "koi_smet": koi_smet,
159
  "koi_kepmag": koi_kepmag,
160
  "koi_model_snr": koi_model_snr,
161
+ "koi_num_transits": koi_num_transits,
162
  }
163
  return predict_single(features)
164
 
165
+
166
  def predict_toi_realtime():
167
+ """Function for real-time TOI"""
168
  try:
169
  if model is None or scaler is None or label_encoder is None:
170
+ return "❌ Model not available"
171
+
172
+ # Query exoplanet API
173
+ where = (
174
+ "(tfopwg_disp like 'PC' or tfopwg_disp like 'APC') "
175
+ "and (pl_orbper is not null or tce_period is not null)"
176
+ )
177
+
178
  params = {"table": "toi", "where": where, "format": "csv"}
179
  resp = requests.get(BASE, params=params, timeout=60)
180
  resp.raise_for_status()
181
  toi_df = pd.read_csv(io.StringIO(resp.text))
182
+
183
  if toi_df.empty:
184
+ return "❌ No TOI objects found"
185
+
186
+ # Take sample
187
  toi_sample = toi_df.sample(min(3, len(toi_df)), random_state=7)
188
  toi_sample.columns = [c.strip().lower() for c in toi_sample.columns]
189
+
190
+ # Synonym mapping
191
  candidates_map = {
192
  "koi_period": ["pl_orbper", "tce_period", "orbper", "period"],
193
+ "koi_duration": [
194
+ "pl_trandurh",
195
+ "tce_duration",
196
+ "tran_dur",
197
+ "trandur",
198
+ "duration",
199
+ "dur",
200
+ ],
201
  "koi_depth": ["pl_trandep", "tce_depth", "depth", "trandep"],
202
  "koi_prad": ["pl_rade", "prad", "rade", "planet_radius"],
203
  "koi_srad": ["st_rad", "srad", "stellar_radius", "star_radius"],
 
207
  "koi_smet": ["st_met", "feh", "metallicity", "smet"],
208
  "koi_kepmag": ["st_tmag", "tmag", "kepmag", "koi_kepmag"],
209
  "koi_model_snr": ["tce_model_snr", "model_snr", "snr"],
210
+ "koi_num_transits": [
211
+ "tce_num_transits",
212
+ "num_transits",
213
+ "ntransits",
214
+ "tran_count",
215
+ ],
216
  }
217
+
218
  def first_present(candidates, cols_set):
219
  for name in candidates:
220
  if name in cols_set:
 
224
  if found:
225
  return found[0]
226
  return None
227
+
228
  cols_set = set(toi_sample.columns)
229
  results = []
230
+
231
  for idx, row in toi_sample.iterrows():
232
+ # Prepare features
233
  features = {}
234
  for feat in feature_columns:
235
  src = first_present(candidates_map.get(feat, []), cols_set)
 
237
  features[feat] = float(row[src])
238
  else:
239
  features[feat] = train_medians.get(feat, 0)
240
+
241
+ # Predict
242
  result = predict_single(features)
243
+
244
  if "error" not in result:
245
+ results.append(
246
+ {
247
+ "TOI": row.get("toi", f"tOI-{idx}"),
248
+ "Disposition": row.get("tfopwg_disp", "Unknown"),
249
+ "Prediction": result["prediction"],
250
+ "P(Confirmed)": f"{result['probabilities']['CONFIRMED']:.3f}",
251
+ "P(Candidate)": f"{result['probabilities']['CANDIDATE']:.3f}",
252
+ "P(False Positive)": f"{result['probabilities']['FALSE_POSITIVE']:.3f}",
253
+ }
254
+ )
255
+
256
  if not results:
257
+ return "❌ Could not generate predictions"
258
+
259
  result_df = pd.DataFrame(results)
260
+ return f"**TOI Predictions:**\n\n{result_df.to_markdown(index=False)}"
261
+
262
  except Exception as e:
263
  return f"❌ Error: {str(e)}"
264
 
265
+
266
  def predict_manual(
267
+ period,
268
+ duration,
269
+ depth,
270
+ prad,
271
+ srad,
272
+ teq,
273
+ steff,
274
+ slogg,
275
+ smet,
276
+ kepmag,
277
+ snr,
278
+ num_transits,
279
  ):
280
+ """Function for manual prediction in Gradio"""
281
  try:
282
  result = predict_from_dict(
283
+ period,
284
+ duration,
285
+ depth,
286
+ prad,
287
+ srad,
288
+ teq,
289
+ steff,
290
+ slogg,
291
+ smet,
292
+ kepmag,
293
+ snr,
294
+ num_transits,
295
  )
296
+
297
  if "error" in result:
298
  return f"❌ {result['error']}"
299
+
300
+ output = f"**Prediction:** {result['prediction']}\n\n**Probabilities:**\n"
301
+ for clase, prob in result["probabilities"].items():
302
  output += f"- {clase}: {prob:.3f}\n"
303
+
304
  return output
305
+
306
  except Exception as e:
307
  return f"❌ Error: {str(e)}"
308
 
309
+
310
+ # ==================== GRADIO INTERFACE ====================
311
 
312
  with gr.Blocks(theme=gr.themes.Soft(), title="Eco Finder API") as demo:
313
  gr.Markdown("# 🌌 Eco Finder API")
314
+ gr.Markdown("Exoplanet classifier")
315
+
316
+ with gr.Tab("🎯 API Prediction"):
317
+ gr.Markdown("### Endpoint for frontend consumption")
318
  gr.Markdown("""
319
  **URL:** `https://jarpalucas-echo-finder-api.hf.space/api/predict`
320
 
321
+ **Method:** POST
322
  **Content-Type:** application/json
323
 
324
+ **Usage example with curl:**
325
  ```bash
326
  curl -X POST "https://jarpalucas-echo-finder-api.hf.space/api/predict" \\
327
  -H "Content-Type: application/json" \\
 
341
  }'
342
  ```
343
  """)
344
+
345
+ # Inputs to test the API locally
346
  with gr.Row():
347
  with gr.Column():
348
  period = gr.Number(label="koi_period", value=10.0)
 
359
  kepmag = gr.Number(label="koi_kepmag", value=12.0)
360
  snr = gr.Number(label="koi_model_snr", value=10.0)
361
  num_transits = gr.Number(label="koi_num_transits", value=3.0)
362
+
363
+ api_btn = gr.Button("🚀 Test Prediction")
364
  api_output = gr.JSON()
365
+
366
  api_btn.click(
367
  fn=predict_from_dict,
368
+ inputs=[
369
+ period,
370
+ duration,
371
+ depth,
372
+ prad,
373
+ srad,
374
+ teq,
375
+ steff,
376
+ slogg,
377
+ smet,
378
+ kepmag,
379
+ snr,
380
+ num_transits,
381
+ ],
382
+ outputs=api_output,
383
  )
384
+
385
+ with gr.Tab("🔭 Real-time TOI"):
386
+ gr.Markdown("Real-time TOI object predictions")
387
+ toi_btn = gr.Button("🔍 Analyze TOI")
388
  toi_output = gr.Markdown()
389
  toi_btn.click(predict_toi_realtime, outputs=toi_output)
390
+
391
+ with gr.Tab("📊 Manual Interface"):
392
+ gr.Markdown("Manual interface for predictions")
393
+ manual_btn = gr.Button("🎯 Predict")
394
  manual_output = gr.Markdown()
395
  manual_btn.click(
396
  fn=predict_manual,
397
+ inputs=[
398
+ period,
399
+ duration,
400
+ depth,
401
+ prad,
402
+ srad,
403
+ teq,
404
+ steff,
405
+ slogg,
406
+ smet,
407
+ kepmag,
408
+ snr,
409
+ num_transits,
410
+ ],
411
+ outputs=manual_output,
412
  )
413
 
414
+ print("🎉 Application started successfully!")
415
+ print("🌐 Interface available at: /")
416
+ print("🔗 API endpoint available at: /api/predict")
417
 
418
  if __name__ == "__main__":
419
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)