File size: 3,957 Bytes
aec8693 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # 🔧 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:
```python
# ❌ 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
```
```python
# ✅ 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=[]` e `incluir_texto=False`, o ES já exclui
> `texto_completo` do `_source` (via `{"excludes": ["texto_completo"]}`),
> então `src.get("texto_completo")` retorna `None` independentemente.
---
## 🧪 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
|