Isaac Quarenta commited on
Commit
5491f79
·
1 Parent(s): 0a5646b

fix(api): NameError explicit_mention_pattern em _build_context_isolation_layer

Browse files

- api.py:7311 define _explicit_mention_local via re.search local em vez de free variable (causava NameError: cannot access free variable em _build_prompt:7326 ao processar reply grupo)
- api.py:7334 usa _explicit_mention_local no state_msg
- Corrige crash 15:02:08 akira tenho duvida... que retornava 200 mas sem resposta

Files changed (2) hide show
  1. modules/api.py +11 -2
  2. modules/listen_engine.py +34 -9
modules/api.py CHANGED
@@ -4241,6 +4241,14 @@ class AkiraAPI:
4241
  "quoted_type": quoted_type,
4242
  "context_hint": context_hint,
4243
  "mensagem_citada": mensagem_citada,
 
 
 
 
 
 
 
 
4244
  "replied_to_author": reply_metadata.get('replied_to_author_name', ''),
4245
  "replied_to_content": reply_metadata.get('replied_to_text', '')
4246
  })
@@ -7300,7 +7308,8 @@ class AkiraAPI:
7300
  isolation_parts.append(reply_section)
7301
 
7302
  # SECAO 2: LSTM CONTEXT (Peso 0.8) — REPLY LOCK: suprime quando reply_to_bot sem menção explícita (evita contaminação ISPETEC vs ditado)
7303
- _lstm_locked = is_reply and reply_to_bot and not explicit_mention_pattern.search(mensagem)
 
7304
  if unified_context:
7305
  if _lstm_locked:
7306
  # Reply citado já tem peso 1.0 — não poluir com tópicos antigos do LSTM
@@ -7323,7 +7332,7 @@ class AkiraAPI:
7323
  lstm_section += "\n--- Topicos por Participante ---"
7324
  for speaker, msgs in speakers_topics.items():
7325
  lstm_section += f"\n[{speaker}]: {' | '.join(msgs[:3])}"
7326
- state_msg = f" - reply_to_bot={reply_to_bot}, explicit_mention={bool(explicit_mention_pattern.search(mensagem))}"
7327
  self.logger.info(f"✔... [LSTM INJETADO COM FOCO reply_to_bot={reply_to_bot}] topic={lstm_ctx.get('topic_principal')}{state_msg}")
7328
  isolation_parts.append(lstm_section)
7329
 
 
4241
  "quoted_type": quoted_type,
4242
  "context_hint": context_hint,
4243
  "mensagem_citada": mensagem_citada,
4244
+ # novos campos emissor / receptor
4245
+ "emissor_nome": reply_metadata.get('emissor_nome', ''),
4246
+ "emissor_numero": reply_metadata.get('emissor_numero', ''),
4247
+ "emissor_jid": reply_metadata.get('emissor_jid', ''),
4248
+ "receptor_nome": reply_metadata.get('receptor_nome', ''),
4249
+ "receptor_numero": reply_metadata.get('receptor_numero', ''),
4250
+ "receptor_jid": reply_metadata.get('receptor_jid', ''),
4251
+ "is_inter_user_reply": reply_metadata.get('is_inter_user_reply', False),
4252
  "replied_to_author": reply_metadata.get('replied_to_author_name', ''),
4253
  "replied_to_content": reply_metadata.get('replied_to_text', '')
4254
  })
 
7308
  isolation_parts.append(reply_section)
7309
 
7310
  # SECAO 2: LSTM CONTEXT (Peso 0.8) — REPLY LOCK: suprime quando reply_to_bot sem menção explícita (evita contaminação ISPETEC vs ditado)
7311
+ _explicit_mention_local = bool(re.search(r'você falou|daquela conversa|anteriormente|você disse|você mencionou', mensagem, re.IGNORECASE))
7312
+ _lstm_locked = is_reply and reply_to_bot and not _explicit_mention_local
7313
  if unified_context:
7314
  if _lstm_locked:
7315
  # Reply citado já tem peso 1.0 — não poluir com tópicos antigos do LSTM
 
7332
  lstm_section += "\n--- Topicos por Participante ---"
7333
  for speaker, msgs in speakers_topics.items():
7334
  lstm_section += f"\n[{speaker}]: {' | '.join(msgs[:3])}"
7335
+ state_msg = f" - reply_to_bot={reply_to_bot}, explicit_mention={_explicit_mention_local}"
7336
  self.logger.info(f"✔... [LSTM INJETADO COM FOCO reply_to_bot={reply_to_bot}] topic={lstm_ctx.get('topic_principal')}{state_msg}")
7337
  isolation_parts.append(lstm_section)
7338
 
modules/listen_engine.py CHANGED
@@ -22,14 +22,14 @@ class MensagemMetadata:
22
  # Identificadores (sem default — obrigatórios)
23
  msg_id: str # ID único da mensagem
24
  timestamp: float # Unix timestamp
25
-
26
  # Autoria (sem default — obrigatórios)
27
  author_id: str # Quem enviou (número/ID)
28
  author_nome: str # Nome do autor
29
-
30
  # Contexto de localização (sem default — obrigatório)
31
  grupo_id: str # ID do grupo (remoteJid)
32
-
33
  # Conteúdo (sem default — obrigatório)
34
  texto_original: str # Mensagem bruta
35
 
@@ -37,30 +37,38 @@ class MensagemMetadata:
37
  grupo_nome: str = "" # Nome do grupo (opcional)
38
  texto_processado: str = "" # Msg processada (se houver)
39
  tipo_mensagem: str = "texto" # texto, media, reply, mention, command
