akra35567 commited on
Commit
fa34f3f
·
verified ·
1 Parent(s): b7f160e

Update modules/database.py

Browse files
Files changed (1) hide show
  1. modules/database.py +301 -40
modules/database.py CHANGED
@@ -1,7 +1,8 @@
1
- # modules/database.py — AKIRA V21 FINAL CORRIGIDO (Dezembro 2025) - CORREÇÃO: message_id
2
  """
3
  ✅ TOTALMENTE ADAPTADO ao index.js atualizado
4
  ✅ CORREÇÃO: Problema com message_id UNIQUE resolvido
 
5
  ✅ Métodos corretos para api.py, contexto.py, treinamento.py
6
  ✅ Estrutura completa com reply_metadata
7
  ✅ Todos os métodos necessários implementados
@@ -101,6 +102,10 @@ class Database:
101
  emocao_detectada TEXT,
102
  confianca_emocao REAL DEFAULT 0.5,
103
 
 
 
 
 
104
  -- Grupo
105
  grupo_id TEXT DEFAULT '',
106
  grupo_nome TEXT DEFAULT '',
@@ -114,7 +119,7 @@ class Database:
114
  comando_executado TEXT,
115
  has_media BOOLEAN DEFAULT 0,
116
  media_type TEXT DEFAULT '',
117
- message_id TEXT, -- CORREÇÃO: Removido UNIQUE
118
  bot_response_time_ms INTEGER DEFAULT 0,
119
  is_mention BOOLEAN DEFAULT 0,
120
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -169,6 +174,7 @@ class Database:
169
  humor_atual TEXT DEFAULT 'normal_ironico',
170
  modo_resposta TEXT DEFAULT 'normal_ironico',
171
  nivel_transicao INTEGER DEFAULT 0,
 
172
  humor_alvo TEXT DEFAULT 'normal_ironico',
173
  termos TEXT,
174
  girias TEXT,
@@ -196,6 +202,7 @@ class Database:
196
  output_text TEXT NOT NULL,
197
  humor TEXT DEFAULT 'normal_ironico',
198
  modo_resposta TEXT DEFAULT 'normal_ironico',
 
199
  emocao_contexto TEXT,
200
  contexto_super_claro TEXT,
201
  tipo_interacao TEXT DEFAULT 'normal',
@@ -216,9 +223,10 @@ class Database:
216
  contexto_id TEXT NOT NULL,
217
  humor_anterior TEXT NOT NULL,
218
  humor_novo TEXT NOT NULL,
 
 
219
  emocao_trigger TEXT,
220
  confianca_emocao REAL,
221
- nivel_transicao INTEGER,
222
  razao TEXT,
223
  intensidade REAL DEFAULT 0.5,
224
  contexto_mensagem TEXT,
@@ -270,8 +278,26 @@ class Database:
270
  )
271
  ''')
272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  conn.commit()
274
- logger.info("✅ Tabelas criadas/verificadas com message_id corrigido")
275
 
276
  except Exception as e:
277
  logger.error(f"❌ Erro ao criar tabelas: {e}")
@@ -298,8 +324,10 @@ class Database:
298
  ("is_mention", "BOOLEAN DEFAULT 0"),
299
  ("has_media", "BOOLEAN DEFAULT 0"),
300
  ("media_type", "TEXT DEFAULT ''"),
301
- ("message_id", "TEXT"), # CORREÇÃO: Sem UNIQUE aqui também
302
- ("bot_response_time_ms", "INTEGER DEFAULT 0")
 
 
303
  ]
304
 
305
  for col_name, col_def in novas_colunas:
@@ -308,6 +336,41 @@ class Database:
308
  except sqlite3.OperationalError:
309
  pass
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  conn.commit()
312
 
313
  except Exception as e:
@@ -326,6 +389,8 @@ class Database:
326
  modo_resposta: str = 'normal_ironico',
327
  emocao_detectada: str = None,
328
  confianca_emocao: float = 0.5,
 
 
329
  tipo_mensagem: str = 'texto',
330
  reply_info_json: str = None,
331
  usuario_nome: str = '',
@@ -341,7 +406,7 @@ class Database:
341
  message_id: str = None,
342
  bot_response_time_ms: int = 0,
343
  is_mention: bool = False) -> bool:
344
- """Salva mensagem no banco - CORREÇÃO: message_id sem UNIQUE constraint"""
345
  try:
346
  numero_final = str(numero or usuario).strip()
347
  contexto_id = self._gerar_contexto_id(numero_final, tipo_conversa)
@@ -350,14 +415,18 @@ class Database:
350
  if isinstance(reply_info_json, dict):
351
  reply_info_json = json.dumps(reply_info_json, ensure_ascii=False)
352
 
353
- # CORREÇÃO: Gera message_id único se não fornecido
 
 
 
 
 
354
  if not message_id:
355
  timestamp = int(time.time() * 1000)
356
  random_suffix = random.randint(1000, 9999)
357
  message_id = f"{numero_final}_{timestamp}_{random_suffix}"
358
 
359
- # CORREÇÃO: Adiciona um sufixo aleatório extra para garantir unicidade
360
- # mesmo que o timestamp seja o mesmo
361
  unique_suffix = random.randint(100, 999)
362
  message_id = f"{message_id}_{unique_suffix}"
363
 
@@ -368,10 +437,10 @@ class Database:
368
  (usuario, usuario_nome, mensagem, resposta, numero, contexto_id, tipo_contexto,
369
  tipo_conversa, tipo_mensagem, is_reply, mensagem_original, mensagem_citada_limpa,
370
  reply_to_bot, reply_info_json, humor, modo_resposta, emocao_detectada,
371
- confianca_emocao, grupo_id, grupo_nome, audio_transcricao, fonte_stt,
372
- confianca_stt, comando_executado, has_media, media_type, message_id,
373
- bot_response_time_ms, is_mention)
374
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
375
  """,
