@danhachuel/pandanews 0.4.9 → 0.4.10
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/CHANGELOG.md +5 -0
- package/backend/__pycache__/__init__.cpython-314.pyc +0 -0
- package/backend/app/main.py +1 -1
- package/backend/core/__pycache__/__init__.cpython-314.pyc +0 -0
- package/backend/core/__pycache__/config.cpython-314.pyc +0 -0
- package/backend/core/__pycache__/llm_router.cpython-314.pyc +0 -0
- package/backend/tests/test_llm_fallback.py +64 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Todas as versões publicadas são registradas aqui antes do publish (regra em AGENTS.md). A etiqueta visual usa PATCH de dois dígitos (ex.: 0.1.00); o registry npm normaliza para a versão canônica (ex.: 0.1.0).
|
|
4
4
|
|
|
5
|
+
## 0.4.10 (2026-11-08)
|
|
6
|
+
|
|
7
|
+
- **Cobertura E2E pelo caminho HTTP real**: o teste de fallback ganhou um cenário que substitui apenas o `httpx.Client` — `chamar_provedor`, a construção da URL, os headers e o `raise_for_status` rodam de verdade. A NVIDIA devolve 410, o Gemini devolve 200 e a notícia nasce do Gemini com o log `Erro na NVIDIA (...). Acionando Google Gemini...` emitido. Fecha a lacuna de testar só a função mockada
|
|
8
|
+
- Teste sobe para 16 verificações, todas verdes local e no CI
|
|
9
|
+
|
|
5
10
|
## 0.4.09 (2026-11-08)
|
|
6
11
|
|
|
7
12
|
- **Frontend — Gemini tratado como a NVIDIA no teste de conexão**: o badge agora distingue `degraded` (âmbar, modelo em fim de vida 404/410) de `falhou` (vermelho) e a mensagem de detalhe usa a cor correspondente. Antes um 404/410 do Gemini aparecia como "falhou" vermelho — agora o usuário entende que é o modelo, não a chave, e que a geração segue pela NVIDIA
|
|
Binary file
|
package/backend/app/main.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -13,8 +13,10 @@ Cobre exatamente o que a correção promete:
|
|
|
13
13
|
"""
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import json
|
|
16
17
|
import logging
|
|
17
18
|
import sys
|
|
19
|
+
import types
|
|
18
20
|
from datetime import timedelta as _timedelta
|
|
19
21
|
from pathlib import Path
|
|
20
22
|
|
|
@@ -165,7 +167,7 @@ resultado_410 = _testar_com_resposta(
|
|
|
165
167
|
check(resultado_410["ok"] is False, "test_provider: HTTP 410 -> ok=False")
|
|
166
168
|
check(resultado_410.get("degraded") is True, "test_provider: HTTP 410 -> degraded=True (fallback avisa)")
|
|
167
169
|
check(
|
|
168
|
-
"fim
|
|
170
|
+
"fim de vida" in resultado_410["detail"] or "410" in resultado_410["detail"],
|
|
169
171
|
"test_provider: a string de erro do provedor volta no JSON para a tela",
|
|
170
172
|
)
|
|
171
173
|
|
|
@@ -181,6 +183,67 @@ check(
|
|
|
181
183
|
"test_provider: HTTP 500 -> código e corpo devolvidos ao frontend",
|
|
182
184
|
)
|
|
183
185
|
|
|
186
|
+
# ------------------------------------ 3b) E2E pelo caminho HTTP real (Chamar)
|
|
187
|
+
|
|
188
|
+
# Diferente dos cenários acima (que trocam `chamar_provedor` por um dublê),
|
|
189
|
+
# aqui só o `httpx.Client` é substituído: `chamar_provedor`, a construção da
|
|
190
|
+
# URL, os headers e o `raise_for_status` rodam de verdade. A NVIDIA devolve
|
|
191
|
+
# 410 e o Gemini devolve 200 — a notícia tem de nascer do Gemini.
|
|
192
|
+
llm_router.chamar_provedor = original_chamar
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class _RespostaHttp:
|
|
196
|
+
def __init__(self, status_code: int, corpo: dict) -> None:
|
|
197
|
+
self.status_code = status_code
|
|
198
|
+
self._corpo = corpo
|
|
199
|
+
self.text = json.dumps(corpo)
|
|
200
|
+
|
|
201
|
+
def raise_for_status(self) -> None:
|
|
202
|
+
if self.status_code >= 400:
|
|
203
|
+
raise RuntimeError(f"HTTP {self.status_code}: {self.text}")
|
|
204
|
+
|
|
205
|
+
def json(self) -> dict:
|
|
206
|
+
return self._corpo
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _post_e2e(self, endpoint, headers=None, json=None):
|
|
210
|
+
# distingue pelo modelo pedido: nvidia (410) vs google/gemini (200)
|
|
211
|
+
modelo = (json or {}).get("model", "")
|
|
212
|
+
if "gemini" in modelo:
|
|
213
|
+
return _RespostaHttp(
|
|
214
|
+
200,
|
|
215
|
+
{"choices": [{"message": {"content": '{"titulo":"T","resumo":"R","corpo":"C"}'}}], "model": modelo},
|
|
216
|
+
)
|
|
217
|
+
return _RespostaHttp(410, {"detail": "The model has reached its end of life"})
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class _ClientE2E:
|
|
221
|
+
def __init__(self, *a, **k) -> None:
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
def __enter__(self):
|
|
225
|
+
return self
|
|
226
|
+
|
|
227
|
+
def __exit__(self, *a):
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
def post(self, endpoint, headers=None, json=None):
|
|
231
|
+
return _post_e2e(self, endpoint, headers, json)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
llm_router.httpx.Client = _ClientE2E
|
|
235
|
+
|
|
236
|
+
registros.clear()
|
|
237
|
+
|
|
238
|
+
texto_e2e, provedor_e2e = llm_router.gerar_texto(_db_com_chaves(), "escreva uma noticia")
|
|
239
|
+
|
|
240
|
+
check(provedor_e2e == "gemini", "E2E HTTP: NVIDIA 410 -> Gemini assume (provedor: gemini)")
|
|
241
|
+
check("titulo" in texto_e2e, "E2E HTTP: o conteúdo devolvido é o do Gemini (JSON utilizável)")
|
|
242
|
+
check(
|
|
243
|
+
any("Erro na NVIDIA (" in m and "Acionando Google Gemini" in m for m in registros),
|
|
244
|
+
"E2E HTTP: log 'Erro na NVIDIA (...). Acionando Google Gemini...' emitido",
|
|
245
|
+
)
|
|
246
|
+
|
|
184
247
|
# ------------------------------------------- 4) endpoint replicado do Thunderbolt
|
|
185
248
|
|
|
186
249
|
check(
|
package/package.json
CHANGED