Francisco135700 commited on
Commit
4c2b83a
·
verified ·
1 Parent(s): 9b1f265

Upload 3 files

Browse files
Files changed (3) hide show
  1. .env +3 -0
  2. app.py +185 -0
  3. requirements.txt +16 -0
.env ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ OPENROUTER_API_KEY=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
2
+
3
+ GROQ_API_KEY=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
app.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sqlite3
3
+ import tempfile
4
+ import pandas as pd
5
+ from bs4 import BeautifulSoup
6
+ import requests
7
+ from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader
8
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
9
+ from langchain_community.vectorstores import FAISS
10
+ from langchain_community.embeddings import HuggingFaceEmbeddings
11
+ from langchain_openai import ChatOpenAI
12
+ from langchain.chains import ConversationalRetrievalChain
13
+ from langchain.memory import ConversationBufferMemory
14
+ import gradio as gr
15
+ from langchain_community.document_loaders import PyPDFLoader
16
+
17
+ # Configurações
18
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
19
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
20
+
21
+ # Variáveis globais para armazenamento em memória
22
+ pdf_documents = []
23
+ vectordb_pdf = None
24
+ qa_chain_pdf = None
25
+
26
+ # Mapeamento de temas para links sobre a prova
27
+ temas_para_links = {
28
+ "estruturas de prova": "https://www.florence.edu.br/blog/como-e-dividida-a-prova-do-enem/",
29
+ "linguagens, códigos e suas tecnologias": "https://www.todamateria.com.br/linguagens-codigos-e-suas-tecnologias/",
30
+ "Ciências Humanas e suas Tecnologias": "https://www.todamateria.com.br/ciencias-humanas-e-suas-tecnologias/",
31
+ "Ciências da Natureza e suas Tecnologias": "https://www.todamateria.com.br/ciencias-da-natureza-e-suas-tecnologias/",
32
+ "Matemática e suas Tecnologias": "https://fia.com.br/blog/matematica-e-suas-tecnologias/",
33
+ "redação": "https://vestibular.brasilescola.uol.com.br/enem/saiba-tudo-sobre-a-redacao-do-enem.htm"
34
+ }
35
+
36
+ # Inicializa o LLM e a memória
37
+ llm = ChatOpenAI(
38
+ model="deepseek/deepseek-r1:free",
39
+ temperature=0.4,
40
+ openai_api_key=os.environ.get("OPENROUTER_API_KEY"),
41
+ openai_api_base="https://openrouter.ai/api/v1"
42
+ )
43
+ memoria = ConversationBufferMemory(memory_key="chat_history", return_messages=True, output_key="answer")
44
+
45
+ # Cria banco de dados SQLite em memória
46
+ conn = sqlite3.connect(":memory:")
47
+ cursor = conn.cursor()
48
+ cursor.execute('''
49
+ CREATE TABLE IF NOT EXISTS conversas (
50
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
51
+ aluno TEXT,
52
+ pergunta TEXT,
53
+ resposta TEXT,
54
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
55
+ )
56
+ ''')
57
+ conn.commit()
58
+
59
+ # Funções auxiliares
60
+ def processar_pdfs(files):
61
+ global pdf_documents, vectordb_pdf, qa_chain_pdf
62
+
63
+ pdf_documents = []
64
+ if not files:
65
+ return "⚠️ Nenhum arquivo foi enviado."
66
+
67
+ for file in files:
68
+ try:
69
+ # Se for NamedString
70
+ if isinstance(file, gr.FileData) or hasattr(file, "path"):
71
+ tmp_path = file.path
72
+ file_name = getattr(file, 'name', 'Arquivo desconhecido')
73
+ elif isinstance(file, str):
74
+ tmp_path = file # Assume que o 'file' é um caminho direto
75
+ file_name = os.path.basename(file)
76
+ else:
77
+ return f"❌ Formato de arquivo não suportado: {str(type(file))}"
78
+
79
+ if not tmp_path or not os.path.exists(tmp_path):
80
+ return f"❌ Arquivo {file_name} não encontrado no caminho {tmp_path}"
81
+
82
+ # Processa o arquivo PDF
83
+ loader = PyPDFLoader(tmp_path)
84
+ docs = loader.load_and_split(text_splitter)
85
+ pdf_documents.extend(docs)
86
+
87
+ except Exception as e:
88
+ return f"❌ Erro ao processar {file_name if 'file_name' in locals() else 'PDF'}: {str(e)}"
89
+
90
+ if pdf_documents:
91
+ try:
92
+ vectordb_pdf = FAISS.from_documents(pdf_documents, embeddings)
93
+ qa_chain_pdf = ConversationalRetrievalChain.from_llm(
94
+ llm=llm,
95
+ retriever=vectordb_pdf.as_retriever(),
96
+ memory=memoria,
97
+ return_source_documents=True,
98
+ output_key="answer"
99
+ )
100
+ return f"✅ {len(files)} PDF(s) processado(s) - {len(pdf_documents)} trechos!"
101
+ except Exception as e:
102
+ return f"❌ Erro ao criar vetores: {str(e)}"
103
+ return "⚠️ Nenhum dado válido para processar."
104
+
105
+ def responder(pergunta, nome):
106
+ try:
107
+ # Primeiro tenta encontrar na documentação web
108
+ link = identificar_tema(pergunta)
109
+ if link:
110
+ loader = WebBaseLoader(link)
111
+ docs = loader.load()
112
+ if docs:
113
+ documents = text_splitter.split_documents(docs)
114
+ vectordb = FAISS.from_documents(documents, embeddings)
115
+ retriever = vectordb.as_retriever()
116
+ resultado = ConversationalRetrievalChain.from_llm(
117
+ llm=llm,
118
+ retriever=retriever,
119
+ memory=memoria,
120
+ return_source_documents=True,
121
+ output_key="answer"
122
+ ).invoke({"question": pergunta})
123
+ resposta = resultado["answer"].content if hasattr(resultado["answer"], "content") else str(resultado["answer"])
124
+ salvar_conversa(nome, pergunta, resposta)
125
+ return resposta
126
+
127
+ # Se não encontrou na web, tenta nos PDFs locais
128
+ if pdf_documents:
129
+ resultado = qa_chain_pdf.invoke({"question": pergunta})
130
+ resposta = resultado["answer"]
131
+ salvar_conversa(nome, pergunta, resposta)
132
+ return resposta
133
+
134
+ # Fallback para o LLM puro
135
+ resposta = llm.invoke(pergunta).content
136
+ salvar_conversa(nome, pergunta, resposta)
137
+ return resposta
138
+
139
+ except Exception as e:
140
+ return f"❌ Erro ao processar sua pergunta: {str(e)}"
141
+
142
+ def resetar_memoria():
143
+ memoria.clear()
144
+ return "✅ Memória da conversa foi resetada!"
145
+
146
+ def exportar_conversas():
147
+ df = pd.read_sql_query("SELECT * FROM conversas ORDER BY timestamp DESC", conn)
148
+
149
+ # Cria arquivos temporários
150
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as tmp_csv:
151
+ df.to_csv(tmp_csv.name, index=False)
152
+
153
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp_xlsx:
154
+ df.to_excel(tmp_xlsx.name, index=False, engine="openpyxl")
155
+
156
+ return [tmp_csv.name, tmp_xlsx.name]
157
+
158
+ # Interface Gradio
159
+ with gr.Blocks(title="Tutor de ENEM - Hugging Face") as app:
160
+ gr.Markdown("## 🤖 Tutor de ENEM - Busca em Múltiplos PDFs")
161
+
162
+ with gr.Tab("📚 Upload de PDFs"):
163
+ file_input = gr.File(label="Selecione os PDFs", file_count="multiple", file_types=[".pdf"])
164
+ upload_button = gr.Button("Processar PDFs")
165
+ upload_status = gr.Textbox(label="Status")
166
+ upload_button.click(processar_pdfs, inputs=file_input, outputs=upload_status)
167
+
168
+ with gr.Tab("💬 Conversar"):
169
+ with gr.Row():
170
+ nome = gr.Textbox(label="Seu nome (opcional)", placeholder="Ex: João")
171
+ pergunta = gr.Textbox(label="Sua dúvida sobre o ENEM", placeholder="Ex: Como se preparar para o ENEM?")
172
+ resposta = gr.Markdown(value="ℹ️ Aguardando sua pergunta...")
173
+
174
+ with gr.Row():
175
+ botao_enviar = gr.Button("Enviar", variant="primary")
176
+ botao_resetar = gr.Button("🔁 Resetar Memória")
177
+ botao_exportar = gr.Button("📤 Exportar Histórico")
178
+
179
+ botao_enviar.click(fn=responder, inputs=[pergunta, nome], outputs=resposta)
180
+ botao_resetar.click(fn=resetar_memoria, outputs=resposta)
181
+
182
+ export_files = gr.Files(label="Arquivos exportados")
183
+ botao_exportar.click(fn=exportar_conversas, outputs=export_files)
184
+
185
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langchain==0.1.13
2
+ langchain-community==0.0.29
3
+ langchain-openai==0.1.1
4
+ sentence-transformers==2.7.0
5
+ faiss-cpu==1.7.4
6
+ python-dotenv==1.0.1
7
+ gradio==4.24.0
8
+ pandas==2.2.1
9
+ openpyxl==3.1.2
10
+ requests==2.31.0
11
+ pypdf==4.1.0
12
+ beautifulsoup4==4.12.3
13
+ html2text==2020.1.16
14
+ huggingface-hub==0.22.2
15
+ tiktoken==0.6.0
16
+ unstructured==0.13.4