akra35567 commited on
Commit
298452e
·
verified ·
1 Parent(s): 9a05dd5

Update modules/api.py

Browse files
Files changed (1) hide show
  1. modules/api.py +198 -86
modules/api.py CHANGED
@@ -1,12 +1,12 @@
1
- # modules/api.py — AKIRA V21 FINAL CORRIGIDO
2
  """
3
- ✅ TOTALMENTE ADAPTADO ao config.py BRUTAL
4
- ✅ TOTALMENTE COMPATÍVEL com index.js otimizado
5
  ✅ Sistema emocional DistilBERT
6
  ✅ Xingamento automático integrado
7
- ✅ Usa prompts de ironia máxima
8
- ✅ Processa reply_info do index.js corretamente
9
- ✅ CORREÇÃO: Instância correta sem parâmetros
10
  """
11
  import time
12
  import datetime
@@ -59,7 +59,7 @@ class SimpleTTLCache:
59
  return default
60
 
61
  # ============================================================================
62
- # 🧠 GERENCIADOR MULTI-API (OTIMIZADO)
63
  # ============================================================================
64
  class MultiAPIManager:
65
  def __init__(self):
@@ -90,11 +90,17 @@ class MultiAPIManager:
90
  reply_info: Optional[Dict] = None
91
  ) -> str:
92
  """
93
- Constrói prompt usando a função do config.py BRUTAL
94
- TOTALMENTE COMPATÍVEL com o index.js otimizado
95
  """
96
  try:
97
- # Usa a função principal do config.py (construir_prompt_api)
 
 
 
 
 
 
98
  prompt = config.construir_prompt_api(
99
  mensagem=mensagem,
100
  historico=historico,
@@ -105,15 +111,21 @@ class MultiAPIManager:
105
  reply_info=reply_info
106
  )
107
 
108
- logger.debug(f"📝 Prompt construído: {len(prompt)} caracteres")
 
 
 
 
109
  return prompt
110
 
111
  except Exception as e:
112
- logger.error(f"Erro ao construir prompt: {e}")
113
- # Fallback para prompt simples
114
  return f"""AKIRA V21 — PERSONALIDADE REAL SEM FILTRO
115
  Você é Akira, angolana de 18 anos de Luanda.
116
 
 
 
117
  USUÁRIO: {usuario}
118
  MENSAGEM: {mensagem}
119
 
@@ -124,7 +136,7 @@ RESPONDA COMO AKIRA (IRÔNICA, SEM FILTRO, 1-2 LINHAS):"""
124
  emocao = analise.get("emocao_primaria", "neutral")
125
  modo = analise.get("modo_resposta", "normal_ironico")
126
 
127
- # Usa função do config.py
128
  return config.obter_parametros_api(emocao, modo)
129
 
