Spaces:
Paused
Paused
Tilawa Server commited on
Commit ·
a8d14b8
1
Parent(s): 650d7e9
S64: fix 5 bugs — dedup v11.0, RAM guard, watchdog, engine_log, status
Browse files
app.py
CHANGED
|
@@ -96,13 +96,11 @@ ENGINE_SCRIPTS = {
|
|
| 96 |
"v11.1": BASE / "true_engine_itiqan_v2_fixed.py",
|
| 97 |
"v11.2": BASE / "engine_isteidad_v12.py",
|
| 98 |
"v10.0": BASE / "engine_v100.py",
|
| 99 |
-
"v11.0": BASE / "engine_tajalli_v1.py",
|
| 100 |
"v9.0": BASE / "engine_v90.py",
|
| 101 |
"v8.5": BASE / "engine_v85.py",
|
| 102 |
-
"v8.4": BASE / "engine_v84.py",
|
| 103 |
"v8.0": BASE / "engine_v80.py",
|
| 104 |
"v7.0": BASE / "engine_v70.py",
|
| 105 |
-
}
|
| 106 |
REF_DIR = BASE / "reference_audio"
|
| 107 |
CHUNK_SIZE = 4 * 1024 * 1024 # 4 MB upload chunks
|
| 108 |
MAX_DOWNLOAD_CHUNK = 32 * 1024 * 1024 # 32 MB max per download_chunk call
|
|
@@ -519,10 +517,10 @@ def _run_engine(job_id):
|
|
| 519 |
|
| 520 |
# ── RAM guard: require ≥ 3.5 GB available before launching subprocess ──
|
| 521 |
ram_gb = _available_ram_gb()
|
| 522 |
-
if ram_gb <
|
| 523 |
with JOBS_LOCK:
|
| 524 |
job["status"] = "error"
|
| 525 |
-
job["error"] = f"Insufficient RAM ({ram_gb:.1f} GB free, need
|
| 526 |
job["label"] = "خطأ: ذاكرة غير كافية — أعد المحاولة لاحقاً"
|
| 527 |
return
|
| 528 |
|
|
@@ -555,16 +553,18 @@ def _run_engine(job_id):
|
|
| 555 |
for rf in ref_files[:3]:
|
| 556 |
cmd += ["--ref", str(rf)]
|
| 557 |
|
|
|
|
|
|
|
| 558 |
proc = subprocess.Popen(
|
| 559 |
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
|
| 560 |
)
|
| 561 |
|
| 562 |
-
engine_lines = [] #
|
| 563 |
for line in proc.stdout:
|
| 564 |
line = line.strip()
|
| 565 |
-
engine_lines.append(line)
|
| 566 |
-
if len(engine_lines) > 60:
|
| 567 |
-
engine_lines.pop(0)
|
| 568 |
# Progress markers — plain scalar writes, no lock needed
|
| 569 |
if "Pass 1" in line or "[٧]" in line or "[١]" in line:
|
| 570 |
job["progress"] = 45; job["label"] = "Pass 1 — تحليل الطيف..."
|
|
@@ -595,17 +595,31 @@ def _run_engine(job_id):
|
|
| 595 |
except Exception:
|
| 596 |
pass
|
| 597 |
|
| 598 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 599 |
_out = Path(job["out_path"])
|
| 600 |
# engine_v100 exits 1 for score<85 but still writes the file.
|
| 601 |
# Use file existence + size as the real success signal.
|
| 602 |
if _out.exists() and _out.stat().st_size > 0:
|
| 603 |
success = True
|
| 604 |
else:
|
| 605 |
-
job["engine_rc"]
|
|
|
|
| 606 |
|
| 607 |
except Exception as exc:
|
| 608 |
job["engine_error"] = str(exc)
|
|
|
|
| 609 |
|
| 610 |
if not success:
|
| 611 |
# Status transition: running → error (held under lock)
|
|
@@ -664,10 +678,13 @@ def status(job_id):
|
|
| 664 |
if "error" in job:
|
| 665 |
resp["error"] = job["error"]
|
| 666 |
|
| 667 |
-
#
|
| 668 |
-
if job.get("status") == "error"
|
| 669 |
-
|
| 670 |
-
|
|
|
|
|
|
|
|
|
|
| 671 |
|
| 672 |
return jsonify(resp)
|
| 673 |
|
|
|
|
| 96 |
"v11.1": BASE / "true_engine_itiqan_v2_fixed.py",
|
| 97 |
"v11.2": BASE / "engine_isteidad_v12.py",
|
| 98 |
"v10.0": BASE / "engine_v100.py",
|
|
|
|
| 99 |
"v9.0": BASE / "engine_v90.py",
|
| 100 |
"v8.5": BASE / "engine_v85.py",
|
|
|
|
| 101 |
"v8.0": BASE / "engine_v80.py",
|
| 102 |
"v7.0": BASE / "engine_v70.py",
|
| 103 |
+
} # S64: removed duplicate v11.0 + dead v8.4
|
| 104 |
REF_DIR = BASE / "reference_audio"
|
| 105 |
CHUNK_SIZE = 4 * 1024 * 1024 # 4 MB upload chunks
|
| 106 |
MAX_DOWNLOAD_CHUNK = 32 * 1024 * 1024 # 32 MB max per download_chunk call
|
|
|
|
| 517 |
|
| 518 |
# ── RAM guard: require ≥ 3.5 GB available before launching subprocess ──
|
| 519 |
ram_gb = _available_ram_gb()
|
| 520 |
+
if ram_gb < 1.5: # S64: raised from 0.5 — engines need ~3.5GB
|
| 521 |
with JOBS_LOCK:
|
| 522 |
job["status"] = "error"
|
| 523 |
+
job["error"] = f"Insufficient RAM ({ram_gb:.1f} GB free, need 1.5 GB)"
|
| 524 |
job["label"] = "خطأ: ذاكرة غير كافية — أعد المحاولة لاحقاً"
|
| 525 |
return
|
| 526 |
|
|
|
|
| 553 |
for rf in ref_files[:3]:
|
| 554 |
cmd += ["--ref", str(rf)]
|
| 555 |
|
| 556 |
+
# S64: watchdog — kill engine if it runs > 35 min
|
| 557 |
+
_ENGINE_TIMEOUT = 35 * 60 # 35 minutes
|
| 558 |
proc = subprocess.Popen(
|
| 559 |
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
|
| 560 |
)
|
| 561 |
|
| 562 |
+
engine_lines = [] # last 60 lines for debug
|
| 563 |
for line in proc.stdout:
|
| 564 |
line = line.strip()
|
| 565 |
+
engine_lines.append(line)
|
| 566 |
+
if len(engine_lines) > 60:
|
| 567 |
+
engine_lines.pop(0)
|
| 568 |
# Progress markers — plain scalar writes, no lock needed
|
| 569 |
if "Pass 1" in line or "[٧]" in line or "[١]" in line:
|
| 570 |
job["progress"] = 45; job["label"] = "Pass 1 — تحليل الطيف..."
|
|
|
|
| 595 |
except Exception:
|
| 596 |
pass
|
| 597 |
|
| 598 |
+
# S64: timed wait — kill if engine hangs past timeout
|
| 599 |
+
try:
|
| 600 |
+
proc.wait(timeout=_ENGINE_TIMEOUT)
|
| 601 |
+
except subprocess.TimeoutExpired:
|
| 602 |
+
proc.kill()
|
| 603 |
+
proc.wait()
|
| 604 |
+
job["engine_error"] = "Engine timed out after 35 min — killed"
|
| 605 |
+
job["engine_log"] = engine_lines
|
| 606 |
+
with JOBS_LOCK:
|
| 607 |
+
job["status"] = "error"
|
| 608 |
+
job["error"] = "Engine timed out (35 min limit)"
|
| 609 |
+
job["label"] = "خطأ: انتهت مهلة المحرك — أعد المحاولة"
|
| 610 |
+
return
|
| 611 |
_out = Path(job["out_path"])
|
| 612 |
# engine_v100 exits 1 for score<85 but still writes the file.
|
| 613 |
# Use file existence + size as the real success signal.
|
| 614 |
if _out.exists() and _out.stat().st_size > 0:
|
| 615 |
success = True
|
| 616 |
else:
|
| 617 |
+
job["engine_rc"] = proc.returncode
|
| 618 |
+
job["engine_log"] = engine_lines # S64: persist last 60 lines
|
| 619 |
|
| 620 |
except Exception as exc:
|
| 621 |
job["engine_error"] = str(exc)
|
| 622 |
+
job["engine_log"] = engine_lines # S64: persist for /status debug
|
| 623 |
|
| 624 |
if not success:
|
| 625 |
# Status transition: running → error (held under lock)
|
|
|
|
| 678 |
if "error" in job:
|
| 679 |
resp["error"] = job["error"]
|
| 680 |
|
| 681 |
+
# S64: expose engine debug info on error
|
| 682 |
+
if job.get("status") == "error":
|
| 683 |
+
if "engine_log" in job:
|
| 684 |
+
resp["engine_log"] = job["engine_log"] # last 60 stdout lines
|
| 685 |
+
if "engine_error" in job:
|
| 686 |
+
resp["engine_error"] = job["engine_error"] # exception message
|
| 687 |
+
resp["engine_rc"] = job.get("engine_rc")
|
| 688 |
|
| 689 |
return jsonify(resp)
|
| 690 |
|