@danhachuel/thunderbolt 0.7.63 → 0.7.65
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.
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import hashlib
|
|
10
10
|
import json
|
|
11
11
|
import os
|
|
12
|
+
import re
|
|
12
13
|
import time
|
|
13
14
|
from contextlib import contextmanager
|
|
14
15
|
from dataclasses import dataclass
|
|
@@ -274,6 +275,38 @@ def classify_response(response: Any) -> None:
|
|
|
274
275
|
)
|
|
275
276
|
|
|
276
277
|
|
|
278
|
+
def _validate_llm_json_response(response: Any) -> None:
|
|
279
|
+
"""Reject HTTP-200 LLM responses that cannot satisfy the JSON pool contract."""
|
|
280
|
+
try:
|
|
281
|
+
payload = response if isinstance(response, Mapping) else response.json()
|
|
282
|
+
except (ValueError, TypeError, AttributeError) as exc:
|
|
283
|
+
raise ProviderCallError(
|
|
284
|
+
"Provider LLM devolveu um corpo JSON de transporte inválido.",
|
|
285
|
+
category="payload",
|
|
286
|
+
retryable=False,
|
|
287
|
+
) from exc
|
|
288
|
+
if not isinstance(payload, Mapping):
|
|
289
|
+
raise ProviderCallError("Provider LLM devolveu um payload inválido.", category="payload", retryable=False)
|
|
290
|
+
choices = payload.get("choices") or []
|
|
291
|
+
message = (choices[0] or {}).get("message") if choices and isinstance(choices[0], Mapping) else None
|
|
292
|
+
content = message.get("content") if isinstance(message, Mapping) else None
|
|
293
|
+
if isinstance(content, list):
|
|
294
|
+
content = "".join(str(item.get("text", "")) for item in content if isinstance(item, Mapping))
|
|
295
|
+
text = str(content or "").strip()
|
|
296
|
+
text = re.sub(r"^```(?:json)?\s*", "", text, flags=re.IGNORECASE)
|
|
297
|
+
text = re.sub(r"\s*```$", "", text).strip()
|
|
298
|
+
try:
|
|
299
|
+
parsed = json.loads(text)
|
|
300
|
+
except json.JSONDecodeError as exc:
|
|
301
|
+
raise ProviderCallError(
|
|
302
|
+
"Provider LLM devolveu conteúdo que não é JSON válido.",
|
|
303
|
+
category="payload",
|
|
304
|
+
retryable=False,
|
|
305
|
+
) from exc
|
|
306
|
+
if not isinstance(parsed, dict):
|
|
307
|
+
raise ProviderCallError("Provider LLM devolveu JSON que não é um objecto.", category="payload", retryable=False)
|
|
308
|
+
|
|
309
|
+
|
|
277
310
|
def enabled_cards(settings: Mapping[str, Any], pool: str) -> list[dict[str, Any]]:
|
|
278
311
|
"""Return cards in deterministic priority order; media pools keep active-card preference."""
|
|
279
312
|
if pool == POOL_LLM:
|
|
@@ -426,11 +459,11 @@ def route_json_request(
|
|
|
426
459
|
record_provider_attempt(record)
|
|
427
460
|
if exc.retryable or exc.category in {"credential", "endpoint_or_model"}:
|
|
428
461
|
set_provider_cooldown(card, exc.retry_after or cooldown or DEFAULT_COOLDOWN_SECONDS)
|
|
429
|
-
if not exc.retryable:
|
|
462
|
+
if not exc.retryable and pool != POOL_LLM:
|
|
430
463
|
raise ProviderRoutingError(str(exc), attempts=attempts) from exc
|
|
431
464
|
if exc.retry_after:
|
|
432
465
|
time.sleep(min(3600.0, exc.retry_after))
|
|
433
|
-
elif cooldown > 0 and len(attempts) < maximum:
|
|
466
|
+
elif exc.retryable and cooldown > 0 and len(attempts) < maximum:
|
|
434
467
|
time.sleep(min(cooldown, 5.0))
|
|
435
468
|
except requests.RequestException as exc:
|
|
436
469
|
classified = classify_request_exception(exc)
|
|
@@ -473,6 +506,9 @@ def route_llm_json(settings: Mapping[str, Any], system_prompt: str, user_prompt:
|
|
|
473
506
|
}
|
|
474
507
|
if not body["model"]:
|
|
475
508
|
raise ProviderCallError("O cartão LLM não tem modelo configurado.", category="payload", retryable=False)
|
|
476
|
-
|
|
509
|
+
response = requests.post(endpoint, headers=headers, json=body, timeout=120)
|
|
510
|
+
if 200 <= int(getattr(response, "status_code", 0) or 0) < 300:
|
|
511
|
+
_validate_llm_json_response(response.json())
|
|
512
|
+
return response
|
|
477
513
|
|
|
478
514
|
return route_json_request(settings, pool=POOL_LLM, cards=None, request=request)
|
package/package.json
CHANGED