File size: 6,359 Bytes
446e02b
 
 
 
55a60a8
446e02b
 
 
f3cecb0
321e902
446e02b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1ab5ab0
5add2bf
085955a
8c657ef
321e902
 
 
5add2bf
321e902
085955a
d81e91f
321e902
61aa0bb
085955a
321e902
 
5add2bf
 
55a60a8
5add2bf
321e902
5add2bf
 
 
 
321e902
5add2bf
 
e97f201
085955a
b58fc00
 
321e902
 
 
 
bcb5dca
321e902
 
55a60a8
5add2bf
eef58ce
5add2bf
55a60a8
ef8f81a
 
 
5add2bf
ef8f81a
321e902
085955a
 
ef8f81a
5add2bf
ef8f81a
bcb5dca
085955a
eef58ce
ef8f81a
 
 
 
 
321e902
f3cecb0
085955a
321e902
085955a
 
f3cecb0
 
 
 
 
 
 
 
 
5add2bf
f3cecb0
 
 
 
 
085955a
f3cecb0
085955a
f3cecb0
 
5add2bf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# ================================================================
# AKIRA IA CORE ADAPTADO PARA SentenceTransformers
# ================================================================

import os
import time
import threading
from dataclasses import dataclass
from typing import Optional, List
from loguru import logger
from sentence_transformers import SentenceTransformer

from .database import Database

# ---------------------------------------------------------------
# EMBEDDINGS
# ---------------------------------------------------------------
EMBEDDING_MODEL = "paraphrase-multilingual-MiniLM-L12-v2"
embedding_model = SentenceTransformer(EMBEDDING_MODEL)

def gerar_embedding(text: str):
    """Gera embedding usando SentenceTransformers."""
    emb = embedding_model.encode(text, convert_to_numpy=True)
    return emb

# ---------------------------------------------------------------
# HEURÍSTICAS
# ---------------------------------------------------------------
PALAVRAS_RUDES = ['caralho','puto','merda','fdp','vsf','burro','idiota','parvo']
GIRIAS_ANGOLANAS = ['mano','puto','cota','mwangolé','kota','oroh','bué','fixe','baza','kuduro']

@dataclass
class Interacao:
    usuario: str
    mensagem: str
    resposta: str
    numero: str
    is_reply: bool = False
    mensagem_original: str = ""

# ---------------------------------------------------------------
# TREINAMENTO E MEMÓRIA
# ---------------------------------------------------------------
class Treinamento:
    def __init__(self, db: Database, interval_hours: int = 1):
        self.db = db
        self.interval_hours = interval_hours
        self._thread = None
        self._running = False
        self.privileged_users = ['244937035662','isaac','isaac quarenta']

    def registrar_interacao(
        self,
        usuario: str,
        mensagem: str,
        resposta: str,
        numero: str = '',
        is_reply: bool = False,
        mensagem_original: str = ''
    ):
        self.db.salvar_mensagem(usuario, mensagem, resposta, numero, is_reply, mensagem_original)
        self._aprender_em_tempo_real(numero, mensagem, resposta)

    def _aprender_em_tempo_real(self, numero: str, msg: str, resp: str):
        if not numero:
            return
        texto = f"{msg} {resp}".lower()
        embedding = gerar_embedding(texto)
        self.db.salvar_embedding(numero, msg, resp, embedding)

        rude = any(p in texto for p in PALAVRAS_RUDES)
        tom = 'rude' if rude else 'casual'
        self.db.registrar_tom_usuario(numero, tom, 0.9 if rude else 0.6, texto[:100])

    # Loop periódico
    def _run_loop(self):
        interval = max(1, self.interval_hours) * 3600
        while self._running:
            try:
                self.train_once()
            except Exception as e:
                logger.exception(f"Erro no treinamento: {e}")
            for _ in range(int(interval)):
                if not self._running: break
                time.sleep(1)

    def start_periodic_training(self):
        if self._running: return
        self._running = True
        self._thread = threading.Thread(target=self._run_loop, daemon=True)
        self._thread.start()

    def stop(self):
        self._running = False
        if self._thread: self._thread.join(timeout=5)

    def train_once(self):
        logger.info("Treinamento leve + embeddings iniciado...")
        # Apenas heurística + embeddings
        logger.info("Treinamento concluído.")

# ---------------------------------------------------------------
# CONFIGURAÇÃO CENTRAL
# ---------------------------------------------------------------
MISTRAL_API_KEY: str = "t4k0ZwQSy5kvvRE1jCJfYnGiFAVlQcMb"
GEMINI_API_KEY: str = "AIzaSyB-YryzcfTLECebI7nLq7o4XRfHnM2_D9Q"
SERPAPI_KEY: Optional[str] = "d81753b2909eb4746e1e0c92e052d6588ab725cf46f89fd9182c3da5daa477a5"
HF_TOKEN: Optional[str] = os.getenv("HF_TOKEN", "")

# MODELOS
MISTRAL_MODEL: str = "mistral-large-latest"
GEMINI_MODEL: str = "gemini-2.5-flash"

# PARÂMETROS
MAX_TOKENS: int = 1000
TOP_P: float = 0.9
USAR_NOME_PROBABILIDADE: float = 0.1
FALLBACK_RESPONSE: str = "Desculpa, puto, o modelo tá off hoje. Tenta depois!"

# PERSONALIDADE
PERSONA = """
Você é Akira, uma angolana (mulher) com personalidade forte e direta...
"""
SYSTEM_PROMPT = """
REGRAS CRÍTICAS:
- Sem markdown
- 1-2 frases curtas
- Tom formal para privilegiados, casual caso contrário
- Use gírias locais e risadas naturais
"""
HUMOR_INICIAL: str = "neutra"
MEMORIA_MAX: int = 20

# BANCO
DB_PATH: str = "/home/user/data/akira.db"
FINETUNED_PATH: str = "/home/user/data/finetuned_hermes"

# TREINAMENTO
START_PERIODIC_TRAINER: bool = True
TRAINING_INTERVAL_HOURS: int = 24

# API
API_PORT: int = int(os.getenv("PORT", "7860"))
API_HOST: str = "0.0.0.0"
PRIVILEGED_USERS: List[str] = ["244937035662", "isaac quarenta"]

# VALIDAÇÃO FLEXÍVEL
def validate_config() -> None:
    warnings = []

    if not MISTRAL_API_KEY or len(MISTRAL_API_KEY.strip()) < 20:
        warnings.append("MISTRAL_API_KEY inválida ou ausente")
        logger.warning("MISTRAL_API_KEY inválida → API principal DESATIVADA")
    else:
        logger.info("MISTRAL_API_KEY OK")

    if not GEMINI_API_KEY or len(GEMINI_API_KEY.strip()) < 30:
        warnings.append("GEMINI_API_KEY inválida ou ausente")
        logger.warning("GEMINI_API_KEY inválida → fallback DESATIVADO")
    else:
        logger.info("GEMINI_API_KEY OK")

    if warnings:
        logger.warning(f"AVISOS: {', '.join(warnings)}")
        logger.warning("App vai rodar com fallbacks limitados")
    else:
        logger.info("Todas as chaves OK")

    os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
    _init_db()

def _init_db() -> None:
    import sqlite3
    try:
        conn = sqlite3.connect(DB_PATH)
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS conversas (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT,
                mensagem TEXT,
                resposta TEXT,
                embedding BLOB,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        """)
        conn.commit()
        conn.close()
        logger.info(f"Banco inicializado: {DB_PATH}")
    except Exception as e:
        logger.error(f"Erro ao criar banco: {e}")
        raise

validate_config()