Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import os | |
| import copy | |
| import re | |
| import requests | |
| import json | |
| import logging | |
| from typing import Dict, List, Optional, Union, Tuple, Any | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') | |
| logger = logging.getLogger(__name__) | |
| GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") | |
| MODEL_NAME = "gemini-1.5-flash-latest" | |
| API_URL_BASE = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL_NAME}:generateContent" | |
| class Config: | |
| MAX_RETRIES = 1 | |
| REQUEST_TIMEOUT = 360 | |
| DEFAULT_TEMPERATURE = 0.75 | |
| PETICAO_MAX_TOKENS = 2200 # Aumentado | |
| CONTESTACAO_MAX_TOKENS = 2200 # Aumentado | |
| AIJ_MAX_TOKENS = 6000 # Aumentado significativamente para AIJ completa e detalhada | |
| PETICAO_MARKERS_MAP = { | |
| "fatos_autor": "### DOS FATOS (Narrativa do Autor)", | |
| "direito_autor": "### DO DIREITO (Principais Fundamentos Jurídicos)", | |
| "pedidos_autor": "### DOS PEDIDOS", | |
| "estrategias_autor": "### PONTOS ESTRATÉGICOS ADICIONAIS PARA O AUTOR" | |
| } | |
| CONTESTACAO_MARKERS_MAP = { | |
| "sintese_reu": "### SÍNTESE DA DEFESA", | |
| "preliminares_reu": "### PRELIMINARES", # Adicionado para parsing explícito | |
| "fatos_reu": "### DA REALIDADE DOS FATOS (Narrativa do Réu e Impugnação Específica)", | |
| "direito_reu": "### DO DIREITO (Principais Fundamentos da Defesa)", | |
| "pedidos_reu": "### DOS PEDIDOS (Contrapedidos/Impugnações)", | |
| "estrategias_reu": "### PONTOS ESTRATÉGICOS ADICIONAIS PARA O RÉU" | |
| } | |
| AIJ_MARKERS_MAP = { | |
| "1_abertura": "### 1. ABERTURA E TENTATIVA DE CONCILIAÇÃO", | |
| "2_dep_autor": "### 2. DEPOIMENTO PESSOAL DO AUTOR (Resumido)", | |
| "3_dep_reu": "### 3. DEPOIMENTO PESSOAL DO RÉU (Preposto do Jornal - Resumido)", | |
| "4_test_autor": "### 4. OITIVA DAS TESTEMUNHAS DO AUTOR", | |
| "5_test_reu": "### 5. OITIVA DAS TESTEMUNHAS DO RÉU", | |
| "6_aleg_autor": "### 6. ALEGAÇÕES FINAIS DO AUTOR (Resumido)", | |
| "7_aleg_reu": "### 7. ALEGAÇÕES FINAIS DO RÉU (Resumido)", | |
| "8_conc_juiz": "### 8. CONCLUSÃO DO JUIZ PARA SENTENÇA (Simulada)", | |
| "9_cenarios": "### 9. ANÁLISE DE CENÁRIOS E PONTOS DE INFLEXÃO" | |
| } | |
| class CasoPadrao: | |
| RESUMO = """Carlos Alberto Mendes processou o jornal "A Verdade Diária" por violação do direito ao esquecimento. Há 15 anos, Carlos foi absolvido de fraude. Recentemente, o jornal revisitou o caso numa matéria retrospectiva, mencionando a absolvição. Carlos alega danos (reputação, emprego, emocional) e pede remoção e indenização. O jornal defende liberdade de imprensa, interesse público histórico e veracidade.""" | |
| AUTOR_PONTOS = """1. Dano à honra e imagem. Prova: Laudo psicológico. 2. Sofrimento social. Prova: Depoimentos. 3. Prejuízo profissional. 4. Direito ao esquecimento (Art. 5º, X, CF; Enunciado 531). 5. Irrelevância atual da notícia.""" | |
| REU_PONTOS = """1. Liberdade de imprensa (Art. 5º, IX, XIV, 220 CF). 2. Interesse público histórico. 3. Menção à absolvição. 4. Animus narrandi. 5. Precedente STF (RE 1.010.606). Provas: Cópia da matéria, docs. processo original.""" | |
| _peticao_completa_exportar = "" | |
| _contestacao_completa_exportar = "" | |
| _aij_texto_completo_para_exportar = "" | |
| class GeminiAPI: | |
| def chamar_api(prompt_text: str, temperature: float = Config.DEFAULT_TEMPERATURE, | |
| max_output_tokens: int = 1500, retries: int = Config.MAX_RETRIES) -> str: | |
| if not GEMINI_API_KEY: | |
| logger.error("ERRO_FATAL: API Key do Gemini não configurada.") | |
| return "ERRO_FATAL: API Key do Gemini não configurada." | |
| api_url_com_chave = f"{API_URL_BASE}?key={GEMINI_API_KEY}" | |
| headers = {'Content-Type': 'application/json'} | |
| data = {"contents": [{"parts": [{"text": prompt_text}]}], | |
| "generationConfig": {"temperature": temperature, "maxOutputTokens": max_output_tokens, "topP": 0.95, "topK": 40}, | |
| "safetySettings": [{"category": f"HARM_CATEGORY_{cat}", "threshold": "BLOCK_NONE"} for cat in ["HARASSMENT", "HATE_SPEECH", "SEXUALLY_EXPLICIT", "DANGEROUS_CONTENT"]]} | |
| for attempt in range(retries + 1): | |
| try: | |
| logger.info(f"API Gemini: Tentativa {attempt+1}/{retries+1}, Prompt: {len(prompt_text)} chars, Max Tokens: {max_output_tokens}") | |
| response = requests.post(api_url_com_chave, headers=headers, json=data, timeout=Config.REQUEST_TIMEOUT) | |
| response.raise_for_status() | |
| response_json = response.json() | |
| if response_json.get("candidates") and response_json["candidates"][0].get("content"): | |
| generated_text = response_json["candidates"][0]["content"]["parts"][0].get("text", "").strip() | |
| logger.info(f"API Gemini retornou {len(generated_text)} caracteres.") | |
| finish_reason = response_json["candidates"][0].get("finishReason", "") | |
| if finish_reason == "MAX_TOKENS": | |
| logger.warning("Geração da API interrompida por MAX_TOKENS.") | |
| generated_text += "\n\n[AVISO: Geração interrompida por limite de tokens.]" | |
| elif finish_reason not in ["STOP", "OTHER", None]: | |
| logger.warning(f"Geração da API terminou com razão: {finish_reason}") | |
| return generated_text | |
| elif response_json.get("promptFeedback"): | |
| feedback = response_json["promptFeedback"] | |
| error_detail = f"Bloqueio: {feedback.get('blockReason', 'N/A')}. Safety: {feedback.get('safetyRatings', 'N/A')}" | |
| logger.error(f"API: Conteúdo bloqueado. {error_detail}") | |
| return f"ERRO_API: Conteúdo bloqueado. {error_detail}" | |
| logger.error(f"API: Resposta inesperada/vazia. JSON: {response_json}") | |
| return "ERRO_API: Resposta inesperada/vazia da API." | |
| except requests.exceptions.Timeout: | |
| logger.error(f"API: Timeout na tentativa {attempt+1}.") | |
| if attempt == retries: return "ERRO_CONEXAO: Timeout excedido." | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"API: Erro na requisição (tentativa {attempt+1}): {e}", exc_info=True) | |
| if attempt == retries: return f"ERRO_CONEXAO: {e}" | |
| except Exception as e: | |
| logger.exception(f"API: Erro desconhecido (tentativa {attempt+1}): {e}") | |
| if attempt == retries: return f"ERRO_DESCONHECIDO: {e}" | |
| return "ERRO_API: Todas as tentativas falharam." | |
| class GeradorDocumentos: | |
| def gerar_peticao_inicial(resumo_caso: str, pontos_autor: str, modificacao_fato: Optional[str], abordagem_autor: str) -> str: | |
| mod_prompt = f"\n**Modificação Fato:** {modificacao_fato}\n" if modificacao_fato else "" | |
| ab_prompt = f"\n**Abordagem Autor:** {abordagem_autor}\n" | |
| prompt = f"""**Persona:** Advogado experiente (Direitos da Personalidade), redigindo Petição Inicial (Esquecimento vs. Imprensa). | |
| {mod_prompt}{ab_prompt}**Caso Base:** {resumo_caso} | |
| **Pontos Autor:** {pontos_autor or "Inferir do resumo e abordagem."} | |
| **Tarefa:** Gere Petição Inicial com seções detalhadas (Fatos, Direito, Pedidos, Estratégias Adicionais), adaptando à modificação do fato e abordagem. Para o Direito, cite 3-4 fundamentos (Dignidade, Privacidade, Esquecimento, Resp. Civil) e explique sua aplicação. Nos Pedidos, seja específico (tutela, remoção, indenização, retratação). Em Estratégias, sugira argumentos/provas alternativas e riscos a antecipar. | |
| **Formato Saída (marcadores exatos):** | |
| `{Config.PETICAO_MARKERS_MAP["fatos_autor"]}` | |
| [Conteúdo] | |
| `{Config.PETICAO_MARKERS_MAP["direito_autor"]}` | |
| [Conteúdo] | |
| `{Config.PETICAO_MARKERS_MAP["pedidos_autor"]}` | |
| [Conteúdo] | |
| `{Config.PETICAO_MARKERS_MAP["estrategias_autor"]}` | |
| [Conteúdo]""" | |
| return GeminiAPI.chamar_api(prompt, max_output_tokens=Config.PETICAO_MAX_TOKENS) | |
| def gerar_contestacao(resumo_caso: str, pontos_reu: str, peticao_autor: str, tom_defesa: str, modificacao_fato: Optional[str]) -> str: | |
| mod_prompt = f"\n**Modificação Fato (do caso original):** {modificacao_fato}\n" if modificacao_fato else "" | |
| prompt = f"""**Persona:** Advogado experiente (Liberdade de Imprensa), redigindo Contestação (Esquecimento vs. Imprensa). | |
| **Tom Defesa:** {tom_defesa} | |
| {mod_prompt}**Caso Base:** {resumo_caso} | |
| **Pontos Réu:** {pontos_reu or "Inferir do resumo."} | |
| **Petição Autor:** ```{peticao_autor}``` | |
| **Tarefa:** Gere Contestação com seções (Síntese, Preliminares (se houver), Fatos (com impugnação), Direito, Pedidos, Estratégias Adicionais), adaptando ao tom e modificação do fato. No Direito, cite 3-4 fundamentos (Liberdade Imprensa/Info, Art. 5/220 CF, Ausência Ilicitude, Verdade, Animus Narrandi, Precedentes STF RE 1.010.606/ADPF 130). Nos Pedidos, peça improcedência total, impugne danos, peça custas/honorários. Em Estratégias, sugira teses/provas e pontos fracos da petição. | |
| **Formato Saída (marcadores exatos):** | |
| `{Config.CONTESTACAO_MARKERS_MAP["sintese_reu"]}` | |
| [Conteúdo] | |
| (Se houver preliminares: `{Config.CONTESTACAO_MARKERS_MAP["preliminares_reu"]}`\n[Conteúdo]) | |
| `{Config.CONTESTACAO_MARKERS_MAP["fatos_reu"]}` | |
| [Conteúdo] | |
| `{Config.CONTESTACAO_MARKERS_MAP["direito_reu"]}` | |
| [Conteúdo] | |
| `{Config.CONTESTACAO_MARKERS_MAP["pedidos_reu"]}` | |
| [Conteúdo] | |
| `{Config.CONTESTACAO_MARKERS_MAP["estrategias_reu"]}` | |
| [Conteúdo]""" | |
| return GeminiAPI.chamar_api(prompt, max_output_tokens=Config.CONTESTACAO_MAX_TOKENS) | |
| def simular_aij(resumo_caso: str, peticao_autor: str, contestacao_reu: str, | |
| pontos_autor: str, pontos_reu: str, tom_defesa_reu: str, | |
| modificacao_fato: Optional[str], abordagem_autor: str) -> str: | |
| mod_prompt = f"\n**Modificação Fato (para AIJ):** {modificacao_fato}\n" if modificacao_fato else "" | |
| ab_prompt = f"\n**Abordagem Autor (para AIJ):** {abordagem_autor}\n" | |
| prompt = f"""**Persona:** Narrador Jurídico imparcial simulando AIJ (Esquecimento vs. Imprensa). | |
| {mod_prompt}{ab_prompt}**Contexto Caso Original:** {resumo_caso} | |
| **Petição Autor:** ```{peticao_autor}``` {("Pontos Autor: " + pontos_autor) if pontos_autor else ""} | |
| **Contestação Réu (Tom: {tom_defesa_reu}):** ```{contestacao_reu}``` {("Pontos Réu: " + pontos_reu) if pontos_reu else ""} | |
| **Tarefa:** Narre a AIJ. Advogados devem refletir abordagens/tons. Fato modificado deve influenciar depoimentos/perguntas/análise. Inferir 1-2 testemunhas relevantes (Autor: psicólogo, amigo; Réu: editor, jornalista) e seus depoimentos/contraditas no contexto do caso (esquecimento, dano, interesse público, veracidade). Juiz deve ponderar direitos conflitantes na conclusão. Seção de Cenários deve explorar 1 reviravolta, 1 estratégia alternativa, 1 fator externo. | |
| **Formato Saída (marcadores exatos):** | |
| `{Config.AIJ_MARKERS_MAP["1_abertura"]}` \n[Conteúdo] | |
| `{Config.AIJ_MARKERS_MAP["2_dep_autor"]}` \n[Conteúdo] | |
| `{Config.AIJ_MARKERS_MAP["3_dep_reu"]}` \n[Conteúdo] | |
| `{Config.AIJ_MARKERS_MAP["4_test_autor"]}` \n[Formato Testemunha: - Testemunha: [Nome/Papel]\n - Ponto Chave: [Depoimento]\n - Contradita: [Foco da pergunta do Adv. Réu e resposta]] | |
| `{Config.AIJ_MARKERS_MAP["5_test_reu"]}` \n[Formato Testemunha: - Testemunha: [Nome/Papel]\n - Ponto Chave: [Depoimento]\n - Contradita: [Foco da pergunta do Adv. Autor e resposta]] | |
| `{Config.AIJ_MARKERS_MAP["6_aleg_autor"]}` \n[Conteúdo] | |
| `{Config.AIJ_MARKERS_MAP["7_aleg_reu"]}` \n[Conteúdo] | |
| `{Config.AIJ_MARKERS_MAP["8_conc_juiz"]}` \n[Conteúdo] | |
| `{Config.AIJ_MARKERS_MAP["9_cenarios"]}` \n[a) Ponto de Virada: ... b) Estratégia Alternativa: ... c) Fator Externo: ...]""" | |
| return GeminiAPI.chamar_api(prompt, temperature=0.8, max_output_tokens=Config.AIJ_MAX_TOKENS) | |
| class ProcessadorTexto: | |
| def _extrair_secao(texto_completo: str, texto_lower: str, marcador_inicio: str, marcadores_subsequentes: List[str]) -> Optional[str]: | |
| start_idx = texto_lower.find(marcador_inicio.lower()) | |
| if start_idx == -1: return None | |
| content_start_idx = start_idx + len(marcador_inicio) | |
| end_idx = len(texto_completo) | |
| for next_marcador in marcadores_subsequentes: | |
| next_marker_idx_found = texto_lower.find(next_marcador.lower(), content_start_idx) | |
| if next_marker_idx_found != -1: | |
| end_idx = min(end_idx, next_marker_idx_found) | |
| return texto_completo[content_start_idx:end_idx].strip() | |
| def processar_documento(texto_completo: str, markers_map: Dict[str, str]) -> Dict[str, str]: | |
| global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar | |
| if markers_map == Config.PETICAO_MARKERS_MAP: _peticao_completa_exportar = texto_completo | |
| elif markers_map == Config.CONTESTACAO_MARKERS_MAP: _contestacao_completa_exportar = texto_completo | |
| elif markers_map == Config.AIJ_MARKERS_MAP: _aij_texto_completo_para_exportar = texto_completo | |
| if not texto_completo or "ERRO_" in texto_completo: | |
| logger.error(f"Erro na entrada do processador: {texto_completo}") | |
| return {key: f"Erro ao gerar/processar seção: {texto_completo}" for key in markers_map.keys()} | |
| partes_documento = {key: f"Seção '{marker_text}' não encontrada." for key, marker_text in markers_map.items()} | |
| texto_lower = texto_completo.lower() | |
| sorted_marcadores_list = list(markers_map.items()) | |
| for i, (key, marcador_inicio) in enumerate(sorted_marcadores_list): | |
| marcadores_subsequentes_text = [m_text for _, m_text in sorted_marcadores_list[i+1:]] | |
| conteudo_secao = ProcessadorTexto._extrair_secao(texto_completo, texto_lower, marcador_inicio, marcadores_subsequentes_text) | |
| if conteudo_secao is not None: partes_documento[key] = conteudo_secao | |
| else: logger.warning(f"Marcador não encontrado ao processar: '{marcador_inicio}' para '{key}'") | |
| return partes_documento | |
| class InterfaceProcesso: | |
| def simular_processo_completo_ui_generator(resumo_caso_input: str, pontos_autor_input: str, | |
| pontos_reu_input: str, tom_defesa_reu_input: str, | |
| modificacao_fato_input: Optional[str], | |
| abordagem_autor_input: Optional[str]): | |
| global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar | |
| _peticao_completa_exportar = _contestacao_completa_exportar = _aij_texto_completo_para_exportar = "" | |
| num_aij_outputs = len(Config.AIJ_MARKERS_MAP) | |
| total_outputs = 2 + num_aij_outputs + 1 # Pet, Cont, AIJ_parts, Status | |
| outputs_tuple_list = [""] * total_outputs | |
| def create_error_tuple(base_error_msg: str): | |
| err_list = [base_error_msg] * (total_outputs -1) | |
| err_list.append(gr.update(value=base_error_msg)) | |
| return tuple(err_list) | |
| if not GEMINI_API_KEY: | |
| yield create_error_tuple("ERRO FATAL: API Key do Gemini não configurada.") | |
| return | |
| if not resumo_caso_input: | |
| yield create_error_tuple("Erro: O resumo do caso é obrigatório.") | |
| return | |
| outputs_tuple_list[total_outputs - 1] = gr.update(value="Iniciando: Gerando Petição Inicial...") | |
| outputs_tuple_list[0] = "Gerando..." | |
| yield tuple(outputs_tuple_list) | |
| peticao_autor_txt = GeradorDocumentos.gerar_peticao_inicial(resumo_caso_input, pontos_autor_input, modificacao_fato_input, abordagem_autor_input) | |
| outputs_tuple_list[0] = peticao_autor_txt | |
| _peticao_completa_exportar = peticao_autor_txt | |
| if "ERRO_" in peticao_autor_txt: | |
| logger.error(f"Falha Petição: {peticao_autor_txt}") | |
| outputs_tuple_list[total_outputs-1] = gr.update(value=f"Erro Petição: {peticao_autor_txt[:100]}...") | |
| for i in range(1, total_outputs -1) : outputs_tuple_list[i] = peticao_autor_txt | |
| yield tuple(outputs_tuple_list) | |
| return | |
| outputs_tuple_list[1] = "Gerando Contestação..." | |
| outputs_tuple_list[total_outputs -1] = gr.update(value="Gerando Contestação...") | |
| yield tuple(outputs_tuple_list) | |
| contestacao_reu_txt = GeradorDocumentos.gerar_contestacao(resumo_caso_input, pontos_reu_input, peticao_autor_txt, tom_defesa_reu_input, modificacao_fato_input) | |
| outputs_tuple_list[1] = contestacao_reu_txt | |
| _contestacao_completa_exportar = contestacao_reu_txt | |
| if "ERRO_" in contestacao_reu_txt: | |
| logger.error(f"Falha Contestação: {contestacao_reu_txt}") | |
| outputs_tuple_list[total_outputs-1] = gr.update(value=f"Erro Contestação: {contestacao_reu_txt[:100]}...") | |
| for i in range(2, total_outputs -1) : outputs_tuple_list[i] = contestacao_reu_txt | |
| yield tuple(outputs_tuple_list) | |
| return | |
| for i in range(num_aij_outputs): outputs_tuple_list[2+i] = "Gerando AIJ..." | |
| outputs_tuple_list[total_outputs -1] = gr.update(value="Simulando AIJ...") | |
| yield tuple(outputs_tuple_list) | |
| aij_simulacao_txt_completa = GeradorDocumentos.simular_aij(resumo_caso_input, peticao_autor_txt, contestacao_reu_txt, pontos_autor_input, pontos_reu_input, tom_defesa_reu_input, modificacao_fato_input, abordagem_autor_input) | |
| if "ERRO_" in aij_simulacao_txt_completa: | |
| logger.error(f"Falha AIJ: {aij_simulacao_txt_completa}") | |
| for i in range(num_aij_outputs): outputs_tuple_list[2+i] = aij_simulacao_txt_completa | |
| outputs_tuple_list[total_outputs-1] = gr.update(value=f"Erro AIJ: {aij_simulacao_txt_completa[:100]}...") | |
| yield tuple(outputs_tuple_list) | |
| return | |
| aij_partes_dict = ProcessadorTexto.processar_aij(aij_simulacao_txt_completa) | |
| for i, key in enumerate(Config.AIJ_MARKERS_MAP.keys()): | |
| outputs_tuple_list[2+i] = aij_partes_dict.get(key, f"Falha ao processar seção AIJ: {key}") | |
| outputs_tuple_list[total_outputs-1] = gr.update(value="Simulação concluída com sucesso.") | |
| logger.info("Simulação completa e UI atualizada.") | |
| yield tuple(outputs_tuple_list) | |
| def limpar_interface_fn(): | |
| num_aij_outputs = len(Config.AIJ_MARKERS_MAP) | |
| return ("", "", "", "Padrão", "", "Técnica e Legalista", # Entradas | |
| "", "", # Petição, Contestação | |
| *[""] * num_aij_outputs, # AIJ parts | |
| gr.update(value="Campos e saídas limpos."), # Status | |
| "" # AIJ export box | |
| ) | |
| def restaurar_padrao_fn(): | |
| num_aij_outputs = len(Config.AIJ_MARKERS_MAP) | |
| return (CasoPadrao.RESUMO, "", CasoPadrao.AUTOR_PONTOS, "Padrão", CasoPadrao.REU_PONTOS, "Técnica e Legalista", | |
| "", "", *[""] * num_aij_outputs, | |
| gr.update(value="Caso padrão restaurado. Modifique e simule."), "" | |
| ) | |
| def criar_interface() -> gr.Blocks: | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="amber"), title="Simulador de Fluxo Processual Avançado") as demo: | |
| gr.Markdown("# ⚖️ Simulador Avançado de Fluxo Processual e AIJ (IA-Powered)") | |
| gr.Markdown("Forneça um caso, modifique fatos, escolha abordagens e veja a IA simular o processo.") | |
| if not GEMINI_API_KEY: gr.Warning("ALERTA: GEMINI_API_KEY não configurada!") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("## Configuração do Caso e Variações") | |
| resumo_caso_input = gr.Textbox(lines=8, label="Resumo Detalhado do Caso Base:", value=CasoPadrao.RESUMO) | |
| modificacao_fato_input = gr.Textbox(lines=3, label="Modificação de Fato Chave (Opcional):", placeholder="Ex: A matéria foi publicada há apenas 1 ano.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| pontos_autor_input = gr.Textbox(lines=4, label="Pontos/Provas do AUTOR (Opcional):", value=CasoPadrao.AUTOR_PONTOS) | |
| abordagem_autor_input = gr.Radio(choices=["Padrão", "Foco Máximo em Danos Morais", "Busca Principal pela Remoção", "Postura Conciliadora Inicial"], label="Abordagem Estratégica do Autor:", value="Padrão") | |
| with gr.Column(): | |
| pontos_reu_input = gr.Textbox(lines=4, label="Pontos/Provas do RÉU (Opcional):", value=CasoPadrao.REU_PONTOS) | |
| tom_defesa_reu_input = gr.Radio(choices=["Combativa e Firme", "Técnica e Legalista", "Ponderada/Buscando Acordo Sutil"], label="Tom da Defesa do Réu:", value="Técnica e Legalista") | |
| btn_simular = gr.Button("🚀 Simular Processo com Variações", variant="primary") | |
| status_output = gr.Label(value="Aguardando simulação...") | |
| with gr.Row(): | |
| btn_limpar_entradas = gr.Button("🧹 Limpar Tudo", variant="secondary", size="sm") | |
| btn_restaurar_padrao = gr.Button("🔄 Restaurar Caso Padrão", variant="secondary", size="sm") | |
| with gr.Column(scale=2): | |
| gr.Markdown("## Resultados da Simulação") | |
| with gr.Tabs(): | |
| with gr.TabItem("📜 Petição Inicial (Autor)"): | |
| output_peticao_inicial = gr.Markdown(show_copy_button=True) | |
| with gr.TabItem("🛡️ Contestação (Réu)"): | |
| output_contestacao = gr.Markdown(show_copy_button=True) | |
| with gr.TabItem("🎤 Audiência (AIJ)"): | |
| output_aij_export_box = gr.Textbox(label="AIJ Completa (para copiar):", lines=1, info="Texto completo da AIJ após simulação.", interactive=False, show_copy_button=True) | |
| aij_outputs_ui = {} | |
| for key, title in Config.AIJ_MARKERS_MAP.items(): | |
| accordion_title = re.sub(r"### \d+\.\s*", "", title) | |
| is_open = key in ["1_abertura", "9_cenarios"] | |
| with gr.Accordion(accordion_title, open=is_open): | |
| aij_outputs_ui[key] = gr.Markdown() | |
| all_inputs = [resumo_caso_input, modificacao_fato_input, pontos_autor_input, abordagem_autor_input, pontos_reu_input, tom_defesa_reu_input] | |
| # Construir lista de outputs na ordem correta | |
| outputs_simulacao_list = [output_peticao_inicial, output_contestacao] | |
| for key_aij in Config.AIJ_MARKERS_MAP.keys(): outputs_simulacao_list.append(aij_outputs_ui[key_aij]) | |
| outputs_simulacao_list.append(status_output) | |
| # Saídas para limpar/restaurar (inclui a caixa de exportação da AIJ) | |
| outputs_to_clear_restore = [output_peticao_inicial, output_contestacao] | |
| for key_aij_clear in Config.AIJ_MARKERS_MAP.keys(): outputs_to_clear_restore.append(aij_outputs_ui[key_aij_clear]) | |
| outputs_to_clear_restore.extend([status_output, output_aij_export_box]) | |
| def update_export_box_after_sim(): | |
| global _aij_texto_completo_para_exportar | |
| return _aij_texto_completo_para_exportar | |
| btn_simular.click( | |
| InterfaceProcesso.simular_processo_completo_ui_generator, | |
| inputs=all_inputs, outputs=outputs_simulacao_list | |
| ).then(update_export_box_after_sim, outputs=[output_aij_export_box]) | |
| btn_limpar_entradas.click( | |
| InterfaceProcesso.limpar_interface_fn, | |
| inputs=None, | |
| outputs=all_inputs + outputs_to_clear_restore | |
| ) | |
| btn_restaurar_padrao.click( | |
| InterfaceProcesso.restaurar_padrao_fn, | |
| inputs=None, | |
| outputs=all_inputs + outputs_to_clear_restore | |
| ) | |
| return demo | |
| if __name__ == '__main__': | |
| if not GEMINI_API_KEY: | |
| logger.critical("A API Key do Gemini não está configurada. A aplicação não pode iniciar.") | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue="red")) as demo_erro: | |
| gr.Markdown("# ERRO DE CONFIGURAÇÃO CRÍTICO") | |
| gr.Markdown("A chave `GEMINI_API_KEY` não foi configurada corretamente...") # Mensagem completa | |
| logger.info("Lançando interface de erro.") | |
| demo_erro.launch(debug=True) | |
| else: | |
| logger.info("Iniciando a interface Gradio do Simulador de Fluxo Processual Avançado...") | |
| simulador_interface = InterfaceProcesso.criar_interface() | |
| simulador_interface.queue().launch(debug=True) |