Spaces:
Running
Running
Update ai_logic.py
Browse files- ai_logic.py +251 -87
ai_logic.py
CHANGED
@@ -29,7 +29,6 @@ headers = {
|
|
29 |
|
30 |
# Modelos fixos que você quer manter
|
31 |
NEW_MODELS_TO_TEST = [
|
32 |
-
|
33 |
("GPT-2", "gpt2"),
|
34 |
("DistilGPT-2", "distilgpt2"),
|
35 |
("GPT-2 Medium", "gpt2-medium"),
|
@@ -52,44 +51,61 @@ NEW_MODELS_TO_TEST = [
|
|
52 |
("Facebook","facebook/bart-large-cnn")
|
53 |
]
|
54 |
|
55 |
-
# --- Consulta a API da Hugging Face ---
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
"full": "true"
|
62 |
-
}
|
63 |
-
|
64 |
-
response = requests.get(url, headers=headers, params=params)
|
65 |
-
|
66 |
-
if response.status_code != 200:
|
67 |
-
raise Exception(f"Erro na API: {response.status_code} - {response.text}")
|
68 |
-
|
69 |
-
models_data = response.json()
|
70 |
-
|
71 |
-
# --- Filtra modelos que possuem base_model ---
|
72 |
-
for model in models_data:
|
73 |
-
tags = model.get("tags", [])
|
74 |
-
base_model_tags = [tag for tag in tags if tag.startswith("base_model:")]
|
75 |
-
if base_model_tags:
|
76 |
-
model_name = model.get("id")
|
77 |
-
display_name = model_name.split("/")[-1]
|
78 |
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
-
# --- Resultado ---
|
84 |
print("Lista atualizada de modelos:\n")
|
85 |
-
for name, model_id in NEW_MODELS_TO_TEST:
|
86 |
print(f'("{name}", "{model_id}"),')
|
87 |
|
88 |
print(f"\nTotal de modelos na lista: {len(NEW_MODELS_TO_TEST)}")
|
89 |
|
90 |
-
# Nota: Alguns modelos podem requerer aprovação ou ter restrições de acesso
|
91 |
-
# Recomenda-se testar cada modelo individualmente para verificar disponibilidade
|
92 |
-
|
93 |
DEFAULT_MODEL = "Zephyr 7B (Meio Termo)"
|
94 |
|
95 |
# --- Gerenciamento de Sessão ---
|
@@ -200,7 +216,7 @@ def get_all_blog_links(url: str) -> set:
|
|
200 |
if current_url in visited:
|
201 |
continue
|
202 |
try:
|
203 |
-
response = requests.get(current_url, timeout=
|
204 |
soup = BeautifulSoup(response.content, 'html.parser')
|
205 |
visited.add(current_url)
|
206 |
for link in soup.find_all('a', href=True):
|
@@ -214,7 +230,7 @@ def get_all_blog_links(url: str) -> set:
|
|
214 |
def scrape_text_from_url(url: str) -> str:
|
215 |
"""Extrai texto de uma URL."""
|
216 |
try:
|
217 |
-
response = requests.get(url, timeout=
|
218 |
soup = BeautifulSoup(response.content, 'html.parser')
|
219 |
content = soup.find('article') or soup.find('main')
|
220 |
return content.get_text(separator='\n', strip=True) if content else ""
|
@@ -264,66 +280,123 @@ def retrieve_context_from_blog(query: str, k: int = 4) -> str:
|
|
264 |
class HuggingFaceAPIClient:
|
265 |
def __init__(self, token: str):
|
266 |
self.headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
|
|
|
|
267 |
|
268 |
def check_model_info(self, model_name: str) -> Tuple[bool, str]:
|
269 |
"""Verifica informações do modelo via API do Hugging Face."""
|
270 |
-
url = f"
|
271 |
try:
|
272 |
-
response = requests.get(url, headers=self.headers, timeout=
|
273 |
if response.status_code == 200:
|
274 |
model_info = response.json()
|
|
|
|
|
275 |
if model_info.get('disabled', False):
|
276 |
return False, "Modelo desabilitado"
|
|
|
|
|
277 |
if model_info.get('gated', False):
|
278 |
return False, "Modelo requer aprovação/aceite de licença"
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
return True, "Modelo disponível"
|
|
|
280 |
elif response.status_code == 404:
|
281 |
return False, "Modelo não encontrado"
|
|
|
|
|
|
|
|
|
282 |
else:
|
283 |
return False, f"Erro HTTP {response.status_code}"
|
|
|
|
|
|
|
284 |
except requests.exceptions.RequestException as e:
|
285 |
return False, f"Erro na requisição: {str(e)}"
|
286 |
|
287 |
def test_model_inference(self, model_name: str) -> Tuple[bool, str]:
|
288 |
"""Testa se o modelo está disponível para inferência."""
|
289 |
-
url = f"
|
|
|
|
|
290 |
test_payload = {
|
291 |
-
"inputs": "
|
292 |
"parameters": {
|
293 |
-
"max_new_tokens":
|
294 |
"temperature": 0.1,
|
|
|
295 |
"return_full_text": False
|
296 |
}
|
297 |
}
|
298 |
|
299 |
try:
|
300 |
-
response = requests.post(url, headers=self.headers, json=test_payload, timeout=
|
301 |
|
302 |
if response.status_code == 200:
|
303 |
result = response.json()
|
|
|
|
|
304 |
if isinstance(result, list) and len(result) > 0:
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
|
311 |
elif response.status_code == 503:
|
312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
elif response.status_code == 400:
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
318 |
elif response.status_code == 401:
|
319 |
return False, "Token inválido ou sem permissão"
|
|
|
|
|
320 |
elif response.status_code == 404:
|
321 |
-
return False, "
|
|
|
|
|
322 |
else:
|
323 |
-
|
|
|
|
|
|
|
|
|
|
|
324 |
|
325 |
except requests.exceptions.Timeout:
|
326 |
-
return False, "Timeout na requisição"
|
327 |
except requests.exceptions.RequestException as e:
|
328 |
return False, f"Erro na requisição: {str(e)}"
|
329 |
|
@@ -331,12 +404,15 @@ class HuggingFaceAPIClient:
|
|
331 |
"""Testa se um modelo está disponível, combinando verificação de info e inferência."""
|
332 |
print(f"Testando modelo: {model_name}")
|
333 |
|
|
|
334 |
info_available, info_msg = self.check_model_info(model_name)
|
335 |
if not info_available:
|
|
|
336 |
return False, f"Info check failed: {info_msg}"
|
337 |
|
338 |
print(f" ✓ Info check: {info_msg}")
|
339 |
|
|
|
340 |
inference_available, inference_msg = self.test_model_inference(model_name)
|
341 |
|
342 |
if inference_available:
|
@@ -347,53 +423,128 @@ class HuggingFaceAPIClient:
|
|
347 |
return False, f"Não disponível para inferência: {inference_msg}"
|
348 |
|
349 |
def query_model(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
350 |
-
"""Faz requisição ao modelo usando text-generation."""
|
351 |
prompt = self._convert_messages_to_prompt(messages)
|
352 |
|
353 |
-
url = f"
|
|
|
|
|
354 |
payload = {
|
355 |
"inputs": prompt,
|
356 |
"parameters": {
|
357 |
-
"max_new_tokens": max_tokens,
|
358 |
"temperature": 0.7,
|
|
|
359 |
"do_sample": True,
|
360 |
-
"return_full_text": False
|
|
|
|
|
|
|
|
|
|
|
|
|
361 |
}
|
362 |
}
|
363 |
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
else:
|
374 |
-
return f"Formato de resposta inesperado: {result}"
|
375 |
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
380 |
|
381 |
def _convert_messages_to_prompt(self, messages: List[Dict]) -> str:
|
382 |
-
"""Converte mensagens do formato chat para prompt
|
383 |
prompt_parts = []
|
|
|
384 |
for msg in messages:
|
385 |
role = msg['role']
|
386 |
content = msg['content']
|
387 |
|
388 |
if role == 'system':
|
389 |
-
prompt_parts.append(f"Sistema
|
390 |
elif role == 'user':
|
391 |
-
prompt_parts.append(f"Usuário
|
392 |
elif role == 'assistant':
|
393 |
-
prompt_parts.append(f"Assistente
|
394 |
-
|
395 |
-
|
396 |
-
|
|
|
|
|
397 |
|
398 |
api_client = HuggingFaceAPIClient(HF_TOKEN)
|
399 |
|
@@ -404,7 +555,7 @@ def test_and_update_models() -> int:
|
|
404 |
Garante que o DEFAULT_MODEL seja sempre o primeiro da lista.
|
405 |
Retorna o número de modelos disponíveis.
|
406 |
"""
|
407 |
-
print("Testando disponibilidade dos
|
408 |
print(f"Token HF disponível: {'Sim' if HF_TOKEN else 'Não'}")
|
409 |
print("-" * 60)
|
410 |
|
@@ -412,7 +563,9 @@ def test_and_update_models() -> int:
|
|
412 |
temp_models = {}
|
413 |
|
414 |
# Primeiro verifica o modelo padrão
|
415 |
-
default_label
|
|
|
|
|
416 |
is_available, message = api_client.test_model_availability(default_name)
|
417 |
|
418 |
if is_available:
|
@@ -421,11 +574,17 @@ def test_and_update_models() -> int:
|
|
421 |
else:
|
422 |
print(f"✗ {default_label} - {message} (MODELO PADRÃO INDISPONÍVEL)")
|
423 |
|
424 |
-
# Depois verifica os outros modelos
|
|
|
|
|
|
|
425 |
for model_label, model_name in NEW_MODELS_TO_TEST:
|
426 |
# Pula o modelo padrão se já foi testado
|
427 |
if model_label == default_label and model_name == default_name:
|
428 |
continue
|
|
|
|
|
|
|
429 |
|
430 |
is_available, message = api_client.test_model_availability(model_name)
|
431 |
|
@@ -435,7 +594,8 @@ def test_and_update_models() -> int:
|
|
435 |
else:
|
436 |
print(f"✗ {model_label} - {message}")
|
437 |
|
438 |
-
|
|
|
439 |
|
440 |
# Atualiza MODELS garantindo que o padrão seja o primeiro
|
441 |
global MODELS
|
@@ -509,7 +669,11 @@ def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFAULT_MO
|
|
509 |
{"role": "user", "content": mensagem_usuario}
|
510 |
]
|
511 |
|
512 |
-
|
|
|
|
|
|
|
|
|
513 |
resposta = api_client.query_model(model_name, messages)
|
514 |
add_to_memory(session_id, pergunta, resposta)
|
515 |
return resposta
|
@@ -517,9 +681,9 @@ def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFAULT_MO
|
|
517 |
# --- Inicialização ---
|
518 |
def inicializar_sistema():
|
519 |
"""
|
520 |
-
Inicializa o sistema, garantindo no mínimo
|
521 |
Retorna uma tupla: (status: bool, models: dict)
|
522 |
-
- status: True se >=
|
523 |
- models: Dicionário com os modelos disponíveis
|
524 |
"""
|
525 |
print("Inicializando Chatbot Dr. Aldo...")
|
@@ -531,7 +695,7 @@ def inicializar_sistema():
|
|
531 |
print("Sistema inicializado e pronto para uso com modelos suficientes!")
|
532 |
return True, MODELS
|
533 |
else:
|
534 |
-
print(f"Erro: Apenas {num_available_models} modelos disponíveis.
|
535 |
return False, MODELS
|
536 |
|
537 |
if __name__ == "__main__":
|
@@ -549,4 +713,4 @@ if __name__ == "__main__":
|
|
549 |
else:
|
550 |
print("\nSistema não pôde ser iniciado devido à falta de modelos suficientes.")
|
551 |
print(f"Modelos disponíveis: {', '.join(models.keys()) if models else 'Nenhum'}")
|
552 |
-
print("Por favor, verifique a conexão com o Hugging Face e o token de acesso.")
|
|
|
29 |
|
30 |
# Modelos fixos que você quer manter
|
31 |
NEW_MODELS_TO_TEST = [
|
|
|
32 |
("GPT-2", "gpt2"),
|
33 |
("DistilGPT-2", "distilgpt2"),
|
34 |
("GPT-2 Medium", "gpt2-medium"),
|
|
|
51 |
("Facebook","facebook/bart-large-cnn")
|
52 |
]
|
53 |
|
54 |
+
# --- Consulta a API da Hugging Face com tratamento de erro melhorado ---
|
55 |
+
def fetch_models_from_api():
|
56 |
+
"""Busca modelos adicionais da API do Hugging Face com tratamento robusto."""
|
57 |
+
url = "https://huggingface.co/api/models"
|
58 |
+
params = {
|
59 |
+
"filter": "text-generation",
|
60 |
+
"sort": "downloads",
|
61 |
+
"direction": -1,
|
62 |
+
"limit": 20,
|
63 |
+
"full": True
|
64 |
+
}
|
65 |
|
66 |
+
try:
|
67 |
+
response = requests.get(url, headers=headers, params=params, timeout=30)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
+
if response.status_code == 200:
|
70 |
+
models_data = response.json()
|
71 |
+
|
72 |
+
# Filtra modelos que possuem base_model
|
73 |
+
for model in models_data:
|
74 |
+
try:
|
75 |
+
tags = model.get("tags", [])
|
76 |
+
model_name = model.get("id")
|
77 |
+
|
78 |
+
if not model_name:
|
79 |
+
continue
|
80 |
+
|
81 |
+
# Verifica se o modelo é adequado
|
82 |
+
if any(tag in tags for tag in ["text-generation", "conversational"]):
|
83 |
+
display_name = model_name.split("/")[-1]
|
84 |
+
|
85 |
+
# Verifica se já não está na lista para evitar duplicados
|
86 |
+
if not any(model_name == m[1] for m in NEW_MODELS_TO_TEST):
|
87 |
+
NEW_MODELS_TO_TEST.append((display_name, model_name))
|
88 |
+
|
89 |
+
except Exception as e:
|
90 |
+
print(f"Erro ao processar modelo {model.get('id', 'unknown')}: {e}")
|
91 |
+
continue
|
92 |
+
|
93 |
+
else:
|
94 |
+
print(f"Erro na API do Hugging Face: {response.status_code}")
|
95 |
+
|
96 |
+
except Exception as e:
|
97 |
+
print(f"Erro ao buscar modelos da API: {e}")
|
98 |
+
print("Continuando com a lista de modelos predefinida...")
|
99 |
+
|
100 |
+
# Executa a busca de modelos
|
101 |
+
fetch_models_from_api()
|
102 |
|
|
|
103 |
print("Lista atualizada de modelos:\n")
|
104 |
+
for name, model_id in NEW_MODELS_TO_TEST[:10]: # Mostra apenas os primeiros 10
|
105 |
print(f'("{name}", "{model_id}"),')
|
106 |
|
107 |
print(f"\nTotal de modelos na lista: {len(NEW_MODELS_TO_TEST)}")
|
108 |
|
|
|
|
|
|
|
109 |
DEFAULT_MODEL = "Zephyr 7B (Meio Termo)"
|
110 |
|
111 |
# --- Gerenciamento de Sessão ---
|
|
|
216 |
if current_url in visited:
|
217 |
continue
|
218 |
try:
|
219 |
+
response = requests.get(current_url, timeout=10)
|
220 |
soup = BeautifulSoup(response.content, 'html.parser')
|
221 |
visited.add(current_url)
|
222 |
for link in soup.find_all('a', href=True):
|
|
|
230 |
def scrape_text_from_url(url: str) -> str:
|
231 |
"""Extrai texto de uma URL."""
|
232 |
try:
|
233 |
+
response = requests.get(url, timeout=10)
|
234 |
soup = BeautifulSoup(response.content, 'html.parser')
|
235 |
content = soup.find('article') or soup.find('main')
|
236 |
return content.get_text(separator='\n', strip=True) if content else ""
|
|
|
280 |
class HuggingFaceAPIClient:
|
281 |
def __init__(self, token: str):
|
282 |
self.headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
283 |
+
self.base_api_url = "https://api-inference.huggingface.co/models"
|
284 |
+
self.base_info_url = "https://huggingface.co/api/models"
|
285 |
|
286 |
def check_model_info(self, model_name: str) -> Tuple[bool, str]:
|
287 |
"""Verifica informações do modelo via API do Hugging Face."""
|
288 |
+
url = f"{self.base_info_url}/{model_name}"
|
289 |
try:
|
290 |
+
response = requests.get(url, headers=self.headers, timeout=15)
|
291 |
if response.status_code == 200:
|
292 |
model_info = response.json()
|
293 |
+
|
294 |
+
# Verifica se o modelo está desabilitado
|
295 |
if model_info.get('disabled', False):
|
296 |
return False, "Modelo desabilitado"
|
297 |
+
|
298 |
+
# Verifica se o modelo requer aprovação
|
299 |
if model_info.get('gated', False):
|
300 |
return False, "Modelo requer aprovação/aceite de licença"
|
301 |
+
|
302 |
+
# Verifica se o modelo existe mas não tem pipeline de text-generation
|
303 |
+
pipeline_tag = model_info.get('pipeline_tag')
|
304 |
+
if pipeline_tag and pipeline_tag not in ['text-generation', 'text2text-generation', 'conversational']:
|
305 |
+
return False, f"Modelo não suporta geração de texto (pipeline: {pipeline_tag})"
|
306 |
+
|
307 |
return True, "Modelo disponível"
|
308 |
+
|
309 |
elif response.status_code == 404:
|
310 |
return False, "Modelo não encontrado"
|
311 |
+
elif response.status_code == 401:
|
312 |
+
return False, "Token inválido ou sem permissão"
|
313 |
+
elif response.status_code == 403:
|
314 |
+
return False, "Acesso negado ao modelo"
|
315 |
else:
|
316 |
return False, f"Erro HTTP {response.status_code}"
|
317 |
+
|
318 |
+
except requests.exceptions.Timeout:
|
319 |
+
return False, "Timeout na verificação do modelo"
|
320 |
except requests.exceptions.RequestException as e:
|
321 |
return False, f"Erro na requisição: {str(e)}"
|
322 |
|
323 |
def test_model_inference(self, model_name: str) -> Tuple[bool, str]:
|
324 |
"""Testa se o modelo está disponível para inferência."""
|
325 |
+
url = f"{self.base_api_url}/{model_name}"
|
326 |
+
|
327 |
+
# Payload simplificado para teste
|
328 |
test_payload = {
|
329 |
+
"inputs": "Hello",
|
330 |
"parameters": {
|
331 |
+
"max_new_tokens": 5,
|
332 |
"temperature": 0.1,
|
333 |
+
"do_sample": False,
|
334 |
"return_full_text": False
|
335 |
}
|
336 |
}
|
337 |
|
338 |
try:
|
339 |
+
response = requests.post(url, headers=self.headers, json=test_payload, timeout=60)
|
340 |
|
341 |
if response.status_code == 200:
|
342 |
result = response.json()
|
343 |
+
|
344 |
+
# Verifica diferentes formatos de resposta
|
345 |
if isinstance(result, list) and len(result) > 0:
|
346 |
+
if 'generated_text' in result[0] or 'translation_text' in result[0] or 'summary_text' in result[0]:
|
347 |
+
return True, "Modelo disponível para inferência"
|
348 |
+
|
349 |
+
elif isinstance(result, dict):
|
350 |
+
if 'generated_text' in result or 'error' not in result:
|
351 |
+
return True, "Modelo disponível para inferência"
|
352 |
+
elif 'error' in result:
|
353 |
+
error_msg = result['error']
|
354 |
+
if 'loading' in error_msg.lower() or 'currently loading' in error_msg.lower():
|
355 |
+
return False, "Modelo está carregando"
|
356 |
+
return False, f"Erro do modelo: {error_msg}"
|
357 |
+
|
358 |
+
return False, f"Formato de resposta inesperado: {str(result)[:200]}"
|
359 |
|
360 |
elif response.status_code == 503:
|
361 |
+
try:
|
362 |
+
error_data = response.json()
|
363 |
+
error_msg = error_data.get('error', 'Serviço indisponível')
|
364 |
+
if 'loading' in error_msg.lower():
|
365 |
+
return False, "Modelo está carregando"
|
366 |
+
return False, f"Serviço indisponível: {error_msg}"
|
367 |
+
except:
|
368 |
+
return False, "Modelo está carregando (503)"
|
369 |
+
|
370 |
elif response.status_code == 400:
|
371 |
+
try:
|
372 |
+
error_data = response.json()
|
373 |
+
error_msg = error_data.get('error', 'Erro 400')
|
374 |
+
if 'loading' in error_msg.lower():
|
375 |
+
return False, "Modelo está carregando"
|
376 |
+
elif 'not supported' in error_msg.lower():
|
377 |
+
return False, "Tipo de requisição não suportado"
|
378 |
+
return False, f"Erro 400: {error_msg}"
|
379 |
+
except:
|
380 |
+
return False, "Erro de requisição malformada"
|
381 |
+
|
382 |
elif response.status_code == 401:
|
383 |
return False, "Token inválido ou sem permissão"
|
384 |
+
elif response.status_code == 403:
|
385 |
+
return False, "Acesso negado ao modelo"
|
386 |
elif response.status_code == 404:
|
387 |
+
return False, "Endpoint do modelo não encontrado"
|
388 |
+
elif response.status_code == 429:
|
389 |
+
return False, "Limite de requisições excedido"
|
390 |
else:
|
391 |
+
try:
|
392 |
+
error_data = response.json()
|
393 |
+
error_msg = error_data.get('error', response.text)
|
394 |
+
return False, f"Erro HTTP {response.status_code}: {error_msg}"
|
395 |
+
except:
|
396 |
+
return False, f"Erro HTTP {response.status_code}: {response.text[:200]}"
|
397 |
|
398 |
except requests.exceptions.Timeout:
|
399 |
+
return False, "Timeout na requisição de inferência"
|
400 |
except requests.exceptions.RequestException as e:
|
401 |
return False, f"Erro na requisição: {str(e)}"
|
402 |
|
|
|
404 |
"""Testa se um modelo está disponível, combinando verificação de info e inferência."""
|
405 |
print(f"Testando modelo: {model_name}")
|
406 |
|
407 |
+
# Primeiro verifica as informações do modelo
|
408 |
info_available, info_msg = self.check_model_info(model_name)
|
409 |
if not info_available:
|
410 |
+
print(f" ✗ Info check: {info_msg}")
|
411 |
return False, f"Info check failed: {info_msg}"
|
412 |
|
413 |
print(f" ✓ Info check: {info_msg}")
|
414 |
|
415 |
+
# Em seguida testa a inferência
|
416 |
inference_available, inference_msg = self.test_model_inference(model_name)
|
417 |
|
418 |
if inference_available:
|
|
|
423 |
return False, f"Não disponível para inferência: {inference_msg}"
|
424 |
|
425 |
def query_model(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
426 |
+
"""Faz requisição ao modelo usando text-generation com retry e fallback."""
|
427 |
prompt = self._convert_messages_to_prompt(messages)
|
428 |
|
429 |
+
url = f"{self.base_api_url}/{model_name}"
|
430 |
+
|
431 |
+
# Configurações otimizadas para diferentes tipos de modelo
|
432 |
payload = {
|
433 |
"inputs": prompt,
|
434 |
"parameters": {
|
435 |
+
"max_new_tokens": min(max_tokens, 500), # Limita para evitar timeouts
|
436 |
"temperature": 0.7,
|
437 |
+
"top_p": 0.9,
|
438 |
"do_sample": True,
|
439 |
+
"return_full_text": False,
|
440 |
+
"repetition_penalty": 1.1,
|
441 |
+
"pad_token_id": 50256 # Token padrão para muitos modelos
|
442 |
+
},
|
443 |
+
"options": {
|
444 |
+
"wait_for_model": True,
|
445 |
+
"use_cache": False
|
446 |
}
|
447 |
}
|
448 |
|
449 |
+
max_retries = 2
|
450 |
+
for attempt in range(max_retries):
|
451 |
+
try:
|
452 |
+
response = requests.post(
|
453 |
+
url,
|
454 |
+
headers=self.headers,
|
455 |
+
json=payload,
|
456 |
+
timeout=120 # Timeout aumentado
|
457 |
+
)
|
|
|
|
|
458 |
|
459 |
+
if response.status_code == 200:
|
460 |
+
result = response.json()
|
461 |
+
|
462 |
+
# Trata diferentes formatos de resposta
|
463 |
+
if isinstance(result, list) and len(result) > 0:
|
464 |
+
generated_text = result[0].get('generated_text', '').strip()
|
465 |
+
if generated_text:
|
466 |
+
return generated_text
|
467 |
+
return "Resposta vazia do modelo"
|
468 |
+
|
469 |
+
elif isinstance(result, dict):
|
470 |
+
if 'generated_text' in result:
|
471 |
+
return result['generated_text'].strip()
|
472 |
+
elif 'error' in result:
|
473 |
+
return f"Erro do modelo: {result['error']}"
|
474 |
+
else:
|
475 |
+
return f"Formato inesperado: {str(result)[:300]}"
|
476 |
+
|
477 |
+
return f"Formato de resposta não reconhecido: {str(result)[:300]}"
|
478 |
+
|
479 |
+
elif response.status_code == 503:
|
480 |
+
if attempt < max_retries - 1:
|
481 |
+
wait_time = 5 * (attempt + 1)
|
482 |
+
print(f"Modelo carregando, aguardando {wait_time}s...")
|
483 |
+
time.sleep(wait_time)
|
484 |
+
continue
|
485 |
+
return "Modelo ainda está carregando após várias tentativas"
|
486 |
+
|
487 |
+
elif response.status_code == 400:
|
488 |
+
try:
|
489 |
+
error_data = response.json()
|
490 |
+
error_msg = error_data.get('error', 'Erro 400')
|
491 |
+
return f"Erro na requisição: {error_msg}"
|
492 |
+
except:
|
493 |
+
return "Erro na formatação da requisição"
|
494 |
+
|
495 |
+
elif response.status_code == 401:
|
496 |
+
return "Token de autenticação inválido ou expirado"
|
497 |
+
|
498 |
+
elif response.status_code == 403:
|
499 |
+
return "Acesso negado ao modelo (pode requerer aprovação)"
|
500 |
+
|
501 |
+
elif response.status_code == 429:
|
502 |
+
if attempt < max_retries - 1:
|
503 |
+
wait_time = 10 * (attempt + 1)
|
504 |
+
print(f"Rate limit atingido, aguardando {wait_time}s...")
|
505 |
+
time.sleep(wait_time)
|
506 |
+
continue
|
507 |
+
return "Limite de requisições excedido"
|
508 |
+
|
509 |
+
else:
|
510 |
+
try:
|
511 |
+
error_data = response.json()
|
512 |
+
error_msg = error_data.get('error', response.text)
|
513 |
+
return f"Erro HTTP {response.status_code}: {error_msg}"
|
514 |
+
except:
|
515 |
+
return f"Erro HTTP {response.status_code}: {response.text[:200]}"
|
516 |
+
|
517 |
+
except requests.exceptions.Timeout:
|
518 |
+
if attempt < max_retries - 1:
|
519 |
+
print(f"Timeout na tentativa {attempt + 1}, tentando novamente...")
|
520 |
+
time.sleep(5)
|
521 |
+
continue
|
522 |
+
return "Timeout: O modelo demorou muito para responder"
|
523 |
+
|
524 |
+
except requests.exceptions.RequestException as e:
|
525 |
+
return f"Erro na requisição: {str(e)}"
|
526 |
+
|
527 |
+
return "Falha após múltiplas tentativas"
|
528 |
|
529 |
def _convert_messages_to_prompt(self, messages: List[Dict]) -> str:
|
530 |
+
"""Converte mensagens do formato chat para prompt otimizado."""
|
531 |
prompt_parts = []
|
532 |
+
|
533 |
for msg in messages:
|
534 |
role = msg['role']
|
535 |
content = msg['content']
|
536 |
|
537 |
if role == 'system':
|
538 |
+
prompt_parts.append(f"### Sistema:\n{content}\n")
|
539 |
elif role == 'user':
|
540 |
+
prompt_parts.append(f"### Usuário:\n{content}\n")
|
541 |
elif role == 'assistant':
|
542 |
+
prompt_parts.append(f"### Assistente:\n{content}\n")
|
543 |
+
|
544 |
+
# Adiciona prompt final para gerar resposta
|
545 |
+
prompt_parts.append("### Assistente:\n")
|
546 |
+
|
547 |
+
return "\n".join(prompt_parts)
|
548 |
|
549 |
api_client = HuggingFaceAPIClient(HF_TOKEN)
|
550 |
|
|
|
555 |
Garante que o DEFAULT_MODEL seja sempre o primeiro da lista.
|
556 |
Retorna o número de modelos disponíveis.
|
557 |
"""
|
558 |
+
print("Testando disponibilidade dos modelos...")
|
559 |
print(f"Token HF disponível: {'Sim' if HF_TOKEN else 'Não'}")
|
560 |
print("-" * 60)
|
561 |
|
|
|
563 |
temp_models = {}
|
564 |
|
565 |
# Primeiro verifica o modelo padrão
|
566 |
+
default_label = "Mistral 7B"
|
567 |
+
default_name = "mistralai/Mistral-7B-Instruct-v0.3"
|
568 |
+
|
569 |
is_available, message = api_client.test_model_availability(default_name)
|
570 |
|
571 |
if is_available:
|
|
|
574 |
else:
|
575 |
print(f"✗ {default_label} - {message} (MODELO PADRÃO INDISPONÍVEL)")
|
576 |
|
577 |
+
# Depois verifica os outros modelos (limitando a quantidade para evitar rate limiting)
|
578 |
+
tested_count = 0
|
579 |
+
max_tests = 15 # Limita o número de testes
|
580 |
+
|
581 |
for model_label, model_name in NEW_MODELS_TO_TEST:
|
582 |
# Pula o modelo padrão se já foi testado
|
583 |
if model_label == default_label and model_name == default_name:
|
584 |
continue
|
585 |
+
|
586 |
+
if tested_count >= max_tests:
|
587 |
+
break
|
588 |
|
589 |
is_available, message = api_client.test_model_availability(model_name)
|
590 |
|
|
|
594 |
else:
|
595 |
print(f"✗ {model_label} - {message}")
|
596 |
|
597 |
+
tested_count += 1
|
598 |
+
time.sleep(2) # Pausa entre testes para evitar rate limiting
|
599 |
|
600 |
# Atualiza MODELS garantindo que o padrão seja o primeiro
|
601 |
global MODELS
|
|
|
669 |
{"role": "user", "content": mensagem_usuario}
|
670 |
]
|
671 |
|
672 |
+
# Verifica se o modelo existe na lista, senão usa o padrão
|
673 |
+
if modelo not in MODELS:
|
674 |
+
modelo = next(iter(MODELS)) if MODELS else DEFAULT_MODEL
|
675 |
+
|
676 |
+
model_name = MODELS.get(modelo, list(MODELS.values())[0] if MODELS else "mistralai/Mistral-7B-Instruct-v0.3")
|
677 |
resposta = api_client.query_model(model_name, messages)
|
678 |
add_to_memory(session_id, pergunta, resposta)
|
679 |
return resposta
|
|
|
681 |
# --- Inicialização ---
|
682 |
def inicializar_sistema():
|
683 |
"""
|
684 |
+
Inicializa o sistema, garantindo no mínimo 1 modelo disponível.
|
685 |
Retorna uma tupla: (status: bool, models: dict)
|
686 |
+
- status: True se >= 1 modelo disponível, False caso contrário
|
687 |
- models: Dicionário com os modelos disponíveis
|
688 |
"""
|
689 |
print("Inicializando Chatbot Dr. Aldo...")
|
|
|
695 |
print("Sistema inicializado e pronto para uso com modelos suficientes!")
|
696 |
return True, MODELS
|
697 |
else:
|
698 |
+
print(f"Erro: Apenas {num_available_models} modelos disponíveis. É necessário pelo menos 1 modelo para iniciar o sistema.")
|
699 |
return False, MODELS
|
700 |
|
701 |
if __name__ == "__main__":
|
|
|
713 |
else:
|
714 |
print("\nSistema não pôde ser iniciado devido à falta de modelos suficientes.")
|
715 |
print(f"Modelos disponíveis: {', '.join(models.keys()) if models else 'Nenhum'}")
|
716 |
+
print("Por favor, verifique a conexão com o Hugging Face e o token de acesso.")
|