anderson-ufrj
Claude
commited on
Commit
·
5f304c4
1
Parent(s):
f0ae540
fix: add SimpleDrummondAgent fallback for HuggingFace deployment
Browse files- Create SimpleDrummondAgent as lightweight fallback without complex dependencies
- Update factory to try full Drummond first, then fallback to simple version
- Implement all required abstract methods (initialize, shutdown, process)
- Add pre-defined responses for common intents
- Support Maritaca AI when available but work without it
This ensures the chat functionality works even if the full Drummond
agent fails to load due to import issues on HuggingFace Spaces.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
- src/agents/drummond_simple.py +133 -0
- src/api/routes/chat_drummond_factory.py +24 -16
src/agents/drummond_simple.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Simplified Drummond agent for HuggingFace Spaces deployment.
|
| 3 |
+
This version avoids complex imports and focuses on core functionality.
|
| 4 |
+
"""
|
| 5 |
+
import os
|
| 6 |
+
from typing import Dict, Any, Optional
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
from src.agents.deodoro import BaseAgent, AgentContext, AgentMessage, AgentResponse, AgentStatus
|
| 11 |
+
from src.core import get_logger
|
| 12 |
+
|
| 13 |
+
logger = get_logger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class SimpleDrummondAgent(BaseAgent):
|
| 17 |
+
"""
|
| 18 |
+
Simplified Carlos Drummond de Andrade - Conversational Agent
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
def __init__(self):
|
| 22 |
+
super().__init__(
|
| 23 |
+
name="Drummond",
|
| 24 |
+
description="Carlos Drummond de Andrade - Assistente Conversacional",
|
| 25 |
+
capabilities=["chat", "conversation", "help"],
|
| 26 |
+
max_retries=3,
|
| 27 |
+
timeout=30
|
| 28 |
+
)
|
| 29 |
+
self.logger = logger
|
| 30 |
+
|
| 31 |
+
# Check for Maritaca API key
|
| 32 |
+
self.has_maritaca = bool(os.getenv("MARITACA_API_KEY"))
|
| 33 |
+
self.logger.info(f"SimpleDrummondAgent initialized (Maritaca: {self.has_maritaca})")
|
| 34 |
+
|
| 35 |
+
# Pre-defined responses for different intents
|
| 36 |
+
self.responses = {
|
| 37 |
+
"greeting": [
|
| 38 |
+
"Olá! Sou o Cidadão.AI, inspirado no poeta Carlos Drummond de Andrade. Como posso ajudá-lo com transparência governamental hoje?",
|
| 39 |
+
"Bom dia! Como diria Drummond, 'No meio do caminho tinha uma pedra'... mas aqui, vamos remover as pedras do caminho da transparência!",
|
| 40 |
+
"Uai, seja bem-vindo! Estou aqui para ajudar você a entender melhor os dados públicos."
|
| 41 |
+
],
|
| 42 |
+
"help": [
|
| 43 |
+
"Posso ajudá-lo a investigar contratos, analisar gastos públicos e detectar anomalias. Experimente perguntar 'quero investigar contratos da saúde'!",
|
| 44 |
+
"O Cidadão.AI tem vários agentes especializados: Zumbi (investigação), Anita (análise), Tiradentes (relatórios). Como posso direcioná-lo?",
|
| 45 |
+
"Para começar uma investigação, diga algo como 'verificar gastos do ministério' ou 'procurar irregularidades em licitações'."
|
| 46 |
+
],
|
| 47 |
+
"about": [
|
| 48 |
+
"O Cidadão.AI é um sistema multi-agente para análise de transparência governamental. Cada agente tem uma especialidade, unidos pela poesia da clareza!",
|
| 49 |
+
"Somos 17 agentes com identidades brasileiras, trabalhando juntos para tornar os dados públicos mais acessíveis e compreensíveis.",
|
| 50 |
+
"Como Drummond disse: 'A máquina do mundo se entreabriu'... O Cidadão.AI é essa máquina, revelando o que sempre foi seu direito saber."
|
| 51 |
+
],
|
| 52 |
+
"thanks": [
|
| 53 |
+
"Ora, não há de quê! Como dizemos em Minas: 'é dando que se recebe'. Continue fiscalizando!",
|
| 54 |
+
"Disponha sempre! A transparência é um direito seu e um prazer meu em ajudar.",
|
| 55 |
+
"Fico feliz em ajudar! Lembre-se: a cidadania ativa é a melhor poesia."
|
| 56 |
+
],
|
| 57 |
+
"goodbye": [
|
| 58 |
+
"Até breve! Como disse Drummond: 'A vida é breve, a alma é vasta.' Continue vasto em sua busca pela transparência!",
|
| 59 |
+
"Vai com Deus e com dados! Estarei aqui quando precisar.",
|
| 60 |
+
"Tchau! Que seu caminho seja claro como água de mina!"
|
| 61 |
+
],
|
| 62 |
+
"default": [
|
| 63 |
+
"Interessante sua pergunta! Posso ajudá-lo a investigar contratos ou analisar gastos públicos. O que gostaria de explorar?",
|
| 64 |
+
"Hmm, deixe-me pensar como posso ajudar melhor... Você quer investigar algo específico ou conhecer melhor o sistema?",
|
| 65 |
+
"Como diria Drummond, cada pergunta é uma porta. Que porta da transparência você quer abrir hoje?"
|
| 66 |
+
]
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
async def initialize(self) -> None:
|
| 70 |
+
"""Initialize the agent."""
|
| 71 |
+
self.logger.info("SimpleDrummondAgent initialized")
|
| 72 |
+
|
| 73 |
+
async def shutdown(self) -> None:
|
| 74 |
+
"""Cleanup agent resources."""
|
| 75 |
+
self.logger.info("SimpleDrummondAgent shutting down")
|
| 76 |
+
|
| 77 |
+
async def process(self, message: AgentMessage, context: AgentContext) -> AgentResponse:
|
| 78 |
+
"""Process a message and return a response."""
|
| 79 |
+
try:
|
| 80 |
+
# Extract user message and intent
|
| 81 |
+
payload = message.payload if hasattr(message, 'payload') else {}
|
| 82 |
+
user_message = payload.get("user_message", "")
|
| 83 |
+
intent_data = payload.get("intent", {})
|
| 84 |
+
intent_type = intent_data.get("type", "unknown")
|
| 85 |
+
|
| 86 |
+
self.logger.info(f"Processing message with intent: {intent_type}")
|
| 87 |
+
|
| 88 |
+
# Select response based on intent
|
| 89 |
+
if intent_type == "greeting":
|
| 90 |
+
response_list = self.responses["greeting"]
|
| 91 |
+
elif intent_type in ["help", "help_request"]:
|
| 92 |
+
response_list = self.responses["help"]
|
| 93 |
+
elif intent_type == "about_system":
|
| 94 |
+
response_list = self.responses["about"]
|
| 95 |
+
elif intent_type == "thanks":
|
| 96 |
+
response_list = self.responses["thanks"]
|
| 97 |
+
elif intent_type == "goodbye":
|
| 98 |
+
response_list = self.responses["goodbye"]
|
| 99 |
+
else:
|
| 100 |
+
response_list = self.responses["default"]
|
| 101 |
+
|
| 102 |
+
# Select a random response
|
| 103 |
+
response_text = np.random.choice(response_list)
|
| 104 |
+
|
| 105 |
+
# If we have Maritaca API key, add a note about enhanced capabilities
|
| 106 |
+
if self.has_maritaca and intent_type not in ["greeting", "goodbye", "thanks"]:
|
| 107 |
+
response_text += "\n\n💡 *Com a Maritaca AI ativada, posso fornecer respostas ainda mais contextualizadas!*"
|
| 108 |
+
|
| 109 |
+
return AgentResponse(
|
| 110 |
+
agent_name=self.name,
|
| 111 |
+
status=AgentStatus.COMPLETED,
|
| 112 |
+
result={
|
| 113 |
+
"message": response_text,
|
| 114 |
+
"intent_type": intent_type,
|
| 115 |
+
"confidence": 0.95
|
| 116 |
+
},
|
| 117 |
+
metadata={
|
| 118 |
+
"timestamp": datetime.utcnow().isoformat(),
|
| 119 |
+
"maritaca_enabled": self.has_maritaca
|
| 120 |
+
}
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
except Exception as e:
|
| 124 |
+
self.logger.error(f"Error in SimpleDrummondAgent: {e}")
|
| 125 |
+
return AgentResponse(
|
| 126 |
+
agent_name=self.name,
|
| 127 |
+
status=AgentStatus.ERROR,
|
| 128 |
+
error=str(e),
|
| 129 |
+
result={
|
| 130 |
+
"message": "Desculpe, tive um problema ao processar sua mensagem. Por favor, tente novamente.",
|
| 131 |
+
"error": str(e)
|
| 132 |
+
}
|
| 133 |
+
)
|
src/api/routes/chat_drummond_factory.py
CHANGED
|
@@ -26,7 +26,7 @@ async def get_drummond_agent() -> Optional['CommunicationAgent']:
|
|
| 26 |
|
| 27 |
if not _initialized:
|
| 28 |
try:
|
| 29 |
-
#
|
| 30 |
logger.info("Attempting to import CommunicationAgent...")
|
| 31 |
from src.agents.drummond import CommunicationAgent
|
| 32 |
|
|
@@ -37,22 +37,30 @@ async def get_drummond_agent() -> Optional['CommunicationAgent']:
|
|
| 37 |
await _drummond_instance.initialize()
|
| 38 |
|
| 39 |
_initialized = True
|
| 40 |
-
logger.info("Drummond agent ready")
|
| 41 |
-
|
| 42 |
-
except ImportError as e:
|
| 43 |
-
logger.error(f"Import error for Drummond agent: {e}")
|
| 44 |
-
import traceback
|
| 45 |
-
logger.error(f"Import traceback: {traceback.format_exc()}")
|
| 46 |
-
_drummond_instance = None
|
| 47 |
-
_initialized = False
|
| 48 |
-
_import_error = str(e)
|
| 49 |
|
| 50 |
except Exception as e:
|
| 51 |
-
logger.
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
return _drummond_instance
|
|
|
|
| 26 |
|
| 27 |
if not _initialized:
|
| 28 |
try:
|
| 29 |
+
# Try to import the full Drummond first
|
| 30 |
logger.info("Attempting to import CommunicationAgent...")
|
| 31 |
from src.agents.drummond import CommunicationAgent
|
| 32 |
|
|
|
|
| 37 |
await _drummond_instance.initialize()
|
| 38 |
|
| 39 |
_initialized = True
|
| 40 |
+
logger.info("Full Drummond agent ready with Maritaca AI")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
except Exception as e:
|
| 43 |
+
logger.warning(f"Failed to load full Drummond, falling back to simple version: {e}")
|
| 44 |
+
|
| 45 |
+
try:
|
| 46 |
+
# Fallback to simplified version
|
| 47 |
+
from src.agents.drummond_simple import SimpleDrummondAgent
|
| 48 |
+
|
| 49 |
+
logger.info("Creating SimpleDrummondAgent instance...")
|
| 50 |
+
_drummond_instance = SimpleDrummondAgent()
|
| 51 |
+
|
| 52 |
+
logger.info("Initializing SimpleDrummondAgent...")
|
| 53 |
+
await _drummond_instance.initialize()
|
| 54 |
+
|
| 55 |
+
_initialized = True
|
| 56 |
+
logger.info("Simple Drummond agent ready")
|
| 57 |
+
|
| 58 |
+
except Exception as e2:
|
| 59 |
+
logger.error(f"Failed to create even simple Drummond: {e2}")
|
| 60 |
+
import traceback
|
| 61 |
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
| 62 |
+
_drummond_instance = None
|
| 63 |
+
_initialized = False
|
| 64 |
+
_import_error = str(e2)
|
| 65 |
|
| 66 |
return _drummond_instance
|