Spaces:
Running
Running
Update modules/api.py
Browse files- modules/api.py +48 -23
modules/api.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# modules/api.py — V18 FINAL — RESPOSTA GARANTIDA
|
| 2 |
import time
|
| 3 |
import re
|
| 4 |
import datetime
|
|
@@ -12,22 +12,31 @@ from .treinamento import Treinamento
|
|
| 12 |
from .web_search import WebSearch
|
| 13 |
import modules.config as config
|
| 14 |
|
|
|
|
| 15 |
# === CACHE ===
|
| 16 |
class SimpleTTLCache:
|
| 17 |
def __init__(self, ttl_seconds: int = 300):
|
| 18 |
self.ttl = ttl_seconds
|
| 19 |
self._store = {}
|
|
|
|
| 20 |
def __contains__(self, key):
|
| 21 |
-
if key not in self._store:
|
|
|
|
| 22 |
_, expires = self._store[key]
|
| 23 |
-
if time.time() > expires:
|
|
|
|
|
|
|
| 24 |
return True
|
|
|
|
| 25 |
def __setitem__(self, key, value):
|
| 26 |
self._store[key] = (value, time.time() + self.ttl)
|
|
|
|
| 27 |
def __getitem__(self, key):
|
| 28 |
-
if key not in self:
|
|
|
|
| 29 |
return self._store[key][0]
|
| 30 |
|
|
|
|
| 31 |
# === OLLAMA PROVIDER ===
|
| 32 |
class OllamaProvider:
|
| 33 |
def __init__(self):
|
|
@@ -56,24 +65,27 @@ class OllamaProvider:
|
|
| 56 |
resp = self.session.post(self.url, json=payload, timeout=90)
|
| 57 |
if resp.status_code == 200:
|
| 58 |
data = resp.json()
|
| 59 |
-
|
|
|
|
| 60 |
if resposta:
|
| 61 |
logger.success(f"Resposta Ollama: {resposta[:60]}...")
|
| 62 |
return resposta
|
| 63 |
else:
|
| 64 |
-
logger.warning("Ollama respondeu vazio")
|
| 65 |
return "Epá, tô aqui, puto! Qual é a cena?"
|
| 66 |
else:
|
| 67 |
logger.warning(f"Ollama erro {resp.status_code}: {resp.text}")
|
| 68 |
except requests.exceptions.Timeout:
|
| 69 |
logger.warning(f"Timeout tentativa {tentativa + 1}/3...")
|
| 70 |
-
if tentativa < 2:
|
|
|
|
| 71 |
except Exception as e:
|
| 72 |
logger.error(f"Erro Ollama: {e}")
|
| 73 |
if tentativa == 2:
|
| 74 |
return "Epá, tô off por agora... volta já!"
|
| 75 |
return "Epá, tô off... tenta de novo, kamba!"
|
| 76 |
|
|
|
|
| 77 |
# === LLM MANAGER ===
|
| 78 |
class LLMManager:
|
| 79 |
def __init__(self, config_instance):
|
|
@@ -84,6 +96,7 @@ class LLMManager:
|
|
| 84 |
def generate(self, user_prompt: str, context_history: List[dict] = []) -> str:
|
| 85 |
return self.ollama.generate(user_prompt, context_history)
|
| 86 |
|
|
|
|
| 87 |
# === API ===
|
| 88 |
class AkiraAPI:
|
| 89 |
def __init__(self, cfg_module):
|
|
@@ -137,7 +150,7 @@ class AkiraAPI:
|
|
| 137 |
|
| 138 |
logger.info(f"[{usuario}] ({numero}): {mensagem[:60]}")
|
| 139 |
|
| 140 |
-
# HORA
|
| 141 |
if any(k in mensagem.lower() for k in ["hora", "horas"]):
|
| 142 |
agora = datetime.datetime.now()
|
| 143 |
return jsonify({'resposta': f"São {agora.strftime('%H:%M')} em Luanda, puto."})
|
|
@@ -149,13 +162,13 @@ class AkiraAPI:
|
|
| 149 |
# GERA RESPOSTA
|
| 150 |
resposta = self.providers.generate(prompt, contexto.obter_historico_para_llm())
|
| 151 |
|
| 152 |
-
# GARANTE
|
| 153 |
if not resposta or resposta.strip() == "":
|
| 154 |
resposta = "Epá, kandando bué! Tô aqui, puto!"
|
| 155 |
|
| 156 |
logger.success(f"RESPOSTA FINAL → {resposta[:60]}...")
|
| 157 |
|
| 158 |
-
# SALVA
|
| 159 |
contexto.atualizar_contexto(mensagem, resposta)
|
| 160 |
|
| 161 |
# TREINAMENTO
|
|
@@ -172,7 +185,7 @@ class AkiraAPI:
|
|
| 172 |
except Exception as e:
|
| 173 |
logger.warning(f"Erro ao salvar: {e}")
|
| 174 |
|
| 175 |
-
#
|
| 176 |
resposta_json = {'resposta': resposta}
|
| 177 |
logger.info(f"RETORNANDO JSON: {resposta_json}")
|
| 178 |
return jsonify(resposta_json), 200
|
|
@@ -186,7 +199,8 @@ class AkiraAPI:
|
|
| 186 |
return 'OK', 200
|
| 187 |
|
| 188 |
def _get_user_context(self, numero: str) -> Contexto:
|
| 189 |
-
if not numero:
|
|
|
|
| 190 |
if numero not in self.contexto_cache:
|
| 191 |
self.contexto_cache[numero] = Contexto(self.db, usuario=numero)
|
| 192 |
logger.info(f"Novo contexto: {numero}")
|
|
@@ -196,17 +210,28 @@ class AkiraAPI:
|
|
| 196 |
historico = contexto.obter_historico()
|
| 197 |
hist_text = '\n'.join([f"Usuário: {m[0]}\nAkira: {m[1]}" for m in historico[-8:]])
|
| 198 |
now = datetime.datetime.now().strftime('%d/%m %H:%M')
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
if historico:
|
| 206 |
-
|
|
|
|
| 207 |
if mensagem_citada:
|
| 208 |
-
|
| 209 |
else:
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
# modules/api.py — V18 FINAL — RESPOSTA GARANTIDA + CHAVE "resposta" CORRIGIDA
|
| 2 |
import time
|
| 3 |
import re
|
| 4 |
import datetime
|
|
|
|
| 12 |
from .web_search import WebSearch
|
| 13 |
import modules.config as config
|
| 14 |
|
| 15 |
+
|
| 16 |
# === CACHE ===
|
| 17 |
class SimpleTTLCache:
|
| 18 |
def __init__(self, ttl_seconds: int = 300):
|
| 19 |
self.ttl = ttl_seconds
|
| 20 |
self._store = {}
|
| 21 |
+
|
| 22 |
def __contains__(self, key):
|
| 23 |
+
if key not in self._store:
|
| 24 |
+
return False
|
| 25 |
_, expires = self._store[key]
|
| 26 |
+
if time.time() > expires:
|
| 27 |
+
del self._store[key]
|
| 28 |
+
return False
|
| 29 |
return True
|
| 30 |
+
|
| 31 |
def __setitem__(self, key, value):
|
| 32 |
self._store[key] = (value, time.time() + self.ttl)
|
| 33 |
+
|
| 34 |
def __getitem__(self, key):
|
| 35 |
+
if key not in self:
|
| 36 |
+
raise KeyError(key)
|
| 37 |
return self._store[key][0]
|
| 38 |
|
| 39 |
+
|
| 40 |
# === OLLAMA PROVIDER ===
|
| 41 |
class OllamaProvider:
|
| 42 |
def __init__(self):
|
|
|
|
| 65 |
resp = self.session.post(self.url, json=payload, timeout=90)
|
| 66 |
if resp.status_code == 200:
|
| 67 |
data = resp.json()
|
| 68 |
+
# CORRIGIDO: OLLAMA DEVOLVE "resposta", NÃO "response"
|
| 69 |
+
resposta = data.get("resposta", "").strip()
|
| 70 |
if resposta:
|
| 71 |
logger.success(f"Resposta Ollama: {resposta[:60]}...")
|
| 72 |
return resposta
|
| 73 |
else:
|
| 74 |
+
logger.warning(f"Ollama respondeu vazio. JSON recebido: {data}")
|
| 75 |
return "Epá, tô aqui, puto! Qual é a cena?"
|
| 76 |
else:
|
| 77 |
logger.warning(f"Ollama erro {resp.status_code}: {resp.text}")
|
| 78 |
except requests.exceptions.Timeout:
|
| 79 |
logger.warning(f"Timeout tentativa {tentativa + 1}/3...")
|
| 80 |
+
if tentativa < 2:
|
| 81 |
+
time.sleep(3)
|
| 82 |
except Exception as e:
|
| 83 |
logger.error(f"Erro Ollama: {e}")
|
| 84 |
if tentativa == 2:
|
| 85 |
return "Epá, tô off por agora... volta já!"
|
| 86 |
return "Epá, tô off... tenta de novo, kamba!"
|
| 87 |
|
| 88 |
+
|
| 89 |
# === LLM MANAGER ===
|
| 90 |
class LLMManager:
|
| 91 |
def __init__(self, config_instance):
|
|
|
|
| 96 |
def generate(self, user_prompt: str, context_history: List[dict] = []) -> str:
|
| 97 |
return self.ollama.generate(user_prompt, context_history)
|
| 98 |
|
| 99 |
+
|
| 100 |
# === API ===
|
| 101 |
class AkiraAPI:
|
| 102 |
def __init__(self, cfg_module):
|
|
|
|
| 150 |
|
| 151 |
logger.info(f"[{usuario}] ({numero}): {mensagem[:60]}")
|
| 152 |
|
| 153 |
+
# HORA RÁPIDA
|
| 154 |
if any(k in mensagem.lower() for k in ["hora", "horas"]):
|
| 155 |
agora = datetime.datetime.now()
|
| 156 |
return jsonify({'resposta': f"São {agora.strftime('%H:%M')} em Luanda, puto."})
|
|
|
|
| 162 |
# GERA RESPOSTA
|
| 163 |
resposta = self.providers.generate(prompt, contexto.obter_historico_para_llm())
|
| 164 |
|
| 165 |
+
# GARANTE RESPOSTA
|
| 166 |
if not resposta or resposta.strip() == "":
|
| 167 |
resposta = "Epá, kandando bué! Tô aqui, puto!"
|
| 168 |
|
| 169 |
logger.success(f"RESPOSTA FINAL → {resposta[:60]}...")
|
| 170 |
|
| 171 |
+
# SALVA CONTEXTO
|
| 172 |
contexto.atualizar_contexto(mensagem, resposta)
|
| 173 |
|
| 174 |
# TREINAMENTO
|
|
|
|
| 185 |
except Exception as e:
|
| 186 |
logger.warning(f"Erro ao salvar: {e}")
|
| 187 |
|
| 188 |
+
# RETORNA JSON
|
| 189 |
resposta_json = {'resposta': resposta}
|
| 190 |
logger.info(f"RETORNANDO JSON: {resposta_json}")
|
| 191 |
return jsonify(resposta_json), 200
|
|
|
|
| 199 |
return 'OK', 200
|
| 200 |
|
| 201 |
def _get_user_context(self, numero: str) -> Contexto:
|
| 202 |
+
if not numero:
|
| 203 |
+
numero = "anonimo"
|
| 204 |
if numero not in self.contexto_cache:
|
| 205 |
self.contexto_cache[numero] = Contexto(self.db, usuario=numero)
|
| 206 |
logger.info(f"Novo contexto: {numero}")
|
|
|
|
| 210 |
historico = contexto.obter_historico()
|
| 211 |
hist_text = '\n'.join([f"Usuário: {m[0]}\nAkira: {m[1]}" for m in historico[-8:]])
|
| 212 |
now = datetime.datetime.now().strftime('%d/%m %H:%M')
|
| 213 |
+
|
| 214 |
+
# PROMPT LIMPO — SEM [SYSTEM], [USER]
|
| 215 |
+
prompt = f"""Você é Akira, uma angolana de Luanda.
|
| 216 |
+
Fala com sotaque, gírias, kandando e sem censura.
|
| 217 |
+
Data/hora: {now}
|
| 218 |
+
Bot: 244952786417
|
| 219 |
+
{self.config.PERSONA}
|
| 220 |
+
|
| 221 |
+
### Usuário ###
|
| 222 |
+
- Nome: {usuario}
|
| 223 |
+
- Número: {numero}
|
| 224 |
+
|
| 225 |
+
"""
|
| 226 |
+
|
| 227 |
if historico:
|
| 228 |
+
prompt += f"### Histórico ###\n{hist_text}\n\n"
|
| 229 |
+
|
| 230 |
if mensagem_citada:
|
| 231 |
+
prompt += f"### MENSAGEM PRINCIPAL (REPLY) ###\n[REPLY]: {mensagem_citada}\n[RESPOSTA]: {mensagem}\n\n"
|
| 232 |
else:
|
| 233 |
+
prompt += f"### Mensagem ###\n{mensagem}\n\n"
|
| 234 |
+
|
| 235 |
+
prompt += "Akira:"
|
| 236 |
+
|
| 237 |
+
return prompt.strip()
|