Update 38 prevent runtime self-rebuild commits
Browse files- CHANGELOG.md +5 -0
- README.md +2 -0
- app.py +28 -15
CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
| 1 |
# Changelog
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
## Update ke-37 - 2026-05-01
|
| 4 |
- Perbaiki training yang berhenti pada tahap `preparing_dataset`.
|
| 5 |
- Persiapan dataset, pseudo-label, training YOLO, penyimpanan `best.pt`, grafik PNG, confusion matrix, log, dan sync artefak kini berjalan dalam background worker sejak awal.
|
|
|
|
| 1 |
# Changelog
|
| 2 |
|
| 3 |
+
## Update ke-38 - 2026-05-02
|
| 4 |
+
- Cegah Space melakukan commit runtime ke repo Space yang sama secara default.
|
| 5 |
+
- Tambah guard `HF_ALLOW_SPACE_RUNTIME_COMMITS` untuk menghindari rebuild otomatis yang menyebabkan HTTP 503 setelah training.
|
| 6 |
+
- Minor update dinaikkan ke-38.
|
| 7 |
+
|
| 8 |
## Update ke-37 - 2026-05-01
|
| 9 |
- Perbaiki training yang berhenti pada tahap `preparing_dataset`.
|
| 10 |
- Persiapan dataset, pseudo-label, training YOLO, penyimpanan `best.pt`, grafik PNG, confusion matrix, log, dan sync artefak kini berjalan dalam background worker sejak awal.
|
README.md
CHANGED
|
@@ -115,10 +115,12 @@ Opsional environment variable:
|
|
| 115 |
- `HF_AUTOSYNC_INCLUDE_GLOBS` - pola file yang ingin disync (default `*.py,*.md,requirements.txt,.gitignore`)
|
| 116 |
- `HF_AUTOSYNC_MAX_FILE_MB` - batas ukuran file yang di-sync (default `200` MB)
|
| 117 |
- `HF_AUTOSYNC_IGNORE_GLOBS` - daftar pola file/folder yang diabaikan (dipisah koma)
|
|
|
|
| 118 |
|
| 119 |
Catatan sync otomatis setelah training:
|
| 120 |
- Jika `HF_MODEL_REPO` terisi, aplikasi akan upload `best.pt` ke `HF_MODEL_FILE`.
|
| 121 |
- Jika tidak terisi, training tetap jalan normal dan hanya menyimpan `best.pt` lokal.
|
|
|
|
| 122 |
|
| 123 |
Catatan auto-save hasil deteksi:
|
| 124 |
- Setelah inferensi sukses, aplikasi menyimpan gambar anotasi ke `evaluation_outputs/` lokal.
|
|
|
|
| 115 |
- `HF_AUTOSYNC_INCLUDE_GLOBS` - pola file yang ingin disync (default `*.py,*.md,requirements.txt,.gitignore`)
|
| 116 |
- `HF_AUTOSYNC_MAX_FILE_MB` - batas ukuran file yang di-sync (default `200` MB)
|
| 117 |
- `HF_AUTOSYNC_IGNORE_GLOBS` - daftar pola file/folder yang diabaikan (dipisah koma)
|
| 118 |
+
- `HF_ALLOW_SPACE_RUNTIME_COMMITS` - default `false`; aktifkan hanya jika benar-benar ingin app melakukan commit ke repo Space yang sedang berjalan
|
| 119 |
|
| 120 |
Catatan sync otomatis setelah training:
|
| 121 |
- Jika `HF_MODEL_REPO` terisi, aplikasi akan upload `best.pt` ke `HF_MODEL_FILE`.
|
| 122 |
- Jika tidak terisi, training tetap jalan normal dan hanya menyimpan `best.pt` lokal.
|
| 123 |
+
- Jika `HF_MODEL_REPO` menunjuk ke Space yang sama, upload runtime dinonaktifkan secara default agar Space tidak rebuild sendiri dan memunculkan 503.
|
| 124 |
|
| 125 |
Catatan auto-save hasil deteksi:
|
| 126 |
- Setelah inferensi sukses, aplikasi menyimpan gambar anotasi ke `evaluation_outputs/` lokal.
|
app.py
CHANGED
|
@@ -24,7 +24,7 @@ from ultralytics import YOLO
|
|
| 24 |
CM_INIT = {"tp": 0, "fp": 0, "fn": 0, "tn": 0, "person": 0, "not_person": 0}
|
| 25 |
EVAL_CLASSES = ["Person", "Not Person/Background"]
|
| 26 |
APP_VERSION = os.getenv("APP_VERSION", "v0.9.0")
|
| 27 |
-
APP_MINOR_UPDATE = os.getenv("APP_MINOR_UPDATE", "
|
| 28 |
|
| 29 |
|
| 30 |
# Gradio/Spaces can trigger noisy asyncio cleanup traces ("Invalid file descriptor: -1")
|
|
@@ -995,14 +995,16 @@ def _prepare_training_dataset(
|
|
| 995 |
def sync_best_to_hf(best_model_path: Path) -> str:
|
| 996 |
hf_repo = os.getenv("HF_MODEL_REPO", "").strip()
|
| 997 |
hf_file = os.getenv("HF_MODEL_FILE", "best.pt").strip() or "best.pt"
|
| 998 |
-
hf_repo_type = os.getenv("HF_MODEL_REPO_TYPE", "space").strip() or "space"
|
| 999 |
-
hf_token = os.getenv("HF_TOKEN", "").strip() or None
|
| 1000 |
-
|
| 1001 |
-
if not hf_repo:
|
| 1002 |
-
return "HF sync dilewati: set HF_MODEL_REPO untuk upload otomatis."
|
| 1003 |
-
|
| 1004 |
-
|
| 1005 |
-
|
|
|
|
|
|
|
| 1006 |
api.upload_file(
|
| 1007 |
path_or_fileobj=str(best_model_path),
|
| 1008 |
path_in_repo=hf_file,
|
|
@@ -1232,10 +1234,12 @@ def sync_training_artifacts_to_hf(
|
|
| 1232 |
repo_dir = os.getenv("HF_TRAINING_DIR", "space_training_archive").strip().strip("/")
|
| 1233 |
run_name = run_name or datetime.now().strftime("training_%Y%m%d_%H%M%S")
|
| 1234 |
|
| 1235 |
-
if not hf_repo:
|
| 1236 |
-
return "HF training artifact sync dilewati: set HF_MODEL_REPO."
|
| 1237 |
-
|
| 1238 |
-
|
|
|
|
|
|
|
| 1239 |
(results_csv, f"{repo_dir}/{run_name}/results.csv"),
|
| 1240 |
(log_file, f"{repo_dir}/{run_name}/training.log"),
|
| 1241 |
(chart_file, f"{repo_dir}/{run_name}/training_curve.png"),
|
|
@@ -1264,11 +1268,20 @@ def sync_training_artifacts_to_hf(
|
|
| 1264 |
return f"HF training artifact sync gagal: {err}"
|
| 1265 |
|
| 1266 |
|
| 1267 |
-
def _env_true(var_name: str, default: bool = False) -> bool:
|
| 1268 |
val = os.getenv(var_name)
|
| 1269 |
if val is None:
|
| 1270 |
return default
|
| 1271 |
-
return val.strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1272 |
|
| 1273 |
|
| 1274 |
def _parse_globs(raw_value: str) -> list[str]:
|
|
|
|
| 24 |
CM_INIT = {"tp": 0, "fp": 0, "fn": 0, "tn": 0, "person": 0, "not_person": 0}
|
| 25 |
EVAL_CLASSES = ["Person", "Not Person/Background"]
|
| 26 |
APP_VERSION = os.getenv("APP_VERSION", "v0.9.0")
|
| 27 |
+
APP_MINOR_UPDATE = os.getenv("APP_MINOR_UPDATE", "38")
|
| 28 |
|
| 29 |
|
| 30 |
# Gradio/Spaces can trigger noisy asyncio cleanup traces ("Invalid file descriptor: -1")
|
|
|
|
| 995 |
def sync_best_to_hf(best_model_path: Path) -> str:
|
| 996 |
hf_repo = os.getenv("HF_MODEL_REPO", "").strip()
|
| 997 |
hf_file = os.getenv("HF_MODEL_FILE", "best.pt").strip() or "best.pt"
|
| 998 |
+
hf_repo_type = os.getenv("HF_MODEL_REPO_TYPE", "space").strip() or "space"
|
| 999 |
+
hf_token = os.getenv("HF_TOKEN", "").strip() or None
|
| 1000 |
+
|
| 1001 |
+
if not hf_repo:
|
| 1002 |
+
return "HF sync dilewati: set HF_MODEL_REPO untuk upload otomatis."
|
| 1003 |
+
if _skip_runtime_commit_to_current_space(hf_repo, hf_repo_type):
|
| 1004 |
+
return "HF sync dilewati: commit runtime ke Space yang sama dinonaktifkan agar Space tidak rebuild sendiri."
|
| 1005 |
+
|
| 1006 |
+
try:
|
| 1007 |
+
api = HfApi(token=hf_token)
|
| 1008 |
api.upload_file(
|
| 1009 |
path_or_fileobj=str(best_model_path),
|
| 1010 |
path_in_repo=hf_file,
|
|
|
|
| 1234 |
repo_dir = os.getenv("HF_TRAINING_DIR", "space_training_archive").strip().strip("/")
|
| 1235 |
run_name = run_name or datetime.now().strftime("training_%Y%m%d_%H%M%S")
|
| 1236 |
|
| 1237 |
+
if not hf_repo:
|
| 1238 |
+
return "HF training artifact sync dilewati: set HF_MODEL_REPO."
|
| 1239 |
+
if _skip_runtime_commit_to_current_space(hf_repo, hf_repo_type):
|
| 1240 |
+
return "HF training artifact sync dilewati: commit runtime ke Space yang sama dinonaktifkan agar Space tidak rebuild sendiri."
|
| 1241 |
+
|
| 1242 |
+
upload_items: list[tuple[Path | None, str]] = [
|
| 1243 |
(results_csv, f"{repo_dir}/{run_name}/results.csv"),
|
| 1244 |
(log_file, f"{repo_dir}/{run_name}/training.log"),
|
| 1245 |
(chart_file, f"{repo_dir}/{run_name}/training_curve.png"),
|
|
|
|
| 1268 |
return f"HF training artifact sync gagal: {err}"
|
| 1269 |
|
| 1270 |
|
| 1271 |
+
def _env_true(var_name: str, default: bool = False) -> bool:
|
| 1272 |
val = os.getenv(var_name)
|
| 1273 |
if val is None:
|
| 1274 |
return default
|
| 1275 |
+
return val.strip().lower() in {"1", "true", "yes", "on"}
|
| 1276 |
+
|
| 1277 |
+
|
| 1278 |
+
def _skip_runtime_commit_to_current_space(repo_id: str, repo_type: str) -> bool:
|
| 1279 |
+
if repo_type != "space":
|
| 1280 |
+
return False
|
| 1281 |
+
if _env_true("HF_ALLOW_SPACE_RUNTIME_COMMITS", default=False):
|
| 1282 |
+
return False
|
| 1283 |
+
current_space = os.getenv("SPACE_ID", "").strip() or os.getenv("HF_SPACE_ID", "").strip()
|
| 1284 |
+
return bool(current_space and repo_id.strip() == current_space)
|
| 1285 |
|
| 1286 |
|
| 1287 |
def _parse_globs(raw_value: str) -> list[str]:
|