Guia de Debug - unified_bot.py¶
🐛 Métodos de Debug para SSE Endpoints¶
1. Debug via Logging (Mais Simples)¶
Configurar Nível de Log¶
# .env
LOG_LEVEL=DEBUG # Altere de INFO para DEBUG
Adicionar Logs Estratégicos nos Endpoints SSE¶
# unified_bot.py - No endpoint /sse/chat
logger.debug(f"📋 Request headers: {dict(request.headers)}")
logger.debug(f"📋 Request body: {await request.json()}")
logger.debug(f"🔐 JWT token extraído: {jwt_token[:20]}..." if jwt_token else "Sem JWT")
# unified_bot.py - No endpoint /sse/stream/{stream_id}
logger.debug(f"🍪 Cookies recebidos: {request.cookies}")
logger.debug(f"🔍 Query params: {dict(request.query_params)}")
logger.debug(f"📡 Stream {stream_id} - eventos: {len(list(stream_manager.get_stream(stream_id).events))}")
Rodar com Debug Ativado¶
# Local
LOG_LEVEL=DEBUG python unified_bot.py
# Com uvicorn
LOG_LEVEL=DEBUG uvicorn unified_bot:app --reload --log-level debug
2. Debug com VS Code (Recomendado)¶
Criar .vscode/launch.json¶
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Debug unified_bot.py",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"unified_bot:app",
"--reload",
"--host", "0.0.0.0",
"--port", "8080"
],
"jinja": true,
"justMyCode": false,
"env": {
"LOG_LEVEL": "DEBUG"
},
"console": "integratedTerminal"
},
{
"name": "Python: Debug SSE Only",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"unified_bot:app",
"--reload",
"--host", "0.0.0.0",
"--port", "8080",
"--log-level", "debug"
],
"jinja": true,
"justMyCode": false,
"env": {
"LOG_LEVEL": "DEBUG",
"SSE_ENABLED": "true"
},
"console": "integratedTerminal",
"preLaunchTask": "Activate venv"
}
]
}
Como Usar¶
- Abra
unified_bot.pyno VS Code - Coloque breakpoints (clique na margem esquerda) nos endpoints SSE:
- Linha do
@app.post("/sse/chat") - Linha do
@app.get("/sse/stream/{stream_id}") - Dentro do
event_generator() - Aperte F5 ou vá em Run > Start Debugging
- Faça requisição para
http://localhost:8080/sse/chat - Debug pausará nos breakpoints
Breakpoints Estratégicos¶
# unified_bot.py
@app.post("/sse/chat")
async def sse_chat_start(request: Request, response: Response):
# 🔴 BREAKPOINT 1: Entrada da requisição
adapter = adapter_factory.get_adapter("sse")
# 🔴 BREAKPOINT 2: Parse da mensagem
incoming_message = await adapter.parse_webhook_request(request)
# 🔴 BREAKPOINT 3: JWT do metadata
jwt_token = incoming_message.metadata.get("jwt_token")
# 🔴 BREAKPOINT 4: Criação do stream
stream_id = adapter.create_stream(incoming_message)
# 🔴 BREAKPOINT 5: Cookie configurado
if jwt_token:
response.set_cookie(...)
@app.get("/sse/stream/{stream_id}")
async def sse_stream(...):
# 🔴 BREAKPOINT 6: Extração de JWT
jwt_token = request.cookies.get("jwt_token")
# 🔴 BREAKPOINT 7: Validação JWT
if jwt_token:
user_context = await validate_jwt(jwt_token)
# 🔴 BREAKPOINT 8: Stream encontrado
stream = stream_manager.get_stream(stream_id)
# 🔴 BREAKPOINT 9: Generator de eventos
async def event_generator():
async for event in stream_manager.subscribe_to_stream(...):
# 🔴 BREAKPOINT 10: Cada evento SSE
yield sse_message
3. Debug Remoto com debugpy (Docker/Cloud Run)¶
Adicionar debugpy ao código¶
# unified_bot.py - No topo do arquivo
import os
# Debug remoto
if os.getenv("ENABLE_DEBUGPY", "false").lower() == "true":
import debugpy
debugpy.listen(("0.0.0.0", 5678))
print("🐛 Debugpy rodando na porta 5678. Aguardando conexão...")
# debugpy.wait_for_client() # Descomente para bloquear até conectar
Configurar .env¶
ENABLE_DEBUGPY=true
VS Code launch.json para Remote¶
{
"name": "Python: Remote Attach",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
]
}
Docker Compose com Debug¶
# docker-compose.yml
services:
unified-bot:
ports:
- "8080:8080"
- "5678:5678" # Porta de debug
environment:
- ENABLE_DEBUGPY=true
4. Debug de SSE com cURL e HTTPie¶
Testar POST /sse/chat¶
# Com cURL
curl -X POST http://localhost:8080/sse/chat \
-H "Content-Type: application/json" \
-d '{
"user_id": "test_user",
"message": "Olá!",
"jwt_token": "eyJ..."
}' \
-v # Verbose para ver headers
# Com HTTPie (mais legível)
http POST localhost:8080/sse/chat \
user_id=test_user \
message="Olá!" \
jwt_token="eyJ..." \
--verbose
Testar GET /sse/stream com curl¶
# Sem auth
curl -N http://localhost:8080/sse/stream/sse_abc123
# Com JWT via query
curl -N "http://localhost:8080/sse/stream/sse_abc123?token=eyJ..."
# Com cookie
curl -N http://localhost:8080/sse/stream/sse_abc123 \
-H "Cookie: jwt_token=eyJ..."
Com EventSource via Browser Console¶
// Abra DevTools > Console
const es = new EventSource('http://localhost:8080/sse/stream/sse_abc123');
es.addEventListener('stream_start', (e) => {
console.log('🚀 Stream Start:', JSON.parse(e.data));
});
es.addEventListener('text_chunk', (e) => {
console.log('📝 Text:', JSON.parse(e.data));
});
es.addEventListener('stream_complete', (e) => {
console.log('✅ Complete:', JSON.parse(e.data));
es.close();
});
es.onerror = (e) => {
console.error('❌ Error:', e);
};
5. Debug de Streaming com Python¶
# test_sse_debug.py
import asyncio
import httpx
import json
async def test_sse_stream():
async with httpx.AsyncClient() as client:
# 1. POST para criar stream
print("📤 Criando stream...")
response = await client.post(
"http://localhost:8080/sse/chat",
json={
"user_id": "debug_user",
"message": "Teste de debug",
"jwt_token": "eyJ..."
}
)
data = response.json()
stream_id = data["stream_id"]
print(f"✅ Stream criado: {stream_id}")
# Pegar cookie se presente
cookies = response.cookies
print(f"🍪 Cookies: {cookies}")
# 2. GET para conectar ao stream
print(f"\n📡 Conectando ao stream {stream_id}...")
async with client.stream(
"GET",
f"http://localhost:8080/sse/stream/{stream_id}",
cookies=cookies,
timeout=60.0
) as stream_response:
print(f"Status: {stream_response.status_code}")
print(f"Headers: {stream_response.headers}")
# 3. Ler eventos SSE
async for line in stream_response.aiter_lines():
if line.startswith("event:"):
event_type = line.split(":", 1)[1].strip()
print(f"\n🎯 Event: {event_type}")
elif line.startswith("data:"):
data = line.split(":", 1)[1].strip()
event_data = json.loads(data)
print(f" Data: {event_data}")
elif line.startswith("id:"):
event_id = line.split(":", 1)[1].strip()
print(f" ID: {event_id}")
if __name__ == "__main__":
asyncio.run(test_sse_stream())
Executar:
python test_sse_debug.py
6. Monitoramento em Produção (Cloud Run)¶
Ver Logs em Tempo Real¶
# Cloud Run
gcloud run services logs tail ifriend-agent-unified \
--region=us-central1 \
--project=ifriend-platform
# Filtrar apenas SSE
gcloud run services logs tail ifriend-agent-unified \
--region=us-central1 \
--project=ifriend-platform \
| grep -i "sse\|stream"
Structured Logging para Debug¶
# unified_bot.py
import json
from datetime import datetime
def log_sse_debug(event_type: str, data: dict):
"""Log estruturado para Cloud Logging"""
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"severity": "DEBUG",
"component": "sse",
"event_type": event_type,
**data
}
print(json.dumps(log_entry))
# Uso nos endpoints
log_sse_debug("stream_created", {
"stream_id": stream_id,
"user_id": incoming_message.user_id,
"has_jwt": bool(jwt_token)
})
7. Ferramentas de Debug Úteis¶
Install¶
pip install debugpy httpx ipdb
IPython Debugger (ipdb)¶
# unified_bot.py - Qualquer lugar
import ipdb; ipdb.set_trace() # Breakpoint interativo
Rich para Logs Bonitos¶
# unified_bot.py
from rich import print as rprint
from rich.console import Console
console = Console()
# Logs formatados
console.log(f"[green]✅ Stream criado:[/green] {stream_id}")
console.log("[red]❌ Erro:[/red]", exc_info=True)
# Print de objetos
rprint(incoming_message)
🎯 Checklist de Debug SSE¶
Antes de Debugar¶
- [ ]
LOG_LEVEL=DEBUGno.env - [ ] Servidor rodando com
--reload - [ ] DevTools aberto (Network + Console)
- [ ] Breakpoints colocados
Durante Debug¶
- [ ] Verificar headers da requisição
- [ ] Verificar cookies enviados/recebidos
- [ ] Verificar JWT extraído e validado
- [ ] Verificar stream_id retornado
- [ ] Verificar eventos SSE sendo gerados
Problemas Comuns¶
| Sintoma | Causa | Debug |
|---|---|---|
| Stream não encontrado | stream_id incorreto | Verificar response do POST |
| Cookie não enviado | SameSite/Secure | DevTools > Application > Cookies |
| JWT inválido | Token expirado | Breakpoint na validação |
| Eventos não chegam | Generator travado | Breakpoint no event_generator() |
| CORS erro | Origem não permitida | Ver logs "CORS origens" |
🛡️ Loop Guard — Diagnosticando Bloqueios Anti-Loop¶
O LoopGuard (messaging/loop_guard.py) protege contra loops bot-to-bot com 4 camadas. Logs aparecem com prefixo [LoopGuard].
Identificando bloqueios nos logs¶
[LoopGuard] DUPLICATE platform=whatsapp user=5511... message_id=xyz
[LoopGuard] CIRCUIT_OPEN platform=whatsapp user=5511... reason=rate_limit burst=6/5
[LoopGuard] CIRCUIT_OPEN platform=whatsapp user=5511... reason=repetition count=3
[LoopGuard] TURN_LIMIT platform=whatsapp user=5511... session=... turns=201
Ajustando thresholds via env vars¶
# Aumentar tolerância (ex: humano digitando rápido)
LOOP_GUARD_MAX_MESSAGES=10 # default: 5
LOOP_GUARD_WINDOW_SECONDS=15 # default: 10
# Reduzir cooldown para testes
LOOP_GUARD_COOLDOWN_SECONDS=5 # default: 30
# Aumentar limite de turnos
LOOP_GUARD_MAX_TURNS_PER_SESSION=500 # default: 200
# Tolerância a repetições
LOOP_GUARD_REPEAT_THRESHOLD=5 # default: 3
LOOP_GUARD_REPEAT_WINDOW_SECONDS=120 # default: 60
Rodando testes¶
python -m pytest tests/test_loop_guard.py -v
Última atualização: 15/02/2026