@danhachuel/pandanews 0.4.0 → 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,21 @@
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
+
11
+ ## 0.4.01 (2026-11-08)
12
+
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
14
+ - **Fim do processo órfão na porta 8000**: ao reabrir o app, o launcher detecta o backend vivo (`backend.pid` + probe de porta) e o **reaproveita**, em vez de subir uma segunda instância
15
+ - **Log sem terminal**: a inicialização do uvicorn e as requisições ficam registradas em `data/backend.log` mesmo com o terminal fechado (auditoria preservada)
16
+ - Encerramento do backend **sempre por código**: `taskkill /PID /F /T` no Windows, `kill` no grupo no Unix — sinais do terminal nunca chegam ao backend
17
+ - `unref()` no processo filho + watchdog de saúde (polling de porta a cada 10s; 3 falhas reerguem o backend silenciosamente)
18
+ - Teste de regressão `scripts/test-start-isolation.mjs` (`npm test`) roda no CI verificando as flags de isolamento — passo adicionado ao workflow de publish
19
+
5
20
  ## 0.4.00 (2026-10-30)
6
21
 
7
22
  - **Supabase opcional (Fase 6)**: detecção automática de credenciais (`SUPABASE_URL`/`SUPABASE_SERVICE_ROLE_KEY` no ambiente ou em Configurações); teste de conexão real (`POST /configuracoes/testar-supabase`); schema SQL pronto para o SQL Editor (`GET /configuracoes/schema-supabase`); migração dos dados locais SQLite → Supabase (`POST /configuracoes/migrar-supabase`) com relatório por tabela. O app continua 100% local e funcional sem Supabase
@@ -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.0", 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