""" Stable Chat API with multiple fallback layers Ensures 100% availability with Maritaca AI integration """ import os from datetime import datetime from typing import Dict, Any, Optional, List from fastapi import APIRouter, HTTPException, Depends from pydantic import BaseModel, Field import httpx from src.core import get_logger logger = get_logger(__name__) from src.services.maritaca_client import MaritacaClient, MaritacaModel from src.services.chat_service import IntentDetector, IntentType from src.services.chat_data_integration import chat_data_integration router = APIRouter(prefix="/api/v1/chat") @router.get("/health") async def chat_stable_health(): """Simple health check for chat stable router""" return {"status": "ok", "router": "chat_stable", "portal_integration": "enabled"} # Initialize services with lazy loading maritaca_client = None intent_detector = None def get_maritaca_client(): """Lazy load Maritaca client""" global maritaca_client if maritaca_client is None: api_key = os.getenv("MARITACA_API_KEY") if api_key: maritaca_client = MaritacaClient( api_key=api_key, model=MaritacaModel.SABIAZINHO_3 # Using efficient model for cost optimization ) return maritaca_client def get_intent_detector(): """Lazy load intent detector""" global intent_detector if intent_detector is None: intent_detector = IntentDetector() return intent_detector # Request/Response models class ChatRequest(BaseModel): message: str = Field(..., min_length=1, max_length=1000) session_id: Optional[str] = None context: Optional[Dict[str, Any]] = None class ChatResponse(BaseModel): session_id: str agent_id: str agent_name: str message: str confidence: float suggested_actions: Optional[List[str]] = None metadata: Dict[str, Any] = {} # Fallback responses for different scenarios FALLBACK_RESPONSES = { IntentType.GREETING: [ "Olá! Sou seu assistente de transparência pública. Como posso ajudar você hoje?", "Oi! Estou aqui para ajudar com informações sobre gastos governamentais. Em que posso ser útil?", "Bem-vindo! Posso ajudar você a entender melhor os gastos públicos. O que gostaria de saber?" ], IntentType.INVESTIGATE: [ "Entendi que você quer investigar gastos públicos. Posso ajudar você a buscar informações sobre contratos, licitações e despesas governamentais. Qual área específica você gostaria de investigar?", "Vou ajudar você a investigar os gastos públicos. Temos dados sobre contratos, fornecedores e órgãos públicos. Por onde gostaria de começar?", "Perfeito! Posso analisar contratos e despesas públicas. Me diga qual órgão, período ou tipo de gasto você quer investigar." ], IntentType.ANALYZE: [ "Vou analisar essas informações para você. Nossa plataforma examina padrões de gastos, identifica anomalias e fornece insights sobre transparência pública.", "Certo, vou fazer uma análise detalhada. Posso examinar tendências, comparar fornecedores e identificar possíveis irregularidades nos dados públicos.", "Entendido! Farei uma análise completa dos dados solicitados, incluindo padrões de gastos e possíveis pontos de atenção." ], IntentType.HELP: [ "Posso ajudar você de várias formas:\n• Investigar contratos e licitações\n• Analisar gastos de órgãos públicos\n• Identificar padrões suspeitos\n• Comparar fornecedores\n• Gerar relatórios de transparência\n\nO que você gostaria de fazer?", "Aqui estão algumas coisas que posso fazer:\n• Buscar informações sobre contratos específicos\n• Analisar gastos por órgão ou período\n• Detectar anomalias em pagamentos\n• Rastrear histórico de fornecedores\n\nComo posso ajudar?", "Sou especializado em transparência pública. Posso:\n• Investigar despesas governamentais\n• Analisar contratos suspeitos\n• Monitorar licitações\n• Gerar insights sobre gastos públicos\n\nQual informação você precisa?" ], IntentType.REPORT: [ "Vou preparar um relatório detalhado com as informações solicitadas. O documento incluirá análises, gráficos e recomendações sobre os dados públicos.", "Certo! Prepararei um relatório completo com todos os dados relevantes, incluindo visualizações e insights sobre possíveis irregularidades.", "Entendido! Seu relatório será gerado com análise detalhada dos gastos, comparações relevantes e indicadores de transparência." ], IntentType.UNKNOWN: [ "Interessante sua pergunta! Como assistente de transparência pública, posso ajudar com investigações sobre gastos governamentais, contratos e licitações. Como posso ajudar você com isso?", "Não tenho certeza se entendi completamente. Posso ajudar você a investigar gastos públicos, analisar contratos ou buscar informações sobre transparência governamental. O que você gostaria de saber?", "Hmm, deixe-me reformular. Sou especializado em dados de transparência pública. Posso investigar contratos, analisar gastos de órgãos públicos ou identificar padrões suspeitos. Como posso ser útil?" ] } def get_fallback_response(intent_type: IntentType, context: Optional[Dict] = None) -> str: """Get appropriate fallback response based on intent""" import random responses = FALLBACK_RESPONSES.get(intent_type, FALLBACK_RESPONSES[IntentType.UNKNOWN]) return random.choice(responses) async def process_with_maritaca(message: str, intent_type: IntentType, session_id: str, context: Optional[Dict] = None) -> Dict[str, Any]: """Process message with Maritaca AI with multiple fallback layers and Portal da Transparência integration""" # Check if user is asking for specific government data message_lower = message.lower() data_keywords = ["contratos", "gastos", "despesas", "licitação", "fornecedor", "servidor", "órgão", "ministério", "prefeitura", "cnpj", "valor", "empresa"] should_fetch_data = any(keyword in message_lower for keyword in data_keywords) portal_data = None # If user is asking for data, fetch from Portal da Transparência if should_fetch_data and (intent_type in [IntentType.INVESTIGATE, IntentType.ANALYZE, IntentType.UNKNOWN]): try: logger.info(f"Fetching real data from Portal da Transparência for query: {message}") portal_result = await chat_data_integration.process_user_query(message, context) if portal_result and portal_result.get("data"): portal_data = portal_result # If we have data, return the formatted response directly if portal_result.get("response"): return { "message": portal_result["response"], "agent_used": "portal_transparencia_ai", "model": "sabiazinho-3", "success": True, "data": portal_result.get("data"), "entities": portal_result.get("entities"), "data_type": portal_result.get("data_type") } except Exception as e: logger.warning(f"Portal da Transparência integration failed: {str(e)}") # Layer 1: Try Maritaca AI client = get_maritaca_client() if client: try: # Prepare context based on intent system_prompt = """Você é um assistente especializado em transparência pública brasileira. Seu papel é ajudar cidadãos a entender e investigar gastos governamentais. Seja claro, objetivo e sempre forneça informações úteis sobre transparência fiscal. Quando apropriado, sugira ações específicas como investigar contratos ou analisar gastos.""" if intent_type == IntentType.INVESTIGATE: system_prompt += "\nO usuário quer investigar gastos. Seja específico sobre como você pode ajudar." elif intent_type == IntentType.ANALYZE: system_prompt += "\nO usuário quer uma análise. Explique que tipo de análise você pode fornecer." # Add portal data context if available if portal_data and portal_data.get("data"): system_prompt += f"\n\nDados reais do Portal da Transparência: {str(portal_data['data'])[:1000]}" messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": message} ] response = await client.chat_completion( messages=messages, temperature=0.7, max_tokens=300 ) if response: result = { "message": response.content if hasattr(response, 'content') else str(response), "agent_used": "maritaca_ai", "model": response.model if hasattr(response, 'model') else "sabiazinho-3", "success": True } # Add portal data if available if portal_data: result.update({ "data": portal_data.get("data"), "entities": portal_data.get("entities"), "data_type": portal_data.get("data_type") }) return result except Exception as e: logger.warning(f"Maritaca AI failed: {str(e)}") # Layer 2: Try simple HTTP request to Maritaca if os.getenv("MARITACA_API_KEY"): try: async with httpx.AsyncClient(timeout=5.0) as http_client: response = await http_client.post( "https://chat.maritaca.ai/api/chat/inference", headers={"authorization": f"Bearer {os.getenv('MARITACA_API_KEY')}"}, json={ "messages": [{"role": "user", "content": message}], "model": "sabiazinho-3", "temperature": 0.7 } ) if response.status_code == 200: data = response.json() result = { "message": data.get("answer", get_fallback_response(intent_type)), "agent_used": "maritaca_direct", "model": "sabiazinho-3", "success": True } if portal_data: result.update({ "data": portal_data.get("data"), "entities": portal_data.get("entities"), "data_type": portal_data.get("data_type") }) return result except Exception as e: logger.warning(f"Direct Maritaca request failed: {str(e)}") # Layer 3: Smart fallback based on intent result = { "message": get_fallback_response(intent_type, {"session_id": session_id}), "agent_used": "fallback_intelligent", "model": "rule_based", "success": True } if portal_data: result.update({ "data": portal_data.get("data"), "entities": portal_data.get("entities"), "data_type": portal_data.get("data_type") }) return result @router.post("/stable", response_model=ChatResponse) async def chat_stable(request: ChatRequest) -> ChatResponse: """ Ultra-stable chat endpoint with multiple fallback layers Guarantees response even if all AI services fail """ session_id = request.session_id or f"session_{datetime.now().timestamp()}" try: # Detect intent with fallback detector = get_intent_detector() if detector: try: intent = await detector.detect(request.message) intent_type = intent.type confidence = intent.confidence except: # Fallback intent detection message_lower = request.message.lower() if any(word in message_lower for word in ["oi", "olá", "bom dia", "boa tarde", "boa noite"]): intent_type = IntentType.GREETING elif any(word in message_lower for word in ["investigar", "verificar", "buscar", "procurar"]): intent_type = IntentType.INVESTIGATE elif any(word in message_lower for word in ["analisar", "análise", "examinar"]): intent_type = IntentType.ANALYZE elif any(word in message_lower for word in ["ajuda", "help", "como", "o que"]): intent_type = IntentType.HELP else: intent_type = IntentType.UNKNOWN confidence = 0.6 else: intent_type = IntentType.UNKNOWN confidence = 0.5 # Process with Maritaca and fallbacks result = await process_with_maritaca( message=request.message, intent_type=intent_type, session_id=session_id, context=request.context ) # Determine agent info based on intent agent_info = { IntentType.GREETING: ("drummond", "Carlos Drummond"), IntentType.INVESTIGATE: ("zumbi", "Zumbi dos Palmares"), IntentType.ANALYZE: ("anita", "Anita Garibaldi"), IntentType.HELP: ("drummond", "Carlos Drummond"), IntentType.REPORT: ("tiradentes", "Tiradentes"), IntentType.UNKNOWN: ("drummond", "Carlos Drummond") } agent_id, agent_name = agent_info.get(intent_type, ("drummond", "Carlos Drummond")) # Prepare suggested actions suggested_actions = { IntentType.GREETING: ["investigate_contracts", "view_recent_expenses", "help"], IntentType.INVESTIGATE: ["filter_by_date", "filter_by_agency", "view_suppliers"], IntentType.ANALYZE: ["generate_report", "view_charts", "compare_periods"], IntentType.HELP: ["start_investigation", "learn_more", "examples"], IntentType.REPORT: ["download_pdf", "share_report", "new_analysis"], IntentType.UNKNOWN: ["help", "examples", "start_investigation"] } # Build metadata with Portal data if available metadata = { "intent_type": intent_type.value, "processing_time": 0, "agent_used": result["agent_used"], "model": result["model"], "timestamp": datetime.utcnow().isoformat(), "stable_version": True } # Add Portal da Transparência data to metadata if available if result.get("data"): metadata["portal_data"] = { "type": result.get("data_type"), "entities_found": result.get("entities", {}), "total_records": result.get("data", {}).get("total", 0), "has_data": True } return ChatResponse( session_id=session_id, agent_id=agent_id, agent_name=agent_name, message=result["message"], confidence=confidence, suggested_actions=suggested_actions.get(intent_type, ["help"]), metadata=metadata ) except Exception as e: # Ultimate fallback - always return a valid response logger.error(f"Critical error in stable chat: {str(e)}") return ChatResponse( session_id=session_id, agent_id="system", agent_name="Sistema", message="Olá! Sou seu assistente de transparência pública. Estou aqui para ajudar você a investigar gastos governamentais, analisar contratos e entender melhor como o dinheiro público é utilizado. Como posso ajudar?", confidence=1.0, suggested_actions=["investigate_contracts", "view_expenses", "help"], metadata={ "error": str(e), "fallback": "ultimate", "timestamp": datetime.utcnow().isoformat() } ) @router.get("/test-portal/{query}") async def test_portal_integration(query: str): """ Test endpoint to verify Portal da Transparência integration Example: /api/v1/chat/test-portal/contratos%20ministerio%20saude """ try: result = await chat_data_integration.process_user_query(query) return { "success": True, "query": query, "data_type": result.get("data_type"), "entities_found": result.get("entities"), "total_records": result.get("data", {}).get("total", 0) if result.get("data") else 0, "response": result.get("response"), "sample_data": result.get("data", {}).get("dados", [])[:3] if result.get("data") else [] } except Exception as e: return { "success": False, "query": query, "error": str(e) } @router.get("/debug/portal-status") async def debug_portal_status(): """Debug endpoint to check Portal da Transparência configuration""" import os from src.core.config import settings # Check environment variable env_key = os.getenv("TRANSPARENCY_API_KEY") # Check settings settings_key = None if hasattr(settings, 'transparency_api_key') and settings.transparency_api_key: settings_key = "Configured" # Check service service_key = None if hasattr(chat_data_integration, 'portal') and chat_data_integration.portal.api_key: service_key = "Loaded" return { "env_variable": "Found" if env_key else "Not Found", "settings_config": settings_key or "Not Configured", "service_loaded": service_key or "Not Loaded", "portal_base_url": chat_data_integration.portal.BASE_URL if hasattr(chat_data_integration, 'portal') else "Not initialized" }