enem-teste-01 / app.py
Francisco135700's picture
Update app.py (#1)
f5b2386 verified
import os
import sqlite3
import tempfile
import pandas as pd
from bs4 import BeautifulSoup
import requests
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
import gradio as gr
from langchain_community.document_loaders import PyPDFLoader
# Configurações
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
# Variáveis globais para armazenamento em memória
pdf_documents = []
vectordb_pdf = None
qa_chain_pdf = None
# Mapeamento de temas para links sobre a prova
temas_para_links = {
"estruturas de prova": "https://www.florence.edu.br/blog/como-e-dividida-a-prova-do-enem/",
"linguagens, códigos e suas tecnologias": "https://www.todamateria.com.br/linguagens-codigos-e-suas-tecnologias/",
"Ciências Humanas e suas Tecnologias": "https://www.todamateria.com.br/ciencias-humanas-e-suas-tecnologias/",
"Ciências da Natureza e suas Tecnologias": "https://www.todamateria.com.br/ciencias-da-natureza-e-suas-tecnologias/",
"Matemática e suas Tecnologias": "https://fia.com.br/blog/matematica-e-suas-tecnologias/",
"redação": "https://vestibular.brasilescola.uol.com.br/enem/saiba-tudo-sobre-a-redacao-do-enem.htm"
}
# Inicializa o LLM e a memória
llm = ChatOpenAI(
model="deepseek/deepseek-r1:free",
temperature=0.4,
openai_api_key=os.getenv("OPENROUTER_API_KEY"),
openai_api_base="https://openrouter.ai/api/v1"
)
memoria = ConversationBufferMemory(memory_key="chat_history", return_messages=True, output_key="answer")
# Cria banco de dados SQLite em memória
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS conversas (
id INTEGER PRIMARY KEY AUTOINCREMENT,
aluno TEXT,
pergunta TEXT,
resposta TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
# Funções auxiliares
def processar_pdfs(files):
global pdf_documents, vectordb_pdf, qa_chain_pdf
pdf_documents = []
if not files:
return "⚠️ Nenhum arquivo foi enviado."
for file in files:
try:
# Se for NamedString
if isinstance(file, gr.FileData) or hasattr(file, "path"):
tmp_path = file.path
file_name = getattr(file, 'name', 'Arquivo desconhecido')
elif isinstance(file, str):
tmp_path = file # Assume que o 'file' é um caminho direto
file_name = os.path.basename(file)
else:
return f"❌ Formato de arquivo não suportado: {str(type(file))}"
if not tmp_path or not os.path.exists(tmp_path):
return f"❌ Arquivo {file_name} não encontrado no caminho {tmp_path}"
# Processa o arquivo PDF
loader = PyPDFLoader(tmp_path)
docs = loader.load_and_split(text_splitter)
pdf_documents.extend(docs)
except Exception as e:
return f"❌ Erro ao processar {file_name if 'file_name' in locals() else 'PDF'}: {str(e)}"
if pdf_documents:
try:
vectordb_pdf = FAISS.from_documents(pdf_documents, embeddings)
qa_chain_pdf = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=vectordb_pdf.as_retriever(),
memory=memoria,
return_source_documents=True,
output_key="answer"
)
return f"✅ {len(files)} PDF(s) processado(s) - {len(pdf_documents)} trechos!"
except Exception as e:
return f"❌ Erro ao criar vetores: {str(e)}"
return "⚠️ Nenhum dado válido para processar."
def responder(pergunta, nome):
try:
# Primeiro tenta encontrar na documentação web
link = identificar_tema(pergunta)
if link:
loader = WebBaseLoader(link)
docs = loader.load()
if docs:
documents = text_splitter.split_documents(docs)
vectordb = FAISS.from_documents(documents, embeddings)
retriever = vectordb.as_retriever()
resultado = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memoria,
return_source_documents=True,
output_key="answer"
).invoke({"question": pergunta})
resposta = resultado["answer"].content if hasattr(resultado["answer"], "content") else str(resultado["answer"])
salvar_conversa(nome, pergunta, resposta)
return resposta
# Se não encontrou na web, tenta nos PDFs locais
if pdf_documents:
resultado = qa_chain_pdf.invoke({"question": pergunta})
resposta = resultado["answer"]
salvar_conversa(nome, pergunta, resposta)
return resposta
# Fallback para o LLM puro
resposta = llm.invoke(pergunta).content
salvar_conversa(nome, pergunta, resposta)
return resposta
except Exception as e:
return f"❌ Erro ao processar sua pergunta: {str(e)}"
def resetar_memoria():
memoria.clear()
return "✅ Memória da conversa foi resetada!"
def exportar_conversas():
df = pd.read_sql_query("SELECT * FROM conversas ORDER BY timestamp DESC", conn)
# Cria arquivos temporários
with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as tmp_csv:
df.to_csv(tmp_csv.name, index=False)
with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp_xlsx:
df.to_excel(tmp_xlsx.name, index=False, engine="openpyxl")
return [tmp_csv.name, tmp_xlsx.name]
# Interface Gradio
with gr.Blocks(title="Tutor de ENEM - Hugging Face") as app:
gr.Markdown("## 🤖 Tutor de ENEM - Busca em Múltiplos PDFs")
with gr.Tab("📚 Upload de PDFs"):
file_input = gr.File(label="Selecione os PDFs", file_count="multiple", file_types=[".pdf"])
upload_button = gr.Button("Processar PDFs")
upload_status = gr.Textbox(label="Status")
upload_button.click(processar_pdfs, inputs=file_input, outputs=upload_status)
with gr.Tab("💬 Conversar"):
with gr.Row():
nome = gr.Textbox(label="Seu nome (opcional)", placeholder="Ex: João")
pergunta = gr.Textbox(label="Sua dúvida sobre o ENEM", placeholder="Ex: Como se preparar para o ENEM?")
resposta = gr.Markdown(value="ℹ️ Aguardando sua pergunta...")
with gr.Row():
botao_enviar = gr.Button("Enviar", variant="primary")
botao_resetar = gr.Button("🔁 Resetar Memória")
botao_exportar = gr.Button("📤 Exportar Histórico")
botao_enviar.click(fn=responder, inputs=[pergunta, nome], outputs=resposta)
botao_resetar.click(fn=resetar_memoria, outputs=resposta)
export_files = gr.Files(label="Arquivos exportados")
botao_exportar.click(fn=exportar_conversas, outputs=export_files)
app.launch()