Spaces:
Running
Running
Update modules/database.py
Browse files- modules/database.py +34 -22
modules/database.py
CHANGED
|
@@ -1,11 +1,6 @@
|
|
| 1 |
"""
|
| 2 |
Banco de dados SQLite para Akira IA.
|
| 3 |
-
|
| 4 |
-
- Migração automática de colunas
|
| 5 |
-
- Índices para performance
|
| 6 |
-
- Suporte a pronomes por tom (formal, rude, casual)
|
| 7 |
-
- WAL + retry para concorrência
|
| 8 |
-
- MÉTODOS: carregar_contexto, salvar_contexto, listar_girias_aprendidas (SEM fetch!)
|
| 9 |
"""
|
| 10 |
import sqlite3
|
| 11 |
import time
|
|
@@ -28,8 +23,10 @@ class Database:
|
|
| 28 |
# CONEXÃO COM RETRY + WAL
|
| 29 |
# ================================================================
|
| 30 |
def _get_connection(self) -> sqlite3.Connection:
|
|
|
|
| 31 |
for attempt in range(self.max_retries):
|
| 32 |
try:
|
|
|
|
| 33 |
conn = sqlite3.connect(self.db_path, timeout=30.0, check_same_thread=False)
|
| 34 |
conn.execute('PRAGMA journal_mode=WAL')
|
| 35 |
conn.execute('PRAGMA synchronous=NORMAL')
|
|
@@ -47,7 +44,7 @@ class Database:
|
|
| 47 |
raise sqlite3.OperationalError("Falha ao conectar após retries")
|
| 48 |
|
| 49 |
def _execute_with_retry(self, query: str, params: Optional[tuple] = None, commit: bool = False) -> Optional[List[Tuple]]:
|
| 50 |
-
|
| 51 |
for attempt in range(self.max_retries):
|
| 52 |
try:
|
| 53 |
with self._get_connection() as conn:
|
|
@@ -72,6 +69,7 @@ class Database:
|
|
| 72 |
# INICIALIZAÇÃO + MIGRAÇÃO AUTOMÁTICA
|
| 73 |
# ================================================================
|
| 74 |
def _init_db(self):
|
|
|
|
| 75 |
try:
|
| 76 |
with self._get_connection() as conn:
|
| 77 |
c = conn.cursor()
|
|
@@ -95,7 +93,7 @@ class Database:
|
|
| 95 |
chave TEXT PRIMARY KEY,
|
| 96 |
valor TEXT NOT NULL
|
| 97 |
);
|
| 98 |
-
-- estilos
|
| 99 |
CREATE TABLE IF NOT EXISTS estilos (
|
| 100 |
numero_usuario TEXT PRIMARY KEY,
|
| 101 |
estilo TEXT NOT NULL
|
|
@@ -230,6 +228,7 @@ class Database:
|
|
| 230 |
raise
|
| 231 |
|
| 232 |
def _ensure_all_columns_and_indexes(self):
|
|
|
|
| 233 |
try:
|
| 234 |
with self._get_connection() as conn:
|
| 235 |
c = conn.cursor()
|
|
@@ -293,11 +292,13 @@ class Database:
|
|
| 293 |
def carregar_contexto(self, user_key: str) -> Dict[str, Any]:
|
| 294 |
"""Carrega contexto do usuário com correção JSON segura"""
|
| 295 |
try:
|
|
|
|
| 296 |
result = self._execute_with_retry(
|
| 297 |
"SELECT historico, emocao_atual, termos, girias, tom FROM contexto WHERE user_key = ?",
|
| 298 |
(user_key,)
|
| 299 |
)
|
| 300 |
|
|
|
|
| 301 |
def safe_json_load(v, default):
|
| 302 |
if not v:
|
| 303 |
return default
|
|
@@ -356,6 +357,7 @@ class Database:
|
|
| 356 |
# MÉTODOS PRINCIPAIS
|
| 357 |
# ================================================================
|
| 358 |
def salvar_mensagem(self, usuario, mensagem, resposta, numero=None, is_reply=False, mensagem_original=None):
|
|
|
|
| 359 |
try:
|
| 360 |
cols = ['usuario', 'mensagem', 'resposta']
|
| 361 |
vals = [usuario, mensagem, resposta]
|
|
@@ -441,17 +443,27 @@ class Database:
|
|
| 441 |
def obter_info_geral(self, chave: str) -> Optional[str]:
|
| 442 |
result = self._execute_with_retry("SELECT valor FROM info_geral WHERE chave=?", (chave,))
|
| 443 |
return result[0][0] if result else None
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
Banco de dados SQLite para Akira IA.
|
| 3 |
+
[...]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
"""
|
| 5 |
import sqlite3
|
| 6 |
import time
|
|
|
|
| 23 |
# CONEXÃO COM RETRY + WAL
|
| 24 |
# ================================================================
|
| 25 |
def _get_connection(self) -> sqlite3.Connection:
|
| 26 |
+
# ... (Mantido o código original)
|
| 27 |
for attempt in range(self.max_retries):
|
| 28 |
try:
|
| 29 |
+
# check_same_thread=False é necessário para WAL em threads
|
| 30 |
conn = sqlite3.connect(self.db_path, timeout=30.0, check_same_thread=False)
|
| 31 |
conn.execute('PRAGMA journal_mode=WAL')
|
| 32 |
conn.execute('PRAGMA synchronous=NORMAL')
|
|
|
|
| 44 |
raise sqlite3.OperationalError("Falha ao conectar após retries")
|
| 45 |
|
| 46 |
def _execute_with_retry(self, query: str, params: Optional[tuple] = None, commit: bool = False) -> Optional[List[Tuple]]:
|
| 47 |
+
# ... (Mantido o código original)
|
| 48 |
for attempt in range(self.max_retries):
|
| 49 |
try:
|
| 50 |
with self._get_connection() as conn:
|
|
|
|
| 69 |
# INICIALIZAÇÃO + MIGRAÇÃO AUTOMÁTICA
|
| 70 |
# ================================================================
|
| 71 |
def _init_db(self):
|
| 72 |
+
# ... (Mantido o código original)
|
| 73 |
try:
|
| 74 |
with self._get_connection() as conn:
|
| 75 |
c = conn.cursor()
|
|
|
|
| 93 |
chave TEXT PRIMARY KEY,
|
| 94 |
valor TEXT NOT NULL
|
| 95 |
);
|
| 96 |
+
-- estilos (OBS: esta tabela parece não ser usada pelo contexto/treinamento)
|
| 97 |
CREATE TABLE IF NOT EXISTS estilos (
|
| 98 |
numero_usuario TEXT PRIMARY KEY,
|
| 99 |
estilo TEXT NOT NULL
|
|
|
|
| 228 |
raise
|
| 229 |
|
| 230 |
def _ensure_all_columns_and_indexes(self):
|
| 231 |
+
# ... (Mantido o código original)
|
| 232 |
try:
|
| 233 |
with self._get_connection() as conn:
|
| 234 |
c = conn.cursor()
|
|
|
|
| 292 |
def carregar_contexto(self, user_key: str) -> Dict[str, Any]:
|
| 293 |
"""Carrega contexto do usuário com correção JSON segura"""
|
| 294 |
try:
|
| 295 |
+
# Seleciona as colunas na ordem esperada: historico, emocao_atual, termos, girias, tom
|
| 296 |
result = self._execute_with_retry(
|
| 297 |
"SELECT historico, emocao_atual, termos, girias, tom FROM contexto WHERE user_key = ?",
|
| 298 |
(user_key,)
|
| 299 |
)
|
| 300 |
|
| 301 |
+
# Define a função de carregamento seguro de JSON (como no original)
|
| 302 |
def safe_json_load(v, default):
|
| 303 |
if not v:
|
| 304 |
return default
|
|
|
|
| 357 |
# MÉTODOS PRINCIPAIS
|
| 358 |
# ================================================================
|
| 359 |
def salvar_mensagem(self, usuario, mensagem, resposta, numero=None, is_reply=False, mensagem_original=None):
|
| 360 |
+
# ... (Mantido o código original)
|
| 361 |
try:
|
| 362 |
cols = ['usuario', 'mensagem', 'resposta']
|
| 363 |
vals = [usuario, mensagem, resposta]
|
|
|
|
| 443 |
def obter_info_geral(self, chave: str) -> Optional[str]:
|
| 444 |
result = self._execute_with_retry("SELECT valor FROM info_geral WHERE chave=?", (chave,))
|
| 445 |
return result[0][0] if result else None
|
| 446 |
+
|
| 447 |
+
# MÉTODO ANALISAR_EMOCOES_MENSAGEM FOI REMOVIDO DA CLASSE DATABASE
|
| 448 |
+
# E PERMANECE NO CONTEXTO/MÓDULO DE NEGÓCIO.
|
| 449 |
+
def salvar_embedding(self, texto: str, embedding: bytes):
|
| 450 |
+
"""Salva o embedding no banco (método adicionado por necessidade do treinamento.py)"""
|
| 451 |
+
try:
|
| 452 |
+
self._execute_with_retry(
|
| 453 |
+
"INSERT INTO embeddings (texto, embedding) VALUES (?, ?)",
|
| 454 |
+
(texto, embedding),
|
| 455 |
+
commit=True
|
| 456 |
+
)
|
| 457 |
+
except Exception as e:
|
| 458 |
+
logger.warning(f"Erro ao salvar embedding: {e}")
|
| 459 |
+
|
| 460 |
+
def salvar_preferencia_tom(self, numero_usuario: str, tom: str):
|
| 461 |
+
"""Salva a preferência de tom no banco (método adicionado por necessidade do treinamento.py)"""
|
| 462 |
+
try:
|
| 463 |
+
self._execute_with_retry(
|
| 464 |
+
"INSERT OR REPLACE INTO preferencias_tom (numero_usuario, tom) VALUES (?, ?)",
|
| 465 |
+
(numero_usuario, tom),
|
| 466 |
+
commit=True
|
| 467 |
+
)
|
| 468 |
+
except Exception as e:
|
| 469 |
+
logger.warning(f"Erro ao salvar preferencia de tom: {e}")
|