jefffffff9 Claude Sonnet 4.6 commited on
Commit
3d43385
·
1 Parent(s): a5737ac

Fix Kaggle trigger: use kaggle CLI push instead of fake /run endpoint

Browse files

The Kaggle REST API has no /run endpoint. The correct way to trigger
a new kernel run is kaggle kernels push, which creates a new kernel
version (which immediately starts executing on Kaggle's servers).

New approach:
- Reads notebook + kernel-metadata.json from the Space filesystem
- Writes temporary kaggle.json from KAGGLE_USERNAME/KAGGLE_KEY secrets
- Runs: python -m kaggle kernels push -p notebooks/
- Returns the CLI output so errors are visible in the UI

Also logs auto-trigger result to Space logs instead of silently failing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +53 -19
app.py CHANGED
@@ -609,26 +609,56 @@ def _count_corrections() -> int:
609
 
610
 
611
  def _trigger_kaggle_training(lang: str = "bam") -> str:
612
- """Fire the Kaggle kernel via REST API if credentials are configured."""
 
 
 
 
 
 
613
  if not KAGGLE_USERNAME or not KAGGLE_KEY:
614
- return "⚠️ KAGGLE_USERNAME / KAGGLE_KEY not set in Space secrets — auto-trigger disabled."
615
- try:
616
- import urllib.request, urllib.parse, base64
617
- token = base64.b64encode(f"{KAGGLE_USERNAME}:{KAGGLE_KEY}".encode()).decode()
618
- url = f"https://www.kaggle.com/api/v1/kernels/{KAGGLE_KERNEL_SLUG}/run"
619
- body = json.dumps({"enableGpu": True}).encode()
620
- req = urllib.request.Request(
621
- url, data=body, method="POST",
622
- headers={
623
- "Authorization": f"Basic {token}",
624
- "Content-Type": "application/json",
625
- },
 
 
 
 
 
 
 
 
 
626
  )
627
- with urllib.request.urlopen(req, timeout=15) as r:
628
- resp = json.loads(r.read())
629
- return f"✅ Kaggle training triggered! Run ID: {resp.get('currentRunningVersion', 'started')}"
630
- except Exception as e:
631
- return f"❌ Kaggle trigger failed: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
 
633
 
634
  def _maybe_auto_trigger() -> None:
@@ -637,7 +667,11 @@ def _maybe_auto_trigger() -> None:
637
  return
638
  count = _count_corrections()
639
  if count > 0 and count % AUTO_TRAIN_THRESHOLD == 0:
640
- threading.Thread(target=_trigger_kaggle_training, daemon=True).start()
 
 
 
 
641
 
642
 
643
  # ── Bulk upload handler ────────────────────────────────────────────────────────
 
609
 
610
 
611
  def _trigger_kaggle_training(lang: str = "bam") -> str:
612
+ """
613
+ Push the master trainer notebook to Kaggle via the kaggle CLI,
614
+ which creates a new kernel version (i.e. triggers a run).
615
+
616
+ Requires KAGGLE_USERNAME + KAGGLE_KEY Space secrets.
617
+ The notebook is read from notebooks/ inside the Space filesystem.
618
+ """
619
  if not KAGGLE_USERNAME or not KAGGLE_KEY:
620
+ return "⚠️ KAGGLE_USERNAME / KAGGLE_KEY not set in Space secrets."
621
+
622
+ import subprocess, tempfile, shutil
623
+
624
+ notebooks_dir = ROOT / "notebooks"
625
+ meta_file = notebooks_dir / "kernel-metadata.json"
626
+ nb_file = notebooks_dir / "kaggle_master_trainer.ipynb"
627
+
628
+ if not nb_file.exists():
629
+ return " notebooks/kaggle_master_trainer.ipynb not found in Space."
630
+ if not meta_file.exists():
631
+ return "❌ notebooks/kernel-metadata.json not found in Space."
632
+
633
+ # Write kaggle.json to a temp dir so the CLI authenticates
634
+ with tempfile.TemporaryDirectory() as tmpdir:
635
+ kaggle_cfg = Path(tmpdir) / ".kaggle"
636
+ kaggle_cfg.mkdir()
637
+ creds_file = kaggle_cfg / "kaggle.json"
638
+ creds_file.write_text(
639
+ json.dumps({"username": KAGGLE_USERNAME, "key": KAGGLE_KEY}),
640
+ encoding="utf-8",
641
  )
642
+ creds_file.chmod(0o600)
643
+
644
+ env = {
645
+ **__import__("os").environ,
646
+ "KAGGLE_CONFIG_DIR": str(kaggle_cfg),
647
+ "PYTHONUTF8": "1",
648
+ "PYTHONIOENCODING": "utf-8",
649
+ }
650
+
651
+ result = subprocess.run(
652
+ ["python", "-m", "kaggle", "kernels", "push", "-p", str(notebooks_dir)],
653
+ capture_output=True, text=True, timeout=60, env=env,
654
+ )
655
+
656
+ if result.returncode == 0:
657
+ output = (result.stdout or "").strip()
658
+ return f"✅ Kaggle training triggered!\n{output or 'Kernel version created — check Kaggle for progress.'}"
659
+ else:
660
+ err = (result.stderr or result.stdout or "unknown error").strip()
661
+ return f"❌ Kaggle push failed:\n{err}"
662
 
663
 
664
  def _maybe_auto_trigger() -> None:
 
667
  return
668
  count = _count_corrections()
669
  if count > 0 and count % AUTO_TRAIN_THRESHOLD == 0:
670
+ import logging
671
+ def _run():
672
+ msg = _trigger_kaggle_training()
673
+ logging.getLogger(__name__).info("Auto-trigger: %s", msg)
674
+ threading.Thread(target=_run, daemon=True).start()
675
 
676
 
677
  # ── Bulk upload handler ────────────────────────────────────────────────────────