akra35567 commited on
Commit
602591b
·
verified ·
1 Parent(s): 5355c8a

Update modules/database.py

Browse files
Files changed (1) hide show
  1. modules/database.py +150 -3
modules/database.py CHANGED
@@ -8,7 +8,7 @@ Banco de dados SQLite para Akira IA - COMPLETAMENTE ATUALIZADO
8
  ✅ Memória emocional BERT GoEmotions
9
  ✅ Backup e otimização automática
10
  ✅ ADAPTADO ao payload do index.js
11
- ✅ NOVAS TABELAS: audio_transcricoes, grupo_contexto
12
  ✅ NOVAS COLUNAS: tipo_mensagem, reply_info_json
13
  """
14
  import sqlite3
@@ -145,6 +145,7 @@ class Database:
145
  except sqlite3.OperationalError:
146
  pass # Coluna já existe
147
 
 
148
  indexes = [
149
  "CREATE INDEX IF NOT EXISTS idx_mensagens_numero ON mensagens(numero, deletado)",
150
  "CREATE INDEX IF NOT EXISTS idx_mensagens_contexto ON mensagens(contexto_id)",
@@ -160,7 +161,8 @@ class Database:
160
  "CREATE INDEX IF NOT EXISTS idx_comandos_numero ON comandos_executados(numero, timestamp)",
161
  "CREATE INDEX IF NOT EXISTS idx_reset_numero ON reset_log(numero, timestamp)",
162
  "CREATE INDEX IF NOT EXISTS idx_audio_usuario ON audio_transcricoes(numero_usuario, sucesso)",
163
- "CREATE INDEX IF NOT EXISTS idx_grupo_contexto_id ON grupo_contexto(grupo_id, contexto_id)"
 
164
  ]
165
 
166
  for idx_query in indexes:
@@ -361,6 +363,19 @@ class Database:
361
  )
362
  ''')
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  conn.commit()
365
 
366
  logger.info("✅ Todas as tabelas criadas/verificadas (ADAPTADO)")
@@ -600,7 +615,7 @@ class Database:
600
  "mensagens", "contexto", "transicoes_humor",
601
  "girias_aprendidas", "training_examples",
602
  "comandos_executados", "reset_log",
603
- "audio_transcricoes", "grupo_contexto"
604
  ]
605
 
606
  for tabela in tabelas_para_resetar:
@@ -1147,6 +1162,107 @@ class Database:
1147
  logger.error(f"Erro ao recuperar histórico de áudio: {e}")
1148
  return []
1149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1150
  # ================================================================
1151
  # MÉTODOS FALTANTES ADICIONADOS (RESOLVEM OS ERROS)
1152
  # ================================================================
@@ -1317,11 +1433,20 @@ class Database:
1317
  )
1318
  girias_aprendidas = result[0][0] if result else 0
1319
 
 
 
 
 
 
 
 
 
1320
  return {
1321
  "total_mensagens": total_mensagens,
1322
  "ultima_atividade": ultima_atividade,
1323
  "transicoes_humor": transicoes_humor,
1324
  "girias_aprendidas": girias_aprendidas,
 
1325
  "privilegiado": self.is_usuario_privilegiado(numero)
1326
  }
1327
 
@@ -1332,6 +1457,7 @@ class Database:
1332
  "ultima_atividade": None,
1333
  "transicoes_humor": 0,
1334
  "girias_aprendidas": 0,
 
1335
  "privilegiado": False
1336
  }
1337
 
@@ -1354,6 +1480,27 @@ class Database:
1354
  logger.error(f"Erro ao limpar mensagens antigas: {e}")
1355
  return 0
1356
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1357
  # ================================================================
1358
  # FECHAMENTO SEGURO DA CONEXÃO
1359
  # ================================================================
 
8
  ✅ Memória emocional BERT GoEmotions
9
  ✅ Backup e otimização automática
10
  ✅ ADAPTADO ao payload do index.js
11
+ ✅ NOVAS TABELAS: audio_transcricoes, grupo_contexto, embeddings
12
  ✅ NOVAS COLUNAS: tipo_mensagem, reply_info_json
13
  """
14
  import sqlite3
 
145
  except sqlite3.OperationalError:
146
  pass # Coluna já existe
147
 
