@danhachuel/thunderbolt 0.5.38 → 0.5.40

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.
@@ -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:
@@ -93,6 +94,29 @@ def _model(card: Mapping[str, Any]) -> str:
93
94
  return str(card.get("model") or "").strip()
94
95
 
95
96
 
97
+ def _hydrate_media_card(settings: Mapping[str, Any], card: Mapping[str, Any]) -> dict[str, Any]:
98
+ """Restore credentials when a persisted thumbnail task contains an old card snapshot."""
99
+ effective = dict(card)
100
+ if str(effective.get("api_key") or "").strip():
101
+ return effective
102
+ card_id = str(effective.get("id") or "").strip()
103
+ stored_cards = settings.get("media_provider_cards")
104
+ if isinstance(stored_cards, list):
105
+ for stored in stored_cards:
106
+ if isinstance(stored, Mapping) and card_id and str(stored.get("id") or "").strip() == card_id:
107
+ key = str(stored.get("api_key") or stored.get("key") or "").strip()
108
+ if key:
109
+ effective["api_key"] = key
110
+ break
111
+ if not str(effective.get("api_key") or "").strip() and str(effective.get("provider") or "").strip().lower() == "agnes":
112
+ for key_name in ("agnes_api_key", "agnes_ai_api_key", "agnes_token", "media_agnes_api_key"):
113
+ key = str(settings.get(key_name) or "").strip()
114
+ if key:
115
+ effective["api_key"] = key
116
+ break
117
+ return effective
118
+
119
+
96
120
  def _base_url(card: Mapping[str, Any]) -> str:
97
121
  return str(card.get("base_url") or "").strip().rstrip("/")
98
122
 
@@ -109,6 +133,17 @@ def _headers(card: Mapping[str, Any], *, fal: bool = False) -> dict[str, str]:
109
133
  return headers
110
134
 
111
135
 
136
+ def _fit_provider_prompt(prompt: str, provider: str) -> str:
137
+ """Keep provider-specific prompt limits from turning a thumbnail into a 400."""
138
+ text = str(prompt or "").strip()
139
+ if provider != "agnes" or len(text) <= AGNES_MAX_PROMPT_CHARS:
140
+ return text
141
+ # Keep both the visual brief and the mandatory lettering layer at the end.
142
+ tail_size = min(1800, AGNES_MAX_PROMPT_CHARS // 4)
143
+ head_size = AGNES_MAX_PROMPT_CHARS - tail_size - 80
144
+ return f"{text[:head_size].rstrip()}\n\n[Prompt longo resumido para o limite Agnes AI.]\n\n{text[-tail_size:].lstrip()}"
145
+
146
+
112
147
  def _append_generation_constraints(
113
148
  prompt: str,
114
149
  *,
@@ -298,6 +333,7 @@ def _image_request(card: dict[str, Any], prompt: str, *, topic: str = "", letter
298
333
  aspect_ratio="16:9",
299
334
  size=requested_size,
300
335
  )
336
+ constrained_prompt = _fit_provider_prompt(constrained_prompt, provider)
301
337
  if style == "cloudflare":
302
338
  return requests.post(endpoint, headers=_headers(card), json={"prompt": constrained_prompt}, timeout=180)
303
339
  if style == "fal_queue":
@@ -350,7 +386,7 @@ def generate_image_for_card(
350
386
  thumbnail_blueprint: Mapping[str, Any] | None = None,
351
387
  ) -> Path:
352
388
  """Generate one image with the selected media card."""
353
- card = dict(card)
389
+ card = _hydrate_media_card(settings, card)
354
390
  provider = str(card.get("provider") or "").strip().lower()
355
391
  if provider == "canva":
356
392
  ensure_storage()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.38",
3
+ "version": "0.5.40",
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",