Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from llama_index.llms.groq import Groq
|
2 |
+
from llama_index.core import PromptTemplate
|
3 |
+
from llama_index.experimental.query_engine.pandas import PandasInstructionParser
|
4 |
+
from llama_index.core.query_pipeline import (QueryPipeline as QP, Link, InputComponent)
|
5 |
+
import gradio as gr
|
6 |
+
import pandas as pd
|
7 |
+
from fpdf import FPDF
|
8 |
+
from datetime import datetime
|
9 |
+
import os
|
10 |
+
|
11 |
+
# API KEY do GROQ
|
12 |
+
api_key = os.getenv("secret_key")
|
13 |
+
|
14 |
+
# Configuração inicial do QP
|
15 |
+
llm = Groq(model="llama3-70b-8192", api_key=api_key)
|
16 |
+
|
17 |
+
# Pipeline de consulta
|
18 |
+
def descrição_colunas(df):
|
19 |
+
descrição = '\n'.join([f"`{col}`: {str(df[col].dtype)}" for col in df.columns])
|
20 |
+
return "Aqui estão os detalhes das colunas do dataframe:\n" + descrição
|
21 |
+
|
22 |
+
def pipeline_consulta(df):
|
23 |
+
instruction_str = (
|
24 |
+
"1. Converta a consulta para código Python executável usando Pandas.\n"
|
25 |
+
"2. A linha final do código deve ser uma expressão Python que possa ser chamada com a função `eval()`.\n"
|
26 |
+
"3. O código deve representar uma solução para a consulta.\n"
|
27 |
+
"4. IMPRIMA APENAS A EXPRESSÃO.\n"
|
28 |
+
"5. Não coloque a expressão entre aspas.\n")
|
29 |
+
|
30 |
+
pandas_prompt_str = (
|
31 |
+
"Você está trabalhando com um dataframe do pandas em Python chamado `df`.\n"
|
32 |
+
"{colunas_detalhes}\n\n"
|
33 |
+
"Este é o resultado de `print(df.head())`:\n"
|
34 |
+
"{df_str}\n\n"
|
35 |
+
"Siga estas instruções:\n"
|
36 |
+
"{instruction_str}\n"
|
37 |
+
"Consulta: {query_str}\n\n"
|
38 |
+
"Expressão:"
|
39 |
+
)
|
40 |
+
|
41 |
+
response_synthesis_prompt_str = (
|
42 |
+
"Dada uma pergunta de entrada, atue como analista de dados e elabore uma resposta a partir dos resultados da consulta.\n"
|
43 |
+
"Responda de forma natural, sem introduções como 'A resposta é:' ou algo semelhante.\n"
|
44 |
+
"Consulta: {query_str}\n\n"
|
45 |
+
"Instruções do Pandas (opcional):\n{pandas_instructions}\n\n"
|
46 |
+
"Saída do Pandas: {pandas_output}\n\n"
|
47 |
+
"Resposta: \n\n"
|
48 |
+
"Ao final, exibir o código usado em para gerar a resposta, no formato: O código utilizado foi `{pandas_instructions}`"
|
49 |
+
)
|
50 |
+
|
51 |
+
pandas_prompt = PromptTemplate(pandas_prompt_str).partial_format(
|
52 |
+
instruction_str=instruction_str,
|
53 |
+
df_str=df.head(5),
|
54 |
+
colunas_detalhes=descrição_colunas(df)
|
55 |
+
)
|
56 |
+
|
57 |
+
pandas_output_parser = PandasInstructionParser(df)
|
58 |
+
response_synthesis_prompt = PromptTemplate(response_synthesis_prompt_str)
|
59 |
+
|
60 |
+
# Criação do QueryPipeline
|
61 |
+
|
62 |
+
qp = QP(
|
63 |
+
modules={
|
64 |
+
"input": InputComponent(),
|
65 |
+
"pandas_prompt": pandas_prompt,
|
66 |
+
"llm1": llm,
|
67 |
+
"pandas_output_parser": pandas_output_parser,
|
68 |
+
"response_synthesis_prompt": response_synthesis_prompt,
|
69 |
+
"llm2": llm,
|
70 |
+
},
|
71 |
+
verbose=True,
|
72 |
+
)
|
73 |
+
qp.add_chain(["input", "pandas_prompt", "llm1", "pandas_output_parser"])
|
74 |
+
qp.add_links(
|
75 |
+
[
|
76 |
+
Link("input", "response_synthesis_prompt", dest_key="query_str"),
|
77 |
+
Link("llm1", "response_synthesis_prompt", dest_key="pandas_instructions"),
|
78 |
+
Link("pandas_output_parser", "response_synthesis_prompt", dest_key="pandas_output"),
|
79 |
+
]
|
80 |
+
)
|
81 |
+
qp.add_link("response_synthesis_prompt", "llm2")
|
82 |
+
return qp
|
83 |
+
|
84 |
+
# Função para carregar os dados
|
85 |
+
def carregar_dados(caminho_arquivo, df_estado):
|
86 |
+
if caminho_arquivo is None or caminho_arquivo == "":
|
87 |
+
return "Por favor, faça o upload de um arquivo CSV para analisar.", pd.DataFrame(), df_estado
|
88 |
+
try:
|
89 |
+
df = pd.read_csv(caminho_arquivo)
|
90 |
+
return "Arquivo carregado com sucesso!", df.head(), df
|
91 |
+
except Exception as e:
|
92 |
+
return f"Erro ao carregar arquivo: {str(e)}", pd.DataFrame(), df_estado
|
93 |
+
|
94 |
+
# Função para processar a pergunta
|
95 |
+
def processar_pergunta(pergunta, df_estado):
|
96 |
+
if df_estado is not None and pergunta:
|
97 |
+
qp = pipeline_consulta(df_estado)
|
98 |
+
resposta = qp.run(query_str=pergunta)
|
99 |
+
return resposta.message.content
|
100 |
+
return ""
|
101 |
+
|
102 |
+
# Função para adicionar a pergunta e a resposta ao histórico
|
103 |
+
def add_historico(pergunta, resposta, historico_estado):
|
104 |
+
if pergunta and resposta:
|
105 |
+
historico_estado.append((pergunta, resposta))
|
106 |
+
gr.Info("Adicionado ao PDF!", duration=2)
|
107 |
+
return historico_estado
|
108 |
+
|
109 |
+
# Função para gerar o PDF
|
110 |
+
def gerar_pdf(historico_estado):
|
111 |
+
if not historico_estado:
|
112 |
+
return "Nenhum dado para adicionar ao PDF.", None
|
113 |
+
|
114 |
+
# Gerar nome de arquivo com timestamp
|
115 |
+
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
116 |
+
caminho_pdf = f"relatorio_perguntas_respostas_{timestamp}.pdf"
|
117 |
+
|
118 |
+
pdf = FPDF()
|
119 |
+
pdf.add_page()
|
120 |
+
pdf.set_auto_page_break(auto=True, margin=15)
|
121 |
+
|
122 |
+
for pergunta, resposta in historico_estado:
|
123 |
+
pdf.set_font("Arial", 'B', 14)
|
124 |
+
pdf.multi_cell(0, 8, txt=pergunta)
|
125 |
+
pdf.ln(2)
|
126 |
+
pdf.set_font("Arial", '', 12)
|
127 |
+
pdf.multi_cell(0, 8, txt=resposta)
|
128 |
+
pdf.ln(6)
|
129 |
+
|
130 |
+
pdf.output(caminho_pdf)
|
131 |
+
return caminho_pdf
|
132 |
+
|
133 |
+
# Função para limpar a pergunta e a resposta
|
134 |
+
def limpar_pergunta_resposta():
|
135 |
+
return "", ""
|
136 |
+
|
137 |
+
# Função para resetar a aplicação
|
138 |
+
def resetar_aplicação():
|
139 |
+
return None, "A aplicação foi resetada. Por favor, faça upload de um novo arquivo CSV.", pd.DataFrame(), "", None, [], ""
|
140 |
+
|
141 |
+
# Criação da interface gradio
|
142 |
+
with gr.Blocks(theme='Soft') as app:
|
143 |
+
|
144 |
+
# Título da app
|
145 |
+
gr.Markdown("# Analisando os dados🔎🎲")
|
146 |
+
|
147 |
+
# Descrição
|
148 |
+
gr.Markdown('''
|
149 |
+
Carregue um arquivo CSV e faça perguntas sobre os dados. A cada pergunta, você poderá
|
150 |
+
visualizar a resposta e, se desejar, adicionar essa interação ao PDF final, basta clicar
|
151 |
+
em "Adicionar ao histórico do PDF". Para fazer uma nova pergunta, clique em "Limpar pergunta e resultado".
|
152 |
+
Após definir as perguntas e respostas no histórico, clique em "Gerar PDF". Assim, será possível
|
153 |
+
baixar um PDF com o registro completo das suas interações. Se você quiser analisar um novo dataset,
|
154 |
+
basta clicar em "Quero analisar outro dataset" ao final da página.
|
155 |
+
''')
|
156 |
+
|
157 |
+
# Campo de entrada de arquivos
|
158 |
+
input_arquivo = gr.File(file_count="single", type="filepath", label="Upload CSV")
|
159 |
+
|
160 |
+
# Status de upload
|
161 |
+
upload_status = gr.Textbox(label="Status do Upload:")
|
162 |
+
|
163 |
+
# Tabela de dados
|
164 |
+
tabela_dados = gr.DataFrame()
|
165 |
+
|
166 |
+
# Exemplos de perguntas
|
167 |
+
gr.Markdown("""
|
168 |
+
Exemplos de perguntas:
|
169 |
+
1. Qual é o número de registros no arquivo?
|
170 |
+
2. Quais são os tipos de dados das colunas?
|
171 |
+
3. Quais são as estatísticas descritivas das colunas numéricas?
|
172 |
+
""")
|
173 |
+
|
174 |
+
# Campo de entrada de texto
|
175 |
+
input_pergunta = gr.Textbox(label="Digite sua pergunta sobre os dados")
|
176 |
+
|
177 |
+
# Botão de envio posicionado após a pergunta
|
178 |
+
botao_submeter = gr.Button("Enviar")
|
179 |
+
|
180 |
+
# Componente de resposta
|
181 |
+
output_resposta = gr.Textbox(label="Resposta")
|
182 |
+
|
183 |
+
# Botões para limpar a pergunta e a resposta, adicionar ao historico e gerar o PDF
|
184 |
+
with gr.Row():
|
185 |
+
botao_limpeza = gr.Button("Limpar pergunta e resultado")
|
186 |
+
botao_add_pdf = gr.Button("Adicionar ao histórico do PDF")
|
187 |
+
botao_gerar_pdf = gr.Button("Gerar PDF")
|
188 |
+
|
189 |
+
# Componente de download
|
190 |
+
arquivo_pdf = gr.File(label="Download do PDF")
|
191 |
+
|
192 |
+
# Botão para resetar a aplicação
|
193 |
+
botao_resetar = gr.Button("Quero analisar outro dataset!")
|
194 |
+
|
195 |
+
# Gerenciamento de estados
|
196 |
+
df_estado = gr.State(value=None)
|
197 |
+
historico_estado = gr.State(value=[])
|
198 |
+
|
199 |
+
# Conectando funções aos componentes
|
200 |
+
input_arquivo.change(fn=carregar_dados, inputs=[input_arquivo, df_estado], outputs=[upload_status, tabela_dados, df_estado])
|
201 |
+
botao_submeter.click(fn=processar_pergunta, inputs=[input_pergunta, df_estado], outputs=output_resposta)
|
202 |
+
botao_limpeza.click(fn=limpar_pergunta_resposta, inputs=[], outputs=[input_pergunta, output_resposta])
|
203 |
+
botao_add_pdf.click(fn=add_historico, inputs=[input_pergunta, output_resposta, historico_estado], outputs=historico_estado)
|
204 |
+
botao_gerar_pdf.click(fn=gerar_pdf, inputs=[historico_estado], outputs=arquivo_pdf)
|
205 |
+
botao_resetar.click(fn=resetar_aplicação, inputs=[], outputs=[input_arquivo, upload_status, tabela_dados, output_resposta, arquivo_pdf, historico_estado, input_pergunta])
|
206 |
+
|
207 |
+
if __name__ == "__main__":
|
208 |
+
app.launch()
|