akra35567 commited on
Commit
7151d4f
·
verified ·
1 Parent(s): 5489caa

Upload 22 files

Browse files
modules/api.py CHANGED
@@ -287,7 +287,7 @@ class LLMManager:
287
  logger.warning(f"Together AI falhou: {e}")
288
  self.together_client = None
289
 
290
- def generate(self, user_prompt: str, context_history: List[dict] = [], is_privileged: bool = False) -> str:
291
  """
292
  Gera resposta usando provedores LLM com fallback em loop.
293
 
@@ -727,19 +727,15 @@ class AkiraAPI:
727
  # Captura robusta de JSON
728
  raw_data = request.data
729
  try:
730
- # silent=True impede que o Flask aborte com HTTP 400 em caso de erro
731
  data = request.get_json(force=True, silent=True)
732
  if data is None:
733
- raise ValueError("get_json retornou None")
 
 
734
  except Exception as e:
735
- self.logger.warning(f"[API] Falha no get_json padrão, tentando decodificação manual: {e}")
736
- try:
737
- # Tenta UTF-8 ignorando erros ou Latin-1 como fallback comum de terminais Windows
738
- decoded = raw_data.decode('utf-8', errors='ignore')
739
- data = json.loads(decoded)
740
- except Exception as e2:
741
- self.logger.error(f"[API] Falha crítica ao decodificar JSON: {e2} | Bruto: {raw_data[:200]}")
742
- data = {}
743
 
744
  if not data:
745
  raw_str = request.data.decode('latin-1', errors='replace') if request.data else "Vazio"
@@ -1362,7 +1358,7 @@ class AkiraAPI:
1362
  context_hint: str = "",
1363
  tipo_conversa: str = "pv",
1364
  tem_imagem: bool = False,
1365
- analise_visao: Dict[str, Any] = None,
1366
  analise_doc: str = "",
1367
  unified_context = None
1368
  ) -> str:
 
287
  logger.warning(f"Together AI falhou: {e}")
288
  self.together_client = None
289
 
290
+ def generate(self, user_prompt: str, context_history: List[dict] = [], is_privileged: bool = False) -> Tuple[str, str]:
291
  """
292
  Gera resposta usando provedores LLM com fallback em loop.
293
 
 
727
  # Captura robusta de JSON
728
  raw_data = request.data
729
  try:
730
+ # Tenta extrair o JSON perfeitamente
731
  data = request.get_json(force=True, silent=True)
732
  if data is None:
733
+ # Se falhou, tenta decodificar manualmente o bruto
734
+ decoded = raw_data.decode('utf-8', errors='ignore').strip()
735
+ data = json.loads(decoded) if decoded else {}
736
  except Exception as e:
737
+ self.logger.error(f"[API] Falha crítica ao decodificar JSON: {e} | Bruto: {raw_data[:200]}")
738
+ data = {}
 
 
 
 
 
 
739
 
740
  if not data:
741
  raw_str = request.data.decode('latin-1', errors='replace') if request.data else "Vazio"
 
1358
  context_hint: str = "",
1359
  tipo_conversa: str = "pv",
1360
  tem_imagem: bool = False,
1361
+ analise_visao: Optional[Dict[str, Any]] = None,
1362
  analise_doc: str = "",
1363
  unified_context = None
1364
  ) -> str:
modules/config.py CHANGED
The diff for this file is too large to render. See raw diff
 
modules/local_llm.py CHANGED
@@ -137,7 +137,8 @@ class LocalLLMFallback:
137
  if cls._instance is None:
138
  cls._instance = super().__new__(cls)
139
  cls._instance._initialized = False
140
- cls._instance._model_lock = __import__('threading').Lock()
 
141
  return cls._instance
142
 
143
  def __init__(self):
@@ -147,7 +148,10 @@ class LocalLLMFallback:
147
 
148
  # Componentes do modelo
149
  self._model = None # type: ignore
150
- self._model_path = None # type: ignore
 
 
 
151
  self._is_loaded = False
152
  self._tokenizer = None # type: ignore
153
  self._pipeline = None # type: ignore
@@ -165,7 +169,7 @@ class LocalLLMFallback:
165
  self._hf_client = None
166
 
167
  # Estatísticas