40
-
41
  # FLAGS CRÍTICAS - DETERMINAM RESPOSTA
42
  is_directed_to_bot: bool = False # Mensagem direcionada ao bot?
43
  is_mention_to_bot: bool = False # Menciona "@Akira" ou "Akira, "
44
  is_reply_to_bot: bool = False # Resposta (reply) à mensagem anterior do bot
45
  is_command_to_bot: bool = False # Começa com #, /, $
46
  is_privileged_user: bool = False # Usuário privilegiado (Isaac)
47
-
48
  # RESPOSTA REQUERIDA? (ANY of above flags)
49
  requer_resposta: bool = field(default=False, init=False)
50
-
51
  # ℹ CONTEXTO PURO - Sempre armazenar
52
  reply_to_msg_id: Optional[str] = None # Se é reply, ID da msg anterior
53
  reply_to_author_id: Optional[str] = None # Quem enviou a msg anterior
54
  reply_to_texto: Optional[str] = None # Preview do texto anterior
55
-
 
 
 
 
 
 
 
 
56
  # Análise
57
  emocao_detectada: str = "neutral"
58
  confianca_emocao: float = 0.5
59
  nivel_importancia: str = "low" # low, medium, high
60
-
61
  # Proatividade
62
  should_proact: bool = False # Mensagem precisa de ação proativa do bot?
63
-
64
  # Privacidade
65
  eh_privado: bool = False
66
  eh_grupo: bool = True
@@ -72,6 +80,23 @@ class MensagemMetadata:
72
  self.is_mention_to_bot or
73
  self.is_reply_to_bot
74
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  @dataclass
77
  class ContextoGrupo:
 
22
  # Identificadores (sem default — obrigatórios)
23
  msg_id: str # ID único da mensagem
24
  timestamp: float # Unix timestamp
25
+
26
  # Autoria (sem default — obrigatórios)
27
  author_id: str # Quem enviou (número/ID)
28
  author_nome: str # Nome do autor
29
+
30
  # Contexto de localização (sem default — obrigatório)
31
  grupo_id: str # ID do grupo (remoteJid)
32
+
33
  # Conteúdo (sem default — obrigatório)
34
  texto_original: str # Mensagem bruta
35
 
 
37
  grupo_nome: str = "" # Nome do grupo (opcional)
38
  texto_processado: str = "" # Msg processada (se houver)
39
  tipo_mensagem: str = "texto" # texto, media, reply, mention, command
40
+
41
  # FLAGS CRÍTICAS - DETERMINAM RESPOSTA
42
  is_directed_to_bot: bool = False # Mensagem direcionada ao bot?
43
  is_mention_to_bot: bool = False # Menciona "@Akira" ou "Akira, "
44
  is_reply_to_bot: bool = False # Resposta (reply) à mensagem anterior do bot
45
  is_command_to_bot: bool = False # Começa com #, /, $
46
  is_privileged_user: bool = False # Usuário privilegiado (Isaac)
47
+
48
  # RESPOSTA REQUERIDA? (ANY of above flags)
49
  requer_resposta: bool = field(default=False, init=False)
50
+
51
  # ℹ CONTEXTO PURO - Sempre armazenar
52
  reply_to_msg_id: Optional[str] = None # Se é reply, ID da msg anterior
53
  reply_to_author_id: Optional[str] = None # Quem enviou a msg anterior
54
  reply_to_texto: Optional[str] = None # Preview do texto anterior
55
+
56
+ # EMISSOR → RECEPTOR (Inter-user reply / passive listen)
57
+ # Quem enviou a msg atual (emissor) e a quem ela responde (receptor)
58
+ emissor_nome: Optional[str] = None # Nome do remetente da mensagem atual
59
+ emissor_id: Optional[str] = None # ID do remetente da mensagem atual
60
+ receptor_nome: Optional[str] = None # Nome de quem recebe o reply (autor msg citada)
61
+ receptor_id: Optional[str] = None # ID de quem recebe o reply
62
+ is_inter_user_reply: bool = False # True se é reply entre usuários (não ao bot)
63
+
64
  # Análise
65
  emocao_detectada: str = "neutral"
66
  confianca_emocao: float = 0.5
67
  nivel_importancia: str = "low" # low, medium, high
68
+
69
  # Proatividade
70
  should_proact: bool = False # Mensagem precisa de ação proativa do bot?
71
+
72
  # Privacidade
73
  eh_privado: bool = False
74
  eh_grupo: bool = True
 
80
  self.is_mention_to_bot or
81
  self.is_reply_to_bot
82
  )
83
+ # Se for reply inter‑user (não ao bot), garantir que os campos emissor/receptor estejam preenchidos
84
+ if self.is_reply_to_bot is False and self.reply_to_author_id:
85
+ # emissor = autor da mensagem atual
86
+ self.emissor_nome = self.emissor_nome or self.author_nome
87
+ self.emissor_id = self.emissor_id or self.author_id
88
+ # receptor = autor da mensagem citada
89
+ self.receptor_nome = self.receptor_nome # name lookup via dict (já temos o ID)
90
+ self.receptor_id = self.receptor_id or self.reply_to_author_id
91
+ self.is_inter_user_reply = True
92
+ else:
93
+ self.is_inter_user_reply = False
94
+ # Para mensagens que não são reply, deixar emissor/receptor como None
95
+ if not self.reply_to_author_id:
96
+ self.emissor_nome = self.emissor_nome or self.author_nome
97
+ self.emissor_id = self.emissor_id or self.author_id
98
+ self.receptor_nome = None
99
+ self.receptor_id = None
100
 
101
  @dataclass
102
  class ContextoGrupo: