@danhachuel/thunderbolt 0.4.60 → 0.4.61

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
@@ -81,7 +81,7 @@ from hermes_ui.thumbnails import (
81
81
  )
82
82
  from hermes_ui.draft_video import DRAFT_SETTING_SECTIONS, missing_content_fields, missing_setting_sections, normalise_saved_script, setting_widget_suffixes
83
83
  from hermes_ui.creative_generation import CreativeGenerationError, generate_creative_package, generate_thumbnail_prompt, generate_title_and_keywords, generate_topic_for_channel, generate_video_description, generate_video_keywords, generate_video_update_metadata
84
- from hermes_ui.media_generation import MediaGenerationError, generate_image_for_card, generate_video_for_card
84
+ from hermes_ui.media_generation import MediaGenerationError, format_media_generation_error, generate_image_for_card, generate_video_for_card
85
85
  from integrations.platforms import IntegrationResult, TikTokAdapter, YouTubeAdapter, fetch_channel_videos_public
86
86
  from integrations.tiktok_public import fetch_public_tiktok_profile, normalize_tiktok_reference
87
87
  from integrations.youtube_update import YOUTUBE_UPDATE_VIDEO, YouTubeVideoUpdater
@@ -4662,6 +4662,8 @@ def render_thumbnails():
4662
4662
  )
4663
4663
  st.success("Lettering refeito; a imagem original foi usada como base.")
4664
4664
  st.rerun()
4665
+ except MediaGenerationError as exc:
4666
+ st.error(format_media_generation_error(exc, operation="refazer o lettering da thumbnail"))
4665
4667
  except ThumbnailGenerationError as exc:
4666
4668
  st.error(str(exc))
4667
4669
 
@@ -12,6 +12,7 @@ import binascii
12
12
  import json
13
13
  import mimetypes
14
14
  import os
15
+ import re
15
16
  import shutil
16
17
  import subprocess
17
18
  import tempfile
@@ -49,6 +50,35 @@ class MediaGenerationError(RuntimeError):
49
50
  self.provider_errors = list(provider_errors or [])
50
51
 
51
52
 
53
+ def format_media_generation_error(exc: MediaGenerationError, *, operation: str = "gerar a imagem") -> str:
54
+ """Return a concise Portuguese diagnostic instead of exposing only a traceback."""
55
+ text = str(exc or "").strip()
56
+ errors = list(exc.provider_errors or [])
57
+ if errors:
58
+ status_codes = [int(match.group(1)) for item in errors for match in [re.search(r"\bHTTP\s+(\d{3})\b", str(item), re.IGNORECASE)] if match]
59
+ if any(code in {401, 403} for code in status_codes):
60
+ code = f"IMG_AUTH_HTTP_{next(code for code in status_codes if code in {401, 403})}"
61
+ cause = "a API key está ausente, inválida ou sem permissão para este provider"
62
+ action = "confirme a API key, o endpoint e as permissões em Configurações > Configuração API > API Keys"
63
+ elif any(code == 402 for code in status_codes):
64
+ code, cause, action = "IMG_QUOTA_HTTP_402", "o provider recusou a chamada por quota, créditos ou facturação", "confirme o saldo/plano do provider ou seleccione outro provider activo"
65
+ elif any(code == 404 for code in status_codes):
66
+ code, cause, action = "IMG_ENDPOINT_HTTP_404", "o endpoint ou modelo configurado não existe", "confirme a Base URL e o Modelo no cartão do provider"
67
+ elif any(code in {408, 429} for code in status_codes):
68
+ code = f"IMG_RATE_LIMIT_HTTP_{next(code for code in status_codes if code in {408, 429})}"
69
+ cause = "o provider está limitado ou não respondeu dentro do tempo permitido"
70
+ action = "aguarde alguns instantes, reduza a frequência ou use outro provider activo"
71
+ elif any(code >= 500 for code in status_codes):
72
+ code, cause, action = "IMG_PROVIDER_HTTP_5XX", "o serviço do provider devolveu um erro temporário", "tente novamente mais tarde ou use outro provider activo"
73
+ else:
74
+ code, cause, action = "IMG_PROVIDER_REQUEST_FAILED", "o provider rejeitou a chamada", "confirme o modelo, endpoint, formato da API key e os campos obrigatórios"
75
+ details = "; ".join(str(item)[:220] for item in errors)
76
+ return f"Não foi possível {operation}. Código: {code}. Causa provável: {cause}. Acção: {action}. Detalhes: {details}"
77
+ if "Não existem providers activos" in text:
78
+ return f"Não foi possível {operation}. Código: IMG_NO_ACTIVE_PROVIDER. Causa provável: não existe nenhum provider activo no pool de imagem. Acção: active um cartão em Configurações > Configuração API > LLM — providers e modelos / Imagem e Video IA."
79
+ return f"Não foi possível {operation}. Código: IMG_GENERATION_FAILED. Causa: {text or 'o provider não devolveu uma imagem utilizável'}. Acção: confirme a configuração do provider e tente novamente."
80
+
81
+
52
82
  AGNES_IMAGE_MODEL = "agnes-image-2.1-flash"
53
83
  AGNES_IMAGE_TIMEOUT_SECONDS = 120
54
84
 
@@ -330,7 +360,15 @@ def generate_image_for_card(
330
360
  try:
331
361
  routed = route_json_request(settings, pool=POOL_IMAGE, cards=[card], request=request)
332
362
  except ProviderRoutingError as exc:
333
- raise MediaGenerationError(str(exc)) from exc
363
+ attempts = list(getattr(exc, "attempts", []) or [])
364
+ provider_errors = []
365
+ for attempt in attempts:
366
+ provider = str(attempt.get("provider") or "provider")
367
+ status_code = attempt.get("status_code")
368
+ error = str(attempt.get("error") or "falha sem detalhe")
369
+ prefix = f"HTTP {int(status_code)}: " if status_code else ""
370
+ provider_errors.append(f"{provider}: {prefix}{error[:180]}")
371
+ raise MediaGenerationError(str(exc), provider_errors=provider_errors) from exc
334
372
  image_bytes, url = _image_value(routed.payload)
335
373
  if not image_bytes and not url:
336
374
  request_id = _image_request_id(routed.payload)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.4.60",
3
+ "version": "0.4.61",
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",