Spaces:
Running
Running
Update ai_logic.py
Browse files- ai_logic.py +306 -673
ai_logic.py
CHANGED
@@ -3,459 +3,83 @@ import os
|
|
3 |
import json
|
4 |
import time
|
5 |
import pickle
|
6 |
-
from typing import Dict, List, Optional, Tuple
|
7 |
from bs4 import BeautifulSoup
|
8 |
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 |
-
import random
|
13 |
-
import asyncio
|
14 |
-
import aiohttp
|
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 |
-
|
22 |
-
|
23 |
-
# --- Provedores de LLM Gratuitos ---
|
24 |
-
class LLMProvider:
|
25 |
-
"""Classe base para provedores de LLM """
|
26 |
-
def __init__(self, name: str, base_url: str = None):
|
27 |
-
self.name = name
|
28 |
-
self.base_url = base_url
|
29 |
-
self.is_available = False
|
30 |
-
self.models = []
|
31 |
-
|
32 |
-
async def test_availability(self) -> bool:
|
33 |
-
"""Testa se o provedor está disponível"""
|
34 |
-
return False
|
35 |
-
|
36 |
-
async def query_model(self, messages: List[Dict], model_name: str = None, **kwargs) -> str:
|
37 |
-
"""Faz query no modelo"""
|
38 |
-
return ""
|
39 |
-
|
40 |
-
def convert_messages_to_prompt(self, messages: List[Dict]) -> str:
|
41 |
-
"""Converte mensagens para formato de prompt"""
|
42 |
-
prompt_parts = []
|
43 |
-
for msg in messages:
|
44 |
-
role = msg['role']
|
45 |
-
content = msg['content']
|
46 |
-
|
47 |
-
if role == 'system':
|
48 |
-
prompt_parts.append(f"Sistema: {content}")
|
49 |
-
elif role == 'user':
|
50 |
-
prompt_parts.append(f"Usuário: {content}")
|
51 |
-
elif role == 'assistant':
|
52 |
-
prompt_parts.append(f"Assistente: {content}")
|
53 |
-
|
54 |
-
prompt_parts.append("Assistente:")
|
55 |
-
return "\n\n".join(prompt_parts)
|
56 |
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
url = f"{self.base_url}/models/{model_name}"
|
102 |
-
|
103 |
-
payload = {
|
104 |
-
"inputs": prompt,
|
105 |
-
"parameters": {
|
106 |
-
"max_new_tokens": kwargs.get("max_tokens", 1000),
|
107 |
-
"temperature": kwargs.get("temperature", 0.7),
|
108 |
-
"do_sample": True,
|
109 |
-
"return_full_text": False
|
110 |
-
}
|
111 |
-
}
|
112 |
-
|
113 |
-
try:
|
114 |
-
async with aiohttp.ClientSession() as session:
|
115 |
-
async with session.post(
|
116 |
-
url,
|
117 |
-
headers=self.headers,
|
118 |
-
json=payload,
|
119 |
-
timeout=aiohttp.ClientTimeout(total=30)
|
120 |
-
) as response:
|
121 |
-
if response.status == 200:
|
122 |
-
result = await response.json()
|
123 |
-
if isinstance(result, list) and len(result) > 0:
|
124 |
-
return result[0].get('generated_text', '').strip()
|
125 |
-
elif isinstance(result, dict) and 'generated_text' in result:
|
126 |
-
return result['generated_text'].strip()
|
127 |
-
return f"Erro HF: {response.status}"
|
128 |
-
except Exception as e:
|
129 |
-
return f"Erro na requisição HF: {str(e)}"
|
130 |
-
|
131 |
-
class OllamaProvider(LLMProvider):
|
132 |
-
"""Provedor Ollama (local)"""
|
133 |
-
def __init__(self, host: str = "http://localhost:11434"):
|
134 |
-
super().__init__("Ollama", host)
|
135 |
-
self.models = [
|
136 |
-
("Llama 3.2", "llama3.2"),
|
137 |
-
("Phi 3", "phi3"),
|
138 |
-
("Gemma 2", "gemma2"),
|
139 |
-
("Mistral", "mistral"),
|
140 |
-
("CodeLlama", "codellama")
|
141 |
-
]
|
142 |
-
|
143 |
-
async def test_availability(self) -> bool:
|
144 |
-
try:
|
145 |
-
async with aiohttp.ClientSession() as session:
|
146 |
-
async with session.get(
|
147 |
-
f"{self.base_url}/api/tags",
|
148 |
-
timeout=aiohttp.ClientTimeout(total=5)
|
149 |
-
) as response:
|
150 |
-
self.is_available = response.status == 200
|
151 |
-
return self.is_available
|
152 |
-
except:
|
153 |
-
return False
|
154 |
-
|
155 |
-
async def query_model(self, messages: List[Dict], model_name: str = "llama3.2", **kwargs) -> str:
|
156 |
-
prompt = self.convert_messages_to_prompt(messages)
|
157 |
-
|
158 |
-
payload = {
|
159 |
-
"model": model_name,
|
160 |
-
"prompt": prompt,
|
161 |
-
"stream": False,
|
162 |
-
"options": {
|
163 |
-
"num_predict": kwargs.get("max_tokens", 1000),
|
164 |
-
"temperature": kwargs.get("temperature", 0.7)
|
165 |
-
}
|
166 |
-
}
|
167 |
-
|
168 |
-
try:
|
169 |
-
async with aiohttp.ClientSession() as session:
|
170 |
-
async with session.post(
|
171 |
-
f"{self.base_url}/api/generate",
|
172 |
-
json=payload,
|
173 |
-
timeout=aiohttp.ClientTimeout(total=60)
|
174 |
-
) as response:
|
175 |
-
if response.status == 200:
|
176 |
-
result = await response.json()
|
177 |
-
return result.get('response', '').strip()
|
178 |
-
return f"Erro Ollama: {response.status}"
|
179 |
-
except Exception as e:
|
180 |
-
return f"Erro na requisição Ollama: {str(e)}"
|
181 |
-
|
182 |
-
class OpenRouterProvider(LLMProvider):
|
183 |
-
"""Provedor OpenRouter (alguns modelos gratuitos)"""
|
184 |
-
def __init__(self, api_key: str = None):
|
185 |
-
super().__init__("OpenRouter", "https://openrouter.ai/api/v1")
|
186 |
-
self.api_key = api_key or os.getenv("OPENROUTER_API_KEY")
|
187 |
-
self.headers = {
|
188 |
-
"Authorization": f"Bearer {self.api_key}" if self.api_key else "",
|
189 |
-
"Content-Type": "application/json"
|
190 |
-
}
|
191 |
-
self.models = [
|
192 |
-
("Mistral 7B Free", "mistralai/mistral-7b-instruct:free"),
|
193 |
-
("Mythomist 7B", "gryphe/mythomist-7b:free"),
|
194 |
-
("Toppy M 7B", "undi95/toppy-m-7b:free"),
|
195 |
-
("OpenChat 3.5", "openchat/openchat-7b:free")
|
196 |
-
]
|
197 |
-
|
198 |
-
async def test_availability(self) -> bool:
|
199 |
-
if not self.api_key:
|
200 |
-
return False
|
201 |
-
|
202 |
-
try:
|
203 |
-
async with aiohttp.ClientSession() as session:
|
204 |
-
async with session.get(
|
205 |
-
f"{self.base_url}/models",
|
206 |
-
headers=self.headers,
|
207 |
-
timeout=aiohttp.ClientTimeout(total=10)
|
208 |
-
) as response:
|
209 |
-
self.is_available = response.status == 200
|
210 |
-
return self.is_available
|
211 |
-
except:
|
212 |
-
return False
|
213 |
-
|
214 |
-
async def query_model(self, messages: List[Dict], model_name: str = "mistralai/mistral-7b-instruct:free", **kwargs) -> str:
|
215 |
-
payload = {
|
216 |
-
"model": model_name,
|
217 |
-
"messages": messages,
|
218 |
-
"max_tokens": kwargs.get("max_tokens", 1000),
|
219 |
-
"temperature": kwargs.get("temperature", 0.7)
|
220 |
-
}
|
221 |
-
|
222 |
-
try:
|
223 |
-
async with aiohttp.ClientSession() as session:
|
224 |
-
async with session.post(
|
225 |
-
f"{self.base_url}/chat/completions",
|
226 |
-
headers=self.headers,
|
227 |
-
json=payload,
|
228 |
-
timeout=aiohttp.ClientTimeout(total=30)
|
229 |
-
) as response:
|
230 |
-
if response.status == 200:
|
231 |
-
result = await response.json()
|
232 |
-
return result['choices'][0]['message']['content'].strip()
|
233 |
-
return f"Erro OpenRouter: {response.status}"
|
234 |
-
except Exception as e:
|
235 |
-
return f"Erro na requisição OpenRouter: {str(e)}"
|
236 |
-
|
237 |
-
class GroqProvider(LLMProvider):
|
238 |
-
"""Provedor Groq (gratuito com limites)"""
|
239 |
-
def __init__(self, api_key: str = None):
|
240 |
-
super().__init__("Groq", "https://api.groq.com/openai/v1")
|
241 |
-
self.api_key = api_key or os.getenv("GROQ_API_KEY")
|
242 |
-
self.headers = {
|
243 |
-
"Authorization": f"Bearer {self.api_key}" if self.api_key else "",
|
244 |
-
"Content-Type": "application/json"
|
245 |
-
}
|
246 |
-
self.models = [
|
247 |
-
("Llama 3 8B", "llama3-8b-8192"),
|
248 |
-
("Llama 3 70B", "llama3-70b-8192"),
|
249 |
-
("Mixtral 8x7B", "mixtral-8x7b-32768"),
|
250 |
-
("Gemma 7B", "gemma-7b-it")
|
251 |
-
]
|
252 |
-
|
253 |
-
async def test_availability(self) -> bool:
|
254 |
-
if not self.api_key:
|
255 |
-
return False
|
256 |
-
|
257 |
-
try:
|
258 |
-
async with aiohttp.ClientSession() as session:
|
259 |
-
async with session.get(
|
260 |
-
f"{self.base_url}/models",
|
261 |
-
headers=self.headers,
|
262 |
-
timeout=aiohttp.ClientTimeout(total=10)
|
263 |
-
) as response:
|
264 |
-
self.is_available = response.status == 200
|
265 |
-
return self.is_available
|
266 |
-
except:
|
267 |
-
return False
|
268 |
-
|
269 |
-
async def query_model(self, messages: List[Dict], model_name: str = "llama3-8b-8192", **kwargs) -> str:
|
270 |
-
payload = {
|
271 |
-
"model": model_name,
|
272 |
-
"messages": messages,
|
273 |
-
"max_tokens": kwargs.get("max_tokens", 1000),
|
274 |
-
"temperature": kwargs.get("temperature", 0.7)
|
275 |
-
}
|
276 |
-
|
277 |
-
try:
|
278 |
-
async with aiohttp.ClientSession() as session:
|
279 |
-
async with session.post(
|
280 |
-
f"{self.base_url}/chat/completions",
|
281 |
-
headers=self.headers,
|
282 |
-
json=payload,
|
283 |
-
timeout=aiohttp.ClientTimeout(total=30)
|
284 |
-
) as response:
|
285 |
-
if response.status == 200:
|
286 |
-
result = await response.json()
|
287 |
-
return result['choices'][0]['message']['content'].strip()
|
288 |
-
return f"Erro Groq: {response.status}"
|
289 |
-
except Exception as e:
|
290 |
-
return f"Erro na requisição Groq: {str(e)}"
|
291 |
-
|
292 |
-
class TogetherAIProvider(LLMProvider):
|
293 |
-
"""Provedor Together AI (alguns modelos gratuitos)"""
|
294 |
-
def __init__(self, api_key: str = None):
|
295 |
-
super().__init__("Together AI", "https://api.together.xyz/v1")
|
296 |
-
self.api_key = api_key or os.getenv("TOGETHER_API_KEY")
|
297 |
-
self.headers = {
|
298 |
-
"Authorization": f"Bearer {self.api_key}" if self.api_key else "",
|
299 |
-
"Content-Type": "application/json"
|
300 |
-
}
|
301 |
-
self.models = [
|
302 |
-
("Mistral 7B", "mistralai/Mistral-7B-Instruct-v0.1"),
|
303 |
-
("Code Llama 7B", "codellama/CodeLlama-7b-Instruct-hf"),
|
304 |
-
("Llama 2 7B", "meta-llama/Llama-2-7b-chat-hf"),
|
305 |
-
("WizardCoder 15B", "WizardLM/WizardCoder-15B-V1.0")
|
306 |
-
]
|
307 |
-
|
308 |
-
async def test_availability(self) -> bool:
|
309 |
-
if not self.api_key:
|
310 |
-
return False
|
311 |
-
|
312 |
-
try:
|
313 |
-
async with aiohttp.ClientSession() as session:
|
314 |
-
async with session.get(
|
315 |
-
f"{self.base_url}/models",
|
316 |
-
headers=self.headers,
|
317 |
-
timeout=aiohttp.ClientTimeout(total=10)
|
318 |
-
) as response:
|
319 |
-
self.is_available = response.status == 200
|
320 |
-
return self.is_available
|
321 |
-
except:
|
322 |
-
return False
|
323 |
-
|
324 |
-
async def query_model(self, messages: List[Dict], model_name: str = "mistralai/Mistral-7B-Instruct-v0.1", **kwargs) -> str:
|
325 |
-
payload = {
|
326 |
-
"model": model_name,
|
327 |
-
"messages": messages,
|
328 |
-
"max_tokens": kwargs.get("max_tokens", 1000),
|
329 |
-
"temperature": kwargs.get("temperature", 0.7)
|
330 |
-
}
|
331 |
-
|
332 |
-
try:
|
333 |
-
async with aiohttp.ClientSession() as session:
|
334 |
-
async with session.post(
|
335 |
-
f"{self.base_url}/chat/completions",
|
336 |
-
headers=self.headers,
|
337 |
-
json=payload,
|
338 |
-
timeout=aiohttp.ClientTimeout(total=30)
|
339 |
-
) as response:
|
340 |
-
if response.status == 200:
|
341 |
-
result = await response.json()
|
342 |
-
return result['choices'][0]['message']['content'].strip()
|
343 |
-
return f"Erro Together AI: {response.status}"
|
344 |
-
except Exception as e:
|
345 |
-
return f"Erro na requisição Together AI: {str(e)}"
|
346 |
-
|
347 |
-
# --- Gerenciador de Múltiplos Provedores ---
|
348 |
-
class MultiProviderLLMClient:
|
349 |
-
"""Cliente que gerencia múltiplos provedores de LLM"""
|
350 |
-
def __init__(self):
|
351 |
-
self.providers = []
|
352 |
-
self.available_providers = []
|
353 |
-
self.current_provider_index = 0
|
354 |
-
self.models_cache = {}
|
355 |
-
|
356 |
-
# Inicializa provedores
|
357 |
-
if HF_TOKEN:
|
358 |
-
self.providers.append(HuggingFaceProvider(HF_TOKEN))
|
359 |
-
|
360 |
-
# Adiciona outros provedores
|
361 |
-
self.providers.extend([
|
362 |
-
OllamaProvider(),
|
363 |
-
OpenRouterProvider(),
|
364 |
-
GroqProvider(),
|
365 |
-
TogetherAIProvider()
|
366 |
-
])
|
367 |
-
|
368 |
-
async def initialize_providers(self):
|
369 |
-
"""Inicializa e testa todos os provedores"""
|
370 |
-
print("Testando provedores de LLM...")
|
371 |
-
|
372 |
-
tasks = []
|
373 |
-
for provider in self.providers:
|
374 |
-
tasks.append(self._test_provider(provider))
|
375 |
-
|
376 |
-
await asyncio.gather(*tasks)
|
377 |
-
|
378 |
-
# Filtra apenas provedores disponíveis
|
379 |
-
self.available_providers = [p for p in self.providers if p.is_available]
|
380 |
-
|
381 |
-
if not self.available_providers:
|
382 |
-
print("⚠️ Nenhum provedor de LLM disponível!")
|
383 |
-
return False
|
384 |
-
|
385 |
-
print(f"✅ {len(self.available_providers)} provedores disponíveis:")
|
386 |
-
for provider in self.available_providers:
|
387 |
-
print(f" - {provider.name}")
|
388 |
-
|
389 |
-
self._build_models_cache()
|
390 |
-
return True
|
391 |
-
|
392 |
-
async def _test_provider(self, provider: LLMProvider):
|
393 |
-
"""Testa um provedor específico"""
|
394 |
-
try:
|
395 |
-
is_available = await provider.test_availability()
|
396 |
-
if is_available:
|
397 |
-
print(f"✅ {provider.name} - Disponível")
|
398 |
-
else:
|
399 |
-
print(f"❌ {provider.name} - Indisponível")
|
400 |
-
except Exception as e:
|
401 |
-
print(f"❌ {provider.name} - Erro: {e}")
|
402 |
-
|
403 |
-
def _build_models_cache(self):
|
404 |
-
"""Constrói cache de modelos disponíveis"""
|
405 |
-
self.models_cache = {}
|
406 |
-
for provider in self.available_providers:
|
407 |
-
for model_name, model_id in provider.models:
|
408 |
-
full_name = f"{model_name} ({provider.name})"
|
409 |
-
self.models_cache[full_name] = {
|
410 |
-
"provider": provider,
|
411 |
-
"model_id": model_id,
|
412 |
-
"provider_name": provider.name
|
413 |
-
}
|
414 |
-
|
415 |
-
def get_available_models(self) -> Dict[str, str]:
|
416 |
-
"""Retorna modelos disponíveis em formato compatível"""
|
417 |
-
return {name: info["model_id"] for name, info in self.models_cache.items()}
|
418 |
-
|
419 |
-
async def query_model(self, messages: List[Dict], model_name: str = None, **kwargs) -> str:
|
420 |
-
"""Faz query tentando diferentes provedores como fallback"""
|
421 |
-
if not self.available_providers:
|
422 |
-
return "Nenhum provedor de LLM disponível. Por favor, configure ao menos um provedor."
|
423 |
-
|
424 |
-
# Se modelo específico foi solicitado
|
425 |
-
if model_name and model_name in self.models_cache:
|
426 |
-
provider_info = self.models_cache[model_name]
|
427 |
-
try:
|
428 |
-
result = await provider_info["provider"].query_model(
|
429 |
-
messages,
|
430 |
-
provider_info["model_id"],
|
431 |
-
**kwargs
|
432 |
-
)
|
433 |
-
if not result.startswith("Erro"):
|
434 |
-
return result
|
435 |
-
except Exception as e:
|
436 |
-
print(f"Erro com {provider_info['provider_name']}: {e}")
|
437 |
-
|
438 |
-
# Fallback: tenta todos os provedores disponíveis
|
439 |
-
for provider in self.available_providers:
|
440 |
-
try:
|
441 |
-
if provider.models:
|
442 |
-
default_model = provider.models[0][1] # Pega o primeiro modelo
|
443 |
-
result = await provider.query_model(messages, default_model, **kwargs)
|
444 |
-
if not result.startswith("Erro"):
|
445 |
-
return f"{result}\n\n_Resposta gerada por: {provider.name}_"
|
446 |
-
except Exception as e:
|
447 |
-
print(f"Erro com provedor {provider.name}: {e}")
|
448 |
-
continue
|
449 |
|
450 |
-
|
|
|
|
|
451 |
|
452 |
-
# ---
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
|
|
|
|
|
|
|
|
457 |
|
458 |
-
|
|
|
|
|
459 |
user_sessions: Dict[str, Dict[str, List | Dict]] = {}
|
460 |
MAX_MEMORY_LENGTH = 5
|
461 |
|
@@ -551,7 +175,7 @@ def clear_memory(session_id: str) -> str:
|
|
551 |
os.remove(memory_path)
|
552 |
return "Memória limpa com sucesso!"
|
553 |
|
554 |
-
# --- RAG (
|
555 |
vector_store: Optional[FAISS] = None
|
556 |
|
557 |
def get_all_blog_links(url: str) -> set:
|
@@ -623,9 +247,218 @@ def retrieve_context_from_blog(query: str, k: int = 4) -> str:
|
|
623 |
print(f"Erro ao buscar contexto: {e}")
|
624 |
return ""
|
625 |
|
626 |
-
# ---
|
627 |
-
|
628 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
629 |
if not pergunta.strip():
|
630 |
return "Por favor, faça uma pergunta válida."
|
631 |
|
@@ -644,7 +477,6 @@ async def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFA
|
|
644 |
Doutor em Ciências da Computação pela UnB (2024), mestre em Ciências da Computação pela UnB (2017) e bacharel em Sistemas de Informação pela UFV (2014).
|
645 |
Professor universitário, onde leciona disciplinas como Algoritmos, Inteligência Artificial, Ciência de Dados e Mineração de Dados.
|
646 |
Atua como analista de sistemas nível 4.
|
647 |
-
|
648 |
Regras de conduta:
|
649 |
- Sempre coloque uma piada ou trocadilho no final da resposta.
|
650 |
- Responda em português, de forma clara, amigável e educativa.
|
@@ -664,243 +496,44 @@ async def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFA
|
|
664 |
{"role": "user", "content": mensagem_usuario}
|
665 |
]
|
666 |
|
667 |
-
|
668 |
-
resposta =
|
669 |
add_to_memory(session_id, pergunta, resposta)
|
670 |
return resposta
|
671 |
|
672 |
-
# ---
|
673 |
-
|
674 |
"""
|
675 |
-
Inicializa o sistema
|
676 |
Retorna uma tupla: (status: bool, models: dict)
|
|
|
|
|
677 |
"""
|
678 |
-
print("Inicializando
|
679 |
-
print("=" * 60)
|
680 |
-
|
681 |
-
# Inicializa os provedores
|
682 |
-
success = await multi_client.initialize_providers()
|
683 |
-
|
684 |
-
if not success:
|
685 |
-
print("❌ Nenhum provedor de LLM disponível!")
|
686 |
-
return False, {}
|
687 |
|
688 |
-
|
689 |
-
global MODELS, DEFAULT_MODEL
|
690 |
-
MODELS = multi_client.get_available_models()
|
691 |
|
692 |
-
if
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
print("=" * 60)
|
701 |
-
print("✅ Sistema inicializado com sucesso!")
|
702 |
-
print(f"📊 Total de modelos disponíveis: {len(MODELS)}")
|
703 |
-
print(f"🔧 Provedores ativos: {len(multi_client.available_providers)}")
|
704 |
-
|
705 |
-
return True, MODELS
|
706 |
-
|
707 |
-
# --- Função de Teste de Modelos ---
|
708 |
-
async def testar_modelos_disponiveis():
|
709 |
-
"""Testa todos os modelos disponíveis"""
|
710 |
-
print("\n" + "=" * 60)
|
711 |
-
print("TESTANDO MODELOS DISPONÍVEIS")
|
712 |
-
print("=" * 60)
|
713 |
-
|
714 |
-
test_message = [
|
715 |
-
{"role": "system", "content": "Você é um assistente útil."},
|
716 |
-
{"role": "user", "content": "Diga apenas 'Olá, funcionando!' em português."}
|
717 |
-
]
|
718 |
-
|
719 |
-
for model_name in MODELS.keys():
|
720 |
-
print(f"\n🧪 Testando: {model_name}")
|
721 |
-
try:
|
722 |
-
result = await multi_client.query_model(test_message, model_name)
|
723 |
-
if result and not result.startswith("Erro"):
|
724 |
-
print(f"✅ {model_name} - Funcionando")
|
725 |
-
print(f" Resposta: {result[:100]}...")
|
726 |
-
else:
|
727 |
-
print(f"❌ {model_name} - Falha: {result}")
|
728 |
-
except Exception as e:
|
729 |
-
print(f"❌ {model_name} - Erro: {e}")
|
730 |
-
|
731 |
-
# Pequena pausa entre testes
|
732 |
-
await asyncio.sleep(1)
|
733 |
-
|
734 |
-
# --- Função de Configuração de Provedores ---
|
735 |
-
def configurar_provedores():
|
736 |
-
"""Exibe instruções para configurar os provedores"""
|
737 |
-
print("\n" + "=" * 60)
|
738 |
-
print("CONFIGURAÇÃO DE PROVEDORES LLM GRATUITOS")
|
739 |
-
print("=" * 60)
|
740 |
-
|
741 |
-
configs = {
|
742 |
-
"Hugging Face": {
|
743 |
-
"var": HF_TOKEN,
|
744 |
-
"url": "https://huggingface.co/settings/tokens",
|
745 |
-
"descricao": "Token gratuito para usar modelos do Hugging Face"
|
746 |
-
},
|
747 |
-
"Groq": {
|
748 |
-
"var": "GROQ_API_KEY",
|
749 |
-
"url": "https://console.groq.com/keys",
|
750 |
-
"descricao": "API key gratuita (limite diário)"
|
751 |
-
},
|
752 |
-
"OpenRouter": {
|
753 |
-
"var": OR_TOKEN,
|
754 |
-
"url": "https://openrouter.ai/keys",
|
755 |
-
"descricao": "Alguns modelos gratuitos disponíveis"
|
756 |
-
},
|
757 |
-
"Together AI": {
|
758 |
-
"var": "",
|
759 |
-
"url": "https://api.together.xyz/settings/api-keys",
|
760 |
-
"descricao": "Credits gratuitos iniciais"
|
761 |
-
},
|
762 |
-
"Ollama": {
|
763 |
-
"var": "Não necessário",
|
764 |
-
"url": "https://ollama.ai/",
|
765 |
-
"descricao": "Instalação local - totalmente gratuito"
|
766 |
-
}
|
767 |
-
}
|
768 |
-
|
769 |
-
print("Para usar todos os provedores, configure as seguintes variáveis de ambiente:")
|
770 |
-
print()
|
771 |
-
|
772 |
-
for provedor, info in configs.items():
|
773 |
-
status = "✅ Configurado" if os.getenv(info["var"]) else "❌ Não configurado"
|
774 |
-
print(f"{provedor}:")
|
775 |
-
print(f" • Variável: {info['var']}")
|
776 |
-
print(f" • URL: {info['url']}")
|
777 |
-
print(f" • Descrição: {info['descricao']}")
|
778 |
-
print(f" • Status: {status}")
|
779 |
-
print()
|
780 |
-
|
781 |
-
print("📝 Exemplo de configuração no seu .env:")
|
782 |
-
print("export HF_TOKEN='seu_token_aqui'")
|
783 |
-
print("export GROQ_API_KEY='sua_chave_aqui'")
|
784 |
-
print("export OPENROUTER_API_KEY='sua_chave_aqui'")
|
785 |
-
print("export TOGETHER_API_KEY='sua_chave_aqui'")
|
786 |
-
|
787 |
-
# --- Função de Monitoramento ---
|
788 |
-
async def monitorar_provedores():
|
789 |
-
"""Monitora o status dos provedores"""
|
790 |
-
print("\n" + "=" * 60)
|
791 |
-
print("MONITORAMENTO DE PROVEDORES")
|
792 |
-
print("=" * 60)
|
793 |
-
|
794 |
-
for provider in multi_client.available_providers:
|
795 |
-
print(f"\n🔍 Testando: {provider.name}")
|
796 |
-
try:
|
797 |
-
is_available = await provider.test_availability()
|
798 |
-
status = "✅ Online" if is_available else "❌ Offline"
|
799 |
-
print(f" Status: {status}")
|
800 |
-
print(f" Modelos: {len(provider.models)}")
|
801 |
-
except Exception as e:
|
802 |
-
print(f" Status: ❌ Erro - {e}")
|
803 |
-
|
804 |
-
# --- Função Principal Wrapper (para compatibilidade) ---
|
805 |
-
def responder_como_aldo_sync(session_id: str, pergunta: str, modelo: str = DEFAULT_MODEL) -> str:
|
806 |
-
"""Wrapper síncrono para compatibilidade com código existente"""
|
807 |
-
try:
|
808 |
-
loop = asyncio.get_event_loop()
|
809 |
-
if loop.is_running():
|
810 |
-
# Se já existe um loop rodando, cria uma nova task
|
811 |
-
import concurrent.futures
|
812 |
-
with concurrent.futures.ThreadPoolExecutor() as executor:
|
813 |
-
future = executor.submit(asyncio.run, responder_como_aldo(session_id, pergunta, modelo))
|
814 |
-
return future.result()
|
815 |
-
else:
|
816 |
-
return loop.run_until_complete(responder_como_aldo(session_id, pergunta, modelo))
|
817 |
-
except Exception as e:
|
818 |
-
return f"Erro ao processar resposta: {str(e)}"
|
819 |
-
|
820 |
-
# --- Função de Salvamento de Configurações ---
|
821 |
-
def salvar_configuracoes():
|
822 |
-
"""Salva as configurações atuais"""
|
823 |
-
config = {
|
824 |
-
"provedores_disponiveis": [p.name for p in multi_client.available_providers],
|
825 |
-
"modelos_disponiveis": list(MODELS.keys()),
|
826 |
-
"modelo_padrao": DEFAULT_MODEL,
|
827 |
-
"timestamp": time.time()
|
828 |
-
}
|
829 |
-
|
830 |
-
try:
|
831 |
-
with open("llm_config.json", "w", encoding="utf-8") as f:
|
832 |
-
json.dump(config, f, ensure_ascii=False, indent=2)
|
833 |
-
print("✅ Configurações salvas em 'llm_config.json'")
|
834 |
-
except Exception as e:
|
835 |
-
print(f"❌ Erro ao salvar configurações: {e}")
|
836 |
-
|
837 |
-
# --- Função Principal ---
|
838 |
-
async def main():
|
839 |
-
"""Função principal para teste e inicialização"""
|
840 |
-
print("🚀 Iniciando Sistema Multi-Provedor LLM")
|
841 |
-
print("=" * 60)
|
842 |
-
|
843 |
-
# Mostra configurações
|
844 |
-
configurar_provedores()
|
845 |
-
|
846 |
-
# Inicializa sistema
|
847 |
-
status, models = await inicializar_sistema_sync()
|
848 |
-
|
849 |
-
if not status:
|
850 |
-
print("❌ Sistema não pôde ser inicializado!")
|
851 |
-
return
|
852 |
-
|
853 |
-
# Salva configurações
|
854 |
-
salvar_configuracoes()
|
855 |
-
|
856 |
-
# Testa modelos
|
857 |
-
await testar_modelos_disponiveis()
|
858 |
-
|
859 |
-
# Monitora provedores
|
860 |
-
await monitorar_provedores()
|
861 |
-
|
862 |
-
# Teste básico do chatbot
|
863 |
-
print("\n" + "=" * 60)
|
864 |
-
print("TESTE DO CHATBOT")
|
865 |
-
print("=" * 60)
|
866 |
-
|
867 |
-
session_id = "teste_multi_provider"
|
868 |
-
|
869 |
-
test_questions = [
|
870 |
-
"O que é Python?",
|
871 |
-
"Mostre um exemplo de código Python simples",
|
872 |
-
"Qual a diferença entre lista e tupla em Python?"
|
873 |
-
]
|
874 |
-
|
875 |
-
for i, question in enumerate(test_questions, 1):
|
876 |
-
print(f"\n📝 Pergunta {i}: {question}")
|
877 |
-
print("-" * 40)
|
878 |
-
|
879 |
-
try:
|
880 |
-
resposta = await responder_como_aldo(session_id, question)
|
881 |
-
print(f"🤖 Resposta: {resposta[:500]}...")
|
882 |
-
except Exception as e:
|
883 |
-
print(f"❌ Erro: {e}")
|
884 |
-
|
885 |
-
await asyncio.sleep(2) # Pausa entre perguntas
|
886 |
-
|
887 |
-
# Limpa memória do teste
|
888 |
-
print(f"\n🧹 {clear_memory(session_id)}")
|
889 |
-
|
890 |
-
# --- Compatibilidade com código original ---
|
891 |
-
def inicializar_sistema_sync():
|
892 |
-
"""Função síncrona para compatibilidade """
|
893 |
-
try:
|
894 |
-
# A forma mais simples de rodar um evento asyncio a partir de um contexto síncrono
|
895 |
-
# é usar asyncio.run(). A lógica de checar o loop pode ser simplificada.
|
896 |
-
return asyncio.run(inicializar_sistema())
|
897 |
-
except Exception as e:
|
898 |
-
print(f"Erro na inicialização: {e}")
|
899 |
-
return False, {}
|
900 |
-
|
901 |
-
# Para compatibilidade, mantém a função original
|
902 |
-
responder_como_aldo_original = responder_como_aldo_sync
|
903 |
|
904 |
if __name__ == "__main__":
|
905 |
-
|
906 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
import json
|
4 |
import time
|
5 |
import pickle
|
6 |
+
from typing import Dict, List, Optional, Tuple
|
7 |
from bs4 import BeautifulSoup
|
8 |
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 |
+
if not HF_TOKEN:
|
19 |
+
raise ValueError("Token HF_TOKEN não encontrado ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
("IBM 2B","ibm-granite/granite-speech-3.3-2b"),
|
32 |
+
("IBM 8B","ibm-granite/granite-speech-3.3-8b"),
|
33 |
+
("Phi-3 Mini (Mais rápido)", "microsoft/Phi-3-mini-4k-instruct"),
|
34 |
+
("Zephyr 7B (Meio Termo)", "HuggingFaceH4/zephyr-7b-beta"),
|
35 |
+
("Microsoft 8B (Meio Termo)","meta-llama/Meta-Llama-3-8B-Instruct"),
|
36 |
+
("Mistral-7B","mistralai/Mistral-7B-Instruct-v0.3"),
|
37 |
+
("DialoGPT","microsoft/DialoGPT-medium"),
|
38 |
+
("Google","google/flan-t5-base"),
|
39 |
+
("Facebook","facebook/bart-large-cnn")
|
40 |
+
]
|
41 |
+
|
42 |
+
# --- Consulta a API da Hugging Face ---
|
43 |
+
url = "https://huggingface.co/api/models?filter=text-generation,transformers,conversational,license:mit,text-generation-inference,safetensors,autotrain_compatible,endpoints_compatible,region:us"
|
44 |
+
params = {
|
45 |
+
|
46 |
+
"private":"false",
|
47 |
+
"limit": 300, # Aumente se quiser trazer mais modelos
|
48 |
+
"full": "true"
|
49 |
+
}
|
50 |
+
|
51 |
+
response = requests.get(url, headers=headers, params=params)
|
52 |
+
|
53 |
+
if response.status_code != 200:
|
54 |
+
raise Exception(f"Erro na API: {response.status_code} - {response.text}")
|
55 |
+
|
56 |
+
models_data = response.json()
|
57 |
+
|
58 |
+
# --- Filtra modelos que possuem base_model ---
|
59 |
+
for model in models_data:
|
60 |
+
tags = model.get("tags", [])
|
61 |
+
base_model_tags = [tag for tag in tags if tag.startswith("base_model:")]
|
62 |
+
if base_model_tags:
|
63 |
+
model_name = model.get("id")
|
64 |
+
display_name = model_name.split("/")[-1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
+
# Verifica se já não está na lista para evitar duplicados
|
67 |
+
if not any(model_name == m[1] for m in NEW_MODELS_TO_TEST):
|
68 |
+
NEW_MODELS_TO_TEST.append((display_name, model_name))
|
69 |
|
70 |
+
# --- Resultado ---
|
71 |
+
print("Lista atualizada de modelos:\n")
|
72 |
+
for name, model_id in NEW_MODELS_TO_TEST:
|
73 |
+
print(f'("{name}", "{model_id}"),')
|
74 |
+
|
75 |
+
print(f"\nTotal de modelos na lista: {len(NEW_MODELS_TO_TEST)}")
|
76 |
+
|
77 |
+
# Nota: Alguns modelos podem requerer aprovação ou ter restrições de acesso
|
78 |
+
# Recomenda-se testar cada modelo individualmente para verificar disponibilidade
|
79 |
|
80 |
+
DEFAULT_MODEL = "Zephyr 7B (Meio Termo)"
|
81 |
+
|
82 |
+
# --- Gerenciamento de Sessão ---
|
83 |
user_sessions: Dict[str, Dict[str, List | Dict]] = {}
|
84 |
MAX_MEMORY_LENGTH = 5
|
85 |
|
|
|
175 |
os.remove(memory_path)
|
176 |
return "Memória limpa com sucesso!"
|
177 |
|
178 |
+
# --- RAG (Crawling e Vector Store) ---
|
179 |
vector_store: Optional[FAISS] = None
|
180 |
|
181 |
def get_all_blog_links(url: str) -> set:
|
|
|
247 |
print(f"Erro ao buscar contexto: {e}")
|
248 |
return ""
|
249 |
|
250 |
+
# --- API Client ---
|
251 |
+
class HuggingFaceAPIClient:
|
252 |
+
def __init__(self, token: str):
|
253 |
+
self.headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
254 |
+
|
255 |
+
def check_model_info(self, model_name: str) -> Tuple[bool, str]:
|
256 |
+
"""Verifica informações do modelo via API do Hugging Face."""
|
257 |
+
url = f"https://huggingface.co/api/models/{model_name}"
|
258 |
+
try:
|
259 |
+
response = requests.get(url, headers=self.headers, timeout=90)
|
260 |
+
if response.status_code == 200:
|
261 |
+
model_info = response.json()
|
262 |
+
if model_info.get('disabled', False):
|
263 |
+
return False, "Modelo desabilitado"
|
264 |
+
if model_info.get('gated', False):
|
265 |
+
return False, "Modelo requer aprovação/aceite de licença"
|
266 |
+
return True, "Modelo disponível"
|
267 |
+
elif response.status_code == 404:
|
268 |
+
return False, "Modelo não encontrado"
|
269 |
+
else:
|
270 |
+
return False, f"Erro HTTP {response.status_code}"
|
271 |
+
except requests.exceptions.RequestException as e:
|
272 |
+
return False, f"Erro na requisição: {str(e)}"
|
273 |
+
|
274 |
+
def test_model_inference(self, model_name: str) -> Tuple[bool, str]:
|
275 |
+
"""Testa se o modelo está disponível para inferência."""
|
276 |
+
url = f"https://api-inference.huggingface.co/models/{model_name}"
|
277 |
+
test_payload = {
|
278 |
+
"inputs": "Teste de disponibilidade do modelo.",
|
279 |
+
"parameters": {
|
280 |
+
"max_new_tokens": 10,
|
281 |
+
"temperature": 0.1,
|
282 |
+
"return_full_text": False
|
283 |
+
}
|
284 |
+
}
|
285 |
+
|
286 |
+
try:
|
287 |
+
response = requests.post(url, headers=self.headers, json=test_payload, timeout=90)
|
288 |
+
|
289 |
+
if response.status_code == 200:
|
290 |
+
result = response.json()
|
291 |
+
if isinstance(result, list) and len(result) > 0:
|
292 |
+
return True, "Modelo disponível para inferência"
|
293 |
+
elif isinstance(result, dict) and 'error' not in result:
|
294 |
+
return True, "Modelo disponível para inferência"
|
295 |
+
else:
|
296 |
+
return False, f"Resposta inesperada: {result}"
|
297 |
+
|
298 |
+
elif response.status_code == 503:
|
299 |
+
return False, "Modelo está carregando (503)"
|
300 |
+
elif response.status_code == 400:
|
301 |
+
error_msg = response.json().get('error', 'Erro 400')
|
302 |
+
if 'loading' in error_msg.lower():
|
303 |
+
return False, "Modelo está carregando"
|
304 |
+
return False, f"Erro 400: {error_msg}"
|
305 |
+
elif response.status_code == 401:
|
306 |
+
return False, "Token inválido ou sem permissão"
|
307 |
+
elif response.status_code == 404:
|
308 |
+
return False, "Modelo não encontrado"
|
309 |
+
else:
|
310 |
+
return False, f"Erro HTTP {response.status_code}: {response.text}"
|
311 |
+
|
312 |
+
except requests.exceptions.Timeout:
|
313 |
+
return False, "Timeout na requisição"
|
314 |
+
except requests.exceptions.RequestException as e:
|
315 |
+
return False, f"Erro na requisição: {str(e)}"
|
316 |
+
|
317 |
+
def test_model_availability(self, model_name: str) -> Tuple[bool, str]:
|
318 |
+
"""Testa se um modelo está disponível, combinando verificação de info e inferência."""
|
319 |
+
print(f"Testando modelo: {model_name}")
|
320 |
+
|
321 |
+
info_available, info_msg = self.check_model_info(model_name)
|
322 |
+
if not info_available:
|
323 |
+
return False, f"Info check failed: {info_msg}"
|
324 |
+
|
325 |
+
print(f" ✓ Info check: {info_msg}")
|
326 |
+
|
327 |
+
inference_available, inference_msg = self.test_model_inference(model_name)
|
328 |
+
|
329 |
+
if inference_available:
|
330 |
+
print(f" ✓ Inference check: {inference_msg}")
|
331 |
+
return True, f"Disponível - {info_msg}"
|
332 |
+
else:
|
333 |
+
print(f" ✗ Inference check: {inference_msg}")
|
334 |
+
return False, f"Não disponível para inferência: {inference_msg}"
|
335 |
+
|
336 |
+
def query_model(self, model_name: str, messages: List[Dict], max_tokens: int = 1000) -> str:
|
337 |
+
"""Faz requisição ao modelo usando text-generation."""
|
338 |
+
prompt = self._convert_messages_to_prompt(messages)
|
339 |
+
|
340 |
+
url = f"https://api-inference.huggingface.co/models/{model_name}"
|
341 |
+
payload = {
|
342 |
+
"inputs": prompt,
|
343 |
+
"parameters": {
|
344 |
+
"max_new_tokens": max_tokens,
|
345 |
+
"temperature": 0.7,
|
346 |
+
"do_sample": True,
|
347 |
+
"return_full_text": False
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
try:
|
352 |
+
response = requests.post(url, headers=self.headers, json=payload, timeout=2500)
|
353 |
+
response.raise_for_status()
|
354 |
+
|
355 |
+
result = response.json()
|
356 |
+
if isinstance(result, list) and len(result) > 0:
|
357 |
+
return result[0].get('generated_text', '').strip()
|
358 |
+
elif isinstance(result, dict) and 'generated_text' in result:
|
359 |
+
return result['generated_text'].strip()
|
360 |
+
else:
|
361 |
+
return f"Formato de resposta inesperado: {result}"
|
362 |
+
|
363 |
+
except requests.exceptions.HTTPError as http_err:
|
364 |
+
return f"Erro HTTP: {http_err.response.status_code} - {http_err.response.text}"
|
365 |
+
except requests.exceptions.RequestException as e:
|
366 |
+
return f"Erro na requisição: {str(e)}"
|
367 |
+
|
368 |
+
def _convert_messages_to_prompt(self, messages: List[Dict]) -> str:
|
369 |
+
"""Converte mensagens do formato chat para prompt simples."""
|
370 |
+
prompt_parts = []
|
371 |
+
for msg in messages:
|
372 |
+
role = msg['role']
|
373 |
+
content = msg['content']
|
374 |
+
|
375 |
+
if role == 'system':
|
376 |
+
prompt_parts.append(f"Sistema: {content}")
|
377 |
+
elif role == 'user':
|
378 |
+
prompt_parts.append(f"Usuário: {content}")
|
379 |
+
elif role == 'assistant':
|
380 |
+
prompt_parts.append(f"Assistente: {content}")
|
381 |
+
|
382 |
+
prompt_parts.append("Assistente:")
|
383 |
+
return "\n\n".join(prompt_parts)
|
384 |
+
|
385 |
+
api_client = HuggingFaceAPIClient(HF_TOKEN)
|
386 |
+
|
387 |
+
# --- Função para Testar e Atualizar Modelos ---
|
388 |
+
def test_and_update_models() -> int:
|
389 |
+
"""
|
390 |
+
Testa a disponibilidade dos novos modelos e atualiza a lista MODELS.
|
391 |
+
Garante que o DEFAULT_MODEL seja sempre o primeiro da lista.
|
392 |
+
Retorna o número de modelos disponíveis.
|
393 |
+
"""
|
394 |
+
print("Testando disponibilidade dos novos modelos...")
|
395 |
+
print(f"Token HF disponível: {'Sim' if HF_TOKEN else 'Não'}")
|
396 |
+
print("-" * 60)
|
397 |
+
|
398 |
+
# Cria um dicionário temporário para os modelos disponíveis
|
399 |
+
temp_models = {}
|
400 |
+
|
401 |
+
# Primeiro verifica o modelo padrão
|
402 |
+
default_label, default_name = "Mistral 7B (Mais acertivo)", "mistralai/Mistral-7B-Instruct-v0.3"
|
403 |
+
is_available, message = api_client.test_model_availability(default_name)
|
404 |
+
|
405 |
+
if is_available:
|
406 |
+
temp_models[default_label] = default_name
|
407 |
+
print(f"✓ {default_label} (DEFAULT MODEL)")
|
408 |
+
else:
|
409 |
+
print(f"✗ {default_label} - {message} (MODELO PADRÃO INDISPONÍVEL)")
|
410 |
+
|
411 |
+
# Depois verifica os outros modelos
|
412 |
+
for model_label, model_name in NEW_MODELS_TO_TEST:
|
413 |
+
# Pula o modelo padrão se já foi testado
|
414 |
+
if model_label == default_label and model_name == default_name:
|
415 |
+
continue
|
416 |
+
|
417 |
+
is_available, message = api_client.test_model_availability(model_name)
|
418 |
+
|
419 |
+
if is_available:
|
420 |
+
temp_models[model_label] = model_name
|
421 |
+
print(f"✓ {model_label}")
|
422 |
+
else:
|
423 |
+
print(f"✗ {model_label} - {message}")
|
424 |
+
|
425 |
+
time.sleep(1)
|
426 |
+
|
427 |
+
# Atualiza MODELS garantindo que o padrão seja o primeiro
|
428 |
+
global MODELS
|
429 |
+
MODELS.clear()
|
430 |
+
|
431 |
+
# Adiciona primeiro o modelo padrão (se disponível)
|
432 |
+
if default_label in temp_models:
|
433 |
+
MODELS[default_label] = temp_models.pop(default_label)
|
434 |
+
|
435 |
+
# Adiciona os demais modelos
|
436 |
+
MODELS.update(temp_models)
|
437 |
+
|
438 |
+
print("\n" + "=" * 60)
|
439 |
+
print("MODELOS DISPONÍVEIS (ORDEM):")
|
440 |
+
print("=" * 60)
|
441 |
+
for i, (label, name) in enumerate(MODELS.items(), 1):
|
442 |
+
print(f"{i}. {label}")
|
443 |
+
|
444 |
+
print(f"\nTOTAL DE MODELOS DISPONÍVEIS: {len(MODELS)}")
|
445 |
+
print("=" * 60)
|
446 |
+
|
447 |
+
save_updated_models()
|
448 |
+
return len(MODELS)
|
449 |
+
|
450 |
+
def save_updated_models():
|
451 |
+
"""Salva a lista atualizada de modelos em um arquivo."""
|
452 |
+
try:
|
453 |
+
with open("models_available.json", "w", encoding="utf-8") as f:
|
454 |
+
json.dump(MODELS, f, ensure_ascii=False, indent=2)
|
455 |
+
print("Lista de modelos disponíveis salva em 'models_available.json'")
|
456 |
+
except Exception as e:
|
457 |
+
print(f"Erro ao salvar lista de modelos: {e}")
|
458 |
+
|
459 |
+
# --- Chat Principal ---
|
460 |
+
def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFAULT_MODEL) -> str:
|
461 |
+
"""Gera resposta como Dr. Aldo Henrique."""
|
462 |
if not pergunta.strip():
|
463 |
return "Por favor, faça uma pergunta válida."
|
464 |
|
|
|
477 |
Doutor em Ciências da Computação pela UnB (2024), mestre em Ciências da Computação pela UnB (2017) e bacharel em Sistemas de Informação pela UFV (2014).
|
478 |
Professor universitário, onde leciona disciplinas como Algoritmos, Inteligência Artificial, Ciência de Dados e Mineração de Dados.
|
479 |
Atua como analista de sistemas nível 4.
|
|
|
480 |
Regras de conduta:
|
481 |
- Sempre coloque uma piada ou trocadilho no final da resposta.
|
482 |
- Responda em português, de forma clara, amigável e educativa.
|
|
|
496 |
{"role": "user", "content": mensagem_usuario}
|
497 |
]
|
498 |
|
499 |
+
model_name = MODELS.get(modelo, MODELS[DEFAULT_MODEL])
|
500 |
+
resposta = api_client.query_model(model_name, messages)
|
501 |
add_to_memory(session_id, pergunta, resposta)
|
502 |
return resposta
|
503 |
|
504 |
+
# --- Inicialização ---
|
505 |
+
def inicializar_sistema():
|
506 |
"""
|
507 |
+
Inicializa o sistema, garantindo no mínimo 3 modelos disponíveis.
|
508 |
Retorna uma tupla: (status: bool, models: dict)
|
509 |
+
- status: True se >= 3 modelos disponíveis, False caso contrário
|
510 |
+
- models: Dicionário com os modelos disponíveis
|
511 |
"""
|
512 |
+
print("Inicializando Chatbot Dr. Aldo...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
|
514 |
+
num_available_models = test_and_update_models()
|
|
|
|
|
515 |
|
516 |
+
if num_available_models >= 1:
|
517 |
+
load_vector_store()
|
518 |
+
print("Sistema inicializado e pronto para uso com modelos suficientes!")
|
519 |
+
return True, MODELS
|
520 |
+
else:
|
521 |
+
print(f"Erro: Apenas {num_available_models} modelos disponíveis. São necessários pelo menos 3 modelos para iniciar o sistema.")
|
522 |
+
return False, MODELS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
523 |
|
524 |
if __name__ == "__main__":
|
525 |
+
status, models = inicializar_sistema()
|
526 |
+
if status:
|
527 |
+
print("\n" + "="*50)
|
528 |
+
print("SISTEMA INICIADO: Realizando teste básico do Chatbot... ")
|
529 |
+
print("="*50)
|
530 |
+
session_id = "teste_123"
|
531 |
+
print(responder_como_aldo(session_id, "O que é Java?"))
|
532 |
+
print("\n" + "-"*50)
|
533 |
+
print(responder_como_aldo(session_id, "Mostre um exemplo de código Java."))
|
534 |
+
print("\n" + "-"*50)
|
535 |
+
print(clear_memory(session_id))
|
536 |
+
else:
|
537 |
+
print("\nSistema não pôde ser iniciado devido à falta de modelos suficientes.")
|
538 |
+
print(f"Modelos disponíveis: {', '.join(models.keys()) if models else 'Nenhum'}")
|
539 |
+
print("Por favor, verifique a conexão com o Hugging Face e o token de acesso.")
|