anderson-ufrj
commited on
Commit
·
0c5f570
1
Parent(s):
1edd4d6
refactor: organize project structure and remove sensitive files
Browse files- Remove all sensitive documentation from repository
- Move internal docs to protected directories
- Update .gitignore to exclude docs/internal/
- Organize test files into proper directories
- Clean up debug scripts and backup files
- Fix DataSourceType.BIDDINGS enum in chat.py
- Maintain clean repository structure
- .gitignore +1 -0
- CODEBASE_ANALYSIS_REPORT.md +0 -330
- EMERGENCY_SOLUTION.md +0 -84
- FIX_HUGGINGFACE_DEPLOYMENT.md +0 -117
- FRONTEND_CHAT_INTEGRATION.md +0 -363
- FRONTEND_INTEGRATION.md +0 -254
- FRONTEND_STABLE_INTEGRATION.md +0 -235
- MARITACA_OPTIMIZATION_GUIDE.md +0 -372
- app_simple.py +0 -706
- debug_drummond_import.py +0 -97
- debug_hf_error.py +0 -34
- src/api/routes/chat.py +1 -0
- test_chat_detailed.py +0 -103
- test_chat_simple.py +0 -99
- test_drummond_import.py +0 -42
- test_drummond_init.py +0 -30
- test_drummond_live.py +0 -46
- test_drummond_minimal.py +0 -60
- test_hf_chat.py +0 -152
- test_hf_spaces.py +0 -74
- test_maritaca_integration.py +0 -118
- test_models_communication.py +0 -235
- test_models_endpoints.py +0 -142
- test_models_only.py +0 -125
- test_quick_connectivity.py +0 -62
- test_stable_endpoint.py +0 -99
.gitignore
CHANGED
|
@@ -235,6 +235,7 @@ prometheus_data/
|
|
| 235 |
|
| 236 |
# Internal documentation - IGNORE FROM REPOSITORY
|
| 237 |
docs-internal/
|
|
|
|
| 238 |
|
| 239 |
# Claude Code artifacts
|
| 240 |
.claude/
|
|
|
|
| 235 |
|
| 236 |
# Internal documentation - IGNORE FROM REPOSITORY
|
| 237 |
docs-internal/
|
| 238 |
+
docs/internal/
|
| 239 |
|
| 240 |
# Claude Code artifacts
|
| 241 |
.claude/
|
CODEBASE_ANALYSIS_REPORT.md
DELETED
|
@@ -1,330 +0,0 @@
|
|
| 1 |
-
# Relatório de Análise Completa - Cidadão.AI Backend
|
| 2 |
-
|
| 3 |
-
**Autor**: Anderson Henrique da Silva
|
| 4 |
-
**Data de Criação**: 2025-09-20 08:45:00 -03 (São Paulo, Brasil)
|
| 5 |
-
**Versão do Sistema**: 2.2.0
|
| 6 |
-
|
| 7 |
-
## Sumário Executivo
|
| 8 |
-
|
| 9 |
-
O Cidadão.AI Backend é uma plataforma de IA multi-agente de nível empresarial para análise de transparência governamental brasileira. O sistema demonstra arquitetura sofisticada com 17 agentes especializados (8 operacionais), integração com Portal da Transparência, detecção avançada de anomalias usando ML/análise espectral, e infraestrutura enterprise-grade com observabilidade completa.
|
| 10 |
-
|
| 11 |
-
### Principais Destaques
|
| 12 |
-
|
| 13 |
-
- **Arquitetura Multi-Agente**: 17 agentes com identidades culturais brasileiras
|
| 14 |
-
- **Performance**: Latência P95 <180ms, throughput 12k req/s, cache hit rate 92%
|
| 15 |
-
- **Segurança**: JWT auth, rate limiting, circuit breakers, audit logging
|
| 16 |
-
- **Observabilidade**: Prometheus + Grafana, métricas customizadas, alertas SLO/SLA
|
| 17 |
-
- **Otimizações**: orjson (3x mais rápido), Brotli (70-90% compressão), cache multi-nível
|
| 18 |
-
|
| 19 |
-
## 1. Estrutura do Projeto
|
| 20 |
-
|
| 21 |
-
### 1.1 Organização de Diretórios
|
| 22 |
-
|
| 23 |
-
```
|
| 24 |
-
cidadao.ai-backend/
|
| 25 |
-
├── app.py # Entry point HuggingFace (porta 7860)
|
| 26 |
-
├── src/ # Código fonte principal
|
| 27 |
-
│ ├── agents/ # 17 agentes IA especializados
|
| 28 |
-
│ ├── api/ # Endpoints REST/WebSocket/GraphQL
|
| 29 |
-
│ ├── core/ # Utilitários centrais
|
| 30 |
-
│ ├── infrastructure/ # Recursos enterprise
|
| 31 |
-
│ ├── ml/ # Pipeline ML/IA
|
| 32 |
-
│ ├── services/ # Lógica de negócio
|
| 33 |
-
│ └── tools/ # Integrações externas
|
| 34 |
-
├── tests/ # Suite de testes (45% cobertura)
|
| 35 |
-
├── docs/ # Documentação completa
|
| 36 |
-
├── monitoring/ # Stack Prometheus + Grafana
|
| 37 |
-
├── scripts/ # Automação e deployment
|
| 38 |
-
└── requirements/ # Gestão de dependências
|
| 39 |
-
```
|
| 40 |
-
|
| 41 |
-
### 1.2 Arquivos de Configuração Principais
|
| 42 |
-
|
| 43 |
-
- **pyproject.toml**: Configuração moderna Python com seções organizadas
|
| 44 |
-
- **Makefile**: 30+ comandos para workflow de desenvolvimento
|
| 45 |
-
- **pytest.ini**: Configuração de testes com markers e coverage
|
| 46 |
-
- **docker-compose.monitoring.yml**: Stack completa de observabilidade
|
| 47 |
-
|
| 48 |
-
## 2. Sistema Multi-Agente
|
| 49 |
-
|
| 50 |
-
### 2.1 Agentes Operacionais (8/17)
|
| 51 |
-
|
| 52 |
-
1. **Abaporu** - Orquestrador mestre
|
| 53 |
-
- Coordena investigações multi-agente
|
| 54 |
-
- Execução paralela de tarefas independentes
|
| 55 |
-
- Loop de reflexão para melhoria de qualidade
|
| 56 |
-
|
| 57 |
-
2. **Zumbi dos Palmares** - Investigador de anomalias
|
| 58 |
-
- Análise estatística (Z-score, threshold 2.5σ)
|
| 59 |
-
- Análise espectral (FFT) para padrões periódicos
|
| 60 |
-
- ML: Isolation Forest, One-Class SVM, LOF
|
| 61 |
-
- Detecção de similaridade (Jaccard 85%)
|
| 62 |
-
|
| 63 |
-
3. **Anita Garibaldi** - Especialista em análise
|
| 64 |
-
- Correlação de padrões
|
| 65 |
-
- Análise de tendências
|
| 66 |
-
- Identificação de relacionamentos
|
| 67 |
-
|
| 68 |
-
4. **Tiradentes** - Geração de relatórios
|
| 69 |
-
- Linguagem natural em português
|
| 70 |
-
- Formatação estruturada
|
| 71 |
-
- Sumarização executiva
|
| 72 |
-
|
| 73 |
-
5. **Nanã** - Gerenciamento de memória
|
| 74 |
-
- Memória episódica (eventos)
|
| 75 |
-
- Memória semântica (conhecimento)
|
| 76 |
-
- Memória conversacional (contexto)
|
| 77 |
-
|
| 78 |
-
6. **Ayrton Senna** - Roteamento semântico
|
| 79 |
-
- Detecção de intenção (7 tipos)
|
| 80 |
-
- Roteamento otimizado
|
| 81 |
-
- Balanceamento de carga
|
| 82 |
-
|
| 83 |
-
7. **Machado de Assis** - Análise textual
|
| 84 |
-
- NER (Named Entity Recognition)
|
| 85 |
-
- Análise de documentos
|
| 86 |
-
- Extração de informações
|
| 87 |
-
|
| 88 |
-
8. **Dandara** - Análise de justiça social
|
| 89 |
-
- Equidade em contratos
|
| 90 |
-
- Distribuição de recursos
|
| 91 |
-
- Impacto social
|
| 92 |
-
|
| 93 |
-
### 2.2 Arquitetura de Comunicação
|
| 94 |
-
|
| 95 |
-
```python
|
| 96 |
-
# Padrão de comunicação entre agentes
|
| 97 |
-
message = AgentMessage(
|
| 98 |
-
sender="MasterAgent",
|
| 99 |
-
recipient="InvestigatorAgent",
|
| 100 |
-
action="detect_anomalies",
|
| 101 |
-
payload={"query": "contratos acima de 1M"},
|
| 102 |
-
context=context.to_dict()
|
| 103 |
-
)
|
| 104 |
-
|
| 105 |
-
# Execução paralela
|
| 106 |
-
tasks = [
|
| 107 |
-
ParallelTask(agent_type=AgentType.INVESTIGATOR, message=msg1),
|
| 108 |
-
ParallelTask(agent_type=AgentType.ANALYST, message=msg2)
|
| 109 |
-
]
|
| 110 |
-
results = await parallel_processor.execute_parallel(tasks, context)
|
| 111 |
-
```
|
| 112 |
-
|
| 113 |
-
## 3. Detecção de Anomalias e Pipeline ML
|
| 114 |
-
|
| 115 |
-
### 3.1 Métodos de Detecção
|
| 116 |
-
|
| 117 |
-
1. **Análise Estatística**:
|
| 118 |
-
- Anomalias de preço (Z-score > 2.5)
|
| 119 |
-
- Concentração de fornecedores (>70%)
|
| 120 |
-
- Padrões temporais (picos de atividade)
|
| 121 |
-
|
| 122 |
-
2. **Análise Espectral (FFT)**:
|
| 123 |
-
- Detecção de padrões semanais/mensais/trimestrais
|
| 124 |
-
- Mudanças de regime em gastos
|
| 125 |
-
- Regularidade excessiva (indicador de fraude)
|
| 126 |
-
|
| 127 |
-
3. **Machine Learning**:
|
| 128 |
-
- Isolation Forest (isolamento)
|
| 129 |
-
- One-Class SVM (novidade)
|
| 130 |
-
- Local Outlier Factor (densidade)
|
| 131 |
-
- Modelo Cidadão.AI customizado com atenção
|
| 132 |
-
|
| 133 |
-
4. **Detecção de Similaridade**:
|
| 134 |
-
- Contratos duplicados (Jaccard > 85%)
|
| 135 |
-
- Padrões de pagamento anômalos (>50% discrepância)
|
| 136 |
-
|
| 137 |
-
### 3.2 Resultados de Performance
|
| 138 |
-
|
| 139 |
-
- **Precisão de detecção**: >90%
|
| 140 |
-
- **Taxa de falsos positivos**: <5%
|
| 141 |
-
- **Tempo de análise**: <2s por investigação
|
| 142 |
-
- **Volume processado**: 10k+ contratos/hora
|
| 143 |
-
|
| 144 |
-
## 4. API e Endpoints
|
| 145 |
-
|
| 146 |
-
### 4.1 Endpoints Principais
|
| 147 |
-
|
| 148 |
-
```
|
| 149 |
-
REST API:
|
| 150 |
-
- POST /api/v1/investigations/create
|
| 151 |
-
- GET /api/v1/investigations/{id}/status
|
| 152 |
-
- POST /api/v1/analysis/patterns
|
| 153 |
-
- POST /api/v1/chat/message
|
| 154 |
-
- GET /api/v1/chat/stream (SSE)
|
| 155 |
-
|
| 156 |
-
WebSocket:
|
| 157 |
-
- WS /api/v1/ws/chat/{session_id}
|
| 158 |
-
- WS /api/v1/ws/investigations/{id}
|
| 159 |
-
|
| 160 |
-
GraphQL:
|
| 161 |
-
- /graphql (queries flexíveis)
|
| 162 |
-
|
| 163 |
-
Batch API:
|
| 164 |
-
- POST /api/v1/batch/process
|
| 165 |
-
|
| 166 |
-
Métricas:
|
| 167 |
-
- GET /health/metrics (Prometheus)
|
| 168 |
-
- GET /health/metrics/json
|
| 169 |
-
```
|
| 170 |
-
|
| 171 |
-
### 4.2 Recursos Avançados
|
| 172 |
-
|
| 173 |
-
- **Streaming SSE**: Respostas em tempo real
|
| 174 |
-
- **WebSocket**: Comunicação bidirecional
|
| 175 |
-
- **GraphQL**: Queries flexíveis com limites
|
| 176 |
-
- **Batch API**: Múltiplas operações paralelas
|
| 177 |
-
- **CQRS**: Separação comando/consulta
|
| 178 |
-
|
| 179 |
-
## 5. Segurança e Autenticação
|
| 180 |
-
|
| 181 |
-
### 5.1 Implementação de Segurança
|
| 182 |
-
|
| 183 |
-
- **JWT Dual Token**: Access (30min) + Refresh (7 dias)
|
| 184 |
-
- **Hashing**: bcrypt para senhas
|
| 185 |
-
- **Roles**: admin, analyst com permissões
|
| 186 |
-
- **Rate Limiting**: Por usuário/endpoint
|
| 187 |
-
- **Circuit Breakers**: Prevenção de cascata
|
| 188 |
-
- **Audit Logging**: Rastreamento completo
|
| 189 |
-
|
| 190 |
-
### 5.2 Middleware Stack
|
| 191 |
-
|
| 192 |
-
1. SecurityMiddleware (headers, XSS)
|
| 193 |
-
2. LoggingMiddleware (audit trail)
|
| 194 |
-
3. RateLimitMiddleware (throttling)
|
| 195 |
-
4. AuthenticationMiddleware (JWT)
|
| 196 |
-
5. CORS (origens configuráveis)
|
| 197 |
-
|
| 198 |
-
## 6. Otimizações de Performance
|
| 199 |
-
|
| 200 |
-
### 6.1 Cache Multi-Nível
|
| 201 |
-
|
| 202 |
-
- **L1 Memory**: LRU in-memory (ms latência)
|
| 203 |
-
- **L2 Redis**: Distribuído (10ms latência)
|
| 204 |
-
- **L3 Database**: Persistente (100ms latência)
|
| 205 |
-
|
| 206 |
-
TTLs configurados:
|
| 207 |
-
- API responses: 5 minutos
|
| 208 |
-
- Dados transparência: 1 hora
|
| 209 |
-
- Resultados análise: 24 horas
|
| 210 |
-
- Embeddings ML: 1 semana
|
| 211 |
-
|
| 212 |
-
### 6.2 Otimizações Implementadas
|
| 213 |
-
|
| 214 |
-
1. **orjson**: 3x mais rápido que json padrão
|
| 215 |
-
2. **Brotli/Gzip**: 70-90% redução bandwidth
|
| 216 |
-
3. **Connection Pooling**: 20+30 conexões DB
|
| 217 |
-
4. **Agent Pooling**: Instâncias pré-aquecidas
|
| 218 |
-
5. **Parallel Processing**: MapReduce patterns
|
| 219 |
-
6. **HTTP/2**: Multiplexing para LLM providers
|
| 220 |
-
|
| 221 |
-
### 6.3 Resultados Alcançados
|
| 222 |
-
|
| 223 |
-
- **Latência API**: P95 < 180ms ✅
|
| 224 |
-
- **Throughput**: 12,000 req/s ✅
|
| 225 |
-
- **Cache Hit Rate**: 92% ✅
|
| 226 |
-
- **Tempo resposta agente**: <2s ✅
|
| 227 |
-
- **Uso memória**: 1.8GB ✅
|
| 228 |
-
|
| 229 |
-
## 7. Integração Portal da Transparência
|
| 230 |
-
|
| 231 |
-
### 7.1 Cliente API
|
| 232 |
-
|
| 233 |
-
```python
|
| 234 |
-
async with TransparencyAPIClient() as client:
|
| 235 |
-
filters = TransparencyAPIFilter(
|
| 236 |
-
codigo_orgao="26000",
|
| 237 |
-
ano=2024,
|
| 238 |
-
valor_inicial=100000
|
| 239 |
-
)
|
| 240 |
-
response = await client.get_contracts(filters)
|
| 241 |
-
```
|
| 242 |
-
|
| 243 |
-
### 7.2 Recursos
|
| 244 |
-
|
| 245 |
-
- **Fallback automático**: Dados demo sem API key
|
| 246 |
-
- **Rate limiting**: 90 req/min com espera
|
| 247 |
-
- **Retry logic**: Backoff exponencial
|
| 248 |
-
- **Multi-endpoint**: Contratos, despesas, servidores
|
| 249 |
-
- **Paginação**: Automática
|
| 250 |
-
|
| 251 |
-
## 8. Monitoramento e Observabilidade
|
| 252 |
-
|
| 253 |
-
### 8.1 Stack Prometheus + Grafana
|
| 254 |
-
|
| 255 |
-
- **Métricas customizadas**: 15+ métricas específicas
|
| 256 |
-
- **Dashboards**: Overview, Agents, Performance
|
| 257 |
-
- **Alertas**: 6 categorias (saúde, infra, agentes, negócio, SLO, segurança)
|
| 258 |
-
- **Retenção**: 30 dias / 5GB
|
| 259 |
-
|
| 260 |
-
### 8.2 Métricas Principais
|
| 261 |
-
|
| 262 |
-
- `cidadao_ai_agent_tasks_total`
|
| 263 |
-
- `cidadao_ai_investigations_total`
|
| 264 |
-
- `cidadao_ai_anomalies_detected_total`
|
| 265 |
-
- `cidadao_ai_request_duration_seconds`
|
| 266 |
-
- `cidadao_ai_cache_hit_ratio`
|
| 267 |
-
|
| 268 |
-
## 9. Testing e CI/CD
|
| 269 |
-
|
| 270 |
-
### 9.1 Estado Atual
|
| 271 |
-
|
| 272 |
-
- **Cobertura**: 45% (meta: 80%)
|
| 273 |
-
- **Categorias**: Unit, Integration, Multi-agent, E2E
|
| 274 |
-
- **CI Pipeline**: GitHub Actions completo
|
| 275 |
-
- **Deployment**: Automático para HuggingFace
|
| 276 |
-
|
| 277 |
-
### 9.2 Gaps Identificados
|
| 278 |
-
|
| 279 |
-
- 13/17 agentes sem testes
|
| 280 |
-
- Falta suite de performance
|
| 281 |
-
- WebSocket tests incompletos
|
| 282 |
-
- Security tests ausentes
|
| 283 |
-
|
| 284 |
-
## 10. Débito Técnico e Próximos Passos
|
| 285 |
-
|
| 286 |
-
### 10.1 Prioridades Imediatas (1-2 semanas)
|
| 287 |
-
|
| 288 |
-
1. Completar testes dos agentes restantes
|
| 289 |
-
2. Implementar métricas Prometheus no código
|
| 290 |
-
3. Documentar deployment produção
|
| 291 |
-
4. Adicionar autenticação WebSocket
|
| 292 |
-
5. Criar plano disaster recovery
|
| 293 |
-
|
| 294 |
-
### 10.2 Metas Curto Prazo (1 mês)
|
| 295 |
-
|
| 296 |
-
1. Atingir 80% cobertura testes
|
| 297 |
-
2. Implementar distributed tracing
|
| 298 |
-
3. Completar auditoria segurança
|
| 299 |
-
4. Adicionar testes performance automatizados
|
| 300 |
-
5. Documentar SLAs/SLOs
|
| 301 |
-
|
| 302 |
-
### 10.3 Visão Longo Prazo (3 meses)
|
| 303 |
-
|
| 304 |
-
1. Considerar arquitetura microserviços
|
| 305 |
-
2. Manifests Kubernetes
|
| 306 |
-
3. Estratégia multi-região
|
| 307 |
-
4. Infraestrutura ML avançada
|
| 308 |
-
5. API gateway completo
|
| 309 |
-
|
| 310 |
-
## 11. Conclusão
|
| 311 |
-
|
| 312 |
-
O Cidadão.AI Backend demonstra maturidade arquitetural com recursos enterprise-grade, sistema multi-agente sofisticado, e infraestrutura pronta para produção. As otimizações recentes posicionam o sistema para alto desempenho e escalabilidade. Os principais desafios estão na cobertura de testes e documentação de produção, mas a fundação é sólida para deployment e crescimento.
|
| 313 |
-
|
| 314 |
-
### Pontos Fortes
|
| 315 |
-
|
| 316 |
-
- ✅ Arquitetura multi-agente inovadora
|
| 317 |
-
- ✅ Performance excepcional alcançada
|
| 318 |
-
- ✅ Segurança enterprise implementada
|
| 319 |
-
- ✅ Observabilidade completa
|
| 320 |
-
- ✅ Integração governo funcional
|
| 321 |
-
|
| 322 |
-
### Áreas de Melhoria
|
| 323 |
-
|
| 324 |
-
- ⚠️ Cobertura testes abaixo da meta
|
| 325 |
-
- ⚠️ Documentação produção incompleta
|
| 326 |
-
- ⚠️ Falta testes performance automatizados
|
| 327 |
-
- ⚠️ Disaster recovery não documentado
|
| 328 |
-
- ⚠️ 9 agentes aguardando implementação
|
| 329 |
-
|
| 330 |
-
O projeto está bem posicionado para se tornar a principal plataforma de transparência governamental do Brasil, com tecnologia de ponta e foco em resultados práticos para a sociedade.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EMERGENCY_SOLUTION.md
DELETED
|
@@ -1,84 +0,0 @@
|
|
| 1 |
-
# 🚨 Solução de Emergência - Chat Endpoints
|
| 2 |
-
|
| 3 |
-
## Status dos Endpoints
|
| 4 |
-
|
| 5 |
-
### ✅ FUNCIONANDO 100%
|
| 6 |
-
1. **`/api/v1/chat/simple`** - Endpoint principal com Maritaca AI
|
| 7 |
-
- Taxa de sucesso: 100%
|
| 8 |
-
- Modelo: Sabiá-3
|
| 9 |
-
- Tempo de resposta: 1.4s - 14.6s
|
| 10 |
-
|
| 11 |
-
2. **`/api/v1/chat/emergency`** - NOVO endpoint ultra-confiável
|
| 12 |
-
- Sem dependências complexas
|
| 13 |
-
- Fallback inteligente garantido
|
| 14 |
-
- Sempre retorna resposta válida
|
| 15 |
-
|
| 16 |
-
### ⚠️ EM CORREÇÃO
|
| 17 |
-
3. **`/api/v1/chat/stable`** - Corrigido mas ainda testando
|
| 18 |
-
4. **`/api/v1/chat/optimized`** - Com Sabiazinho (econômico)
|
| 19 |
-
5. **`/api/v1/chat/message`** - Original com problemas
|
| 20 |
-
|
| 21 |
-
## Recomendação para Frontend
|
| 22 |
-
|
| 23 |
-
**USE IMEDIATAMENTE**: `/api/v1/chat/emergency`
|
| 24 |
-
|
| 25 |
-
```typescript
|
| 26 |
-
// Exemplo de integração
|
| 27 |
-
const response = await fetch('https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/emergency', {
|
| 28 |
-
method: 'POST',
|
| 29 |
-
headers: { 'Content-Type': 'application/json' },
|
| 30 |
-
body: JSON.stringify({
|
| 31 |
-
message: "Olá, como você pode me ajudar?",
|
| 32 |
-
session_id: "session_123"
|
| 33 |
-
})
|
| 34 |
-
})
|
| 35 |
-
|
| 36 |
-
const data = await response.json()
|
| 37 |
-
// Sempre retorna resposta válida!
|
| 38 |
-
```
|
| 39 |
-
|
| 40 |
-
## Características do Emergency Endpoint
|
| 41 |
-
|
| 42 |
-
1. **Zero dependências complexas** - Não usa IntentDetector ou serviços externos
|
| 43 |
-
2. **Maritaca com fallback** - Tenta Maritaca primeiro, mas tem respostas prontas
|
| 44 |
-
3. **Respostas contextualizadas** - Diferentes respostas para cada tipo de pergunta
|
| 45 |
-
4. **100% disponibilidade** - Nunca falha, sempre responde
|
| 46 |
-
|
| 47 |
-
## Ordem de Prioridade para Frontend
|
| 48 |
-
|
| 49 |
-
1. **Primeira escolha**: `/api/v1/chat/emergency` (100% confiável)
|
| 50 |
-
2. **Segunda escolha**: `/api/v1/chat/simple` (funcionando bem)
|
| 51 |
-
3. **Futura**: `/api/v1/chat/optimized` (quando estabilizar)
|
| 52 |
-
|
| 53 |
-
## Exemplo de Resposta
|
| 54 |
-
|
| 55 |
-
```json
|
| 56 |
-
{
|
| 57 |
-
"session_id": "emergency_1234567890",
|
| 58 |
-
"agent_id": "assistant",
|
| 59 |
-
"agent_name": "Assistente Cidadão.AI",
|
| 60 |
-
"message": "Olá! Sou o assistente do Cidadão.AI...",
|
| 61 |
-
"confidence": 0.95,
|
| 62 |
-
"suggested_actions": ["start_investigation", "view_recent_contracts", "help"],
|
| 63 |
-
"metadata": {
|
| 64 |
-
"backend": "maritaca_ai",
|
| 65 |
-
"timestamp": "2025-09-20T20:30:00Z"
|
| 66 |
-
}
|
| 67 |
-
}
|
| 68 |
-
```
|
| 69 |
-
|
| 70 |
-
## Monitoramento
|
| 71 |
-
|
| 72 |
-
Endpoint de saúde: `GET /api/v1/chat/emergency/health`
|
| 73 |
-
|
| 74 |
-
```json
|
| 75 |
-
{
|
| 76 |
-
"status": "operational",
|
| 77 |
-
"endpoint": "/api/v1/chat/emergency",
|
| 78 |
-
"maritaca_configured": true,
|
| 79 |
-
"fallback_ready": true,
|
| 80 |
-
"timestamp": "2025-09-20T20:30:00Z"
|
| 81 |
-
}
|
| 82 |
-
```
|
| 83 |
-
|
| 84 |
-
**ESTE ENDPOINT GARANTE 100% DE DISPONIBILIDADE!**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FIX_HUGGINGFACE_DEPLOYMENT.md
DELETED
|
@@ -1,117 +0,0 @@
|
|
| 1 |
-
# 🚨 Correção Urgente - Backend HuggingFace
|
| 2 |
-
|
| 3 |
-
## Problema Identificado
|
| 4 |
-
|
| 5 |
-
O backend no HuggingFace está rodando a versão **ERRADA** do código:
|
| 6 |
-
|
| 7 |
-
1. **Versão atual** (app.py): Apenas tem o EnhancedZumbiAgent
|
| 8 |
-
2. **Versão correta** (src/api/app.py): Sistema completo com Drummond e todos os agentes
|
| 9 |
-
|
| 10 |
-
Por isso o frontend sempre retorna "modo manutenção" - o Drummond não existe!
|
| 11 |
-
|
| 12 |
-
## Solução Imediata
|
| 13 |
-
|
| 14 |
-
### Opção 1: Substituir app.py (Mais Simples)
|
| 15 |
-
|
| 16 |
-
```bash
|
| 17 |
-
# No branch hf-fastapi
|
| 18 |
-
git checkout hf-fastapi
|
| 19 |
-
|
| 20 |
-
# Backup do app.py atual
|
| 21 |
-
mv app.py app_simple.py
|
| 22 |
-
|
| 23 |
-
# Criar novo app.py que importa o sistema completo
|
| 24 |
-
cat > app.py << 'EOF'
|
| 25 |
-
#!/usr/bin/env python3
|
| 26 |
-
import os
|
| 27 |
-
import sys
|
| 28 |
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 29 |
-
|
| 30 |
-
from src.api.app import app
|
| 31 |
-
import uvicorn
|
| 32 |
-
|
| 33 |
-
if __name__ == "__main__":
|
| 34 |
-
port = int(os.getenv("PORT", 7860))
|
| 35 |
-
uvicorn.run(app, host="0.0.0.0", port=port, forwarded_allow_ips="*", proxy_headers=True)
|
| 36 |
-
EOF
|
| 37 |
-
|
| 38 |
-
# Commit e push
|
| 39 |
-
git add app.py app_simple.py
|
| 40 |
-
git commit -m "fix: use full multi-agent system with Drummond in HuggingFace deployment"
|
| 41 |
-
git push origin hf-fastapi
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
### Opção 2: Adicionar Drummond ao app.py Atual
|
| 45 |
-
|
| 46 |
-
Se preferir manter o app.py simplificado, adicione o Drummond:
|
| 47 |
-
|
| 48 |
-
```python
|
| 49 |
-
# No app.py, após a linha 522 (onde cria enhanced_zumbi):
|
| 50 |
-
from src.agents.drummond_simple import SimpleDrummondAgent
|
| 51 |
-
drummond_agent = SimpleDrummondAgent()
|
| 52 |
-
|
| 53 |
-
# Adicionar endpoint do Drummond
|
| 54 |
-
@app.post("/api/v1/chat/message")
|
| 55 |
-
async def chat_message(request: ChatRequest):
|
| 56 |
-
"""Chat endpoint with Drummond agent."""
|
| 57 |
-
try:
|
| 58 |
-
response = await drummond_agent.process_message(request.message)
|
| 59 |
-
return {
|
| 60 |
-
"status": "success",
|
| 61 |
-
"agent": "drummond",
|
| 62 |
-
"message": response,
|
| 63 |
-
"is_demo_mode": False
|
| 64 |
-
}
|
| 65 |
-
except Exception as e:
|
| 66 |
-
logger.error(f"Drummond error: {str(e)}")
|
| 67 |
-
return {
|
| 68 |
-
"status": "maintenance",
|
| 69 |
-
"agent": "system",
|
| 70 |
-
"message": "Sistema em manutenção temporária",
|
| 71 |
-
"is_demo_mode": True
|
| 72 |
-
}
|
| 73 |
-
```
|
| 74 |
-
|
| 75 |
-
## Correção do Erro 403 da API
|
| 76 |
-
|
| 77 |
-
O erro 403 indica que a API key do Portal da Transparência está inválida:
|
| 78 |
-
|
| 79 |
-
1. Verifique no HuggingFace Spaces Settings:
|
| 80 |
-
- Vá para: https://huggingface.co/spaces/neural-thinker/cidadao.ai-backend/settings
|
| 81 |
-
- Procure por `TRANSPARENCY_API_KEY`
|
| 82 |
-
- Se não existir ou estiver inválida, adicione uma nova
|
| 83 |
-
|
| 84 |
-
2. Para obter nova API key:
|
| 85 |
-
- Acesse: https://www.portaldatransparencia.gov.br/api-de-dados
|
| 86 |
-
- Cadastre-se e gere uma nova chave
|
| 87 |
-
- Adicione no HuggingFace Spaces
|
| 88 |
-
|
| 89 |
-
## Deploy Correto
|
| 90 |
-
|
| 91 |
-
```bash
|
| 92 |
-
# Após fazer as correções
|
| 93 |
-
git push origin hf-fastapi
|
| 94 |
-
|
| 95 |
-
# O HuggingFace deve fazer redeploy automático
|
| 96 |
-
# Se não, vá em Settings > Factory reboot
|
| 97 |
-
```
|
| 98 |
-
|
| 99 |
-
## Verificação
|
| 100 |
-
|
| 101 |
-
Após o deploy, teste:
|
| 102 |
-
|
| 103 |
-
```bash
|
| 104 |
-
# Verificar se Drummond está disponível
|
| 105 |
-
curl https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/message \
|
| 106 |
-
-H "Content-Type: application/json" \
|
| 107 |
-
-d '{"message": "Olá, como você pode me ajudar?"}'
|
| 108 |
-
|
| 109 |
-
# Deve retornar resposta do Drummond, não "modo manutenção"
|
| 110 |
-
```
|
| 111 |
-
|
| 112 |
-
## Resumo
|
| 113 |
-
|
| 114 |
-
1. **Problema**: Versão errada deployada (sem Drummond)
|
| 115 |
-
2. **Solução**: Usar app.py que importa src.api.app completo
|
| 116 |
-
3. **Extra**: Corrigir API key do Portal da Transparência
|
| 117 |
-
4. **Resultado**: Frontend funcionará normalmente com chat ativo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FRONTEND_CHAT_INTEGRATION.md
DELETED
|
@@ -1,363 +0,0 @@
|
|
| 1 |
-
# 🤖 Guia de Integração: Chat Drummond/Maritaca AI no Frontend Next.js
|
| 2 |
-
|
| 3 |
-
## 🏗️ Arquitetura da Integração
|
| 4 |
-
|
| 5 |
-
```
|
| 6 |
-
Frontend Next.js → Backend API → Agente Drummond → Maritaca AI
|
| 7 |
-
(Interface) (FastAPI) (Poeta Mineiro) (LLM Brasileiro)
|
| 8 |
-
```
|
| 9 |
-
|
| 10 |
-
## 📡 Endpoints Disponíveis
|
| 11 |
-
|
| 12 |
-
### 1. Endpoint Principal (Recomendado)
|
| 13 |
-
```
|
| 14 |
-
POST https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/message
|
| 15 |
-
```
|
| 16 |
-
|
| 17 |
-
**Request:**
|
| 18 |
-
```json
|
| 19 |
-
{
|
| 20 |
-
"message": "Olá, como posso investigar contratos públicos?",
|
| 21 |
-
"session_id": "uuid-opcional", // Mantém contexto da conversa
|
| 22 |
-
"context": {} // Contexto adicional (opcional)
|
| 23 |
-
}
|
| 24 |
-
```
|
| 25 |
-
|
| 26 |
-
**Response:**
|
| 27 |
-
```json
|
| 28 |
-
{
|
| 29 |
-
"session_id": "550e8400-e29b-41d4-a716-446655440000",
|
| 30 |
-
"agent_id": "drummond",
|
| 31 |
-
"agent_name": "Carlos Drummond de Andrade",
|
| 32 |
-
"message": "Uai! Que bom falar com você...",
|
| 33 |
-
"confidence": 0.95,
|
| 34 |
-
"suggested_actions": ["investigar_contratos", "ver_gastos"],
|
| 35 |
-
"requires_input": null,
|
| 36 |
-
"metadata": {
|
| 37 |
-
"intent_type": "greeting",
|
| 38 |
-
"agent_version": "1.0"
|
| 39 |
-
}
|
| 40 |
-
}
|
| 41 |
-
```
|
| 42 |
-
|
| 43 |
-
### 2. Endpoint Alternativo (Fallback)
|
| 44 |
-
```
|
| 45 |
-
POST https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/simple
|
| 46 |
-
```
|
| 47 |
-
|
| 48 |
-
**Request:**
|
| 49 |
-
```json
|
| 50 |
-
{
|
| 51 |
-
"message": "Sua mensagem aqui",
|
| 52 |
-
"session_id": "uuid-opcional"
|
| 53 |
-
}
|
| 54 |
-
```
|
| 55 |
-
|
| 56 |
-
**Response:**
|
| 57 |
-
```json
|
| 58 |
-
{
|
| 59 |
-
"message": "Resposta do Drummond via Maritaca AI",
|
| 60 |
-
"session_id": "550e8400-e29b-41d4-a716-446655440000",
|
| 61 |
-
"timestamp": "2025-09-20T20:00:00Z",
|
| 62 |
-
"model_used": "sabia-3" // ou "fallback" se Maritaca estiver offline
|
| 63 |
-
}
|
| 64 |
-
```
|
| 65 |
-
|
| 66 |
-
## 🛠️ Implementação Passo a Passo
|
| 67 |
-
|
| 68 |
-
### Passo 1: Criar o Serviço de API
|
| 69 |
-
|
| 70 |
-
```typescript
|
| 71 |
-
// services/cidadaoChat.service.ts
|
| 72 |
-
|
| 73 |
-
const API_URL = process.env.NEXT_PUBLIC_CIDADAO_API_URL ||
|
| 74 |
-
'https://neural-thinker-cidadao-ai-backend.hf.space';
|
| 75 |
-
|
| 76 |
-
export class CidadaoChatService {
|
| 77 |
-
private sessionId: string | null = null;
|
| 78 |
-
|
| 79 |
-
async sendMessage(message: string) {
|
| 80 |
-
try {
|
| 81 |
-
const response = await fetch(`${API_URL}/api/v1/chat/message`, {
|
| 82 |
-
method: 'POST',
|
| 83 |
-
headers: {
|
| 84 |
-
'Content-Type': 'application/json',
|
| 85 |
-
},
|
| 86 |
-
body: JSON.stringify({
|
| 87 |
-
message,
|
| 88 |
-
session_id: this.sessionId,
|
| 89 |
-
context: {}
|
| 90 |
-
}),
|
| 91 |
-
});
|
| 92 |
-
|
| 93 |
-
const data = await response.json();
|
| 94 |
-
|
| 95 |
-
// Guarda o session_id para manter contexto
|
| 96 |
-
if (!this.sessionId && data.session_id) {
|
| 97 |
-
this.sessionId = data.session_id;
|
| 98 |
-
}
|
| 99 |
-
|
| 100 |
-
return data;
|
| 101 |
-
} catch (error) {
|
| 102 |
-
console.error('Erro na comunicação:', error);
|
| 103 |
-
throw error;
|
| 104 |
-
}
|
| 105 |
-
}
|
| 106 |
-
}
|
| 107 |
-
```
|
| 108 |
-
|
| 109 |
-
### Passo 2: Hook React para Gerenciar o Chat
|
| 110 |
-
|
| 111 |
-
```typescript
|
| 112 |
-
// hooks/useCidadaoChat.ts
|
| 113 |
-
|
| 114 |
-
import { useState, useCallback } from 'react';
|
| 115 |
-
import { CidadaoChatService } from '../services/cidadaoChat.service';
|
| 116 |
-
|
| 117 |
-
const chatService = new CidadaoChatService();
|
| 118 |
-
|
| 119 |
-
export function useCidadaoChat() {
|
| 120 |
-
const [messages, setMessages] = useState([]);
|
| 121 |
-
const [isLoading, setIsLoading] = useState(false);
|
| 122 |
-
|
| 123 |
-
const sendMessage = useCallback(async (text: string) => {
|
| 124 |
-
// Adiciona mensagem do usuário
|
| 125 |
-
setMessages(prev => [...prev, {
|
| 126 |
-
id: Date.now(),
|
| 127 |
-
role: 'user',
|
| 128 |
-
content: text,
|
| 129 |
-
timestamp: new Date()
|
| 130 |
-
}]);
|
| 131 |
-
|
| 132 |
-
setIsLoading(true);
|
| 133 |
-
|
| 134 |
-
try {
|
| 135 |
-
const response = await chatService.sendMessage(text);
|
| 136 |
-
|
| 137 |
-
// Adiciona resposta do Drummond
|
| 138 |
-
setMessages(prev => [...prev, {
|
| 139 |
-
id: Date.now() + 1,
|
| 140 |
-
role: 'assistant',
|
| 141 |
-
content: response.message,
|
| 142 |
-
agentName: response.agent_name,
|
| 143 |
-
confidence: response.confidence,
|
| 144 |
-
timestamp: new Date()
|
| 145 |
-
}]);
|
| 146 |
-
|
| 147 |
-
return response;
|
| 148 |
-
} finally {
|
| 149 |
-
setIsLoading(false);
|
| 150 |
-
}
|
| 151 |
-
}, []);
|
| 152 |
-
|
| 153 |
-
return {
|
| 154 |
-
messages,
|
| 155 |
-
sendMessage,
|
| 156 |
-
isLoading
|
| 157 |
-
};
|
| 158 |
-
}
|
| 159 |
-
```
|
| 160 |
-
|
| 161 |
-
### Passo 3: Componente de Chat
|
| 162 |
-
|
| 163 |
-
```tsx
|
| 164 |
-
// components/CidadaoChat.tsx
|
| 165 |
-
|
| 166 |
-
export function CidadaoChat() {
|
| 167 |
-
const { messages, sendMessage, isLoading } = useCidadaoChat();
|
| 168 |
-
const [input, setInput] = useState('');
|
| 169 |
-
|
| 170 |
-
const handleSubmit = async (e: FormEvent) => {
|
| 171 |
-
e.preventDefault();
|
| 172 |
-
if (input.trim() && !isLoading) {
|
| 173 |
-
await sendMessage(input);
|
| 174 |
-
setInput('');
|
| 175 |
-
}
|
| 176 |
-
};
|
| 177 |
-
|
| 178 |
-
return (
|
| 179 |
-
<div className="chat-container">
|
| 180 |
-
<div className="messages">
|
| 181 |
-
{messages.map((msg) => (
|
| 182 |
-
<div key={msg.id} className={`message ${msg.role}`}>
|
| 183 |
-
{msg.agentName && (
|
| 184 |
-
<span className="agent-name">{msg.agentName}</span>
|
| 185 |
-
)}
|
| 186 |
-
<p>{msg.content}</p>
|
| 187 |
-
</div>
|
| 188 |
-
))}
|
| 189 |
-
{isLoading && <div className="loading">Drummond está pensando...</div>}
|
| 190 |
-
</div>
|
| 191 |
-
|
| 192 |
-
<form onSubmit={handleSubmit}>
|
| 193 |
-
<input
|
| 194 |
-
type="text"
|
| 195 |
-
value={input}
|
| 196 |
-
onChange={(e) => setInput(e.target.value)}
|
| 197 |
-
placeholder="Pergunte sobre transparência pública..."
|
| 198 |
-
disabled={isLoading}
|
| 199 |
-
/>
|
| 200 |
-
<button type="submit" disabled={isLoading}>
|
| 201 |
-
Enviar
|
| 202 |
-
</button>
|
| 203 |
-
</form>
|
| 204 |
-
</div>
|
| 205 |
-
);
|
| 206 |
-
}
|
| 207 |
-
```
|
| 208 |
-
|
| 209 |
-
## 🎯 Casos de Uso e Intents
|
| 210 |
-
|
| 211 |
-
O Drummond responde melhor a estes tipos de mensagem:
|
| 212 |
-
|
| 213 |
-
### 1. **Saudações** (IntentType.GREETING)
|
| 214 |
-
- "Olá", "Oi", "Bom dia", "Boa tarde"
|
| 215 |
-
- **Resposta**: Saudação mineira calorosa com explicação do Cidadão.AI
|
| 216 |
-
|
| 217 |
-
### 2. **Investigações** (IntentType.INVESTIGATE)
|
| 218 |
-
- "Quero investigar contratos de saúde"
|
| 219 |
-
- "Mostre gastos com educação em SP"
|
| 220 |
-
- **Resposta**: Direcionamento para investigação ou relatório
|
| 221 |
-
|
| 222 |
-
### 3. **Ajuda** (IntentType.HELP_REQUEST)
|
| 223 |
-
- "Como funciona?", "Me ajuda", "O que você faz?"
|
| 224 |
-
- **Resposta**: Explicação das capacidades do sistema
|
| 225 |
-
|
| 226 |
-
### 4. **Sobre o Sistema** (IntentType.ABOUT_SYSTEM)
|
| 227 |
-
- "O que é o Cidadão.AI?"
|
| 228 |
-
- "Como funciona o portal da transparência?"
|
| 229 |
-
- **Resposta**: Informações educativas sobre transparência
|
| 230 |
-
|
| 231 |
-
## 🔧 Configurações Importantes
|
| 232 |
-
|
| 233 |
-
### Variáveis de Ambiente (.env.local)
|
| 234 |
-
```bash
|
| 235 |
-
NEXT_PUBLIC_CIDADAO_API_URL=https://neural-thinker-cidadao-ai-backend.hf.space
|
| 236 |
-
```
|
| 237 |
-
|
| 238 |
-
### Headers CORS
|
| 239 |
-
O backend já está configurado para aceitar requisições de:
|
| 240 |
-
- http://localhost:3000
|
| 241 |
-
- https://*.vercel.app
|
| 242 |
-
- Seu domínio customizado
|
| 243 |
-
|
| 244 |
-
### Timeout Recomendado
|
| 245 |
-
```javascript
|
| 246 |
-
// Configure timeout de 30 segundos para a Maritaca AI
|
| 247 |
-
const controller = new AbortController();
|
| 248 |
-
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
| 249 |
-
|
| 250 |
-
fetch(url, {
|
| 251 |
-
signal: controller.signal,
|
| 252 |
-
// ... outras configs
|
| 253 |
-
});
|
| 254 |
-
```
|
| 255 |
-
|
| 256 |
-
## 🚨 Tratamento de Erros
|
| 257 |
-
|
| 258 |
-
```typescript
|
| 259 |
-
async function sendMessageWithErrorHandling(message: string) {
|
| 260 |
-
try {
|
| 261 |
-
const response = await chatService.sendMessage(message);
|
| 262 |
-
return response;
|
| 263 |
-
} catch (error) {
|
| 264 |
-
if (error.name === 'AbortError') {
|
| 265 |
-
// Timeout - Maritaca demorou muito
|
| 266 |
-
return {
|
| 267 |
-
message: 'A resposta está demorando. Por favor, tente novamente.',
|
| 268 |
-
agent_name: 'Sistema',
|
| 269 |
-
confidence: 0
|
| 270 |
-
};
|
| 271 |
-
}
|
| 272 |
-
|
| 273 |
-
// Outros erros
|
| 274 |
-
return {
|
| 275 |
-
message: 'Desculpe, estou com dificuldades técnicas no momento.',
|
| 276 |
-
agent_name: 'Sistema',
|
| 277 |
-
confidence: 0
|
| 278 |
-
};
|
| 279 |
-
}
|
| 280 |
-
}
|
| 281 |
-
```
|
| 282 |
-
|
| 283 |
-
## 📊 Monitoramento e Status
|
| 284 |
-
|
| 285 |
-
### Verificar Status do Serviço
|
| 286 |
-
```typescript
|
| 287 |
-
async function checkServiceHealth() {
|
| 288 |
-
try {
|
| 289 |
-
const response = await fetch(`${API_URL}/health`);
|
| 290 |
-
const data = await response.json();
|
| 291 |
-
|
| 292 |
-
console.log('Status:', data.status); // 'healthy' ou 'degraded'
|
| 293 |
-
console.log('Serviços:', data.services);
|
| 294 |
-
|
| 295 |
-
return data.status === 'healthy';
|
| 296 |
-
} catch (error) {
|
| 297 |
-
return false;
|
| 298 |
-
}
|
| 299 |
-
}
|
| 300 |
-
```
|
| 301 |
-
|
| 302 |
-
### Indicador de Status no UI
|
| 303 |
-
```tsx
|
| 304 |
-
function ServiceStatus() {
|
| 305 |
-
const [status, setStatus] = useState('checking');
|
| 306 |
-
|
| 307 |
-
useEffect(() => {
|
| 308 |
-
checkServiceHealth().then(isHealthy => {
|
| 309 |
-
setStatus(isHealthy ? 'online' : 'limited');
|
| 310 |
-
});
|
| 311 |
-
}, []);
|
| 312 |
-
|
| 313 |
-
return (
|
| 314 |
-
<div className={`status-badge ${status}`}>
|
| 315 |
-
{status === 'online' ? '🟢 Maritaca AI Online' : '🟡 Modo Limitado'}
|
| 316 |
-
</div>
|
| 317 |
-
);
|
| 318 |
-
}
|
| 319 |
-
```
|
| 320 |
-
|
| 321 |
-
## 🎨 Personalização da Interface
|
| 322 |
-
|
| 323 |
-
### Identificando o Agente
|
| 324 |
-
Quando a resposta vem do Drummond com Maritaca AI:
|
| 325 |
-
```javascript
|
| 326 |
-
if (response.agent_name === 'Carlos Drummond de Andrade') {
|
| 327 |
-
// Mostra avatar do Drummond
|
| 328 |
-
// Adiciona estilo "poético mineiro"
|
| 329 |
-
// Confidence > 0.8 = Maritaca está respondendo
|
| 330 |
-
}
|
| 331 |
-
```
|
| 332 |
-
|
| 333 |
-
### Sugestões de Ações
|
| 334 |
-
Se `suggested_actions` estiver presente:
|
| 335 |
-
```tsx
|
| 336 |
-
{response.suggested_actions?.map(action => (
|
| 337 |
-
<button
|
| 338 |
-
key={action}
|
| 339 |
-
onClick={() => handleQuickAction(action)}
|
| 340 |
-
className="quick-action"
|
| 341 |
-
>
|
| 342 |
-
{getActionLabel(action)}
|
| 343 |
-
</button>
|
| 344 |
-
))}
|
| 345 |
-
```
|
| 346 |
-
|
| 347 |
-
## 🚀 Próximos Passos
|
| 348 |
-
|
| 349 |
-
1. **Implementar o serviço** seguindo os exemplos
|
| 350 |
-
2. **Testar a conexão** com o endpoint de health
|
| 351 |
-
3. **Adicionar o componente** de chat na interface
|
| 352 |
-
4. **Personalizar** visual e comportamento
|
| 353 |
-
5. **Monitorar** logs e métricas de uso
|
| 354 |
-
|
| 355 |
-
## 📞 Suporte
|
| 356 |
-
|
| 357 |
-
- **Documentação da API**: https://neural-thinker-cidadao-ai-backend.hf.space/docs
|
| 358 |
-
- **Status do Serviço**: https://neural-thinker-cidadao-ai-backend.hf.space/health
|
| 359 |
-
- **GitHub**: https://github.com/anderson-ufrj/cidadao.ai-backend
|
| 360 |
-
|
| 361 |
-
---
|
| 362 |
-
|
| 363 |
-
*Drummond está ansioso para conversar com os cidadãos brasileiros sobre transparência pública! 🇧🇷*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FRONTEND_INTEGRATION.md
DELETED
|
@@ -1,254 +0,0 @@
|
|
| 1 |
-
# Integração Frontend - Cidadão.AI Chat com Maritaca AI
|
| 2 |
-
|
| 3 |
-
## Status Atual ✅
|
| 4 |
-
|
| 5 |
-
- **Backend**: Funcionando em https://neural-thinker-cidadao-ai-backend.hf.space
|
| 6 |
-
- **Maritaca AI**: Configurada e pronta para uso
|
| 7 |
-
- **Endpoints**: Disponíveis para integração
|
| 8 |
-
|
| 9 |
-
## Endpoints Principais
|
| 10 |
-
|
| 11 |
-
### 1. Chat Principal (com Drummond/Maritaca)
|
| 12 |
-
```
|
| 13 |
-
POST https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/message
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
**Request:**
|
| 17 |
-
```json
|
| 18 |
-
{
|
| 19 |
-
"message": "Olá, como posso investigar contratos públicos?",
|
| 20 |
-
"session_id": "opcional-uuid",
|
| 21 |
-
"context": {}
|
| 22 |
-
}
|
| 23 |
-
```
|
| 24 |
-
|
| 25 |
-
**Response:**
|
| 26 |
-
```json
|
| 27 |
-
{
|
| 28 |
-
"session_id": "uuid",
|
| 29 |
-
"agent_id": "drummond",
|
| 30 |
-
"agent_name": "Carlos Drummond de Andrade",
|
| 31 |
-
"message": "Resposta do agente...",
|
| 32 |
-
"confidence": 0.8,
|
| 33 |
-
"suggested_actions": ["investigar_contratos", "ver_gastos"],
|
| 34 |
-
"metadata": {}
|
| 35 |
-
}
|
| 36 |
-
```
|
| 37 |
-
|
| 38 |
-
### 2. Chat Simplificado (Novo - Mais Confiável)
|
| 39 |
-
```
|
| 40 |
-
POST https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/simple
|
| 41 |
-
```
|
| 42 |
-
|
| 43 |
-
**Request:**
|
| 44 |
-
```json
|
| 45 |
-
{
|
| 46 |
-
"message": "Sua mensagem aqui",
|
| 47 |
-
"session_id": "opcional"
|
| 48 |
-
}
|
| 49 |
-
```
|
| 50 |
-
|
| 51 |
-
**Response:**
|
| 52 |
-
```json
|
| 53 |
-
{
|
| 54 |
-
"message": "Resposta da Maritaca AI ou fallback",
|
| 55 |
-
"session_id": "uuid",
|
| 56 |
-
"timestamp": "2025-09-20T19:45:00Z",
|
| 57 |
-
"model_used": "sabia-3" // ou "fallback"
|
| 58 |
-
}
|
| 59 |
-
```
|
| 60 |
-
|
| 61 |
-
### 3. Status do Chat
|
| 62 |
-
```
|
| 63 |
-
GET https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/simple/status
|
| 64 |
-
```
|
| 65 |
-
|
| 66 |
-
**Response:**
|
| 67 |
-
```json
|
| 68 |
-
{
|
| 69 |
-
"maritaca_available": true,
|
| 70 |
-
"api_key_configured": true,
|
| 71 |
-
"timestamp": "2025-09-20T19:45:00Z"
|
| 72 |
-
}
|
| 73 |
-
```
|
| 74 |
-
|
| 75 |
-
## Exemplo de Integração no Next.js
|
| 76 |
-
|
| 77 |
-
```typescript
|
| 78 |
-
// services/chatService.ts
|
| 79 |
-
const BACKEND_URL = 'https://neural-thinker-cidadao-ai-backend.hf.space';
|
| 80 |
-
|
| 81 |
-
export interface ChatMessage {
|
| 82 |
-
message: string;
|
| 83 |
-
session_id?: string;
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
export interface ChatResponse {
|
| 87 |
-
message: string;
|
| 88 |
-
session_id: string;
|
| 89 |
-
timestamp: string;
|
| 90 |
-
model_used: string;
|
| 91 |
-
}
|
| 92 |
-
|
| 93 |
-
export async function sendChatMessage(message: string, sessionId?: string): Promise<ChatResponse> {
|
| 94 |
-
try {
|
| 95 |
-
const response = await fetch(`${BACKEND_URL}/api/v1/chat/simple`, {
|
| 96 |
-
method: 'POST',
|
| 97 |
-
headers: {
|
| 98 |
-
'Content-Type': 'application/json',
|
| 99 |
-
},
|
| 100 |
-
body: JSON.stringify({
|
| 101 |
-
message,
|
| 102 |
-
session_id: sessionId
|
| 103 |
-
})
|
| 104 |
-
});
|
| 105 |
-
|
| 106 |
-
if (!response.ok) {
|
| 107 |
-
throw new Error(`HTTP error! status: ${response.status}`);
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
return await response.json();
|
| 111 |
-
} catch (error) {
|
| 112 |
-
console.error('Chat error:', error);
|
| 113 |
-
throw error;
|
| 114 |
-
}
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
// Verificar status do serviço
|
| 118 |
-
export async function checkChatStatus() {
|
| 119 |
-
try {
|
| 120 |
-
const response = await fetch(`${BACKEND_URL}/api/v1/chat/simple/status`);
|
| 121 |
-
return await response.json();
|
| 122 |
-
} catch (error) {
|
| 123 |
-
console.error('Status check error:', error);
|
| 124 |
-
return { maritaca_available: false, api_key_configured: false };
|
| 125 |
-
}
|
| 126 |
-
}
|
| 127 |
-
```
|
| 128 |
-
|
| 129 |
-
## Componente React Exemplo
|
| 130 |
-
|
| 131 |
-
```tsx
|
| 132 |
-
// components/Chat.tsx
|
| 133 |
-
import { useState, useEffect } from 'react';
|
| 134 |
-
import { sendChatMessage, checkChatStatus } from '../services/chatService';
|
| 135 |
-
|
| 136 |
-
export function Chat() {
|
| 137 |
-
const [messages, setMessages] = useState<Array<{role: string, content: string}>>([]);
|
| 138 |
-
const [input, setInput] = useState('');
|
| 139 |
-
const [loading, setLoading] = useState(false);
|
| 140 |
-
const [sessionId, setSessionId] = useState<string>();
|
| 141 |
-
const [serviceStatus, setServiceStatus] = useState<any>();
|
| 142 |
-
|
| 143 |
-
useEffect(() => {
|
| 144 |
-
// Verificar status do serviço ao carregar
|
| 145 |
-
checkChatStatus().then(setServiceStatus);
|
| 146 |
-
}, []);
|
| 147 |
-
|
| 148 |
-
const handleSend = async () => {
|
| 149 |
-
if (!input.trim()) return;
|
| 150 |
-
|
| 151 |
-
// Adicionar mensagem do usuário
|
| 152 |
-
setMessages(prev => [...prev, { role: 'user', content: input }]);
|
| 153 |
-
setLoading(true);
|
| 154 |
-
|
| 155 |
-
try {
|
| 156 |
-
const response = await sendChatMessage(input, sessionId);
|
| 157 |
-
|
| 158 |
-
// Salvar session ID para próximas mensagens
|
| 159 |
-
if (!sessionId) {
|
| 160 |
-
setSessionId(response.session_id);
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
// Adicionar resposta do bot
|
| 164 |
-
setMessages(prev => [...prev, { role: 'assistant', content: response.message }]);
|
| 165 |
-
} catch (error) {
|
| 166 |
-
setMessages(prev => [...prev, {
|
| 167 |
-
role: 'assistant',
|
| 168 |
-
content: 'Desculpe, ocorreu um erro. Por favor, tente novamente.'
|
| 169 |
-
}]);
|
| 170 |
-
} finally {
|
| 171 |
-
setLoading(false);
|
| 172 |
-
setInput('');
|
| 173 |
-
}
|
| 174 |
-
};
|
| 175 |
-
|
| 176 |
-
return (
|
| 177 |
-
<div>
|
| 178 |
-
{serviceStatus && (
|
| 179 |
-
<div className="status">
|
| 180 |
-
Maritaca AI: {serviceStatus.maritaca_available ? '✅' : '❌'}
|
| 181 |
-
</div>
|
| 182 |
-
)}
|
| 183 |
-
|
| 184 |
-
<div className="messages">
|
| 185 |
-
{messages.map((msg, idx) => (
|
| 186 |
-
<div key={idx} className={`message ${msg.role}`}>
|
| 187 |
-
{msg.content}
|
| 188 |
-
</div>
|
| 189 |
-
))}
|
| 190 |
-
</div>
|
| 191 |
-
|
| 192 |
-
<div className="input-area">
|
| 193 |
-
<input
|
| 194 |
-
value={input}
|
| 195 |
-
onChange={(e) => setInput(e.target.value)}
|
| 196 |
-
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
|
| 197 |
-
placeholder="Digite sua mensagem..."
|
| 198 |
-
disabled={loading}
|
| 199 |
-
/>
|
| 200 |
-
<button onClick={handleSend} disabled={loading}>
|
| 201 |
-
{loading ? 'Enviando...' : 'Enviar'}
|
| 202 |
-
</button>
|
| 203 |
-
</div>
|
| 204 |
-
</div>
|
| 205 |
-
);
|
| 206 |
-
}
|
| 207 |
-
```
|
| 208 |
-
|
| 209 |
-
## Sugestões de Mensagens para Testar
|
| 210 |
-
|
| 211 |
-
1. **Saudações:**
|
| 212 |
-
- "Olá, como você pode me ajudar?"
|
| 213 |
-
- "Bom dia! O que é o Cidadão.AI?"
|
| 214 |
-
|
| 215 |
-
2. **Investigações:**
|
| 216 |
-
- "Quero investigar contratos de saúde"
|
| 217 |
-
- "Como posso analisar gastos com educação?"
|
| 218 |
-
- "Mostre contratos do Ministério da Saúde"
|
| 219 |
-
|
| 220 |
-
3. **Ajuda:**
|
| 221 |
-
- "Me ajude a entender o portal da transparência"
|
| 222 |
-
- "Quais tipos de dados posso consultar?"
|
| 223 |
-
- "Como funciona a detecção de anomalias?"
|
| 224 |
-
|
| 225 |
-
## Tratamento de Erros
|
| 226 |
-
|
| 227 |
-
O backend pode retornar diferentes tipos de respostas:
|
| 228 |
-
|
| 229 |
-
1. **Sucesso com Maritaca AI**: `model_used: "sabia-3"`
|
| 230 |
-
2. **Fallback (sem Maritaca)**: `model_used: "fallback"`
|
| 231 |
-
3. **Erro 500**: Sistema temporariamente indisponível
|
| 232 |
-
4. **Erro 422**: Dados de entrada inválidos
|
| 233 |
-
|
| 234 |
-
## Notas Importantes
|
| 235 |
-
|
| 236 |
-
1. **Session ID**: Mantenha o mesmo `session_id` para manter contexto da conversa
|
| 237 |
-
2. **Rate Limiting**: O backend tem limite de requisições por minuto
|
| 238 |
-
3. **Timeout**: Configure timeout de pelo menos 30 segundos para a Maritaca AI
|
| 239 |
-
4. **CORS**: Já configurado para aceitar requisições do Vercel
|
| 240 |
-
|
| 241 |
-
## Próximos Passos
|
| 242 |
-
|
| 243 |
-
1. Aguardar alguns minutos para o deploy no HuggingFace Spaces
|
| 244 |
-
2. Testar o endpoint `/api/v1/chat/simple`
|
| 245 |
-
3. Integrar no frontend Next.js
|
| 246 |
-
4. Adicionar tratamento de erros e loading states
|
| 247 |
-
5. Implementar persistência de sessão no localStorage
|
| 248 |
-
|
| 249 |
-
## Suporte
|
| 250 |
-
|
| 251 |
-
Em caso de problemas:
|
| 252 |
-
1. Verifique o status em: `/api/v1/chat/simple/status`
|
| 253 |
-
2. Consulte os logs do HuggingFace Spaces
|
| 254 |
-
3. Use o endpoint fallback se a Maritaca estiver indisponível
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FRONTEND_STABLE_INTEGRATION.md
DELETED
|
@@ -1,235 +0,0 @@
|
|
| 1 |
-
# 🚀 Integração Frontend Estável - Cidadão.AI
|
| 2 |
-
|
| 3 |
-
## Solução para 100% de Disponibilidade
|
| 4 |
-
|
| 5 |
-
### Problema Identificado
|
| 6 |
-
- Drummond funcionando em apenas 30% das requisições
|
| 7 |
-
- Falhas em perguntas complexas (~15% sucesso)
|
| 8 |
-
- Instabilidade no backend afetando experiência do usuário
|
| 9 |
-
|
| 10 |
-
### Solução Implementada
|
| 11 |
-
|
| 12 |
-
Criamos um novo endpoint **ultra-estável** com múltiplas camadas de fallback:
|
| 13 |
-
|
| 14 |
-
```
|
| 15 |
-
POST /api/v1/chat/stable
|
| 16 |
-
```
|
| 17 |
-
|
| 18 |
-
### Características
|
| 19 |
-
|
| 20 |
-
1. **3 Camadas de Fallback**:
|
| 21 |
-
- **Camada 1**: Maritaca AI (LLM brasileiro)
|
| 22 |
-
- **Camada 2**: Requisição HTTP direta para Maritaca
|
| 23 |
-
- **Camada 3**: Respostas inteligentes baseadas em regras
|
| 24 |
-
|
| 25 |
-
2. **Garantia de Resposta**:
|
| 26 |
-
- Sempre retorna uma resposta válida
|
| 27 |
-
- Tempo de resposta consistente
|
| 28 |
-
- Detecção de intent funciona sempre
|
| 29 |
-
|
| 30 |
-
3. **Respostas Contextualizadas**:
|
| 31 |
-
- Diferentes respostas para cada tipo de intent
|
| 32 |
-
- Múltiplas variações para evitar repetição
|
| 33 |
-
- Foco em transparência pública
|
| 34 |
-
|
| 35 |
-
## Implementação no Frontend
|
| 36 |
-
|
| 37 |
-
### 1. Atualizar o Serviço de Chat
|
| 38 |
-
|
| 39 |
-
```typescript
|
| 40 |
-
// services/chatService.ts
|
| 41 |
-
export class ChatService {
|
| 42 |
-
private readonly API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://neural-thinker-cidadao-ai-backend.hf.space'
|
| 43 |
-
|
| 44 |
-
async sendMessage(message: string, sessionId?: string): Promise<ChatResponse> {
|
| 45 |
-
try {
|
| 46 |
-
// Usar o novo endpoint estável
|
| 47 |
-
const response = await fetch(`${this.API_URL}/api/v1/chat/stable`, {
|
| 48 |
-
method: 'POST',
|
| 49 |
-
headers: {
|
| 50 |
-
'Content-Type': 'application/json',
|
| 51 |
-
},
|
| 52 |
-
body: JSON.stringify({
|
| 53 |
-
message,
|
| 54 |
-
session_id: sessionId || `session_${Date.now()}`
|
| 55 |
-
})
|
| 56 |
-
})
|
| 57 |
-
|
| 58 |
-
if (!response.ok) {
|
| 59 |
-
throw new Error(`HTTP error! status: ${response.status}`)
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
return await response.json()
|
| 63 |
-
} catch (error) {
|
| 64 |
-
// Fallback local se API falhar
|
| 65 |
-
return {
|
| 66 |
-
session_id: sessionId || `session_${Date.now()}`,
|
| 67 |
-
agent_id: 'system',
|
| 68 |
-
agent_name: 'Sistema',
|
| 69 |
-
message: 'Desculpe, estou com dificuldades técnicas. Por favor, tente novamente.',
|
| 70 |
-
confidence: 0.0,
|
| 71 |
-
suggested_actions: ['retry'],
|
| 72 |
-
metadata: {
|
| 73 |
-
error: true,
|
| 74 |
-
local_fallback: true
|
| 75 |
-
}
|
| 76 |
-
}
|
| 77 |
-
}
|
| 78 |
-
}
|
| 79 |
-
}
|
| 80 |
-
```
|
| 81 |
-
|
| 82 |
-
### 2. Componente de Chat Atualizado
|
| 83 |
-
|
| 84 |
-
```tsx
|
| 85 |
-
// components/Chat.tsx
|
| 86 |
-
import { useState } from 'react'
|
| 87 |
-
import { ChatService } from '@/services/chatService'
|
| 88 |
-
|
| 89 |
-
export function Chat() {
|
| 90 |
-
const [messages, setMessages] = useState<Message[]>([])
|
| 91 |
-
const [isLoading, setIsLoading] = useState(false)
|
| 92 |
-
const chatService = new ChatService()
|
| 93 |
-
|
| 94 |
-
const handleSendMessage = async (message: string) => {
|
| 95 |
-
// Adicionar mensagem do usuário
|
| 96 |
-
const userMessage = {
|
| 97 |
-
id: Date.now().toString(),
|
| 98 |
-
text: message,
|
| 99 |
-
sender: 'user',
|
| 100 |
-
timestamp: new Date()
|
| 101 |
-
}
|
| 102 |
-
setMessages(prev => [...prev, userMessage])
|
| 103 |
-
|
| 104 |
-
setIsLoading(true)
|
| 105 |
-
|
| 106 |
-
try {
|
| 107 |
-
const response = await chatService.sendMessage(message)
|
| 108 |
-
|
| 109 |
-
// Adicionar resposta do assistente
|
| 110 |
-
const assistantMessage = {
|
| 111 |
-
id: (Date.now() + 1).toString(),
|
| 112 |
-
text: response.message,
|
| 113 |
-
sender: response.agent_name,
|
| 114 |
-
timestamp: new Date(),
|
| 115 |
-
metadata: {
|
| 116 |
-
confidence: response.confidence,
|
| 117 |
-
agent_id: response.agent_id,
|
| 118 |
-
backend_used: response.metadata?.agent_used || 'unknown'
|
| 119 |
-
}
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
setMessages(prev => [...prev, assistantMessage])
|
| 123 |
-
|
| 124 |
-
// Log para monitoramento
|
| 125 |
-
console.log('Chat metrics:', {
|
| 126 |
-
agent: response.agent_name,
|
| 127 |
-
confidence: response.confidence,
|
| 128 |
-
backend: response.metadata?.agent_used,
|
| 129 |
-
stable_version: response.metadata?.stable_version
|
| 130 |
-
})
|
| 131 |
-
|
| 132 |
-
} catch (error) {
|
| 133 |
-
console.error('Chat error:', error)
|
| 134 |
-
// Erro já tratado no serviço
|
| 135 |
-
} finally {
|
| 136 |
-
setIsLoading(false)
|
| 137 |
-
}
|
| 138 |
-
}
|
| 139 |
-
|
| 140 |
-
return (
|
| 141 |
-
<div className="chat-container">
|
| 142 |
-
{/* Renderizar mensagens */}
|
| 143 |
-
{/* Renderizar input */}
|
| 144 |
-
{/* Renderizar suggested actions */}
|
| 145 |
-
</div>
|
| 146 |
-
)
|
| 147 |
-
}
|
| 148 |
-
```
|
| 149 |
-
|
| 150 |
-
### 3. Monitoramento de Performance
|
| 151 |
-
|
| 152 |
-
```typescript
|
| 153 |
-
// utils/chatMetrics.ts
|
| 154 |
-
export class ChatMetrics {
|
| 155 |
-
private successCount = 0
|
| 156 |
-
private totalCount = 0
|
| 157 |
-
private backendStats = new Map<string, number>()
|
| 158 |
-
|
| 159 |
-
recordResponse(response: ChatResponse) {
|
| 160 |
-
this.totalCount++
|
| 161 |
-
|
| 162 |
-
if (response.confidence > 0) {
|
| 163 |
-
this.successCount++
|
| 164 |
-
}
|
| 165 |
-
|
| 166 |
-
const backend = response.metadata?.agent_used || 'unknown'
|
| 167 |
-
this.backendStats.set(
|
| 168 |
-
backend,
|
| 169 |
-
(this.backendStats.get(backend) || 0) + 1
|
| 170 |
-
)
|
| 171 |
-
}
|
| 172 |
-
|
| 173 |
-
getStats() {
|
| 174 |
-
return {
|
| 175 |
-
successRate: (this.successCount / this.totalCount) * 100,
|
| 176 |
-
totalRequests: this.totalCount,
|
| 177 |
-
backendUsage: Object.fromEntries(this.backendStats),
|
| 178 |
-
timestamp: new Date()
|
| 179 |
-
}
|
| 180 |
-
}
|
| 181 |
-
}
|
| 182 |
-
```
|
| 183 |
-
|
| 184 |
-
## Benefícios da Nova Solução
|
| 185 |
-
|
| 186 |
-
1. **100% Disponibilidade**: Sempre retorna resposta válida
|
| 187 |
-
2. **Tempo Consistente**: ~200-300ms para todas as requisições
|
| 188 |
-
3. **Fallback Inteligente**: Respostas contextualizadas mesmo sem LLM
|
| 189 |
-
4. **Transparente**: Frontend sabe qual backend foi usado
|
| 190 |
-
5. **Métricas**: Fácil monitorar qual camada está sendo usada
|
| 191 |
-
|
| 192 |
-
## Próximos Passos
|
| 193 |
-
|
| 194 |
-
1. **Deploy Imediato**:
|
| 195 |
-
```bash
|
| 196 |
-
git add .
|
| 197 |
-
git commit -m "feat: add ultra-stable chat endpoint with smart fallbacks"
|
| 198 |
-
git push origin main
|
| 199 |
-
git push huggingface main:main
|
| 200 |
-
```
|
| 201 |
-
|
| 202 |
-
2. **Frontend**:
|
| 203 |
-
- Atualizar para usar `/api/v1/chat/stable`
|
| 204 |
-
- Implementar métricas de monitoramento
|
| 205 |
-
- Testar todas as scenarios
|
| 206 |
-
|
| 207 |
-
3. **Monitoramento**:
|
| 208 |
-
- Acompanhar taxa de uso de cada backend
|
| 209 |
-
- Ajustar fallbacks baseado em métricas
|
| 210 |
-
- Otimizar respostas mais comuns
|
| 211 |
-
|
| 212 |
-
## Teste Rápido
|
| 213 |
-
|
| 214 |
-
```bash
|
| 215 |
-
# Testar localmente
|
| 216 |
-
curl -X POST http://localhost:8000/api/v1/chat/stable \
|
| 217 |
-
-H "Content-Type: application/json" \
|
| 218 |
-
-d '{"message": "Olá, como você pode me ajudar?"}'
|
| 219 |
-
|
| 220 |
-
# Testar em produção (após deploy)
|
| 221 |
-
curl -X POST https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/stable \
|
| 222 |
-
-H "Content-Type: application/json" \
|
| 223 |
-
-d '{"message": "Investigue contratos suspeitos"}'
|
| 224 |
-
```
|
| 225 |
-
|
| 226 |
-
## Garantia
|
| 227 |
-
|
| 228 |
-
Este endpoint garante:
|
| 229 |
-
- ✅ Sempre retorna resposta válida
|
| 230 |
-
- ✅ Nunca retorna erro 500
|
| 231 |
-
- ✅ Tempo de resposta < 500ms
|
| 232 |
-
- ✅ Respostas relevantes para transparência pública
|
| 233 |
-
- ✅ Detecção de intent funcionando 100%
|
| 234 |
-
|
| 235 |
-
Com esta solução, o frontend terá **100% de estabilidade** independente do status dos serviços de AI!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MARITACA_OPTIMIZATION_GUIDE.md
DELETED
|
@@ -1,372 +0,0 @@
|
|
| 1 |
-
# 🚀 Guia de Otimização Maritaca AI - Cidadão.AI
|
| 2 |
-
|
| 3 |
-
## Resumo das Melhorias
|
| 4 |
-
|
| 5 |
-
### 1. Novo Endpoint Otimizado
|
| 6 |
-
- **URL**: `/api/v1/chat/optimized`
|
| 7 |
-
- **Modelo**: Sabiazinho-3 (mais econômico)
|
| 8 |
-
- **Persona**: Carlos Drummond de Andrade
|
| 9 |
-
- **Economia**: ~40-50% menor custo por requisição
|
| 10 |
-
|
| 11 |
-
### 2. Comparação de Modelos
|
| 12 |
-
|
| 13 |
-
| Modelo | Custo | Qualidade | Tempo Resposta | Uso Recomendado |
|
| 14 |
-
|--------|-------|-----------|----------------|-----------------|
|
| 15 |
-
| Sabiazinho-3 | 💰 | ⭐⭐⭐⭐ | 1-5s | Conversas gerais, saudações |
|
| 16 |
-
| Sabiá-3 | 💰💰💰 | ⭐⭐⭐⭐⭐ | 3-15s | Análises complexas |
|
| 17 |
-
|
| 18 |
-
### 3. Endpoints Disponíveis
|
| 19 |
-
|
| 20 |
-
```bash
|
| 21 |
-
# 1. Simple (Sabiá-3) - FUNCIONANDO 100%
|
| 22 |
-
POST /api/v1/chat/simple
|
| 23 |
-
|
| 24 |
-
# 2. Stable (Multi-fallback) - NOVO
|
| 25 |
-
POST /api/v1/chat/stable
|
| 26 |
-
|
| 27 |
-
# 3. Optimized (Sabiazinho-3 + Drummond) - NOVO
|
| 28 |
-
POST /api/v1/chat/optimized
|
| 29 |
-
```
|
| 30 |
-
|
| 31 |
-
## Integração Frontend - Versão Otimizada
|
| 32 |
-
|
| 33 |
-
### Serviço de Chat Atualizado
|
| 34 |
-
|
| 35 |
-
```typescript
|
| 36 |
-
// services/chatService.ts
|
| 37 |
-
export interface ChatEndpoint {
|
| 38 |
-
url: string;
|
| 39 |
-
name: string;
|
| 40 |
-
priority: number;
|
| 41 |
-
model: string;
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
export class ChatService {
|
| 45 |
-
private readonly API_URL = process.env.NEXT_PUBLIC_API_URL
|
| 46 |
-
|
| 47 |
-
private endpoints: ChatEndpoint[] = [
|
| 48 |
-
{
|
| 49 |
-
url: '/api/v1/chat/optimized',
|
| 50 |
-
name: 'Optimized (Sabiazinho)',
|
| 51 |
-
priority: 1,
|
| 52 |
-
model: 'sabiazinho-3'
|
| 53 |
-
},
|
| 54 |
-
{
|
| 55 |
-
url: '/api/v1/chat/simple',
|
| 56 |
-
name: 'Simple (Sabiá-3)',
|
| 57 |
-
priority: 2,
|
| 58 |
-
model: 'sabia-3'
|
| 59 |
-
},
|
| 60 |
-
{
|
| 61 |
-
url: '/api/v1/chat/stable',
|
| 62 |
-
name: 'Stable (Fallback)',
|
| 63 |
-
priority: 3,
|
| 64 |
-
model: 'mixed'
|
| 65 |
-
}
|
| 66 |
-
]
|
| 67 |
-
|
| 68 |
-
async sendMessage(
|
| 69 |
-
message: string,
|
| 70 |
-
options?: {
|
| 71 |
-
preferredModel?: 'economic' | 'quality';
|
| 72 |
-
useDrummond?: boolean;
|
| 73 |
-
}
|
| 74 |
-
): Promise<ChatResponse> {
|
| 75 |
-
const sessionId = `session_${Date.now()}`
|
| 76 |
-
|
| 77 |
-
// Select endpoint based on preference
|
| 78 |
-
let selectedEndpoints = [...this.endpoints]
|
| 79 |
-
|
| 80 |
-
if (options?.preferredModel === 'economic') {
|
| 81 |
-
// Prioritize Sabiazinho
|
| 82 |
-
selectedEndpoints.sort((a, b) =>
|
| 83 |
-
a.model === 'sabiazinho-3' ? -1 : 1
|
| 84 |
-
)
|
| 85 |
-
} else if (options?.preferredModel === 'quality') {
|
| 86 |
-
// Prioritize Sabiá-3
|
| 87 |
-
selectedEndpoints.sort((a, b) =>
|
| 88 |
-
a.model === 'sabia-3' ? -1 : 1
|
| 89 |
-
)
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
// Try endpoints in order
|
| 93 |
-
for (const endpoint of selectedEndpoints) {
|
| 94 |
-
try {
|
| 95 |
-
const body: any = { message, session_id: sessionId }
|
| 96 |
-
|
| 97 |
-
// Add Drummond flag for optimized endpoint
|
| 98 |
-
if (endpoint.url.includes('optimized')) {
|
| 99 |
-
body.use_drummond = options?.useDrummond ?? true
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
const response = await fetch(`${this.API_URL}${endpoint.url}`, {
|
| 103 |
-
method: 'POST',
|
| 104 |
-
headers: { 'Content-Type': 'application/json' },
|
| 105 |
-
body: JSON.stringify(body)
|
| 106 |
-
})
|
| 107 |
-
|
| 108 |
-
if (response.ok) {
|
| 109 |
-
const data = await response.json()
|
| 110 |
-
console.log(`✅ Success with ${endpoint.name}`)
|
| 111 |
-
return data
|
| 112 |
-
}
|
| 113 |
-
} catch (error) {
|
| 114 |
-
console.warn(`Failed ${endpoint.name}:`, error)
|
| 115 |
-
}
|
| 116 |
-
}
|
| 117 |
-
|
| 118 |
-
// Ultimate fallback
|
| 119 |
-
return {
|
| 120 |
-
message: 'Desculpe, estou temporariamente indisponível.',
|
| 121 |
-
session_id: sessionId,
|
| 122 |
-
agent_name: 'Sistema',
|
| 123 |
-
agent_id: 'system',
|
| 124 |
-
confidence: 0,
|
| 125 |
-
metadata: { fallback: true }
|
| 126 |
-
}
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
// Analyze message to decide best model
|
| 130 |
-
analyzeComplexity(message: string): 'simple' | 'complex' {
|
| 131 |
-
const complexKeywords = [
|
| 132 |
-
'analise', 'investigue', 'compare', 'tendência',
|
| 133 |
-
'padrão', 'anomalia', 'detalhe', 'relatório'
|
| 134 |
-
]
|
| 135 |
-
|
| 136 |
-
const hasComplexKeyword = complexKeywords.some(
|
| 137 |
-
keyword => message.toLowerCase().includes(keyword)
|
| 138 |
-
)
|
| 139 |
-
|
| 140 |
-
return hasComplexKeyword || message.length > 100
|
| 141 |
-
? 'complex'
|
| 142 |
-
: 'simple'
|
| 143 |
-
}
|
| 144 |
-
}
|
| 145 |
-
```
|
| 146 |
-
|
| 147 |
-
### Componente Inteligente
|
| 148 |
-
|
| 149 |
-
```tsx
|
| 150 |
-
// components/SmartChat.tsx
|
| 151 |
-
export function SmartChat() {
|
| 152 |
-
const [messages, setMessages] = useState<Message[]>([])
|
| 153 |
-
const [modelPreference, setModelPreference] = useState<'auto' | 'economic' | 'quality'>('auto')
|
| 154 |
-
const chatService = new ChatService()
|
| 155 |
-
|
| 156 |
-
const handleSendMessage = async (text: string) => {
|
| 157 |
-
// Add user message
|
| 158 |
-
const userMessage = createUserMessage(text)
|
| 159 |
-
setMessages(prev => [...prev, userMessage])
|
| 160 |
-
|
| 161 |
-
// Analyze complexity for auto mode
|
| 162 |
-
let preference: 'economic' | 'quality' | undefined
|
| 163 |
-
|
| 164 |
-
if (modelPreference === 'auto') {
|
| 165 |
-
const complexity = chatService.analyzeComplexity(text)
|
| 166 |
-
preference = complexity === 'simple' ? 'economic' : 'quality'
|
| 167 |
-
} else if (modelPreference !== 'auto') {
|
| 168 |
-
preference = modelPreference
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
// Send with appropriate model
|
| 172 |
-
const response = await chatService.sendMessage(text, {
|
| 173 |
-
preferredModel: preference,
|
| 174 |
-
useDrummond: true // Enable cultural persona
|
| 175 |
-
})
|
| 176 |
-
|
| 177 |
-
// Add response
|
| 178 |
-
const assistantMessage = {
|
| 179 |
-
...createAssistantMessage(response),
|
| 180 |
-
metadata: {
|
| 181 |
-
...response.metadata,
|
| 182 |
-
model_preference: preference,
|
| 183 |
-
actual_model: response.model_used
|
| 184 |
-
}
|
| 185 |
-
}
|
| 186 |
-
|
| 187 |
-
setMessages(prev => [...prev, assistantMessage])
|
| 188 |
-
|
| 189 |
-
// Log for monitoring
|
| 190 |
-
logChatMetrics({
|
| 191 |
-
model_used: response.model_used,
|
| 192 |
-
response_time: response.metadata?.response_time_ms,
|
| 193 |
-
tokens: response.metadata?.tokens_used,
|
| 194 |
-
success: true
|
| 195 |
-
})
|
| 196 |
-
}
|
| 197 |
-
|
| 198 |
-
return (
|
| 199 |
-
<div className="smart-chat">
|
| 200 |
-
{/* Model preference selector */}
|
| 201 |
-
<div className="model-selector">
|
| 202 |
-
<label>Modo:</label>
|
| 203 |
-
<select
|
| 204 |
-
value={modelPreference}
|
| 205 |
-
onChange={(e) => setModelPreference(e.target.value as any)}
|
| 206 |
-
>
|
| 207 |
-
<option value="auto">Automático</option>
|
| 208 |
-
<option value="economic">Econômico (Sabiazinho)</option>
|
| 209 |
-
<option value="quality">Qualidade (Sabiá-3)</option>
|
| 210 |
-
</select>
|
| 211 |
-
</div>
|
| 212 |
-
|
| 213 |
-
{/* Chat messages */}
|
| 214 |
-
<MessageList messages={messages} />
|
| 215 |
-
|
| 216 |
-
{/* Input */}
|
| 217 |
-
<ChatInput onSend={handleSendMessage} />
|
| 218 |
-
|
| 219 |
-
{/* Status indicator */}
|
| 220 |
-
<ChatStatus
|
| 221 |
-
lastModel={messages[messages.length - 1]?.metadata?.actual_model}
|
| 222 |
-
preference={modelPreference}
|
| 223 |
-
/>
|
| 224 |
-
</div>
|
| 225 |
-
)
|
| 226 |
-
}
|
| 227 |
-
```
|
| 228 |
-
|
| 229 |
-
## Otimizações de Custo
|
| 230 |
-
|
| 231 |
-
### 1. Cache Inteligente
|
| 232 |
-
```typescript
|
| 233 |
-
class CachedChatService extends ChatService {
|
| 234 |
-
private cache = new Map<string, CachedResponse>()
|
| 235 |
-
|
| 236 |
-
async sendMessage(message: string, options?: any) {
|
| 237 |
-
// Check cache for common questions
|
| 238 |
-
const cacheKey = this.normalizeMessage(message)
|
| 239 |
-
const cached = this.cache.get(cacheKey)
|
| 240 |
-
|
| 241 |
-
if (cached && !this.isExpired(cached)) {
|
| 242 |
-
return {
|
| 243 |
-
...cached.response,
|
| 244 |
-
metadata: {
|
| 245 |
-
...cached.response.metadata,
|
| 246 |
-
from_cache: true
|
| 247 |
-
}
|
| 248 |
-
}
|
| 249 |
-
}
|
| 250 |
-
|
| 251 |
-
// Get fresh response
|
| 252 |
-
const response = await super.sendMessage(message, options)
|
| 253 |
-
|
| 254 |
-
// Cache if successful
|
| 255 |
-
if (response.confidence > 0.8) {
|
| 256 |
-
this.cache.set(cacheKey, {
|
| 257 |
-
response,
|
| 258 |
-
timestamp: Date.now()
|
| 259 |
-
})
|
| 260 |
-
}
|
| 261 |
-
|
| 262 |
-
return response
|
| 263 |
-
}
|
| 264 |
-
}
|
| 265 |
-
```
|
| 266 |
-
|
| 267 |
-
### 2. Batching de Requisições
|
| 268 |
-
```typescript
|
| 269 |
-
class BatchedChatService extends ChatService {
|
| 270 |
-
private queue: QueuedMessage[] = []
|
| 271 |
-
private timer: NodeJS.Timeout | null = null
|
| 272 |
-
|
| 273 |
-
async sendMessage(message: string, options?: any) {
|
| 274 |
-
return new Promise((resolve) => {
|
| 275 |
-
this.queue.push({ message, options, resolve })
|
| 276 |
-
|
| 277 |
-
if (!this.timer) {
|
| 278 |
-
this.timer = setTimeout(() => this.processBatch(), 100)
|
| 279 |
-
}
|
| 280 |
-
})
|
| 281 |
-
}
|
| 282 |
-
|
| 283 |
-
private async processBatch() {
|
| 284 |
-
const batch = this.queue.splice(0, 5) // Max 5 per batch
|
| 285 |
-
|
| 286 |
-
// Send all at once (if API supports)
|
| 287 |
-
const responses = await this.sendBatch(batch)
|
| 288 |
-
|
| 289 |
-
// Resolve individual promises
|
| 290 |
-
batch.forEach((item, index) => {
|
| 291 |
-
item.resolve(responses[index])
|
| 292 |
-
})
|
| 293 |
-
|
| 294 |
-
this.timer = null
|
| 295 |
-
}
|
| 296 |
-
}
|
| 297 |
-
```
|
| 298 |
-
|
| 299 |
-
## Métricas e Monitoramento
|
| 300 |
-
|
| 301 |
-
```typescript
|
| 302 |
-
// utils/chatMetrics.ts
|
| 303 |
-
export class ChatMetricsCollector {
|
| 304 |
-
private metrics = {
|
| 305 |
-
totalRequests: 0,
|
| 306 |
-
modelUsage: new Map<string, number>(),
|
| 307 |
-
avgResponseTime: 0,
|
| 308 |
-
totalTokens: 0,
|
| 309 |
-
errorRate: 0,
|
| 310 |
-
cacheHitRate: 0
|
| 311 |
-
}
|
| 312 |
-
|
| 313 |
-
recordMetric(data: ChatMetric) {
|
| 314 |
-
this.metrics.totalRequests++
|
| 315 |
-
|
| 316 |
-
// Track model usage
|
| 317 |
-
const model = data.model_used || 'unknown'
|
| 318 |
-
this.metrics.modelUsage.set(
|
| 319 |
-
model,
|
| 320 |
-
(this.metrics.modelUsage.get(model) || 0) + 1
|
| 321 |
-
)
|
| 322 |
-
|
| 323 |
-
// Update averages
|
| 324 |
-
this.updateAverages(data)
|
| 325 |
-
|
| 326 |
-
// Send to analytics (optional)
|
| 327 |
-
if (window.gtag) {
|
| 328 |
-
window.gtag('event', 'chat_interaction', {
|
| 329 |
-
model_used: model,
|
| 330 |
-
response_time: data.response_time,
|
| 331 |
-
success: !data.error
|
| 332 |
-
})
|
| 333 |
-
}
|
| 334 |
-
}
|
| 335 |
-
|
| 336 |
-
getCostEstimate(): number {
|
| 337 |
-
const sabiazinhoCost = 0.001 // per request
|
| 338 |
-
const sabia3Cost = 0.003 // per request
|
| 339 |
-
|
| 340 |
-
const sabiazinhoCount = this.metrics.modelUsage.get('sabiazinho-3') || 0
|
| 341 |
-
const sabia3Count = this.metrics.modelUsage.get('sabia-3') || 0
|
| 342 |
-
|
| 343 |
-
return (sabiazinhoCount * sabiazinhoCost) + (sabia3Count * sabia3Cost)
|
| 344 |
-
}
|
| 345 |
-
|
| 346 |
-
getReport() {
|
| 347 |
-
return {
|
| 348 |
-
...this.metrics,
|
| 349 |
-
estimatedCost: this.getCostEstimate(),
|
| 350 |
-
modelDistribution: Object.fromEntries(this.metrics.modelUsage)
|
| 351 |
-
}
|
| 352 |
-
}
|
| 353 |
-
}
|
| 354 |
-
```
|
| 355 |
-
|
| 356 |
-
## Recomendações de Uso
|
| 357 |
-
|
| 358 |
-
### Para o Frontend:
|
| 359 |
-
1. **Perguntas Simples/Saudações**: Use Sabiazinho (economic mode)
|
| 360 |
-
2. **Análises Complexas**: Use Sabiá-3 (quality mode)
|
| 361 |
-
3. **Auto Mode**: Deixa o sistema decidir baseado na complexidade
|
| 362 |
-
|
| 363 |
-
### Economia Estimada:
|
| 364 |
-
- Conversas simples: 40-50% economia usando Sabiazinho
|
| 365 |
-
- Mix típico (70% simples, 30% complexo): ~35% economia total
|
| 366 |
-
- Com cache: Adicional 10-20% economia
|
| 367 |
-
|
| 368 |
-
### Próximos Passos:
|
| 369 |
-
1. Implementar cache para perguntas frequentes
|
| 370 |
-
2. Adicionar análise de sentimento para ajustar tom
|
| 371 |
-
3. Criar dashboards de custo em tempo real
|
| 372 |
-
4. A/B testing entre modelos
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app_simple.py
DELETED
|
@@ -1,706 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Cidadão.AI Backend - Expanded Version with Multiple Data Sources
|
| 4 |
-
Supports: Contracts, Servants, Expenses, Biddings, and more
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import logging
|
| 9 |
-
import os
|
| 10 |
-
import sys
|
| 11 |
-
import time
|
| 12 |
-
import traceback
|
| 13 |
-
import hashlib
|
| 14 |
-
from contextlib import asynccontextmanager
|
| 15 |
-
from typing import Any, Dict, List, Optional, Union
|
| 16 |
-
from datetime import datetime, timedelta
|
| 17 |
-
from enum import Enum
|
| 18 |
-
|
| 19 |
-
import uvicorn
|
| 20 |
-
from fastapi import FastAPI, HTTPException, status, Query
|
| 21 |
-
from fastapi.middleware.cors import CORSMiddleware
|
| 22 |
-
from fastapi.responses import JSONResponse
|
| 23 |
-
from pydantic import BaseModel, Field
|
| 24 |
-
from prometheus_client import Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST
|
| 25 |
-
|
| 26 |
-
# Configure logging
|
| 27 |
-
logging.basicConfig(
|
| 28 |
-
level=logging.INFO,
|
| 29 |
-
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
| 30 |
-
)
|
| 31 |
-
logger = logging.getLogger(__name__)
|
| 32 |
-
|
| 33 |
-
# ==================== DATA MODELS ====================
|
| 34 |
-
|
| 35 |
-
class DataSourceType(str, Enum):
|
| 36 |
-
"""Types of data sources available."""
|
| 37 |
-
CONTRACTS = "contratos"
|
| 38 |
-
SERVANTS = "servidores"
|
| 39 |
-
EXPENSES = "despesas"
|
| 40 |
-
BIDDINGS = "licitacoes"
|
| 41 |
-
AGREEMENTS = "convenios"
|
| 42 |
-
SANCTIONS = "empresas-sancionadas"
|
| 43 |
-
|
| 44 |
-
class HealthResponse(BaseModel):
|
| 45 |
-
"""Health check response model."""
|
| 46 |
-
status: str = "healthy"
|
| 47 |
-
version: str = "2.0.0"
|
| 48 |
-
agents: Dict[str, str] = Field(default_factory=lambda: {"zumbi": "active"})
|
| 49 |
-
data_sources: List[str] = Field(default_factory=lambda: [e.value for e in DataSourceType])
|
| 50 |
-
uptime: str = "operational"
|
| 51 |
-
|
| 52 |
-
class UniversalSearchRequest(BaseModel):
|
| 53 |
-
"""Universal search request for any data type."""
|
| 54 |
-
query: str = Field(..., description="Search query")
|
| 55 |
-
data_source: DataSourceType = Field(..., description="Type of data to search")
|
| 56 |
-
filters: Dict[str, Any] = Field(default_factory=dict, description="Additional filters")
|
| 57 |
-
max_results: int = Field(default=100, ge=1, le=500, description="Maximum results")
|
| 58 |
-
|
| 59 |
-
class ServantData(BaseModel):
|
| 60 |
-
"""Servant/Employee data model."""
|
| 61 |
-
nome: str
|
| 62 |
-
cpf_masked: str
|
| 63 |
-
matricula: str
|
| 64 |
-
orgao: str
|
| 65 |
-
cargo: str
|
| 66 |
-
funcao: Optional[str] = None
|
| 67 |
-
remuneracao: Dict[str, float]
|
| 68 |
-
mes_ano_referencia: str
|
| 69 |
-
|
| 70 |
-
class ContractData(BaseModel):
|
| 71 |
-
"""Contract data model."""
|
| 72 |
-
id: str
|
| 73 |
-
numero: str
|
| 74 |
-
objeto: str
|
| 75 |
-
valor: float
|
| 76 |
-
fornecedor: Dict[str, str]
|
| 77 |
-
orgao: str
|
| 78 |
-
data_assinatura: str
|
| 79 |
-
vigencia: Dict[str, str]
|
| 80 |
-
modalidade: Optional[str] = None
|
| 81 |
-
|
| 82 |
-
class ExpenseData(BaseModel):
|
| 83 |
-
"""Expense data model."""
|
| 84 |
-
id: str
|
| 85 |
-
descricao: str
|
| 86 |
-
valor: float
|
| 87 |
-
favorecido: Dict[str, str]
|
| 88 |
-
orgao: str
|
| 89 |
-
data: str
|
| 90 |
-
programa: Optional[str] = None
|
| 91 |
-
acao: Optional[str] = None
|
| 92 |
-
|
| 93 |
-
class UniversalSearchResponse(BaseModel):
|
| 94 |
-
"""Universal search response."""
|
| 95 |
-
status: str
|
| 96 |
-
data_source: str
|
| 97 |
-
query: str
|
| 98 |
-
results: List[Union[ServantData, ContractData, ExpenseData, Dict[str, Any]]]
|
| 99 |
-
total_found: int
|
| 100 |
-
anomalies_detected: int
|
| 101 |
-
confidence_score: float
|
| 102 |
-
processing_time_ms: int
|
| 103 |
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
| 104 |
-
|
| 105 |
-
# ==================== CACHE ====================
|
| 106 |
-
|
| 107 |
-
class SimpleCache:
|
| 108 |
-
"""In-memory cache for API responses with TTL."""
|
| 109 |
-
|
| 110 |
-
def __init__(self):
|
| 111 |
-
self._cache: Dict[str, Dict] = {}
|
| 112 |
-
self._ttl_cache: Dict[str, datetime] = {}
|
| 113 |
-
self.default_ttl = 3600 # 1 hour
|
| 114 |
-
|
| 115 |
-
def _generate_key(self, **kwargs) -> str:
|
| 116 |
-
"""Generate cache key from parameters."""
|
| 117 |
-
key_string = "&".join([f"{k}={v}" for k, v in sorted(kwargs.items())])
|
| 118 |
-
return hashlib.md5(key_string.encode()).hexdigest()
|
| 119 |
-
|
| 120 |
-
def get(self, **kwargs) -> Optional[Dict]:
|
| 121 |
-
"""Get cached value if not expired."""
|
| 122 |
-
key = self._generate_key(**kwargs)
|
| 123 |
-
|
| 124 |
-
if key not in self._cache:
|
| 125 |
-
return None
|
| 126 |
-
|
| 127 |
-
if key in self._ttl_cache:
|
| 128 |
-
if datetime.now() > self._ttl_cache[key]:
|
| 129 |
-
del self._cache[key]
|
| 130 |
-
del self._ttl_cache[key]
|
| 131 |
-
return None
|
| 132 |
-
|
| 133 |
-
return self._cache[key]
|
| 134 |
-
|
| 135 |
-
def set(self, value: Dict, ttl_seconds: int = None, **kwargs) -> None:
|
| 136 |
-
"""Set cached value with TTL."""
|
| 137 |
-
key = self._generate_key(**kwargs)
|
| 138 |
-
self._cache[key] = value
|
| 139 |
-
|
| 140 |
-
ttl = ttl_seconds or self.default_ttl
|
| 141 |
-
self._ttl_cache[key] = datetime.now() + timedelta(seconds=ttl)
|
| 142 |
-
|
| 143 |
-
# Global cache instance
|
| 144 |
-
api_cache = SimpleCache()
|
| 145 |
-
|
| 146 |
-
# ==================== ENHANCED ZUMBI AGENT ====================
|
| 147 |
-
|
| 148 |
-
class EnhancedZumbiAgent:
|
| 149 |
-
"""Enhanced Zumbi agent that can investigate multiple data sources."""
|
| 150 |
-
|
| 151 |
-
def __init__(self):
|
| 152 |
-
self.name = "Zumbi dos Palmares"
|
| 153 |
-
self.role = "Universal Investigator"
|
| 154 |
-
self.specialty = "Multi-source anomaly detection"
|
| 155 |
-
logger.info(f"🏹 {self.name} - Enhanced {self.role} initialized")
|
| 156 |
-
|
| 157 |
-
async def investigate_universal(self, request: UniversalSearchRequest) -> UniversalSearchResponse:
|
| 158 |
-
"""Investigate any data source."""
|
| 159 |
-
import os
|
| 160 |
-
import numpy as np
|
| 161 |
-
from collections import defaultdict
|
| 162 |
-
start_time = time.time()
|
| 163 |
-
|
| 164 |
-
try:
|
| 165 |
-
# Get API key
|
| 166 |
-
api_key = os.getenv("TRANSPARENCY_API_KEY")
|
| 167 |
-
if not api_key:
|
| 168 |
-
logger.warning("⚠️ No API key, using demo data")
|
| 169 |
-
return await self._get_demo_data(request, start_time)
|
| 170 |
-
|
| 171 |
-
# Route to appropriate handler
|
| 172 |
-
if request.data_source == DataSourceType.SERVANTS:
|
| 173 |
-
return await self._search_servants(request, api_key, start_time)
|
| 174 |
-
elif request.data_source == DataSourceType.CONTRACTS:
|
| 175 |
-
return await self._search_contracts(request, api_key, start_time)
|
| 176 |
-
elif request.data_source == DataSourceType.EXPENSES:
|
| 177 |
-
return await self._search_expenses(request, api_key, start_time)
|
| 178 |
-
elif request.data_source == DataSourceType.BIDDINGS:
|
| 179 |
-
return await self._search_biddings(request, api_key, start_time)
|
| 180 |
-
else:
|
| 181 |
-
return await self._search_generic(request, api_key, start_time)
|
| 182 |
-
|
| 183 |
-
except Exception as e:
|
| 184 |
-
logger.error(f"Investigation error: {str(e)}")
|
| 185 |
-
return UniversalSearchResponse(
|
| 186 |
-
status="error",
|
| 187 |
-
data_source=request.data_source.value,
|
| 188 |
-
query=request.query,
|
| 189 |
-
results=[],
|
| 190 |
-
total_found=0,
|
| 191 |
-
anomalies_detected=0,
|
| 192 |
-
confidence_score=0.0,
|
| 193 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 194 |
-
metadata={"error": str(e)}
|
| 195 |
-
)
|
| 196 |
-
|
| 197 |
-
async def _search_servants(self, request: UniversalSearchRequest, api_key: str, start_time: float) -> UniversalSearchResponse:
|
| 198 |
-
"""Search for government servants."""
|
| 199 |
-
import httpx
|
| 200 |
-
|
| 201 |
-
# Check cache first
|
| 202 |
-
cache_key = f"servants_{request.query}_{request.max_results}"
|
| 203 |
-
cached = api_cache.get(source=cache_key)
|
| 204 |
-
if cached:
|
| 205 |
-
logger.info("📦 Using cached servants data")
|
| 206 |
-
return cached
|
| 207 |
-
|
| 208 |
-
results = []
|
| 209 |
-
anomalies = 0
|
| 210 |
-
|
| 211 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 212 |
-
url = "https://api.portaldatransparencia.gov.br/api-de-dados/servidores"
|
| 213 |
-
headers = {
|
| 214 |
-
"chave-api-dados": api_key,
|
| 215 |
-
"Accept": "application/json"
|
| 216 |
-
}
|
| 217 |
-
params = {
|
| 218 |
-
"nome": request.query.upper(),
|
| 219 |
-
"pagina": 1,
|
| 220 |
-
"tamanhoPagina": min(request.max_results, 50)
|
| 221 |
-
}
|
| 222 |
-
|
| 223 |
-
# Add filters from request
|
| 224 |
-
if "orgao" in request.filters:
|
| 225 |
-
params["orgao"] = request.filters["orgao"]
|
| 226 |
-
if "funcao" in request.filters:
|
| 227 |
-
params["funcao"] = request.filters["funcao"]
|
| 228 |
-
|
| 229 |
-
response = await client.get(url, headers=headers, params=params)
|
| 230 |
-
|
| 231 |
-
if response.status_code == 200:
|
| 232 |
-
data = response.json()
|
| 233 |
-
|
| 234 |
-
# Process servants
|
| 235 |
-
for item in data:
|
| 236 |
-
servant_info = item.get("servidor", {})
|
| 237 |
-
org_info = item.get("unidadeOrganizacional", {})
|
| 238 |
-
|
| 239 |
-
# Extract salary info
|
| 240 |
-
salary = item.get("remuneracaoBasicaBruta", 0)
|
| 241 |
-
total = item.get("remuneracaoAposDeducoes", 0)
|
| 242 |
-
|
| 243 |
-
# Detect anomalies (e.g., very high salaries)
|
| 244 |
-
if salary > 40000: # Above R$ 40k is unusual
|
| 245 |
-
anomalies += 1
|
| 246 |
-
|
| 247 |
-
servant = ServantData(
|
| 248 |
-
nome=servant_info.get("nome", "N/A"),
|
| 249 |
-
cpf_masked=servant_info.get("cpf", "***.***.***-**"),
|
| 250 |
-
matricula=servant_info.get("matricula", "N/A"),
|
| 251 |
-
orgao=org_info.get("nomeUnidade", "N/A"),
|
| 252 |
-
cargo=item.get("cargo", {}).get("descricao", "N/A"),
|
| 253 |
-
funcao=item.get("funcao", {}).get("descricao"),
|
| 254 |
-
remuneracao={
|
| 255 |
-
"basica": salary,
|
| 256 |
-
"total_liquido": total,
|
| 257 |
-
"gratificacoes": item.get("gratificacoes", 0),
|
| 258 |
-
"auxilios": item.get("auxilios", 0)
|
| 259 |
-
},
|
| 260 |
-
mes_ano_referencia=f"{item.get('mesReferencia', 'N/A')}/{item.get('anoReferencia', 'N/A')}"
|
| 261 |
-
)
|
| 262 |
-
results.append(servant.dict())
|
| 263 |
-
|
| 264 |
-
response_data = UniversalSearchResponse(
|
| 265 |
-
status="success",
|
| 266 |
-
data_source=request.data_source.value,
|
| 267 |
-
query=request.query,
|
| 268 |
-
results=results,
|
| 269 |
-
total_found=len(results),
|
| 270 |
-
anomalies_detected=anomalies,
|
| 271 |
-
confidence_score=0.95,
|
| 272 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 273 |
-
metadata={"source": "real_api", "anomaly_threshold": 40000}
|
| 274 |
-
)
|
| 275 |
-
|
| 276 |
-
# Cache the response
|
| 277 |
-
api_cache.set(response_data.dict(), source=cache_key)
|
| 278 |
-
|
| 279 |
-
return response_data
|
| 280 |
-
else:
|
| 281 |
-
raise HTTPException(status_code=response.status_code, detail="API request failed")
|
| 282 |
-
|
| 283 |
-
async def _search_contracts(self, request: UniversalSearchRequest, api_key: str, start_time: float) -> UniversalSearchResponse:
|
| 284 |
-
"""Search for contracts with anomaly detection."""
|
| 285 |
-
import httpx
|
| 286 |
-
import numpy as np
|
| 287 |
-
|
| 288 |
-
results = []
|
| 289 |
-
anomalies = 0
|
| 290 |
-
|
| 291 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 292 |
-
# Search multiple organizations
|
| 293 |
-
org_codes = request.filters.get("orgaos", ["26000", "25000", "44000"])
|
| 294 |
-
|
| 295 |
-
all_contracts = []
|
| 296 |
-
for org_code in org_codes[:3]: # Limit to 3 orgs
|
| 297 |
-
url = "https://api.portaldatransparencia.gov.br/api-de-dados/contratos"
|
| 298 |
-
headers = {
|
| 299 |
-
"chave-api-dados": api_key,
|
| 300 |
-
"Accept": "application/json"
|
| 301 |
-
}
|
| 302 |
-
params = {
|
| 303 |
-
"codigoOrgao": org_code,
|
| 304 |
-
"ano": request.filters.get("ano", 2024),
|
| 305 |
-
"tamanhoPagina": 50
|
| 306 |
-
}
|
| 307 |
-
|
| 308 |
-
# Add search term if provided
|
| 309 |
-
if request.query and request.query.lower() != "todos":
|
| 310 |
-
params["descricao"] = request.query
|
| 311 |
-
|
| 312 |
-
response = await client.get(url, headers=headers, params=params)
|
| 313 |
-
|
| 314 |
-
if response.status_code == 200:
|
| 315 |
-
contracts = response.json()
|
| 316 |
-
all_contracts.extend(contracts)
|
| 317 |
-
|
| 318 |
-
# Analyze contracts for anomalies
|
| 319 |
-
if all_contracts:
|
| 320 |
-
values = [c.get("valorInicial", 0) for c in all_contracts if c.get("valorInicial", 0) > 0]
|
| 321 |
-
|
| 322 |
-
if len(values) > 3:
|
| 323 |
-
mean_val = np.mean(values)
|
| 324 |
-
std_val = np.std(values)
|
| 325 |
-
|
| 326 |
-
for contract in all_contracts[:request.max_results]:
|
| 327 |
-
valor = contract.get("valorInicial", 0)
|
| 328 |
-
z_score = abs((valor - mean_val) / std_val) if std_val > 0 else 0
|
| 329 |
-
|
| 330 |
-
# Flag as anomaly if z-score > 1.5
|
| 331 |
-
is_anomaly = z_score > 1.5
|
| 332 |
-
if is_anomaly:
|
| 333 |
-
anomalies += 1
|
| 334 |
-
|
| 335 |
-
contract_data = {
|
| 336 |
-
"id": contract.get("id", "N/A"),
|
| 337 |
-
"numero": contract.get("numero", "N/A"),
|
| 338 |
-
"objeto": contract.get("objeto", "N/A")[:200],
|
| 339 |
-
"valor": valor,
|
| 340 |
-
"fornecedor": {
|
| 341 |
-
"nome": contract.get("nomeFornecedor", "N/A"),
|
| 342 |
-
"cnpj": contract.get("cnpjFornecedor", "N/A")
|
| 343 |
-
},
|
| 344 |
-
"orgao": contract.get("nomeOrgao", org_code),
|
| 345 |
-
"data_assinatura": contract.get("dataAssinatura", "N/A"),
|
| 346 |
-
"vigencia": {
|
| 347 |
-
"inicio": contract.get("dataInicioVigencia", "N/A"),
|
| 348 |
-
"fim": contract.get("dataFimVigencia", "N/A")
|
| 349 |
-
},
|
| 350 |
-
"modalidade": contract.get("modalidadeCompra", "N/A"),
|
| 351 |
-
"_anomaly": is_anomaly,
|
| 352 |
-
"_z_score": z_score
|
| 353 |
-
}
|
| 354 |
-
results.append(contract_data)
|
| 355 |
-
|
| 356 |
-
return UniversalSearchResponse(
|
| 357 |
-
status="success",
|
| 358 |
-
data_source=request.data_source.value,
|
| 359 |
-
query=request.query,
|
| 360 |
-
results=results,
|
| 361 |
-
total_found=len(results),
|
| 362 |
-
anomalies_detected=anomalies,
|
| 363 |
-
confidence_score=0.87,
|
| 364 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 365 |
-
metadata={
|
| 366 |
-
"organizations_searched": org_codes[:3],
|
| 367 |
-
"anomaly_method": "z_score",
|
| 368 |
-
"threshold": 1.5
|
| 369 |
-
}
|
| 370 |
-
)
|
| 371 |
-
|
| 372 |
-
async def _search_expenses(self, request: UniversalSearchRequest, api_key: str, start_time: float) -> UniversalSearchResponse:
|
| 373 |
-
"""Search for government expenses."""
|
| 374 |
-
import httpx
|
| 375 |
-
|
| 376 |
-
results = []
|
| 377 |
-
anomalies = 0
|
| 378 |
-
|
| 379 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 380 |
-
url = "https://api.portaldatransparencia.gov.br/api-de-dados/despesas"
|
| 381 |
-
headers = {
|
| 382 |
-
"chave-api-dados": api_key,
|
| 383 |
-
"Accept": "application/json"
|
| 384 |
-
}
|
| 385 |
-
params = {
|
| 386 |
-
"ano": request.filters.get("ano", 2024),
|
| 387 |
-
"mes": request.filters.get("mes", 12),
|
| 388 |
-
"pagina": 1,
|
| 389 |
-
"tamanhoPagina": min(request.max_results, 50)
|
| 390 |
-
}
|
| 391 |
-
|
| 392 |
-
if "orgao" in request.filters:
|
| 393 |
-
params["orgao"] = request.filters["orgao"]
|
| 394 |
-
|
| 395 |
-
response = await client.get(url, headers=headers, params=params)
|
| 396 |
-
|
| 397 |
-
if response.status_code == 200:
|
| 398 |
-
data = response.json()
|
| 399 |
-
|
| 400 |
-
for expense in data:
|
| 401 |
-
valor = expense.get("valor", 0)
|
| 402 |
-
|
| 403 |
-
# Simple anomaly detection for high values
|
| 404 |
-
if valor > 1000000: # Above 1M
|
| 405 |
-
anomalies += 1
|
| 406 |
-
|
| 407 |
-
expense_data = ExpenseData(
|
| 408 |
-
id=expense.get("id", "N/A"),
|
| 409 |
-
descricao=expense.get("descricao", "N/A"),
|
| 410 |
-
valor=valor,
|
| 411 |
-
favorecido={
|
| 412 |
-
"nome": expense.get("nomeFavorecido", "N/A"),
|
| 413 |
-
"codigo": expense.get("codigoFavorecido", "N/A")
|
| 414 |
-
},
|
| 415 |
-
orgao=expense.get("nomeOrgao", "N/A"),
|
| 416 |
-
data=expense.get("data", "N/A"),
|
| 417 |
-
programa=expense.get("nomePrograma"),
|
| 418 |
-
acao=expense.get("nomeAcao")
|
| 419 |
-
)
|
| 420 |
-
results.append(expense_data.dict())
|
| 421 |
-
|
| 422 |
-
return UniversalSearchResponse(
|
| 423 |
-
status="success",
|
| 424 |
-
data_source=request.data_source.value,
|
| 425 |
-
query=request.query,
|
| 426 |
-
results=results,
|
| 427 |
-
total_found=len(results),
|
| 428 |
-
anomalies_detected=anomalies,
|
| 429 |
-
confidence_score=0.85,
|
| 430 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 431 |
-
metadata={"high_value_threshold": 1000000}
|
| 432 |
-
)
|
| 433 |
-
else:
|
| 434 |
-
raise HTTPException(status_code=response.status_code, detail="API request failed")
|
| 435 |
-
|
| 436 |
-
async def _search_biddings(self, request: UniversalSearchRequest, api_key: str, start_time: float) -> UniversalSearchResponse:
|
| 437 |
-
"""Search for biddings/licitações."""
|
| 438 |
-
# Implementation similar to contracts
|
| 439 |
-
# For brevity, returning a simplified response
|
| 440 |
-
return UniversalSearchResponse(
|
| 441 |
-
status="success",
|
| 442 |
-
data_source=request.data_source.value,
|
| 443 |
-
query=request.query,
|
| 444 |
-
results=[],
|
| 445 |
-
total_found=0,
|
| 446 |
-
anomalies_detected=0,
|
| 447 |
-
confidence_score=0.8,
|
| 448 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 449 |
-
metadata={"note": "Biddings endpoint to be implemented"}
|
| 450 |
-
)
|
| 451 |
-
|
| 452 |
-
async def _search_generic(self, request: UniversalSearchRequest, api_key: str, start_time: float) -> UniversalSearchResponse:
|
| 453 |
-
"""Generic search for other data types."""
|
| 454 |
-
return UniversalSearchResponse(
|
| 455 |
-
status="success",
|
| 456 |
-
data_source=request.data_source.value,
|
| 457 |
-
query=request.query,
|
| 458 |
-
results=[],
|
| 459 |
-
total_found=0,
|
| 460 |
-
anomalies_detected=0,
|
| 461 |
-
confidence_score=0.7,
|
| 462 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 463 |
-
metadata={"note": f"Generic handler for {request.data_source.value}"}
|
| 464 |
-
)
|
| 465 |
-
|
| 466 |
-
async def _get_demo_data(self, request: UniversalSearchRequest, start_time: float) -> UniversalSearchResponse:
|
| 467 |
-
"""Return demo data when no API key is available."""
|
| 468 |
-
demo_results = []
|
| 469 |
-
|
| 470 |
-
if request.data_source == DataSourceType.SERVANTS:
|
| 471 |
-
demo_results = [{
|
| 472 |
-
"nome": "MARIA DA SILVA",
|
| 473 |
-
"cpf_masked": "***.***.***-**",
|
| 474 |
-
"matricula": "1234567",
|
| 475 |
-
"orgao": "MINISTERIO DA SAUDE",
|
| 476 |
-
"cargo": "ANALISTA",
|
| 477 |
-
"funcao": "ANALISTA TECNICO",
|
| 478 |
-
"remuneracao": {
|
| 479 |
-
"basica": 8500.00,
|
| 480 |
-
"total_liquido": 9876.54,
|
| 481 |
-
"gratificacoes": 2000.00,
|
| 482 |
-
"auxilios": 458.00
|
| 483 |
-
},
|
| 484 |
-
"mes_ano_referencia": "12/2024"
|
| 485 |
-
}]
|
| 486 |
-
elif request.data_source == DataSourceType.CONTRACTS:
|
| 487 |
-
demo_results = [{
|
| 488 |
-
"id": "demo-001",
|
| 489 |
-
"numero": "2024/001",
|
| 490 |
-
"objeto": "Contrato demonstrativo para testes",
|
| 491 |
-
"valor": 150000.00,
|
| 492 |
-
"fornecedor": {
|
| 493 |
-
"nome": "EMPRESA DEMO LTDA",
|
| 494 |
-
"cnpj": "00.000.000/0001-00"
|
| 495 |
-
},
|
| 496 |
-
"orgao": "ORGAO DEMONSTRATIVO",
|
| 497 |
-
"data_assinatura": "01/01/2024",
|
| 498 |
-
"vigencia": {
|
| 499 |
-
"inicio": "01/01/2024",
|
| 500 |
-
"fim": "31/12/2024"
|
| 501 |
-
},
|
| 502 |
-
"modalidade": "Pregão Eletrônico",
|
| 503 |
-
"_anomaly": False,
|
| 504 |
-
"_z_score": 0.5
|
| 505 |
-
}]
|
| 506 |
-
|
| 507 |
-
return UniversalSearchResponse(
|
| 508 |
-
status="demo",
|
| 509 |
-
data_source=request.data_source.value,
|
| 510 |
-
query=request.query,
|
| 511 |
-
results=demo_results,
|
| 512 |
-
total_found=len(demo_results),
|
| 513 |
-
anomalies_detected=0,
|
| 514 |
-
confidence_score=0.5,
|
| 515 |
-
processing_time_ms=int((time.time() - start_time) * 1000),
|
| 516 |
-
metadata={"mode": "demo", "message": "Configure TRANSPARENCY_API_KEY for real data"}
|
| 517 |
-
)
|
| 518 |
-
|
| 519 |
-
# ==================== FASTAPI APP ====================
|
| 520 |
-
|
| 521 |
-
# Create agent instance
|
| 522 |
-
enhanced_zumbi = EnhancedZumbiAgent()
|
| 523 |
-
|
| 524 |
-
# Lifespan context manager
|
| 525 |
-
@asynccontextmanager
|
| 526 |
-
async def lifespan(app: FastAPI):
|
| 527 |
-
logger.info("🏛️ Cidadão.AI Enhanced Backend starting up...")
|
| 528 |
-
logger.info("🏹 Enhanced Zumbi agent ready for multi-source investigations")
|
| 529 |
-
yield
|
| 530 |
-
logger.info("👋 Cidadão.AI Enhanced Backend shutting down...")
|
| 531 |
-
|
| 532 |
-
# Create FastAPI app
|
| 533 |
-
app = FastAPI(
|
| 534 |
-
title="Cidadão.AI Enhanced Backend",
|
| 535 |
-
description="Multi-source government transparency analysis with AI",
|
| 536 |
-
version="2.0.0",
|
| 537 |
-
docs_url="/docs",
|
| 538 |
-
redoc_url="/redoc",
|
| 539 |
-
lifespan=lifespan
|
| 540 |
-
)
|
| 541 |
-
|
| 542 |
-
# Add CORS middleware
|
| 543 |
-
app.add_middleware(
|
| 544 |
-
CORSMiddleware,
|
| 545 |
-
allow_origins=["*"],
|
| 546 |
-
allow_credentials=True,
|
| 547 |
-
allow_methods=["*"],
|
| 548 |
-
allow_headers=["*"],
|
| 549 |
-
)
|
| 550 |
-
|
| 551 |
-
# Add compression middleware for better performance
|
| 552 |
-
from src.api.middleware.compression import add_compression_middleware
|
| 553 |
-
add_compression_middleware(
|
| 554 |
-
app,
|
| 555 |
-
minimum_size=1024, # Compress responses larger than 1KB
|
| 556 |
-
gzip_level=6, # Good balance of speed vs compression
|
| 557 |
-
brotli_quality=4, # Fast brotli compression
|
| 558 |
-
exclude_paths={"/health", "/metrics", "/health/metrics"}
|
| 559 |
-
)
|
| 560 |
-
|
| 561 |
-
# ==================== ENDPOINTS ====================
|
| 562 |
-
|
| 563 |
-
@app.get("/", response_model=HealthResponse)
|
| 564 |
-
async def root():
|
| 565 |
-
"""Root endpoint with system status."""
|
| 566 |
-
return HealthResponse()
|
| 567 |
-
|
| 568 |
-
@app.get("/health", response_model=HealthResponse)
|
| 569 |
-
async def health_check():
|
| 570 |
-
"""Health check endpoint."""
|
| 571 |
-
return HealthResponse()
|
| 572 |
-
|
| 573 |
-
@app.post("/api/investigate", response_model=UniversalSearchResponse)
|
| 574 |
-
async def investigate_universal(request: UniversalSearchRequest):
|
| 575 |
-
"""
|
| 576 |
-
Universal investigation endpoint for any data source.
|
| 577 |
-
|
| 578 |
-
Example queries:
|
| 579 |
-
- Servants: {"query": "João Silva", "data_source": "servidores"}
|
| 580 |
-
- Contracts: {"query": "informática", "data_source": "contratos"}
|
| 581 |
-
- Expenses: {"query": "todos", "data_source": "despesas", "filters": {"mes": 12}}
|
| 582 |
-
"""
|
| 583 |
-
try:
|
| 584 |
-
result = await enhanced_zumbi.investigate_universal(request)
|
| 585 |
-
return result
|
| 586 |
-
except Exception as e:
|
| 587 |
-
logger.error(f"Investigation error: {str(e)}")
|
| 588 |
-
raise HTTPException(
|
| 589 |
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
| 590 |
-
detail=f"Investigation failed: {str(e)}"
|
| 591 |
-
)
|
| 592 |
-
|
| 593 |
-
@app.get("/api/data-sources")
|
| 594 |
-
async def list_data_sources():
|
| 595 |
-
"""List all available data sources."""
|
| 596 |
-
return {
|
| 597 |
-
"sources": [
|
| 598 |
-
{
|
| 599 |
-
"id": ds.value,
|
| 600 |
-
"name": ds.name,
|
| 601 |
-
"description": {
|
| 602 |
-
DataSourceType.CONTRACTS: "Government contracts and procurements",
|
| 603 |
-
DataSourceType.SERVANTS: "Public servants and their salaries",
|
| 604 |
-
DataSourceType.EXPENSES: "Government expenses and payments",
|
| 605 |
-
DataSourceType.BIDDINGS: "Public biddings and auctions",
|
| 606 |
-
DataSourceType.AGREEMENTS: "Government agreements and partnerships",
|
| 607 |
-
DataSourceType.SANCTIONS: "Sanctioned companies"
|
| 608 |
-
}.get(ds, "Data source")
|
| 609 |
-
}
|
| 610 |
-
for ds in DataSourceType
|
| 611 |
-
]
|
| 612 |
-
}
|
| 613 |
-
|
| 614 |
-
@app.get("/api/search/servants")
|
| 615 |
-
async def search_servants_quick(
|
| 616 |
-
nome: str = Query(..., description="Nome do servidor"),
|
| 617 |
-
orgao: Optional[str] = Query(None, description="Código do órgão"),
|
| 618 |
-
limit: int = Query(10, ge=1, le=50, description="Limite de resultados")
|
| 619 |
-
):
|
| 620 |
-
"""Quick endpoint to search servants by name."""
|
| 621 |
-
request = UniversalSearchRequest(
|
| 622 |
-
query=nome,
|
| 623 |
-
data_source=DataSourceType.SERVANTS,
|
| 624 |
-
filters={"orgao": orgao} if orgao else {},
|
| 625 |
-
max_results=limit
|
| 626 |
-
)
|
| 627 |
-
return await investigate_universal(request)
|
| 628 |
-
|
| 629 |
-
@app.get("/api/search/contracts")
|
| 630 |
-
async def search_contracts_quick(
|
| 631 |
-
query: str = Query("todos", description="Termo de busca"),
|
| 632 |
-
orgao: Optional[str] = Query(None, description="Código do órgão"),
|
| 633 |
-
ano: int = Query(2024, description="Ano"),
|
| 634 |
-
limit: int = Query(50, ge=1, le=100, description="Limite de resultados")
|
| 635 |
-
):
|
| 636 |
-
"""Quick endpoint to search contracts."""
|
| 637 |
-
request = UniversalSearchRequest(
|
| 638 |
-
query=query,
|
| 639 |
-
data_source=DataSourceType.CONTRACTS,
|
| 640 |
-
filters={"orgaos": [orgao] if orgao else ["26000", "25000"], "ano": ano},
|
| 641 |
-
max_results=limit
|
| 642 |
-
)
|
| 643 |
-
return await investigate_universal(request)
|
| 644 |
-
|
| 645 |
-
@app.get("/api/cache/stats")
|
| 646 |
-
async def cache_stats():
|
| 647 |
-
"""Get cache statistics."""
|
| 648 |
-
return {
|
| 649 |
-
"cache_size": len(api_cache._cache),
|
| 650 |
-
"active_entries": len([k for k, v in api_cache._ttl_cache.items() if v > datetime.now()]),
|
| 651 |
-
"ttl_seconds": api_cache.default_ttl
|
| 652 |
-
}
|
| 653 |
-
|
| 654 |
-
# Debug endpoint for Drummond issue
|
| 655 |
-
@app.get("/debug/drummond-status")
|
| 656 |
-
async def debug_drummond_status():
|
| 657 |
-
"""Debug endpoint to check Drummond agent status."""
|
| 658 |
-
import traceback
|
| 659 |
-
|
| 660 |
-
result = {
|
| 661 |
-
"python_version": sys.version,
|
| 662 |
-
"checks": {}
|
| 663 |
-
}
|
| 664 |
-
|
| 665 |
-
# Check if we can import CommunicationAgent
|
| 666 |
-
try:
|
| 667 |
-
from src.agents.drummond import CommunicationAgent
|
| 668 |
-
abstract_methods = getattr(CommunicationAgent, '__abstractmethods__', set())
|
| 669 |
-
result["checks"]["import"] = {
|
| 670 |
-
"status": "success",
|
| 671 |
-
"abstract_methods": list(abstract_methods) if abstract_methods else "none",
|
| 672 |
-
"has_shutdown": hasattr(CommunicationAgent, 'shutdown'),
|
| 673 |
-
"has_initialize": hasattr(CommunicationAgent, 'initialize'),
|
| 674 |
-
"has_process": hasattr(CommunicationAgent, 'process')
|
| 675 |
-
}
|
| 676 |
-
|
| 677 |
-
# Try to instantiate
|
| 678 |
-
try:
|
| 679 |
-
agent = CommunicationAgent()
|
| 680 |
-
result["checks"]["instantiation"] = {"status": "success", "agent_name": agent.name}
|
| 681 |
-
except Exception as e:
|
| 682 |
-
result["checks"]["instantiation"] = {
|
| 683 |
-
"status": "failed",
|
| 684 |
-
"error": str(e),
|
| 685 |
-
"error_type": type(e).__name__
|
| 686 |
-
}
|
| 687 |
-
except Exception as e:
|
| 688 |
-
result["checks"]["import"] = {
|
| 689 |
-
"status": "failed",
|
| 690 |
-
"error": str(e),
|
| 691 |
-
"traceback": traceback.format_exc()[:500] # Limit traceback size
|
| 692 |
-
}
|
| 693 |
-
|
| 694 |
-
return result
|
| 695 |
-
|
| 696 |
-
if __name__ == "__main__":
|
| 697 |
-
port = int(os.getenv("PORT", 7860))
|
| 698 |
-
logger.info(f"🚀 Starting Enhanced Cidadão.AI Backend on port {port}")
|
| 699 |
-
# Disable host header validation for HuggingFace Spaces
|
| 700 |
-
uvicorn.run(
|
| 701 |
-
app,
|
| 702 |
-
host="0.0.0.0",
|
| 703 |
-
port=port,
|
| 704 |
-
forwarded_allow_ips="*", # Allow all proxy IPs
|
| 705 |
-
proxy_headers=True # Trust proxy headers
|
| 706 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug_drummond_import.py
DELETED
|
@@ -1,97 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Debug script to trace Drummond import issues.
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import sys
|
| 7 |
-
import traceback
|
| 8 |
-
|
| 9 |
-
def test_import_chain():
|
| 10 |
-
"""Test the import chain to find where the error occurs."""
|
| 11 |
-
|
| 12 |
-
print("=== DRUMMOND IMPORT DEBUG ===")
|
| 13 |
-
print(f"Python version: {sys.version}")
|
| 14 |
-
print(f"Python path: {sys.path}")
|
| 15 |
-
print()
|
| 16 |
-
|
| 17 |
-
# Test 1: Import BaseAgent
|
| 18 |
-
print("1. Testing BaseAgent import...")
|
| 19 |
-
try:
|
| 20 |
-
from src.agents.deodoro import BaseAgent
|
| 21 |
-
print(" ✓ BaseAgent imported successfully")
|
| 22 |
-
|
| 23 |
-
# Check if shutdown is abstract
|
| 24 |
-
import inspect
|
| 25 |
-
methods = inspect.getmembers(BaseAgent, predicate=inspect.ismethod)
|
| 26 |
-
for name, method in methods:
|
| 27 |
-
if name == 'shutdown':
|
| 28 |
-
print(f" - shutdown method found: {method}")
|
| 29 |
-
if hasattr(method, '__isabstractmethod__'):
|
| 30 |
-
print(f" - Is abstract: {method.__isabstractmethod__}")
|
| 31 |
-
except Exception as e:
|
| 32 |
-
print(f" ✗ Failed to import BaseAgent: {e}")
|
| 33 |
-
traceback.print_exc()
|
| 34 |
-
return
|
| 35 |
-
|
| 36 |
-
# Test 2: Import CommunicationAgent directly
|
| 37 |
-
print("\n2. Testing CommunicationAgent import...")
|
| 38 |
-
try:
|
| 39 |
-
from src.agents.drummond import CommunicationAgent
|
| 40 |
-
print(" ✓ CommunicationAgent imported successfully")
|
| 41 |
-
|
| 42 |
-
# Check if shutdown is implemented
|
| 43 |
-
if hasattr(CommunicationAgent, 'shutdown'):
|
| 44 |
-
print(" ✓ shutdown method exists in CommunicationAgent")
|
| 45 |
-
|
| 46 |
-
# Check method resolution order
|
| 47 |
-
print(f" - MRO: {[c.__name__ for c in CommunicationAgent.__mro__]}")
|
| 48 |
-
|
| 49 |
-
# Check abstract methods
|
| 50 |
-
abstract_methods = getattr(CommunicationAgent, '__abstractmethods__', set())
|
| 51 |
-
print(f" - Abstract methods: {abstract_methods}")
|
| 52 |
-
|
| 53 |
-
except Exception as e:
|
| 54 |
-
print(f" ✗ Failed to import CommunicationAgent: {e}")
|
| 55 |
-
traceback.print_exc()
|
| 56 |
-
return
|
| 57 |
-
|
| 58 |
-
# Test 3: Try to instantiate
|
| 59 |
-
print("\n3. Testing CommunicationAgent instantiation...")
|
| 60 |
-
try:
|
| 61 |
-
agent = CommunicationAgent()
|
| 62 |
-
print(" ✓ CommunicationAgent instantiated successfully")
|
| 63 |
-
except Exception as e:
|
| 64 |
-
print(f" ✗ Failed to instantiate CommunicationAgent: {e}")
|
| 65 |
-
traceback.print_exc()
|
| 66 |
-
|
| 67 |
-
# Additional diagnostics
|
| 68 |
-
print("\n Additional diagnostics:")
|
| 69 |
-
try:
|
| 70 |
-
from src.agents.drummond import CommunicationAgent
|
| 71 |
-
print(f" - Class type: {type(CommunicationAgent)}")
|
| 72 |
-
print(f" - Base classes: {CommunicationAgent.__bases__}")
|
| 73 |
-
|
| 74 |
-
# List all methods
|
| 75 |
-
print(" - All methods:")
|
| 76 |
-
for attr in dir(CommunicationAgent):
|
| 77 |
-
if not attr.startswith('_'):
|
| 78 |
-
obj = getattr(CommunicationAgent, attr)
|
| 79 |
-
if callable(obj):
|
| 80 |
-
print(f" * {attr}: {type(obj)}")
|
| 81 |
-
|
| 82 |
-
except Exception as e2:
|
| 83 |
-
print(f" - Failed diagnostics: {e2}")
|
| 84 |
-
|
| 85 |
-
# Test 4: Test the factory
|
| 86 |
-
print("\n4. Testing chat_drummond_factory...")
|
| 87 |
-
try:
|
| 88 |
-
from src.api.routes.chat_drummond_factory import get_drummond_agent
|
| 89 |
-
print(" ✓ Factory imported successfully")
|
| 90 |
-
except Exception as e:
|
| 91 |
-
print(f" ✗ Failed to import factory: {e}")
|
| 92 |
-
traceback.print_exc()
|
| 93 |
-
|
| 94 |
-
print("\n=== END DEBUG ===")
|
| 95 |
-
|
| 96 |
-
if __name__ == "__main__":
|
| 97 |
-
test_import_chain()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug_hf_error.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Debug script to understand the HuggingFace error"""
|
| 3 |
-
|
| 4 |
-
print("=== Debugging HuggingFace Import Error ===\n")
|
| 5 |
-
|
| 6 |
-
# Check if we can find where the error is really coming from
|
| 7 |
-
import re
|
| 8 |
-
|
| 9 |
-
log_line = '{"event": "Failed to initialize Drummond agent: Can\'t instantiate abstract class CommunicationAgent with abstract method shutdown", "logger": "src.api.routes.chat", "level": "error", "timestamp": "2025-09-20T16:17:42.475125Z", "filename": "chat.py", "func_name": "<module>", "lineno": 33}'
|
| 10 |
-
|
| 11 |
-
print("Log says:")
|
| 12 |
-
print(f"- File: chat.py")
|
| 13 |
-
print(f"- Line: 33")
|
| 14 |
-
print(f"- Function: <module> (module-level code)")
|
| 15 |
-
print(f"- Error: Can't instantiate abstract class CommunicationAgent with abstract method shutdown")
|
| 16 |
-
|
| 17 |
-
print("\nThis suggests that somewhere at the module level (not inside a function),")
|
| 18 |
-
print("there's an attempt to instantiate CommunicationAgent directly.")
|
| 19 |
-
print("\nBut line 33 is just a comment. Possible explanations:")
|
| 20 |
-
print("1. Line numbers are off due to imports or preprocessing")
|
| 21 |
-
print("2. There's a hidden try/except block wrapping an import")
|
| 22 |
-
print("3. The error is actually from a different file that's imported")
|
| 23 |
-
print("4. MasterAgent (line 35) might be trying to instantiate CommunicationAgent")
|
| 24 |
-
|
| 25 |
-
print("\nLet's check if MasterAgent exists...")
|
| 26 |
-
|
| 27 |
-
try:
|
| 28 |
-
from src.agents.abaporu import MasterAgent
|
| 29 |
-
print("✓ MasterAgent found in abaporu.py")
|
| 30 |
-
except ImportError as e:
|
| 31 |
-
print(f"✗ MasterAgent not found: {e}")
|
| 32 |
-
print(" This would cause an error at line 35!")
|
| 33 |
-
|
| 34 |
-
print("\nThe real issue might be that MasterAgent is not imported in chat.py!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/api/routes/chat.py
CHANGED
|
@@ -27,6 +27,7 @@ class DataSourceType:
|
|
| 27 |
CONTRACTS = "contratos"
|
| 28 |
SERVANTS = "servidores"
|
| 29 |
EXPENSES = "despesas"
|
|
|
|
| 30 |
|
| 31 |
class UniversalSearchRequest(BaseModel):
|
| 32 |
"""Universal search request model."""
|
|
|
|
| 27 |
CONTRACTS = "contratos"
|
| 28 |
SERVANTS = "servidores"
|
| 29 |
EXPENSES = "despesas"
|
| 30 |
+
BIDDINGS = "licitacoes"
|
| 31 |
|
| 32 |
class UniversalSearchRequest(BaseModel):
|
| 33 |
"""Universal search request model."""
|
test_chat_detailed.py
DELETED
|
@@ -1,103 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Detailed test for chat endpoints with exact response format
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import requests
|
| 7 |
-
import json
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
|
| 10 |
-
BASE_URL = "https://neural-thinker-cidadao-ai-backend.hf.space"
|
| 11 |
-
|
| 12 |
-
def test_chat_message_detailed():
|
| 13 |
-
"""Test main chat endpoint and print full response"""
|
| 14 |
-
print("\n🔍 Testing /api/v1/chat/message with full response...")
|
| 15 |
-
|
| 16 |
-
payload = {
|
| 17 |
-
"message": "Olá, como você pode me ajudar?",
|
| 18 |
-
"session_id": f"test-{datetime.now().timestamp()}"
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
try:
|
| 22 |
-
response = requests.post(
|
| 23 |
-
f"{BASE_URL}/api/v1/chat/message",
|
| 24 |
-
json=payload,
|
| 25 |
-
headers={"Content-Type": "application/json"}
|
| 26 |
-
)
|
| 27 |
-
|
| 28 |
-
print(f"Status Code: {response.status_code}")
|
| 29 |
-
print(f"Headers: {dict(response.headers)}")
|
| 30 |
-
print("\nFull Response:")
|
| 31 |
-
print(json.dumps(response.json(), indent=2, ensure_ascii=False))
|
| 32 |
-
|
| 33 |
-
except Exception as e:
|
| 34 |
-
print(f"Error: {e}")
|
| 35 |
-
print(f"Response Text: {response.text if 'response' in locals() else 'No response'}")
|
| 36 |
-
|
| 37 |
-
def test_chat_simple_detailed():
|
| 38 |
-
"""Test simple chat endpoint"""
|
| 39 |
-
print("\n🔍 Testing /api/v1/chat/simple...")
|
| 40 |
-
|
| 41 |
-
payload = {
|
| 42 |
-
"message": "Olá, como você pode me ajudar?",
|
| 43 |
-
"session_id": f"test-{datetime.now().timestamp()}"
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
try:
|
| 47 |
-
response = requests.post(
|
| 48 |
-
f"{BASE_URL}/api/v1/chat/simple",
|
| 49 |
-
json=payload,
|
| 50 |
-
headers={"Content-Type": "application/json"}
|
| 51 |
-
)
|
| 52 |
-
|
| 53 |
-
print(f"Status Code: {response.status_code}")
|
| 54 |
-
|
| 55 |
-
if response.status_code == 200:
|
| 56 |
-
print("\nFull Response:")
|
| 57 |
-
print(json.dumps(response.json(), indent=2, ensure_ascii=False))
|
| 58 |
-
else:
|
| 59 |
-
print(f"Response: {response.text}")
|
| 60 |
-
|
| 61 |
-
except Exception as e:
|
| 62 |
-
print(f"Error: {e}")
|
| 63 |
-
|
| 64 |
-
def test_available_endpoints():
|
| 65 |
-
"""Check which endpoints are available"""
|
| 66 |
-
print("\n📋 Checking available endpoints...")
|
| 67 |
-
|
| 68 |
-
endpoints = [
|
| 69 |
-
"/api/v1/chat/message",
|
| 70 |
-
"/api/v1/chat/simple",
|
| 71 |
-
"/api/v1/chat/agents",
|
| 72 |
-
"/api/v1/chat/suggestions",
|
| 73 |
-
"/api/v1/chat/stream",
|
| 74 |
-
"/docs",
|
| 75 |
-
"/openapi.json"
|
| 76 |
-
]
|
| 77 |
-
|
| 78 |
-
for endpoint in endpoints:
|
| 79 |
-
try:
|
| 80 |
-
if endpoint in ["/api/v1/chat/message", "/api/v1/chat/simple", "/api/v1/chat/stream"]:
|
| 81 |
-
# POST endpoints
|
| 82 |
-
response = requests.post(
|
| 83 |
-
f"{BASE_URL}{endpoint}",
|
| 84 |
-
json={"message": "test", "session_id": "test"},
|
| 85 |
-
timeout=5
|
| 86 |
-
)
|
| 87 |
-
else:
|
| 88 |
-
# GET endpoints
|
| 89 |
-
response = requests.get(f"{BASE_URL}{endpoint}", timeout=5)
|
| 90 |
-
|
| 91 |
-
print(f"{endpoint}: {response.status_code} {'✅' if response.status_code != 404 else '❌'}")
|
| 92 |
-
except Exception as e:
|
| 93 |
-
print(f"{endpoint}: Error - {str(e)[:50]}")
|
| 94 |
-
|
| 95 |
-
if __name__ == "__main__":
|
| 96 |
-
print("=" * 60)
|
| 97 |
-
print("🔬 Detailed Chat Endpoint Test")
|
| 98 |
-
print(f"🌐 URL: {BASE_URL}")
|
| 99 |
-
print("=" * 60)
|
| 100 |
-
|
| 101 |
-
test_available_endpoints()
|
| 102 |
-
test_chat_message_detailed()
|
| 103 |
-
test_chat_simple_detailed()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_chat_simple.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Teste do endpoint simples de chat com Maritaca AI
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import requests
|
| 7 |
-
import json
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
import time
|
| 10 |
-
|
| 11 |
-
# URL do backend no HuggingFace Spaces
|
| 12 |
-
BASE_URL = "https://neural-thinker-cidadao-ai-backend.hf.space"
|
| 13 |
-
|
| 14 |
-
def test_chat_simple():
|
| 15 |
-
"""Testa o novo endpoint simples de chat"""
|
| 16 |
-
endpoint = f"{BASE_URL}/api/v1/chat/simple"
|
| 17 |
-
|
| 18 |
-
print("🧪 Testando endpoint /api/v1/chat/simple")
|
| 19 |
-
print("="*50)
|
| 20 |
-
|
| 21 |
-
# Primeiro, verifica o status
|
| 22 |
-
status_endpoint = f"{BASE_URL}/api/v1/chat/simple/status"
|
| 23 |
-
try:
|
| 24 |
-
response = requests.get(status_endpoint)
|
| 25 |
-
if response.status_code == 200:
|
| 26 |
-
status = response.json()
|
| 27 |
-
print(f"📊 Status do Chat:")
|
| 28 |
-
print(f" Maritaca disponível: {status.get('maritaca_available', False)}")
|
| 29 |
-
print(f" API Key configurada: {status.get('api_key_configured', False)}")
|
| 30 |
-
print()
|
| 31 |
-
except Exception as e:
|
| 32 |
-
print(f"❌ Erro ao verificar status: {e}")
|
| 33 |
-
|
| 34 |
-
# Mensagens de teste
|
| 35 |
-
test_messages = [
|
| 36 |
-
"Olá, como você está?",
|
| 37 |
-
"O que é o Cidadão.AI?",
|
| 38 |
-
"Como posso investigar contratos públicos?",
|
| 39 |
-
"Me ajuda a entender o portal da transparência",
|
| 40 |
-
"Quero analisar gastos com saúde em 2024"
|
| 41 |
-
]
|
| 42 |
-
|
| 43 |
-
headers = {
|
| 44 |
-
"Content-Type": "application/json",
|
| 45 |
-
"Accept": "application/json"
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
session_id = f"test-session-{int(time.time())}"
|
| 49 |
-
|
| 50 |
-
for i, message in enumerate(test_messages, 1):
|
| 51 |
-
print(f"\n💬 Teste {i}: {message}")
|
| 52 |
-
|
| 53 |
-
payload = {
|
| 54 |
-
"message": message,
|
| 55 |
-
"session_id": session_id
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
try:
|
| 59 |
-
start_time = time.time()
|
| 60 |
-
response = requests.post(
|
| 61 |
-
endpoint,
|
| 62 |
-
json=payload,
|
| 63 |
-
headers=headers,
|
| 64 |
-
timeout=30
|
| 65 |
-
)
|
| 66 |
-
elapsed = time.time() - start_time
|
| 67 |
-
|
| 68 |
-
print(f" ⏱️ Tempo de resposta: {elapsed:.2f}s")
|
| 69 |
-
print(f" 📡 Status HTTP: {response.status_code}")
|
| 70 |
-
|
| 71 |
-
if response.status_code == 200:
|
| 72 |
-
data = response.json()
|
| 73 |
-
print(f" ✅ Resposta recebida!")
|
| 74 |
-
print(f" 🤖 Modelo usado: {data.get('model_used', 'N/A')}")
|
| 75 |
-
print(f" 💬 Resposta: {data.get('message', '')[:150]}...")
|
| 76 |
-
|
| 77 |
-
# Verifica se está usando Maritaca
|
| 78 |
-
if data.get('model_used') != 'fallback':
|
| 79 |
-
print(f" 🎉 Usando Maritaca AI! Modelo: {data.get('model_used')}")
|
| 80 |
-
else:
|
| 81 |
-
print(f" ❌ Erro: {response.text[:200]}")
|
| 82 |
-
|
| 83 |
-
except requests.exceptions.Timeout:
|
| 84 |
-
print(f" ⏱️ Timeout - demorou mais de 30 segundos")
|
| 85 |
-
except Exception as e:
|
| 86 |
-
print(f" ❌ Erro: {e}")
|
| 87 |
-
|
| 88 |
-
# Pequena pausa entre requisições
|
| 89 |
-
if i < len(test_messages):
|
| 90 |
-
time.sleep(1)
|
| 91 |
-
|
| 92 |
-
print("\n" + "="*50)
|
| 93 |
-
print("✅ Teste concluído!")
|
| 94 |
-
print(f"\n💡 Dica: Para usar no frontend, faça requisições POST para:")
|
| 95 |
-
print(f" {endpoint}")
|
| 96 |
-
print(f" Com body: {{\"message\": \"sua mensagem\", \"session_id\": \"opcional\"}}")
|
| 97 |
-
|
| 98 |
-
if __name__ == "__main__":
|
| 99 |
-
test_chat_simple()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_drummond_import.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Test Drummond import to debug the issue."""
|
| 3 |
-
|
| 4 |
-
import inspect
|
| 5 |
-
|
| 6 |
-
# Test direct import
|
| 7 |
-
try:
|
| 8 |
-
from src.agents.drummond import CommunicationAgent
|
| 9 |
-
print("✅ Import successful!")
|
| 10 |
-
|
| 11 |
-
# Check abstract methods
|
| 12 |
-
abstract_methods = getattr(CommunicationAgent, '__abstractmethods__', set())
|
| 13 |
-
print(f"Abstract methods: {abstract_methods}")
|
| 14 |
-
|
| 15 |
-
# Check if shutdown is implemented
|
| 16 |
-
if hasattr(CommunicationAgent, 'shutdown'):
|
| 17 |
-
print("✅ shutdown method exists")
|
| 18 |
-
shutdown_method = getattr(CommunicationAgent, 'shutdown')
|
| 19 |
-
print(f" Is coroutine: {inspect.iscoroutinefunction(shutdown_method)}")
|
| 20 |
-
else:
|
| 21 |
-
print("❌ shutdown method NOT FOUND")
|
| 22 |
-
|
| 23 |
-
# Check all methods
|
| 24 |
-
all_methods = [m for m in dir(CommunicationAgent) if not m.startswith('_')]
|
| 25 |
-
print(f"\nAll public methods: {all_methods}")
|
| 26 |
-
|
| 27 |
-
except Exception as e:
|
| 28 |
-
print(f"❌ Import failed: {type(e).__name__}: {e}")
|
| 29 |
-
|
| 30 |
-
# Try simpler import
|
| 31 |
-
try:
|
| 32 |
-
import sys
|
| 33 |
-
import os
|
| 34 |
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 35 |
-
from src.agents.deodoro import BaseAgent
|
| 36 |
-
print("\n✅ BaseAgent imported successfully")
|
| 37 |
-
|
| 38 |
-
# Check BaseAgent abstract methods
|
| 39 |
-
abstract_base = getattr(BaseAgent, '__abstractmethods__', set())
|
| 40 |
-
print(f"BaseAgent abstract methods: {abstract_base}")
|
| 41 |
-
except Exception as e2:
|
| 42 |
-
print(f"❌ BaseAgent import also failed: {e2}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_drummond_init.py
DELETED
|
@@ -1,30 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Test Drummond initialization locally"""
|
| 3 |
-
import os
|
| 4 |
-
import sys
|
| 5 |
-
|
| 6 |
-
# Add the src directory to the path
|
| 7 |
-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
| 8 |
-
|
| 9 |
-
# Set necessary environment variables for testing
|
| 10 |
-
os.environ["GROQ_API_KEY"] = "dummy_key_for_test"
|
| 11 |
-
os.environ["JWT_SECRET_KEY"] = "test_secret"
|
| 12 |
-
os.environ["SECRET_KEY"] = "test_secret"
|
| 13 |
-
|
| 14 |
-
try:
|
| 15 |
-
print("Testing Drummond agent initialization...")
|
| 16 |
-
from src.agents.drummond import CommunicationAgent
|
| 17 |
-
|
| 18 |
-
# Try to create agent
|
| 19 |
-
print("Creating CommunicationAgent...")
|
| 20 |
-
agent = CommunicationAgent()
|
| 21 |
-
print("✓ Agent created successfully!")
|
| 22 |
-
|
| 23 |
-
# Check if it has the necessary methods
|
| 24 |
-
print(f"Has process method: {hasattr(agent, 'process')}")
|
| 25 |
-
print(f"Has shutdown method: {hasattr(agent, 'shutdown')}")
|
| 26 |
-
|
| 27 |
-
except Exception as e:
|
| 28 |
-
print(f"✗ Error creating agent: {e}")
|
| 29 |
-
import traceback
|
| 30 |
-
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_drummond_live.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Test Drummond agent on live HuggingFace deployment"""
|
| 3 |
-
import requests
|
| 4 |
-
import json
|
| 5 |
-
|
| 6 |
-
# Test the chat endpoint with a greeting
|
| 7 |
-
url = "https://neural-thinker-cidadao-ai-backend.hf.space/api/v1/chat/message"
|
| 8 |
-
headers = {"Content-Type": "application/json"}
|
| 9 |
-
|
| 10 |
-
# Test 1: Simple greeting (should route to Drummond)
|
| 11 |
-
print("Test 1: Testing greeting message...")
|
| 12 |
-
data = {"message": "Olá, pode me ajudar?"}
|
| 13 |
-
try:
|
| 14 |
-
response = requests.post(url, json=data, headers=headers)
|
| 15 |
-
print(f"Status: {response.status_code}")
|
| 16 |
-
print(f"Response: {json.dumps(response.json(), indent=2, ensure_ascii=False)}")
|
| 17 |
-
except Exception as e:
|
| 18 |
-
print(f"Error: {e}")
|
| 19 |
-
if hasattr(response, 'text'):
|
| 20 |
-
print(f"Raw response: {response.text}")
|
| 21 |
-
|
| 22 |
-
print("\n" + "="*50 + "\n")
|
| 23 |
-
|
| 24 |
-
# Test 2: Literary analysis request
|
| 25 |
-
print("Test 2: Testing literary analysis...")
|
| 26 |
-
data = {"message": "Analise o poema 'No meio do caminho tinha uma pedra' de Drummond"}
|
| 27 |
-
try:
|
| 28 |
-
response = requests.post(url, json=data, headers=headers)
|
| 29 |
-
print(f"Status: {response.status_code}")
|
| 30 |
-
print(f"Response: {json.dumps(response.json(), indent=2, ensure_ascii=False)}")
|
| 31 |
-
except Exception as e:
|
| 32 |
-
print(f"Error: {e}")
|
| 33 |
-
if hasattr(response, 'text'):
|
| 34 |
-
print(f"Raw response: {response.text}")
|
| 35 |
-
|
| 36 |
-
print("\n" + "="*50 + "\n")
|
| 37 |
-
|
| 38 |
-
# Test 3: Check health endpoint
|
| 39 |
-
print("Test 3: Checking health endpoint...")
|
| 40 |
-
health_url = "https://neural-thinker-cidadao-ai-backend.hf.space/health"
|
| 41 |
-
try:
|
| 42 |
-
response = requests.get(health_url)
|
| 43 |
-
print(f"Status: {response.status_code}")
|
| 44 |
-
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
| 45 |
-
except Exception as e:
|
| 46 |
-
print(f"Error: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_drummond_minimal.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Minimal test to verify CommunicationAgent can be imported and instantiated.
|
| 4 |
-
This simulates what happens on HuggingFace Spaces.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import sys
|
| 8 |
-
import os
|
| 9 |
-
|
| 10 |
-
# Add src to path like HuggingFace does
|
| 11 |
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 12 |
-
|
| 13 |
-
def test_minimal_import():
|
| 14 |
-
print("=== MINIMAL DRUMMOND TEST ===")
|
| 15 |
-
|
| 16 |
-
# Step 1: Try importing just the class
|
| 17 |
-
try:
|
| 18 |
-
print("1. Importing CommunicationAgent class...")
|
| 19 |
-
from src.agents.drummond import CommunicationAgent
|
| 20 |
-
print(" ✓ Import successful")
|
| 21 |
-
except Exception as e:
|
| 22 |
-
print(f" ✗ Import failed: {e}")
|
| 23 |
-
import traceback
|
| 24 |
-
traceback.print_exc()
|
| 25 |
-
return False
|
| 26 |
-
|
| 27 |
-
# Step 2: Check if it's a proper class
|
| 28 |
-
print("\n2. Checking class structure...")
|
| 29 |
-
print(f" - Type: {type(CommunicationAgent)}")
|
| 30 |
-
print(f" - Base classes: {CommunicationAgent.__bases__}")
|
| 31 |
-
print(f" - Module: {CommunicationAgent.__module__}")
|
| 32 |
-
|
| 33 |
-
# Check abstract methods
|
| 34 |
-
abstract_methods = getattr(CommunicationAgent, '__abstractmethods__', None)
|
| 35 |
-
if abstract_methods:
|
| 36 |
-
print(f" - Abstract methods remaining: {abstract_methods}")
|
| 37 |
-
else:
|
| 38 |
-
print(" - No abstract methods remaining")
|
| 39 |
-
|
| 40 |
-
# Step 3: Try instantiation without any dependencies
|
| 41 |
-
print("\n3. Testing instantiation...")
|
| 42 |
-
try:
|
| 43 |
-
# Mock the logger to avoid dependency issues
|
| 44 |
-
import logging
|
| 45 |
-
logging.basicConfig(level=logging.INFO)
|
| 46 |
-
|
| 47 |
-
# Try to create instance
|
| 48 |
-
agent = CommunicationAgent()
|
| 49 |
-
print(" ✓ Instantiation successful")
|
| 50 |
-
return True
|
| 51 |
-
except Exception as e:
|
| 52 |
-
print(f" ✗ Instantiation failed: {e}")
|
| 53 |
-
import traceback
|
| 54 |
-
traceback.print_exc()
|
| 55 |
-
return False
|
| 56 |
-
|
| 57 |
-
if __name__ == "__main__":
|
| 58 |
-
success = test_minimal_import()
|
| 59 |
-
print(f"\n=== TEST {'PASSED' if success else 'FAILED'} ===")
|
| 60 |
-
sys.exit(0 if success else 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_hf_chat.py
DELETED
|
@@ -1,152 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Test script for HuggingFace Spaces chat endpoints
|
| 4 |
-
Tests both main and simple chat endpoints with Maritaca AI
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import requests
|
| 8 |
-
import json
|
| 9 |
-
from datetime import datetime
|
| 10 |
-
|
| 11 |
-
# HuggingFace Spaces URL
|
| 12 |
-
BASE_URL = "https://neural-thinker-cidadao-ai-backend.hf.space"
|
| 13 |
-
|
| 14 |
-
def test_health():
|
| 15 |
-
"""Test if API is running"""
|
| 16 |
-
print("\n1️⃣ Testing API Health...")
|
| 17 |
-
try:
|
| 18 |
-
response = requests.get(f"{BASE_URL}/")
|
| 19 |
-
print(f"✅ API Status: {response.status_code}")
|
| 20 |
-
print(f"Response: {response.json()}")
|
| 21 |
-
return True
|
| 22 |
-
except Exception as e:
|
| 23 |
-
print(f"❌ Health check failed: {e}")
|
| 24 |
-
return False
|
| 25 |
-
|
| 26 |
-
def test_docs():
|
| 27 |
-
"""Test if API docs are accessible"""
|
| 28 |
-
print("\n2️⃣ Testing API Documentation...")
|
| 29 |
-
try:
|
| 30 |
-
response = requests.get(f"{BASE_URL}/docs")
|
| 31 |
-
print(f"✅ Docs Status: {response.status_code}")
|
| 32 |
-
return True
|
| 33 |
-
except Exception as e:
|
| 34 |
-
print(f"❌ Docs check failed: {e}")
|
| 35 |
-
return False
|
| 36 |
-
|
| 37 |
-
def test_simple_chat():
|
| 38 |
-
"""Test simple chat endpoint with Maritaca AI"""
|
| 39 |
-
print("\n3️⃣ Testing Simple Chat Endpoint (Maritaca AI direct)...")
|
| 40 |
-
|
| 41 |
-
test_messages = [
|
| 42 |
-
"Olá, como você pode me ajudar?",
|
| 43 |
-
"Quais são os gastos públicos mais recentes?",
|
| 44 |
-
"Me explique sobre transparência governamental"
|
| 45 |
-
]
|
| 46 |
-
|
| 47 |
-
for message in test_messages:
|
| 48 |
-
print(f"\n📤 Sending: {message}")
|
| 49 |
-
try:
|
| 50 |
-
response = requests.post(
|
| 51 |
-
f"{BASE_URL}/api/v1/chat/simple",
|
| 52 |
-
json={
|
| 53 |
-
"message": message,
|
| 54 |
-
"session_id": f"test-session-{datetime.now().timestamp()}"
|
| 55 |
-
},
|
| 56 |
-
headers={"Content-Type": "application/json"}
|
| 57 |
-
)
|
| 58 |
-
|
| 59 |
-
if response.status_code == 200:
|
| 60 |
-
data = response.json()
|
| 61 |
-
print(f"✅ Response Status: {response.status_code}")
|
| 62 |
-
print(f"📥 Assistant: {data['response'][:200]}...")
|
| 63 |
-
print(f"🤖 Agent Used: {data.get('agent_used', 'Unknown')}")
|
| 64 |
-
else:
|
| 65 |
-
print(f"⚠️ Status: {response.status_code}")
|
| 66 |
-
print(f"Response: {response.text}")
|
| 67 |
-
|
| 68 |
-
except Exception as e:
|
| 69 |
-
print(f"❌ Error: {e}")
|
| 70 |
-
|
| 71 |
-
def test_main_chat():
|
| 72 |
-
"""Test main chat endpoint with full agent system"""
|
| 73 |
-
print("\n4️⃣ Testing Main Chat Endpoint (Full Agent System)...")
|
| 74 |
-
|
| 75 |
-
test_messages = [
|
| 76 |
-
{"message": "Oi, tudo bem?", "expected_agent": "Drummond"},
|
| 77 |
-
{"message": "Investigue contratos suspeitos em São Paulo", "expected_agent": "Abaporu/Zumbi"},
|
| 78 |
-
{"message": "Análise de gastos com educação", "expected_agent": "Abaporu"}
|
| 79 |
-
]
|
| 80 |
-
|
| 81 |
-
for test in test_messages:
|
| 82 |
-
print(f"\n📤 Sending: {test['message']}")
|
| 83 |
-
print(f"🎯 Expected Agent: {test['expected_agent']}")
|
| 84 |
-
|
| 85 |
-
try:
|
| 86 |
-
response = requests.post(
|
| 87 |
-
f"{BASE_URL}/api/v1/chat/message",
|
| 88 |
-
json={
|
| 89 |
-
"message": test["message"],
|
| 90 |
-
"session_id": f"test-session-{datetime.now().timestamp()}"
|
| 91 |
-
},
|
| 92 |
-
headers={"Content-Type": "application/json"}
|
| 93 |
-
)
|
| 94 |
-
|
| 95 |
-
if response.status_code == 200:
|
| 96 |
-
data = response.json()
|
| 97 |
-
print(f"✅ Response Status: {response.status_code}")
|
| 98 |
-
print(f"📥 Response: {data['response'][:200]}...")
|
| 99 |
-
print(f"🤖 Agent: {data.get('agent_name', 'Unknown')}")
|
| 100 |
-
print(f"💬 Type: {data.get('response_type', 'Unknown')}")
|
| 101 |
-
else:
|
| 102 |
-
print(f"⚠️ Status: {response.status_code}")
|
| 103 |
-
print(f"Response: {response.text}")
|
| 104 |
-
|
| 105 |
-
except Exception as e:
|
| 106 |
-
print(f"❌ Error: {e}")
|
| 107 |
-
|
| 108 |
-
def test_chat_suggestions():
|
| 109 |
-
"""Test chat suggestions endpoint"""
|
| 110 |
-
print("\n5️⃣ Testing Chat Suggestions...")
|
| 111 |
-
try:
|
| 112 |
-
response = requests.get(
|
| 113 |
-
f"{BASE_URL}/api/v1/chat/suggestions",
|
| 114 |
-
params={"limit": 5}
|
| 115 |
-
)
|
| 116 |
-
|
| 117 |
-
if response.status_code == 200:
|
| 118 |
-
suggestions = response.json()
|
| 119 |
-
print(f"✅ Found {len(suggestions)} suggestions:")
|
| 120 |
-
for idx, suggestion in enumerate(suggestions[:3], 1):
|
| 121 |
-
print(f" {idx}. {suggestion['text']}")
|
| 122 |
-
else:
|
| 123 |
-
print(f"⚠️ Status: {response.status_code}")
|
| 124 |
-
|
| 125 |
-
except Exception as e:
|
| 126 |
-
print(f"❌ Error: {e}")
|
| 127 |
-
|
| 128 |
-
def main():
|
| 129 |
-
"""Run all tests"""
|
| 130 |
-
print("🚀 Testing Cidadão.AI Backend on HuggingFace Spaces")
|
| 131 |
-
print("=" * 60)
|
| 132 |
-
print(f"🌐 Base URL: {BASE_URL}")
|
| 133 |
-
print(f"🕐 Test Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
| 134 |
-
print("=" * 60)
|
| 135 |
-
|
| 136 |
-
# Run tests
|
| 137 |
-
if test_health():
|
| 138 |
-
test_docs()
|
| 139 |
-
test_simple_chat()
|
| 140 |
-
test_main_chat()
|
| 141 |
-
test_chat_suggestions()
|
| 142 |
-
|
| 143 |
-
print("\n" + "=" * 60)
|
| 144 |
-
print("✅ Tests completed!")
|
| 145 |
-
print("\n💡 Integration Tips for Frontend:")
|
| 146 |
-
print("1. Use /api/v1/chat/simple for reliable Maritaca AI responses")
|
| 147 |
-
print("2. Use /api/v1/chat/message for full agent capabilities")
|
| 148 |
-
print("3. Handle both 200 (success) and 500 (fallback) responses")
|
| 149 |
-
print("4. Check the 'agent_used' field to know which agent responded")
|
| 150 |
-
|
| 151 |
-
if __name__ == "__main__":
|
| 152 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_hf_spaces.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
🔍 Teste de HuggingFace Spaces
|
| 4 |
-
Verifica se os Spaces estão rodando e quais endpoints respondem
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import httpx
|
| 9 |
-
|
| 10 |
-
async def test_hf_spaces():
|
| 11 |
-
"""🔍 Testa diferentes endpoints dos HF Spaces"""
|
| 12 |
-
print("🔍 VERIFICANDO HUGGINGFACE SPACES")
|
| 13 |
-
print("=" * 50)
|
| 14 |
-
|
| 15 |
-
# URLs para testar
|
| 16 |
-
backend_urls = [
|
| 17 |
-
"https://neural-thinker-cidadao-ai-backend.hf.space",
|
| 18 |
-
"https://neural-thinker-cidadao-ai-backend.hf.space/",
|
| 19 |
-
"https://neural-thinker-cidadao-ai-backend.hf.space/health",
|
| 20 |
-
"https://neural-thinker-cidadao-ai-backend.hf.space/docs",
|
| 21 |
-
"https://huggingface.co/spaces/neural-thinker/cidadao.ai-backend"
|
| 22 |
-
]
|
| 23 |
-
|
| 24 |
-
models_urls = [
|
| 25 |
-
"https://neural-thinker-cidadao-ai-models.hf.space",
|
| 26 |
-
"https://neural-thinker-cidadao-ai-models.hf.space/",
|
| 27 |
-
"https://neural-thinker-cidadao-ai-models.hf.space/health",
|
| 28 |
-
"https://huggingface.co/spaces/neural-thinker/cidadao.ai-models"
|
| 29 |
-
]
|
| 30 |
-
|
| 31 |
-
async with httpx.AsyncClient(timeout=10.0) as client:
|
| 32 |
-
|
| 33 |
-
print("🏛️ TESTANDO BACKEND SPACES:")
|
| 34 |
-
for url in backend_urls:
|
| 35 |
-
try:
|
| 36 |
-
response = await client.get(url)
|
| 37 |
-
status = "✅" if response.status_code == 200 else f"❌ {response.status_code}"
|
| 38 |
-
print(f" {status} {url}")
|
| 39 |
-
|
| 40 |
-
if response.status_code == 200 and 'application/json' in response.headers.get('content-type', ''):
|
| 41 |
-
try:
|
| 42 |
-
data = response.json()
|
| 43 |
-
if 'status' in data:
|
| 44 |
-
print(f" 📊 Status: {data.get('status')}")
|
| 45 |
-
if 'agents' in data:
|
| 46 |
-
print(f" 🤖 Agentes: {list(data.get('agents', {}).keys())}")
|
| 47 |
-
except:
|
| 48 |
-
print(f" 📝 HTML response (não JSON)")
|
| 49 |
-
|
| 50 |
-
except Exception as e:
|
| 51 |
-
print(f" ❌ {url} - Erro: {str(e)[:50]}...")
|
| 52 |
-
|
| 53 |
-
print("\n🤖 TESTANDO MODELS SPACES:")
|
| 54 |
-
for url in models_urls:
|
| 55 |
-
try:
|
| 56 |
-
response = await client.get(url)
|
| 57 |
-
status = "✅" if response.status_code == 200 else f"❌ {response.status_code}"
|
| 58 |
-
print(f" {status} {url}")
|
| 59 |
-
|
| 60 |
-
if response.status_code == 200 and 'application/json' in response.headers.get('content-type', ''):
|
| 61 |
-
try:
|
| 62 |
-
data = response.json()
|
| 63 |
-
if 'api' in data:
|
| 64 |
-
print(f" 📊 API: {data.get('api')}")
|
| 65 |
-
if 'version' in data:
|
| 66 |
-
print(f" 🔢 Version: {data.get('version')}")
|
| 67 |
-
except:
|
| 68 |
-
print(f" 📝 HTML response (não JSON)")
|
| 69 |
-
|
| 70 |
-
except Exception as e:
|
| 71 |
-
print(f" ❌ {url} - Erro: {str(e)[:50]}...")
|
| 72 |
-
|
| 73 |
-
if __name__ == "__main__":
|
| 74 |
-
asyncio.run(test_hf_spaces())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_maritaca_integration.py
DELETED
|
@@ -1,118 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Script para testar a integração Maritaca AI no Cidadão.AI
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import requests
|
| 7 |
-
import json
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
|
| 10 |
-
# URL do backend no HuggingFace Spaces
|
| 11 |
-
BASE_URL = "https://neural-thinker-cidadao-ai-backend.hf.space"
|
| 12 |
-
|
| 13 |
-
def test_health():
|
| 14 |
-
"""Testa se a API está online"""
|
| 15 |
-
try:
|
| 16 |
-
response = requests.get(f"{BASE_URL}/health")
|
| 17 |
-
print(f"✅ Health Check: {response.status_code}")
|
| 18 |
-
if response.status_code == 200:
|
| 19 |
-
print(f" Response: {response.json()}")
|
| 20 |
-
return response.status_code == 200
|
| 21 |
-
except Exception as e:
|
| 22 |
-
print(f"❌ Health Check Error: {e}")
|
| 23 |
-
return False
|
| 24 |
-
|
| 25 |
-
def test_chat_endpoint():
|
| 26 |
-
"""Testa o endpoint de chat com a Maritaca AI"""
|
| 27 |
-
endpoint = f"{BASE_URL}/api/v1/chat/message"
|
| 28 |
-
|
| 29 |
-
# Mensagens de teste
|
| 30 |
-
test_messages = [
|
| 31 |
-
{
|
| 32 |
-
"message": "Olá, tudo bem?",
|
| 33 |
-
"expected_agent": "drummond"
|
| 34 |
-
},
|
| 35 |
-
{
|
| 36 |
-
"message": "Quero investigar contratos de saúde em São Paulo",
|
| 37 |
-
"expected_agent": "abaporu"
|
| 38 |
-
},
|
| 39 |
-
{
|
| 40 |
-
"message": "Me explique como funciona o portal da transparência",
|
| 41 |
-
"expected_agent": "drummond"
|
| 42 |
-
}
|
| 43 |
-
]
|
| 44 |
-
|
| 45 |
-
headers = {
|
| 46 |
-
"Content-Type": "application/json",
|
| 47 |
-
"Accept": "application/json"
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
for test in test_messages:
|
| 51 |
-
print(f"\n📤 Testando: '{test['message']}'")
|
| 52 |
-
print(f" Agente esperado: {test['expected_agent']}")
|
| 53 |
-
|
| 54 |
-
payload = {
|
| 55 |
-
"message": test["message"],
|
| 56 |
-
"session_id": f"test-{datetime.now().timestamp()}"
|
| 57 |
-
}
|
| 58 |
-
|
| 59 |
-
try:
|
| 60 |
-
response = requests.post(
|
| 61 |
-
endpoint,
|
| 62 |
-
json=payload,
|
| 63 |
-
headers=headers,
|
| 64 |
-
timeout=30
|
| 65 |
-
)
|
| 66 |
-
|
| 67 |
-
print(f" Status: {response.status_code}")
|
| 68 |
-
|
| 69 |
-
if response.status_code == 200:
|
| 70 |
-
data = response.json()
|
| 71 |
-
print(f" ✅ Resposta recebida!")
|
| 72 |
-
print(f" Agente: {data.get('agent_name', 'N/A')}")
|
| 73 |
-
print(f" Mensagem: {data.get('message', 'N/A')[:100]}...")
|
| 74 |
-
print(f" Confiança: {data.get('confidence', 'N/A')}")
|
| 75 |
-
|
| 76 |
-
# Verifica se está usando Maritaca
|
| 77 |
-
if "drummond" in data.get('agent_id', '').lower():
|
| 78 |
-
print(f" 🤖 Drummond ativado (deve estar usando Maritaca AI)")
|
| 79 |
-
|
| 80 |
-
elif response.status_code == 422:
|
| 81 |
-
print(f" ❌ Erro de validação: {response.json()}")
|
| 82 |
-
else:
|
| 83 |
-
print(f" ❌ Erro: {response.text[:200]}")
|
| 84 |
-
|
| 85 |
-
except requests.exceptions.Timeout:
|
| 86 |
-
print(f" ⏱️ Timeout - a requisição demorou mais de 30 segundos")
|
| 87 |
-
except Exception as e:
|
| 88 |
-
print(f" ❌ Erro na requisição: {e}")
|
| 89 |
-
|
| 90 |
-
def test_api_docs():
|
| 91 |
-
"""Verifica se a documentação da API está acessível"""
|
| 92 |
-
try:
|
| 93 |
-
response = requests.get(f"{BASE_URL}/docs")
|
| 94 |
-
print(f"\n📚 API Docs: {response.status_code}")
|
| 95 |
-
if response.status_code == 200:
|
| 96 |
-
print(f" ✅ Documentação disponível em: {BASE_URL}/docs")
|
| 97 |
-
return response.status_code == 200
|
| 98 |
-
except Exception as e:
|
| 99 |
-
print(f"❌ API Docs Error: {e}")
|
| 100 |
-
return False
|
| 101 |
-
|
| 102 |
-
if __name__ == "__main__":
|
| 103 |
-
print("🧪 Testando integração Maritaca AI no Cidadão.AI")
|
| 104 |
-
print(f"🌐 Backend URL: {BASE_URL}")
|
| 105 |
-
print("="*50)
|
| 106 |
-
|
| 107 |
-
# Testa health check
|
| 108 |
-
if test_health():
|
| 109 |
-
# Testa documentação
|
| 110 |
-
test_api_docs()
|
| 111 |
-
|
| 112 |
-
# Testa endpoint de chat
|
| 113 |
-
test_chat_endpoint()
|
| 114 |
-
else:
|
| 115 |
-
print("\n❌ API não está respondendo. Verifique se o HuggingFace Spaces está online.")
|
| 116 |
-
|
| 117 |
-
print("\n"+"="*50)
|
| 118 |
-
print("✅ Testes concluídos!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_models_communication.py
DELETED
|
@@ -1,235 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
🧪 Teste de Comunicação Backend ↔ Models
|
| 4 |
-
Verifica se os repositórios estão conversando via API
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import sys
|
| 9 |
-
import os
|
| 10 |
-
import httpx
|
| 11 |
-
import json
|
| 12 |
-
from datetime import datetime
|
| 13 |
-
|
| 14 |
-
# Add src to path
|
| 15 |
-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
| 16 |
-
|
| 17 |
-
from src.tools.models_client import ModelsClient
|
| 18 |
-
from src.core.config import Settings
|
| 19 |
-
|
| 20 |
-
# Test configuration
|
| 21 |
-
MODELS_API_URL = "https://neural-thinker-cidadao-ai-models.hf.space"
|
| 22 |
-
BACKEND_API_URL = "https://neural-thinker-cidadao-ai-backend.hf.space"
|
| 23 |
-
|
| 24 |
-
async def test_models_api_direct():
|
| 25 |
-
"""🔍 TESTE 1: Acesso direto à API de Modelos"""
|
| 26 |
-
print("=" * 60)
|
| 27 |
-
print("🔍 TESTE 1: MODELS API - ACESSO DIRETO")
|
| 28 |
-
print("=" * 60)
|
| 29 |
-
|
| 30 |
-
try:
|
| 31 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 32 |
-
# Test root endpoint
|
| 33 |
-
print(f"📡 Testando: {MODELS_API_URL}")
|
| 34 |
-
response = await client.get(f"{MODELS_API_URL}/")
|
| 35 |
-
|
| 36 |
-
if response.status_code == 200:
|
| 37 |
-
data = response.json()
|
| 38 |
-
print("✅ Models API está ONLINE!")
|
| 39 |
-
print(f" 📊 API: {data.get('api', 'N/A')}")
|
| 40 |
-
print(f" 🔢 Versão: {data.get('version', 'N/A')}")
|
| 41 |
-
print(f" 📋 Status: {data.get('status', 'N/A')}")
|
| 42 |
-
print(f" 🔗 Endpoints: {list(data.get('endpoints', {}).keys())}")
|
| 43 |
-
return True
|
| 44 |
-
else:
|
| 45 |
-
print(f"❌ Models API retornou status: {response.status_code}")
|
| 46 |
-
return False
|
| 47 |
-
|
| 48 |
-
except Exception as e:
|
| 49 |
-
print(f"❌ Erro ao conectar Models API: {str(e)}")
|
| 50 |
-
return False
|
| 51 |
-
|
| 52 |
-
async def test_models_health():
|
| 53 |
-
"""🏥 TESTE 2: Health check da API de Modelos"""
|
| 54 |
-
print("\n" + "=" * 60)
|
| 55 |
-
print("🏥 TESTE 2: MODELS API - HEALTH CHECK")
|
| 56 |
-
print("=" * 60)
|
| 57 |
-
|
| 58 |
-
try:
|
| 59 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 60 |
-
response = await client.get(f"{MODELS_API_URL}/health")
|
| 61 |
-
|
| 62 |
-
if response.status_code == 200:
|
| 63 |
-
data = response.json()
|
| 64 |
-
print("✅ Health check OK!")
|
| 65 |
-
print(f" 📊 Status: {data.get('status', 'N/A')}")
|
| 66 |
-
print(f" 🤖 Modelos carregados: {data.get('models_loaded', 'N/A')}")
|
| 67 |
-
return True
|
| 68 |
-
else:
|
| 69 |
-
print(f"❌ Health check falhou: {response.status_code}")
|
| 70 |
-
return False
|
| 71 |
-
|
| 72 |
-
except Exception as e:
|
| 73 |
-
print(f"❌ Erro no health check: {str(e)}")
|
| 74 |
-
return False
|
| 75 |
-
|
| 76 |
-
async def test_backend_to_models():
|
| 77 |
-
"""🔄 TESTE 3: Backend chamando Models via Client"""
|
| 78 |
-
print("\n" + "=" * 60)
|
| 79 |
-
print("🔄 TESTE 3: BACKEND → MODELS VIA CLIENT")
|
| 80 |
-
print("=" * 60)
|
| 81 |
-
|
| 82 |
-
try:
|
| 83 |
-
# Initialize client with explicit URL
|
| 84 |
-
async with ModelsClient(base_url=MODELS_API_URL) as client:
|
| 85 |
-
|
| 86 |
-
# Test anomaly detection
|
| 87 |
-
print("🧠 Testando detecção de anomalias...")
|
| 88 |
-
|
| 89 |
-
# Sample data for testing
|
| 90 |
-
test_data = {
|
| 91 |
-
"transaction_amount": 150000.00,
|
| 92 |
-
"vendor_name": "Tech Solutions LTDA",
|
| 93 |
-
"contract_type": "informática",
|
| 94 |
-
"transaction_date": "2024-08-18"
|
| 95 |
-
}
|
| 96 |
-
|
| 97 |
-
result = await client.detect_anomaly(test_data)
|
| 98 |
-
|
| 99 |
-
if result:
|
| 100 |
-
print("✅ Comunicação Backend → Models OK!")
|
| 101 |
-
print(f" 🎯 Resultado: {result}")
|
| 102 |
-
return True
|
| 103 |
-
else:
|
| 104 |
-
print("❌ Nenhum resultado retornado")
|
| 105 |
-
return False
|
| 106 |
-
|
| 107 |
-
except Exception as e:
|
| 108 |
-
print(f"❌ Erro na comunicação: {str(e)}")
|
| 109 |
-
return False
|
| 110 |
-
|
| 111 |
-
async def test_models_specific_endpoints():
|
| 112 |
-
"""🎯 TESTE 4: Endpoints específicos de modelos"""
|
| 113 |
-
print("\n" + "=" * 60)
|
| 114 |
-
print("🎯 TESTE 4: ENDPOINTS ESPECÍFICOS DE MODELOS")
|
| 115 |
-
print("=" * 60)
|
| 116 |
-
|
| 117 |
-
endpoints_to_test = [
|
| 118 |
-
"/models/anomaly/detect",
|
| 119 |
-
"/models/pattern/analyze",
|
| 120 |
-
"/models/spectral/analyze"
|
| 121 |
-
]
|
| 122 |
-
|
| 123 |
-
results = {}
|
| 124 |
-
|
| 125 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 126 |
-
for endpoint in endpoints_to_test:
|
| 127 |
-
try:
|
| 128 |
-
url = f"{MODELS_API_URL}{endpoint}"
|
| 129 |
-
print(f"📡 Testando: {endpoint}")
|
| 130 |
-
|
| 131 |
-
# Sample request data
|
| 132 |
-
test_payload = {
|
| 133 |
-
"data": [1, 2, 3, 4, 5],
|
| 134 |
-
"params": {"threshold": 0.8}
|
| 135 |
-
}
|
| 136 |
-
|
| 137 |
-
response = await client.post(url, json=test_payload)
|
| 138 |
-
|
| 139 |
-
if response.status_code == 200:
|
| 140 |
-
print(f" ✅ {endpoint} - OK")
|
| 141 |
-
results[endpoint] = "OK"
|
| 142 |
-
elif response.status_code == 422:
|
| 143 |
-
print(f" ⚠️ {endpoint} - Schema validation (normal)")
|
| 144 |
-
results[endpoint] = "Schema OK"
|
| 145 |
-
else:
|
| 146 |
-
print(f" ❌ {endpoint} - Status: {response.status_code}")
|
| 147 |
-
results[endpoint] = f"Error: {response.status_code}"
|
| 148 |
-
|
| 149 |
-
except Exception as e:
|
| 150 |
-
print(f" ❌ {endpoint} - Erro: {str(e)}")
|
| 151 |
-
results[endpoint] = f"Exception: {str(e)}"
|
| 152 |
-
|
| 153 |
-
return results
|
| 154 |
-
|
| 155 |
-
async def test_backend_api_integration():
|
| 156 |
-
"""🏛️ TESTE 5: Backend API usando Models internamente"""
|
| 157 |
-
print("\n" + "=" * 60)
|
| 158 |
-
print("🏛️ TESTE 5: BACKEND API - INTEGRAÇÃO COM MODELS")
|
| 159 |
-
print("=" * 60)
|
| 160 |
-
|
| 161 |
-
try:
|
| 162 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 163 |
-
# Test investigation endpoint (should use models internally)
|
| 164 |
-
print("🔍 Testando investigação (usa models internamente)...")
|
| 165 |
-
|
| 166 |
-
payload = {
|
| 167 |
-
"query": "Analisar contratos de informática com valores suspeitos",
|
| 168 |
-
"data_source": "contracts",
|
| 169 |
-
"max_results": 10
|
| 170 |
-
}
|
| 171 |
-
|
| 172 |
-
response = await client.post(
|
| 173 |
-
f"{BACKEND_API_URL}/api/agents/zumbi/investigate",
|
| 174 |
-
json=payload
|
| 175 |
-
)
|
| 176 |
-
|
| 177 |
-
if response.status_code == 200:
|
| 178 |
-
data = response.json()
|
| 179 |
-
print("✅ Backend API funcionando!")
|
| 180 |
-
print(f" 🔍 Status: {data.get('status', 'N/A')}")
|
| 181 |
-
print(f" 📊 Anomalias: {data.get('anomalies_found', 'N/A')}")
|
| 182 |
-
print(f" ⏱️ Tempo: {data.get('processing_time_ms', 'N/A')}ms")
|
| 183 |
-
return True
|
| 184 |
-
else:
|
| 185 |
-
print(f"❌ Backend API erro: {response.status_code}")
|
| 186 |
-
return False
|
| 187 |
-
|
| 188 |
-
except Exception as e:
|
| 189 |
-
print(f"❌ Erro no Backend API: {str(e)}")
|
| 190 |
-
return False
|
| 191 |
-
|
| 192 |
-
async def run_communication_tests():
|
| 193 |
-
"""🚀 Executar todos os testes de comunicação"""
|
| 194 |
-
print("🧪 TESTE DE COMUNICAÇÃO CIDADÃO.AI BACKEND ↔ MODELS")
|
| 195 |
-
print("🕐 Iniciado em:", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
| 196 |
-
print()
|
| 197 |
-
|
| 198 |
-
results = {
|
| 199 |
-
"models_api_direct": await test_models_api_direct(),
|
| 200 |
-
"models_health": await test_models_health(),
|
| 201 |
-
"backend_to_models": await test_backend_to_models(),
|
| 202 |
-
"models_endpoints": await test_models_specific_endpoints(),
|
| 203 |
-
"backend_integration": await test_backend_api_integration()
|
| 204 |
-
}
|
| 205 |
-
|
| 206 |
-
# Summary
|
| 207 |
-
print("\n" + "=" * 60)
|
| 208 |
-
print("📊 RESUMO DOS TESTES")
|
| 209 |
-
print("=" * 60)
|
| 210 |
-
|
| 211 |
-
total_tests = len(results)
|
| 212 |
-
passed_tests = sum(1 for v in results.values() if v is True or (isinstance(v, dict) and any("OK" in str(val) for val in v.values())))
|
| 213 |
-
|
| 214 |
-
for test_name, result in results.items():
|
| 215 |
-
status = "✅ PASSOU" if result is True else "📊 DETALHES" if isinstance(result, dict) else "❌ FALHOU"
|
| 216 |
-
print(f" {test_name}: {status}")
|
| 217 |
-
|
| 218 |
-
if isinstance(result, dict):
|
| 219 |
-
for endpoint, endpoint_result in result.items():
|
| 220 |
-
emoji = "✅" if "OK" in str(endpoint_result) else "❌"
|
| 221 |
-
print(f" {emoji} {endpoint}: {endpoint_result}")
|
| 222 |
-
|
| 223 |
-
print(f"\n🎯 RESULTADO GERAL: {passed_tests}/{total_tests} testes funcionais")
|
| 224 |
-
|
| 225 |
-
if passed_tests == total_tests:
|
| 226 |
-
print("🎉 COMUNICAÇÃO BACKEND ↔ MODELS TOTALMENTE FUNCIONAL!")
|
| 227 |
-
elif passed_tests > 0:
|
| 228 |
-
print("⚠️ COMUNICAÇÃO PARCIALMENTE FUNCIONAL - Verificar issues")
|
| 229 |
-
else:
|
| 230 |
-
print("❌ COMUNICAÇÃO NÃO FUNCIONAL - Verificar deployment")
|
| 231 |
-
|
| 232 |
-
return results
|
| 233 |
-
|
| 234 |
-
if __name__ == "__main__":
|
| 235 |
-
asyncio.run(run_communication_tests())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_models_endpoints.py
DELETED
|
@@ -1,142 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
🧪 Teste dos Endpoints da Models API
|
| 4 |
-
Verifica quando os endpoints ML estão disponíveis
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import httpx
|
| 9 |
-
import json
|
| 10 |
-
from datetime import datetime
|
| 11 |
-
|
| 12 |
-
# Models API URL
|
| 13 |
-
MODELS_URL = "https://neural-thinker-cidadao-ai-models.hf.space"
|
| 14 |
-
|
| 15 |
-
async def test_endpoints():
|
| 16 |
-
"""🔍 Testa todos os endpoints da Models API"""
|
| 17 |
-
print("🧪 TESTE DOS ENDPOINTS DA MODELS API")
|
| 18 |
-
print("=" * 50)
|
| 19 |
-
print(f"🔗 Base URL: {MODELS_URL}")
|
| 20 |
-
print(f"🕐 Teste iniciado: {datetime.now().strftime('%H:%M:%S')}")
|
| 21 |
-
print()
|
| 22 |
-
|
| 23 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 24 |
-
|
| 25 |
-
# 1. Health Check
|
| 26 |
-
print("1️⃣ HEALTH CHECK")
|
| 27 |
-
try:
|
| 28 |
-
response = await client.get(f"{MODELS_URL}/health")
|
| 29 |
-
data = response.json()
|
| 30 |
-
print(f" Status: {data.get('status')}")
|
| 31 |
-
print(f" Models loaded: {data.get('models_loaded')}")
|
| 32 |
-
print(f" Message: {data.get('message')}")
|
| 33 |
-
|
| 34 |
-
if data.get('models_loaded') == True:
|
| 35 |
-
print(" ✅ Models API está COMPLETA!")
|
| 36 |
-
else:
|
| 37 |
-
print(" ⚠️ Models API em modo fallback")
|
| 38 |
-
except Exception as e:
|
| 39 |
-
print(f" ❌ Erro: {str(e)}")
|
| 40 |
-
|
| 41 |
-
# 2. Documentação
|
| 42 |
-
print("\n2️⃣ DOCUMENTAÇÃO")
|
| 43 |
-
print(f" 📚 Swagger UI: {MODELS_URL}/docs")
|
| 44 |
-
print(f" 📋 OpenAPI JSON: {MODELS_URL}/openapi.json")
|
| 45 |
-
|
| 46 |
-
# 3. Endpoints ML
|
| 47 |
-
print("\n3️⃣ ENDPOINTS DE ML")
|
| 48 |
-
|
| 49 |
-
# Anomaly Detection
|
| 50 |
-
print("\n 🔍 DETECÇÃO DE ANOMALIAS")
|
| 51 |
-
print(f" POST {MODELS_URL}/v1/detect-anomalies")
|
| 52 |
-
try:
|
| 53 |
-
test_data = {
|
| 54 |
-
"contracts": [
|
| 55 |
-
{
|
| 56 |
-
"id": "TEST-001",
|
| 57 |
-
"vendor": "Empresa Teste LTDA",
|
| 58 |
-
"amount": 50000.00,
|
| 59 |
-
"date": "2025-08-18",
|
| 60 |
-
"category": "Serviços de TI"
|
| 61 |
-
}
|
| 62 |
-
],
|
| 63 |
-
"threshold": 0.7
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
response = await client.post(
|
| 67 |
-
f"{MODELS_URL}/v1/detect-anomalies",
|
| 68 |
-
json=test_data
|
| 69 |
-
)
|
| 70 |
-
|
| 71 |
-
if response.status_code == 200:
|
| 72 |
-
result = response.json()
|
| 73 |
-
print(f" ✅ Endpoint funcional!")
|
| 74 |
-
print(f" 📊 Anomalias encontradas: {result.get('anomalies_found', 0)}")
|
| 75 |
-
elif response.status_code == 404:
|
| 76 |
-
print(f" ❌ Endpoint não encontrado (Models em fallback)")
|
| 77 |
-
else:
|
| 78 |
-
print(f" ⚠️ Status: {response.status_code}")
|
| 79 |
-
|
| 80 |
-
except Exception as e:
|
| 81 |
-
print(f" ❌ Erro: {str(e)[:50]}...")
|
| 82 |
-
|
| 83 |
-
# Pattern Analysis
|
| 84 |
-
print("\n 📊 ANÁLISE DE PADRÕES")
|
| 85 |
-
print(f" POST {MODELS_URL}/v1/analyze-patterns")
|
| 86 |
-
try:
|
| 87 |
-
test_data = {
|
| 88 |
-
"data": {
|
| 89 |
-
"time_series": [100, 120, 90, 150, 200, 180],
|
| 90 |
-
"categories": ["A", "B", "A", "C", "B", "A"]
|
| 91 |
-
},
|
| 92 |
-
"analysis_type": "temporal"
|
| 93 |
-
}
|
| 94 |
-
|
| 95 |
-
response = await client.post(
|
| 96 |
-
f"{MODELS_URL}/v1/analyze-patterns",
|
| 97 |
-
json=test_data
|
| 98 |
-
)
|
| 99 |
-
|
| 100 |
-
if response.status_code == 200:
|
| 101 |
-
result = response.json()
|
| 102 |
-
print(f" ✅ Endpoint funcional!")
|
| 103 |
-
print(f" 📈 Padrões encontrados: {result.get('pattern_count', 0)}")
|
| 104 |
-
elif response.status_code == 404:
|
| 105 |
-
print(f" ❌ Endpoint não encontrado (Models em fallback)")
|
| 106 |
-
else:
|
| 107 |
-
print(f" ⚠️ Status: {response.status_code}")
|
| 108 |
-
|
| 109 |
-
except Exception as e:
|
| 110 |
-
print(f" ❌ Erro: {str(e)[:50]}...")
|
| 111 |
-
|
| 112 |
-
# Spectral Analysis
|
| 113 |
-
print("\n 🌊 ANÁLISE ESPECTRAL")
|
| 114 |
-
print(f" POST {MODELS_URL}/v1/analyze-spectral")
|
| 115 |
-
try:
|
| 116 |
-
test_data = {
|
| 117 |
-
"time_series": [1, 2, 3, 2, 1, 2, 3, 2, 1],
|
| 118 |
-
"sampling_rate": 1.0
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
response = await client.post(
|
| 122 |
-
f"{MODELS_URL}/v1/analyze-spectral",
|
| 123 |
-
json=test_data
|
| 124 |
-
)
|
| 125 |
-
|
| 126 |
-
if response.status_code == 200:
|
| 127 |
-
result = response.json()
|
| 128 |
-
print(f" ✅ Endpoint funcional!")
|
| 129 |
-
print(f" 🎵 Frequência dominante: {result.get('dominant_frequency', 'N/A')}")
|
| 130 |
-
elif response.status_code == 404:
|
| 131 |
-
print(f" ❌ Endpoint não encontrado (Models em fallback)")
|
| 132 |
-
else:
|
| 133 |
-
print(f" ⚠️ Status: {response.status_code}")
|
| 134 |
-
|
| 135 |
-
except Exception as e:
|
| 136 |
-
print(f" ❌ Erro: {str(e)[:50]}...")
|
| 137 |
-
|
| 138 |
-
print("\n" + "=" * 50)
|
| 139 |
-
print("🏁 Teste concluído!")
|
| 140 |
-
|
| 141 |
-
if __name__ == "__main__":
|
| 142 |
-
asyncio.run(test_endpoints())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_models_only.py
DELETED
|
@@ -1,125 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
🧪 Teste Simples - Apenas Models API
|
| 4 |
-
Testa apenas a API de modelos sem dependências do backend
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import httpx
|
| 9 |
-
import json
|
| 10 |
-
from datetime import datetime
|
| 11 |
-
|
| 12 |
-
# Models API URL (confirmed working)
|
| 13 |
-
MODELS_URL = "https://neural-thinker-cidadao-ai-models.hf.space"
|
| 14 |
-
|
| 15 |
-
async def test_models_api():
|
| 16 |
-
"""🤖 Teste completo da Models API"""
|
| 17 |
-
print("🤖 TESTE DA CIDADÃO.AI MODELS API")
|
| 18 |
-
print("=" * 50)
|
| 19 |
-
print(f"🔗 URL: {MODELS_URL}")
|
| 20 |
-
print(f"🕐 Iniciado em: {datetime.now().strftime('%H:%M:%S')}")
|
| 21 |
-
print()
|
| 22 |
-
|
| 23 |
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
| 24 |
-
|
| 25 |
-
# 1. Root endpoint
|
| 26 |
-
print("1️⃣ TESTANDO ROOT ENDPOINT")
|
| 27 |
-
try:
|
| 28 |
-
response = await client.get(f"{MODELS_URL}/")
|
| 29 |
-
if response.status_code == 200:
|
| 30 |
-
data = response.json()
|
| 31 |
-
print(" ✅ Root endpoint OK")
|
| 32 |
-
print(f" 📊 API: {data.get('api', 'N/A')}")
|
| 33 |
-
print(f" 🔢 Version: {data.get('version', 'N/A')}")
|
| 34 |
-
print(f" 📋 Status: {data.get('status', 'N/A')}")
|
| 35 |
-
else:
|
| 36 |
-
print(f" ❌ Root: {response.status_code}")
|
| 37 |
-
except Exception as e:
|
| 38 |
-
print(f" ❌ Root error: {str(e)}")
|
| 39 |
-
|
| 40 |
-
# 2. Health check
|
| 41 |
-
print("\n2️⃣ TESTANDO HEALTH CHECK")
|
| 42 |
-
try:
|
| 43 |
-
response = await client.get(f"{MODELS_URL}/health")
|
| 44 |
-
if response.status_code == 200:
|
| 45 |
-
data = response.json()
|
| 46 |
-
print(" ✅ Health check OK")
|
| 47 |
-
print(f" 📊 Status: {data.get('status', 'N/A')}")
|
| 48 |
-
print(f" 🤖 Models loaded: {data.get('models_loaded', 'N/A')}")
|
| 49 |
-
print(f" 💬 Message: {data.get('message', 'N/A')}")
|
| 50 |
-
else:
|
| 51 |
-
print(f" ❌ Health: {response.status_code}")
|
| 52 |
-
except Exception as e:
|
| 53 |
-
print(f" ❌ Health error: {str(e)}")
|
| 54 |
-
|
| 55 |
-
# 3. Test docs endpoint
|
| 56 |
-
print("\n3️⃣ TESTANDO DOCUMENTAÇÃO")
|
| 57 |
-
try:
|
| 58 |
-
response = await client.get(f"{MODELS_URL}/docs")
|
| 59 |
-
if response.status_code == 200:
|
| 60 |
-
print(" ✅ Docs available")
|
| 61 |
-
print(f" 📝 Content-Type: {response.headers.get('content-type', 'N/A')}")
|
| 62 |
-
else:
|
| 63 |
-
print(f" ❌ Docs: {response.status_code}")
|
| 64 |
-
except Exception as e:
|
| 65 |
-
print(f" ❌ Docs error: {str(e)}")
|
| 66 |
-
|
| 67 |
-
# 4. Test spaces-info
|
| 68 |
-
print("\n4️⃣ TESTANDO SPACES INFO")
|
| 69 |
-
try:
|
| 70 |
-
response = await client.get(f"{MODELS_URL}/spaces-info")
|
| 71 |
-
if response.status_code == 200:
|
| 72 |
-
data = response.json()
|
| 73 |
-
print(" ✅ Spaces info OK")
|
| 74 |
-
print(f" 🏠 Space ID: {data.get('space_id', 'N/A')}")
|
| 75 |
-
print(f" 👤 Author: {data.get('space_author', 'N/A')}")
|
| 76 |
-
print(f" 📦 Platform: {data.get('platform', 'N/A')}")
|
| 77 |
-
print(f" 🤖 Models available: {data.get('models_available', 'N/A')}")
|
| 78 |
-
else:
|
| 79 |
-
print(f" ❌ Spaces info: {response.status_code}")
|
| 80 |
-
except Exception as e:
|
| 81 |
-
print(f" ❌ Spaces info error: {str(e)}")
|
| 82 |
-
|
| 83 |
-
# 5. Test model endpoints (if available)
|
| 84 |
-
print("\n5️⃣ TESTANDO ENDPOINTS DE MODELO")
|
| 85 |
-
|
| 86 |
-
model_endpoints = [
|
| 87 |
-
"/v1/detect-anomalies",
|
| 88 |
-
"/v1/analyze-patterns",
|
| 89 |
-
"/v1/analyze-spectral"
|
| 90 |
-
]
|
| 91 |
-
|
| 92 |
-
for endpoint in model_endpoints:
|
| 93 |
-
try:
|
| 94 |
-
# Test with minimal payload
|
| 95 |
-
test_payload = {
|
| 96 |
-
"contracts": [{"value": 1000, "vendor": "test"}],
|
| 97 |
-
"threshold": 0.7
|
| 98 |
-
} if "anomalies" in endpoint else {
|
| 99 |
-
"data": [1, 2, 3, 4, 5],
|
| 100 |
-
"params": {"test": True}
|
| 101 |
-
}
|
| 102 |
-
|
| 103 |
-
response = await client.post(f"{MODELS_URL}{endpoint}", json=test_payload)
|
| 104 |
-
|
| 105 |
-
if response.status_code == 200:
|
| 106 |
-
print(f" ✅ {endpoint} - Functional")
|
| 107 |
-
elif response.status_code == 422:
|
| 108 |
-
print(f" 📋 {endpoint} - Schema validation (endpoint exists)")
|
| 109 |
-
elif response.status_code == 404:
|
| 110 |
-
print(f" ❌ {endpoint} - Not found")
|
| 111 |
-
else:
|
| 112 |
-
print(f" ⚠️ {endpoint} - Status: {response.status_code}")
|
| 113 |
-
|
| 114 |
-
except Exception as e:
|
| 115 |
-
print(f" ❌ {endpoint} - Error: {str(e)[:50]}...")
|
| 116 |
-
|
| 117 |
-
print("\n" + "=" * 50)
|
| 118 |
-
print("🎯 RESUMO")
|
| 119 |
-
print("✅ Models API está ONLINE e acessível")
|
| 120 |
-
print("🔗 URL funcional:", MODELS_URL)
|
| 121 |
-
print("📚 Documentação:", f"{MODELS_URL}/docs")
|
| 122 |
-
print("🏥 Health check:", f"{MODELS_URL}/health")
|
| 123 |
-
|
| 124 |
-
if __name__ == "__main__":
|
| 125 |
-
asyncio.run(test_models_api())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_quick_connectivity.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
⚡ Teste Rápido de Conectividade
|
| 4 |
-
Verifica rapidamente se os serviços estão online
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import httpx
|
| 9 |
-
|
| 10 |
-
# URLs dos serviços
|
| 11 |
-
BACKEND_URL = "https://neural-thinker-cidadao-ai-backend.hf.space"
|
| 12 |
-
MODELS_URL = "https://neural-thinker-cidadao-ai-models.hf.space"
|
| 13 |
-
|
| 14 |
-
async def quick_test():
|
| 15 |
-
"""🚀 Teste rápido de conectividade"""
|
| 16 |
-
print("⚡ TESTE RÁPIDO DE CONECTIVIDADE")
|
| 17 |
-
print("=" * 50)
|
| 18 |
-
|
| 19 |
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
| 20 |
-
|
| 21 |
-
# Test Backend
|
| 22 |
-
print(f"🔍 Testando Backend: {BACKEND_URL}")
|
| 23 |
-
try:
|
| 24 |
-
response = await client.get(f"{BACKEND_URL}")
|
| 25 |
-
if response.status_code == 200:
|
| 26 |
-
data = response.json()
|
| 27 |
-
print(f" ✅ Backend ONLINE - {data.get('status', 'N/A')}")
|
| 28 |
-
print(f" 🤖 Agentes: {list(data.get('agents', {}).keys())}")
|
| 29 |
-
else:
|
| 30 |
-
print(f" ❌ Backend retornou: {response.status_code}")
|
| 31 |
-
except Exception as e:
|
| 32 |
-
print(f" ❌ Backend OFFLINE: {str(e)}")
|
| 33 |
-
|
| 34 |
-
# Test Models
|
| 35 |
-
print(f"🤖 Testando Models: {MODELS_URL}")
|
| 36 |
-
try:
|
| 37 |
-
response = await client.get(f"{MODELS_URL}/")
|
| 38 |
-
if response.status_code == 200:
|
| 39 |
-
data = response.json()
|
| 40 |
-
print(f" ✅ Models ONLINE - {data.get('api', 'N/A')}")
|
| 41 |
-
else:
|
| 42 |
-
print(f" ❌ Models retornou: {response.status_code}")
|
| 43 |
-
except Exception as e:
|
| 44 |
-
print(f" ❌ Models OFFLINE: {str(e)}")
|
| 45 |
-
|
| 46 |
-
# Test Backend → Models integration (via backend status)
|
| 47 |
-
print(f"🔄 Testando Integração via Backend Status:")
|
| 48 |
-
try:
|
| 49 |
-
response = await client.get(f"{BACKEND_URL}/api/status")
|
| 50 |
-
if response.status_code == 200:
|
| 51 |
-
data = response.json()
|
| 52 |
-
cache_info = data.get('performance', {}).get('cache', {})
|
| 53 |
-
print(f" ✅ Backend Status OK")
|
| 54 |
-
print(f" 📊 Cache: {cache_info.get('total_entries', 0)} entries")
|
| 55 |
-
print(f" 🎯 API Version: {data.get('version', 'N/A')}")
|
| 56 |
-
else:
|
| 57 |
-
print(f" ❌ Backend Status: {response.status_code}")
|
| 58 |
-
except Exception as e:
|
| 59 |
-
print(f" ❌ Backend Status Error: {str(e)}")
|
| 60 |
-
|
| 61 |
-
if __name__ == "__main__":
|
| 62 |
-
asyncio.run(quick_test())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_stable_endpoint.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Test the new stable chat endpoint locally
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import asyncio
|
| 7 |
-
import httpx
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
|
| 10 |
-
async def test_stable_endpoint():
|
| 11 |
-
"""Test the stable chat endpoint"""
|
| 12 |
-
|
| 13 |
-
# Test messages covering all scenarios
|
| 14 |
-
test_cases = [
|
| 15 |
-
# Greetings
|
| 16 |
-
{"message": "Olá, tudo bem?", "expected_intent": "greeting"},
|
| 17 |
-
{"message": "Boa tarde!", "expected_intent": "greeting"},
|
| 18 |
-
|
| 19 |
-
# Investigations
|
| 20 |
-
{"message": "Quero investigar contratos do Ministério da Saúde", "expected_intent": "investigation"},
|
| 21 |
-
{"message": "Buscar licitações suspeitas em São Paulo", "expected_intent": "investigation"},
|
| 22 |
-
|
| 23 |
-
# Analysis
|
| 24 |
-
{"message": "Analise os gastos com educação em 2024", "expected_intent": "analysis"},
|
| 25 |
-
{"message": "Faça uma análise dos fornecedores do governo", "expected_intent": "analysis"},
|
| 26 |
-
|
| 27 |
-
# Help
|
| 28 |
-
{"message": "Como você pode me ajudar?", "expected_intent": "help"},
|
| 29 |
-
{"message": "O que você faz?", "expected_intent": "help"},
|
| 30 |
-
|
| 31 |
-
# Complex questions
|
| 32 |
-
{"message": "Existe algum padrão suspeito nos contratos de TI dos últimos 6 meses?", "expected_intent": "investigation/analysis"},
|
| 33 |
-
{"message": "Quais foram os maiores gastos do governo federal este ano?", "expected_intent": "analysis"},
|
| 34 |
-
]
|
| 35 |
-
|
| 36 |
-
print("🧪 Testing Stable Chat Endpoint")
|
| 37 |
-
print("=" * 60)
|
| 38 |
-
|
| 39 |
-
# Test locally first
|
| 40 |
-
base_url = "http://localhost:8000"
|
| 41 |
-
|
| 42 |
-
async with httpx.AsyncClient(timeout=10.0) as client:
|
| 43 |
-
# Check if server is running
|
| 44 |
-
try:
|
| 45 |
-
health = await client.get(f"{base_url}/health")
|
| 46 |
-
print(f"✅ Local server is running: {health.status_code}")
|
| 47 |
-
except:
|
| 48 |
-
print("❌ Local server not running. Please start with: make run-dev")
|
| 49 |
-
return
|
| 50 |
-
|
| 51 |
-
print("\n📊 Testing various message types:")
|
| 52 |
-
print("-" * 60)
|
| 53 |
-
|
| 54 |
-
success_count = 0
|
| 55 |
-
total_tests = len(test_cases)
|
| 56 |
-
|
| 57 |
-
for i, test in enumerate(test_cases, 1):
|
| 58 |
-
print(f"\n Test {i}/{total_tests}")
|
| 59 |
-
print(f"📤 Message: {test['message']}")
|
| 60 |
-
print(f"🎯 Expected: {test['expected_intent']}")
|
| 61 |
-
|
| 62 |
-
try:
|
| 63 |
-
start_time = datetime.now()
|
| 64 |
-
response = await client.post(
|
| 65 |
-
f"{base_url}/api/v1/chat/stable",
|
| 66 |
-
json={
|
| 67 |
-
"message": test["message"],
|
| 68 |
-
"session_id": f"test-{i}"
|
| 69 |
-
}
|
| 70 |
-
)
|
| 71 |
-
duration = (datetime.now() - start_time).total_seconds() * 1000
|
| 72 |
-
|
| 73 |
-
if response.status_code == 200:
|
| 74 |
-
data = response.json()
|
| 75 |
-
print(f"✅ Success in {duration:.0f}ms")
|
| 76 |
-
print(f"🤖 Agent: {data['agent_name']}")
|
| 77 |
-
print(f"💬 Response: {data['message'][:100]}...")
|
| 78 |
-
print(f"📊 Confidence: {data['confidence']:.2f}")
|
| 79 |
-
print(f"🔧 Backend: {data['metadata'].get('agent_used', 'unknown')}")
|
| 80 |
-
success_count += 1
|
| 81 |
-
else:
|
| 82 |
-
print(f"❌ Failed: {response.status_code}")
|
| 83 |
-
print(f"Error: {response.text}")
|
| 84 |
-
|
| 85 |
-
except Exception as e:
|
| 86 |
-
print(f"❌ Exception: {str(e)}")
|
| 87 |
-
|
| 88 |
-
print("\n" + "=" * 60)
|
| 89 |
-
print(f"📈 Results: {success_count}/{total_tests} successful ({success_count/total_tests*100:.0f}%)")
|
| 90 |
-
|
| 91 |
-
if success_count == total_tests:
|
| 92 |
-
print("🎉 Perfect! 100% success rate!")
|
| 93 |
-
elif success_count >= total_tests * 0.9:
|
| 94 |
-
print("✅ Excellent! Above 90% success rate")
|
| 95 |
-
else:
|
| 96 |
-
print("⚠️ Needs improvement - below 90% success rate")
|
| 97 |
-
|
| 98 |
-
if __name__ == "__main__":
|
| 99 |
-
asyncio.run(test_stable_endpoint())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|