#!/usr/bin/env python # coding=utf-8 # Copyright 2024 The HuggingFace Inc. team. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import re import json from typing import Optional, List import gradio as gr from smolagents.agents import ActionStep, MultiStepAgent from smolagents.memory import MemoryStep from smolagents.utils import _is_package_available def get_step_logs(step_log: MemoryStep): """ Gera os logs intermediários do agente no formato [usuário, assistente]. O usuário é 'None' pois são mensagens do sistema/assistente. """ if isinstance(step_log, ActionStep): # Exibe o número do passo yield [None, f"**--- Passo {step_log.step_number} ---**"] # Exibe o pensamento/raciocínio do LLM if hasattr(step_log, "model_output") and step_log.model_output is not None: model_output = step_log.model_output.strip() model_output = re.sub(r"```\s*", "```", model_output) yield [None, model_output] # Exibe as chamadas de ferramentas if hasattr(step_log, "tool_calls") and step_log.tool_calls is not None: tool_call = step_log.tool_calls[0] content = str(tool_call.arguments).strip() if tool_call.name == "python_interpreter": content = re.sub(r"\s*\s*", "", content).strip() content = f"```python\n{content}\n```" yield [None, f"**🛠️ Ferramenta Usada: {tool_call.name}**\n{content}"] # Mostra os logs de execução ou erros if hasattr(step_log, "error") and step_log.error is not None: yield [None, f"**💥 Erro na Execução:**\n```\n{str(step_log.error)}\n```"] elif hasattr(step_log, "observations") and step_log.observations and step_log.observations.strip(): log_content = step_log.observations.strip() yield [None, f"**📝 Observação / Log de Execução:**\n```\n{log_content}\n```"] def render_single_question(q_dict): """Renderiza um único dicionário de questão para uma tupla (texto, caminho_da_imagem).""" text = q_dict.get("enunciado", "") if "tabela_markdown" in q_dict: text += "\n\n" + q_dict["tabela_markdown"] if "alternativas" in q_dict and isinstance(q_dict["alternativas"], dict): text += "\n\n**Alternativas:**\n" for key, value in q_dict["alternativas"].items(): text += f"**{key})** {value}\n" if "resposta_correta" in q_dict: text += f"\n**Resposta Correta:** {q_dict['resposta_correta']}" if "resolucao_passo_a_passo" in q_dict: text += "\n\n**Resolução:**\n" + "\n".join(q_dict['resolucao_passo_a_passo']) img_key = q_dict.get("url_grafico") or q_dict.get("url_imagem") img_path = None if img_key: img_path = img_key.split(":")[-1].strip() if not os.path.exists(img_path): img_path = None return text, img_path class GradioUI: """Interface para lançar o agente no Gradio.""" def __init__(self, agent: MultiStepAgent): self.agent = agent def interact_with_agent(self, prompt, chat_history): """Função que gerencia a interação com o formato [pergunta, resposta].""" if not prompt.strip(): return chat_history, "" #Limpa a memória antes de cada execução para garantir novas respostas. self.agent.memory.reset() chat_history.append([prompt, ""]) yield chat_history, "" # Limpa a caixa de texto step_log = None # Mostra os passos intermediários do agente for step_log in self.agent.run(task=prompt, stream=True): for user_msg, assistant_msg in get_step_logs(step_log): chat_history.append([user_msg, assistant_msg]) yield chat_history, "" # Renderiza a resposta final if step_log: final_answer = step_log.final_answer if hasattr(step_log, 'final_answer') else step_log # Remove o log do último passo para não poluir a resposta final chat_history.pop() chat_history.append([None, "--- \n\n ### Resposta Final"]) if isinstance(final_answer, list): # Se for uma lista, processa e exibe cada item individualmente for i, item in enumerate(final_answer): if isinstance(item, dict): q_text, q_image_path = render_single_question(item) chat_history.append([None, f"#### Questão {i+1} de {len(final_answer)}\n{q_text}"]) if q_image_path: chat_history.append([None, (q_image_path,)]) yield chat_history, "" elif isinstance(final_answer, dict): # Se for um item único q_text, q_image_path = render_single_question(final_answer) chat_history.append([None, q_text]) if q_image_path: chat_history.append([None, (q_image_path,)]) yield chat_history, "" else: chat_history.append([None, f"**Resposta Final:** {str(final_answer)}"]) yield chat_history, "" else: chat_history.append([None, "**ERRO:** O agente não produziu nenhuma saída."]) yield chat_history, "" def launch(self, **kwargs): with gr.Blocks(fill_height=True) as demo: gr.Markdown("# Agente Gerador de Questões de Matemática") #chatbot = gr.Chatbot(label="Interação com o Agente", bubble_full_width=True, height=700) chatbot = gr.Chatbot( label="Interação com o Agente", bubble_full_width=True, height=700, #instrui o chatbot a procurar por \(...\) e renderizar como matemática latex_delimiters=[{"left": "\\(", "right": "\\)", "display": False}] ) text_input = gr.Textbox(lines=1, label="Sua Tarefa", placeholder="Ex: habilidade_bncc:(EM13MAT501) Investigar ..., nivel_dificuldade: médio (fácil ou difícil), quantidade:número, tipo_questao: multipla_escolha ou dissertativa, observação: aqui é um campo facultativo") text_input.submit(self.interact_with_agent, [text_input, chatbot], [chatbot, text_input]) demo.launch(debug=True, **kwargs) __all__ = ["GradioUI"]