| """ |
| Interface Gradio moderna para o AgentPDF com tema escuro. |
| |
| Esta interface replica o design moderno da imagem fornecida. |
| """ |
|
|
| import os |
| import shutil |
| import gradio as gr |
| from typing import List, Tuple, Optional |
|
|
| from main_graph import get_agent_graph, process_pdf_file, ask_pdf_question |
| from utils.config import Config |
| from utils.logger import main_logger |
|
|
|
|
| class ModernAgentPDFInterface: |
| """Interface moderna para o AgentPDF.""" |
| |
| def __init__(self): |
| """Inicializa a interface.""" |
| self.current_state = None |
| self.chat_history = [] |
| self.pdf_processed = False |
| self.current_pdf_name = None |
| |
| def upload_pdf(self, file) -> Tuple[str, str, List[List[str]]]: |
| """Processa o upload de um arquivo PDF.""" |
| try: |
| if file is None: |
| return "❌ Erro: Nenhum arquivo selecionado", "Selecione um arquivo PDF", [] |
| |
| if not file.name.lower().endswith('.pdf'): |
| return "❌ Erro: Formato inválido", "Apenas arquivos PDF são aceitos", [] |
| |
| |
| upload_dir = Config.UPLOAD_DIR |
| os.makedirs(upload_dir, exist_ok=True) |
| |
| filename = f"uploaded_{os.path.basename(file.name)}" |
| pdf_path = os.path.join(upload_dir, filename) |
| shutil.copy2(file.name, pdf_path) |
| |
| |
| result = process_pdf_file(pdf_path) |
| |
| if result["success"]: |
| self.current_state = result["result"] |
| self.pdf_processed = True |
| self.current_pdf_name = os.path.basename(file.name) |
| self.chat_history = [] |
| |
| welcome_message = [ |
| ["Sistema", f"✅ PDF '{self.current_pdf_name}' processado com sucesso!"] |
| ] |
| |
| return ( |
| "✅ Documento processado", |
| f"PDF: {self.current_pdf_name}\nStatus: Pronto para perguntas", |
| welcome_message |
| ) |
| else: |
| self.pdf_processed = False |
| return "❌ Erro no processamento", f"Erro: {result['error']}", [] |
| |
| except Exception as e: |
| main_logger.error(f"Erro no upload: {e}") |
| return "❌ Erro inesperado", f"Erro: {str(e)}", [] |
| |
| def chat_with_pdf(self, message: str, history: List[List[str]]) -> Tuple[List[List[str]], str]: |
| """Processa uma mensagem do chat.""" |
| try: |
| if not self.pdf_processed or not self.current_state: |
| error_msg = "❌ Faça upload de um PDF primeiro" |
| history.append([message, error_msg]) |
| return history, "" |
| |
| if not message.strip(): |
| return history, "" |
| |
| |
| result = ask_pdf_question(message, self.current_state) |
| |
| if result["success"]: |
| answer = result["answer"] |
| if result.get("result"): |
| self.current_state = result["result"] |
| else: |
| answer = f"❌ Erro: {result['error']}" |
| |
| history.append([message, answer]) |
| return history, "" |
| |
| except Exception as e: |
| main_logger.error(f"Erro no chat: {e}") |
| error_msg = f"❌ Erro inesperado: {str(e)}" |
| history.append([message, error_msg]) |
| return history, "" |
| |
| def clear_chat(self) -> Tuple[List, str]: |
| """Limpa o histórico do chat.""" |
| self.chat_history = [] |
| return [], "" |
| |
| def get_pdf_info(self) -> str: |
| """Retorna informações sobre o PDF atual.""" |
| if not self.pdf_processed or not self.current_pdf_name: |
| return "Nenhum documento carregado" |
| |
| info = f"📄 {self.current_pdf_name}\n" |
| info += f"✅ Processado e indexado\n" |
| |
| if self.current_state: |
| chunks = self.current_state.get("pdf_chunks", []) |
| if chunks: |
| info += f"📊 {len(chunks)} seções" |
| |
| return info |
| |
| def create_interface(self) -> gr.Blocks: |
| """Cria a interface moderna usando templates nativos do Gradio.""" |
|
|
| |
| css = """ |
| .chat-container { |
| height: 70vh !important; |
| } |
| |
| .send-button { |
| height: 56px !important; |
| min-height: 56px !important; |
| } |
| |
| .sidebar-config { |
| max-width: 320px !important; |
| min-width: 320px !important; |
| width: 320px !important; |
| } |
| """ |
|
|
| with gr.Blocks( |
| title="AgentPDF", |
| theme=gr.themes.Soft( |
| primary_hue="blue", |
| secondary_hue="slate", |
| neutral_hue="slate" |
| ), |
| css=css |
| ) as interface: |
|
|
| with gr.Row(): |
| |
| with gr.Column(scale=1, elem_classes=["sidebar-config"]): |
| gr.Markdown("## ⚙️ Configurações") |
|
|
| with gr.Group(): |
| file_upload = gr.File( |
| label="Selecione um PDF", |
| file_types=[".pdf"], |
| type="filepath" |
| ) |
|
|
| upload_btn = gr.Button( |
| "🚀 Processar PDF", |
| variant="primary", |
| size="lg" |
| ) |
|
|
| with gr.Group(): |
| upload_status = gr.Textbox( |
| label="📊 Status", |
| interactive=False, |
| placeholder="Aguardando upload...", |
| lines=1 |
| ) |
|
|
| pdf_info = gr.Textbox( |
| label="📄 Informações", |
| interactive=False, |
| value="Nenhum documento carregado", |
| lines=2 |
| ) |
|
|
|
|
| |
| with gr.Column(scale=3): |
| gr.Markdown("## 💬 Conversa") |
|
|
| chatbot = gr.Chatbot( |
| elem_classes=["chat-container"], |
| show_copy_button=True, |
| bubble_full_width=False |
| ) |
|
|
| with gr.Row(): |
| msg_input = gr.Textbox( |
| placeholder="Digite sua pergunta sobre o PDF...", |
| show_label=False, |
| scale=5, |
| lines=1 |
| ) |
|
|
| send_btn = gr.Button( |
| "📤", |
| variant="primary", |
| scale=1, |
| elem_classes=["send-button"] |
| ) |
|
|
| |
| upload_btn.click( |
| fn=self.upload_pdf, |
| inputs=[file_upload], |
| outputs=[upload_status, pdf_info, chatbot], |
| show_progress=True |
| ) |
|
|
| send_btn.click( |
| fn=self.chat_with_pdf, |
| inputs=[msg_input, chatbot], |
| outputs=[chatbot, msg_input], |
| show_progress=True |
| ) |
|
|
| msg_input.submit( |
| fn=self.chat_with_pdf, |
| inputs=[msg_input, chatbot], |
| outputs=[chatbot, msg_input], |
| show_progress=True |
| ) |
|
|
| return interface |
|
|
|
|
| def create_modern_gradio_app() -> gr.Blocks: |
| """Cria a aplicação Gradio moderna.""" |
| interface = ModernAgentPDFInterface() |
| return interface.create_interface() |
|
|
|
|
| if __name__ == "__main__": |
| app = create_modern_gradio_app() |
| app.launch( |
| server_name="0.0.0.0", |
| server_port=Config.GRADIO_PORT, |
| share=Config.GRADIO_SHARE, |
| show_error=True |
| ) |
|
|