@danhachuel/thunderbolt 0.5.37 → 0.5.39

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
@@ -7344,22 +7344,6 @@ def render_media_provider_cards(settings: dict[str, Any], *, embedded: bool = Fa
7344
7344
  with st.expander("Imagem e Video IA", expanded=False):
7345
7345
  full_ia_labels = ", ".join(media_provider_definition(code).label for code in FULL_IA_VIDEO_PROVIDER_CODES)
7346
7346
  st.caption(f"Configure providers de imagem e vídeo em cartões independentes. O router usa apenas o pool correspondente e faz failover entre providers activos. Pool Full IA: {full_ia_labels}.")
7347
- image_cards = [card for card in cards if card.get("supports_image")]
7348
- video_cards = [card for card in cards if card.get("supports_video")]
7349
- selector_cols = st.columns(3)
7350
- image_options = [""] + [str(card.get("id")) for card in image_cards]
7351
- video_options = [""] + [str(card.get("id")) for card in video_cards]
7352
- with selector_cols[0]:
7353
- image_active_id = st.selectbox("Provider principal de imagem", image_options, index=image_options.index(str(settings.get(MEDIA_IMAGE_ACTIVE_CARD_KEY) or "")) if str(settings.get(MEDIA_IMAGE_ACTIVE_CARD_KEY) or "") in image_options else 0, format_func=lambda value: "Automático / primeiro activo" if not value else next((media_provider_definition(card.get("provider")).label for card in image_cards if str(card.get("id")) == value), value), key="media_image_active_selector")
7354
- with selector_cols[1]:
7355
- video_active_id = st.selectbox("Provider principal de vídeo", video_options, index=video_options.index(str(settings.get(MEDIA_VIDEO_ACTIVE_CARD_KEY) or "")) if str(settings.get(MEDIA_VIDEO_ACTIVE_CARD_KEY) or "") in video_options else 0, format_func=lambda value: "Não usar pool externo" if not value else next((media_provider_definition(card.get("provider")).label for card in video_cards if str(card.get("id")) == value), value), key="media_video_active_selector")
7356
- with selector_cols[2]:
7357
- video_pool_enabled = st.checkbox("Usar pool de vídeo externo", value=bool(settings.get("media_video_pool_enabled", False)), key="media_video_pool_enabled_ui")
7358
- if st.button("Salvar selecção dos pools", use_container_width=True, key="save_media_pool_selection") if not embedded else st.form_submit_button("Salvar selecção dos pools", use_container_width=True, key="save_media_pool_selection"):
7359
- settings["media_video_pool_enabled"] = bool(video_pool_enabled)
7360
- _persist_media_cards(settings, cards, image_active_id, video_active_id)
7361
- st.success("Selecção dos pools guardada.")
7362
- st.rerun()
7363
7347
  for index in range(len(cards)):
7364
7348
  _render_media_provider_card(settings, cards, index, embedded=embedded)
7365
7349
  st.divider()
@@ -7369,7 +7353,7 @@ def render_media_provider_cards(settings: dict[str, Any], *, embedded: bool = Fa
7369
7353
  add_clicked = st.form_submit_button("Adicionar provider de imagem/vídeo", use_container_width=True, key="add_media_provider_card") if embedded else st.button("Adicionar provider de imagem/vídeo", use_container_width=True, key="add_media_provider_card")
7370
7354
  if add_clicked:
7371
7355
  cards.append(new_media_card(provider_to_add, card_id=f"media-{provider_to_add}-{uuid.uuid4().hex[:8]}"))
7372
- _persist_media_cards(settings, cards, image_active_id, video_active_id)
7356
+ _persist_media_cards(settings, cards)
7373
7357
  st.success("Novo provider de imagem/vídeo adicionado.")
7374
7358
  st.rerun()
7375
7359
 
@@ -83,6 +83,7 @@ def format_media_generation_error(exc: MediaGenerationError, *, operation: str =
83
83
 
84
84
  AGNES_IMAGE_MODEL = "agnes-image-2.1-flash"
85
85
  AGNES_IMAGE_TIMEOUT_SECONDS = 120
86
+ AGNES_MAX_PROMPT_CHARS = 9500
86
87
 
87
88
 
88
89
  def _api_key(card: Mapping[str, Any]) -> str:
@@ -109,6 +110,17 @@ def _headers(card: Mapping[str, Any], *, fal: bool = False) -> dict[str, str]:
109
110
  return headers
110
111
 
111
112
 
113
+ def _fit_provider_prompt(prompt: str, provider: str) -> str:
114
+ """Keep provider-specific prompt limits from turning a thumbnail into a 400."""
115
+ text = str(prompt or "").strip()
116
+ if provider != "agnes" or len(text) <= AGNES_MAX_PROMPT_CHARS:
117
+ return text
118
+ # Keep both the visual brief and the mandatory lettering layer at the end.
119
+ tail_size = min(1800, AGNES_MAX_PROMPT_CHARS // 4)
120
+ head_size = AGNES_MAX_PROMPT_CHARS - tail_size - 80
121
+ return f"{text[:head_size].rstrip()}\n\n[Prompt longo resumido para o limite Agnes AI.]\n\n{text[-tail_size:].lstrip()}"
122
+
123
+
112
124
  def _append_generation_constraints(
113
125
  prompt: str,
114
126
  *,
@@ -298,6 +310,7 @@ def _image_request(card: dict[str, Any], prompt: str, *, topic: str = "", letter
298
310
  aspect_ratio="16:9",
299
311
  size=requested_size,
300
312
  )
313
+ constrained_prompt = _fit_provider_prompt(constrained_prompt, provider)
301
314
  if style == "cloudflare":
302
315
  return requests.post(endpoint, headers=_headers(card), json={"prompt": constrained_prompt}, timeout=180)
303
316
  if style == "fal_queue":
@@ -382,13 +382,10 @@ def apply_media_provider_cards_to_settings(settings: Mapping[str, Any], cards: l
382
382
  def media_cards_for_pool(settings: Mapping[str, Any], pool: str, *, thumbnail_only: bool = False) -> list[dict[str, Any]]:
383
383
  migrated, _ = ensure_media_provider_cards(settings)
384
384
  capability = "supports_image" if pool == "image" else "supports_video" if pool == "video" else "supports_text"
385
- active_key = MEDIA_IMAGE_ACTIVE_CARD_KEY if pool == "image" else MEDIA_VIDEO_ACTIVE_CARD_KEY if pool == "video" else ""
386
- active_id = str(migrated.get(active_key) or "")
387
385
  cards = [dict(item) for item in migrated.get(MEDIA_CARDS_KEY, []) if item.get("enabled", True) and item.get(capability)]
388
386
  if pool == "image" and not thumbnail_only:
389
387
  cards = [card for card in cards if not card.get("thumbnail_only")]
390
- # A prioridade é o contrato principal do failover. O cartão activo legado
391
- # só serve de desempate entre cartões com a mesma prioridade, para não
392
- # ignorar uma escolha explícita do utilizador por causa de estado antigo.
393
- cards.sort(key=lambda card: (int(card.get("priority", 0)), 0 if str(card.get("id")) == active_id else 1))
388
+ # A prioridade definida no próprio cartão é a única regra do failover.
389
+ # O estado activo legado não pode alterar a ordem escolhida pelo utilizador.
390
+ cards.sort(key=lambda card: int(card.get("priority", 0)))
394
391
  return cards
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.37",
3
+ "version": "0.5.39",
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",