akra35567 commited on
Commit
4de9ae8
·
verified ·
1 Parent(s): fa1cc80

Update modules/api.py

Browse files
Files changed (1) hide show
  1. modules/api.py +63 -125
modules/api.py CHANGED
@@ -1,19 +1,14 @@
1
- # modules/api.py — V18 FINAL RESPOSTA GARANTIDA + CHAVE "resposta" CORRIGIDA
2
  import time
3
- import re
4
  import datetime
5
- from typing import List
6
  from flask import Blueprint, request, jsonify, make_response
7
  from loguru import logger
8
- import requests
9
  from .contexto import Contexto
10
  from .database import Database
11
  from .treinamento import Treinamento
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
@@ -36,80 +31,12 @@ class SimpleTTLCache:
36
  raise KeyError(key)
37
  return self._store[key][0]
38
 
39
-
40
- # === OLLAMA PROVIDER ===
41
- class OllamaProvider:
42
- def __init__(self):
43
- self.url = config.OLLAMA_SERVER_URL
44
- self.model = config.OLLAMA_MODEL
45
- self.session = requests.Session()
46
- self.session.headers.update({"Content-Type": "application/json"})
47
- logger.info(f"OLLAMA PROVIDER → {self.url}")
48
-
49
- def generate(self, prompt: str, context_history: List[dict] = []) -> str:
50
- payload = {
51
- "model": self.model,
52
- "prompt": prompt,
53
- "stream": False,
54
- "options": {
55
- "temperature": config.TEMPERATURE,
56
- "num_predict": config.MAX_TOKENS,
57
- "num_ctx": config.NUM_CTX,
58
- "num_thread": 2
59
- }
60
- }
61
- logger.info(f"Enviando para Ollama: {prompt[:100]}...")
62
-
63
- for tentativa in range(3):
64
- try:
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):
92
- self.ollama = OllamaProvider()
93
- self.providers = ['ollama']
94
- logger.info("PROVEDOR: Ollama (único)")
95
-
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):
103
  self.config = cfg_module
104
  self.api = Blueprint("akira_api", __name__)
105
  self.contexto_cache = SimpleTTLCache(ttl_seconds=300)
106
- self.providers = LLMManager(self.config)
107
  self.db = Database(getattr(self.config, 'DB_PATH', 'akira.db'))
108
- try:
109
- self.web_search = WebSearch()
110
- logger.info("WebSearch OK")
111
- except:
112
- self.web_search = None
113
  self._setup_routes()
114
  self._setup_trainer()
115
 
@@ -155,23 +82,68 @@ class AkiraAPI:
155
  agora = datetime.datetime.now()
156
  return jsonify({'resposta': f"São {agora.strftime('%H:%M')} em Luanda, puto."})
157
 
158
- # CONTEXTO
159
  contexto = self._get_user_context(numero)
160
- prompt = self._build_prompt(usuario, numero, mensagem, mensagem_citada, contexto)
161
-
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
175
  try:
176
  trainer = Treinamento(self.db)
177
  trainer.registrar_interacao(
@@ -183,12 +155,9 @@ class AkiraAPI:
183
  mensagem_original=mensagem_citada
184
  )
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
192
 
193
  except Exception as e:
194
  logger.exception("Erro em /akira")