376
  (
377
  usuario[:50], usuario_nome[:100] or usuario[:100],
@@ -379,7 +448,8 @@ class Database:
379
  contexto_id, tipo_conversa, tipo_conversa, tipo_mensagem,
380
  int(is_reply), mensagem_original, mensagem_citada_limpa,
381
  int(reply_to_bot), reply_info_json, humor, modo_resposta,
382
- emocao_detectada, confianca_emocao, grupo_id[:50], grupo_nome[:100],
 
383
  audio_transcricao[:2000] if audio_transcricao else None,
384
  fonte_stt[:50], confianca_stt, comando_executado[:100] if comando_executado else None,
385
  int(has_media), media_type[:50], message_id[:200],
@@ -389,7 +459,7 @@ class Database:
389
  fetch=False
390
  )
391
 
392
- logger.debug(f"✅ Mensagem salva: {numero_final} | Message ID: {message_id}")
393
  return True
394
 
395
  except sqlite3.IntegrityError as e:
@@ -405,10 +475,10 @@ class Database:
405
  (usuario, usuario_nome, mensagem, resposta, numero, contexto_id, tipo_contexto,
406
  tipo_conversa, tipo_mensagem, is_reply, mensagem_original, mensagem_citada_limpa,
407
  reply_to_bot, reply_info_json, humor, modo_resposta, emocao_detectada,
408
- confianca_emocao, grupo_id, grupo_nome, audio_transcricao, fonte_stt,
409
- confianca_stt, comando_executado, has_media, media_type, message_id,
410
- bot_response_time_ms, is_mention)
411
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
412
  """,
413
  (
414
  usuario[:50], usuario_nome[:100] or usuario[:100],
@@ -416,7 +486,8 @@ class Database:
416
  contexto_id, tipo_conversa, tipo_conversa, tipo_mensagem,
417
  int(is_reply), mensagem_original, mensagem_citada_limpa,
418
  int(reply_to_bot), reply_info_json, humor, modo_resposta,
419
- emocao_detectada, confianca_emocao, grupo_id[:50], grupo_nome[:100],
 
420
  audio_transcricao[:2000] if audio_transcricao else None,
421
  fonte_stt[:50], confianca_stt, comando_executado[:100] if comando_executado else None,
422
  int(has_media), media_type[:50], new_message_id[:200],
@@ -437,13 +508,14 @@ class Database:
437
  def salvar_training_example(self, input_text: str, output_text: str,
438
  humor: str = "normal_ironico",
439
  modo_resposta: str = "normal_ironico",
 
440
  emocao_contexto: str = None,
441
  qualidade_score: float = 1.0,
442
  contexto_super_claro: Dict = None,
443
  tipo_interacao: str = "normal",
444
  score_relevancia: float = 1.0,
445
  tags: List[str] = None) -> bool:
446
- """Salva exemplo de treinamento"""
447
  try:
448
  contexto_json = json.dumps(contexto_super_claro, ensure_ascii=False) if contexto_super_claro else None
449
  tags_str = ",".join(tags) if tags else ""
@@ -451,50 +523,54 @@ class Database:
451
  self._execute_with_retry(
452
  """
453
  INSERT INTO training_examples