130
  def gerar_resposta(
@@ -137,8 +149,16 @@ RESPONDA COMO AKIRA (IRÔNICA, SEM FILTRO, 1-2 LINHAS):"""
137
  tipo_conversa: str,
138
  reply_info: Optional[Dict] = None
139
  ) -> str:
140
- """Gera resposta usando multi-API fallback"""
141
- # Construir prompt
 
 
 
 
 
 
 
 
142
  prompt = self._construir_prompt(
143
  mensagem=mensagem,
144
  historico=historico,
@@ -160,25 +180,19 @@ RESPONDA COMO AKIRA (IRÔNICA, SEM FILTRO, 1-2 LINHAS):"""
160
  continue
161
 
162
  try:
 
163
  resposta = self._chamar_api(api_name, prompt, parametros)
164
  if resposta:
165
  resposta_limpa = self._limpar_resposta(resposta)
166
- logger.debug(f"✅ API {api_name} respondeu: {resposta_limpa[:100]}...")
167
  return resposta_limpa
168
 
169
  except Exception as e:
170
- logger.debug(f"❌ API {api_name} falhou: {str(e)[:100]}")
171
  continue
172
 
173
- # Fallback: respostas curtas e brutais
174
- fallbacks = [
175
- "Puto, nem ligo.",
176
- "Foda-se.",
177
- "Ya, tá bom.",
178
- "Caralho.",
179
- "Não quero saber.",
180
- "Vai à merda."
181
- ]
182
  return random.choice(fallbacks)
183
 
184
  def _chamar_api(self, api_name: str, prompt: str, parametros: Dict[str, Any]) -> str:
@@ -285,18 +299,66 @@ RESPONDA COMO AKIRA (IRÔNICA, SEM FILTRO, 1-2 LINHAS):"""
285
  # Remove prefixos como "Akira:" ou "AKIRA:"
286
  texto = re.sub(r'^(Akira|AKIRA)[:\s\-]+', '', texto, flags=re.IGNORECASE)
287
 
288
- # Limita tamanho
 
 
 
 
 
 
289
  if len(texto) > 400:
290
- texto = texto[:397] + "..."
 
 
 
 
 
291
 
292
  return texto.strip()
293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  # ============================================================================
295
- # 🎯 CLASSE PRINCIPAL AKIRA API
296
  # ============================================================================
297
  class AkiraAPI:
298
  def __init__(self):
299
- """CORREÇÃO: Não aceita parâmetros - usa config.py diretamente"""
300
  self.api = Blueprint("akira_api", __name__)
301
  self.contexto_cache = SimpleTTLCache(ttl_seconds=300)
302
  self.db = Database(config.DB_PATH)
@@ -304,7 +366,7 @@ class AkiraAPI:
304
  self.web_search = get_web_search()
305
  self._setup_routes()
306
  self._setup_trainer()
307
- logger.success("🚀 AKIRA V21 BRUTAL inicializada (compatível com index.js)")
308
 
309
  def _setup_trainer(self):
310
  """Inicializa treinamento periódico"""
@@ -361,7 +423,7 @@ class AkiraAPI:
361
  }
362
 
363
  def _extrair_payload_indexjs(self, data: Dict[str, Any]) -> Dict[str, Any]:
364
- """Extrai e formata dados do payload do index.js"""
365
  payload = {
366
  'numero': str(data.get('numero', '')).strip(),
367
  'usuario': data.get('usuario', 'Anônimo').strip(),
@@ -371,15 +433,30 @@ class AkiraAPI:
371
  'grupo_id': data.get('grupo_id', ''),
372
  'grupo_nome': data.get('grupo_nome', ''),
373
  'is_reset': False,
374
- 'reply_info': data.get('reply_info'),
375
- 'mensagem_citada': ''
 
376
  }
377
 
378
- # Processa mensagem_citada do payload
379
- if 'mensagem_citada' in data:
380
- payload['mensagem_citada'] = data['mensagem_citada']
381
- elif data.get('reply_info') and data['reply_info'].get('texto_mensagem_citada'):
382
- payload['mensagem_citada'] = data['reply_info']['texto_mensagem_citada']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
 
384
  # Detecta /reset
385
  if payload['mensagem'].strip().lower() == '/reset':
@@ -424,7 +501,7 @@ class AkiraAPI:
424
  return ""
425
 
426
  def _setup_routes(self):
427
- """Configura rotas da API"""
428
 
429
  @self.api.before_request
430
  def handle_options():
@@ -444,17 +521,19 @@ class AkiraAPI:
444
 
445
  @self.api.route('/akira', methods=['POST'])
446
  def akira_endpoint():
447
- """Endpoint principal - TOTALMENTE COMPATÍVEL com index.js"""
448
  try:
449
  # Recebe e processa payload
450
  data = request.get_json() or {}
451
  payload = self._extrair_payload_indexjs(data)
452
 
453
- # Log da requisição
454
  logger.info(
455
  f"📨 [{payload['usuario']}] ({payload['numero']}) "
456
  f"[{payload['tipo_conversa']}]: {payload['mensagem'][:80]}..."
457
  )
 
 
458
 
459
  # Validação básica
460
  if not payload['mensagem']:
@@ -489,19 +568,24 @@ class AkiraAPI:
489
  # Verifica se usuário é privilegiado
490
  usuario_privilegiado = self.db.is_usuario_privilegiado(payload['numero'])
491
 
492
- # Analisa a mensagem (com reply_info do payload)
493
  analise = contexto.analisar_intencao_e_normalizar(
494
  payload['mensagem'],
495
  historico,
496
  payload['mensagem_citada']
497
  )
498
 
499
- # Adiciona informações extras à análise
 
 
 
 
500
  analise.update({
501
  'usuario_privilegiado': usuario_privilegiado,
502
  'numero': payload['numero'],
503
  'tipo_mensagem': payload['tipo_mensagem'],
504
- 'reply_info': payload.get('reply_info')
 
505
  })
506
 
507
  # Adiciona contexto web ao histórico se houver
@@ -512,37 +596,39 @@ class AkiraAPI:
512
  "content": f"[INFO WEB]: {contexto_web}"
513
  })
514
 
515
- # Gera resposta (PASSA reply_info para o LLM)
516
  resposta = self.llm_manager.gerar_resposta(
517
  mensagem=payload['mensagem'],
518
  historico=historico_com_web,
519
- mensagem_citada=payload['mensagem_citada'],
520
  analise=analise,
521
  usuario=payload['usuario'],
522
  tipo_conversa=payload['tipo_conversa'],
523
- reply_info=payload.get('reply_info')
524
  )
525
 
526
- # Determina se é reply e reply_to_bot
527
- is_reply = bool(payload['mensagem_citada']) or bool(payload.get('reply_info'))
528
  reply_to_bot = False
529
 
530
- if payload.get('reply_info'):
 
 
531
  reply_to_bot = payload['reply_info'].get('reply_to_bot', False)
532
  elif payload['mensagem_citada'] and payload['mensagem_citada'].startswith("[Respondendo à Akira:"):
533
  reply_to_bot = True
534
 
535
- # Atualiza contexto
536
  contexto.atualizar_contexto(
537
  mensagem=payload['mensagem'],
538
  resposta=resposta,
539
  numero=payload['numero'],
540
  is_reply=is_reply,
541
- mensagem_original=payload['mensagem_citada'],
542
  reply_to_bot=reply_to_bot
543
  )
544
 
545
- # Registra interação para treinamento
546
  try:
547
  trainer = Treinamento(self.db)
548
  trainer.registrar_interacao(
@@ -551,16 +637,17 @@ class AkiraAPI:
551
  resposta=resposta,
552
  numero=payload['numero'],
553
  is_reply=is_reply,
554
- mensagem_original=payload['mensagem_citada'],
555
  contexto=analise,
556
  tipo_conversa=payload['tipo_conversa'],
557
- tipo_mensagem=payload['tipo_mensagem']
 
558
  )
559
  except Exception as e:
560
- logger.warning(f"Erro ao registrar interação: {e}")
561
 
562
  # Log da resposta
563
- logger.info(f"📤 Resposta: {resposta[:80]}...")
564
 
565
  return jsonify({"resposta": resposta})
566
 
@@ -570,22 +657,28 @@ class AkiraAPI:
570
  logger.error(traceback.format_exc())
571
  return jsonify({
572
  "error": "Erro interno",
573
- "details": str(e)[:100]
 
574
  }), 500
575
 
576
  @self.api.route('/health', methods=['GET'])
577
  def health():
578
- """Endpoint de health check"""
579
  agora = datetime.datetime.now() + datetime.timedelta(hours=config.TIMEZONE_OFFSET_HOURS)
580
  return jsonify({
581
- "status": "✅ AKIRA V21 BRUTAL ONLINE",
582
  "hora_luanda": agora.strftime("%H:%M"),
583
- "versao": "Dezembro 2025",
584
  "apis_disponiveis": self.llm_manager.apis_disponiveis,
585
  "compatibilidade": {
586
- "index_js": True,
587
- "reply_info": True,
588
- "contexto_otimizado": True
 
 
 
 
 
589
  }
590
  })
591
 
@@ -612,17 +705,26 @@ class AkiraAPI:
612
 
613
  @self.api.route('/info', methods=['GET'])
614
  def info():
615
- """Informações sobre a API"""
616
  return jsonify({
617
- "nome": "Akira V21",
618
- "descricao": "IA com personalidade brutal e irônica",
619
  "desenvolvedor": "Isaac Quarenta",
620
  "empresa": "Softedge",
 
621
  "endpoints": ["/akira", "/health", "/reset", "/info"],
 
 
 
 
 
 
 
622
  "configuracoes": {
623
  "temperatura_padrao": config.TEMPERATURE,
624
  "max_tokens": config.MAX_TOKENS,
625
- "timezone_offset": config.TIMEZONE_OFFSET_HOURS
 
626
  }
627
  })
628
 
@@ -631,7 +733,7 @@ class AkiraAPI:
631
  return self.api
632
 
633
  # ============================================================================
634
- # 🎯 FUNÇÃO PARA USO DIRETO
635
  # ============================================================================
636
  def gerar_resposta_direta(
637
  mensagem: str,
@@ -640,26 +742,32 @@ def gerar_resposta_direta(
640
  tipo_conversa: str = "pv",
641
  tipo_mensagem: str = "texto",
642
  mensagem_citada: str = "",
 
643
  reply_info: Optional[Dict] = None
644
  ) -> str:
645
  """
646
- Função para uso direto (sem HTTP)
647
- Útil para testes e integração direta
648
  """
649
  try:
650
  # Cria instância
651
  api = AkiraAPI()
652
 
653
- # Prepara análise básica
654
  analise = {
655
  "numero": numero,
656
  "usuario_privilegiado": api.db.is_usuario_privilegiado(numero),
657
  "emocao_primaria": "neutral",
658
  "modo_resposta": "normal_ironico",
659
- "tipo_mensagem": tipo_mensagem
 
 
660
  }
661
 
662
- # Gera resposta
 
 
 
663
  resposta = api.llm_manager.gerar_resposta(
664
  mensagem=mensagem,
665
  historico=[],
@@ -667,39 +775,43 @@ def gerar_resposta_direta(
667
  analise=analise,
668
  usuario=usuario,
669
  tipo_conversa=tipo_conversa,
670
- reply_info=reply_info
671
  )
672
 
 
673
  return resposta
674
 
675
  except Exception as e:
676
- logger.error(f"Erro na resposta direta: {e}")
677
  return "Erro ao processar mensagem."
678
 
679
  # ============================================================================
680
- # 🎯 TESTE RÁPIDO
681
  # ============================================================================
682
  if __name__ == "__main__":
683
  print("=" * 80)
684
- print("TESTANDO API.PY - COMPATIBILIDADE COM INDEX.JS")
685
  print("=" * 80)
686
 
687
- # Teste com payload do index.js
688
  test_payload = {
689
  "numero": "244978787009",
690
  "usuario": "Isaac Quarenta",
691
- "mensagem": "Você disse que gosta de futebol, né?",
692
  "tipo_conversa": "pv",
693
  "tipo_mensagem": "texto",
694
- "reply_info": {
 
 
695
  "reply_to_bot": True,
696
- "quem_escreveu_citacao_nome": "Akira",
697
- "texto_mensagem_citada": "Sim, gosto do Petro de Luanda"
 
698
  }
699
  }
700
 
701
  try:
702
- # Testa função direta
703
  resposta = gerar_resposta_direta(
704
  mensagem=test_payload["mensagem"],
705
  usuario=test_payload["usuario"],
 
1
+ # modules/api.py — AKIRA V21 FINAL ADAPTADO (DEZEMBRO 2025)
2
  """
3
+ ✅ TOTALMENTE ADAPTADO ao config.py BRUTAL ATUALIZADO
4
+ ✅ TOTALMENTE COMPATÍVEL com index.js otimizado (mensagem_citada completa)
5
  ✅ Sistema emocional DistilBERT
6
  ✅ Xingamento automático integrado
7
+ ✅ Usa prompts de ironia máxima com contexto completo
8
+ ✅ Processa reply_metadata e mensagem_citada do index.js corretamente
9
+ ✅ CORREÇÃO: Compatível com novo payload (reply_metadata + mensagem_citada)
10
  """
11
  import time
12
  import datetime
 
59
  return default
60
 
61
  # ============================================================================
62
+ # 🧠 GERENCIADOR MULTI-API (OTIMIZADO PARA NOVO CONTEXTO)
63
  # ============================================================================
64
  class MultiAPIManager:
65
  def __init__(self):
 
90
  reply_info: Optional[Dict] = None
91
  ) -> str:
92
  """
93
+ Constrói prompt usando a função do config.py ATUALIZADO
94
+ TOTALMENTE COMPATÍVEL com o novo index.js (mensagem_citada completa)
95
  """
96
  try:
97
+ # Log do que estamos recebendo
98
+ logger.debug(f"📝 Construindo prompt para: {usuario}")
99
+ logger.debug(f"📝 Mensagem: {mensagem[:100]}...")
100
+ logger.debug(f"📝 Mensagem citada: {mensagem_citada[:100] if mensagem_citada else '[Nenhuma]'}...")
101
+ logger.debug(f"📝 Reply info disponível: {bool(reply_info)}")
102
+
103
+ # Usa a função principal do config.py atualizado
104
  prompt = config.construir_prompt_api(
105
  mensagem=mensagem,
106
  historico=historico,
 
111
  reply_info=reply_info
112
  )
113
 
114
+ logger.debug(f" Prompt construído: {len(prompt)} caracteres")
115
+ logger.debug(f"📄 Primeiras 3 linhas do prompt:")
116
+ for i, line in enumerate(prompt.split('\n')[:3]):
117
+ logger.debug(f" {i}: {line[:100]}...")
118
+
119
  return prompt
120
 
121
  except Exception as e:
122
+ logger.error(f"Erro ao construir prompt: {e}")
123
+ # Fallback para prompt simples (com mensagem citada)
124
  return f"""AKIRA V21 — PERSONALIDADE REAL SEM FILTRO
125
  Você é Akira, angolana de 18 anos de Luanda.
126
 
127
+ CONTEXTO: {f'O usuário está respondendo a uma mensagem: "{mensagem_citada[:150]}{"..." if len(mensagem_citada) > 150 else ""}"' if mensagem_citada else 'Conversa normal'}
128
+
129
  USUÁRIO: {usuario}
130
  MENSAGEM: {mensagem}
131
 
 
136
  emocao = analise.get("emocao_primaria", "neutral")
137
  modo = analise.get("modo_resposta", "normal_ironico")
138
 
139
+ # Usa função do config.py atualizado
140
  return config.obter_parametros_api(emocao, modo)
141
 
142
  def gerar_resposta(
 
149
  tipo_conversa: str,
150
  reply_info: Optional[Dict] = None
151
  ) -> str:
152
+ """Gera resposta usando multi-API fallback (com contexto completo)"""
153
+ # Log do que está sendo processado
154
+ logger.info(f"🎯 Gerando resposta para {usuario}")
155
+ logger.info(f"📤 Mensagem: {mensagem[:80]}...")
156
+ if mensagem_citada:
157
+ logger.info(f"📝 Mensagem citada disponível: {mensagem_citada[:80]}...")
158
+ if reply_info:
159
+ logger.info(f"🔍 Reply metadata: reply_to_bot={reply_info.get('reply_to_bot', False)}")
160
+
161
+ # Construir prompt com contexto completo
162
  prompt = self._construir_prompt(
163
  mensagem=mensagem,
164
  historico=historico,
 
180
  continue
181
 
182
  try:
183
+ logger.debug(f"🔄 Tentando API: {api_name}")
184
  resposta = self._chamar_api(api_name, prompt, parametros)
185
  if resposta:
186
  resposta_limpa = self._limpar_resposta(resposta)
187
+ logger.info(f"✅ API {api_name} respondeu: {resposta_limpa[:100]}...")
188
  return resposta_limpa
189
 
190
  except Exception as e:
191
+ logger.warning(f"❌ API {api_name} falhou: {str(e)[:100]}")
192
  continue
193
 
194
+ # Fallback: respostas curtas e brutais baseadas no contexto
195
+ fallbacks = self._gerar_fallback_contextual(mensagem, mensagem_citada, reply_info)
 
 
 
 
 
 
 
196
  return random.choice(fallbacks)
197
 
198
  def _chamar_api(self, api_name: str, prompt: str, parametros: Dict[str, Any]) -> str:
 
299
  # Remove prefixos como "Akira:" ou "AKIRA:"
300
  texto = re.sub(r'^(Akira|AKIRA)[:\s\-]+', '', texto, flags=re.IGNORECASE)
301
 
302
+ # Remove "Resposta:" ou "Resposta de Akira:"
303
+ texto = re.sub(r'^(Resposta|Resposta de Akira)[:\s\-]+', '', texto, flags=re.IGNORECASE)
304
+
305
+ # Remove múltiplos espaços
306
+ texto = re.sub(r'\s+', ' ', texto)
307
+
308
+ # Limita tamanho (mas preserva contexto)
309
  if len(texto) > 400:
310
+ # Tenta encontrar um ponto final próximo
311
+ last_period = texto[:397].rfind('.')
312
+ if last_period > 300:
313
+ texto = texto[:last_period + 1]
314
+ else:
315
+ texto = texto[:397] + "..."
316
 
317
  return texto.strip()
318
 
319
+ def _gerar_fallback_contextual(self, mensagem: str, mensagem_citada: str, reply_info: Optional[Dict]) -> List[str]:
320
+ """Gera fallbacks contextuais baseados na mensagem e contexto"""
321
+ base_fallbacks = [
322
+ "Puto, nem ligo.",
323
+ "Foda-se.",
324
+ "Ya, tá bom.",
325
+ "Caralho.",
326
+ "Não quero saber.",
327
+ "Vai à merda.",
328
+ "Tás a brincar?",
329
+ "Ok.",
330
+ "Sim.",
331
+ "Não."
332
+ ]
333
+
334
+ # Se há mensagem citada, adiciona fallbacks contextuais
335
+ if mensagem_citada:
336
+ contextual_fallbacks = [
337
+ f"Já disse isso.",
338
+ f"Repito: {mensagem_citada[:50]}...",
339
+ f"Ya, como disse.",
340
+ f"Exactamente isso."
341
+ ]
342
+ base_fallbacks.extend(contextual_fallbacks)
343
+
344
+ # Se é reply ao bot
345
+ if reply_info and reply_info.get('reply_to_bot'):
346
+ reply_fallbacks = [
347
+ "Já disse isso, caralho.",
348
+ "Repito o que disse.",
349
+ "Ya, como eu disse.",
350
+ "Exactamente o que falei."
351
+ ]
352
+ base_fallbacks.extend(reply_fallbacks)
353
+
354
+ return base_fallbacks
355
+
356
  # ============================================================================
357
+ # 🎯 CLASSE PRINCIPAL AKIRA API (ATUALIZADA)
358
  # ============================================================================
359
  class AkiraAPI:
360
  def __init__(self):
361
+ """CORREÇÃO: Não aceita parâmetros - usa config.py atualizado diretamente"""
362
  self.api = Blueprint("akira_api", __name__)
363
  self.contexto_cache = SimpleTTLCache(ttl_seconds=300)
364
  self.db = Database(config.DB_PATH)
 
366
  self.web_search = get_web_search()
367
  self._setup_routes()
368
  self._setup_trainer()
369
+ logger.success("🚀 AKIRA V21 BRUTAL ATUALIZADA inicializada (compatível com novo index.js)")
370
 
371
  def _setup_trainer(self):
372
  """Inicializa treinamento periódico"""
 
423
  }
424
 
425
  def _extrair_payload_indexjs(self, data: Dict[str, Any]) -> Dict[str, Any]:
426
+ """Extrai e formata dados do payload do NOVO index.js (com mensagem_citada completa)"""
427
  payload = {
428
  'numero': str(data.get('numero', '')).strip(),
429
  'usuario': data.get('usuario', 'Anônimo').strip(),
 
433
  'grupo_id': data.get('grupo_id', ''),
434
  'grupo_nome': data.get('grupo_nome', ''),
435
  'is_reset': False,
436
+ 'reply_metadata': data.get('reply_metadata', {}), # AGORA É reply_metadata
437
+ 'reply_info': data.get('reply_info', {}), # Mantém para compatibilidade
438
+ 'mensagem_citada': data.get('mensagem_citada', '') # AGORA RECEBE DIRETO DO PAYLOAD
439
  }
440
 
441
+ # Log do que foi recebido
442
+ logger.debug(f"📦 Payload recebido:")
443
+ logger.debug(f" Usuário: {payload['usuario']}")
444
+ logger.debug(f" Mensagem: {payload['mensagem'][:80]}...")
445
+ logger.debug(f" Mensagem citada: {payload['mensagem_citada'][:80] if payload['mensagem_citada'] else '[Nenhuma]'}...")
446
+ logger.debug(f" Reply metadata: {payload['reply_metadata']}")
447
+ logger.debug(f" É reply: {payload['reply_metadata'].get('is_reply', False) if payload['reply_metadata'] else False}")
448
+
449
+ # CORREÇÃO: Preferir reply_metadata, mas manter compatibilidade com reply_info
450
+ reply_data = payload['reply_metadata'] if payload['reply_metadata'] else payload['reply_info']
451
+
452
+ # Se não tem mensagem_citada diretamente, tenta extrair do reply_data
453
+ if not payload['mensagem_citada'] and reply_data:
454
+ # Tenta várias chaves possíveis
455
+ for key in ['texto_mensagem_citada', 'texto', 'mensagem_citada', 'quoted_text']:
456
+ if key in reply_data and reply_data[key]:
457
+ payload['mensagem_citada'] = reply_data[key]
458
+ logger.debug(f" Extraído mensagem_citada de {key}")
459
+ break
460
 
461
  # Detecta /reset
462
  if payload['mensagem'].strip().lower() == '/reset':
 
501
  return ""
502
 
503
  def _setup_routes(self):
504
+ """Configura rotas da API ATUALIZADAS"""
505
 
506
  @self.api.before_request
507
  def handle_options():
 
521
 
522
  @self.api.route('/akira', methods=['POST'])
523
  def akira_endpoint():
524
+ """Endpoint principal - TOTALMENTE COMPATÍVEL com NOVO index.js"""
525
  try:
526
  # Recebe e processa payload
527
  data = request.get_json() or {}
528
  payload = self._extrair_payload_indexjs(data)
529
 
530
+ # Log detalhado da requisição
531
  logger.info(
532
  f"📨 [{payload['usuario']}] ({payload['numero']}) "
533
  f"[{payload['tipo_conversa']}]: {payload['mensagem'][:80]}..."
534
  )
535
+ if payload['mensagem_citada']:
536
+ logger.info(f"📝 Mensagem citada: {payload['mensagem_citada'][:80]}...")
537
 
538
  # Validação básica
539
  if not payload['mensagem']:
 
568
  # Verifica se usuário é privilegiado
569
  usuario_privilegiado = self.db.is_usuario_privilegiado(payload['numero'])
570
 
571
+ # Analisa a mensagem (com mensagem_citada completa)
572
  analise = contexto.analisar_intencao_e_normalizar(
573
  payload['mensagem'],
574
  historico,
575
  payload['mensagem_citada']
576
  )
577
 
578
+ # ADAPTAÇÃO: Prepara reply_info para passar ao config.py
579
+ # Preferir reply_metadata, mas manter compatibilidade
580
+ reply_info_for_config = payload['reply_metadata'] if payload['reply_metadata'] else payload['reply_info']
581
+
582
+ # Adiciona informações extras à análise (INCLUI reply_metadata)
583
  analise.update({
584
  'usuario_privilegiado': usuario_privilegiado,
585
  'numero': payload['numero'],
586
  'tipo_mensagem': payload['tipo_mensagem'],
587
+ 'reply_metadata': payload['reply_metadata'], # ENVIA PARA CONFIG.PY
588
+ 'reply_info': reply_info_for_config # Para compatibilidade
589
  })
590
 
591
  # Adiciona contexto web ao histórico se houver
 
596
  "content": f"[INFO WEB]: {contexto_web}"
597
  })
