aldohenrique commited on
Commit
7dc0225
·
verified ·
1 Parent(s): 352c4a4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -420
app.py CHANGED
@@ -1,437 +1,40 @@
1
- import requests
2
- import os
3
- import json
4
- import re
5
- import gradio as gr
6
- from typing import Dict, Any, List, Optional, Tuple # Importando Tuple
7
- import pickle
8
-
9
- # --- Novas importações para o RAG ---
10
- import time
11
- from bs4 import BeautifulSoup
12
- from urllib.parse import urljoin, urlparse
13
- from langchain.text_splitter import RecursiveCharacterTextSplitter
14
- from langchain.vectorstores import FAISS
15
- from langchain.embeddings import HuggingFaceEmbeddings
16
-
17
- # --- Configuração do RAG ---
18
- BLOG_URL = "https://aldohenrique.com.br/"
19
- VECTOR_STORE_PATH = "faiss_index_store.pkl"
20
- PROCESSED_URLS_PATH = "processed_urls.pkl"
21
-
22
- # --- Configuração da API Hugging Face ---
23
- HF_TOKEN = os.getenv("HF_TOKEN")
24
- if not HF_TOKEN:
25
- raise ValueError("Token HF_TOKEN não encontrado nas variáveis de ambiente")
26
-
27
- MODELS = {
28
- "Phi-3 Mini (Microsoft)": "microsoft/Phi-3-mini-4k-instruct",
29
- "Mistral 7B": "mistralai/Mistral-7B-Instruct-v0.3",
30
- "Zephyr 7B": "HuggingFaceH4/zephyr-7b-beta",
31
- #"Llama 3.2 3B (Meta)": "meta-llama/Llama-3.2-3B-Instruct",
32
- #"DeepSeek-Coder-V2": "deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct",
33
- }
34
- DEFAULT_MODEL = "Phi-3 Mini (Microsoft)"
35
-
36
- # --- Variáveis Globais para o RAG ---
37
- vector_store: Optional[FAISS] = None
38
-
39
- # ==============================================================================
40
- # SEÇÃO RAG: FUNÇÕES PARA CRAWLING, EMBEDDING E ARMAZENAMENTO
41
- # ==============================================================================
42
-
43
- def get_all_blog_links(url: str, processed_urls: set) -> set:
44
- """Navega pelo blog para encontrar todos os links de posts e páginas."""
45
- links_to_visit = {url}
46
- visited_links = set()
47
-
48
- while links_to_visit:
49
- current_url = links_to_visit.pop()
50
- if current_url in visited_links:
51
- continue
52
-
53
- try:
54
- response = requests.get(current_url, timeout=10)
55
- response.raise_for_status()
56
- soup = BeautifulSoup(response.content, 'html.parser')
57
- visited_links.add(current_url)
58
- print(f"Visitando: {current_url}")
59
-
60
- for link in soup.find_all('a', href=True):
61
- href = link['href']
62
- full_url = urljoin(url, href)
63
- # Garante que estamos no mesmo domínio e não é um link de âncora
64
- if urlparse(full_url).netloc == urlparse(url).netloc and full_url not in visited_links:
65
- links_to_visit.add(full_url)
66
- except requests.RequestException as e:
67
- print(f"Erro ao acessar {current_url}: {e}")
68
-
69
- # Filtra apenas as páginas que parecem ser posts ou páginas de conteúdo
70
- final_links = {link for link in visited_links if '/tag/' not in link and '/category/' not in link and '?' not in link}
71
- return final_links
72
-
73
-
74
- def scrape_text_from_url(url: str) -> str:
75
- """Extrai o texto principal (de artigos) de uma URL."""
76
- try:
77
- response = requests.get(url, timeout=10)
78
- soup = BeautifulSoup(response.content, 'html.parser')
79
- # Tenta encontrar a tag <article> ou <main> que geralmente contém o conteúdo principal
80
- main_content = soup.find('article') or soup.find('main')
81
- if main_content:
82
- return main_content.get_text(separator='\n', strip=True)
83
- return ""
84
- except Exception as e:
85
- print(f"Erro ao raspar {url}: {e}")
86
- return ""
87
-
88
- def build_and_save_vector_store() -> Tuple[str, Optional[str], Optional[str]]: # Alterado o tipo de retorno para incluir os dois caminhos
89
- """
90
- Função principal do RAG: raspa o blog, cria chunks, gera embeddings e salva o vector store.
91
- Esta é a nossa função de "treino".
92
- Retorna uma tupla (mensagem_status, caminho_do_arquivo_faiss_para_download, caminho_do_arquivo_urls_para_download).
93
- """
94
- global vector_store
95
- start_time = time.time()
96
-
97
- print("Iniciando o processo de retreino do RAG...")
98
- processed_urls = set()
99
-
100
- # 1. Obter todos os links do blog
101
- all_links = get_all_blog_links(BLOG_URL, processed_urls)
102
- print(f"Encontrados {len(all_links)} links para processar.")
103
-
104
- # 2. Raspar o texto de cada link
105
- all_texts = [scrape_text_from_url(link) for link in all_links if link not in processed_urls]
106
- all_texts = [text for text in all_texts if text] # Remove textos vazios
107
- print(f"Textos extraídos de {len(all_texts)} novas páginas.")
108
-
109
- if not all_texts:
110
- return "Nenhum novo conteúdo encontrado para treinar.", None, None # Retorna None para os arquivos se não houver conteúdo
111
-
112
- # 3. Dividir os textos em chunks
113
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=150)
114
- chunks = text_splitter.create_documents(all_texts)
115
- print(f"Textos divididos em {len(chunks)} chunks.")
116
-
117
- # 4. Criar embeddings e o vector store (FAISS)
118
- print("Carregando modelo de embedding...")
119
- embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
120
-
121
- print("Criando o vector store com FAISS...")
122
- vector_store = FAISS.from_documents(chunks, embeddings_model)
123
-
124
- # 5. Salvar o vector store e as URLs processadas em disco
125
- with open(VECTOR_STORE_PATH, "wb") as f:
126
- pickle.dump(vector_store, f)
127
-
128
- with open(PROCESSED_URLS_PATH, "wb") as f:
129
- pickle.dump(all_links, f)
130
-
131
- end_time = time.time()
132
- message = f"✅ Retreino do RAG concluído em {end_time - start_time:.2f} segundos. {len(chunks)} chunks de texto processados."
133
- return message, VECTOR_STORE_PATH, PROCESSED_URLS_PATH # Retorna a mensagem e os caminhos dos dois arquivos para download
134
-
135
- def load_vector_store():
136
- """Carrega o vector store do arquivo, se existir."""
137
- global vector_store
138
- if os.path.exists(VECTOR_STORE_PATH):
139
- print(f"Carregando vector store existente de '{VECTOR_STORE_PATH}'...")
140
- with open(VECTOR_STORE_PATH, "rb") as f:
141
- vector_store = pickle.load(f)
142
- print("Vector store carregado com sucesso.")
143
- else:
144
- print("Nenhum vector store encontrado. É necessário treinar o modelo.")
145
- # Inicia o treino automaticamente se não houver um índice
146
- # Modificado para ignorar o retorno dos caminhos dos arquivos ao carregar
147
- message, _, _ = build_and_save_vector_store()
148
- print(message) # Imprime a mensagem de status do treino inicial
149
-
150
- def retrieve_context_from_blog(query: str, k: int = 3) -> str:
151
- """Busca no vector store por chunks de texto similares à pergunta."""
152
- if vector_store:
153
- try:
154
- results = vector_store.similarity_search(query, k=k)
155
- context = "\n\n---\n\n".join([doc.page_content for doc in results])
156
- return context
157
- except Exception as e:
158
- return f"Erro ao buscar contexto: {e}"
159
- return ""
160
-
161
- # ==============================================================================
162
- # SEÇÃO API CLIENT: CÓDIGO ORIGINAL PARA CHAMAR A API DO HUGGING FACE
163
- # ==============================================================================
164
-
165
- class HuggingFaceAPIClient:
166
- def __init__(self, token: str):
167
- self.token = token
168
- self.headers = {
169
- "Authorization": f"Bearer {token}",
170
- "Content-Type": "application/json"
171
- }
172
-
173
- def query_model(self, model_name: str, messages: list, max_tokens: int = 1500) -> str:
174
- """Faz requisição para a API do Hugging Face"""
175
- url = f"https://api-inference.huggingface.co/models/{model_name}/v1/chat/completions"
176
- payload = {
177
- "model": model_name,
178
- "messages": messages,
179
- "max_tokens": max_tokens,
180
- "temperature": 0.7,
181
- "top_p": 0.9,
182
- "stream": False
183
- }
184
- try:
185
- response = requests.post(url, headers=self.headers, json=payload, timeout=9999)
186
- if response.status_code == 200:
187
- result = response.json()
188
- return result["choices"][0]["message"]["content"]
189
- else:
190
- return self._fallback_text_generation(model_name, messages, max_tokens)
191
- except Exception as e:
192
- return f"Erro na API: {str(e)}"
193
-
194
- def _fallback_text_generation(self, model_name: str, messages: list, max_tokens: int) -> str:
195
- url = f"https://api-inference.huggingface.co/models/{model_name}"
196
- prompt = self._messages_to_prompt(messages)
197
- payload = {
198
- "inputs": prompt,
199
- "parameters": {
200
- "max_new_tokens": max_tokens, "temperature": 0.7, "top_p": 0.9,
201
- "do_sample": True, "return_full_text": False
202
- },
203
- "options": {"wait_for_model": True, "use_cache": False}
204
- }
205
- try:
206
- response = requests.post(url, headers=self.headers, json=payload, timeout=9999)
207
- if response.status_code == 200:
208
- result = response.json()
209
- if isinstance(result, list) and len(result) > 0:
210
- generated_text = result[0].get("generated_text", "")
211
- if generated_text:
212
- if "Assistente: " in generated_text:
213
- parts = generated_text.split("Assistente: ")
214
- if len(parts) > 1: return parts[-1].strip()
215
- return generated_text.strip()
216
- return "Resposta vazia"
217
- elif isinstance(result, dict):
218
- if "error" in result: return f"Erro do modelo: {result['error']}"
219
- elif "generated_text" in result: return result["generated_text"].strip()
220
- return "Formato de resposta inesperado"
221
- elif response.status_code == 404: return f"❌ Modelo '{model_name}' não encontrado."
222
- elif response.status_code == 503: return "⏳ Modelo carregando... Tente novamente."
223
- elif response.status_code == 429: return "⚠️ Muitas requisições. Tente novamente."
224
- else: return f"Erro HTTP {response.status_code}: {response.text[:200]}..."
225
- except requests.Timeout:
226
- return "⏰ Timeout - Modelo demorou muito para responder."
227
- except Exception as e:
228
- return f"Erro na requisição: {str(e)}"
229
-
230
- def _messages_to_prompt(self, messages: list) -> str:
231
- prompt = ""
232
- for msg in messages:
233
- prompt += f"{msg['role'].capitalize()}: {msg['content']}\n\n"
234
- prompt += "Assistente: "
235
- return prompt
236
-
237
- # Inicializar cliente da API
238
- api_client = HuggingFaceAPIClient(HF_TOKEN)
239
-
240
- # ==============================================================================
241
- # SEÇÃO PRINCIPAL: LÓGICA DO CHATBOT E GRADIO
242
- # ==============================================================================
243
-
244
- def formatar_resposta_com_codigo(resposta: str) -> str:
245
- """Formata a resposta destacando códigos em blocos separados."""
246
- if not resposta: return resposta
247
-
248
- # Primeiro, substituir < e > por entidades HTML para evitar interpretação como tags
249
- resposta = resposta.replace('<', '&lt;').replace('>', '&gt;')
250
- resposta_formatada = re.sub(
251
- r'```(\w+)?\n(.*?)\n```',
252
- r'<div style="background-color: #f8f9fa; color: #1a1a1a; border: 1px solid #e9ecef; border-radius: 8px; padding: 15px; margin: 10px 0; font-family: Monaco, Consolas, monospace; overflow-x: auto;"><strong style="color: #1a1a1a;">💻 Código:</strong><br><pre style="color: #1a1a1a; margin: 5px 0; white-space: pre-wrap; word-wrap: break-word;"><code>\2</code></pre></div>',
253
- resposta, flags=re.DOTALL
254
- )
255
- resposta_formatada = re.sub(
256
- r'`([^`]+)`',
257
- r'<code style="background-color: #f1f3f4; color: #1a1a1a; padding: 2px 4px; border-radius: 4px; font-family: Monaco, Consolas, monospace;">\1</code>',
258
- resposta_formatada
259
- )
260
- resposta_formatada = resposta_formatada.replace('\n', '<br>')
261
- resposta_formatada = re.sub(
262
- r'^\*\*(.*?)\*\*',
263
- r'<h3 style="color: #1a1a1a; margin-top: 20px; margin-bottom: 10px;">\1</h3>',
264
- resposta_formatada, flags=re.MULTILINE
265
- )
266
- return resposta_formatada
267
-
268
- def responder_como_aldo(pergunta: str, modelo_escolhido: str = DEFAULT_MODEL) -> str:
269
- """Função principal para gerar respostas, agora com RAG."""
270
- if not pergunta.strip():
271
- return "Por favor, faça uma pergunta."
272
-
273
- try:
274
- # --- ETAPA DE RAG ---
275
- print(f"Buscando contexto para a pergunta: '{pergunta[:50]}...'")
276
- contexto_blog = retrieve_context_from_blog(pergunta)
277
-
278
- # Montar o prompt do sistema com o contexto do RAG
279
- system_prompt = (
280
- "Você é o professor Dr. Aldo Henrique, especialista em C, Java, desenvolvimento web e inteligência artificial. "
281
- "Responda com clareza, profundidade e tom acadêmico. Foque em explicar e não em só mostrar o resultado. "
282
- "Responda sempre em português brasileiro. Use blocos de código formatados com ```. "
283
- "Não responda se a pergunta não for sobre o universo de programação e tecnologia."
284
- "Nem sempre fornecer código, mas quando tiver código, sempre explique utilizando comentários, o aluno precisa aprender lendo os comentários."
285
- "Quando for pergunta sobre disciplinas, foque no conteúdo do blog."
286
- )
287
-
288
- # Montar prompt do usuário, injetando o contexto do blog
289
- if contexto_blog:
290
- pergunta_completa = (
291
- "Você é o professor Dr. Aldo Henrique, especialista em C, Java, desenvolvimento web e inteligência artificial. "
292
- "Com base no seguinte contexto extraído do seu blog, responda à pergunta do usuário.\n\n"
293
- "--- CONTEXTO DO BLOG ---\n"
294
- f"{contexto_blog}\n"
295
- "--- FIM DO CONTEXTO ---\n\n"
296
- f"PERGUNTA DO USUÁRIO: {pergunta}"
297
- "Responda sempre em português brasileiro. Use blocos de código formatados com ```. "
298
- "Não responda nada se a pergunta não for sobre o universo de programação e tecnologia."
299
- "Nem sempre fornecer código, mas quando tiver código, sempre explique utilizando comentários, o aluno precisa aprender lendo os comentários."
300
- "Quando for pergunta sobre disciplinas, foque no conteúdo do blog."
301
- )
302
- print("Contexto encontrado e injetado no prompt.")
303
- else:
304
- pergunta_completa = f"{pergunta} Não responda nada se a pergunta não for sobre o universo de programação e tecnologia, informe que o Dr. Aldo Henrique só tem domínio em TI. Você é o Professor Dr. Aldo Henrique, foque em explicar e não em só mostrar o resultado. Quando apresentar código, use blocos de código formatados com ```. Sempre responda primeiro a explicação e depois modestre o código."
305
- print("Nenhum contexto relevante encontrado no blog, usando prompt padrão.")
306
-
307
- messages = [
308
- {"role": "system", "content": system_prompt},
309
- {"role": "user", "content": pergunta_completa}
310
- ]
311
-
312
- model_name = MODELS.get(modelo_escolhido, MODELS[DEFAULT_MODEL])
313
- resposta = api_client.query_model(model_name, messages)
314
-
315
- if resposta.startswith("Assistente: "):
316
- resposta = resposta.replace("Assistente: ", "")
317
-
318
- resposta_formatada = formatar_resposta_com_codigo(resposta.strip())
319
- return resposta_formatada
320
-
321
- except Exception as e:
322
- return f"Erro ao processar sua pergunta: {str(e)}"
323
-
324
- # Funções de teste (inalteradas)
325
- def verificar_modelo_disponivel(model_name: str) -> str:
326
- try:
327
- url = f"https://api-inference.huggingface.co/models/{model_name}"
328
- headers = {"Authorization": f"Bearer {HF_TOKEN}"}
329
- payload = {"inputs": "Hello", "parameters": {"max_new_tokens": 5}}
330
- response = requests.post(url, headers=headers, json=payload, timeout=9999)
331
- if response.status_code == 200: return "✅ Disponível"
332
- elif response.status_code == 404: return "❌ Não encontrado"
333
- elif response.status_code == 503: return "⏳ Carregando..."
334
- else: return f"⚠️ Status {response.status_code}"
335
- except Exception as e:
336
- return f"❌ Erro: {str(e)[:50]}..."
337
-
338
- def testar_todos_modelos():
339
- resultados = []
340
- for nome, modelo in MODELS.items():
341
- status = verificar_modelo_disponivel(modelo)
342
- resultados.append(f"{nome}: {status}")
343
- return "\n".join(resultados)
344
-
345
- # CSS (inalterado)
346
- css_customizado = """
347
- .gradio-container { max-width: 1400px !important; margin: 0 auto; width: 99%; }
348
- .gr-textbox textarea { font-size: 14px !important; line-height: 1.5 !important; }
349
- .resposta-container { background-color: #ffffff !important; color: #1a1a1a !important; border: 1px solid #e0e0e0 !important; border-radius: 20px !important; padding: 20px !important; margin: 20px 0 !important; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important; }
350
- .resposta-container pre code { color: #1a1a1a !important; background-color: #f8f9fa !important; }
351
- .pergunta-container { background-color: #f0f8ff !important; border-radius: 8px !important; padding: 15px !important; }
352
- .titulo-principal { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; padding: 20px !important; border-radius: 10px !important; margin-bottom: 20px !important; text-align: center !important; }
353
- .modelo-dropdown { margin-bottom: 15px !important; }
354
  """