@@ -203,35 +172,4 @@ class AkiraAPI:
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}")
207
- return self.contexto_cache[numero]
208
-
209
- def _build_prompt(self, usuario, numero, mensagem, mensagem_citada, contexto):
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 = f"""Você é Akira, uma angolana de Luanda, 18 anos, forte e debochada.
215
- Fala com gírias: epá, puto, kandando, kota, kamba, bué, xingue, mambo.
216
- Responde em 1-3 frases curtas, sem censura.
217
-
218
- Data/hora: {now}
219
- Bot: 244952786417
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"### Resposta ao reply ###\n{mensagem_citada}\n\n{mensagem}\n\n"
232
- else:
233
- prompt += f"### Mensagem ###\n{mensagem}\n\n"
234
-
235
- prompt += "Akira:"
236
-
237
- return prompt.strip()
 
1
+ # modules/api.py — V26OLLAMA PRINCIPAL + MISTRAL + GEMINI FALLBACK
2
  import time
 
3
  import datetime
4
+ import requests
5
  from flask import Blueprint, request, jsonify, make_response
6
  from loguru import logger
 
7
  from .contexto import Contexto
8
  from .database import Database
9
  from .treinamento import Treinamento
 
10
  import modules.config as config
11
 
 
 
12
  class SimpleTTLCache:
13
  def __init__(self, ttl_seconds: int = 300):
14
  self.ttl = ttl_seconds
 
31
  raise KeyError(key)
32
  return self._store[key][0]
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  class AkiraAPI:
35
  def __init__(self, cfg_module):
36
  self.config = cfg_module
37
  self.api = Blueprint("akira_api", __name__)
38
  self.contexto_cache = SimpleTTLCache(ttl_seconds=300)
 
39
  self.db = Database(getattr(self.config, 'DB_PATH', 'akira.db'))
 
 
 
 
 
40
  self._setup_routes()
41
  self._setup_trainer()
42
 
 
82
  agora = datetime.datetime.now()
83
  return jsonify({'resposta': f"São {agora.strftime('%H:%M')} em Luanda, puto."})
84
 
85
+ # CONTEXTO LOCAL
86
  contexto = self._get_user_context(numero)
 
 
 
 
 
 
 
 
87
 
88
+ # === 1. TENTA OLLAMA AUTÔNOMO (PRINCIPAL) ===
89
+ payload = {
90
+ "usuario": usuario,
91
+ "numero": numero,
92
+ "mensagem": mensagem,
93
+ "mensagem_citada": mensagem_citada,
94
+ "historico": contexto.obter_historico_para_llm()[-8:],
95
+ "data_hora": datetime.datetime.now().strftime('%d/%m %H:%M')
96
+ }
97
+
98
+ resposta = None
99
+ try:
100
+ resp = requests.post(config.OLLAMA_SERVER_URL, json=payload, timeout=60)
101
+ if resp.status_code == 200:
102
+ resposta = resp.json().get("resposta", "").strip()
103
+ logger.success("Resposta do Ollama (principal)")
104
+ except Exception as e:
105
+ logger.warning(f"Ollama falhou: {e}")
106
+
107
+ # === 2. FALLBACK 1: MISTRAL ===
108
+ if not resposta:
109
+ logger.info("Tentando Mistral como fallback...")
110
+ try:
111
+ mistral_payload = {
112
+ "model": "mistral-large-latest",
113
+ "messages": [{"role": "user", "content": f"Fala como angolana debochada: {mensagem}"}]
114
+ }
115
+ headers = {"Authorization": f"Bearer {os.getenv('MISTRAL_API_KEY')}"}
116
+ resp = requests.post("https://api.mistral.ai/v1/chat/completions", json=mistral_payload, headers=headers, timeout=60)
117
+ if resp.status_code == 200:
118
+ resposta = resp.json()["choices"][0]["message"]["content"].strip()
119
+ logger.success("Resposta do Mistral (fallback 1)")
120
+ except Exception as e:
121
+ logger.warning(f"Mistral falhou: {e}")
122
+
123
+ # === 3. FALLBACK 2: GEMINI ===
124
+ if not resposta:
125
+ logger.info("Tentando Gemini como fallback...")
126
+ try:
127
+ gemini_payload = {
128
+ "contents": [{"parts": [{"text": f"Fala como angolana debochada: {mensagem}"}]}]
129
+ }
130
+ headers = {"Content-Type": "application/json"}
131
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={os.getenv('GEMINI_API_KEY')}"
132
+ resp = requests.post(url, json=gemini_payload, headers=headers, timeout=60)
133
+ if resp.status_code == 200:
134
+ resposta = resp.json()["candidates"][0]["content"]["parts"][0]["text"].strip()
135
+ logger.success("Resposta do Gemini (fallback 2)")
136
+ except Exception as e:
137
+ logger.warning(f"Gemini falhou: {e}")
138
+
139
+ # === RESPOSTA FINAL (SE TODOS FALHAREM) ===
140
+ if not resposta:
141
+ resposta = resposta.replace("Olá", "Epá").replace("como posso ajudar", "kandando bué")
142
+ else:
143
+ resposta = "Epá, tô off... tenta de novo, kamba!"
144
 
145
+ # SALVA E TREINA
146
  contexto.atualizar_contexto(mensagem, resposta)
 
 
147
  try:
148
  trainer = Treinamento(self.db)
149
  trainer.registrar_interacao(
 
155
  mensagem_original=mensagem_citada
156
  )
157
  except Exception as e:
158
+ logger.warning(f"Erro ao treinar: {e}")
159
 
160
+ return jsonify({'resposta': resposta})
 
 
 
161
 
162
  except Exception as e:
163
  logger.exception("Erro em /akira")
 
172
  numero = "anonimo"
173
  if numero not in self.contexto_cache:
174
  self.contexto_cache[numero] = Contexto(self.db, usuario=numero)
175
+ return self.contexto_cache[numero]