🔧 fixes.md — Diário de Bordo de Produção
Para.AI — Assuntos Jurídicos API v1.0.0 Versão inicial para usuários testadores
📅 24/02/2026 — v1.0.0
✅ Código entregue
| Arquivo | Linhas | Status |
|---|---|---|
app/schemas.py |
~240 | ✅ OK |
app/builders.py |
219 | ✅ Corrigido (fix #1) |
app/es_client.py |
~490 | ✅ OK |
app/routes.py |
~350 | ✅ OK |
app/main.py |
~110 | ✅ OK |
app/config.py |
~30 | ✅ OK |
data/es_mapping.json |
~140 | ✅ OK |
Dockerfile |
~50 | ✅ OK |
docker-compose.yml |
~60 | ✅ OK |
entrypoint.sh |
~100 | ✅ OK |
scripts/test_api.py |
~210 | ✅ 13 testes |
🐛 Bug Corrigido
FIX #1 — app/builders.py — _src_to_ficha() — CRÍTICO
Arquivo: app/builders.py
Função: _src_to_ficha()
Linha original: ~141
Testes afetados: 2 (❌→✅)
Score antes: 77/79 | Score depois: 79/79
Descrição
Quando o chamador passa retornar=['texto'] explicitamente,
o Elasticsearch já retorna texto_completo no _source
(via _ficha_to_es_source() em es_client.py).
Porém o response builder descartava o valor por usar AND
em vez de avaliar a intenção explícita do caller:
# ❌ ANTES (bugado) — AND exige AMBAS as condições
texto = src.get("texto_completo") if (incluir_texto and _want("texto")) else None,
# Quando retornar=['texto'] e incluir_texto_completo=False (default):
# incluir_texto = False
# _want("texto") = True ← caller pediu explicitamente
# False AND True = False ← bug: descarta valor que o ES trouxe
# ✅ DEPOIS (corrigido) — trata intenção explícita separadamente
_explicitly_texto = want is not None and "texto" in want
texto = src.get("texto_completo") if (incluir_texto or _explicitly_texto) else None,
Tabela de comportamento após fix
retornar |
incluir_texto_completo |
Antes | Depois |
|---|---|---|---|
['texto'] |
False |
None ❌ |
valor ✅ |
['texto'] |
True |
valor ✅ | valor ✅ |
[] (vazio) |
False |
None ✅ |
None ✅ |
[] (vazio) |
True |
valor ✅ | valor ✅ |
['titulo'] |
False |
None ✅ |
None ✅ |
['titulo'] |
True |
None ✅ |
None ✅ |
Nota: quando
retornar=[]eincluir_texto=False, o ES já excluitexto_completodo_source(via{"excludes": ["texto_completo"]}), entãosrc.get("texto_completo")retornaNoneindependentemente.
🧪 Resultado dos Testes
❌ retornar=texto — texto_completo GET /busca-q → ✅ CORRIGIDO
❌ POST retornar=['texto'] — texto_completo POST /busca-q → ✅ CORRIGIDO
ANTES: 77/79 testes passaram (105945ms)
DEPOIS: 79/79 testes passaram ✅
🚧 Limitações Conhecidas (v1.0)
- Sem cache Redis (latência ~800ms p50 em
/busca) - Sem rate limiting (risco em produção pública)
- Sem autenticação/API keys
- Logs não estruturados (texto puro, não JSON)
- Sem métricas Prometheus/OTEL
- Cold start ~2s (ES + primeiro request)
🔮 v1.1 Planejado (Março 2026)
- Cache Redis (target p95 < 200ms)
- Rate limiting (1000 req/min)
- Logs JSON estruturados
- Health check por componente
- Testes de carga (Locust)
- CI/CD GitHub Actions
📊 Performance Baseline (v1.0 — Local)
| Endpoint | p50 | p95 | p99 |
|---|---|---|---|
GET /busca |
~800ms | ~1200ms | ~1850ms |
GET /busca-q |
~74ms | ~120ms | ~180ms |
GET /autocomplete |
~45ms | ~80ms | ~110ms |
GET /hierarquia |
~250ms | ~400ms | ~550ms |
GET /assuntos/{id} |
~20ms | ~35ms | ~50ms |
Ambiente: Docker local, ES single-node, Python 3.11
🐛 Como Reportar
Issues: https://github.com/para-ai/assuntos-juridicos/issues
Template obrigatório: descrição + endpoint + payload + resposta recebida + resposta esperada