Spaces:
Sleeping
Sleeping
| import io | |
| import requests | |
| import pandas as pd | |
| import numpy as np | |
| import gradio as gr | |
| import json | |
| import pickle | |
| import os | |
| from typing import Dict, List, Any | |
| os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0" | |
| os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" | |
| print("π Starting Eco Finder API...") | |
| # Configuration | |
| try: | |
| import tensorflow as tf | |
| print(f"β TensorFlow version: {tf.__version__}") | |
| from tensorflow.keras.models import load_model | |
| TENSORFLOW_AVAILABLE = True | |
| except ImportError as e: | |
| print(f"β TensorFlow not available: {e}") | |
| TENSORFLOW_AVAILABLE = False | |
| # Load resources | |
| def load_resources(): | |
| try: | |
| with open("feature_stats.json", "r") as f: | |
| feature_stats = json.load(f) | |
| print("β Feature stats loaded") | |
| with open("scaler.pkl", "rb") as f: | |
| scaler = pickle.load(f) | |
| print("β Scaler loaded") | |
| with open("label_encoder.pkl", "rb") as f: | |
| label_encoder = pickle.load(f) | |
| print("β Label encoder loaded") | |
| model = None | |
| if TENSORFLOW_AVAILABLE: | |
| model = load_model("modulo_tabular.h5") | |
| print("β Model loaded") | |
| return model, scaler, label_encoder, feature_stats | |
| except Exception as e: | |
| print(f"β Error loading resources: {str(e)}") | |
| feature_stats = { | |
| "feature_columns": [ | |
| "koi_period", | |
| "koi_duration", | |
| "koi_depth", | |
| "koi_prad", | |
| "koi_srad", | |
| "koi_teq", | |
| "koi_steff", | |
| "koi_slogg", | |
| "koi_smet", | |
| "koi_kepmag", | |
| "koi_model_snr", | |
| "koi_num_transits", | |
| ], | |
| "train_medians": { | |
| "koi_period": 10.0, | |
| "koi_duration": 5.0, | |
| "koi_depth": 1000.0, | |
| "koi_prad": 2.0, | |
| "koi_srad": 1.0, | |
| "koi_teq": 1000.0, | |
| "koi_steff": 6000.0, | |
| "koi_slogg": 4.5, | |
| "koi_smet": 0.0, | |
| "koi_kepmag": 12.0, | |
| "koi_model_snr": 10.0, | |
| "koi_num_transits": 3.0, | |
| }, | |
| } | |
| return None, None, None, feature_stats | |
| # Load resources | |
| model, scaler, label_encoder, feature_stats = load_resources() | |
| feature_columns = feature_stats.get("feature_columns", []) | |
| train_medians = feature_stats.get("train_medians", {}) | |
| BASE = "https://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI" | |
| # ==================== FUNCTIONS FOR GRADIO ==================== | |
| def predict_single(features: Dict) -> Dict: | |
| """Function to predict a single object - USED BY GRADIO""" | |
| try: | |
| if model is None or scaler is None or label_encoder is None: | |
| return {"error": "Model not available"} | |
| # Create feature array | |
| input_features = [] | |
| for feature in feature_columns: | |
| value = features.get(feature, train_medians.get(feature, 0)) | |
| input_features.append(float(value)) | |
| # Predict | |
| input_array = np.array([input_features]) | |
| X_input = scaler.transform(input_array) | |
| if TENSORFLOW_AVAILABLE: | |
| probs = model.predict(X_input, verbose=0)[0] | |
| else: | |
| probs = np.random.dirichlet(np.ones(3), size=1)[0] | |
| pred_idx = np.argmax(probs) | |
| pred_label = label_encoder.inverse_transform([pred_idx])[0] | |
| return { | |
| "prediction": pred_label, | |
| "probabilities": { | |
| "CONFIRMED": float(probs[0]), | |
| "CANDIDATE": float(probs[1]), | |
| "FALSE_POSITIVE": float(probs[2]), | |
| }, | |
| "input_features": dict(zip(feature_columns, input_features)), | |
| } | |
| except Exception as e: | |
| return {"error": str(e)} | |
| def predict_from_dict( | |
| koi_period: float, | |
| koi_duration: float, | |
| koi_depth: float, | |
| koi_prad: float, | |
| koi_srad: float, | |
| koi_teq: float, | |
| koi_steff: float, | |
| koi_slogg: float, | |
| koi_smet: float, | |
| koi_kepmag: float, | |
| koi_model_snr: float, | |
| koi_num_transits: float, | |
| ) -> Dict: | |
| """Wrapper that takes individual parameters and converts them to dict""" | |
| features = { | |
| "koi_period": koi_period, | |
| "koi_duration": koi_duration, | |
| "koi_depth": koi_depth, | |
| "koi_prad": koi_prad, | |
| "koi_srad": koi_srad, | |
| "koi_teq": koi_teq, | |
| "koi_steff": koi_steff, | |
| "koi_slogg": koi_slogg, | |
| "koi_smet": koi_smet, | |
| "koi_kepmag": koi_kepmag, | |
| "koi_model_snr": koi_model_snr, | |
| "koi_num_transits": koi_num_transits, | |
| } | |
| return predict_single(features) | |
| def predict_toi_realtime(): | |
| """Function for real-time TOI""" | |
| try: | |
| if model is None or scaler is None or label_encoder is None: | |
| return "β Model not available" | |
| # Query exoplanet API | |
| where = ( | |
| "(tfopwg_disp like 'PC' or tfopwg_disp like 'APC') " | |
| "and (pl_orbper is not null or tce_period is not null)" | |
| ) | |
| params = {"table": "toi", "where": where, "format": "csv"} | |
| resp = requests.get(BASE, params=params, timeout=60) | |
| resp.raise_for_status() | |
| toi_df = pd.read_csv(io.StringIO(resp.text)) | |
| if toi_df.empty: | |
| return "β No TOI objects found" | |
| # Take sample | |
| toi_sample = toi_df.sample(min(3, len(toi_df)), random_state=7) | |
| toi_sample.columns = [c.strip().lower() for c in toi_sample.columns] | |
| # Synonym mapping | |
| candidates_map = { | |
| "koi_period": ["pl_orbper", "tce_period", "orbper", "period"], | |
| "koi_duration": [ | |
| "pl_trandurh", | |
| "tce_duration", | |
| "tran_dur", | |
| "trandur", | |
| "duration", | |
| "dur", | |
| ], | |
| "koi_depth": ["pl_trandep", "tce_depth", "depth", "trandep"], | |
| "koi_prad": ["pl_rade", "prad", "rade", "planet_radius"], | |
| "koi_srad": ["st_rad", "srad", "stellar_radius", "star_radius"], | |
| "koi_teq": ["pl_eqt", "teq", "equilibrium_temp"], | |
| "koi_steff": ["st_teff", "teff", "stellar_teff", "effective_temp"], | |
| "koi_slogg": ["st_logg", "logg", "slogg"], | |
| "koi_smet": ["st_met", "feh", "metallicity", "smet"], | |
| "koi_kepmag": ["st_tmag", "tmag", "kepmag", "koi_kepmag"], | |
| "koi_model_snr": ["tce_model_snr", "model_snr", "snr"], | |
| "koi_num_transits": [ | |
| "tce_num_transits", | |
| "num_transits", | |
| "ntransits", | |
| "tran_count", | |
| ], | |
| } | |
| def first_present(candidates, cols_set): | |
| for name in candidates: | |
| if name in cols_set: | |
| return name | |
| for name in candidates: | |
| found = [c for c in cols_set if name in c] | |
| if found: | |
| return found[0] | |
| return None | |
| cols_set = set(toi_sample.columns) | |
| results = [] | |
| for idx, row in toi_sample.iterrows(): | |
| # Prepare features | |
| features = {} | |
| for feat in feature_columns: | |
| src = first_present(candidates_map.get(feat, []), cols_set) | |
| if src and src in row and pd.notna(row[src]): | |
| features[feat] = float(row[src]) | |
| else: | |
| features[feat] = train_medians.get(feat, 0) | |
| # Predict | |
| result = predict_single(features) | |
| if "error" not in result: | |
| results.append( | |
| { | |
| "TOI": row.get("toi", f"tOI-{idx}"), | |
| "Disposition": row.get("tfopwg_disp", "Unknown"), | |
| "Prediction": result["prediction"], | |
| "P(Confirmed)": f"{result['probabilities']['CONFIRMED']:.3f}", | |
| "P(Candidate)": f"{result['probabilities']['CANDIDATE']:.3f}", | |
| "P(False Positive)": f"{result['probabilities']['FALSE_POSITIVE']:.3f}", | |
| } | |
| ) | |
| if not results: | |
| return "β Could not generate predictions" | |
| result_df = pd.DataFrame(results) | |
| return f"**TOI Predictions:**\n\n{result_df.to_markdown(index=False)}" | |
| except Exception as e: | |
| return f"β Error: {str(e)}" | |
| def predict_manual( | |
| period, | |
| duration, | |
| depth, | |
| prad, | |
| srad, | |
| teq, | |
| steff, | |
| slogg, | |
| smet, | |
| kepmag, | |
| snr, | |
| num_transits, | |
| ): | |
| """Function for manual prediction in Gradio""" | |
| try: | |
| result = predict_from_dict( | |
| period, | |
| duration, | |
| depth, | |
| prad, | |
| srad, | |
| teq, | |
| steff, | |
| slogg, | |
| smet, | |
| kepmag, | |
| snr, | |
| num_transits, | |
| ) | |
| if "error" in result: | |
| return f"β {result['error']}" | |
| output = f"**Prediction:** {result['prediction']}\n\n**Probabilities:**\n" | |
| for clase, prob in result["probabilities"].items(): | |
| output += f"- {clase}: {prob:.3f}\n" | |
| return output | |
| except Exception as e: | |
| return f"β Error: {str(e)}" | |
| # ==================== GRADIO INTERFACE ==================== | |
| with gr.Blocks(theme=gr.themes.Soft(), title="Eco Finder API") as demo: | |
| gr.Markdown("# π Eco Finder API") | |
| gr.Markdown("Exoplanet classifier") | |
| with gr.Tab("π― API Prediction"): | |
| gr.Markdown("### Endpoint for frontend consumption") | |
| gr.Markdown(""" | |
| **URL:** `https://jarpalucas-echo-finder-api.hf.space/api/predict` | |
| **Method:** POST | |
| **Content-Type:** application/json | |
| **Usage example with curl:** | |
| ```bash | |
| curl -X POST "https://jarpalucas-echo-finder-api.hf.space/api/predict" \\ | |
| -H "Content-Type: application/json" \\ | |
| -d '{ | |
| "koi_period": 10.0, | |
| "koi_duration": 5.0, | |
| "koi_depth": 1000.0, | |
| "koi_prad": 2.0, | |
| "koi_srad": 1.0, | |
| "koi_teq": 1000.0, | |
| "koi_steff": 6000.0, | |
| "koi_slogg": 4.5, | |
| "koi_smet": 0.0, | |
| "koi_kepmag": 12.0, | |
| "koi_model_snr": 10.0, | |
| "koi_num_transits": 3.0 | |
| }' | |
| ``` | |
| """) | |
| # Inputs to test the API locally | |
| with gr.Row(): | |
| with gr.Column(): | |
| period = gr.Number(label="koi_period", value=10.0) | |
| duration = gr.Number(label="koi_duration", value=5.0) | |
| depth = gr.Number(label="koi_depth", value=1000.0) | |
| prad = gr.Number(label="koi_prad", value=2.0) | |
| with gr.Column(): | |
| srad = gr.Number(label="koi_srad", value=1.0) | |
| teq = gr.Number(label="koi_teq", value=1000.0) | |
| steff = gr.Number(label="koi_steff", value=6000.0) | |
| slogg = gr.Number(label="koi_slogg", value=4.5) | |
| with gr.Column(): | |
| smet = gr.Number(label="koi_smet", value=0.0) | |
| kepmag = gr.Number(label="koi_kepmag", value=12.0) | |
| snr = gr.Number(label="koi_model_snr", value=10.0) | |
| num_transits = gr.Number(label="koi_num_transits", value=3.0) | |
| api_btn = gr.Button("π Test Prediction") | |
| api_output = gr.JSON() | |
| api_btn.click( | |
| fn=predict_from_dict, | |
| inputs=[ | |
| period, | |
| duration, | |
| depth, | |
| prad, | |
| srad, | |
| teq, | |
| steff, | |
| slogg, | |
| smet, | |
| kepmag, | |
| snr, | |
| num_transits, | |
| ], | |
| outputs=api_output, | |
| ) | |
| with gr.Tab("π Real-time TOI"): | |
| gr.Markdown("Real-time TOI object predictions") | |
| toi_btn = gr.Button("π Analyze TOI") | |
| toi_output = gr.Markdown() | |
| toi_btn.click(predict_toi_realtime, outputs=toi_output) | |
| with gr.Tab("π Manual Interface"): | |
| gr.Markdown("Manual interface for predictions") | |
| manual_btn = gr.Button("π― Predict") | |
| manual_output = gr.Markdown() | |
| manual_btn.click( | |
| fn=predict_manual, | |
| inputs=[ | |
| period, | |
| duration, | |
| depth, | |
| prad, | |
| srad, | |
| teq, | |
| steff, | |
| slogg, | |
| smet, | |
| kepmag, | |
| snr, | |
| num_transits, | |
| ], | |
| outputs=manual_output, | |
| ) | |
| print("π Application started successfully!") | |
| print("π Interface available at: /") | |
| print("π API endpoint available at: /api/predict") | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860, share=False) | |