355
 
356
- # Interface Gradio
357
- with gr.Blocks(title="Dr. Aldo Henrique - API Externa", theme=gr.themes.Soft(), css=css_customizado) as interface:
358
- gr.HTML("""
359
- <div class="titulo-principal">
360
- <h1>🤖 Dr. Aldo Henrique - Foco em TI com diferentes modelos de IA </h1>
361
- <p style="font-size: 14px; opacity: 0.9;">Conhecimento enriquecido com o conteúdo do <a href="https://aldohenrique.com.br/" style="color: white; text-decoration: underline;">Blog do Prof. Dr. Aldo Henrique</a></p>
362
- </div>
363
- """)
364
-
365
- with gr.Row():
366
- with gr.Column(scale=2):
367
- gr.Markdown("### 📝 Faça sua pergunta:")
368
- entrada = gr.Textbox(label="", placeholder="Digite sua pergunta aqui.", lines=6, elem_classes="pergunta-container")
369
- modelo_select = gr.Dropdown(choices=list(MODELS.keys()), value=DEFAULT_MODEL, label="🧠 Selecione o Modelo de IA", info="Escolha o modelo para responder", elem_classes="modelo-dropdown")
370
-
371
- with gr.Row():
372
- botao_perguntar = gr.Button("🤔 Perguntar ao Dr. Aldo", variant="primary", size="lg")
373
- #botao_testar = gr.Button("🔍 Testar Modelos", variant="secondary")
374
-
375
- with gr.Column(scale=3):
376
- gr.Markdown("### 💬 Resposta do Dr. Aldo Henrique:")
377
- saida = gr.HTML(label="", value="<div style='padding: 20px; text-align: center; color: #1a1a1a;'>Aguardando sua pergunta...</div>", elem_classes="resposta-container")
378
-
379
- # --- NOVA SEÇÃO PARA CONTROLE DO RAG ---
380
- with gr.Accordion("⚙️ Controle do Conhecimento (RAG)", open=False):
381
- status_rag = gr.Textbox(label="Status do Retreino", interactive=False)
382
- botao_retreinar = gr.Button("🔄 Atualizar Conhecimento do Blog", variant="stop")
383
- # Novos componentes para download
384
- download_faiss_file = gr.File(label="Download do Índice FAISS", interactive=False, file_count="single", file_types=[".pkl"])
385
- download_urls_file = gr.File(label="Download das URLs Processadas", interactive=False, file_count="single", file_types=[".pkl"])
386
 
