Create workflow.py
Browse files- workflow.py +87 -0
workflow.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# workflow.py
|
| 2 |
+
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from typing import Any
|
| 5 |
+
from pydantic import ConfigDict
|
| 6 |
+
|
| 7 |
+
from llama_index.core import Settings
|
| 8 |
+
from llama_index.core.workflow import Workflow, Event, StartEvent as BaseStartEvent, StopEvent, step
|
| 9 |
+
from llama_index.llms.groq import Groq
|
| 10 |
+
|
| 11 |
+
# Importações de nossos módulos locais
|
| 12 |
+
from config import pandas_prompt_str, instruction_str, RESPONSE_SYNTHESIS_PROMPT_STR
|
| 13 |
+
from utils import descricao_colunas, limpar_codigo_pandas
|
| 14 |
+
|
| 15 |
+
# --- MODELOS DE EVENTOS DO WORKFLOW ---
|
| 16 |
+
class StartEvent(BaseStartEvent):
|
| 17 |
+
query: str
|
| 18 |
+
df: pd.DataFrame
|
| 19 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 20 |
+
|
| 21 |
+
class CodeEvent(Event):
|
| 22 |
+
pandas_prompt: str; query: str; df: pd.DataFrame
|
| 23 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 24 |
+
|
| 25 |
+
class OutputEvent(Event):
|
| 26 |
+
pandas_code: str; query: str; df: pd.DataFrame
|
| 27 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 28 |
+
|
| 29 |
+
class ExecutedEvent(Event):
|
| 30 |
+
pandas_code: str; pandas_output: Any; query: str; df: pd.DataFrame
|
| 31 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 32 |
+
|
| 33 |
+
# --- CLASSE WORKFLOW PRINCIPAL ---
|
| 34 |
+
class PandasWorkflow(Workflow):
|
| 35 |
+
@step
|
| 36 |
+
async def iniciar_processamento(self, ev: StartEvent) -> CodeEvent:
|
| 37 |
+
colunas_info = descricao_colunas(ev.df)
|
| 38 |
+
prompt_text = pandas_prompt_str.format(
|
| 39 |
+
colunas_detalhes=colunas_info, df_str=ev.df.head(5).to_string(),
|
| 40 |
+
instruction_str=instruction_str, query_str=ev.query
|
| 41 |
+
)
|
| 42 |
+
return CodeEvent(pandas_prompt=prompt_text, query=ev.query, df=ev.df)
|
| 43 |
+
|
| 44 |
+
@step
|
| 45 |
+
async def gerar_codigo(self, ev: CodeEvent) -> OutputEvent:
|
| 46 |
+
response = await Settings.llm.acomplete(ev.pandas_prompt)
|
| 47 |
+
codigo_limpo = limpar_codigo_pandas(str(response).strip())
|
| 48 |
+
print(f"✅ Código gerado: {codigo_limpo}")
|
| 49 |
+
return OutputEvent(pandas_code=codigo_limpo, query=ev.query, df=ev.df)
|
| 50 |
+
|
| 51 |
+
@step
|
| 52 |
+
async def executar_codigo(self, ev: OutputEvent) -> ExecutedEvent:
|
| 53 |
+
try:
|
| 54 |
+
resultado = eval(ev.pandas_code, {"__builtins__": {}}, {"df": ev.df, "pd": pd})
|
| 55 |
+
except Exception as e:
|
| 56 |
+
resultado = f"Erro ao executar o código: {str(e)}"
|
| 57 |
+
return ExecutedEvent(
|
| 58 |
+
pandas_code=ev.pandas_code, pandas_output=resultado,
|
| 59 |
+
query=ev.query, df=ev.df
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
@step
|
| 63 |
+
async def finalizar_e_sintetizar(self, ev: ExecutedEvent) -> StopEvent:
|
| 64 |
+
if isinstance(ev.pandas_output, str) and "Erro" in ev.pandas_output:
|
| 65 |
+
resposta_final = f"Não foi possível processar: {ev.pandas_output}"
|
| 66 |
+
else:
|
| 67 |
+
prompt = RESPONSE_SYNTHESIS_PROMPT_STR.format(
|
| 68 |
+
query_str=ev.query, pandas_instructions=ev.pandas_code,
|
| 69 |
+
pandas_output=str(ev.pandas_output)
|
| 70 |
+
)
|
| 71 |
+
response = await Settings.llm.acomplete(prompt)
|
| 72 |
+
resposta_final = str(response).strip()
|
| 73 |
+
return StopEvent(result={"resposta_final": resposta_final})
|
| 74 |
+
|
| 75 |
+
# --- FUNÇÃO DE EXECUÇÃO ---
|
| 76 |
+
async def executar_consulta(query: str, df_local: pd.DataFrame):
|
| 77 |
+
if df_local is None: return {"resposta_final": "Erro: DataFrame não carregado."}
|
| 78 |
+
if not query.strip(): return {"resposta_final": "Erro: Consulta vazia."}
|
| 79 |
+
try:
|
| 80 |
+
wf = PandasWorkflow()
|
| 81 |
+
resultado_final = await wf.run(query=query, df=df_local)
|
| 82 |
+
if not isinstance(resultado_final, dict):
|
| 83 |
+
raise TypeError(f"Workflow retornou tipo inesperado: {type(resultado_final)}")
|
| 84 |
+
return resultado_final
|
| 85 |
+
except Exception as e:
|
| 86 |
+
import traceback; traceback.print_exc()
|
| 87 |
+
return {"resposta_final": f"Erro crítico no workflow: {str(e)}"}
|