454
- (input_text, output_text, humor, modo_resposta, emocao_contexto,
455
- qualidade_score, contexto_super_claro, tipo_interacao, score_relevancia, tags)
456
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
457
  """,
458
  (
459
  input_text[:2000], output_text[:2000], humor, modo_resposta,
460
- emocao_contexto, qualidade_score, contexto_json,
461
  tipo_interacao, score_relevancia, tags_str[:200]
462
  ),
463
  commit=True,
464
  fetch=False
465
  )
466
- logger.debug(f"✅ Training example salvo | Score: {qualidade_score:.2f}")
467
  return True
468
  except Exception as e:
469
  logger.error(f"❌ Erro ao salvar training: {e}")
470
  return False
471
 
472
  def salvar_transicao_humor(self, numero: str, humor_anterior: str,
473
- humor_novo: str, emocao_trigger: str = None,
 
 
474
  confianca_emocao: float = 0.5,
475
- nivel_transicao: int = 0,
476
  razao: str = "", intensidade: float = 0.5,
477
  contexto_mensagem: str = None):
478
- """Salva transição de humor"""
479
  try:
480
  contexto_id = self._gerar_contexto_id(numero, 'auto')
481
 
482
  self._execute_with_retry(
483
  """
484
  INSERT INTO transicoes_humor
485
- (numero, contexto_id, humor_anterior, humor_novo, emocao_trigger,
486
- confianca_emocao, nivel_transicao, razao, intensidade, contexto_mensagem)
487
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
 
488
  """,
489
  (
490
  str(numero).strip(), contexto_id, humor_anterior, humor_novo,
491
- emocao_trigger, confianca_emocao, nivel_transicao,
492
- razao[:200], intensidade, contexto_mensagem[:500] if contexto_mensagem else None
 
493
  ),
494
  commit=True,
495
  fetch=False
496
  )
497
- logger.debug(f"🎭 Transição salva: {humor_anterior} {humor_novo}")
498
  except Exception as e:
499
  logger.error(f"❌ Erro ao salvar transição: {e}")
500
 
@@ -538,6 +614,136 @@ class Database:
538
  logger.error(f"❌ Erro ao salvar gíria: {e}")
539
  return False
540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  # ========================================================================
542
  # MÉTODOS DE RECUPERAÇÃO
543
  # ========================================================================
@@ -548,7 +754,7 @@ class Database:
548
  results = self._execute_with_retry(
549
  """
550
  SELECT mensagem, resposta, is_reply, mensagem_original,
551
- reply_to_bot, humor, modo_resposta, timestamp
552
  FROM mensagens
553
  WHERE numero = ? AND deletado = 0
554
  ORDER BY timestamp DESC
@@ -589,13 +795,25 @@ class Database:
589
  except Exception:
590
  return "normal_ironico"
591
 
 
 
 
 
 
 
 
 
 
 
 
 
592
  def recuperar_training_examples(self, limite: int = 100, usado: bool = False) -> List[Dict]:
593
  """Recupera exemplos de treinamento"""
594
  try:
595
  where_clause = "WHERE usado = 0" if not usado else ""
