aldohenrique commited on
Commit
098b973
·
verified ·
1 Parent(s): c239445

Update ai_logic.py

Browse files
Files changed (1) hide show
  1. 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, Any
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
- OR_TOKEN= os.getenv("OR_TOKEN")
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
- class HuggingFaceProvider(LLMProvider):
58
- """Provedor Hugging Face (original) """
59
- def __init__(self, token: str):
60
- super().__init__("Hugging Face", "https://api-inference.huggingface.co")
61
- self.token = token
62
- self.headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
63
- self.models = [
64
- ("IBM 2B","ibm-granite/granite-speech-3.3-2b"),
65
- ("IBM 8B","ibm-granite/granite-speech-3.3-8b"),
66
- ("Phi-3 Mini (Mais rápido)", "microsoft/Phi-3-mini-4k-instruct"),
67
- ("Zephyr 7B (Meio Termo)", "HuggingFaceH4/zephyr-7b-beta"),
68
- ("Mistral-7B", "mistralai/Mistral-7B-Instruct-v0.3"),
69
- ("Microsoft 8B", "meta-llama/Meta-Llama-3-8B-Instruct"),
70
- ("DialoGPT", "microsoft/DialoGPT-medium"),
71
- ("Google Flan-T5", "google/flan-t5-base"),
72
- ("Facebook BART", "facebook/bart-large-cnn")
73
- ]
74
-
75
- async def test_availability(self) -> bool:
76
- if not self.token:
77
- return False
78
-
79
- # Testa com um modelo simples
80
- test_model = "microsoft/DialoGPT-medium"
81
- url = f"{self.base_url}/models/{test_model}"
82
-
83
- try:
84
- async with aiohttp.ClientSession() as session:
85
- async with session.post(
86
- url,
87
- headers=self.headers,
88
- json={"inputs": "Hello", "parameters": {"max_new_tokens": 5}},
89
- timeout=aiohttp.ClientTimeout(total=10)
90
- ) as response:
91
- self.is_available = response.status in [200, 503] # 503 = modelo carregando
92
- return self.is_available
93
- except:
94
- return False
95
-
96
- async def query_model(self, messages: List[Dict], model_name: str = None, **kwargs) -> str:
97
- if not model_name:
98
- model_name = "mistralai/Mistral-7B-Instruct-v0.3"
99
-
100
- prompt = self.convert_messages_to_prompt(messages)
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
- return "Desculpe, todos os provedores de LLM estão indisponíveis no momento. Tente novamente mais tarde."
 
 
451
 
452
- # --- Integração com o código original ---
453
- # Variáveis globais atualizadas
454
- MODELS = {}
455
- DEFAULT_MODEL = "Mistral 7B (Hugging Face)"
456
- multi_client = MultiProviderLLMClient()
 
 
 
 
457
 
458
- # Gerenciamento de Sessão (mantido igual)
 
 
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 (mantido igual) ---
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
- # --- Função Principal Atualizada ---
627
- async def responder_como_aldo(session_id: str, pergunta: str, modelo: str = DEFAULT_MODEL) -> str:
628
- """Gera resposta como Dr. Aldo Henrique usando múltiplos provedores."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # Usa o cliente multi-provedor
668
- resposta = await multi_client.query_model(messages, modelo)
669
  add_to_memory(session_id, pergunta, resposta)
670
  return resposta
671
 
672
- # --- Função de Inicialização Atualizada ---
673
- async def inicializar_sistema():
674
  """
675
- Inicializa o sistema com múltiplos provedores de LLM.
676
  Retorna uma tupla: (status: bool, models: dict)
 
 
677
  """
678
- print("Inicializando Sistema Multi-Provedor Dr. Aldo...")
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
- # Atualiza variáveis globais
689
- global MODELS, DEFAULT_MODEL
690
- MODELS = multi_client.get_available_models()
691
 
692
- if MODELS:
693
- # Define o primeiro modelo como padrão
694
- DEFAULT_MODEL = list(MODELS.keys())[0]
695
- print(f"✅ Modelo padrão definido: {DEFAULT_MODEL}")
696
-
697
- # Carrega o vector store
698
- load_vector_store()
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
- # Executa o sistema
906
- asyncio.run(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 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.")