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