598
 
599
+ # Gera resposta (PASSA mensagem_citada COMPLETA e reply_info)
600
  resposta = self.llm_manager.gerar_resposta(
601
  mensagem=payload['mensagem'],
602
  historico=historico_com_web,
603
+ mensagem_citada=payload['mensagem_citada'], # ENVIA COMPLETA
604
  analise=analise,
605
  usuario=payload['usuario'],
606
  tipo_conversa=payload['tipo_conversa'],
607
+ reply_info=reply_info_for_config # PASSA PARA CONFIG.PY
608
  )
609
 
610
+ # Determina se é reply e reply_to_bot (USANDO reply_metadata)
611
+ is_reply = bool(payload['mensagem_citada']) or (payload['reply_metadata'] and payload['reply_metadata'].get('is_reply', False))
612
  reply_to_bot = False
613
 
614
+ if payload['reply_metadata']:
615
+ reply_to_bot = payload['reply_metadata'].get('reply_to_bot', False)
616
+ elif payload['reply_info']:
617
  reply_to_bot = payload['reply_info'].get('reply_to_bot', False)
618
  elif payload['mensagem_citada'] and payload['mensagem_citada'].startswith("[Respondendo à Akira:"):
619
  reply_to_bot = True
620
 
621
+ # Atualiza contexto (INCLUI mensagem_citada completa)
622
  contexto.atualizar_contexto(
623
  mensagem=payload['mensagem'],
624
  resposta=resposta,
625
  numero=payload['numero'],
626
  is_reply=is_reply,
627
+ mensagem_original=payload['mensagem_citada'], # GUARDA COMPLETA
628
  reply_to_bot=reply_to_bot
629
  )
