@danhachuel/thunderbolt 0.3.95 → 0.3.96

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 CHANGED
@@ -22,7 +22,7 @@ try:
22
22
  except (OSError, json.JSONDecodeError):
23
23
  APP_VERSION = ""
24
24
 
25
- from hermes_ui.domain import STAGES, create_batch, create_channel, create_tasks_for_batch, delete_channel, delete_task, pipeline_summary, set_channel_defaults, transition_task, update_channel, update_channel_video
25
+ from hermes_ui.domain import STAGES, create_batch, create_channel, create_tasks_for_batch, delete_channel, delete_task, pipeline_summary, retry_task_with_current_settings, set_channel_defaults, transition_task, update_channel, update_channel_video
26
26
  from hermes_ui.drafts import list_drafts, save_draft
27
27
  from hermes_ui.automation_worker import load_worker_status
28
28
  from hermes_ui.pipeline_worker import load_pipeline_worker_status, recover_stale_tasks, STALE_TASK_SECONDS, WORKER_HEARTBEAT_TIMEOUT_SECONDS
@@ -4054,7 +4054,7 @@ def render_automation():
4054
4054
 
4055
4055
  st.divider()
4056
4056
  st.subheader("Vídeos cadastrados")
4057
- st.caption("Start retoma as etapas já concluídas e só gera novamente o que ainda não estiver pronto. Apagar remove o card da fila após confirmação e preserva os artefactos locais.")
4057
+ st.caption("Start retoma as etapas já concluídas e só gera novamente o que ainda não estiver pronto. Em tarefas falhadas ou bloqueadas, a nova tentativa lê as chaves, prioridades e configurações actualmente guardadas. Apagar remove o card da fila após confirmação e preserva os artefactos locais.")
4058
4058
  tasks = load_video_tasks_for_catalog()
4059
4059
  if not tasks:
4060
4060
  st.info("Ainda não existem vídeos cadastrados.")
@@ -4074,7 +4074,10 @@ def render_automation():
4074
4074
  start_col, stop_col, delete_col = st.columns(3)
4075
4075
  with start_col:
4076
4076
  if st.button("Start", key=f"automation_start_{task['id']}", use_container_width=True, disabled=state not in {"to_do", "blocked", "failed"}):
4077
- transition_task(task["id"], "doing")
4077
+ if state in {"failed", "blocked"}:
4078
+ retry_task_with_current_settings(task["id"])
4079
+ else:
4080
+ transition_task(task["id"], "doing")
4078
4081
  st.rerun()
4079
4082
  with stop_col:
4080
4083
  if st.button("Stop", key=f"automation_stop_{task['id']}", use_container_width=True, disabled=state != "doing"):
@@ -328,6 +328,37 @@ def transition_task(task_id: str, state: str | None = None, stage: str | None =
328
328
  return None
329
329
 
330
330
 
331
+ def retry_task_with_current_settings(task_id: str) -> dict[str, Any] | None:
332
+ """Queue a failed or blocked task without persisting API credentials or provider snapshots.
333
+
334
+ The pipeline reloads settings.json immediately before every execution, so a
335
+ retry always uses the currently saved API keys, active provider priorities,
336
+ and provider endpoints while retaining the task's completed artefacts.
337
+ """
338
+ tasks = read_json("tasks.json", [])
339
+ for task in tasks:
340
+ if task.get("id") != task_id:
341
+ continue
342
+ previous_state = str(task.get("state") or "")
343
+ if previous_state not in {"failed", "blocked"}:
344
+ raise ValueError("Apenas tarefas falhadas ou bloqueadas podem ser retomadas.")
345
+ try:
346
+ retry_count = int(task.get("retry_count") or 0)
347
+ except (TypeError, ValueError):
348
+ retry_count = 0
349
+ task["state"] = "to_do"
350
+ task["error"] = None
351
+ task["retry_count"] = retry_count + 1
352
+ task["retry_requested_at"] = now()
353
+ task["retry_config_source"] = "settings.json_at_execution"
354
+ for field in ("failure_api", "failure_provider", "failure_service", "failure_config_fields"):
355
+ task.pop(field, None)
356
+ task["updated_at"] = now()
357
+ write_json("tasks.json", tasks)
358
+ return task
359
+ return None
360
+
361
+
331
362
  def pipeline_summary() -> dict[str, Any]:
332
363
  tasks = read_json("tasks.json", [])
333
364
  channels = read_json("channels.json", [])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.3.95",
3
+ "version": "0.3.96",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",