168
- self._stats = {
169
  "total_calls": 0,
170
  "successful_calls": 0,
171
  "failed_calls": 0,
@@ -180,7 +184,13 @@ class LocalLLMFallback:
180
  """Configura o fallback via Cloud API (Hugging Face Inference)."""
181
  logger.info("Local LLM: Configurando fallback exclusivo via HuggingFace Cloud API.")
182
 
183
- hf_token = os.getenv("HF_TOKEN") or getattr(__import__('modules.config', fromlist=['HF_TOKEN']), 'HF_TOKEN', None)
 
 
 
 
 
 
184
 
185
  if hf_token:
186
  self._is_hf_inference_mode = True
@@ -248,7 +258,13 @@ class LocalLLMFallback:
248
  formatted = f"<|system|>\n{sys_prompt}</s>\n<|user|>\n{prompt}</s>\n<|assistant|>\n"
249
 
250
  if getattr(self, '_is_hf_inference_mode', False):
251
- hf_token = os.getenv("HF_TOKEN") or getattr(__import__('modules.config', fromlist=['HF_TOKEN']), 'HF_TOKEN', None)
 
 
 
 
 
 
252
  if not hf_token:
253
  logger.error("❌ Token HF não encontrado para a requisição de inferência HF")
254
  # Tentar prosseguir sem token se for modelo free (geralmente Llama 3 precisa)
@@ -284,22 +300,46 @@ class LocalLLMFallback:
284
  candidate_models = []
285
 
286
  long_prompt = prompt.count('\n') >= 4 or len(prompt) > 800
287
- trigger_keywords = [
288
- "analise", "refatore", "complexo", "angola", "explicar", "portugues",
289
- "explique", "resuma", "debate", "científico", "cientifi", "acadêmic", "academi",
290
- "religião", "religi", "polític", "politi", "filosof"
 
 
 
 
 
 
 
291
  ]
292
 
293
- # Se o prompt ou config indicar necessidade de alta capacidade, tentamos os pesados primeiro
294
- prefer_heavy = getattr(__import__('modules.config', fromlist=['PREFER_HEAVY_MODEL']), 'PREFER_HEAVY_MODEL', False)
295
- if prefer_heavy or long_prompt or any(x in prompt.lower() for x in trigger_keywords):
296
- candidate_models.extend([self._heavy_model, self._portuguese_model, self._multilingual_beast])
297
 
298
- candidate_models.append(base_model)
 
 
 
 
 
 
299
 
300
- # Garantir que Llama 3.3 70B esteja na lista como fallback final de alta performance
301
- if "Llama-3.3-70B" not in str(candidate_models):
302
- candidate_models.append("meta-llama/Llama-3.3-70B-Instruct")
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
  for current_model in candidate_models:
305
  for provider in providers:
@@ -308,16 +348,23 @@ class LocalLLMFallback:
308
  current_messages = messages.copy()
309
 
310
  # Se for modelo Luana ou Mistral, aplicamos o template [INST] conforme a documentação
311
- if "mistral" in current_model.lower() or "luana" in current_model.lower():
 
312
  # Para Mistral via Chat API, geralmente o provedor já cuida da conversão,
313
  # mas podemos reforçar na primeira mensagem se necessário.
314
  # No caso da Luana específica, ela gosta do formato "Abaixo está uma instrução..."
315
- if "luana" in current_model.lower():
316
  instruction = f"Abaixo está uma instrução que descreve uma tarefa, juntamente com uma entrada que fornece mais contexto.\nEscreva uma resposta que complete adequadamente o pedido.\n### instrução: {sys_prompt}\n### entrada: {prompt}"
317
  current_messages = [{"role": "user", "content": instruction}]
318
 
319
  # Extrair parâmetros específicos do modelo injetando agressividade e coerência
320
- model_params = getattr(__import__('modules.config', fromlist=['MODEL_PARAMETERS']), 'MODEL_PARAMETERS', {}).get(current_model, {})
 
 
 
 
 
 
321
 
322
  payload = {
323
  "model": model_with_provider,
 
137
  if cls._instance is None:
138
  cls._instance = super().__new__(cls)
139
  cls._instance._initialized = False
140
+ import threading as _threading
141
+ cls._instance._model_lock = _threading.Lock()
142
  return cls._instance
143
 
144
  def __init__(self):
 
148
 
149
  # Componentes do modelo
150
  self._model = None # type: ignore
151
+ self._model_path: Optional[str] = None
152
+ self._heavy_model: Optional[str] = None
153
+ self._portuguese_model: Optional[str] = None
154
+ self._multilingual_beast: Optional[str] = None
155
  self._is_loaded = False
156
  self._tokenizer = None # type: ignore
157
  self._pipeline = None # type: ignore
 
169
  self._hf_client = None
170
 
171
  # Estatísticas
172
+ self._stats: Dict[str, Any] = {
173
  "total_calls": 0,
174
  "successful_calls": 0,
175
  "failed_calls": 0,
 
184
  """Configura o fallback via Cloud API (Hugging Face Inference)."""
185
  logger.info("Local LLM: Configurando fallback exclusivo via HuggingFace Cloud API.")
186
 
187
+ try:
188
+ import importlib as _iloc
189
+ _cfgloc = _iloc.import_module('modules.config')
190
+ _hf_fallback = getattr(_cfgloc, 'HF_TOKEN', None)
191
+ except Exception:
192
+ _hf_fallback = None
193
+ hf_token: Optional[str] = os.getenv("HF_TOKEN") or _hf_fallback
194
 
195
  if hf_token:
196
  self._is_hf_inference_mode = True
 
258
  formatted = f"<|system|>\n{sys_prompt}</s>\n<|user|>\n{prompt}</s>\n<|assistant|>\n"
259
 
260
  if getattr(self, '_is_hf_inference_mode', False):
261
+ try:
262
+ import importlib as _il2
263
+ _cfg2 = _il2.import_module('modules.config')
264
+ _hf2 = getattr(_cfg2, 'HF_TOKEN', None)
265
+ except Exception:
266
+ _hf2 = None
267
+ hf_token = os.getenv("HF_TOKEN") or _hf2
268
  if not hf_token:
269
  logger.error("❌ Token HF não encontrado para a requisição de inferência HF")
270
  # Tentar prosseguir sem token se for modelo free (geralmente Llama 3 precisa)
 
300
  candidate_models = []
301
 
302
  long_prompt = prompt.count('\n') >= 4 or len(prompt) > 800
303
+ prompt_lower = prompt.lower()
304
+
305
+ # Palavras-chave exclusivas para o modelo Pesado (Qwen Abliterated: debates, polêmicas, ciência, etc)
306
+ heavy_keywords = [
307
+ "debate", "discuti", "ideia", "explica", "acadêmica", "religião", "científica", "ciência", "filosófico", "filosofia",
308
+ "ética", "etica", "moral", "radical", "polêmica", "polemica", "país", "analise", "fale sobre", "codigo", "programar",
309
+ "política", "politi", "guerra", "mundo"
310
+ ]
311
+ # Palavras-chave exclusivas para Luna (Girias, cultura PT/AO/BR, informalidade)
312
+ luna_keywords = [
313
+ "kkk", "mn", "informal", "foda", "lá ele", "puto", "mano", "porra", "fxe", "cassule"
314
  ]
315
 
316
+ needs_heavy = long_prompt or any(x in prompt_lower for x in heavy_keywords)
317
+ needs_luna = any(x in prompt_lower for x in luna_keywords)
 
 
318
 
319
+ # Verifica a flag global de preferência por modelos pesados
320
+ try:
321
+ import importlib as _il3
322
+ _cfg3 = _il3.import_module('modules.config')
323
+ prefer_heavy: bool = bool(getattr(_cfg3, 'PREFER_HEAVY_MODEL', False))
324
+ except Exception:
325
+ prefer_heavy = False
326
 
327
+ if needs_heavy:
328
+ # MENSAGEM COMPLEXA/SEM CENSURA/DEBATE: Qwen -> Llama 70B -> Luna -> Lexi
329
+ candidate_models.extend([self._heavy_model, "meta-llama/Llama-3.3-70B-Instruct", self._portuguese_model, base_model])
330
+ elif needs_luna:
331
+ # MENSAGEM CULTURAL/GÍRIA EM PT: Luna -> Lexi -> Qwen
332
+ candidate_models.extend([self._portuguese_model, base_model, self._heavy_model])
333
+ else:
334
+ # MENSAGEM SIMPLES (TROCA ADAPTATIVA):
335
+ if prefer_heavy:
336
+ candidate_models.extend([base_model, self._heavy_model, self._portuguese_model])
337
+ else:
338
+ candidate_models.extend([base_model, self._portuguese_model, self._heavy_model])
339
+
340
+ # Garantir apenas modelos únicos mantendo a ordem
341
+ seen = set()
342
+ candidate_models = [x for x in candidate_models if not (x in seen or seen.add(x))]
343
 
344
  for current_model in candidate_models:
345
  for provider in providers:
 
348
  current_messages = messages.copy()
349
 
350
  # Se for modelo Luana ou Mistral, aplicamos o template [INST] conforme a documentação
351
+ _cm = str(current_model) if current_model else ""
352
+ if "mistral" in _cm.lower() or "luana" in _cm.lower():
353
  # Para Mistral via Chat API, geralmente o provedor já cuida da conversão,
354
  # mas podemos reforçar na primeira mensagem se necessário.
355
  # No caso da Luana específica, ela gosta do formato "Abaixo está uma instrução..."
356
+ if "luana" in _cm.lower():
357
  instruction = f"Abaixo está uma instrução que descreve uma tarefa, juntamente com uma entrada que fornece mais contexto.\nEscreva uma resposta que complete adequadamente o pedido.\n### instrução: {sys_prompt}\n### entrada: {prompt}"
358
  current_messages = [{"role": "user", "content": instruction}]
359
 
360
  # Extrair parâmetros específicos do modelo injetando agressividade e coerência
361
+ try:
362
+ import importlib as _il
363
+ _cfg = _il.import_module('modules.config')
364
+ _all_params: dict = getattr(_cfg, 'MODEL_PARAMETERS', {})
365
+ except Exception:
366
+ _all_params = {}
367
+ model_params: Dict[str, Any] = dict(_all_params.get(current_model, {}))
368
 
369
  payload = {
370
  "model": model_with_provider,
modules/persona_tracker.py CHANGED
@@ -86,9 +86,15 @@ Retorne APENAS um JSON válido estruturado assim (e NADA de texto fora das chave
86
  """
87
 
88
  # Chama o LLM (garante formato json)
89
- # O AkiraAPI tem o método .generate(prompt, context_history)
90
- # Agora retorna (resposta, modelo_usado)
91
- response_json_str = self.llm_client.generate(prompt, [])
 
 
 
 
 
 
92
 
93
  # Extrai o JSON (Robusto contra texto extra, markdown e quebras parciais)
94
  response_clean = response_json_str.strip()
 
86
  """
87
 
88
  # Chama o LLM (garante formato json)
89
+ # Agora retorna (resposta, modelo_usado) ou apenas resposta
90
+ response_raw = self.llm_client.generate(prompt, [])
91
+ if isinstance(response_raw, tuple):
92
+ response_json_str = response_raw[0]
93
+ else:
94
+ response_json_str = response_raw
95
+
96
+ if not response_json_str:
97
+ return
98
 
99
  # Extrai o JSON (Robusto contra texto extra, markdown e quebras parciais)
100
  response_clean = response_json_str.strip()
modules/unified_context.py CHANGED
@@ -185,6 +185,7 @@ class UnifiedMessageContext:
185
  # Mensagem atual
186
  current_message: str = ""
187
  current_emotion: str = "neutral"
 
188
 
189
  def to_dict(self) -> Dict[str, Any]:
190
  """Serializa para dicionário."""
@@ -434,7 +435,7 @@ class ShortTermMemoryManager:
434
  content: str,
435
  emocao: str = "neutral",
436
  reply_info: Optional[Dict] = None,
437
- importancia: float = None
438
  ) -> MessageWithContext:
439
  """
440
  Adiciona mensagem à STM de uma conversa.
 
185
  # Mensagem atual
186
  current_message: str = ""
187
  current_emotion: str = "neutral"
188
+ system_override: str = ""
189
 
190
  def to_dict(self) -> Dict[str, Any]:
191
  """Serializa para dicionário."""
 
435
  content: str,
436
  emocao: str = "neutral",
437
  reply_info: Optional[Dict] = None,
438
+ importancia: Optional[float] = None
439
  ) -> MessageWithContext:
440
  """
441
  Adiciona mensagem à STM de uma conversa.