630
 
631
+ # Registra interação para treinamento (COM CONTEXTO COMPLETO)
632
  try:
633
  trainer = Treinamento(self.db)
634
  trainer.registrar_interacao(
 
637
  resposta=resposta,
638
  numero=payload['numero'],
639
  is_reply=is_reply,
640
+ mensagem_original=payload['mensagem_citada'], # REGISTRA COMPLETA
641
  contexto=analise,
642
  tipo_conversa=payload['tipo_conversa'],
643
+ tipo_mensagem=payload['tipo_mensagem'],
644
+ reply_metadata=payload['reply_metadata'] # REGISTRA METADATA
645
  )
646
  except Exception as e:
647
+ logger.warning(f"⚠️ Erro ao registrar interação: {e}")
648
 
649
  # Log da resposta
650
+ logger.info(f"📤 Resposta gerada: {resposta[:80]}...")
651
 
652
  return jsonify({"resposta": resposta})
653
 
 
657
  logger.error(traceback.format_exc())
658
  return jsonify({
659
  "error": "Erro interno",
660
+ "details": str(e)[:100],
661
+ "resposta": "Barra no bardeado, puto. Erro interno."
662
  }), 500
663
 
664
  @self.api.route('/health', methods=['GET'])
665
  def health():
666
+ """Endpoint de health check ATUALIZADO"""
667
  agora = datetime.datetime.now() + datetime.timedelta(hours=config.TIMEZONE_OFFSET_HOURS)
