import os import socket import gradio as gr import logging from pathlib import Path from typing import List, Dict, Optional, Tuple from datetime import datetime from dotenv import load_dotenv import google.generativeai as genai from document_agent import DocumentAgent, DocumentRepository # Configuração de logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Carrega variáveis de ambiente load_dotenv() def find_free_port(start_port: int = 7860, max_tries: int = 50) -> int: """Encontra uma porta livre para o servidor""" for port in range(start_port, start_port + max_tries): try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(('', port)) return port except OSError: continue raise OSError(f"Não foi possível encontrar uma porta livre entre {start_port} e {start_port + max_tries}") class DocumentAnalysisUI: def __init__(self): """Inicializa a interface do sistema""" try: # Inicializa componentes self.agent = DocumentAgent() self.repository = DocumentRepository() # Estado da interface self.processed_files = set() self.conversation_history = [] # Cria diretório para documentos processados se não existir Path("processed_documents").mkdir(exist_ok=True) logger.info("Interface inicializada com sucesso!") except Exception as e: logger.error(f"Erro na inicialização da interface: {str(e)}") raise def reset_state(self): """Reseta o estado do sistema""" try: # Limpa a memória do DocumentAgent self.agent = DocumentAgent() # Limpa o repositório self.repository = DocumentRepository() # Limpa o histórico de arquivos processados self.processed_files.clear() # Limpa o histórico de conversas self.conversation_history.clear() # Limpa os arquivos processados for file in Path("processed_documents").glob("*.json"): file.unlink() logger.info("Estado do sistema resetado com sucesso") except Exception as e: logger.error(f"Erro ao limpar estado: {str(e)}") raise def process_files(self, file_paths: List[str], chatbot) -> tuple[str, List]: """Processa arquivos enviados pelo usuário""" if not file_paths: return "Nenhum arquivo selecionado.", [] try: # Reseta o estado antes de processar novos arquivos self.reset_state() total_chunks = 0 processed_files = [] for file_path in file_paths: if file_path and Path(file_path).exists(): if file_path not in self.processed_files: # Processa o documento logger.info(f"Processando arquivo: {file_path}") num_chunks = self.agent.process_document(file_path) total_chunks += num_chunks self.processed_files.add(file_path) processed_files.append(Path(file_path).name) logger.info(f"Arquivo processado: {file_path}") if processed_files: return (f""" Processamento concluído com sucesso! 📚 Arquivos processados: {', '.join(processed_files)} 📑 Total de chunks gerados: {total_chunks} 🔍 Sistema pronto para responder perguntas! O histórico anterior foi limpo. Você pode começar uma nova conversa. """, []) else: return "Nenhum novo arquivo para processar.", [] except Exception as e: logger.error(f"Erro no processamento: {str(e)}") return f"❌ Erro no processamento: {str(e)}", [] def get_response(self, message: str, history: List[Dict]) -> List[Dict]: """Processa pergunta e retorna resposta""" try: if not self.processed_files: return history + [ {"role": "user", "content": message}, {"role": "assistant", "content": "Por favor, faça upload de documentos primeiro."} ] logger.info(f"Processando pergunta: {message}") # Obtém resposta do agente answer = self.agent.answer_question(message) logger.info("Resposta gerada com sucesso") return history + [ {"role": "user", "content": message}, {"role": "assistant", "content": answer} ] except Exception as e: logger.error(f"Erro ao gerar resposta: {str(e)}") return history + [ {"role": "user", "content": message}, {"role": "assistant", "content": f"Erro ao gerar resposta: {str(e)}"} ] def update_stats_display(self) -> str: """Atualiza display de estatísticas""" stats = { "total_documents": len(self.processed_files), "total_chunks": len(list(Path("processed_documents").glob("*.json"))), "last_update": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } return f""" 📊 Estatísticas do Sistema: 📚 Documentos processados: {stats.get('total_documents', 0)} 📑 Total de chunks: {stats.get('total_chunks', 0)} 🕒 Última atualização: {stats.get('last_update', 'N/A')} """ def create_interface() -> gr.Blocks: """Cria interface Gradio""" try: ui = DocumentAnalysisUI() logger.info("Interface criada com sucesso") except Exception as e: logger.error(f"Erro ao criar interface: {str(e)}") raise with gr.Blocks( css=""" :root { --background-fill-primary: #ffffff; --text-color: #000000; --button-primary-background-fill: #ffbe00; --button-primary-text-color: #000000; --border-color-primary: #ffbe00; } * { font-family: Helvetica, Arial, sans-serif !important; } .gr-button-primary { transition: all 0.3s ease; } .gr-button-primary:hover { background-color: #e5ab00 !important; } .chatbot .message.user { background-color: #f5f5f5 !important; } .chatbot .message.bot { background-color: #fff9e6 !important; } .gr-button-secondary { background-color: white !important; color: black !important; border: 1px solid #ffbe00 !important; } .gr-button-secondary:hover { background-color: #fff9e6 !important; } .gr-box { border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important; } .upload-box { border: 2px dashed #ffbe00 !important; padding: 20px !important; } .gr-input, .gr-textbox { border: 1px solid #e0e0e0 !important; border-radius: 6px !important; } .gr-input:focus, .gr-textbox:focus { border-color: #ffbe00 !important; box-shadow: 0 0 0 2px rgba(255,190,0,0.2) !important; } """ ) as demo: gr.Markdown(""" # 📚 Sistema de Análise de Documentos Análise inteligente de documentos com IA avançada. """) with gr.Row(): with gr.Column(scale=2): file_output = gr.File( label="Upload de Documentos (PDF)", file_count="multiple", file_types=[".pdf"], type="filepath", elem_classes="upload-box" ) upload_button = gr.Button( "📥 Processar Documentos", variant="primary", size="lg" ) with gr.Column(scale=1): stats_display = gr.Textbox( label="Estatísticas", value="Aguardando processamento...", interactive=False, elem_classes="stats-box" ) status_output = gr.Textbox( label="Status", value="Sistema pronto para receber documentos.", lines=2, interactive=False ) gr.Markdown("### 💬 Chat Inteligente") chatbot = gr.Chatbot( value=[], height=500, show_copy_button=True, container=True, type="messages", layout="bubble", bubble_full_width=False, elem_classes="chat-container" ) with gr.Row(elem_classes="input-row"): msg = gr.Textbox( placeholder="Faça uma pergunta sobre os documentos...", scale=8, container=False, show_label=False, elem_classes="chat-input" ) clear = gr.Button( "🗑️", scale=1, variant="secondary", size="lg", elem_classes="clear-button" ) # Event handlers def process_and_update(files, chatbot_state): status, new_chat = ui.process_files(files, chatbot_state) return status, new_chat, ui.update_stats_display() upload_button.click( fn=process_and_update, inputs=[file_output, chatbot], outputs=[status_output, chatbot, stats_display] ) msg.submit( fn=ui.get_response, inputs=[msg, chatbot], outputs=[chatbot], show_progress=True ) clear.click(lambda: [], None, chatbot, queue=False) demo.load( fn=ui.update_stats_display, outputs=[stats_display] ) return demo if __name__ == "__main__": try: # Encontra uma porta livre port = find_free_port() logger.info(f"Usando porta: {port}") demo = create_interface() logger.info("Interface criada com sucesso") demo.launch( share=False, debug=True, show_error=True, server_name="0.0.0.0", server_port=port ) except Exception as e: logger.error(f"Erro ao iniciar o servidor: {str(e)}")