""" ═══════════════════════════════════════════════════════════════════════ PATCH ESPECÍFICO PARA api.py — APLICAR LINHA POR LINHA ═══════════════════════════════════════════════════════════════════════ Este arquivo mostra as MODIFICAÇÕES EXATAS no api.py existente. Arquivo: i:\\Isaac Quarenta\\Programação\\AKIRA-SOFTEDGE\\modules\\api.py MODIFICATIONS: 1. Adicionar imports (top do arquivo) 2. Modificar _get_user_context() (line 2311) 3. Modificar _get_history_for_llm() (line 2320+) 4. Modificar akira_endpoint() - parte de obtém histórico ═══════════════════════════════════════════════════════════════════════ """ # ═══════════════════════════════════════════════════════════════════════ # MODIFICATION 1: ADICIONAR IMPORTS (TOP DO ARQUIVO) # ═══════════════════════════════════════════════════════════════════════ """ Localização: Próximo aos outros imports do modules/ ANTES: ──────────────────────────────────────────────────────────────── import json import hashlib import logging from skills_registry import SkillsRegistry ... DEPOIS (ADICIONAR ESTAS LINHAS): ──────────────────────────────────────────────────────────────── import json import hashlib import logging from skills_registry import SkillsRegistry # ✅ NOVOS IMPORTS PARA CONTEXT V2 from modules.context_manager_v2 import ( ContextManagerV2, get_context_manager, MessageType, ContextType ) from modules.listen_stream_processor import ( ListenStreamProcessor, get_listen_processor ) # Inicializa singletons ctx_manager = get_context_manager() listen_processor = get_listen_processor() ... """ # ═══════════════════════════════════════════════════════════════════════ # MODIFICATION 2: ATUALIZAR _get_user_context (line 2311) # ═══════════════════════════════════════════════════════════════════════ """ Localização: api.py::_get_user_context (line 2311) ANTES: ──────────────────────────────────────────────────────────────── def _get_user_context(self, usuario, conversation_id=None): # 🔧 FIX: Usa conversation_id como chave primária para isolamento total cache_key = conversation_id if conversation_id else usuario if cache_key not in self.contexto_cache: db_path = getattr(self.config, 'DB_PATH', 'akira.db') db = Database(db_path) # Passa conversation_id para o objeto Contexto para persistência isolada self.contexto_cache[cache_key] = Contexto(db, usuario=usuario, conversation_id=conversation_id) return self.contexto_cache[cache_key] DEPOIS: ──────────────────────────────────────────────────────────────── def _get_user_context(self, usuario, numero, conversation_id=None, tipo_conversa='pv', grupo_id=None): # 🔧 FIX V2: Usa novo ContextManagerV2 para isolamento robusto # Legacy: mantém cache para compatibilidade cache_key = conversation_id if conversation_id else usuario if cache_key not in self.contexto_cache: db_path = getattr(self.config, 'DB_PATH', 'akira.db') db = Database(db_path) self.contexto_cache[cache_key] = Contexto(db, usuario=usuario, conversation_id=conversation_id) # NOVO: Retorna contexto isolado via ContextManagerV2 contexto_isolado = ctx_manager.obter_ou_criar_contexto( numero=numero, tipo_conversa=tipo_conversa, grupo_id=grupo_id, usuario=usuario ) # Retorna ambos para compatibilidade backward return { 'legacy_contexto': self.contexto_cache[cache_key], # Compatibilidade 'novo_contexto_manager': contexto_isolado, 'numero': numero, 'tipo_conversa': tipo_conversa, 'grupo_id': grupo_id, } NOTA: A assinatura da função MUDA. Precisa ser atualizada em TODOS os lugares que chamam _get_user_context() """ # ═══════════════════════════════════════════════════════════════════════ # MODIFICATION 3: INTEGRAR LISTEN STREAM EM akira_endpoint # ═══════════════════════════════════════════════════════════════════════ """ Localização: api.py::akira_endpoint() - após extrair dados (line ~1250) ANTES (buggy): ──────────────────────────────────────────────────────────────── usuario = validate_sender_name(usuario, numero, "usuario_principal") ... # Captura contexto aqui (BUGGY - sem isolação) contexto = self._get_user_context(usuario) # Histórico SEM FILTRO (PROBLEMA!) historico = contexto.obter_historico() # Process LLM... DEPOIS (robusto): ──────────────────────────────────────────────────────────────── usuario = validate_sender_name(usuario, numero, "usuario_principal") ... # ✅ NOVO: PROCESSA VIA LISTEN STREAM evento = { 'usuario': usuario, 'numero': numero, 'texto': mensagem, 'tipo_conversa': tipo_conversa, 'grupo_id': grupo_id, 'referenced_message_author': quoted_author_name, 'referenced_message_texto': mensagem_citada, 'referenced_message_id': data.get('message_id_citada', ''), } resultado_processamento = listen_processor.processar_mensagem_chegando(evento) self.logger.info( f"🔍 Classificação Listen: {resultado_processamento['tipo_message']} | " f"Deve processar: {resultado_processamento['deve_processar']}" ) # ✅ SE NÃO FOR DIRETO, NÃO PROCESSA if not resultado_processamento['deve_processar']: self.logger.info(f"📚 Mensagem contextual (escuta). Não respondendo.") return jsonify({ 'resposta': '', 'status': 'context_registered', 'tipo': 'contextual', 'modelo_usado': 'listen_stream', 'conversation_id': resultado_processamento['conversation_id'] }) # ✅ É DIRETO: Obtém contexto ISOLADO conversation_id = resultado_processamento['conversation_id'] contexto_data = listen_processor.obter_contexto_para_resposta( numero=numero, tipo_conversa=tipo_conversa, grupo_id=grupo_id, include_contextual=True ) # Extrai histórico ISOLADO historico = contexto_data['direct_messages'] # Context enriquecido com fluxo do grupo contexto_grupo_info = contexto_data.get('grupo_flow', {}) participants = contexto_data.get('participants', []) # Se em grupo, inclui info de participantes if tipo_conversa == "grupo": self.logger.info(f"👥 Contexto grupo: {len(participants)} participantes") # LEGACY: obtém também via contexto_cache para compatibilidade contexto = self._get_user_context( usuario=usuario, numero=numero, conversation_id=conversation_id, tipo_conversa=tipo_conversa, grupo_id=grupo_id ) # Process LLM... """ # ═══════════════════════════════════════════════════════════════════════ # MODIFICATION 4: ACEITAR NOVOS CAMPOS NO PAYLOAD # ═══════════════════════════════════════════════════════════════════════ """ Localização: api.py::akira_endpoint() - seção data extraction (line ~1240) ANTES: ──────────────────────────────────────────────────────────────── usuario = data.get('usuario', 'anonimo') numero = data.get('numero', '') mensagem = data.get('mensagem', '') message_id = data.get('message_id', '') tipo_conversa = data.get('tipo_conversa', 'pv') grupo_id = data.get('grupo_id') or data.get('contexto_grupo') or '' imagem_dados = data.get('imagem', {}) ... DEPOIS (adiciona suporte): ──────────────────────────────────────────────────────────────── usuario = data.get('usuario', 'anonimo') numero = data.get('numero', '') mensagem = data.get('mensagem', '') message_id = data.get('message_id', '') tipo_conversa = data.get('tipo_conversa', 'pv') grupo_id = data.get('grupo_id') or data.get('contexto_grupo') or '' # ✅ NOVOS CAMPOS PARA LISTEN STREAM # Referência a outra mensagem (reply) referenced_message_author = data.get('referenced_message_author', data.get('quoted_author_name', '')) referenced_message_texto = data.get('referenced_message_texto', data.get('mensagem_citada', '')) referenced_message_id = data.get('referenced_message_id', data.get('message_id_citada', '')) imagem_dados = data.get('imagem', {}) ... """ # ═══════════════════════════════════════════════════════════════════════ # MODIFICATION 5: ATUALIZAR PAYLOAD DO RESPOSTA # ═══════════════════════════════════════════════════════════════════════ """ Localização: api.py::akira_endpoint() - retorno jsonify (line ~1400+) ADICIONAR CAMPO: ──────────────────────────────────────────────────────────────── return jsonify({ 'resposta': resposta, 'modelo_usado': modelo, 'confidence': confidence, 'conversation_id': conversation_id, # ✅ NOVO 'tipo_message': resultado_processamento['tipo_message'], # ✅ NOVO 'participants': participants if tipo_conversa == 'grupo' else [], # ✅ NOVO ... }) """ # ═══════════════════════════════════════════════════════════════════════ # QUICK CHECKLIST # ═══════════════════════════════════════════════════════════════════════ """ ANTES DE APLICAR ESTAS MUDANÇAS: □ Verificar que context_manager_v2.py existe e está correto □ Verificar que listen_stream_processor.py existe e está correto □ Fazer BACKUP do api.py ANTES DE MODIFICAR □ Testar com um grupo de teste (não produção) APÓS APLICAR: □ Testar endpoint POST /akira com payload novo □ Testar com tipo_conversa='pv' (deve ser DIRECT) □ Testar com tipo_conversa='grupo' sem @AKIRA (deve ser CONTEXTUAL) □ Testar com tipo_conversa='grupo' com @AKIRA (deve ser DIRECT) □ Verificar logs para "🔍 Classificação Listen" □ Verificar que histórico é isolado por conversation_id □ Monitorar para memory leaks no context_manager (stats) TROUBLESHOOTING: ❌ ImportError: context_manager_v2 → Verificar que arquivo está em modules/ → Verificar import path ❌ NameError: ctx_manager not defined → Verificar que get_context_manager() foi chamado no top → Verificar que imports foram adicionados ❌ Contexto ainda está misturado → Verificar que listen_processor.processar_mensagem_chegando() foi integrado → Verificar que resultado_processamento['deve_processar'] é respeitado → Verificar conversation_id está sendo passado corretamente """ __all__ = [ 'PATCH_INSTRUCTIONS' ]