668
  return jsonify({
669
+ "status": "✅ AKIRA V21 BRUTAL ONLINE (ATUALIZADA)",
670
  "hora_luanda": agora.strftime("%H:%M"),
671
+ "versao": "Dezembro 2025 - Mensagem Citada Completa",
672
  "apis_disponiveis": self.llm_manager.apis_disponiveis,
673
  "compatibilidade": {
674
+ "index_js_atualizado": True,
675
+ "mensagem_citada_completa": True,
676
+ "reply_metadata": True,
677
+ "contexto_completo": True
678
+ },
679
+ "estatisticas": {
680
+ "cache_size": len(self.contexto_cache._store),
681
+ "apis_ativas": len(self.llm_manager.apis_disponiveis)
682
  }
683
  })
684
 
 
705
 
706
  @self.api.route('/info', methods=['GET'])
707
  def info():
708
+ """Informações sobre a API ATUALIZADA"""
709
  return jsonify({
710
+ "nome": "Akira V21 (ATUALIZADA)",
711
+ "descricao": "IA com personalidade brutal e irônica - COM CONTEXTO COMPLETO",
712
  "desenvolvedor": "Isaac Quarenta",
713
  "empresa": "Softedge",
714
+ "versao": config.VERSAO,
715
  "endpoints": ["/akira", "/health", "/reset", "/info"],
716
+ "caracteristicas": [
717
+ "Mensagem citada completa",
718
+ "Contexto de reply otimizado",
719
+ "Xingamento automático",
720
+ "Respostas curtas e brutais",
721
+ "Compatível com novo index.js"
722
+ ],
723
  "configuracoes": {
724
  "temperatura_padrao": config.TEMPERATURE,
725
  "max_tokens": config.MAX_TOKENS,
726
+ "timezone_offset": config.TIMEZONE_OFFSET_HOURS,
727
+ "apis_suportadas": config.API_FALLBACK_ORDER
728
  }
729
  })