596
  results = self._execute_with_retry(
597
  f"""
598
- SELECT input_text, output_text, humor, modo_resposta,
599
  qualidade_score, tipo_interacao
600
  FROM training_examples
601
  {where_clause}
@@ -612,8 +830,9 @@ class Database:
612
  "output": r[1],
613
  "humor": r[2],
614
  "modo": r[3],
615
- "score": r[4],
616
- "tipo": r[5]
 
617
  }
618
  for r in results
619
  ]
@@ -637,6 +856,44 @@ class Database:
637
  except Exception as e:
638
  logger.error(f"❌ Erro ao marcar exemplos: {e}")
639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
  # ========================================================================
641
  # PRIVILÉGIOS
642
  # ========================================================================
@@ -757,10 +1014,14 @@ class Database:
757
  'timestamp': time.time()
758
  }
759
 
 
 
760
  return self.salvar_training_example(
761
  input_text=input_text,
762
  output_text=output_text,
763
  humor=contexto.get("humor_atualizado", "normal_ironico"),
 
 
764
  qualidade_score=qualidade_score,
765
  contexto_super_claro=contexto_super_claro,
766
  tipo_interacao=tipo_aprendizado
 
1
+ # modules/database.py — AKIRA V21 FINAL CORRIGIDO (Dezembro 2025) - CORREÇÃO: Com suporte a nivel_transicao
2
  """
3
  ✅ TOTALMENTE ADAPTADO ao index.js atualizado
4
  ✅ CORREÇÃO: Problema com message_id UNIQUE resolvido
5
+ ✅ CORREÇÃO: Suporte para nivel_transicao adicionado
6
  ✅ Métodos corretos para api.py, contexto.py, treinamento.py
7
  ✅ Estrutura completa com reply_metadata
8
  ✅ Todos os métodos necessários implementados
 
102
  emocao_detectada TEXT,
103
  confianca_emocao REAL DEFAULT 0.5,
104
 
105
+ -- Transição
106
+ nivel_transicao INTEGER DEFAULT 0,
107
+ info_transicao_json TEXT,
108
+
109
  -- Grupo
110
  grupo_id TEXT DEFAULT '',
111
  grupo_nome TEXT DEFAULT '',
 
119
  comando_executado TEXT,
120
  has_media BOOLEAN DEFAULT 0,
121
  media_type TEXT DEFAULT '',
122
+ message_id TEXT,
123
  bot_response_time_ms INTEGER DEFAULT 0,
124
  is_mention BOOLEAN DEFAULT 0,
125
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
 
174
  humor_atual TEXT DEFAULT 'normal_ironico',
175
  modo_resposta TEXT DEFAULT 'normal_ironico',
176
  nivel_transicao INTEGER DEFAULT 0,
177
+ info_transicao_json TEXT,
178
  humor_alvo TEXT DEFAULT 'normal_ironico',
179
  termos TEXT,
180
  girias TEXT,
 
202
  output_text TEXT NOT NULL,
203
  humor TEXT DEFAULT 'normal_ironico',
204
  modo_resposta TEXT DEFAULT 'normal_ironico',
205
+ nivel_transicao INTEGER DEFAULT 0,
206
  emocao_contexto TEXT,
207
  contexto_super_claro TEXT,
208
  tipo_interacao TEXT DEFAULT 'normal',
 
223
  contexto_id TEXT NOT NULL,
224
  humor_anterior TEXT NOT NULL,
225
  humor_novo TEXT NOT NULL,
226
+ nivel_transicao_anterior INTEGER DEFAULT 0,
227
+ nivel_transicao_novo INTEGER DEFAULT 0,
228
  emocao_trigger TEXT,
229
  confianca_emocao REAL,
 
230
  razao TEXT,
231
  intensidade REAL DEFAULT 0.5,
232
  contexto_mensagem TEXT,
 
278
  )
