| """ |
| Master retraining script for NutriLoop AI. |
| Called by GitHub Actions nightly cron or manually. |
| Runs: Supabase data pull → Prophet training → Cluster training → HF upload. |
| """ |
| import os |
| import sys |
| from datetime import datetime |
| from pathlib import Path |
|
|
| |
| project_root = Path(__file__).parent.parent |
| sys.path.insert(0, str(project_root)) |
|
|
| from dotenv import load_dotenv |
| load_dotenv() |
|
|
| from supabase import create_client |
|
|
|
|
| def run_retrain(): |
| """ |
| Execute the full NutriLoop retraining pipeline. |
| |
| Steps: |
| 1. Pull latest data from Supabase sales_logs |
| 2. Train Prophet models (train_prophet.py) |
| 3. Train KMeans clusters (train_clusters.py) |
| 4. Upload models to Hugging Face (upload_models.py) |
| 5. Log success/failure to Supabase retrain_log |
| """ |
| print("=" * 60) |
| print("[NutriLoop Retrain] Starting full retraining pipeline") |
| print("=" * 60) |
| start_time = datetime.now() |
|
|
| supabase_url = os.environ.get("SUPABASE_URL") |
| supabase_key = os.environ.get("SUPABASE_KEY") |
|
|
| if not supabase_url or not supabase_key: |
| print("[NutriLoop Retrain] ERROR: SUPABASE_URL and SUPABASE_KEY not set") |
| sys.exit(1) |
|
|
| client = create_client(supabase_url, supabase_key) |
|
|
| |
| print("\n[Step 1] Checking Supabase data...") |
| try: |
| response = client.table("sales_logs").select("id").limit(1).execute() |
| print("[Step 1] Supabase connection OK") |
| except Exception as e: |
| print(f"[Step 1] ERROR: Cannot connect to Supabase: {e}") |
| _log_failure(client, str(e)) |
| sys.exit(1) |
|
|
| |
| print("\n[Step 2] Training Global Multivariate Model...") |
| try: |
| from training.train_global import train_global_model |
| success, registry = train_global_model() |
| if not success: |
| raise Exception("Global model training failed.") |
| print(f"[Step 2] Global Model trained successfully") |
| except Exception as e: |
| print(f"[Step 2] ERROR: Global training failed: {e}") |
| _log_failure(client, f"Prophet training: {e}") |
| sys.exit(1) |
|
|
| |
| print("\n[Step 3] Training KMeans clusters...") |
| try: |
| from training.train_clusters import train_clusters |
| n_clusters, _ = train_clusters() |
| print(f"[Step 3] Clusters: {n_clusters} clusters created") |
| except Exception as e: |
| print(f"[Step 3] WARNING: Cluster training failed: {e}") |
| |
| n_clusters = 0 |
|
|
| |
| print("\n[Step 4] Uploading models to Hugging Face...") |
| hf_uploaded = False |
| try: |
| from training.upload_models import upload_models |
| upload_models() |
| hf_uploaded = True |
| print("[Step 4] HF upload complete") |
| except Exception as e: |
| print(f"[Step 4] WARNING: HF upload failed: {e}") |
| |
|
|
| |
| elapsed = (datetime.now() - start_time).total_seconds() |
| avg_mae = sum(m["mae"] for m in registry.values()) / max(1, len(registry)) if registry else 0.0 |
|
|
| current_iso = datetime.now().isoformat() |
| try: |
| last_retrain_path = project_root / "models" / "last_retrain.txt" |
| last_retrain_path.parent.mkdir(exist_ok=True) |
| last_retrain_path.write_text(current_iso) |
| except Exception as e: |
| print(f"[Step 5] WARNING: Could not write last_retrain.txt: {e}") |
|
|
| try: |
| client.table("retrain_log").insert({ |
| "model_version": current_iso, |
| "rows_used": len(registry), |
| "mae_score": round(avg_mae, 4), |
| "status": "success", |
| }).execute() |
| except Exception as e: |
| print(f"[Step 5] WARNING: Could not log to retrain_log: {e}") |
|
|
| |
| hf_status = "Yes" if hf_uploaded else "No" |
| print("\n" + "=" * 60) |
| print(f"[NutriLoop Retrain] Complete in {elapsed:.1f}s") |
| print(f" Summary:") |
| print(f" Global model trained: {'Yes' if 'success' in locals() and success else 'No'}") |
| print(f" Cluster model trained: Yes") |
| print(f" Clusters: {n_clusters}") |
| print(f" Avg MAE: {avg_mae:.4f}") |
| print(f" Uploaded to HF: {hf_status}") |
| print("=" * 60) |
|
|
|
|
| def _log_failure(client, error_msg: str) -> None: |
| """Log a failed retrain run to Supabase.""" |
| try: |
| client.table("retrain_log").insert({ |
| "model_version": datetime.now().isoformat(), |
| "rows_used": 0, |
| "mae_score": 0.0, |
| "status": "failed", |
| "error_msg": error_msg, |
| }).execute() |
| except Exception: |
| pass |
|
|
|
|
| if __name__ == "__main__": |
| run_retrain() |