730
 
 
733
  return self.api
734
 
735
  # ============================================================================
736
+ # 🎯 FUNÇÃO PARA USO DIRETO (ATUALIZADA)
737
  # ============================================================================
738
  def gerar_resposta_direta(
739
  mensagem: str,
 
742
  tipo_conversa: str = "pv",
743
  tipo_mensagem: str = "texto",
744
  mensagem_citada: str = "",
745
+ reply_metadata: Optional[Dict] = None,
746
  reply_info: Optional[Dict] = None
747
  ) -> str:
748
  """
749
+ Função para uso direto (sem HTTP) - ATUALIZADA
750
+ Útil para testes e integração direta com contexto completo
751
  """
752
  try:
753
  # Cria instância
754
  api = AkiraAPI()
755
 
756
+ # Prepara análise básica com contexto
757
  analise = {
758
  "numero": numero,
759
  "usuario_privilegiado": api.db.is_usuario_privilegiado(numero),
760
  "emocao_primaria": "neutral",
761
  "modo_resposta": "normal_ironico",
762
+ "tipo_mensagem": tipo_mensagem,
763
+ "reply_metadata": reply_metadata,
764
+ "reply_info": reply_info or reply_metadata
765
  }
766
 
767
+ # Prepara reply_info para config.py
768
+ reply_info_for_config = reply_metadata if reply_metadata else reply_info
769
+
770
+ # Gera resposta com contexto completo
771
  resposta = api.llm_manager.gerar_resposta(
772
  mensagem=mensagem,
773
  historico=[],
 
775
  analise=analise,
776
  usuario=usuario,
777
  tipo_conversa=tipo_conversa,
778
+ reply_info=reply_info_for_config
779
  )