148
+ # ÍNDICES (ATUALIZADO COM NOVA TABELA EMBEDDINGS)
149
  indexes = [
150
  "CREATE INDEX IF NOT EXISTS idx_mensagens_numero ON mensagens(numero, deletado)",
151
  "CREATE INDEX IF NOT EXISTS idx_mensagens_contexto ON mensagens(contexto_id)",
 
161
  "CREATE INDEX IF NOT EXISTS idx_comandos_numero ON comandos_executados(numero, timestamp)",
162
  "CREATE INDEX IF NOT EXISTS idx_reset_numero ON reset_log(numero, timestamp)",
163
  "CREATE INDEX IF NOT EXISTS idx_audio_usuario ON audio_transcricoes(numero_usuario, sucesso)",
164
+ "CREATE INDEX IF NOT EXISTS idx_grupo_contexto_id ON grupo_contexto(grupo_id, contexto_id)",
165
+ "CREATE INDEX IF NOT EXISTS idx_embeddings_numero ON embeddings(numero, timestamp)"
166
  ]
167
 
168
  for idx_query in indexes:
 
363
  )
364
  ''')
365
 
366
+ # NOVA TABELA: EMBEDDINGS (PARA BUSCA SEMÂNTICA)
367
+ c.execute('''
368
+ CREATE TABLE IF NOT EXISTS embeddings (
369
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
370
+ numero TEXT NOT NULL,
371
+ texto TEXT NOT NULL,
372
+ embedding BLOB NOT NULL,
373
+ modelo TEXT DEFAULT 'MiniLM-L12-v2',
374
+ hash_texto TEXT,
375
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
376
+ )
377
+ ''')
378
+
379
  conn.commit()
380
 
381
  logger.info("✅ Todas as tabelas criadas/verificadas (ADAPTADO)")
 
615
  "mensagens", "contexto", "transicoes_humor",
616
  "girias_aprendidas", "training_examples",
617
  "comandos_executados", "reset_log",
618
+ "audio_transcricoes", "grupo_contexto", "embeddings"
619
  ]
620
 
621
  for tabela in tabelas_para_resetar:
 
1162
  logger.error(f"Erro ao recuperar histórico de áudio: {e}")
1163
  return []
1164
 
1165
+ # ================================================================
1166
+ # NOVO MÉTODO: SALVAR EMBEDDINGS (RESOLVE O ERRO)
1167
+ # ================================================================
1168
+ def salvar_embedding(self, numero: str, texto: str, embedding: bytes) -> bool:
1169
+ """
1170
+ Salva embedding para busca semântica.
1171
+ """
1172
+ try:
1173
+ if not embedding or len(embedding) == 0:
1174
+ logger.warning(f"Embedding vazio para {numero}")
1175
+ return False
1176
+
1177
+ if len(embedding) > 1000000: # Limite de 1MB
1178
+ logger.warning(f"Embedding muito grande: {len(embedding)} bytes, truncando")
1179
+ embedding = embedding[:1000000]
1180
+
1181
+ # Cria hash do texto para evitar duplicatas
1182
+ hash_texto = hashlib.md5(texto.encode()).hexdigest()
1183
+
1184
+ # Verifica se já existe
1185
+ result = self._execute_with_retry(
1186
+ "SELECT 1 FROM embeddings WHERE numero = ? AND hash_texto = ?",
1187
+ (str(numero).strip(), hash_texto),
1188
+ fetch=True
1189
+ )
1190
+
1191
+ if result:
1192
+ # Atualiza existente
1193
+ self._execute_with_retry(
1194
+ """
1195
+ UPDATE embeddings
1196
+ SET embedding = ?, timestamp = CURRENT_TIMESTAMP
1197
+ WHERE numero = ? AND hash_texto = ?
1198
+ """,
1199
+ (embedding, str(numero).strip(), hash_texto),
1200
+ commit=True,
1201
+ fetch=False
1202
+ )
1203
+ logger.debug(f"✅ Embedding atualizado: {numero}, texto: {texto[:50]}...")
1204
+ else:
1205
+ # Insere novo
1206
+ self._execute_with_retry(
1207
+ """
1208
+ INSERT INTO embeddings
1209
+ (numero, texto, embedding, modelo, hash_texto)
1210
+ VALUES (?, ?, ?, ?, ?)
1211
+ """,
1212
+ (
1213
+ str(numero).strip(),
1214
+ texto[:1000],
1215
+ embedding,
1216
+ 'MiniLM-L12-v2',
1217
+ hash_texto
1218
+ ),
1219
+ commit=True,
1220
+ fetch=False
1221
+ )
1222
+ logger.debug(f"✅ Embedding salvo: {numero}, tamanho: {len(embedding)} bytes, texto: {texto[:50]}...")
1223
+
1224
+ return True
1225
+
1226
+ except Exception as e:
1227
+ logger.error(f"❌ Erro ao salvar embedding: {e}")
1228
+ return False
1229
+
1230
+ # ================================================================
1231
+ # MÉTODO PARA RECUPERAR EMBEDDINGS
1232
+ # ================================================================
1233
+ def recuperar_embeddings(self, numero: str, limite: int = 20) -> List[Dict]:
1234
+ """Recupera embeddings do usuário"""
1235
+ try:
1236
+ results = self._execute_with_retry(
1237
+ """
1238
+ SELECT texto, embedding, modelo, timestamp
1239
+ FROM embeddings
1240
+ WHERE numero = ?
1241
+ ORDER BY timestamp DESC
1242
+ LIMIT ?
1243
+ """,
1244
+ (str(numero).strip(), limite),
1245
+ fetch=True
1246
+ )
1247
+
1248
+ embeddings = []
1249
+ for r in results:
1250
+ try:
1251
+ embeddings.append({
1252
+ "texto": r[0],
1253
+ "embedding": r[1], # bytes
1254
+ "modelo": r[2],
1255
+ "timestamp": r[3]
1256
+ })
1257
+ except:
1258
+ continue
1259
+
1260
+ return embeddings
1261
+
1262
+ except Exception as e:
1263
+ logger.error(f"Erro ao recuperar embeddings: {e}")
1264
+ return []
1265
+
1266
  # ================================================================
1267
  # MÉTODOS FALTANTES ADICIONADOS (RESOLVEM OS ERROS)
1268
  # ================================================================
 
1433
  )
1434
  girias_aprendidas = result[0][0] if result else 0
1435
 
1436
+ # Embeddings salvos
1437
+ result = self._execute_with_retry(
1438
+ "SELECT COUNT(*) FROM embeddings WHERE numero = ?",
1439
+ (str(numero).strip(),),
1440
+ fetch=True
1441
+ )
1442
+ embeddings_salvos = result[0][0] if result else 0
1443
+
1444
  return {
1445
  "total_mensagens": total_mensagens,
1446
  "ultima_atividade": ultima_atividade,
1447
  "transicoes_humor": transicoes_humor,
1448
  "girias_aprendidas": girias_aprendidas,
1449
+ "embeddings_salvos": embeddings_salvos,
1450
  "privilegiado": self.is_usuario_privilegiado(numero)
1451
  }
1452
 
 
1457
  "ultima_atividade": None,
1458
  "transicoes_humor": 0,
1459
  "girias_aprendidas": 0,
1460
+ "embeddings_salvos": 0,
1461
  "privilegiado": False
1462
  }
1463
 
 
1480
  logger.error(f"Erro ao limpar mensagens antigas: {e}")
1481
  return 0
1482
 
1483
+ # ================================================================
1484
+ # MÉTODO PARA LIMPAR EMBEDDINGS ANTIGOS
1485
+ # ================================================================
1486
+ def limpar_embeddings_antigos(self, dias: int = 90) -> int:
1487
+ """Limpa embeddings antigos"""
1488
+ try:
1489
+ result = self._execute_with_retry(
1490
+ """
1491
+ DELETE FROM embeddings
1492
+ WHERE timestamp < datetime('now', ?)
1493
+ """,
1494
+ (f'-{dias} days',),
1495
+ commit=True,
1496
+ fetch=False
1497
+ )
1498
+ logger.info(f"🧹 Embeddings antigos ({dias} dias) limpos: {result} registros")
1499
+ return result
1500
+ except Exception as e:
1501
+ logger.error(f"Erro ao limpar embeddings antigos: {e}")
1502
+ return 0
1503
+
1504
  # ================================================================
1505
  # FECHAMENTO SEGURO DA CONEXÃO
1506
  # ================================================================