279
  ''')
280
 
281
+ # Interações (para treinamento)
282
+ c.execute('''
283
+ CREATE TABLE IF NOT EXISTS interacoes (
284
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
285
+ numero TEXT NOT NULL,
286
+ mensagem TEXT NOT NULL,
287
+ resposta TEXT NOT NULL,
288
+ humor TEXT DEFAULT 'normal_ironico',
289
+ modo_resposta TEXT DEFAULT 'normal_ironico',
290
+ nivel_transicao INTEGER DEFAULT 0,
291
+ emocao_detectada TEXT,
292
+ tipo_conversa TEXT DEFAULT 'pv',
293
+ reply_info_json TEXT,
294
+ qualidade_score REAL DEFAULT 1.0,
295
+ data_criacao DATETIME DEFAULT CURRENT_TIMESTAMP
296
+ )
297
+ ''')
298
+
299
  conn.commit()
300
+ logger.info("✅ Tabelas criadas/verificadas com suporte a nivel_transicao")
301
 
302
  except Exception as e:
303
  logger.error(f"❌ Erro ao criar tabelas: {e}")
 
324
  ("is_mention", "BOOLEAN DEFAULT 0"),
325
  ("has_media", "BOOLEAN DEFAULT 0"),
326
  ("media_type", "TEXT DEFAULT ''"),
327
+ ("message_id", "TEXT"),
328
+ ("bot_response_time_ms", "INTEGER DEFAULT 0"),
329
+ ("nivel_transicao", "INTEGER DEFAULT 0"),
330
+ ("info_transicao_json", "TEXT")
331
  ]
332
 
333
  for col_name, col_def in novas_colunas:
 
336
  except sqlite3.OperationalError:
337
  pass
338
 
339
+ # Colunas para contexto
340
+ contexto_colunas = [
341
+ ("info_transicao_json", "TEXT"),
342
+ ("nivel_transicao", "INTEGER DEFAULT 0")
343
+ ]
344
+
345
+ for col_name, col_def in contexto_colunas:
346
+ try:
347
+ c.execute(f"ALTER TABLE contexto ADD COLUMN {col_name} {col_def}")
348
+ except sqlite3.OperationalError:
349
+ pass
350
+
351
+ # Colunas para transições
352
+ transicoes_colunas = [
353
+ ("nivel_transicao_anterior", "INTEGER DEFAULT 0"),
354
+ ("nivel_transicao_novo", "INTEGER DEFAULT 0")
355
+ ]
356
+
357
+ for col_name, col_def in transicoes_colunas:
358
+ try:
359
+ c.execute(f"ALTER TABLE transicoes_humor ADD COLUMN {col_name} {col_def}")
360
+ except sqlite3.OperationalError:
361
+ pass
362
+
363
+ # Colunas para training_examples
364
+ training_colunas = [
365
+ ("nivel_transicao", "INTEGER DEFAULT 0")
366
+ ]
367
+
368
+ for col_name, col_def in training_colunas:
369
+ try:
370
+ c.execute(f"ALTER TABLE training_examples ADD COLUMN {col_name} {col_def}")
371
+ except sqlite3.OperationalError:
372
+ pass
373
+
374
  conn.commit()
375
 
376
  except Exception as e:
 
389
  modo_resposta: str = 'normal_ironico',
390
  emocao_detectada: str = None,
391
  confianca_emocao: float = 0.5,
392
+ nivel_transicao: int = 0,
393
+ info_transicao: dict = None,
394
  tipo_mensagem: str = 'texto',
395
  reply_info_json: str = None,
396
  usuario_nome: str = '',
 
406
  message_id: str = None,
407
  bot_response_time_ms: int = 0,
408
  is_mention: bool = False) -> bool:
409
+ """Salva mensagem no banco - COM SUPORTE A nivel_transicao"""
410
  try:
411
  numero_final = str(numero or usuario).strip()
412
  contexto_id = self._gerar_contexto_id(numero_final, tipo_conversa)
 
415
  if isinstance(reply_info_json, dict):
416
  reply_info_json = json.dumps(reply_info_json, ensure_ascii=False)
417
 
418
+ # Converte info_transicao para JSON se for dict
419
+ info_transicao_json = None
420
+ if info_transicao:
421
+ info_transicao_json = json.dumps(info_transicao, ensure_ascii=False)
422
+
423
+ # Gera message_id único se não fornecido
424
  if not message_id:
425
  timestamp = int(time.time() * 1000)
426
  random_suffix = random.randint(1000, 9999)
427
  message_id = f"{numero_final}_{timestamp}_{random_suffix}"
428
 
429
+ # Adiciona um sufixo aleatório extra para garantir unicidade
 
430
  unique_suffix = random.randint(100, 999)
431
  message_id = f"{message_id}_{unique_suffix}"
432
 
 
437
  (usuario, usuario_nome, mensagem, resposta, numero, contexto_id, tipo_contexto,
438
  tipo_conversa, tipo_mensagem, is_reply, mensagem_original, mensagem_citada_limpa,
439
  reply_to_bot, reply_info_json, humor, modo_resposta, emocao_detectada,
440
+ confianca_emocao, nivel_transicao, info_transicao_json, grupo_id, grupo_nome,
441
+ audio_transcricao, fonte_stt, confianca_stt, comando_executado, has_media,
442
+ media_type, message_id, bot_response_time_ms, is_mention)
443
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
444
  """,
445
  (
446
  usuario[:50], usuario_nome[:100] or usuario[:100],
 
448
  contexto_id, tipo_conversa, tipo_conversa, tipo_mensagem,
449
  int(is_reply), mensagem_original, mensagem_citada_limpa,
450
  int(reply_to_bot), reply_info_json, humor, modo_resposta,
451
+ emocao_detectada, confianca_emocao, nivel_transicao,
452
+ info_transicao_json, grupo_id[:50], grupo_nome[:100],
453
  audio_transcricao[:2000] if audio_transcricao else None,
454
  fonte_stt[:50], confianca_stt, comando_executado[:100] if comando_executado else None,
455
  int(has_media), media_type[:50], message_id[:200],
 
459
  fetch=False
460
  )
461
 
462
+ logger.debug(f"✅ Mensagem salva: {numero_final} | Nível: {nivel_transicao} | Message ID: {message_id}")
463
  return True
464
 
465
  except sqlite3.IntegrityError as e:
 
475
  (usuario, usuario_nome, mensagem, resposta, numero, contexto_id, tipo_contexto,
476
  tipo_conversa, tipo_mensagem, is_reply, mensagem_original, mensagem_citada_limpa,
477
  reply_to_bot, reply_info_json, humor, modo_resposta, emocao_detectada,
478
+ confianca_emocao, nivel_transicao, info_transicao_json, grupo_id, grupo_nome,
479
+ audio_transcricao, fonte_stt, confianca_stt, comando_executado, has_media,
480
+ media_type, message_id, bot_response_time_ms, is_mention)
481
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
482
  """,
483
  (
484
  usuario[:50], usuario_nome[:100] or usuario[:100],
 
486
  contexto_id, tipo_conversa, tipo_conversa, tipo_mensagem,
487
  int(is_reply), mensagem_original, mensagem_citada_limpa,
488
  int(reply_to_bot), reply_info_json, humor, modo_resposta,
489
+ emocao_detectada, confianca_emocao, nivel_transicao,
490
+ info_transicao_json, grupo_id[:50], grupo_nome[:100],
491
  audio_transcricao[:2000] if audio_transcricao else None,
492
  fonte_stt[:50], confianca_stt, comando_executado[:100] if comando_executado else None,
493
  int(has_media), media_type[:50], new_message_id[:200],
 
508
  def salvar_training_example(self, input_text: str, output_text: str,
509
  humor: str = "normal_ironico",
510
  modo_resposta: str = "normal_ironico",
511
+ nivel_transicao: int = 0,
512
  emocao_contexto: str = None,
513
  qualidade_score: float = 1.0,
514
  contexto_super_claro: Dict = None,
515
  tipo_interacao: str = "normal",
516
  score_relevancia: float = 1.0,
517
  tags: List[str] = None) -> bool:
518
+ """Salva exemplo de treinamento - COM nivel_transicao"""
519
  try:
520
  contexto_json = json.dumps(contexto_super_claro, ensure_ascii=False) if contexto_super_claro else None
521
  tags_str = ",".join(tags) if tags else ""
 
523
  self._execute_with_retry(
524
  """
525
  INSERT INTO training_examples
526
+ (input_text, output_text, humor, modo_resposta, nivel_transicao,
527
+ emocao_contexto, qualidade_score, contexto_super_claro,
528
+ tipo_interacao, score_relevancia, tags)
529
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
530
  """,
531
  (
532
  input_text[:2000], output_text[:2000], humor, modo_resposta,
533
+ nivel_transicao, emocao_contexto, qualidade_score, contexto_json,
534
  tipo_interacao, score_relevancia, tags_str[:200]
535
  ),
536
  commit=True,
537
  fetch=False
538
  )
539
+ logger.debug(f"✅ Training example salvo | Nível: {nivel_transicao} | Score: {qualidade_score:.2f}")
540
  return True
541
  except Exception as e:
542
  logger.error(f"❌ Erro ao salvar training: {e}")
543
  return False
544
 
545
  def salvar_transicao_humor(self, numero: str, humor_anterior: str,
546
+ humor_novo: str, nivel_transicao_anterior: int = 0,
547
+ nivel_transicao_novo: int = 0,
548
+ emocao_trigger: str = None,
549
  confianca_emocao: float = 0.5,
 
550
  razao: str = "", intensidade: float = 0.5,
551
  contexto_mensagem: str = None):
552
+ """Salva transição de humor - COM nivel_transicao"""
553
  try:
554
  contexto_id = self._gerar_contexto_id(numero, 'auto')
555
 
556
  self._execute_with_retry(
557
  """
558
  INSERT INTO transicoes_humor
559
+ (numero, contexto_id, humor_anterior, humor_novo,
560
+ nivel_transicao_anterior, nivel_transicao_novo, emocao_trigger,
561
+ confianca_emocao, razao, intensidade, contexto_mensagem)
562
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
563
  """,
564
  (
565
  str(numero).strip(), contexto_id, humor_anterior, humor_novo,
566
+ nivel_transicao_anterior, nivel_transicao_novo, emocao_trigger,
567
+ confianca_emocao, razao[:200], intensidade,
568
+ contexto_mensagem[:500] if contexto_mensagem else None
569
  ),
570
  commit=True,
571
  fetch=False
572
  )
573
+ logger.debug(f"🎭 Transição salva: {humor_anterior}→{humor_novo} | Nível: {nivel_transicao_anterior}→{nivel_transicao_novo}")
574
  except Exception as e:
575
  logger.error(f"❌ Erro ao salvar transição: {e}")
576
 
 
614
  logger.error(f"❌ Erro ao salvar gíria: {e}")
615
  return False
616
 
617
+ # ========================================================================
618
+ # MÉTODOS DE CONTEXTO - COM nivel_transicao
619
+ # ========================================================================
620
+
621
+ def atualizar_contexto(self, numero: str, humor_atual: str = None,
622
+ modo_resposta: str = None, nivel_transicao: int = None,
623
+ info_transicao: dict = None, tom: str = None,
624
+ emocao_tendencia: str = None) -> bool:
625
+ """Atualiza contexto do usuário - COM nivel_transicao"""
626
+ try:
627
+ numero_final = str(numero).strip()
628
+ contexto_id = self._gerar_contexto_id(numero_final, 'auto')
629
+
630
+ # Verifica se contexto existe
631
+ result = self._execute_with_retry(
632
+ "SELECT 1 FROM contexto WHERE numero = ?",
633
+ (numero_final,),
634
+ fetch=True
635
+ )
636
+
637
+ info_transicao_json = None
638
+ if info_transicao:
639
+ info_transicao_json = json.dumps(info_transicao, ensure_ascii=False)
640
+
641
+ if result:
642
+ # Atualiza existente
643
+ updates = []
644
+ params = []
645
+
646
+ if humor_atual:
647
+ updates.append("humor_atual = ?")
648
+ params.append(humor_atual)
649
+ if modo_resposta:
650
+ updates.append("modo_resposta = ?")
651
+ params.append(modo_resposta)
652
+ if nivel_transicao is not None:
653
+ updates.append("nivel_transicao = ?")
654
+ params.append(nivel_transicao)
655
+ if info_transicao_json:
656
+ updates.append("info_transicao_json = ?")
657
+ params.append(info_transicao_json)
658
+ if tom:
659
+ updates.append("tom = ?")
660
+ params.append(tom)
661
+ if emocao_tendencia:
662
+ updates.append("emocao_tendencia = ?")
663
+ params.append(emocao_tendencia)
664
+
665
+ updates.append("ultimo_contato = CURRENT_TIMESTAMP")
666
+ updates.append("data_atualizacao = CURRENT_TIMESTAMP")
667
+
668
+ if updates:
669
+ query = f"UPDATE contexto SET {', '.join(updates)} WHERE numero = ?"
670
+ params.append(numero_final)
671
+ self._execute_with_retry(query, tuple(params), commit=True, fetch=False)
672
+ else:
673
+ # Cria novo contexto
674
+ self._execute_with_retry(
675
+ """
676
+ INSERT INTO contexto
677
+ (numero, contexto_id, humor_atual, modo_resposta, nivel_transicao,
678
+ info_transicao_json, tom, emocao_tendencia, ultimo_contato)
679
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
680
+ """,
681
+ (
682
+ numero_final, contexto_id,
683
+ humor_atual or 'normal_ironico',
684
+ modo_resposta or 'normal_ironico',
685
+ nivel_transicao or 0,
686
+ info_transicao_json,
687
+ tom or 'normal',
688
+ emocao_tendencia or 'neutral'
689
+ ),
690
+ commit=True,
691
+ fetch=False
692
+ )
693
+
694
+ logger.debug(f"✅ Contexto atualizado: {numero_final} | Nível: {nivel_transicao}")
695
+ return True
696
+
697
+ except Exception as e:
698
+ logger.error(f"❌ Erro ao atualizar contexto: {e}")
699
+ return False
700
+
701
+ def recuperar_contexto(self, numero: str) -> Dict[str, Any]:
702
+ """Recupera contexto completo do usuário"""
703
+ try:
704
+ result = self._execute_with_retry(
705
+ """
706
+ SELECT humor_atual, modo_resposta, nivel_transicao, info_transicao_json,
707
+ tom, emocao_tendencia, ultimo_contato
708
+ FROM contexto WHERE numero = ?
709
+ """,
710
+ (str(numero).strip(),),
711
+ fetch=True
712
+ )
713
+
714
+ if result:
715
+ row = result[0]
716
+ info_transicao = {}
717
+ if row[3]:
718
+ try:
719
+ info_transicao = json.loads(row[3])
720
+ except:
721
+ pass
722
+
723
+ return {
724
+ "humor_atual": row[0] or "normal_ironico",
725
+ "modo_resposta": row[1] or "normal_ironico",
726
+ "nivel_transicao": row[2] or 0,
727
+ "info_transicao": info_transicao,
728
+ "tom": row[4] or "normal",
729
+ "emocao_tendencia": row[5] or "neutral",
730
+ "ultimo_contato": row[6]
731
+ }
732
+
733
+ return {
734
+ "humor_atual": "normal_ironico",
735
+ "modo_resposta": "normal_ironico",
736
+ "nivel_transicao": 0,
737
+ "info_transicao": {},
738
+ "tom": "normal",
739
+ "emocao_tendencia": "neutral",
740
+ "ultimo_contato": None
741
+ }
742
+
743
+ except Exception as e:
744
+ logger.error(f"❌ Erro ao recuperar contexto: {e}")
745
+ return {}
746
+
747
  # ========================================================================
748
  # MÉTODOS DE RECUPERAÇÃO
749
  # ========================================================================
 
754
  results = self._execute_with_retry(
755
  """
756
  SELECT mensagem, resposta, is_reply, mensagem_original,
757
+ reply_to_bot, humor, modo_resposta, nivel_transicao, timestamp
758
  FROM mensagens
759
  WHERE numero = ? AND deletado = 0
760
  ORDER BY timestamp DESC
 
795
  except Exception:
796
  return "normal_ironico"
797
 
798
+ def recuperar_nivel_transicao(self, numero: str) -> int:
799
+ """Recupera nível de transição"""
800
+ try:
801
+ result = self._execute_with_retry(
802
+ "SELECT nivel_transicao FROM contexto WHERE numero = ?",
803
+ (str(numero).strip(),),
804
+ fetch=True
805
+ )
806
+ return result[0][0] if result else 0
807
+ except Exception:
808
+ return 0
809
+
810
  def recuperar_training_examples(self, limite: int = 100, usado: bool = False) -> List[Dict]:
811
  """Recupera exemplos de treinamento"""
812
  try:
813
  where_clause = "WHERE usado = 0" if not usado else ""
814
  results = self._execute_with_retry(
815
  f"""
816
+ SELECT input_text, output_text, humor, modo_resposta, nivel_transicao,
817
  qualidade_score, tipo_interacao
818
  FROM training_examples
819
  {where_clause}
 
830
  "output": r[1],
831
  "humor": r[2],
832
  "modo": r[3],
833
+ "nivel_transicao": r[4],
834
+ "score": r[5],
835
+ "tipo": r[6]
836
  }
837
  for r in results
838
  ]
 
856
  except Exception as e:
857
  logger.error(f"❌ Erro ao marcar exemplos: {e}")
858
 
859
+ # ========================================================================
860
+ # MÉTODO PARA REGISTRAR INTERAÇÃO (PARA TREINAMENTO)
861
+ # ========================================================================
862
+
863
+ def registrar_interacao(self, numero: str, mensagem: str, resposta: str,
864
+ humor: str = 'normal_ironico',
865
+ modo_resposta: str = 'normal_ironico',
866
+ nivel_transicao: int = 0,
867
+ emocao_detectada: str = None,
868
+ tipo_conversa: str = 'pv',
869
+ reply_info_json: str = None,
870
+ qualidade_score: float = 1.0) -> bool:
871
+ """Registra interação para treinamento - COM nivel_transicao"""
872
+ try:
873
+ if isinstance(reply_info_json, dict):
874
+ reply_info_json = json.dumps(reply_info_json, ensure_ascii=False)
875
+
876
+ self._execute_with_retry(
877
+ """
878
+ INSERT INTO interacoes
879
+ (numero, mensagem, resposta, humor, modo_resposta, nivel_transicao,
880
+ emocao_detectada, tipo_conversa, reply_info_json, qualidade_score)
881
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
882
+ """,
883
+ (
884
+ str(numero).strip(), mensagem[:2000], resposta[:2000], humor, modo_resposta,
885
+ nivel_transicao, emocao_detectada, tipo_conversa, reply_info_json,
886
+ qualidade_score
887
+ ),
888
+ commit=True,
889
+ fetch=False
890
+ )
891
+ logger.debug(f"✅ Interação registrada: {numero} | Nível: {nivel_transicao}")
892
+ return True
893
+ except Exception as e:
894
+ logger.error(f"❌ Erro ao registrar interação: {e}")
895
+ return False
896
+
897
  # ========================================================================
898
  # PRIVILÉGIOS
899
  # ========================================================================
 
1014
  'timestamp': time.time()
1015
  }
1016
 
1017
+ nivel_transicao = contexto.get('nivel_transicao', 0)
1018
+
1019
  return self.salvar_training_example(
1020
  input_text=input_text,
1021
  output_text=output_text,
1022
  humor=contexto.get("humor_atualizado", "normal_ironico"),
1023
+ modo_resposta=contexto.get("modo_resposta", "normal_ironico"),
1024
+ nivel_transicao=nivel_transicao,
1025
  qualidade_score=qualidade_score,
1026
  contexto_super_claro=contexto_super_claro,
1027
  tipo_interacao=tipo_aprendizado