780
 
781
+ logger.info(f"✅ Resposta direta gerada para {usuario}: {resposta[:80]}...")
782
  return resposta
783
 
784
  except Exception as e:
785
+ logger.error(f"Erro na resposta direta: {e}")
786
  return "Erro ao processar mensagem."
787
 
788
  # ============================================================================
789
+ # 🎯 TESTE RÁPIDO - ATUALIZADO PARA NOVO CONTEXTO
790
  # ============================================================================
791
  if __name__ == "__main__":
792
  print("=" * 80)
793
+ print("TESTANDO API.PY ATUALIZADA - COMPATIBILIDADE COM NOVO INDEX.JS")
794
  print("=" * 80)
795
 
796
+ # Teste com payload do NOVO index.js (com mensagem_citada completa)
797
  test_payload = {
798
  "numero": "244978787009",
799
  "usuario": "Isaac Quarenta",
800
+ "mensagem": "Você disse que gosta de futebol, né? O que acha do jogo de ontem?",
801
  "tipo_conversa": "pv",
802
  "tipo_mensagem": "texto",
803
+ "mensagem_citada": "Sim, gosto do Petro de Luanda. É o melhor time de Angola, sem discussão. Jogo de qualidade europeia.",
804
+ "reply_metadata": {
805
+ "is_reply": True,
806
  "reply_to_bot": True,
807
+ "quoted_author_name": "Akira",
808
+ "quoted_type": "texto",
809
+ "context_hint": "(Usuário está respondendo à MINHA mensagem anterior: \"Sim, gosto do Petro de Luanda. É o me...\")"
810
  }
811
  }
812
 
813
  try:
814
+ # Testa função direta com contexto completo
815
  resposta = gerar_resposta_direta(
816
  mensagem=test_payload["mensagem"],
817
  usuario=test_payload["usuario"],