Spaces:
Running
Running
| """ | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| INTEGRATION GUIDE β COMO INTEGRAR NO api.py EXISTENTE | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Este arquivo mostra: | |
| 1. Como modificar o endpoint POST /akira existente | |
| 2. Como usar ContextManagerV2 | |
| 3. Como usar ListenStreamProcessor | |
| 4. Exemplos prΓ‘ticos de fluxo | |
| IMPORTANTE: Isso SUBSTITUI o sistema antigo de context, mantendo | |
| compatibilidade com code existente. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # π¦ IMPORTS NECESSΓRIOS | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| from modules.context_manager_v2 import ( | |
| ContextManagerV2, | |
| get_context_manager, | |
| MessageType | |
| ) | |
| from modules.listen_stream_processor import ( | |
| ListenStreamProcessor, | |
| get_listen_processor | |
| ) | |
| from typing import Dict, Any | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| ctx_manager = get_context_manager() | |
| listen_processor = get_listen_processor() | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # π NOVO FLUXO DO ENDPOINT /akira | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| ANTES (buggy): | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| POST /akira | |
| { | |
| "usuario": "Isaac", | |
| "numero": "202391978787009", | |
| "texto": "@AKIRA qual Γ© a capital?" | |
| } | |
| β api.py chama obter_historico() | |
| β PROBLEMA: Retorna ALL mensagens, misturando grupos | |
| β AKIRA responde sem isolaΓ§Γ£o | |
| DEPOIS (robusto): | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| POST /akira | |
| { | |
| "usuario": "Isaac", | |
| "numero": "202391978787009", | |
| "texto": "@AKIRA qual Γ© a capital?", | |
| "tipo_conversa": "grupo" | "pv", | |
| "grupo_id": "g120363392399993499" (opcional), | |
| "referenced_message_author": "AKIRA", (opcional) | |
| } | |
| β listen_processor.processar_mensagem_chegando() | |
| β Classifica como DIRECT ou CONTEXTUAL | |
| β Adiciona Γ context_manager isolado por conversation_id | |
| β Se DIRECT (true): | |
| β ctx_manager.obter_historico_direto() | |
| β APENAS mensagens direcionadas a AKIRA | |
| β Isolado por conversation_id | |
| β AKIRA responde | |
| β Se CONTEXTUAL (false): | |
| β Registra mas NΓO processa | |
| β AKIRA entende fluxo via obter_contexto_grupo_amplificado() | |
| """ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # π§ MODIFICAΓΓO NO ENDPOINT /akira | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def novo_akira_endpoint_handler(request_data: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Novo handler para POST /akira com isolaΓ§Γ£o de contexto. | |
| Substitui a lΓ³gica antiga em api.py:akira_endpoint() | |
| """ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 1: EXTRAI DADOS CHEGANDO | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| usuario = request_data.get('usuario', 'desconhecido') | |
| numero = request_data.get('numero', '') | |
| texto = request_data.get('texto', '') | |
| tipo_conversa = request_data.get('tipo_conversa', 'pv') | |
| grupo_id = request_data.get('grupo_id') | |
| # ReferΓͺncia (se Γ© resposta a outra mensagem) | |
| referenced_author = request_data.get('referenced_message_author') | |
| referenced_texto = request_data.get('referenced_message_texto') | |
| referenced_id = request_data.get('referenced_message_id') | |
| logger.info( | |
| f"π¨ Mensagem chegando: {usuario} ({numero}) em {tipo_conversa} | " | |
| f"Texto: {texto[:50]}..." | |
| ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 2: PROCESSA VIA LISTEN STREAM | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| evento = { | |
| 'usuario': usuario, | |
| 'numero': numero, | |
| 'texto': texto, | |
| 'tipo_conversa': tipo_conversa, | |
| 'grupo_id': grupo_id, | |
| 'referenced_message_author': referenced_author, | |
| 'referenced_message_texto': referenced_texto, | |
| 'referenced_message_id': referenced_id, | |
| } | |
| resultado_processamento = listen_processor.processar_mensagem_chegando(evento) | |
| logger.info( | |
| f"π ClassificaΓ§Γ£o: {resultado_processamento['tipo_message']} | " | |
| f"Deve processar: {resultado_processamento['deve_processar']}" | |
| ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 3: DECIDE SE AKIRA RESPONDE | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if not resultado_processamento['deve_processar']: | |
| # Mensagem CONTEXTUAL β apenas registra, nΓ£o responde | |
| logger.info(f"π Mensagem contextual (escuta). ConversΓ£o_id: {resultado_processamento['conversation_id']}") | |
| return { | |
| 'status': 'context_registered', | |
| 'tipo': 'contextual', | |
| 'message': f"Entendi. {usuario} falou sobre isso no grupo.", | |
| 'deve_responder': False, | |
| 'conversation_id': resultado_processamento['conversation_id'], | |
| } | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 4: AKIRA RESPONDE (mensagem Γ© DIRETA) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| conversation_id = resultado_processamento['conversation_id'] | |
| # ObtΓ©m contexto ISOLADO | |
| contexto = listen_processor.obter_contexto_para_resposta( | |
| numero=numero, | |
| tipo_conversa=tipo_conversa, | |
| grupo_id=grupo_id, | |
| include_contextual=True | |
| ) | |
| logger.info( | |
| f"π Contexto obtido: {contexto['total_direct']} mensagens diretas | " | |
| f"Participantes: {contexto.get('participants', [])}" | |
| ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 5: ENRIQUECER COM CONTEXTO DE GRUPO | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| contexto_grupo_info = contexto.get('grupo_flow', {}) | |
| participants = contexto.get('participants', []) | |
| # Monta prompt para AKIRA | |
| prompt_system = f""" | |
| VocΓͺ Γ© AKIRA, assistente IA em um grupo. | |
| Contexto: | |
| - Participantes do grupo: {', '.join(participants) if participants else 'Desconhecido'} | |
| - TΓ³picos recentes: {', '.join(contexto.get('topics', [])) if contexto.get('topics') else 'VΓ‘rios'} | |
| - Sua conversa com {usuario}: {len(contexto['direct_messages'])} mensagens anteriores | |
| IMPORTANTE: Responda APENAS sobre o que foi perguntado a vocΓͺ. | |
| NΓO comente sobre outras conversas do grupo (escuta sem falar). | |
| """ | |
| # HistΓ³rico ISOLADO | |
| mensagens_anteriores = [ | |
| { | |
| 'usuario': m['usuario'], | |
| 'texto': m['texto'], | |
| 'timestamp': m['timestamp'] | |
| } | |
| for m in contexto['direct_messages'] | |
| ] | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 6: CHAMA LLM (seu cΓ³digo existente) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Isso seria integrado com seu chain LLM existente: | |
| # resposta = gerar_resposta_llm( | |
| # sistema=prompt_system, | |
| # historico=mensagens_anteriores, | |
| # mensagem_atual=texto, | |
| # usuario=usuario | |
| # ) | |
| resposta = f"[RESPOSTA DE AKIRA] VocΓͺ perguntou: {texto[:100]}" # Placeholder | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PASSO 7: RETORNA | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| return { | |
| 'status': 'success', | |
| 'tipo': 'direct', | |
| 'resposta': resposta, | |
| 'deve_responder': True, | |
| 'conversation_id': conversation_id, | |
| 'usuario': usuario, | |
| 'contexto_info': { | |
| 'num_direct_messages': contexto['total_direct'], | |
| 'participants': participants, | |
| 'topics': contexto.get('topics', []), | |
| } | |
| } | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # π ALTERAΓΓES NECESSΓRIAS NO api.py | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| LOCAIS ESPECΓFICOS A MODIFICAR EM api.py: | |
| 1οΈβ£ IMPORT SECTION (top do arquivo) | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Adicionar: | |
| from modules.context_manager_v2 import get_context_manager | |
| from modules.listen_stream_processor import get_listen_processor | |
| ctx_manager = get_context_manager() | |
| listen_processor = get_listen_processor() | |
| 2οΈβ£ FUNΓΓO akira_endpoint() - SUBSTITUIR INTEIRA | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LocalizaΓ§Γ£o: api.py::akira_endpoint() (aproximadamente linha 950) | |
| AΓ§Γ£o: SUBSTITUIR todo o corpo da funΓ§Γ£o pelo code do | |
| novo_akira_endpoint_handler() acima. | |
| O que muda: | |
| β Remove: data = contexto.obter_historico() (BUGGY - sem filtro) | |
| β Adiciona: resultado_processamento = listen_processor.processar_mensagem_chegando() | |
| β Adiciona: if not resultado_processamento['deve_processar']: return (skip contextual) | |
| β Adiciona: contexto = listen_processor.obter_contexto_para_resposta() (ISOLADO) | |
| 3οΈβ£ FUNΓΓO _get_user_context() - MODIFICAR | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LocalizaΓ§Γ£o: api.py::_get_user_context() (aproximadamente linha 1000) | |
| ANTES: | |
| def _get_user_context(numero, usuario): | |
| historico = contexto.obter_historico() # β SEM FILTRO | |
| return { | |
| 'historico': historico, | |
| 'usuario': usuario | |
| } | |
| DEPOIS: | |
| def _get_user_context(numero, usuario, tipo_conversa='pv', grupo_id=None): | |
| # Usa novo context manager ISOLADO | |
| historico = ctx_manager.obter_historico_direto( | |
| numero=numero, | |
| tipo_conversa=tipo_conversa, | |
| grupo_id=grupo_id, | |
| limit=50 | |
| ) | |
| contexto_grupo = ctx_manager.obter_contexto_grupo_amplificado( | |
| numero=numero, | |
| tipo_conversa=tipo_conversa, | |
| grupo_id=grupo_id | |
| ) if tipo_conversa == "grupo" else None | |
| return { | |
| 'historico': [m.to_dict() for m in historico], | |
| 'usuario': usuario, | |
| 'contexto_grupo': contexto_grupo, | |
| 'conversation_id': historico[0].conversation_id if historico else None | |
| } | |
| 4οΈβ£ FUNΓΓO obter_historico() - DEPRECAR OU SUBSTITUIR | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LocalizaΓ§Γ£o: database.py ou modules/database.py | |
| ANTES: | |
| def obter_historico(): | |
| return db.query(Mensagem).all() # β RETORNA TUDO | |
| DEPOIS: | |
| def obter_historico(conversation_id: str, msg_type: str = 'all'): | |
| # msg_type: 'direct', 'contextual', 'all' | |
| query = db.query(Mensagem).filter( | |
| Mensagem.conversation_id == conversation_id | |
| ) | |
| if msg_type == 'direct': | |
| query = query.filter(Mensagem.tipo == 'direct') | |
| elif msg_type == 'contextual': | |
| query = query.filter(Mensagem.tipo == 'contextual') | |
| return query.order_by(Mensagem.timestamp.desc()).limit(100).all() | |
| 5οΈβ£ SUPORTE AO PAYLOAD NOVO | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| O endpoint POST /akira AGORA ACEITA: | |
| { | |
| "usuario": "Isaac", | |
| "numero": "202391978787009", | |
| "texto": "@AKIRA qual Γ© a capital?", | |
| "tipo_conversa": "grupo", β NOVO | |
| "grupo_id": "g120363392399993499", β NOVO (opcional) | |
| "referenced_message_author": "StefΓ’nio", β NOVO (opcional) | |
| "referenced_message_texto": "Oi", β NOVO (opcional) | |
| "referenced_message_id": "msg_123" β NOVO (opcional) | |
| } | |
| Os campos 'tipo_conversa', 'grupo_id', 'referenced_*' sΓ£o opcionais | |
| e vΓͺm do discord-ts quando disponΓveis. Se nΓ£o vierem, valor default | |
| Γ© 'pv' (conversa privada). | |
| """ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # π EXEMPLO DE USO COMPLETO | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| CENΓRIO: Isaac e StefΓ’nio em grupo | |
| 1οΈβ£ ISAAC envia: "@AKIRA qual Γ© a capital de Portugal?" | |
| grupo: "g120363392399993499" | |
| β listen_processor classifica como DIRECT (menciona @AKIRA) | |
| β ctx_manager.adicionar_message_direta() com conversation_id ΓΊnico | |
| β AKIRA responde: "A capital Γ© Lisboa" | |
| 2οΈβ£ STEFΓNIO responde (sem @AKIRA): "Bacano, eu nΓ£o sabia" | |
| β listen_processor classifica como CONTEXTUAL | |
| β ctx_manager.adicionar_message_contextual() | |
| β AKIRA NΓO responde (nΓ£o foi direcionada a ela) | |
| β Mas AKIRA "entende" que StefΓ’nio achou legal | |
| 3οΈβ£ STEFΓNIO envia: "Qual Γ© a capital da FranΓ§a?" (sem @AKIRA) | |
| β listen_processor classifica como CONTEXTUAL | |
| β AKIRA nΓ£o responde | |
| 4οΈβ£ ISAAC responde: "@AKIRA? JΓ‘ que perguntou, qual Γ©?" | |
| β listen_processor classifica como DIRECT (menciona @AKIRA) | |
| β ObtΓ©m contexto ISOLADO (sΓ³ conversas diretas com Isaac) | |
| β AKIRA NΓO vΓͺ a pergunta de StefΓ’nio (contextual) | |
| β AKIRA responde baseada sΓ³ no que Isaac perguntou | |
| RESULTADO: β AKIRA entende quem fala o quΓͺ | |
| β NΓ£o mistura contextos | |
| β Responde apenas quando direcionada | |
| β Escuta o fluxo sem contaminar | |
| """ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # π― PRΓXIMOS PASSOS | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| TAREFAS PARA IMPLEMENTAR: | |
| 1. β Criar context_manager_v2.py (FEITO) | |
| 2. β Criar listen_stream_processor.py (FEITO) | |
| 3. π Modificar api.py: | |
| - Importar novos mΓ³dulos | |
| - Reescrever akira_endpoint() | |
| - Atualizar _get_user_context() | |
| 4. π Modificar database.py: | |
| - Adicionar suporte a conversation_id na query | |
| - Adicionar campo 'tipo' na tabela Mensagem | |
| 5. π Testar com fluxo real: | |
| - Isaac + StefΓ’nio em grupo | |
| - Verificar isolamento | |
| 6. π Integrar com discord-ts: | |
| - discord-ts deve enviar 'tipo_conversa' e 'grupo_id' | |
| - Enviar 'referenced_message_author' quando Γ© reply | |
| """ | |
| __all__ = [ | |
| 'novo_akira_endpoint_handler', | |
| 'ctx_manager', | |
| 'listen_processor', | |
| ] | |