Spaces:
Running
Running
Update ai_logic.py
Browse files- ai_logic.py +684 -222
ai_logic.py
CHANGED
@@ -9,75 +9,57 @@ from urllib.parse import urljoin, urlparse
|
|
9 |
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
10 |
from langchain_community.vectorstores import FAISS
|
11 |
from langchain_community.embeddings import HuggingFaceEmbeddings
|
|
|
|
|
|
|
12 |
|
13 |
# --- Configurações ---
|
14 |
BLOG_URL = "https://aldohenrique.com.br/"
|
15 |
VECTOR_STORE_PATH = "faiss_index_store.pkl"
|
16 |
PROCESSED_URLS_PATH = "processed_urls.pkl"
|
17 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
18 |
-
|
19 |
-
|
|
|
20 |
|
21 |
-
# Lista inicial de modelos
|
22 |
-
MODELS = {}
|
23 |
-
|
24 |
-
# --- Função para buscar modelos ---
|
25 |
-
headers = {
|
26 |
-
"Authorization": f"Bearer {HF_TOKEN}"
|
27 |
-
}
|
28 |
-
|
29 |
-
# Modelos fixos que você quer manter
|
30 |
NEW_MODELS_TO_TEST = [
|
31 |
("Phi-3 Mini (Mais rápido)", "microsoft/Phi-3-mini-4k-instruct"),
|
32 |
("Zephyr 7B (Meio Termo)", "HuggingFaceH4/zephyr-7b-beta"),
|
33 |
-
("Microsoft 8B (Meio Termo)","meta-llama/Meta-Llama-3-8B-Instruct"),
|
34 |
-
("Mistral-7B","mistralai/Mistral-7B-Instruct-v0.3"),
|
35 |
-
("DialoGPT","microsoft/DialoGPT-medium"),
|
36 |
-
("Google","google/flan-t5-base"),
|
37 |
-
("Facebook","facebook/bart-large-cnn")
|
38 |
]
|
39 |
|
40 |
-
#
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
43 |
|
44 |
-
|
45 |
-
"
|
46 |
-
"
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
tags = model.get("tags", [])
|
59 |
-
base_model_tags = [tag for tag in tags if tag.startswith("base_model:")]
|
60 |
-
if base_model_tags:
|
61 |
-
model_name = model.get("id")
|
62 |
-
display_name = model_name.split("/")[-1]
|
63 |
-
|
64 |
-
# Verifica se já não está na lista para evitar duplicados
|
65 |
-
if not any(model_name == m[1] for m in NEW_MODELS_TO_TEST):
|
66 |
-
NEW_MODELS_TO_TEST.append((display_name, model_name))
|
67 |
-
|
68 |
-
# --- Resultado ---
|
69 |
-
print("Lista atualizada de modelos:\n")
|
70 |
-
for name, model_id in NEW_MODELS_TO_TEST:
|
71 |
-
print(f'("{name}", "{model_id}"),')
|
72 |
-
|
73 |
-
print(f"\nTotal de modelos na lista: {len(NEW_MODELS_TO_TEST)}")
|
74 |
-
|
75 |
-
# Nota: Alguns modelos podem requerer aprovação ou ter restrições de acesso
|
76 |
-
# Recomenda-se testar cada modelo individualmente para verificar disponibilidade
|
77 |
|
78 |
-
|
|
|
|
|
79 |
|
80 |
-
# --- Gerenciamento de Sessão ---
|
81 |
user_sessions: Dict[str, Dict[str, List | Dict]] = {}
|
82 |
MAX_MEMORY_LENGTH = 5
|
83 |
|
@@ -173,7 +155,7 @@ def clear_memory(session_id: str) -> str:
|
|
173 |
os.remove(memory_path)
|
174 |
return "Memória limpa com sucesso!"
|
175 |
|
176 |
-
# --- RAG (Crawling e Vector Store) ---
|
177 |
vector_store: Optional[FAISS] = None
|
178 |
|
179 |
def get_all_blog_links(url: str) -> set:
|
@@ -245,94 +227,25 @@ def retrieve_context_from_blog(query: str, k: int = 4) -> str:
|
|
245 |
print(f"Erro ao buscar contexto: {e}")
|
246 |
return ""
|
247 |
|
248 |
-
# --- API Client ---
|
249 |
-
class
|
250 |
-
def __init__(self,
|
251 |
-
self.
|
252 |
-
|
253 |
-
|
254 |
-
"""Verifica informações do modelo via API do Hugging Face."""
|
255 |
-
url = f"https://huggingface.co/api/models/{model_name}"
|
256 |
-
try:
|
257 |
-
response = requests.get(url, headers=self.headers, timeout=90)
|
258 |
-
if response.status_code == 200:
|
259 |
-
model_info = response.json()
|
260 |
-
if model_info.get('disabled', False):
|
261 |
-
return False, "Modelo desabilitado"
|
262 |
-
if model_info.get('gated', False):
|
263 |
-
return False, "Modelo requer aprovação/aceite de licença"
|
264 |
-
return True, "Modelo disponível"
|
265 |
-
elif response.status_code == 404:
|
266 |
-
return False, "Modelo não encontrado"
|
267 |
-
else:
|
268 |
-
return False, f"Erro HTTP {response.status_code}"
|
269 |
-
except requests.exceptions.RequestException as e:
|
270 |
-
return False, f"Erro na requisição: {str(e)}"
|
271 |
-
|
272 |
-
def test_model_inference(self, model_name: str) -> Tuple[bool, str]:
|
273 |
-
"""Testa se o modelo está disponível para inferência."""
|
274 |
-
url = f"https://api-inference.huggingface.co/models/{model_name}"
|
275 |
-
test_payload = {
|
276 |
-
"inputs": "Teste de disponibilidade do modelo.",
|
277 |
-
"parameters": {
|
278 |
-
"max_new_tokens": 10,
|
279 |
-
"temperature": 0.1,
|
280 |
-
"return_full_text": False
|
281 |
-
}
|
282 |
-
}
|
283 |
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
if response.status_code == 200:
|
288 |
-
result = response.json()
|
289 |
-
if isinstance(result, list) and len(result) > 0:
|
290 |
-
return True, "Modelo disponível para inferência"
|
291 |
-
elif isinstance(result, dict) and 'error' not in result:
|
292 |
-
return True, "Modelo disponível para inferência"
|
293 |
-
else:
|
294 |
-
return False, f"Resposta inesperada: {result}"
|
295 |
-
|
296 |
-
elif response.status_code == 503:
|
297 |
-
return False, "Modelo está carregando (503)"
|
298 |
-
elif response.status_code == 400:
|
299 |
-
error_msg = response.json().get('error', 'Erro 400')
|
300 |
-
if 'loading' in error_msg.lower():
|
301 |
-
return False, "Modelo está carregando"
|
302 |
-
return False, f"Erro 400: {error_msg}"
|
303 |
-
elif response.status_code == 401:
|
304 |
-
return False, "Token inválido ou sem permissão"
|
305 |
-
elif response.status_code == 404:
|
306 |
-
return False, "Modelo não encontrado"
|
307 |
-
else:
|
308 |
-
return False, f"Erro HTTP {response.status_code}: {response.text}"
|
309 |
-
|
310 |
-
except requests.exceptions.Timeout:
|
311 |
-
return False, "Timeout na requisição"
|
312 |
-
except requests.exceptions.RequestException as e:
|
313 |
-
return False, f"Erro na requisição: {str(e)}"
|
314 |
-
|
315 |
-
def test_model_availability(self, model_name: str) -> Tuple[bool, str]:
|
316 |
-
"""Testa se um modelo está disponível, combinando verificação de info e inferência."""
|
317 |
-
print(f"Testando modelo: {model_name}")
|
318 |
-
|
319 |
-
info_available, info_msg = self.check_model_info(model_name)
|
320 |
-
if not info_available:
|
321 |
-
return False, f"Info check failed: {info_msg}"
|
322 |
-
|
323 |
-
print(f" ✓ Info check: {info_msg}")
|
324 |
-
|
325 |
-
inference_available, inference_msg = self.test_model_inference(model_name)
|
326 |
|
327 |
-
if
|
328 |
-
|
329 |
-
return True, f"Disponível - {info_msg}"
|
330 |
-
else:
|
331 |
-
print(f" ✗ Inference check: {inference_msg}")
|
332 |
-
return False, f"Não disponível para inferência: {inference_msg}"
|
333 |
|
334 |
-
def
|
335 |
-
"""
|
|
|
|
|
|
|
336 |
prompt = self._convert_messages_to_prompt(messages)
|
337 |
|
338 |
url = f"https://api-inference.huggingface.co/models/{model_name}"
|
@@ -347,7 +260,7 @@ class HuggingFaceAPIClient:
|
|
347 |
}
|
348 |
|
349 |
try:
|
350 |
-
response = requests.post(url, headers=self.
|
351 |
response.raise_for_status()
|
352 |
|
353 |
result = response.json()
|
@@ -358,10 +271,171 @@ class HuggingFaceAPIClient:
|
|
358 |
else:
|
359 |
return f"Formato de resposta inesperado: {result}"
|
360 |
|
361 |
-
except requests.exceptions.HTTPError as http_err:
|
362 |
-
return f"Erro HTTP: {http_err.response.status_code} - {http_err.response.text}"
|
363 |
except requests.exceptions.RequestException as e:
|
364 |
-
return f"Erro na requisição: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
365 |
|
366 |
def _convert_messages_to_prompt(self, messages: List[Dict]) -> str:
|
367 |
"""Converte mensagens do formato chat para prompt simples."""
|
@@ -380,67 +454,70 @@ class HuggingFaceAPIClient:
|
|
380 |
prompt_parts.append("Assistente:")
|
381 |
return "\n\n".join(prompt_parts)
|
382 |
|
383 |
-
|
|
|
384 |
|
385 |
# --- Função para Testar e Atualizar Modelos ---
|
386 |
def test_and_update_models() -> int:
|
387 |
-
"""
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
"""
|
392 |
-
print("Testando disponibilidade dos novos modelos...")
|
393 |
-
print(f"Token HF disponível: {'Sim' if HF_TOKEN else 'Não'}")
|
394 |
-
print("-" * 60)
|
395 |
-
|
396 |
-
# Cria um dicionário temporário para os modelos disponíveis
|
397 |
-
temp_models = {}
|
398 |
|
399 |
-
|
400 |
-
|
401 |
-
is_available, message = api_client.test_model_availability(default_name)
|
402 |
-
|
403 |
-
if is_available:
|
404 |
-
temp_models[default_label] = default_name
|
405 |
-
print(f"✓ {default_label} (DEFAULT MODEL)")
|
406 |
-
else:
|
407 |
-
print(f"✗ {default_label} - {message} (MODELO PADRÃO INDISPONÍVEL)")
|
408 |
|
409 |
-
#
|
|
|
410 |
for model_label, model_name in NEW_MODELS_TO_TEST:
|
411 |
-
|
412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
413 |
continue
|
414 |
-
|
415 |
-
is_available, message = api_client.test_model_availability(model_name)
|
416 |
|
|
|
417 |
if is_available:
|
418 |
-
|
419 |
-
print(f"
|
420 |
else:
|
421 |
-
print(f"
|
422 |
-
|
423 |
time.sleep(1)
|
424 |
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
# Adiciona primeiro o modelo padrão (se disponível)
|
430 |
-
if default_label in temp_models:
|
431 |
-
MODELS[default_label] = temp_models.pop(default_label)
|
432 |
|
433 |
-
#
|
434 |
-
|
|
|
|
|
|
|
|
|
435 |
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
print(f"{i}. {label}")
|
441 |
|
442 |
-
print(f"\
|
443 |
-
print("=" *
|
444 |
|
445 |
save_updated_models()
|
446 |
return len(MODELS)
|
@@ -448,18 +525,31 @@ def test_and_update_models() -> int:
|
|
448 |
def save_updated_models():
|
449 |
"""Salva a lista atualizada de modelos em um arquivo."""
|
450 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
451 |
with open("models_available.json", "w", encoding="utf-8") as f:
|
452 |
-
json.dump(
|
453 |
-
print("Lista de modelos disponíveis salva em 'models_available.json'")
|
454 |
except Exception as e:
|
455 |
-
print(f"Erro ao salvar lista de modelos: {e}")
|
456 |
|
457 |
# --- Chat Principal ---
|
458 |
-
def responder_como_aldo(session_id: str, pergunta: str, modelo: str =
|
459 |
-
"""Gera resposta como Dr. Aldo Henrique."""
|
460 |
if not pergunta.strip():
|
461 |
return "Por favor, faça uma pergunta válida."
|
462 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
load_conversation_memory(session_id)
|
464 |
update_user_profile(session_id, pergunta)
|
465 |
|
@@ -483,7 +573,7 @@ def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFAULT_MO
|
|
483 |
- Use exemplos práticos.
|
484 |
- Considere o nível do usuário (iniciante, intermediário ou avançado).
|
485 |
- Use Markdown para formatar respostas, com ``` para blocos de código.
|
486 |
-
- Dentro do código sempre coloque comentários explicando para o
|
487 |
- Foque em tecnologia; se a pergunta for fora do escopo, informe educadamente que não é seu domínio.
|
488 |
"""
|
489 |
|
@@ -495,44 +585,416 @@ def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFAULT_MO
|
|
495 |
{"role": "user", "content": mensagem_usuario}
|
496 |
]
|
497 |
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
502 |
|
503 |
# --- Inicialização ---
|
504 |
def inicializar_sistema():
|
505 |
"""
|
506 |
-
Inicializa o sistema,
|
507 |
-
Retorna uma tupla: (status: bool, models: dict)
|
508 |
-
- status: True se >= 3 modelos disponíveis, False caso contrário
|
509 |
-
- models: Dicionário com os modelos disponíveis
|
510 |
"""
|
511 |
-
print("Inicializando Chatbot Dr. Aldo...")
|
512 |
|
513 |
num_available_models = test_and_update_models()
|
514 |
|
515 |
if num_available_models >= 1:
|
516 |
load_vector_store()
|
517 |
-
|
518 |
-
|
|
|
519 |
else:
|
520 |
-
|
521 |
-
|
|
|
522 |
|
|
|
523 |
if __name__ == "__main__":
|
524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
if status:
|
526 |
-
print("\n" + "="*
|
527 |
-
print("
|
528 |
-
print("="*
|
529 |
-
|
530 |
-
|
531 |
-
print(
|
532 |
-
|
533 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
534 |
print(clear_memory(session_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
535 |
else:
|
536 |
-
print("
|
537 |
-
|
538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
10 |
from langchain_community.vectorstores import FAISS
|
11 |
from langchain_community.embeddings import HuggingFaceEmbeddings
|
12 |
+
import openai
|
13 |
+
from groq import Groq
|
14 |
+
import google.generativeai as genai
|
15 |
|
16 |
# --- Configurações ---
|
17 |
BLOG_URL = "https://aldohenrique.com.br/"
|
18 |
VECTOR_STORE_PATH = "faiss_index_store.pkl"
|
19 |
PROCESSED_URLS_PATH = "processed_urls.pkl"
|
20 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
21 |
+
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
22 |
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
23 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # Para usar modelos gratuitos do OpenAI via outras APIs
|
24 |
|
25 |
+
# Lista inicial de modelos HuggingFace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
NEW_MODELS_TO_TEST = [
|
27 |
("Phi-3 Mini (Mais rápido)", "microsoft/Phi-3-mini-4k-instruct"),
|
28 |
("Zephyr 7B (Meio Termo)", "HuggingFaceH4/zephyr-7b-beta"),
|
29 |
+
("Microsoft 8B (Meio Termo)", "meta-llama/Meta-Llama-3-8B-Instruct"),
|
30 |
+
("Mistral-7B", "mistralai/Mistral-7B-Instruct-v0.3"),
|
31 |
+
("DialoGPT", "microsoft/DialoGPT-medium"),
|
32 |
+
("Google", "google/flan-t5-base"),
|
33 |
+
("Facebook", "facebook/bart-large-cnn")
|
34 |
]
|
35 |
|
36 |
+
# Modelos alternativos gratuitos
|
37 |
+
FALLBACK_MODELS = [
|
38 |
+
# Groq (gratuito com rate limit)
|
39 |
+
("Llama 3 8B (Groq)", "llama3-8b-8192", "groq"),
|
40 |
+
("Mixtral 8x7B (Groq)", "mixtral-8x7b-32768", "groq"),
|
41 |
+
("Gemma 7B (Groq)", "gemma-7b-it", "groq"),
|
42 |
|
43 |
+
# Google Gemini (gratuito com rate limit)
|
44 |
+
("Gemini 1.5 Flash (Google)", "gemini-1.5-flash", "gemini"),
|
45 |
+
("Gemini 1.5 Pro (Google)", "gemini-1.5-pro", "gemini"),
|
46 |
+
|
47 |
+
# Cohere (gratuito com rate limit)
|
48 |
+
("Command R+ (Cohere)", "command-r-plus", "cohere"),
|
49 |
+
|
50 |
+
# Anthropic via outras APIs (quando disponível gratuitamente)
|
51 |
+
("Claude 3 Haiku (Fallback)", "claude-3-haiku-20240307", "anthropic"),
|
52 |
+
|
53 |
+
# Modelos via APIs públicas gratuitas
|
54 |
+
("Llama 2 7B (Together)", "togethercomputer/llama-2-7b-chat", "together"),
|
55 |
+
("CodeLlama 7B (Together)", "togethercomputer/CodeLlama-7b-Instruct", "together"),
|
56 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
+
# Dicionário unificado de modelos
|
59 |
+
MODELS = {}
|
60 |
+
DEFAULT_MODEL = "Phi-3 Mini (Mais rápido)"
|
61 |
|
62 |
+
# --- Gerenciamento de Sessão (mantido igual) ---
|
63 |
user_sessions: Dict[str, Dict[str, List | Dict]] = {}
|
64 |
MAX_MEMORY_LENGTH = 5
|
65 |
|
|
|
155 |
os.remove(memory_path)
|
156 |
return "Memória limpa com sucesso!"
|
157 |
|
158 |
+
# --- RAG (Crawling e Vector Store) - Mantido igual ---
|
159 |
vector_store: Optional[FAISS] = None
|
160 |
|
161 |
def get_all_blog_links(url: str) -> set:
|
|
|
227 |
print(f"Erro ao buscar contexto: {e}")
|
228 |
return ""
|
229 |
|
230 |
+
# --- Classe Unificada de API Client ---
|
231 |
+
class UnifiedAPIClient:
|
232 |
+
def __init__(self, hf_token: str = None, groq_api_key: str = None, gemini_api_key: str = None):
|
233 |
+
self.hf_token = hf_token
|
234 |
+
self.groq_api_key = groq_api_key
|
235 |
+
self.gemini_api_key = gemini_api_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
+
# Inicializa clientes
|
238 |
+
self.hf_headers = {"Authorization": f"Bearer {hf_token}", "Content-Type": "application/json"} if hf_token else None
|
239 |
+
self.groq_client = Groq(api_key=groq_api_key) if groq_api_key else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
240 |
|
241 |
+
if gemini_api_key:
|
242 |
+
genai.configure(api_key=gemini_api_key)
|
|
|
|
|
|
|
|
|
243 |
|
244 |
+
def query_huggingface(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
245 |
+
"""Consulta modelos do Hugging Face."""
|
246 |
+
if not self.hf_headers:
|
247 |
+
return "Token do Hugging Face não configurado"
|
248 |
+
|
249 |
prompt = self._convert_messages_to_prompt(messages)
|
250 |
|
251 |
url = f"https://api-inference.huggingface.co/models/{model_name}"
|
|
|
260 |
}
|
261 |
|
262 |
try:
|
263 |
+
response = requests.post(url, headers=self.hf_headers, json=payload, timeout=30)
|
264 |
response.raise_for_status()
|
265 |
|
266 |
result = response.json()
|
|
|
271 |
else:
|
272 |
return f"Formato de resposta inesperado: {result}"
|
273 |
|
|
|
|
|
274 |
except requests.exceptions.RequestException as e:
|
275 |
+
return f"Erro na requisição HuggingFace: {str(e)}"
|
276 |
+
|
277 |
+
def query_groq(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
278 |
+
"""Consulta modelos do Groq."""
|
279 |
+
if not self.groq_client:
|
280 |
+
return "API Key do Groq não configurada"
|
281 |
+
|
282 |
+
try:
|
283 |
+
# Converte mensagens para formato do Groq
|
284 |
+
groq_messages = []
|
285 |
+
for msg in messages:
|
286 |
+
groq_messages.append({
|
287 |
+
"role": msg["role"],
|
288 |
+
"content": msg["content"]
|
289 |
+
})
|
290 |
+
|
291 |
+
response = self.groq_client.chat.completions.create(
|
292 |
+
model=model_name,
|
293 |
+
messages=groq_messages,
|
294 |
+
max_tokens=max_tokens,
|
295 |
+
temperature=0.7
|
296 |
+
)
|
297 |
+
|
298 |
+
return response.choices[0].message.content.strip()
|
299 |
+
|
300 |
+
except Exception as e:
|
301 |
+
return f"Erro na requisição Groq: {str(e)}"
|
302 |
+
|
303 |
+
def query_gemini(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
304 |
+
"""Consulta modelos do Google Gemini."""
|
305 |
+
if not self.gemini_api_key:
|
306 |
+
return "API Key do Gemini não configurada"
|
307 |
+
|
308 |
+
try:
|
309 |
+
# Converte mensagens para formato do Gemini
|
310 |
+
conversation_text = ""
|
311 |
+
for msg in messages:
|
312 |
+
if msg["role"] == "system":
|
313 |
+
conversation_text += f"Instrução: {msg['content']}\n\n"
|
314 |
+
elif msg["role"] == "user":
|
315 |
+
conversation_text += f"Usuário: {msg['content']}\n\n"
|
316 |
+
elif msg["role"] == "assistant":
|
317 |
+
conversation_text += f"Assistente: {msg['content']}\n\n"
|
318 |
+
|
319 |
+
conversation_text += "Assistente:"
|
320 |
+
|
321 |
+
model = genai.GenerativeModel(model_name)
|
322 |
+
response = model.generate_content(
|
323 |
+
conversation_text,
|
324 |
+
generation_config=genai.types.GenerationConfig(
|
325 |
+
max_output_tokens=max_tokens,
|
326 |
+
temperature=0.7
|
327 |
+
)
|
328 |
+
)
|
329 |
+
|
330 |
+
return response.text.strip()
|
331 |
+
|
332 |
+
except Exception as e:
|
333 |
+
return f"Erro na requisição Gemini: {str(e)}"
|
334 |
+
|
335 |
+
def query_cohere(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
336 |
+
"""Consulta modelos do Cohere via API pública."""
|
337 |
+
try:
|
338 |
+
# Converte mensagens para prompt
|
339 |
+
prompt = self._convert_messages_to_prompt(messages)
|
340 |
+
|
341 |
+
# Usa API pública do Cohere (se disponível)
|
342 |
+
url = "https://api.cohere.ai/v1/generate"
|
343 |
+
headers = {
|
344 |
+
"Content-Type": "application/json",
|
345 |
+
"Authorization": f"Bearer {os.getenv('COHERE_API_KEY', 'demo-key')}"
|
346 |
+
}
|
347 |
+
|
348 |
+
payload = {
|
349 |
+
"model": model_name,
|
350 |
+
"prompt": prompt,
|
351 |
+
"max_tokens": max_tokens,
|
352 |
+
"temperature": 0.7
|
353 |
+
}
|
354 |
+
|
355 |
+
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
356 |
+
|
357 |
+
if response.status_code == 200:
|
358 |
+
result = response.json()
|
359 |
+
return result.get("generations", [{}])[0].get("text", "").strip()
|
360 |
+
else:
|
361 |
+
return f"Erro Cohere: {response.status_code}"
|
362 |
+
|
363 |
+
except Exception as e:
|
364 |
+
return f"Erro na requisição Cohere: {str(e)}"
|
365 |
+
|
366 |
+
def query_together(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
367 |
+
"""Consulta modelos via Together AI (API pública)."""
|
368 |
+
try:
|
369 |
+
prompt = self._convert_messages_to_prompt(messages)
|
370 |
+
|
371 |
+
# API do Together AI
|
372 |
+
url = "https://api.together.xyz/inference"
|
373 |
+
headers = {
|
374 |
+
"Content-Type": "application/json",
|
375 |
+
"Authorization": f"Bearer {os.getenv('TOGETHER_API_KEY', 'demo-key')}"
|
376 |
+
}
|
377 |
+
|
378 |
+
payload = {
|
379 |
+
"model": model_name,
|
380 |
+
"prompt": prompt,
|
381 |
+
"max_tokens": max_tokens,
|
382 |
+
"temperature": 0.7
|
383 |
+
}
|
384 |
+
|
385 |
+
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
386 |
+
|
387 |
+
if response.status_code == 200:
|
388 |
+
result = response.json()
|
389 |
+
return result.get("output", {}).get("choices", [{}])[0].get("text", "").strip()
|
390 |
+
else:
|
391 |
+
return f"Erro Together: {response.status_code}"
|
392 |
+
|
393 |
+
except Exception as e:
|
394 |
+
return f"Erro na requisição Together: {str(e)}"
|
395 |
+
|
396 |
+
def query_model(self, model_display_name: str, model_info: Tuple[str, str], messages: List[Dict], max_tokens: int = 1000) -> str:
|
397 |
+
"""Consulta um modelo baseado no seu tipo/provedor."""
|
398 |
+
model_name, provider = model_info
|
399 |
+
|
400 |
+
try:
|
401 |
+
if provider == "huggingface":
|
402 |
+
return self.query_huggingface(model_name, messages, max_tokens)
|
403 |
+
elif provider == "groq":
|
404 |
+
return self.query_groq(model_name, messages, max_tokens)
|
405 |
+
elif provider == "gemini":
|
406 |
+
return self.query_gemini(model_name, messages, max_tokens)
|
407 |
+
elif provider == "cohere":
|
408 |
+
return self.query_cohere(model_name, messages, max_tokens)
|
409 |
+
elif provider == "together":
|
410 |
+
return self.query_together(model_name, messages, max_tokens)
|
411 |
+
else:
|
412 |
+
return f"Provedor desconhecido: {provider}"
|
413 |
+
|
414 |
+
except Exception as e:
|
415 |
+
return f"Erro ao consultar modelo {model_display_name}: {str(e)}"
|
416 |
+
|
417 |
+
def test_model_availability(self, model_name: str, provider: str) -> Tuple[bool, str]:
|
418 |
+
"""Testa se um modelo está disponível."""
|
419 |
+
test_messages = [
|
420 |
+
{"role": "system", "content": "Você é um assistente útil."},
|
421 |
+
{"role": "user", "content": "Teste de disponibilidade. Responda apenas 'OK'."}
|
422 |
+
]
|
423 |
+
|
424 |
+
try:
|
425 |
+
response = self.query_model(
|
426 |
+
f"Teste {model_name}",
|
427 |
+
(model_name, provider),
|
428 |
+
test_messages,
|
429 |
+
max_tokens=10
|
430 |
+
)
|
431 |
+
|
432 |
+
if response and not response.startswith("Erro"):
|
433 |
+
return True, "Modelo disponível"
|
434 |
+
else:
|
435 |
+
return False, response
|
436 |
+
|
437 |
+
except Exception as e:
|
438 |
+
return False, f"Erro no teste: {str(e)}"
|
439 |
|
440 |
def _convert_messages_to_prompt(self, messages: List[Dict]) -> str:
|
441 |
"""Converte mensagens do formato chat para prompt simples."""
|
|
|
454 |
prompt_parts.append("Assistente:")
|
455 |
return "\n\n".join(prompt_parts)
|
456 |
|
457 |
+
# Inicializa o cliente unificado
|
458 |
+
api_client = UnifiedAPIClient(HF_TOKEN, GROQ_API_KEY, GEMINI_API_KEY)
|
459 |
|
460 |
# --- Função para Testar e Atualizar Modelos ---
|
461 |
def test_and_update_models() -> int:
|
462 |
+
"""Testa a disponibilidade de todos os modelos e atualiza a lista MODELS."""
|
463 |
+
print("Testando disponibilidade dos modelos...")
|
464 |
+
print(f"Tokens disponíveis: HF={bool(HF_TOKEN)}, Groq={bool(GROQ_API_KEY)}, Gemini={bool(GEMINI_API_KEY)}")
|
465 |
+
print("-" * 80)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
466 |
|
467 |
+
global MODELS
|
468 |
+
MODELS.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
469 |
|
470 |
+
# Testa modelos do Hugging Face
|
471 |
+
print("🔍 TESTANDO MODELOS HUGGING FACE:")
|
472 |
for model_label, model_name in NEW_MODELS_TO_TEST:
|
473 |
+
if HF_TOKEN:
|
474 |
+
is_available, message = api_client.test_model_availability(model_name, "huggingface")
|
475 |
+
if is_available:
|
476 |
+
MODELS[model_label] = (model_name, "huggingface")
|
477 |
+
print(f"✅ {model_label}")
|
478 |
+
else:
|
479 |
+
print(f"❌ {model_label} - {message}")
|
480 |
+
else:
|
481 |
+
print(f"⚠️ {model_label} - Token HF não configurado")
|
482 |
+
time.sleep(1)
|
483 |
+
|
484 |
+
# Testa modelos de fallback
|
485 |
+
print("\n🔍 TESTANDO MODELOS DE FALLBACK:")
|
486 |
+
for model_label, model_name, provider in FALLBACK_MODELS:
|
487 |
+
# Verifica se as credenciais estão disponíveis
|
488 |
+
if provider == "groq" and not GROQ_API_KEY:
|
489 |
+
print(f"⚠️ {model_label} - API Key Groq não configurada")
|
490 |
+
continue
|
491 |
+
elif provider == "gemini" and not GEMINI_API_KEY:
|
492 |
+
print(f"⚠️ {model_label} - API Key Gemini não configurada")
|
493 |
continue
|
|
|
|
|
494 |
|
495 |
+
is_available, message = api_client.test_model_availability(model_name, provider)
|
496 |
if is_available:
|
497 |
+
MODELS[model_label] = (model_name, provider)
|
498 |
+
print(f"✅ {model_label} ({provider.upper()})")
|
499 |
else:
|
500 |
+
print(f"❌ {model_label} - {message}")
|
|
|
501 |
time.sleep(1)
|
502 |
|
503 |
+
print("\n" + "=" * 80)
|
504 |
+
print("📊 RESUMO DOS MODELOS DISPONÍVEIS:")
|
505 |
+
print("=" * 80)
|
|
|
|
|
|
|
|
|
506 |
|
507 |
+
# Organiza por provedor
|
508 |
+
providers = {}
|
509 |
+
for label, (model_name, provider) in MODELS.items():
|
510 |
+
if provider not in providers:
|
511 |
+
providers[provider] = []
|
512 |
+
providers[provider].append(label)
|
513 |
|
514 |
+
for provider, models in providers.items():
|
515 |
+
print(f"\n🔹 {provider.upper()}:")
|
516 |
+
for i, model in enumerate(models, 1):
|
517 |
+
print(f" {i}. {model}")
|
|
|
518 |
|
519 |
+
print(f"\n🎯 TOTAL DE MODELOS DISPONÍVEIS: {len(MODELS)}")
|
520 |
+
print("=" * 80)
|
521 |
|
522 |
save_updated_models()
|
523 |
return len(MODELS)
|
|
|
525 |
def save_updated_models():
|
526 |
"""Salva a lista atualizada de modelos em um arquivo."""
|
527 |
try:
|
528 |
+
models_info = {}
|
529 |
+
for label, (model_name, provider) in MODELS.items():
|
530 |
+
models_info[label] = {
|
531 |
+
"model_name": model_name,
|
532 |
+
"provider": provider
|
533 |
+
}
|
534 |
+
|
535 |
with open("models_available.json", "w", encoding="utf-8") as f:
|
536 |
+
json.dump(models_info, f, ensure_ascii=False, indent=2)
|
537 |
+
print("📁 Lista de modelos disponíveis salva em 'models_available.json'")
|
538 |
except Exception as e:
|
539 |
+
print(f"❌ Erro ao salvar lista de modelos: {e}")
|
540 |
|
541 |
# --- Chat Principal ---
|
542 |
+
def responder_como_aldo(session_id: str, pergunta: str, modelo: str = None) -> str:
|
543 |
+
"""Gera resposta como Dr. Aldo Henrique usando o modelo selecionado."""
|
544 |
if not pergunta.strip():
|
545 |
return "Por favor, faça uma pergunta válida."
|
546 |
+
|
547 |
+
# Seleciona modelo disponível
|
548 |
+
if not modelo or modelo not in MODELS:
|
549 |
+
if not MODELS:
|
550 |
+
return "❌ Nenhum modelo está disponível no momento. Verifique as configurações das APIs."
|
551 |
+
modelo = next(iter(MODELS.keys())) # Pega o primeiro modelo disponível
|
552 |
+
|
553 |
load_conversation_memory(session_id)
|
554 |
update_user_profile(session_id, pergunta)
|
555 |
|
|
|
573 |
- Use exemplos práticos.
|
574 |
- Considere o nível do usuário (iniciante, intermediário ou avançado).
|
575 |
- Use Markdown para formatar respostas, com ``` para blocos de código.
|
576 |
+
- Dentro do código sempre coloque comentários explicando para o aluno aprender com os comentários.
|
577 |
- Foque em tecnologia; se a pergunta for fora do escopo, informe educadamente que não é seu domínio.
|
578 |
"""
|
579 |
|
|
|
585 |
{"role": "user", "content": mensagem_usuario}
|
586 |
]
|
587 |
|
588 |
+
# Obtém informações do modelo
|
589 |
+
model_info = MODELS[modelo]
|
590 |
+
provider = model_info[1]
|
591 |
+
|
592 |
+
print(f"🤖 Usando modelo: {modelo} ({provider.upper()})")
|
593 |
+
|
594 |
+
resposta = api_client.query_model(modelo, model_info, messages)
|
595 |
+
|
596 |
+
# Adiciona informação do modelo usado na resposta
|
597 |
+
resposta_final = f"{resposta}\n\n---\n*Resposta gerada por: {modelo} ({provider.upper()})*"
|
598 |
+
|
599 |
+
add_to_memory(session_id, pergunta, resposta_final)
|
600 |
+
return resposta_final
|
601 |
+
|
602 |
+
def listar_modelos_disponiveis() -> str:
|
603 |
+
"""Retorna uma lista formatada dos modelos disponíveis."""
|
604 |
+
if not MODELS:
|
605 |
+
return "❌ Nenhum modelo está disponível no momento."
|
606 |
+
|
607 |
+
resultado = "🤖 **MODELOS DISPONÍVEIS:**\n\n"
|
608 |
+
|
609 |
+
# Organiza por provedor
|
610 |
+
providers = {}
|
611 |
+
for label, (model_name, provider) in MODELS.items():
|
612 |
+
if provider not in providers:
|
613 |
+
providers[provider] = []
|
614 |
+
providers[provider].append(label)
|
615 |
+
|
616 |
+
for provider, models in providers.items():
|
617 |
+
resultado += f"**{provider.upper()}:**\n"
|
618 |
+
for i, model in enumerate(models, 1):
|
619 |
+
resultado += f" {i}. {model}\n"
|
620 |
+
resultado += "\n"
|
621 |
+
|
622 |
+
return resultado
|
623 |
|
624 |
# --- Inicialização ---
|
625 |
def inicializar_sistema():
|
626 |
"""
|
627 |
+
Inicializa o sistema, testando todos os modelos disponíveis.
|
628 |
+
Retorna uma tupla: (status: bool, models: dict, message: str)
|
|
|
|
|
629 |
"""
|
630 |
+
print("🚀 Inicializando Chatbot Dr. Aldo com Fallback para Modelos Gratuitos...")
|
631 |
|
632 |
num_available_models = test_and_update_models()
|
633 |
|
634 |
if num_available_models >= 1:
|
635 |
load_vector_store()
|
636 |
+
message = f"✅ Sistema inicializado com {num_available_models} modelo(s) disponível(is)!"
|
637 |
+
print(message)
|
638 |
+
return True, MODELS, message
|
639 |
else:
|
640 |
+
message = "❌ Nenhum modelo disponível. Verifique as configurações das APIs."
|
641 |
+
print(message)
|
642 |
+
return False, MODELS, message
|
643 |
|
644 |
+
# --- Exemplo de uso ---
|
645 |
if __name__ == "__main__":
|
646 |
+
# Configuração de exemplo das APIs (coloque suas chaves aqui)
|
647 |
+
print("🔧 Configuração das APIs:")
|
648 |
+
print(f" HuggingFace: {'✅ Configurado' if HF_TOKEN else '❌ Não configurado'}")
|
649 |
+
print(f" Groq: {'✅ Configurado' if GROQ_API_KEY else '❌ Não configurado'}")
|
650 |
+
print(f" Gemini: {'✅ Configurado' if GEMINI_API_KEY else '❌ Não configurado'}")
|
651 |
+
print()
|
652 |
+
|
653 |
+
# Inicializa o sistema
|
654 |
+
status, models, message = inicializar_sistema()
|
655 |
+
|
656 |
if status:
|
657 |
+
print("\n" + "="*70)
|
658 |
+
print("🧪 REALIZANDO TESTE BÁSICO DO CHATBOT...")
|
659 |
+
print("="*70)
|
660 |
+
|
661 |
+
# Lista modelos disponíveis
|
662 |
+
print(listar_modelos_disponiveis())
|
663 |
+
|
664 |
+
# Testa com diferentes modelos
|
665 |
+
session_id = "teste_demo"
|
666 |
+
|
667 |
+
# Primeira pergunta
|
668 |
+
print("👤 PERGUNTA 1: O que é Java?")
|
669 |
+
print("🤖 RESPOSTA:")
|
670 |
+
resposta1 = responder_como_aldo(session_id, "O que é Java?")
|
671 |
+
print(resposta1)
|
672 |
+
|
673 |
+
print("\n" + "-"*70)
|
674 |
+
|
675 |
+
# Segunda pergunta
|
676 |
+
print("👤 PERGUNTA 2: Mostre um exemplo de código Java simples.")
|
677 |
+
print("🤖 RESPOSTA:")
|
678 |
+
resposta2 = responder_como_aldo(session_id, "Mostre um exemplo de código Java simples.")
|
679 |
+
print(resposta2)
|
680 |
+
|
681 |
+
print("\n" + "-"*70)
|
682 |
+
|
683 |
+
# Terceira pergunta para testar contexto
|
684 |
+
print("👤 PERGUNTA 3: Explique melhor o conceito de classe no exemplo anterior.")
|
685 |
+
print("🤖 RESPOSTA:")
|
686 |
+
resposta3 = responder_como_aldo(session_id, "Explique melhor o conceito de classe no exemplo anterior.")
|
687 |
+
print(resposta3)
|
688 |
+
|
689 |
+
print("\n" + "-"*70)
|
690 |
+
print("🧹 LIMPANDO MEMÓRIA DE TESTE...")
|
691 |
print(clear_memory(session_id))
|
692 |
+
print("="*70)
|
693 |
+
|
694 |
+
else:
|
695 |
+
print(f"\n❌ {message}")
|
696 |
+
print("\n💡 DICAS PARA RESOLVER:")
|
697 |
+
print("1. Configure pelo menos uma das APIs:")
|
698 |
+
print(" - HuggingFace: export HF_TOKEN='seu_token_aqui'")
|
699 |
+
print(" - Groq: export GROQ_API_KEY='sua_chave_aqui'")
|
700 |
+
print(" - Gemini: export GEMINI_API_KEY='sua_chave_aqui'")
|
701 |
+
print("\n2. APIs gratuitas recomendadas:")
|
702 |
+
print(" - Groq: https://console.groq.com/ (gratuito)")
|
703 |
+
print(" - Google AI Studio: https://aistudio.google.com/ (gratuito)")
|
704 |
+
print(" - HuggingFace: https://huggingface.co/settings/tokens (gratuito)")
|
705 |
+
|
706 |
+
|
707 |
+
# --- Funções Auxiliares para Integração ---
|
708 |
+
|
709 |
+
def criar_interface_gradio():
|
710 |
+
"""Cria interface Gradio para o chatbot (opcional)."""
|
711 |
+
try:
|
712 |
+
import gradio as gr
|
713 |
+
|
714 |
+
def chat_interface(message, history, model_choice):
|
715 |
+
"""Interface do chat para Gradio."""
|
716 |
+
session_id = "gradio_session"
|
717 |
+
|
718 |
+
if not message.strip():
|
719 |
+
return "", history
|
720 |
+
|
721 |
+
# Seleciona modelo se especificado
|
722 |
+
modelo_selecionado = model_choice if model_choice in MODELS else None
|
723 |
+
|
724 |
+
# Gera resposta
|
725 |
+
resposta = responder_como_aldo(session_id, message, modelo_selecionado)
|
726 |
+
|
727 |
+
# Atualiza histórico
|
728 |
+
history.append((message, resposta))
|
729 |
+
|
730 |
+
return "", history
|
731 |
+
|
732 |
+
def listar_modelos():
|
733 |
+
"""Lista modelos para dropdown."""
|
734 |
+
return list(MODELS.keys()) if MODELS else ["Nenhum modelo disponível"]
|
735 |
+
|
736 |
+
# Cria interface
|
737 |
+
with gr.Blocks(title="Dr. Aldo Henrique - Chatbot Educacional") as demo:
|
738 |
+
gr.Markdown("# 🤖 Dr. Aldo Henrique - Chatbot Educacional")
|
739 |
+
gr.Markdown("Assistente especializado em Ciência da Computação, IA e Programação")
|
740 |
+
|
741 |
+
with gr.Row():
|
742 |
+
with gr.Column(scale=3):
|
743 |
+
chatbot = gr.Chatbot(
|
744 |
+
label="Conversa",
|
745 |
+
height=500,
|
746 |
+
show_label=True
|
747 |
+
)
|
748 |
+
|
749 |
+
with gr.Row():
|
750 |
+
msg = gr.Textbox(
|
751 |
+
label="Sua pergunta",
|
752 |
+
placeholder="Digite sua pergunta sobre tecnologia...",
|
753 |
+
lines=2,
|
754 |
+
scale=4
|
755 |
+
)
|
756 |
+
send_btn = gr.Button("Enviar", scale=1)
|
757 |
+
|
758 |
+
with gr.Column(scale=1):
|
759 |
+
model_dropdown = gr.Dropdown(
|
760 |
+
choices=listar_modelos(),
|
761 |
+
label="Modelo",
|
762 |
+
value=list(MODELS.keys())[0] if MODELS else None,
|
763 |
+
interactive=True
|
764 |
+
)
|
765 |
+
|
766 |
+
gr.Markdown("### 📊 Modelos Disponíveis")
|
767 |
+
model_info = gr.Markdown(listar_modelos_disponiveis())
|
768 |
+
|
769 |
+
refresh_btn = gr.Button("🔄 Atualizar Modelos")
|
770 |
+
clear_btn = gr.Button("🧹 Limpar Conversa")
|
771 |
+
|
772 |
+
# Eventos
|
773 |
+
send_btn.click(
|
774 |
+
chat_interface,
|
775 |
+
inputs=[msg, chatbot, model_dropdown],
|
776 |
+
outputs=[msg, chatbot]
|
777 |
+
)
|
778 |
+
|
779 |
+
msg.submit(
|
780 |
+
chat_interface,
|
781 |
+
inputs=[msg, chatbot, model_dropdown],
|
782 |
+
outputs=[msg, chatbot]
|
783 |
+
)
|
784 |
+
|
785 |
+
def refresh_models():
|
786 |
+
test_and_update_models()
|
787 |
+
return gr.Dropdown(choices=listar_modelos()), listar_modelos_disponiveis()
|
788 |
+
|
789 |
+
refresh_btn.click(
|
790 |
+
refresh_models,
|
791 |
+
outputs=[model_dropdown, model_info]
|
792 |
+
)
|
793 |
+
|
794 |
+
def clear_chat():
|
795 |
+
clear_memory("gradio_session")
|
796 |
+
return []
|
797 |
+
|
798 |
+
clear_btn.click(clear_chat, outputs=[chatbot])
|
799 |
+
|
800 |
+
return demo
|
801 |
+
|
802 |
+
except ImportError:
|
803 |
+
print("⚠️ Gradio não está instalado. Para usar a interface web, instale com:")
|
804 |
+
print(" pip install gradio")
|
805 |
+
return None
|
806 |
+
|
807 |
+
def criar_interface_streamlit():
|
808 |
+
"""Cria interface Streamlit para o chatbot (opcional)."""
|
809 |
+
try:
|
810 |
+
import streamlit as st
|
811 |
+
|
812 |
+
def main():
|
813 |
+
st.title("🤖 Dr. Aldo Henrique - Chatbot Educacional")
|
814 |
+
st.markdown("Assistente especializado em Ciência da Computação, IA e Programação")
|
815 |
+
|
816 |
+
# Sidebar com informações
|
817 |
+
with st.sidebar:
|
818 |
+
st.header("🔧 Configurações")
|
819 |
+
|
820 |
+
# Seleção de modelo
|
821 |
+
if MODELS:
|
822 |
+
modelo_selecionado = st.selectbox(
|
823 |
+
"Selecione o modelo:",
|
824 |
+
options=list(MODELS.keys()),
|
825 |
+
index=0
|
826 |
+
)
|
827 |
+
else:
|
828 |
+
st.error("Nenhum modelo disponível")
|
829 |
+
return
|
830 |
+
|
831 |
+
# Botão para atualizar modelos
|
832 |
+
if st.button("🔄 Atualizar Modelos"):
|
833 |
+
with st.spinner("Testando modelos..."):
|
834 |
+
test_and_update_models()
|
835 |
+
st.success("Modelos atualizados!")
|
836 |
+
st.rerun()
|
837 |
+
|
838 |
+
# Informações dos modelos
|
839 |
+
st.markdown("### 📊 Modelos Disponíveis")
|
840 |
+
st.markdown(listar_modelos_disponiveis())
|
841 |
+
|
842 |
+
# Limpar conversa
|
843 |
+
if st.button("🧹 Limpar Conversa"):
|
844 |
+
clear_memory("streamlit_session")
|
845 |
+
if "messages" in st.session_state:
|
846 |
+
del st.session_state.messages
|
847 |
+
st.success("Conversa limpa!")
|
848 |
+
st.rerun()
|
849 |
+
|
850 |
+
# Inicializar mensagens
|
851 |
+
if "messages" not in st.session_state:
|
852 |
+
st.session_state.messages = []
|
853 |
+
|
854 |
+
# Exibir mensagens
|
855 |
+
for message in st.session_state.messages:
|
856 |
+
with st.chat_message(message["role"]):
|
857 |
+
st.markdown(message["content"])
|
858 |
+
|
859 |
+
# Input do usuário
|
860 |
+
if prompt := st.chat_input("Digite sua pergunta sobre tecnologia..."):
|
861 |
+
# Adicionar mensagem do usuário
|
862 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
863 |
+
with st.chat_message("user"):
|
864 |
+
st.markdown(prompt)
|
865 |
+
|
866 |
+
# Gerar resposta
|
867 |
+
with st.chat_message("assistant"):
|
868 |
+
with st.spinner("Pensando..."):
|
869 |
+
resposta = responder_como_aldo("streamlit_session", prompt, modelo_selecionado)
|
870 |
+
st.markdown(resposta)
|
871 |
+
|
872 |
+
# Adicionar resposta do assistente
|
873 |
+
st.session_state.messages.append({"role": "assistant", "content": resposta})
|
874 |
+
|
875 |
+
return main
|
876 |
+
|
877 |
+
except ImportError:
|
878 |
+
print("⚠️ Streamlit não está instalado. Para usar a interface web, instale com:")
|
879 |
+
print(" pip install streamlit")
|
880 |
+
return None
|
881 |
+
|
882 |
+
def executar_interface_web(tipo="gradio"):
|
883 |
+
"""Executa interface web do chatbot."""
|
884 |
+
print(f"🌐 Iniciando interface web ({tipo})...")
|
885 |
+
|
886 |
+
# Inicializa sistema
|
887 |
+
status, models, message = inicializar_sistema()
|
888 |
+
|
889 |
+
if not status:
|
890 |
+
print(f"❌ Não foi possível inicializar: {message}")
|
891 |
+
return
|
892 |
+
|
893 |
+
if tipo.lower() == "gradio":
|
894 |
+
demo = criar_interface_gradio()
|
895 |
+
if demo:
|
896 |
+
demo.launch(
|
897 |
+
server_name="0.0.0.0",
|
898 |
+
server_port=7860,
|
899 |
+
share=True,
|
900 |
+
debug=True
|
901 |
+
)
|
902 |
+
elif tipo.lower() == "streamlit":
|
903 |
+
main_func = criar_interface_streamlit()
|
904 |
+
if main_func:
|
905 |
+
print("Execute com: streamlit run nome_do_arquivo.py")
|
906 |
+
return main_func
|
907 |
else:
|
908 |
+
print("❌ Tipo de interface não suportado. Use 'gradio' ou 'streamlit'")
|
909 |
+
|
910 |
+
# --- Função para Instalar Dependências ---
|
911 |
+
def instalar_dependencias():
|
912 |
+
"""Instala dependências necessárias para o sistema."""
|
913 |
+
import subprocess
|
914 |
+
import sys
|
915 |
+
|
916 |
+
dependencias = [
|
917 |
+
"requests",
|
918 |
+
"beautifulsoup4",
|
919 |
+
"langchain",
|
920 |
+
"langchain-community",
|
921 |
+
"faiss-cpu",
|
922 |
+
"sentence-transformers",
|
923 |
+
"groq",
|
924 |
+
"google-generativeai",
|
925 |
+
"gradio",
|
926 |
+
"streamlit"
|
927 |
+
]
|
928 |
+
|
929 |
+
print("📦 Instalando dependências...")
|
930 |
+
for dep in dependencias:
|
931 |
+
try:
|
932 |
+
subprocess.check_call([sys.executable, "-m", "pip", "install", dep])
|
933 |
+
print(f"✅ {dep} instalado com sucesso")
|
934 |
+
except subprocess.CalledProcessError:
|
935 |
+
print(f"❌ Erro ao instalar {dep}")
|
936 |
+
|
937 |
+
# --- Instruções de Uso ---
|
938 |
+
def mostrar_instrucoes():
|
939 |
+
"""Mostra instruções de uso do sistema."""
|
940 |
+
print("""
|
941 |
+
🚀 INSTRUÇÕES DE USO - Dr. Aldo Henrique Chatbot
|
942 |
+
|
943 |
+
1. 🔑 CONFIGURAÇÃO DAS APIs:
|
944 |
+
Configure pelo menos uma das variáveis de ambiente:
|
945 |
+
|
946 |
+
export HF_TOKEN="seu_token_huggingface"
|
947 |
+
export GROQ_API_KEY="sua_chave_groq"
|
948 |
+
export GEMINI_API_KEY="sua_chave_gemini"
|
949 |
+
|
950 |
+
2. 🆓 APIS GRATUITAS RECOMENDADAS:
|
951 |
+
|
952 |
+
• Groq (Recomendado): https://console.groq.com/
|
953 |
+
- Modelos: Llama 3, Mixtral, Gemma
|
954 |
+
- Rate limit: 30 req/min (gratuito)
|
955 |
+
|
956 |
+
• Google AI Studio: https://aistudio.google.com/
|
957 |
+
- Modelos: Gemini 1.5 Flash/Pro
|
958 |
+
- Rate limit: 15 req/min (gratuito)
|
959 |
+
|
960 |
+
• HuggingFace: https://huggingface.co/settings/tokens
|
961 |
+
- Modelos: Diversos modelos open source
|
962 |
+
- Rate limit: Variável por modelo
|
963 |
+
|
964 |
+
3. 📚 EXEMPLOS DE USO:
|
965 |
+
|
966 |
+
# Uso básico
|
967 |
+
python nome_do_arquivo.py
|
968 |
+
|
969 |
+
# Interface web Gradio
|
970 |
+
executar_interface_web("gradio")
|
971 |
+
|
972 |
+
# Interface web Streamlit
|
973 |
+
executar_interface_web("streamlit")
|
974 |
+
|
975 |
+
4. 🎯 FUNCIONALIDADES:
|
976 |
+
|
977 |
+
• Chat inteligente com memória de conversa
|
978 |
+
• Múltiplos modelos de IA como fallback
|
979 |
+
• RAG (busca no blog do Dr. Aldo)
|
980 |
+
• Perfil adaptativo do usuário
|
981 |
+
• Respostas educacionais personalizadas
|
982 |
+
|
983 |
+
5. 🔧 SOLUÇÃO DE PROBLEMAS:
|
984 |
+
|
985 |
+
• Sem modelos disponíveis: Verifique as chaves das APIs
|
986 |
+
• Erro de token: Regenere as chaves nas respectivas plataformas
|
987 |
+
• Rate limit: Aguarde alguns minutos entre as requisições
|
988 |
+
• Erro de dependência: Execute instalar_dependencias()
|
989 |
+
|
990 |
+
6. 📞 SUPORTE:
|
991 |
+
|
992 |
+
Para dúvidas técnicas sobre o código, consulte:
|
993 |
+
• Blog: https://aldohenrique.com.br/
|
994 |
+
• Documentação das APIs utilizadas
|
995 |
+
|
996 |
+
""")
|
997 |
+
|
998 |
+
# Executar instruções se chamado diretamente
|
999 |
+
if __name__ == "__main__":
|
1000 |
+
mostrar_instrucoes()
|