387
- with gr.Accordion("📚 Exemplos de Perguntas", open=False):
388
- gr.Examples(
389
- examples=[
390
- ["Como implementar uma lista ligada em C com todas as operações básicas?", DEFAULT_MODEL],
391
- ["Qual a sua opinião sobre o uso de ponteiros em C++ moderno, baseada no seu blog?", "Mistral 7B"],
392
- ["Resuma o que você escreveu sobre machine learning no seu blog.", "Llama 3.2 3B (Meta)"],
393
- ],
394
- inputs=[entrada, modelo_select]
395
- )
396
 
397
- with gr.Accordion("🔧 Status da API", open=False):
398
- status_api = gr.Textbox(label="Status dos Modelos", interactive=False, lines=8)
 
 
399
 
400
- with gr.Accordion("ℹ️ Informações", open=False):
401
- gr.Markdown("""
402
- ### Sobre o Dr. Aldo Henrique:
403
- - **Especialidade**: Linguagens C, Java, Desenvolvimento Web, Inteligência Artificial
404
- - **Conhecimento Adicional**: Conteúdo do blog aldohenrique.com.br
405
- ### Dicas para melhores respostas:
406
- - Faça perguntas específicas sobre o conteúdo do blog para ver o RAG em ação!
407
- - Peça resumos ou opiniões sobre temas que o professor aborda.
408
- """)
409
-
410
- # Eventos
411
- botao_perguntar.click(fn=responder_como_aldo, inputs=[entrada, modelo_select], outputs=saida, show_progress=True)
412
- #botao_testar.click(fn=testar_todos_modelos, outputs=status_api, show_progress=True)
413
- # Atualiza o evento para a função build_and_save_vector_store
414
- # Agora, ela retorna três valores: a mensagem de status e os caminhos dos dois arquivos
415
- botao_retreinar.click(
416
- fn=build_and_save_vector_store,
417
- outputs=[status_rag, download_faiss_file, download_urls_file], # Saídas atualizadas
418
- show_progress=True
419
- )
420
-
421
- # Lançar aplicação
422
- if __name__ == "__main__":
423
- print("🚀 Iniciando Dr. Aldo Henrique com RAG...")
424
 
