@danhachuel/pandanews 0.4.1 → 0.4.2

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 CHANGED
@@ -2,6 +2,12 @@
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.02 (2026-11-08)
6
+
7
+ - **Seletor dinâmico de modelos NVIDIA NIM**: novo endpoint proxy `GET /api/v1/configuracoes/llm/nvidia/models` que consulta a NVIDIA em tempo real com a chave do usuário — a API key nunca é exposta ao frontend/Network tab
8
+ - Campo "Modelo" da NVIDIA em Configurações agora é um `<select>` populado automaticamente (5 min de cache, botão ↻ para recarregar) — o usuário não digita mais IDs
9
+ - Resiliência: sem chave, offline ou lista indisponível, o select mantém o modelo atual como opção (fallback) com aviso âmbar — nada de travar a tela
10
+
5
11
  ## 0.4.01 (2026-11-08)
6
12
 
7
13
  - **Isolamento de processos (Windows/MobaXterm)**: o backend Python roda em grupo de processos próprio (`detached: true` → `CREATE_NEW_PROCESS_GROUP`) com `windowsHide: true` (sem janela fantasma) e `stdio` em fd do arquivo — um Ctrl+C no terminal agora derruba **apenas o launcher Node**; o backend e as gerações em andamento sobrevivem
@@ -31,7 +31,7 @@ async def lifespan(_: FastAPI):
31
31
  scheduler.parar()
32
32
 
33
33
 
34
- app = FastAPI(title="PandaNews API", version="0.4.1", lifespan=lifespan)
34
+ app = FastAPI(title="PandaNews API", version="0.4.2", lifespan=lifespan)
35
35
 
36
36
  app.add_middleware(
37
37
  CORSMiddleware,
@@ -1,7 +1,7 @@
1
1
  """Configurações (/api/v1/configuracoes) — API keys mascaradas, sem travas."""
2
2
  from __future__ import annotations
3
3
 
4
- from fastapi import APIRouter, Depends
4
+ from fastapi import APIRouter, Depends, HTTPException
5
5
  from sqlalchemy.orm import Session
6
6
 
7
7
  from backend.app.schemas import ConfiguracoesPatch
@@ -43,6 +43,34 @@ def obter_configuracoes(db: Session = Depends(get_db)) -> dict:
43
43
  }
44
44
 
45
45
 
46
+ @router.get("/llm/nvidia/models")
47
+ def listar_modelos_nvidia(db: Session = Depends(get_db)) -> dict:
48
+ """Lista os modelos NVIDIA NIM disponíveis para a chave configurada.
49
+
50
+ Proxy: a API key fica no banco local e nunca é exposta ao frontend.
51
+ Retorna 400 sem chave; 502 em falha de consulta.
52
+ """
53
+ import httpx
54
+
55
+ from backend.core.llm_router import provider_config
56
+
57
+ cfg = provider_config(db, "nvidia")
58
+ if not cfg["api_key"]:
59
+ raise HTTPException(status_code=400, detail="API Key da NVIDIA não configurada — adicione-a antes de listar os modelos.")
60
+ url = cfg["base_url"].rstrip("/") + "/models"
61
+ try:
62
+ resposta = httpx.get(url, headers={"Authorization": f"Bearer {cfg['api_key']}"}, timeout=20.0)
63
+ except httpx.HTTPError as exc:
64
+ raise HTTPException(status_code=502, detail=f"Erro ao consultar a NVIDIA: {exc}") from exc
65
+ if resposta.status_code != 200:
66
+ raise HTTPException(
67
+ status_code=502,
68
+ detail=f"NVIDIA respondeu {resposta.status_code}: {resposta.text[:200]}",
69
+ )
70
+ ids = sorted(str(m.get("id")) for m in resposta.json().get("data", []) if m.get("id"))
71
+ return {"models": ids, "modelo_atual": cfg["model"], "base_url": cfg["base_url"]}
72
+
73
+
46
74
  @router.post("/testar-supabase")
47
75
  def testar_supabase() -> dict:
48
76
  from backend.core import supabase_db