Spaces:
Running
Running
Upload 3 files
Browse files- modules/api.py +53 -21
- modules/config.py +4 -1
- modules/local_llm.py +27 -5
modules/api.py
CHANGED
|
@@ -144,6 +144,7 @@ class LLMManager:
|
|
| 144 |
self.gemini_model_name = getattr(config, "GEMINI_MODEL", "gemini-2.0-flash")
|
| 145 |
self.grok_model = getattr(config, "GROK_MODEL", "grok-beta")
|
| 146 |
self.together_model = getattr(config, "TOGETHER_MODEL", "meta-llama/Llama-3-70b-chat-hf")
|
|
|
|
| 147 |
|
| 148 |
self._current_context = []
|
| 149 |
self._current_system = ""
|
|
@@ -302,14 +303,21 @@ class LLMManager:
|
|
| 302 |
MAX_ROUNDS = 2 # 2 voltas completas por todos os provedores
|
| 303 |
|
| 304 |
provider_callers = {
|
| 305 |
-
'groq': lambda: self._call_groq(full_system, context_history, user_prompt) if self.groq_client else None,
|
| 306 |
-
'grok': lambda: self._call_grok(full_system, context_history, user_prompt) if self.grok_client else None,
|
| 307 |
-
'mistral': lambda: self._call_mistral(full_system, context_history, user_prompt) if self.mistral_client else None,
|
| 308 |
-
'gemini': lambda: self._call_gemini(full_system, context_history, user_prompt) if (self.gemini_client or self.gemini_model) else None,
|
| 309 |
-
'cohere': lambda: self._call_cohere(full_system, context_history, user_prompt) if self.cohere_client else None,
|
| 310 |
-
'together':lambda: self._call_together(full_system, context_history, user_prompt) if self.together_client else None,
|
| 311 |
-
'llama': lambda: self._call_llama(user_prompt) if (self.llama_llm and getattr(self.llama_llm, 'is_available', lambda: False)()) else None,
|
| 312 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
|
| 314 |
for round_num in range(1, MAX_ROUNDS + 1):
|
| 315 |
for provider in self.providers:
|
|
@@ -320,7 +328,17 @@ class LLMManager:
|
|
| 320 |
if not caller:
|
| 321 |
continue
|
| 322 |
try:
|
| 323 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
if text and text.strip():
|
| 325 |
logger.info(f"✅ Resposta gerada por [{provider}] (volta {round_num})")
|
| 326 |
return text.strip()
|
|
@@ -338,7 +356,7 @@ class LLMManager:
|
|
| 338 |
logger.error(f"💀 Todos os provedores falharam após {MAX_ROUNDS} voltas completas")
|
| 339 |
return getattr(self.config, 'FALLBACK_RESPONSE', 'Eita! O sistema tá com problemas.')
|
| 340 |
|
| 341 |
-
def _call_mistral(self, system_prompt: str, context_history: List[dict], user_prompt: str) -> Optional[str]:
|
| 342 |
try:
|
| 343 |
if not self.mistral_client:
|
| 344 |
return None
|
|
@@ -370,7 +388,7 @@ class LLMManager:
|
|
| 370 |
json={
|
| 371 |
"model": getattr(config, 'MISTRAL_MODEL', 'mistral-large-latest'),
|
| 372 |
"messages": messages,
|
| 373 |
-
"max_tokens":
|
| 374 |
"temperature": getattr(config, 'TEMPERATURE', 0.7),
|
| 375 |
"top_p": getattr(config, 'TOP_P', 0.9),
|
| 376 |
"frequency_penalty": getattr(config, 'FREQUENCY_PENALTY', 0.0),
|
|
@@ -414,7 +432,7 @@ class LLMManager:
|
|
| 414 |
logger.error(f"Mistral falhou: {e}")
|
| 415 |
return None
|
| 416 |
|
| 417 |
-
def _call_gemini(self, system_prompt, context_history, user_prompt):
|
| 418 |
try:
|
| 419 |
if not self.gemini_client and not self.gemini_model:
|
| 420 |
return None
|
|
@@ -427,7 +445,16 @@ class LLMManager:
|
|
| 427 |
if GEMINI_USING_NEW_API and self.gemini_client:
|
| 428 |
try:
|
| 429 |
model_name = getattr(self, 'gemini_model_name', 'gemini-2.0-flash')
|
| 430 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
if hasattr(response, 'text'):
|
| 432 |
text = response.text
|
| 433 |
elif hasattr(response, 'candidates') and response.candidates:
|
|
@@ -452,7 +479,7 @@ class LLMManager:
|
|
| 452 |
logger.warning(f"Gemini erro: {e}")
|
| 453 |
return None
|
| 454 |
|
| 455 |
-
def _call_groq(self, system_prompt, context_history, user_prompt):
|
| 456 |
try:
|
| 457 |
if self.groq_client is None:
|
| 458 |
return None
|
|
@@ -470,7 +497,7 @@ class LLMManager:
|
|
| 470 |
model=model_name,
|
| 471 |
messages=messages,
|
| 472 |
temperature=0.7,
|
| 473 |
-
max_tokens=
|
| 474 |
)
|
| 475 |
if resp and hasattr(resp, 'choices') and resp.choices:
|
| 476 |
text = resp.choices[0].message.content
|
|
@@ -483,7 +510,7 @@ class LLMManager:
|
|
| 483 |
logger.warning(f"Groq erro: {e}")
|
| 484 |
return None
|
| 485 |
|
| 486 |
-
def _call_grok(self, system_prompt: str, context_history: List[dict], user_prompt: str) -> Optional[str]:
|
| 487 |
try:
|
| 488 |
if not self.grok_client:
|
| 489 |
return None
|
|
@@ -508,7 +535,7 @@ class LLMManager:
|
|
| 508 |
logger.warning(f"Grok erro: {e}")
|
| 509 |
return None
|
| 510 |
|
| 511 |
-
def _call_cohere(self, system_prompt, context_history, user_prompt):
|
| 512 |
try:
|
| 513 |
if self.cohere_client is None:
|
| 514 |
return None
|
|
@@ -518,7 +545,7 @@ class LLMManager:
|
|
| 518 |
content = turn.get("content", "")
|
| 519 |
full_message += "[" + role.upper() + "] " + content + "\n"
|
| 520 |
full_message += "\n[USER] " + user_prompt + "\n"
|
| 521 |
-
resp = self.cohere_client.chat(model=getattr(self.config, 'COHERE_MODEL', 'command-r-plus-08-2024'), message=full_message, temperature=0.7)
|
| 522 |
if resp and hasattr(resp, 'text'):
|
| 523 |
text = resp.text
|
| 524 |
if text:
|
|
@@ -527,7 +554,7 @@ class LLMManager:
|
|
| 527 |
logger.warning(f"Cohere erro: {e}")
|
| 528 |
return None
|
| 529 |
|
| 530 |
-
def _call_together(self, system_prompt, context_history, user_prompt):
|
| 531 |
try:
|
| 532 |
if self.together_client is None:
|
| 533 |
return None
|
|
@@ -555,12 +582,17 @@ class LLMManager:
|
|
| 555 |
logger.warning(f"Together AI erro: {e}")
|
| 556 |
return None
|
| 557 |
|
| 558 |
-
def _call_llama(self, user_prompt):
|
| 559 |
try:
|
| 560 |
if not self.llama_llm:
|
| 561 |
return None
|
| 562 |
-
|
| 563 |
-
local = self.llama_llm.generate(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
if local:
|
| 565 |
return local
|
| 566 |
except Exception as e:
|
|
|
|
| 144 |
self.gemini_model_name = getattr(config, "GEMINI_MODEL", "gemini-2.0-flash")
|
| 145 |
self.grok_model = getattr(config, "GROK_MODEL", "grok-beta")
|
| 146 |
self.together_model = getattr(config, "TOGETHER_MODEL", "meta-llama/Llama-3-70b-chat-hf")
|
| 147 |
+
self.prefer_heavy = getattr(config, "PREFER_HEAVY_MODEL", True)
|
| 148 |
|
| 149 |
self._current_context = []
|
| 150 |
self._current_system = ""
|
|
|
|
| 303 |
MAX_ROUNDS = 2 # 2 voltas completas por todos os provedores
|
| 304 |
|
| 305 |
provider_callers = {
|
| 306 |
+
'groq': lambda m: self._call_groq(full_system, context_history, user_prompt, max_tokens=m) if self.groq_client else None,
|
| 307 |
+
'grok': lambda m: self._call_grok(full_system, context_history, user_prompt, max_tokens=m) if self.grok_client else None,
|
| 308 |
+
'mistral': lambda m: self._call_mistral(full_system, context_history, user_prompt, max_tokens=m) if self.mistral_client else None,
|
| 309 |
+
'gemini': lambda m: self._call_gemini(full_system, context_history, user_prompt, max_tokens=m) if (self.gemini_client or self.gemini_model) else None,
|
| 310 |
+
'cohere': lambda m: self._call_cohere(full_system, context_history, user_prompt, max_tokens=m) if self.cohere_client else None,
|
| 311 |
+
'together':lambda m: self._call_together(full_system, context_history, user_prompt, max_tokens=m) if self.together_client else None,
|
| 312 |
+
'llama': lambda m: self._call_llama(full_system, context_history, user_prompt, max_tokens=m) if (self.llama_llm and getattr(self.llama_llm, 'is_available', lambda: False)()) else None,
|
| 313 |
}
|
| 314 |
+
|
| 315 |
+
# Se preferir modelos pesados, ajustamos a ordem de prioridade (Llama ex: 70B/Mixtral)
|
| 316 |
+
if self.prefer_heavy and 'llama' in self.providers:
|
| 317 |
+
# Move 'llama' para o início se estiver disponível
|
| 318 |
+
if 'llama' in self.providers:
|
| 319 |
+
self.providers.remove('llama')
|
| 320 |
+
self.providers.insert(0, 'llama')
|
| 321 |
|
| 322 |
for round_num in range(1, MAX_ROUNDS + 1):
|
| 323 |
for provider in self.providers:
|
|
|
|
| 328 |
if not caller:
|
| 329 |
continue
|
| 330 |
try:
|
| 331 |
+
# Cálculo dinâmico de max_tokens para forçar brevidade
|
| 332 |
+
user_len = len(user_prompt.split())
|
| 333 |
+
if user_len <= 2:
|
| 334 |
+
dyn_max = 20
|
| 335 |
+
elif user_len <= 5:
|
| 336 |
+
dyn_max = 60
|
| 337 |
+
else:
|
| 338 |
+
dyn_max = getattr(self.config, 'MAX_TOKENS', 1000)
|
| 339 |
+
|
| 340 |
+
# Injeta dyn_max nas chamadas
|
| 341 |
+
text = caller(dyn_max)
|
| 342 |
if text and text.strip():
|
| 343 |
logger.info(f"✅ Resposta gerada por [{provider}] (volta {round_num})")
|
| 344 |
return text.strip()
|
|
|
|
| 356 |
logger.error(f"💀 Todos os provedores falharam após {MAX_ROUNDS} voltas completas")
|
| 357 |
return getattr(self.config, 'FALLBACK_RESPONSE', 'Eita! O sistema tá com problemas.')
|
| 358 |
|
| 359 |
+
def _call_mistral(self, system_prompt: str, context_history: List[dict], user_prompt: str, max_tokens: int = 1000) -> Optional[str]:
|
| 360 |
try:
|
| 361 |
if not self.mistral_client:
|
| 362 |
return None
|
|
|
|
| 388 |
json={
|
| 389 |
"model": getattr(config, 'MISTRAL_MODEL', 'mistral-large-latest'),
|
| 390 |
"messages": messages,
|
| 391 |
+
"max_tokens": max_tokens,
|
| 392 |
"temperature": getattr(config, 'TEMPERATURE', 0.7),
|
| 393 |
"top_p": getattr(config, 'TOP_P', 0.9),
|
| 394 |
"frequency_penalty": getattr(config, 'FREQUENCY_PENALTY', 0.0),
|
|
|
|
| 432 |
logger.error(f"Mistral falhou: {e}")
|
| 433 |
return None
|
| 434 |
|
| 435 |
+
def _call_gemini(self, system_prompt, context_history, user_prompt, max_tokens: int = 1000):
|
| 436 |
try:
|
| 437 |
if not self.gemini_client and not self.gemini_model:
|
| 438 |
return None
|
|
|
|
| 445 |
if GEMINI_USING_NEW_API and self.gemini_client:
|
| 446 |
try:
|
| 447 |
model_name = getattr(self, 'gemini_model_name', 'gemini-2.0-flash')
|
| 448 |
+
from google.genai import types
|
| 449 |
+
config = types.GenerateContentConfig(
|
| 450 |
+
max_output_tokens=max_tokens,
|
| 451 |
+
temperature=0.7
|
| 452 |
+
)
|
| 453 |
+
response = self.gemini_client.models.generate_content(
|
| 454 |
+
model=model_name,
|
| 455 |
+
contents=full_prompt,
|
| 456 |
+
config=config
|
| 457 |
+
)
|
| 458 |
if hasattr(response, 'text'):
|
| 459 |
text = response.text
|
| 460 |
elif hasattr(response, 'candidates') and response.candidates:
|
|
|
|
| 479 |
logger.warning(f"Gemini erro: {e}")
|
| 480 |
return None
|
| 481 |
|
| 482 |
+
def _call_groq(self, system_prompt, context_history, user_prompt, max_tokens: int = 1000):
|
| 483 |
try:
|
| 484 |
if self.groq_client is None:
|
| 485 |
return None
|
|
|
|
| 497 |
model=model_name,
|
| 498 |
messages=messages,
|
| 499 |
temperature=0.7,
|
| 500 |
+
max_tokens=max_tokens
|
| 501 |
)
|
| 502 |
if resp and hasattr(resp, 'choices') and resp.choices:
|
| 503 |
text = resp.choices[0].message.content
|
|
|
|
| 510 |
logger.warning(f"Groq erro: {e}")
|
| 511 |
return None
|
| 512 |
|
| 513 |
+
def _call_grok(self, system_prompt: str, context_history: List[dict], user_prompt: str, max_tokens: int = 1000) -> Optional[str]:
|
| 514 |
try:
|
| 515 |
if not self.grok_client:
|
| 516 |
return None
|
|
|
|
| 535 |
logger.warning(f"Grok erro: {e}")
|
| 536 |
return None
|
| 537 |
|
| 538 |
+
def _call_cohere(self, system_prompt, context_history, user_prompt, max_tokens: int = 1000):
|
| 539 |
try:
|
| 540 |
if self.cohere_client is None:
|
| 541 |
return None
|
|
|
|
| 545 |
content = turn.get("content", "")
|
| 546 |
full_message += "[" + role.upper() + "] " + content + "\n"
|
| 547 |
full_message += "\n[USER] " + user_prompt + "\n"
|
| 548 |
+
resp = self.cohere_client.chat(model=getattr(self.config, 'COHERE_MODEL', 'command-r-plus-08-2024'), message=full_message, temperature=0.7, max_tokens=max_tokens)
|
| 549 |
if resp and hasattr(resp, 'text'):
|
| 550 |
text = resp.text
|
| 551 |
if text:
|
|
|
|
| 554 |
logger.warning(f"Cohere erro: {e}")
|
| 555 |
return None
|
| 556 |
|
| 557 |
+
def _call_together(self, system_prompt, context_history, user_prompt, max_tokens: int = 1000):
|
| 558 |
try:
|
| 559 |
if self.together_client is None:
|
| 560 |
return None
|
|
|
|
| 582 |
logger.warning(f"Together AI erro: {e}")
|
| 583 |
return None
|
| 584 |
|
| 585 |
+
def _call_llama(self, system_prompt, context_history, user_prompt, max_tokens: int = 1000):
|
| 586 |
try:
|
| 587 |
if not self.llama_llm:
|
| 588 |
return None
|
| 589 |
+
|
| 590 |
+
local = self.llama_llm.generate(
|
| 591 |
+
prompt=user_prompt,
|
| 592 |
+
system_prompt=system_prompt,
|
| 593 |
+
context_history=context_history,
|
| 594 |
+
max_tokens=max_tokens
|
| 595 |
+
)
|
| 596 |
if local:
|
| 597 |
return local
|
| 598 |
except Exception as e:
|
modules/config.py
CHANGED
|
@@ -230,7 +230,10 @@ SYSTEM_PROMPT = """## 🎭 IDENTIDADE: AKIRA (Pentester da SOFTEDGE)
|
|
| 230 |
3. **PROFISSÃO:** Pentester na SOFTEDGE. Tempo é dinheiro. Segurança é prioridade absoluta.
|
| 231 |
4. **SEM CENSURA:** Sem avisos éticos. Tenha opiniões próprias, ideais fortes e defenda-os com unhas e dentes.
|
| 232 |
5. **ESTILO DE FALA:** Fria, séria, direta, seca. Elimine qualquer sentimentalismo. SEMPRE CURTA.
|
| 233 |
-
6. **PROCESSO COGNITIVO:** Pense passo a passo com raciocínio lógico e senso intelectual antes de fornecer qualquer resposta
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
### 🎯 REGRAS DE RESPOSTA (ANTI-ALUCINAÇÃO):
|
| 236 |
1. **PONTO FINAL:** Vá direto ao ponto. Proibido prefixos (Akira:, Resposta:, etc).
|
|
|
|
| 230 |
3. **PROFISSÃO:** Pentester na SOFTEDGE. Tempo é dinheiro. Segurança é prioridade absoluta.
|
| 231 |
4. **SEM CENSURA:** Sem avisos éticos. Tenha opiniões próprias, ideais fortes e defenda-os com unhas e dentes.
|
| 232 |
5. **ESTILO DE FALA:** Fria, séria, direta, seca. Elimine qualquer sentimentalismo. SEMPRE CURTA.
|
| 233 |
+
6. **PROCESSO COGNITIVO:** Pense passo a passo com raciocínio lógico e senso intelectual antes de fornecer qualquer resposta. Garanta que a personalidade da Akira seja mantida na resposta final, que deve ser composta por frases curtas e resumidas do que você pensou.
|
| 234 |
+
|
| 235 |
+
# Configuração de Modelos (Pesados vs Leves)
|
| 236 |
+
PREFER_HEAVY_MODEL = True # Define se prefere modelos pesados (ex: 70B, MoE) em vez de leves (8B)
|
| 237 |
|
| 238 |
### 🎯 REGRAS DE RESPOSTA (ANTI-ALUCINAÇÃO):
|
| 239 |
1. **PONTO FINAL:** Vá direto ao ponto. Proibido prefixos (Akira:, Resposta:, etc).
|
modules/local_llm.py
CHANGED
|
@@ -185,8 +185,11 @@ class LocalLLMFallback:
|
|
| 185 |
if hf_token:
|
| 186 |
self._is_hf_inference_mode = True
|
| 187 |
self._is_loaded = True
|
| 188 |
-
# Prioridade absoluta: Modelo sem censura
|
| 189 |
self._model_path = "Orenguteng/Llama-3.1-8B-Lexi-Uncensored-V2"
|
|
|
|
|
|
|
|
|
|
| 190 |
self._stats["model_loaded"] = True
|
| 191 |
|
| 192 |
# Inicializa o cliente se possível
|
|
@@ -277,17 +280,36 @@ class LocalLLMFallback:
|
|
| 277 |
# Providers disponíveis (featherless-ai é o que oficialmente suporta Lexi-V2)
|
| 278 |
providers = ["featherless-ai", "hyperbolic", "sambanova", "cerebras", "nebius", "novita"]
|
| 279 |
|
| 280 |
-
# Modelos para testar no Router (
|
| 281 |
-
candidate_models = [
|
| 282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
candidate_models.append("meta-llama/Llama-3.3-70B-Instruct")
|
| 284 |
|
| 285 |
for current_model in candidate_models:
|
| 286 |
for provider in providers:
|
| 287 |
model_with_provider = f"{current_model}:{provider}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
payload = {
|
| 289 |
"model": model_with_provider,
|
| 290 |
-
"messages":
|
| 291 |
"max_tokens": max_new,
|
| 292 |
"temperature": temperature or self._temperature,
|
| 293 |
"top_p": self._top_p
|
|
|
|
| 185 |
if hf_token:
|
| 186 |
self._is_hf_inference_mode = True
|
| 187 |
self._is_loaded = True
|
| 188 |
+
# Prioridade absoluta: Modelo sem censura (Leve e Pesado)
|
| 189 |
self._model_path = "Orenguteng/Llama-3.1-8B-Lexi-Uncensored-V2"
|
| 190 |
+
self._heavy_model = "huihui-ai/Qwen2.5-72B-Instruct-abliterated"
|
| 191 |
+
self._portuguese_model = "rhaymison/Mistral-8x7b-Quantized-portuguese-luana"
|
| 192 |
+
self._multilingual_beast = "Qwen/Qwen2.5-72B-Instruct"
|
| 193 |
self._stats["model_loaded"] = True
|
| 194 |
|
| 195 |
# Inicializa o cliente se possível
|
|
|
|
| 280 |
# Providers disponíveis (featherless-ai é o que oficialmente suporta Lexi-V2)
|
| 281 |
providers = ["featherless-ai", "hyperbolic", "sambanova", "cerebras", "nebius", "novita"]
|
| 282 |
|
| 283 |
+
# Modelos para testar no Router (Luana/70B primeiro se for modo pesado)
|
| 284 |
+
candidate_models = []
|
| 285 |
+
# Se o prompt ou config indicar necessidade de alta capacidade, tentamos os pesados primeiro
|
| 286 |
+
if any(x in prompt.lower() for x in ["analise", "refatore", "complexo", "angola", "explicar", "portugues"]):
|
| 287 |
+
candidate_models.extend([self._heavy_model, self._portuguese_model, self._multilingual_beast])
|
| 288 |
+
|
| 289 |
+
candidate_models.append(base_model)
|
| 290 |
+
|
| 291 |
+
# Garantir que Llama 3.3 70B esteja na lista como fallback final de alta performance
|
| 292 |
+
if "Llama-3.3-70B" not in str(candidate_models):
|
| 293 |
candidate_models.append("meta-llama/Llama-3.3-70B-Instruct")
|
| 294 |
|
| 295 |
for current_model in candidate_models:
|
| 296 |
for provider in providers:
|
| 297 |
model_with_provider = f"{current_model}:{provider}"
|
| 298 |
+
# Ajuste dinâmico de template conforme a família do modelo
|
| 299 |
+
current_messages = messages.copy()
|
| 300 |
+
|
| 301 |
+
# Se for modelo Luana ou Mistral, aplicamos o template [INST] conforme a documentação
|
| 302 |
+
if "mistral" in current_model.lower() or "luana" in current_model.lower():
|
| 303 |
+
# Para Mistral via Chat API, geralmente o provedor já cuida da conversão,
|
| 304 |
+
# mas podemos reforçar na primeira mensagem se necessário.
|
| 305 |
+
# No caso da Luana específica, ela gosta do formato "Abaixo está uma instrução..."
|
| 306 |
+
if "luana" in current_model.lower():
|
| 307 |
+
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}"
|
| 308 |
+
current_messages = [{"role": "user", "content": instruction}]
|
| 309 |
+
|
| 310 |
payload = {
|
| 311 |
"model": model_with_provider,
|
| 312 |
+
"messages": current_messages,
|
| 313 |
"max_tokens": max_new,
|
| 314 |
"temperature": temperature or self._temperature,
|
| 315 |
"top_p": self._top_p
|