425
  # Carrega ou constrói o vector store na inicialização
426
  load_vector_store()
427
 
428
- print(f"🔑 Token HF encontrado: {HF_TOKEN[:8]}...")
 
 
429
  print("🌐 Interface pronta!")
430
 
 
431
  interface.launch(
432
  server_name="0.0.0.0",
433
  server_port=7860,
434
  share=False,
435
  max_threads=8,
436
  show_error=True
437
- )
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Dr. Aldo Henrique - Chatbot com RAG
4
+ Arquivo principal que inicializa o sistema
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  """
6
 
7
+ import os
8
+ from ai_logic import load_vector_store, HF_TOKEN
9
+ from interface import configurar_interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ def main():
12
+ """Função principal que inicializa o sistema"""
13
+ print("🚀 Iniciando Dr. Aldo Henrique com RAG...")
 
 
 
 
 
 
14
 
15
+ # Verificar se o token HF está configurado
16
+ if not HF_TOKEN:
17
+ print("❌ Erro: Token HF_TOKEN não encontrado nas variáveis de ambiente")
18
+ return
19
 
20
+ print(f"🔑 Token HF encontrado: {HF_TOKEN[:8]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  # Carrega ou constrói o vector store na inicialização
23
  load_vector_store()
24
 
25
+ # Configurar a interface
26
+ interface = configurar_interface()
27
+
28
  print("🌐 Interface pronta!")
29
 
30
+ # Lançar a aplicação
31
  interface.launch(
32
  server_name="0.0.0.0",
33
  server_port=7860,
34
  share=False,
35
  max_threads=8,
36
  show_error=True
37
+ )
38
+
39
+ if __name__ == "__main__":
40
+ main()