@danhachuel/thunderbolt 0.3.83 → 0.3.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/main.py +1 -1
- package/hermes_ui/pipeline_worker.py +34 -1
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -3505,7 +3505,7 @@ def _render_pipeline_worker_banner(worker_status: dict[str, Any], active_count:
|
|
|
3505
3505
|
st.warning("Worker de vídeo sem heartbeat recente. O launcher deve estar aberto para processar as tarefas.")
|
|
3506
3506
|
heartbeat = worker_status.get("last_heartbeat_at") or worker_status.get("updated_at")
|
|
3507
3507
|
if heartbeat:
|
|
3508
|
-
st.caption(f"{_pipeline_time_age(heartbeat)} ·
|
|
3508
|
+
st.caption(f"{_pipeline_time_age(heartbeat)} · vídeo: até 90 min com watchdog de inactividade de 10 min")
|
|
3509
3509
|
if worker_status.get("last_error"):
|
|
3510
3510
|
st.error(f"Último erro do worker: {worker_status['last_error']}")
|
|
3511
3511
|
|
|
@@ -28,7 +28,8 @@ from hermes_ui.thumbnail_generation import ThumbnailGenerationError, generate_th
|
|
|
28
28
|
PIPELINE_LOCK_FILENAME = "pipeline_worker.lock"
|
|
29
29
|
PIPELINE_LOG_FILENAME = "pipeline_worker.json"
|
|
30
30
|
VIDEO_TIMEOUT_SECONDS = 20 * 60
|
|
31
|
-
LONG_STOCK_VIDEO_TIMEOUT_SECONDS =
|
|
31
|
+
LONG_STOCK_VIDEO_TIMEOUT_SECONDS = 90 * 60
|
|
32
|
+
VIDEO_IDLE_TIMEOUT_SECONDS = 10 * 60
|
|
32
33
|
STALE_TASK_SECONDS = VIDEO_TIMEOUT_SECONDS + 5 * 60
|
|
33
34
|
WORKER_HEARTBEAT_TIMEOUT_SECONDS = 15
|
|
34
35
|
CASCADE_STAGE_ORDER = ("topic", "script", "title", "keywords", "video", "thumbnail_prompt", "thumbnail", "upload")
|
|
@@ -636,6 +637,17 @@ def _task_stale_timeout_seconds(task: dict[str, Any]) -> int:
|
|
|
636
637
|
return STALE_TASK_SECONDS
|
|
637
638
|
|
|
638
639
|
|
|
640
|
+
def _latest_helper_log_activity(log_path: Path | None, previous_mtime: float) -> tuple[float, bool]:
|
|
641
|
+
"""Return whether the helper's file log advanced without reading its content."""
|
|
642
|
+
if log_path is None:
|
|
643
|
+
return previous_mtime, False
|
|
644
|
+
try:
|
|
645
|
+
current_mtime = log_path.stat().st_mtime
|
|
646
|
+
except OSError:
|
|
647
|
+
return previous_mtime, False
|
|
648
|
+
return (current_mtime, current_mtime > previous_mtime)
|
|
649
|
+
|
|
650
|
+
|
|
639
651
|
def _mpt_aspect_ratio(value: Any, format_value: Any = "") -> str:
|
|
640
652
|
raw = str(value or format_value or "").strip().casefold()
|
|
641
653
|
if raw in {"landscape 16:9", "wide", "16:9", "landscape"}:
|
|
@@ -915,6 +927,9 @@ def _run_video_helper(task: dict[str, Any]) -> Path:
|
|
|
915
927
|
output_finished = False
|
|
916
928
|
last_heartbeat = 0.0
|
|
917
929
|
last_output_line = ""
|
|
930
|
+
last_activity_at = started_at
|
|
931
|
+
helper_log_path: Path | None = None
|
|
932
|
+
helper_log_mtime = 0.0
|
|
918
933
|
try:
|
|
919
934
|
while True:
|
|
920
935
|
try:
|
|
@@ -924,9 +939,19 @@ def _run_video_helper(task: dict[str, Any]) -> Path:
|
|
|
924
939
|
elif line:
|
|
925
940
|
last_output_line = _redact_helper_output(line).strip()
|
|
926
941
|
output_lines.append(line)
|
|
942
|
+
last_activity_at = time.monotonic()
|
|
943
|
+
match = re.search(r"full generation log:\s*(.+)$", last_output_line, flags=re.IGNORECASE)
|
|
944
|
+
if match:
|
|
945
|
+
candidate = Path(match.group(1).strip()).expanduser()
|
|
946
|
+
helper_log_path = candidate if candidate.is_file() else None
|
|
927
947
|
except queue.Empty:
|
|
928
948
|
pass
|
|
929
949
|
elapsed = time.monotonic() - started_at
|
|
950
|
+
helper_log_mtime, helper_log_advanced = _latest_helper_log_activity(helper_log_path, helper_log_mtime)
|
|
951
|
+
if helper_log_advanced:
|
|
952
|
+
last_activity_at = time.monotonic()
|
|
953
|
+
if not last_output_line:
|
|
954
|
+
last_output_line = "[MoneyPrinterTurbo] actividade de geração confirmada"
|
|
930
955
|
if elapsed - last_heartbeat >= 5:
|
|
931
956
|
# O helper expõe o resultado final, mas não uma percentagem estável.
|
|
932
957
|
# Mantemos uma faixa reservada para a etapa de vídeo e avançamos-a
|
|
@@ -958,6 +983,14 @@ def _run_video_helper(task: dict[str, Any]) -> Path:
|
|
|
958
983
|
message = f"A etapa Vídeo excedeu o limite de {timeout_seconds // 60} minutos e foi encerrada."
|
|
959
984
|
metadata = _failure_attribution(task, settings, "video", error=message)
|
|
960
985
|
raise PipelineError(_failure_message(message, metadata), failure_metadata=metadata)
|
|
986
|
+
if time.monotonic() - last_activity_at >= VIDEO_IDLE_TIMEOUT_SECONDS:
|
|
987
|
+
_stop_process(process)
|
|
988
|
+
message = (
|
|
989
|
+
"A etapa Vídeo não apresentou actividade comprovada do motor durante "
|
|
990
|
+
f"{VIDEO_IDLE_TIMEOUT_SECONDS // 60} minutos e foi encerrada."
|
|
991
|
+
)
|
|
992
|
+
metadata = _failure_attribution(task, settings, "video", error=message)
|
|
993
|
+
raise PipelineError(_failure_message(message, metadata), failure_metadata=metadata)
|
|
961
994
|
finally:
|
|
962
995
|
reader.join(timeout=2)
|
|
963
996
|
_persist_video_diagnostics(task, "\n".join(output_lines))
|
package/package.json
CHANGED