Files changed (1) hide show
  1. app.py +202 -494
app.py CHANGED
@@ -7,35 +7,21 @@ import json
7
  import logging
8
  from typing import Dict, List, Optional, Union, Tuple, Any
9
 
10
- # --- Configuração de Logging ---
11
- logging.basicConfig(
12
- level=logging.INFO,
13
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
14
- datefmt='%Y-%m-%d %H:%M:%S'
15
- )
16
  logger = logging.getLogger(__name__)
17
 
18
- # --- Configuração da API Gemini ---
19
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
20
- if not GEMINI_API_KEY:
21
- logger.warning("ALERTA: GEMINI_API_KEY não encontrada como Secret no Hugging Face Space ou variável de ambiente.")
22
- logger.warning("A aplicação não funcionará corretamente sem ela.")
23
- # Para teste local:
24
- # GEMINI_API_KEY = "SUA_CHAVE_AQUI"
25
-
26
- MODEL_NAME = "gemini-1.5-flash-latest"
27
  API_URL_BASE = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL_NAME}:generateContent"
28
 
29
- # --- Constantes e configurações ---
30
  class Config:
31
  MAX_RETRIES = 1
32
- REQUEST_TIMEOUT = 360 # Aumentado um pouco mais
33
  DEFAULT_TEMPERATURE = 0.75
34
- PETICAO_MAX_TOKENS = 2000
35
- CONTESTACAO_MAX_TOKENS = 2000
36
- AIJ_MAX_TOKENS = 5000 # Para AIJ detalhada
37
 
38
- # Marcadores para parsear as peças e a AIJ
39
  PETICAO_MARKERS_MAP = {
40
  "fatos_autor": "### DOS FATOS (Narrativa do Autor)",
41
  "direito_autor": "### DO DIREITO (Principais Fundamentos Jurídicos)",
@@ -44,7 +30,8 @@ class Config:
44
  }
45
  CONTESTACAO_MARKERS_MAP = {
46
  "sintese_reu": "### SÍNTESE DA DEFESA",
47
- "fatos_reu": "### DA REALIDADE DOS FATOS (Narrativa do Réu)",
 
48
  "direito_reu": "### DO DIREITO (Principais Fundamentos da Defesa)",
49
  "pedidos_reu": "### DOS PEDIDOS (Contrapedidos/Impugnações)",
50
  "estrategias_reu": "### PONTOS ESTRATÉGICOS ADICIONAIS PARA O RÉU"
@@ -61,514 +48,269 @@ class Config:
61
  "9_cenarios": "### 9. ANÁLISE DE CENÁRIOS E PONTOS DE INFLEXÃO"
62
  }
63
 
64
- # --- Dados do Caso Padrão ---
65
  class CasoPadrao:
66
- RESUMO = """
67
- Carlos Alberto Mendes processou o jornal "A Verdade Diária" por violação do direito ao esquecimento.
68
- 15 anos, Carlos foi acusado de fraude em um caso de grande repercussão local, mas foi absolvido por falta de provas.
69
- Recentemente, o jornal publicou uma matéria retrospectiva intitulada "Crimes que Marcaram a Cidade", que revisitou o caso de Carlos, mencionando sua absolvição, mas detalhando as acusações originais.
70
- Carlos alega que a matéria, apesar de mencionar a absolvição, reacendeu o estigma, prejudicou sua reputação, dificultou a obtenção de novo emprego e causou-lhe profundo abalo emocional e constrangimento social.
71
- Ele busca a remoção da matéria online e indenização por danos morais.
72
- O jornal defende a liberdade de imprensa, o interesse público histórico da matéria e a veracidade dos fatos reportados, incluindo a absolvição.
73
- """
74
- AUTOR_PONTOS = """
75
- 1. Dano à honra e imagem pela reexposição de caso antigo superado. Prova: Laudo psicológico, prints de comentários online.
76
- 2. Sofrimento psicológico e constrangimento social. Prova: Depoimento de amigos/familiares.
77
- 3. Prejuízo profissional e dificuldades de reinserção. Prova: Cartas de recusa de emprego (se houver), depoimento sobre oportunidades perdidas.
78
- 4. Direito ao esquecimento como proteção da dignidade da pessoa humana e direito à privacidade (Art. 5º, X, CF).
79
- 5. Irrelevância atual da notícia, considerando o lapso temporal e a absolvição.
80
- """
81
- REU_PONTOS = """
82
- 1. Liberdade de imprensa (Art. 5º, IX, XIV, e Art. 220 CF) e direito à informação da sociedade.
83
- 2. Matéria de interesse público histórico e cultural, narrando fatos verídicos e de relevância para a memória coletiva.
84
- 3. Menção clara e inequívoca à absolvição do autor na matéria, afastando má-fé.
85
- 4. Ausência de intenção de difamar (animus narrandi).
86
- 5. Precedente do STF (RE 1.010.606) que pondera o direito ao esquecimento com a liberdade de expressão e informação, não o reconhecendo como absoluto.
87
- Provas: Cópia da matéria publicada, documentos do processo original (públicos), parecer de historiador sobre a relevância do caso.
88
- """
89
 
90
- # Variável global para armazenar os textos completos para exportação/cópia
91
  _peticao_completa_exportar = ""
92
  _contestacao_completa_exportar = ""
93
  _aij_texto_completo_para_exportar = ""
94
 
95
- # --- Classe para Chamadas à API Gemini ---
96
  class GeminiAPI:
97
  @staticmethod
98
  def chamar_api(prompt_text: str, temperature: float = Config.DEFAULT_TEMPERATURE,
99
  max_output_tokens: int = 1500, retries: int = Config.MAX_RETRIES) -> str:
100
- # ... (Implementação robusta da chamar_api como na resposta anterior) ...
101
  if not GEMINI_API_KEY:
102
  logger.error("ERRO_FATAL: API Key do Gemini não configurada.")
103
  return "ERRO_FATAL: API Key do Gemini não configurada."
104
-
105
  api_url_com_chave = f"{API_URL_BASE}?key={GEMINI_API_KEY}"
106
  headers = {'Content-Type': 'application/json'}
107
- data = {
108
- "contents": [{"parts": [{"text": prompt_text}]}],
109
- "generationConfig": {
110
- "temperature": temperature,
111
- "maxOutputTokens": max_output_tokens,
112
- "topP": 0.95,
113
- "topK": 40
114
- },
115
- "safetySettings": [
116
- {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
117
- {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
118
- {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
119
- {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"}
120
- ]
121
- }
122
-
123
- attempts = 0
124
- while attempts <= retries:
125
  try:
126
- logger.info(f"Chamando API Gemini (tentativa {attempts+1}/{retries+1}) para prompt de {len(prompt_text)} caracteres, max_tokens: {max_output_tokens}")
127
-
128
- response = requests.post(
129
- api_url_com_chave,
130
- headers=headers,
131
- data=json.dumps(data),
132
- timeout=Config.REQUEST_TIMEOUT
133
- )
134
  response.raise_for_status()
135
  response_json = response.json()
136
-
137
- if response_json.get("candidates") and \
138
- isinstance(response_json["candidates"], list) and \
139
- len(response_json["candidates"]) > 0 and \
140
- response_json["candidates"][0].get("content") and \
141
- isinstance(response_json["candidates"][0]["content"].get("parts"), list) and \
142
- len(response_json["candidates"][0]["content"]["parts"]) > 0:
143
-
144
  generated_text = response_json["candidates"][0]["content"]["parts"][0].get("text", "").strip()
145
  logger.info(f"API Gemini retornou {len(generated_text)} caracteres.")
146
-
147
  finish_reason = response_json["candidates"][0].get("finishReason", "")
148
  if finish_reason == "MAX_TOKENS":
149
  logger.warning("Geração da API interrompida por MAX_TOKENS.")
150
- generated_text += "\n\n[AVISO: A IA pode ter interrompido a geração aqui devido ao limite de tokens de saída.]"
151
- elif finish_reason not in ["STOP", "OTHER", None]: # OTHER e None às vezes são usados para stop normal
152
- logger.warning(f"Geração da API terminou com razão incomum: {finish_reason}")
153
-
154
  return generated_text
155
-
156
  elif response_json.get("promptFeedback"):
157
- block_reason = response_json["promptFeedback"].get("blockReason", "Não especificada")
158
- safety_ratings_str = str(response_json["promptFeedback"].get("safetyRatings", {}))
159
- error_detail = f"Razão do bloqueio: {block_reason}. Classificações de segurança: {safety_ratings_str}"
160
- logger.error(f"Conteúdo bloqueado pela API. {error_detail}")
161
- return f"ERRO_API: Conteúdo bloqueado pela API. {error_detail}"
162
-
163
- logger.error(f"Resposta inesperada ou malformada da API: {response_json}")
164
- return "ERRO_API: Resposta inesperada ou malformada da API Gemini."
165
-
166
  except requests.exceptions.Timeout:
167
- logger.error(f"Timeout na tentativa {attempts+1} ao chamar a API Gemini.")
168
- attempts += 1
169
- if attempts > retries:
170
- return "ERRO_CONEXAO: Timeout excedido após múltiplas tentativas."
171
- logger.info(f"Tentando novamente ({attempts+1}/{retries+1})...")
172
-
173
  except requests.exceptions.RequestException as e:
174
- error_msg = f"ERRO_CONEXAO: {e}"
175
- if e.response is not None:
176
- try:
177
- error_details = e.response.json().get('error', {}).get('message', e.response.text)
178
- error_msg += f" - Detalhes: {error_details}"
179
- except json.JSONDecodeError:
180
- error_msg += f" - Resposta não JSON: {e.response.text}"
181
- logger.error(error_msg)
182
- return error_msg
183
-
184
  except Exception as e:
185
- logger.exception(f"Erro desconhecido na API na tentativa {attempts+1}: {e}")
186
- attempts += 1
187
- if attempts > retries:
188
- return f"ERRO_DESCONHECIDO: {e}"
189
- return "ERRO_API: Todas as tentativas de chamada à API falharam."
190
 
191
- # --- Classes para geração de documentos ---
192
  class GeradorDocumentos:
193
  @staticmethod
194
- def gerar_peticao_inicial(resumo_caso: str, pontos_autor: str) -> str:
195
- prompt = f"""
196
- **Persona:** Você é um advogado experiente e meticuloso, especializado em casos de Direitos da Personalidade e Responsabilidade Civil, redigindo os elementos chave de uma Petição Inicial para um caso de Direito ao Esquecimento vs. Liberdade de Imprensa.
197
- **Caso:**
198
- {resumo_caso}
199
- **Principais Pontos e Provas do Autor (use-os para fundamentar a petição; se não fornecidos, infira do resumo do caso, focando em elementos que sustentem o pedido de esquecimento):**
200
- {pontos_autor if pontos_autor else "Inferir do resumo do caso, focando nos danos alegados e na ausência de interesse público atual na informação."}
201
-
202
- **Tarefa:** Gere os seguintes tópicos para uma Petição Inicial da parte AUTORA, de forma clara, objetiva e juridicamente fundamentada:
203
- 1. **DOS FATOS (Narrativa do Autor):** (Descreva os fatos sob a ótica do autor, detalhando a publicação original, a absolvição, o lapso temporal, a republicação/nova matéria, e o impacto negativo específico e comprovável na vida do autor – profissional, social, emocional. Enfatize a desatualização da informação e a ausência de interesse público contemporâneo que justifique a nova exposição).
204
- 2. **DO DIREITO (Principais Fundamentos Jurídicos):** (Cite e explique brevemente 3-4 fundamentos legais/constitucionais, como:
205
- * Dignidade da Pessoa Humana (Art. 1º, III, CF) como base para o direito ao esquecimento.
206
- * Direito à Privacidade, Intimidade, Honra e Imagem (Art. 5º, X, CF).
207
- * Direito ao Esquecimento (construção doutrinária e jurisprudencial, citar Enunciado 531 da VI Jornada de Direito Civil se pertinente).
208
- * Responsabilidade Civil por Dano Moral (Art. 186, 187 e 927 do Código Civil), explicando o ato ilícito (republicação indevida), o dano e o nexo causal.
209
- * Ponderação de direitos: argumentar por que, no caso concreto, o direito ao esquecimento/privacidade do autor deve prevalecer sobre a liberdade de imprensa, dada a ausência de interesse público atual e o dano causado).
210
- 3. **DOS PEDIDOS:** (Liste os pedidos principais do autor de forma clara e específica:
211
- * Tutela de urgência para remoção imediata da matéria (se aplicável).
212
- * Confirmação da tutela e remoção definitiva da matéria de todas as plataformas do réu.
213
- * Condenação do réu ao pagamento de indenização por danos morais (sugerir um valor ou critério, justificando-o com base na extensão do dano, capacidade econômica do réu e caráter punitivo-pedagógico).
214
- * (Opcional) Publicação de direito de resposta ou retratação.
215
- * Condenação do réu em custas processuais e honorários advocatícios).
216
- 4. **PONTOS ESTRATÉGICOS ADICIONAIS PARA O AUTOR:** (Sugira brevemente:
217
- * Outras linhas de argumentação (ex: abuso de direito do veículo de imprensa).
218
- * Tipos de provas adicionais cruciais (ex: perícia em prints para comprovar alcance, mais depoimentos específicos sobre o dano).
219
- * Principais contrapontos da defesa a serem antecipados e como refutá-los).
220
-
221
- **Formato da Saída:**
222
- Use os seguintes marcadores EXATOS para cada seção, iniciando em nova linha:
223
- `{Config.PETICAO_MARKERS_MAP["fatos_autor"]}`
224
- `{Config.PETICAO_MARKERS_MAP["direito_autor"]}`
225
- `{Config.PETICAO_MARKERS_MAP["pedidos_autor"]}`
226
- `{Config.PETICAO_MARKERS_MAP["estrategias_autor"]}`
227
-
228
- O conteúdo de cada seção deve vir após o marcador. Seja completo e detalhado dentro de cada seção.
229
- """
230
- return GeminiAPI.chamar_api(
231
- prompt,
232
- temperature=Config.DEFAULT_TEMPERATURE,
233
- max_output_tokens=Config.PETICAO_MAX_TOKENS
234
- )
235
 
236
  @staticmethod
237
- def gerar_contestacao(resumo_caso: str, pontos_reu: str, peticao_autor: str, tom_defesa: str) -> str:
238
- prompt = f"""
239
- **Persona:** Você é um advogado experiente e combativo, especializado em defender veículos de imprensa em casos envolvendo Liberdade de Expressão e Direito ao Esquecimento, redigindo os elementos chave de uma Contestação.
240
- **Tom da Defesa Solicitado:** {tom_defesa}. Adapte a linguagem, a ênfase dos argumentos e a assertividade a este tom.
241
-
242
- **Caso (Resumo Geral):**
243
- {resumo_caso}
244
- **Principais Pontos e Provas do Réu (use-os para fundamentar a contestação; se não fornecidos, infira do resumo do caso, focando em elementos que sustentem a liberdade de imprensa e o interesse público):**
245
- {pontos_reu if pontos_reu else "Inferir do resumo do caso, focando na liberdade de imprensa, interesse público e veracidade."}
246
-
247
- **Petição Inicial do Autor (para contextualizar a resposta e permitir refutação específica):**
248
- ```
249
- {peticao_autor}
250
- ```
251
-
252
- **Tarefa:** Gere os seguintes tópicos para uma Contestação da parte RÉ, de forma clara, objetiva e juridicamente robusta, refletindo o tom da defesa solicitado:
253
- 1. **SÍNTESE DA DEFESA:** (Resumo assertivo da tese de defesa do réu, negando a pretensão autoral e afirmando a legalidade da publicação, alinhado ao tom).
254
- 2. **PRELIMINARES (Se aplicável):** (Sugira brevemente se alguma preliminar seria cabível, ex: inépcia da inicial, ilegitimidade de parte – apenas se claramente inferível e relevante para o tom combativo).
255
- 3. **DA REALIDADE DOS FATOS (Narrativa do Réu e Impugnação Específica):** (Apresente a versão dos fatos sob a ótica do réu, destacando a correção da matéria, o interesse público, a menção à absolvição, e o contexto da publicação. Impugne especificamente os fatos narrados pelo autor que sejam inverídicos ou distorcidos, usando o tom solicitado).
256
- 4. **DO DIREITO (Principais Fundamentos da Defesa):** (Cite e explique brevemente 3-4 fundamentos legais/constitucionais robustos para a defesa:
257
- * Liberdade de Imprensa e de Informação (Art., IV, IX, XIV, e Art. 220 da CF/88). Argumentar sobre a impossibilidade de censura e o dever de informar.
258
- * Direito da Sociedade à Informação e à Memória Coletiva.
259
- * Veracidade dos Fatos Publicados e Animus Narrandi (intenção de informar, não de ofender). Destacar que a absolvição foi mencionada.
260
- * Inexistência de Ato Ilícito e de Dano Indenizável (ou, subsidiariamente, excesso no valor pleiteado).
261
- * Precedentes Relevantes (STF - RE 1.010.606, ADPF 130) que limitam o direito ao esquecimento frente à liberdade de imprensa em casos de interesse público.
262
- * Argumentar sobre a não configuração do direito ao esquecimento no caso concreto, pela relevância histórica ou pela natureza pública dos fatos).
263
- 5. **DOS PEDIDOS (Contrapedidos/Impugnações):** (Peça a total improcedência dos pedidos do autor. Impugne especificamente o pedido de dano moral e seu valor, demonstrando sua exorbitância ou inexistência. Reitere a legalidade da conduta do jornal. Peça a condenação do autor em custas processuais e honorários advocatícios de sucumbência, de forma consistente com o tom).
264
- 6. **PONTOS ESTRATÉGICOS ADICIONAIS PARA O RÉU:** (Sugira brevemente:
265
- * Outras teses de defesa (ex: culpa exclusiva da vítima pela forma como lidou com a exposição prévia, ausência de prova robusta do dano alegado).
266
- * Tipos de prova adicional (ex: parecer de especialista em comunicação/jornalismo sobre o interesse público, dados de acesso à matéria que demonstrem interesse).
267
- * Pontos fracos da petição inicial a serem explorados na instrução ou em alegações finais).
268
-
269
- **Formato da Saída:**
270
- Use os seguintes marcadores EXATOS para cada seção, iniciando em nova linha:
271
- `{Config.CONTESTACAO_MARKERS_MAP["sintese_reu"]}`
272
- (Se houver preliminares, use: `### PRELIMINARES`)
273
- `{Config.CONTESTACAO_MARKERS_MAP["fatos_reu"]}`
274
- `{Config.CONTESTACAO_MARKERS_MAP["direito_reu"]}`
275
- `{Config.CONTESTACAO_MARKERS_MAP["pedidos_reu"]}`
276
- `{Config.CONTESTACAO_MARKERS_MAP["estrategias_reu"]}`
277
-
278
- O conteúdo de cada seção deve vir após o marcador. Seja completo, detalhado e assertivo dentro de cada seção, conforme o tom.
279
- """
280
- return GeminiAPI.chamar_api(
281
- prompt,
282
- temperature=Config.DEFAULT_TEMPERATURE,
283
- max_output_tokens=Config.CONTESTACAO_MAX_TOKENS
284
- )
285
 
286
  @staticmethod
287
  def simular_aij(resumo_caso: str, peticao_autor: str, contestacao_reu: str,
288
- pontos_autor: str, pontos_reu: str, tom_defesa_reu: str) -> str:
289
- prompt = f"""
290
- **Persona:** Você é um Narrador Jurídico experiente e imparcial, simulando uma Audiência de Instrução e Julgamento (AIJ) em um caso de Direito ao Esquecimento vs. Liberdade de Imprensa.
291
- **Contexto do Caso:**
292
- {resumo_caso}
293
-
294
- **Argumentos Chave do Autor (da Petição Inicial):**
295
- ```
296
- {peticao_autor}
297
- ```
298
- {("Pontos adicionais do Autor fornecidos: " + pontos_autor) if pontos_autor else ""}
299
-
300
- **Argumentos Chave do Réu (da Contestação) - Tom da Defesa do Réu a ser refletido: {tom_defesa_reu}:**
301
- ```
302
- {contestacao_reu}
303
- ```
304
- {("Pontos adicionais do Réu fornecidos: " + pontos_reu) if pontos_reu else ""}
305
-
306
- **Tarefa:** Narre resumidamente as seguintes etapas da AIJ. Para cada etapa, descreva as ações e falas principais do Juiz, Advogado do Autor e Advogado do Réu. O Advogado do Réu deve refletir o "Tom da Defesa" especificado. Para os depoimentos das testemunhas (inferir 1-2 testemunhas chave para cada parte com base nos "Pontos Chave/Provas" fornecidos ou no resumo do caso, focando em sua relevância para o tema de esquecimento vs. imprensa), resuma as perguntas chave e as respostas esperadas, bem como os pontos principais da contradita, destacando como cada advogado tenta fortalecer sua tese.
307
-
308
- **Etapas a Narrar:**
309
- 1. **ABERTURA E TENTATIVA DE CONCILIAÇÃO:** (Juiz abre a audiência, qualifica as partes e advogados. Juiz indaga sobre a possibilidade de conciliação. Advogado do Autor se manifesta sobre eventual proposta ou desinteresse. Advogado do Réu se manifesta, refletindo o tom da defesa e a posição do jornal sobre acordo).
310
- 2. **DEPOIMENTO PESSOAL DO AUTOR (Resumido):** (Principais declarações do Autor sobre como a matéria o afetou, o que ele sentiu, os prejuízos que alega, e por que acredita que a informação não deveria mais ser pública).
311
- 3. **DEPOIMENTO PESSOAL DO RÉU (Preposto do Jornal - Resumido):** (Principais declarações do preposto sobre a decisão de publicar a matéria, o processo editorial, a política do jornal sobre arquivos, a verificação da informação (incluindo a absolvição), e o interesse público percebido, refletindo a linha de defesa e o tom).
312
- 4. **OITIVA DAS TESTEMUNHAS DO AUTOR (Simular 1 ou 2 testemunhas):**
313
- * (Identifique a Testemunha 1 do Autor - ex: Psicólogo do autor, amigo que presenciou constrangimento. Indique seu principal ponto a testemunhar em relação ao dano e à desatualização da notícia).
314
- * (Adv. Autor pergunta - foco da pergunta para confirmar o dano, o abalo emocional, a ausência de interesse público atual).
315
- * (Resposta resumida da Testemunha 1).
316
- * (Adv. Réu contradita/pergunta - foco da pergunta para desqualificar o testemunho, minimizar o dano, ou questionar a percepção da testemunha sobre o interesse público, usando o tom da defesa).
317
- * (Resposta resumida da Testemunha 1 à contradita).
318
- * (Se aplicável, repita para uma Testemunha 2 do Autor, ex: especialista em privacidade digital, ex-colega de trabalho).
319
- 5. **OITIVA DAS TESTEMUNHAS DO RÉU (Simular 1 ou 2 testemunhas):**
320
- * (Identifique a Testemunha 1 do Réu - ex: Editor do jornal, jornalista que escreveu a matéria, historiador/especialista em mídia. Indique seu principal ponto a testemunhar sobre o interesse público, a veracidade, o processo editorial).
321
- * (Adv. Réu pergunta - foco da pergunta para sustentar a tese de defesa, usando o tom da defesa).
322
- * (Resposta resumida da Testemunha 1).
323
- * (Adv. Autor contradita/pergunta - foco da pergunta para questionar o alegado interesse público, a proporcionalidade da exposição, ou a diligência do jornal).
324
- * (Resposta resumida da Testemunha 1 à contradita).
325
- * (Se aplicável, repita para uma Testemunha 2 do Réu).
326
- 6. **ALEGAÇÕES FINAIS DO AUTOR (Resumido):** (Advogado do Autor resume suas provas, reitera os fundamentos jurídicos do direito ao esquecimento e da responsabilidade civil, e os pedidos, enfatizando o dano e a falta de interesse público atual).
327
- 7. **ALEGAÇÕES FINAIS DO RÉU (Resumido):** (Advogado do Réu resume suas provas, reitera os fundamentos da liberdade de imprensa, do direito à informação, da veracidade dos fatos, e os pedidos de improcedência, refletindo o tom da defesa).
328
- 8. **CONCLUSÃO DO JUIZ PARA SENTENÇA (Simulada):** (Juiz faz uma ponderação analítica sobre os principais argumentos de ambas as partes, a força aparente das provas produzidas, os direitos fundamentais em conflito (esquecimento/privacidade vs. liberdade de imprensa/informação), e os desafios jurídicos para a decisão no caso concreto, citando a complexidade da matéria. Informa que o caso será decidido em sentença fundamentada).
329
- 9. **ANÁLISE DE CENÁRIOS E PONTOS DE INFLEXÃO:** (Com base na simulação, descreva brevemente:
330
- a) Um possível "ponto de virada" na audiência (ex: uma testemunha do autor que se mostra pouco crível sobre a extensão do dano; uma testemunha do réu que admite descuido editorial; uma pergunta da contradita que efetivamente abala um depoimento). Como isso alteraria a dinâmica?
331
- b) Uma estratégia alternativa que o advogado do réu poderia ter adotado (ex: focar mais em demonstrar o contínuo, ainda que sutil, interesse público na história, ou oferecer uma forma de anonimização parcial como alternativa à remoção total). Qual seria seu potencial impacto?
332
- c) Um fator externo (ex: uma decisão recente de um tribunal superior sobre um caso similar de direito ao esquecimento, ou uma grande repercussão negativa/positiva da matéria após ajuizamento da ação) que, se ocorresse, poderia influenciar significativamente a percepção do caso ou a decisão do juiz).
333
-
334
- **Formato da Saída:**
335
- Use os seguintes marcadores EXATOS para cada etapa, iniciando em nova linha:
336
- `{Config.AIJ_MARKERS_MAP["1_abertura"]}`
337
- `{Config.AIJ_MARKERS_MAP["2_dep_autor"]}`
338
- `{Config.AIJ_MARKERS_MAP["3_dep_reu"]}`
339
- `{Config.AIJ_MARKERS_MAP["4_test_autor"]}`
340
- `{Config.AIJ_MARKERS_MAP["5_test_reu"]}`
341
- `{Config.AIJ_MARKERS_MAP["6_aleg_autor"]}`
342
- `{Config.AIJ_MARKERS_MAP["7_aleg_reu"]}`
343
- `{Config.AIJ_MARKERS_MAP["8_conc_juiz"]}`
344
- `{Config.AIJ_MARKERS_MAP["9_cenarios"]}`
345
-
346
- Para as testemunhas, dentro das seções 4 e 5, use o seguinte formato para cada testemunha:
347
- `- Testemunha do [Autor/Réu]: [Nome da Testemunha e seu Papel/Profissão]`
348
- `- Ponto Chave do Depoimento: [Resumo do que ela testemunhou a favor da parte que a arrolou, focado no conflito esquecimento vs. imprensa].`
349
- `- Perguntas da Defesa/Contradita (Foco e Resposta Resumida): [Advogado da parte contrária pergunta sobre X (ex: relevância atual, proporcionalidade, veracidade). Testemunha responde Y, mantendo/esclarecendo seu ponto].`
350
- """
351
- return GeminiAPI.chamar_api(
352
- prompt,
353
- temperature=0.8,
354
- max_output_tokens=Config.AIJ_MAX_TOKENS
355
- )
356
 
357
  class ProcessadorTexto:
358
  @staticmethod
359
  def _extrair_secao(texto_completo: str, texto_lower: str, marcador_inicio: str, marcadores_subsequentes: List[str]) -> Optional[str]:
360
- """Helper para extrair conteúdo de uma seção."""
361
  start_idx = texto_lower.find(marcador_inicio.lower())
362
- if start_idx == -1:
363
- logger.warning(f"Marcador inicial não encontrado: '{marcador_inicio}'")
364
- return None
365
-
366
  content_start_idx = start_idx + len(marcador_inicio)
367
-
368
  end_idx = len(texto_completo)
369
  for next_marcador in marcadores_subsequentes:
370
  next_marker_idx_found = texto_lower.find(next_marcador.lower(), content_start_idx)
371
  if next_marker_idx_found != -1:
372
  end_idx = min(end_idx, next_marker_idx_found)
373
-
374
  return texto_completo[content_start_idx:end_idx].strip()
375
 
376
  @staticmethod
377
  def processar_documento(texto_completo: str, markers_map: Dict[str, str]) -> Dict[str, str]:
378
- """Processa um texto genérico (Petição, Contestação) e separa em seções."""
379
- global _peticao_completa_exportar, _contestacao_completa_exportar
380
- if markers_map == Config.PETICAO_MARKERS_MAP:
381
- _peticao_completa_exportar = texto_completo
382
- elif markers_map == Config.CONTESTACAO_MARKERS_MAP:
383
- _contestacao_completa_exportar = texto_completo
384
 
385
  if not texto_completo or "ERRO_" in texto_completo:
386
- logger.error(f"Erro na entrada de processar_documento: {texto_completo}")
387
- return {key: f"Erro ao gerar seção: {texto_completo}" for key in markers_map.keys()}
388
 
389
- partes_documento = {
390
- key: f"Seção '{marker_text}' não encontrada na simulação."
391
- for key, marker_text in markers_map.items()
392
- }
393
  texto_lower = texto_completo.lower()
394
- sorted_marcadores_list = list(markers_map.items()) # Lista de tuplas (key, marker_text)
395
-
396
  for i, (key, marcador_inicio) in enumerate(sorted_marcadores_list):
397
- # Lista de marcadores subsequentes para definir o fim da seção atual
398
- marcadores_subsequentes_text = [m_text for k, m_text in sorted_marcadores_list[i+1:]]
399
-
400
  conteudo_secao = ProcessadorTexto._extrair_secao(texto_completo, texto_lower, marcador_inicio, marcadores_subsequentes_text)
401
- if conteudo_secao is not None:
402
- partes_documento[key] = conteudo_secao
403
-
404
  return partes_documento
405
 
406
-
407
- @staticmethod
408
- def processar_aij(aij_texto_completo: str) -> Dict[str, str]:
409
- global _aij_texto_completo_para_exportar
410
- _aij_texto_completo_para_exportar = aij_texto_completo
411
- return ProcessadorTexto.processar_documento(aij_texto_completo, Config.AIJ_MARKERS_MAP)
412
-
413
-
414
  class InterfaceProcesso:
415
-
416
  @staticmethod
417
  def simular_processo_completo_ui_generator(resumo_caso_input: str, pontos_autor_input: str,
418
- pontos_reu_input: str, tom_defesa_reu_input: str):
 
 
419
  global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar
 
 
 
 
 
420
 
421
- # Limpar estados de exportação anteriores
422
- _peticao_completa_exportar = ""
423
- _contestacao_completa_exportar = ""
424
- _aij_texto_completo_para_exportar = ""
425
-
426
- # Definir o número total de saídas esperadas (1 para petição, 1 para contestação, 9 para AIJ, 1 para status)
427
- total_outputs = 1 + 1 + len(Config.AIJ_MARKERS_MAP) + 1
428
- outputs_tuple = [""] * total_outputs
429
-
430
- def create_error_outputs(base_error_msg: str):
431
- """Cria uma tupla de saída preenchida com mensagens de erro."""
432
- error_list = [base_error_msg] * (total_outputs -1) # Para todas as saídas de texto
433
- error_list.append(gr.update(value=base_error_msg)) # Para o status
434
- return tuple(error_list)
435
 
436
  if not GEMINI_API_KEY:
437
- msg_erro_api = "ERRO FATAL: API Key do Gemini não configurada."
438
- logger.critical(msg_erro_api)
439
- yield create_error_outputs(msg_erro_api)
440
  return
441
-
442
  if not resumo_caso_input:
443
- msg_erro = "Erro: O resumo do caso é obrigatório."
444
- logger.error(msg_erro)
445
- yield create_error_outputs(msg_erro)
446
  return
447
 
448
- # Atualização inicial
449
- outputs_tuple[total_outputs - 1] = gr.update(value="Iniciando simulação...")
450
- outputs_tuple[0] = "Gerando Petição Inicial..." # output_peticao_inicial
451
- yield tuple(outputs_tuple)
452
-
453
- logger.info("Gerando Petição Inicial do Autor...")
454
- peticao_autor_txt = GeradorDocumentos.gerar_peticao_inicial(resumo_caso_input, pontos_autor_input)
455
- _peticao_completa_exportar = peticao_autor_txt # Salva para cópia
456
- outputs_tuple[0] = peticao_autor_txt
457
  if "ERRO_" in peticao_autor_txt:
458
- error_msg = f"Falha ao gerar Petição Inicial: {peticao_autor_txt}"
459
- logger.error(error_msg)
460
- outputs_tuple[total_outputs-1] = gr.update(value=error_msg) # Status
461
- # Preenche outras saídas de texto com o erro também
462
- for i in range(1, total_outputs -1) : outputs_tuple[i] = error_msg
463
- yield tuple(outputs_tuple)
464
  return
465
 
466
- outputs_tuple[1] = "Gerando Contestação..." # output_contestacao
467
- outputs_tuple[total_outputs -1] = gr.update(value="Gerando Contestação...")
468
- yield tuple(outputs_tuple)
469
-
470
- logger.info("Gerando Contestação do Réu...")
471
- contestacao_reu_txt = GeradorDocumentos.gerar_contestacao(resumo_caso_input, pontos_reu_input, peticao_autor_txt, tom_defesa_reu_input)
472
- _contestacao_completa_exportar = contestacao_reu_txt # Salva para cópia
473
- outputs_tuple[1] = contestacao_reu_txt
474
  if "ERRO_" in contestacao_reu_txt:
475
- error_msg = f"Falha ao gerar Contestação: {contestacao_reu_txt}"
476
- logger.error(error_msg)
477
- outputs_tuple[total_outputs-1] = gr.update(value=error_msg)
478
- for i in range(2, total_outputs -1) : outputs_tuple[i] = error_msg
479
- yield tuple(outputs_tuple)
480
  return
481
 
482
- # Preencher AIJ parts com "Gerando..."
483
- for i in range(len(Config.AIJ_MARKERS_MAP)):
484
- outputs_tuple[2+i] = "Gerando AIJ (Etapa)..."
485
- outputs_tuple[total_outputs -1] = gr.update(value="Simulando AIJ...")
486
- yield tuple(outputs_tuple)
487
-
488
- logger.info("Simulando Audiência de Instrução e Julgamento...")
489
- 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)
490
-
491
  if "ERRO_" in aij_simulacao_txt_completa:
492
- error_msg = f"Falha ao simular AIJ: {aij_simulacao_txt_completa}"
493
- logger.error(error_msg)
494
- for i in range(len(Config.AIJ_MARKERS_MAP)):
495
- outputs_tuple[2+i] = error_msg
496
- outputs_tuple[total_outputs-1] = gr.update(value=error_msg)
497
- yield tuple(outputs_tuple)
498
  return
499
 
500
- aij_partes_dict = ProcessadorTexto.processar_aij(aij_simulacao_txt_completa) # _aij_texto_completo_para_exportar é atualizado aqui
501
-
502
- # Mapear chaves do aij_partes_dict para os índices corretos de outputs_tuple
503
- # outputs_tuple[0] = petição, outputs_tuple[1] = contestação
504
- # outputs_tuple[2] = aij_abertura, ..., outputs_tuple[2 + N-1] = N-ésima parte da AIJ
505
- # outputs_tuple[total_outputs-1] = status
506
  for i, key in enumerate(Config.AIJ_MARKERS_MAP.keys()):
507
- outputs_tuple[2+i] = aij_partes_dict.get(key, f"Falha ao processar seção: {key}")
508
-
509
- outputs_tuple[total_outputs-1] = gr.update(value="Simulação concluída com sucesso.")
510
-
511
  logger.info("Simulação completa e UI atualizada.")
512
- yield tuple(outputs_tuple)
513
 
514
  @staticmethod
515
- def limpar_campos_ui():
516
- # Retorna valores para limpar todos os inputs e outputs
517
  num_aij_outputs = len(Config.AIJ_MARKERS_MAP)
518
- return tuple( [""] * 3 + # resumo, pontos_autor, pontos_reu
519
- [""] * 2 + # peticao, contestacao
520
- [""] * num_aij_outputs + # todas as seções da AIJ
521
- [""] + # caixa de exportação AIJ
522
- [gr.update(value="Campos limpos. Aguardando nova simulação.")] # status
523
- )
524
 
525
  @staticmethod
526
- def restaurar_caso_padrao_ui():
527
  num_aij_outputs = len(Config.AIJ_MARKERS_MAP)
528
- return (
529
- CasoPadrao.RESUMO,
530
- CasoPadrao.AUTOR_PONTOS,
531
- CasoPadrao.REU_PONTOS,
532
- # Limpar outputs
533
- "", "", # peticao, contestacao
534
- *[""] * num_aij_outputs, # AIJ parts
535
- "", # AIJ export box
536
- gr.update(value="Caso padrão restaurado. Clique em Simular.") # status
537
- )
538
 
539
  @staticmethod
540
  def criar_interface() -> gr.Blocks:
541
- global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar
542
-
543
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="amber"), title="Simulador de Fluxo Processual") as demo:
544
- gr.Markdown("# ⚖️ Simulador de Fluxo Processual e AIJ (IA-Powered)")
545
- gr.Markdown("Forneça um resumo do caso e, opcionalmente, pontos chave das partes. A IA gerará as peças principais e simulará a AIJ.")
546
-
547
- if not GEMINI_API_KEY:
548
- gr.Warning("ALERTA: GEMINI_API_KEY não configurada! A aplicação não funcionará adequadamente.")
549
 
550
  with gr.Row():
551
  with gr.Column(scale=1):
552
- gr.Markdown("## Configuração do Caso")
553
- resumo_caso_input = gr.Textbox(
554
- lines=10, label="Resumo Detalhado do Caso:",
555
- placeholder="Descreva os fatos principais...", value=CasoPadrao.RESUMO
556
- )
557
- pontos_autor_input = gr.Textbox(
558
- lines=5, label="Pontos Chave / Provas do AUTOR (Opcional):",
559
- placeholder="Ex: Dano moral...", value=CasoPadrao.AUTOR_PONTOS
560
- )
561
- pontos_reu_input = gr.Textbox(
562
- lines=5, label="Pontos Chave / Provas do RÉU (Opcional):",
563
- placeholder="Ex: Liberdade de imprensa...", value=CasoPadrao.REU_PONTOS
564
- )
565
- tom_defesa_reu_input = gr.Radio(
566
- choices=["Combativa e Firme", "Técnica e Legalista", "Ponderada/Buscando Acordo Sutil"],
567
- label="Tom da Defesa do Réu:", value="Técnica e Legalista"
568
- )
569
- btn_simular = gr.Button("🚀 Simular Processo Completo com IA", variant="primary")
570
  status_output = gr.Label(value="Aguardando simulação...")
571
-
572
  with gr.Row():
573
  btn_limpar_entradas = gr.Button("🧹 Limpar Tudo", variant="secondary", size="sm")
574
  btn_restaurar_padrao = gr.Button("🔄 Restaurar Caso Padrão", variant="secondary", size="sm")
@@ -578,94 +320,60 @@ class InterfaceProcesso:
578
  with gr.Tabs():
579
  with gr.TabItem("📜 Petição Inicial (Autor)"):
580
  output_peticao_inicial = gr.Markdown(show_copy_button=True)
581
- # Opcional: Textbox para cópia se Markdown não for suficiente
582
- # output_peticao_copy_box = gr.Textbox(label="Petição para Copiar:", lines=1, interactive=False, show_copy_button=True)
583
  with gr.TabItem("🛡️ Contestação (Réu)"):
584
  output_contestacao = gr.Markdown(show_copy_button=True)
585
- # output_contestacao_copy_box = gr.Textbox(label="Contestação para Copiar:", lines=1, interactive=False, show_copy_button=True)
586
- with gr.TabItem("🎤 Audiência de Instrução e Julgamento (AIJ)"):
587
- output_aij_export_box = gr.Textbox(label="AIJ Completa (para copiar):", lines=1, info="Este campo será preenchido com o texto completo da AIJ após a simulação.", interactive=False, show_copy_button=True)
588
-
589
- # Criar dinamicamente os Accordions para AIJ
590
  aij_outputs_ui = {}
591
  for key, title in Config.AIJ_MARKERS_MAP.items():
592
- # Remove o "### X. " do título para o Accordion
593
  accordion_title = re.sub(r"### \d+\.\s*", "", title)
594
- with gr.Accordion(accordion_title, open=(key=="1_abertura")): # Abre o primeiro por padrão
 
595
  aij_outputs_ui[key] = gr.Markdown()
596
 
597
- # Lista de todos os componentes de saída da simulação principal
598
- # A ordem DEVE corresponder ao que simular_processo_completo_ui_generator retorna/yield
599
- outputs_simulacao_list = [
600
- output_peticao_inicial,
601
- output_contestacao,
602
- ]
603
- # Adicionar as saídas da AIJ na ordem correta
604
- for key in Config.AIJ_MARKERS_MAP.keys(): # Garante a ordem
605
- outputs_simulacao_list.append(aij_outputs_ui[key])
606
 
 
 
 
607
  outputs_simulacao_list.append(status_output)
608
 
609
- # Lista de componentes a serem limpos/restaurados (entradas + saídas)
610
- componentes_para_limpar_ou_restaurar_entradas = [resumo_caso_input, pontos_autor_input, pontos_reu_input]
611
- componentes_para_limpar_ou_restaurar_saidas = [
612
- output_peticao_inicial, output_contestacao,
613
- output_aij_export_box # Caixa de exportação da AIJ
614
- ]
615
- for key in Config.AIJ_MARKERS_MAP.keys():
616
- componentes_para_limpar_ou_restaurar_saidas.append(aij_outputs_ui[key])
617
- componentes_para_limpar_ou_restaurar_saidas.append(status_output)
618
 
619
 
620
- def update_export_boxes_fn():
621
- global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar
622
- # As caixas de cópia para petição e contestação foram removidas para simplificar,
623
- # pois gr.Markdown já tem botão de cópia.
624
- # Se fossem mantidas, seriam atualizadas aqui:
625
- # yield gr.update(value=_peticao_completa_exportar)
626
- # yield gr.update(value=_contestacao_completa_exportar)
627
- yield gr.update(value=_aij_texto_completo_para_exportar)
628
 
629
  btn_simular.click(
630
  InterfaceProcesso.simular_processo_completo_ui_generator,
631
- inputs=[resumo_caso_input, pontos_autor_input, pontos_reu_input, tom_defesa_reu_input],
632
- outputs=outputs_simulacao_list
633
- ).then(update_export_boxes_fn, outputs=[output_aij_export_box])
634
-
635
 
636
  btn_limpar_entradas.click(
637
- lambda: tuple([""] * len(componentes_para_limpar_ou_restaurar_entradas) +
638
- [""] * len(componentes_para_limpar_ou_restaurar_saidas - [status_output]) + # Exclui status da limpeza de texto
639
- [gr.update(value="Campos limpos. Aguardando nova simulação.")]),
640
  inputs=None,
641
- outputs=componentes_para_limpar_ou_restaurar_entradas + componentes_para_limpar_ou_restaurar_saidas
642
  )
643
-
644
  btn_restaurar_padrao.click(
645
- lambda: (CasoPadrao.RESUMO, CasoPadrao.AUTOR_PONTOS, CasoPadrao.REU_PONTOS) +
646
- tuple([""] * (len(componentes_para_limpar_ou_restaurar_saidas) -1 )) + # Limpa outputs exceto status
647
- (gr.update(value="Caso padrão restaurado. Clique em Simular."),),
648
  inputs=None,
649
- outputs=componentes_para_limpar_ou_restaurar_entradas + componentes_para_limpar_ou_restaurar_saidas
650
  )
651
  return demo
652
 
653
- # --- Ponto de Entrada Principal ---
654
  if __name__ == '__main__':
655
  if not GEMINI_API_KEY:
656
  logger.critical("A API Key do Gemini não está configurada. A aplicação não pode iniciar.")
657
- # Cria uma demo de erro se a chave não estiver presente
658
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="red")) as demo_erro:
659
  gr.Markdown("# ERRO DE CONFIGURAÇÃO CRÍTICO")
660
- gr.Markdown("A chave `GEMINI_API_KEY` não foi configurada corretamente. "
661
- "Verifique os Secrets do seu Space (se estiver no Hugging Face) "
662
- "ou as variáveis de ambiente (se rodando localmente). "
663
- "A aplicação não pode funcionar sem esta chave.")
664
  logger.info("Lançando interface de erro.")
665
- demo_erro.launch(debug=True) # share=True para Colab/link público se necessário
666
  else:
667
- logger.info("Iniciando a interface Gradio do Simulador de Fluxo Processual...")
668
  simulador_interface = InterfaceProcesso.criar_interface()
669
- simulador_interface.queue().launch(debug=True)
670
- # Para Hugging Face, share=False (ou omitir) é geralmente o correto.
671
- # Para Colab, use share=True se o código de setup da API Key do Colab estiver ativo.
 
7
  import logging
8
  from typing import Dict, List, Optional, Union, Tuple, Any
9
 
10
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
 
 
 
 
 
11
  logger = logging.getLogger(__name__)
12
 
 
13
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
14
+ MODEL_NAME = "gemini-1.5-flash-latest"
 
 
 
 
 
 
15
  API_URL_BASE = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL_NAME}:generateContent"
16
 
 
17
  class Config:
18
  MAX_RETRIES = 1
19
+ REQUEST_TIMEOUT = 360
20
  DEFAULT_TEMPERATURE = 0.75
21
+ PETICAO_MAX_TOKENS = 2200 # Aumentado
22
+ CONTESTACAO_MAX_TOKENS = 2200 # Aumentado
23
+ AIJ_MAX_TOKENS = 6000 # Aumentado significativamente para AIJ completa e detalhada
24
 
 
25
  PETICAO_MARKERS_MAP = {
26
  "fatos_autor": "### DOS FATOS (Narrativa do Autor)",
27
  "direito_autor": "### DO DIREITO (Principais Fundamentos Jurídicos)",
 
30
  }
31
  CONTESTACAO_MARKERS_MAP = {
32
  "sintese_reu": "### SÍNTESE DA DEFESA",
33
+ "preliminares_reu": "### PRELIMINARES", # Adicionado para parsing explícito
34
+ "fatos_reu": "### DA REALIDADE DOS FATOS (Narrativa do Réu e Impugnação Específica)",
35
  "direito_reu": "### DO DIREITO (Principais Fundamentos da Defesa)",
36
  "pedidos_reu": "### DOS PEDIDOS (Contrapedidos/Impugnações)",
37
  "estrategias_reu": "### PONTOS ESTRATÉGICOS ADICIONAIS PARA O RÉU"
 
48
  "9_cenarios": "### 9. ANÁLISE DE CENÁRIOS E PONTOS DE INFLEXÃO"
49
  }
50
 
 
51
  class CasoPadrao:
52
+ 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."""
53
+ 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."""
54
+ 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."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
 
56
  _peticao_completa_exportar = ""
57
  _contestacao_completa_exportar = ""
58
  _aij_texto_completo_para_exportar = ""
59
 
 
60
  class GeminiAPI:
61
  @staticmethod
62
  def chamar_api(prompt_text: str, temperature: float = Config.DEFAULT_TEMPERATURE,
63
  max_output_tokens: int = 1500, retries: int = Config.MAX_RETRIES) -> str:
 
64
  if not GEMINI_API_KEY:
65
  logger.error("ERRO_FATAL: API Key do Gemini não configurada.")
66
  return "ERRO_FATAL: API Key do Gemini não configurada."
 
67
  api_url_com_chave = f"{API_URL_BASE}?key={GEMINI_API_KEY}"
68
  headers = {'Content-Type': 'application/json'}
69
+ data = {"contents": [{"parts": [{"text": prompt_text}]}],
70
+ "generationConfig": {"temperature": temperature, "maxOutputTokens": max_output_tokens, "topP": 0.95, "topK": 40},
71
+ "safetySettings": [{"category": f"HARM_CATEGORY_{cat}", "threshold": "BLOCK_NONE"} for cat in ["HARASSMENT", "HATE_SPEECH", "SEXUALLY_EXPLICIT", "DANGEROUS_CONTENT"]]}
72
+ for attempt in range(retries + 1):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  try:
74
+ logger.info(f"API Gemini: Tentativa {attempt+1}/{retries+1}, Prompt: {len(prompt_text)} chars, Max Tokens: {max_output_tokens}")
75
+ response = requests.post(api_url_com_chave, headers=headers, json=data, timeout=Config.REQUEST_TIMEOUT)
 
 
 
 
 
 
76
  response.raise_for_status()
77
  response_json = response.json()
78
+ if response_json.get("candidates") and response_json["candidates"][0].get("content"):
 
 
 
 
 
 
 
79
  generated_text = response_json["candidates"][0]["content"]["parts"][0].get("text", "").strip()
80
  logger.info(f"API Gemini retornou {len(generated_text)} caracteres.")
 
81
  finish_reason = response_json["candidates"][0].get("finishReason", "")
82
  if finish_reason == "MAX_TOKENS":
83
  logger.warning("Geração da API interrompida por MAX_TOKENS.")
84
+ generated_text += "\n\n[AVISO: Geração interrompida por limite de tokens.]"
85
+ elif finish_reason not in ["STOP", "OTHER", None]:
86
+ logger.warning(f"Geração da API terminou com razão: {finish_reason}")
 
87
  return generated_text
 
88
  elif response_json.get("promptFeedback"):
89
+ feedback = response_json["promptFeedback"]
90
+ error_detail = f"Bloqueio: {feedback.get('blockReason', 'N/A')}. Safety: {feedback.get('safetyRatings', 'N/A')}"
91
+ logger.error(f"API: Conteúdo bloqueado. {error_detail}")
92
+ return f"ERRO_API: Conteúdo bloqueado. {error_detail}"
93
+ logger.error(f"API: Resposta inesperada/vazia. JSON: {response_json}")
94
+ return "ERRO_API: Resposta inesperada/vazia da API."
 
 
 
95
  except requests.exceptions.Timeout:
96
+ logger.error(f"API: Timeout na tentativa {attempt+1}.")
97
+ if attempt == retries: return "ERRO_CONEXAO: Timeout excedido."
 
 
 
 
98
  except requests.exceptions.RequestException as e:
99
+ logger.error(f"API: Erro na requisição (tentativa {attempt+1}): {e}", exc_info=True)
100
+ if attempt == retries: return f"ERRO_CONEXAO: {e}"
 
 
 
 
 
 
 
 
101
  except Exception as e:
102
+ logger.exception(f"API: Erro desconhecido (tentativa {attempt+1}): {e}")
103
+ if attempt == retries: return f"ERRO_DESCONHECIDO: {e}"
104
+ return "ERRO_API: Todas as tentativas falharam."
 
 
105
 
 
106
  class GeradorDocumentos:
107
  @staticmethod
108
+ def gerar_peticao_inicial(resumo_caso: str, pontos_autor: str, modificacao_fato: Optional[str], abordagem_autor: str) -> str:
109
+ mod_prompt = f"\n**Modificação Fato:** {modificacao_fato}\n" if modificacao_fato else ""
110
+ ab_prompt = f"\n**Abordagem Autor:** {abordagem_autor}\n"
111
+ prompt = f"""**Persona:** Advogado experiente (Direitos da Personalidade), redigindo Petição Inicial (Esquecimento vs. Imprensa).
112
+ {mod_prompt}{ab_prompt}**Caso Base:** {resumo_caso}
113
+ **Pontos Autor:** {pontos_autor or "Inferir do resumo e abordagem."}
114
+ **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.
115
+ **Formato Saída (marcadores exatos):**
116
+ `{Config.PETICAO_MARKERS_MAP["fatos_autor"]}`
117
+ [Conteúdo]
118
+ `{Config.PETICAO_MARKERS_MAP["direito_autor"]}`
119
+ [Conteúdo]
120
+ `{Config.PETICAO_MARKERS_MAP["pedidos_autor"]}`
121
+ [Conteúdo]
122
+ `{Config.PETICAO_MARKERS_MAP["estrategias_autor"]}`
123
+ [Conteúdo]"""
124
+ return GeminiAPI.chamar_api(prompt, max_output_tokens=Config.PETICAO_MAX_TOKENS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  @staticmethod
127
+ def gerar_contestacao(resumo_caso: str, pontos_reu: str, peticao_autor: str, tom_defesa: str, modificacao_fato: Optional[str]) -> str:
128
+ mod_prompt = f"\n**Modificação Fato (do caso original):** {modificacao_fato}\n" if modificacao_fato else ""
129
+ prompt = f"""**Persona:** Advogado experiente (Liberdade de Imprensa), redigindo Contestação (Esquecimento vs. Imprensa).
130
+ **Tom Defesa:** {tom_defesa}
131
+ {mod_prompt}**Caso Base:** {resumo_caso}
132
+ **Pontos Réu:** {pontos_reu or "Inferir do resumo."}
133
+ **Petição Autor:** ```{peticao_autor}```
134
+ **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.
135
+ **Formato Saída (marcadores exatos):**
136
+ `{Config.CONTESTACAO_MARKERS_MAP["sintese_reu"]}`
137
+ [Conteúdo]
138
+ (Se houver preliminares: `{Config.CONTESTACAO_MARKERS_MAP["preliminares_reu"]}`\n[Conteúdo])
139
+ `{Config.CONTESTACAO_MARKERS_MAP["fatos_reu"]}`
140
+ [Conteúdo]
141
+ `{Config.CONTESTACAO_MARKERS_MAP["direito_reu"]}`
142
+ [Conteúdo]
143
+ `{Config.CONTESTACAO_MARKERS_MAP["pedidos_reu"]}`
144
+ [Conteúdo]
145
+ `{Config.CONTESTACAO_MARKERS_MAP["estrategias_reu"]}`
146
+ [Conteúdo]"""
147
+ return GeminiAPI.chamar_api(prompt, max_output_tokens=Config.CONTESTACAO_MAX_TOKENS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  @staticmethod
150
  def simular_aij(resumo_caso: str, peticao_autor: str, contestacao_reu: str,
151
+ pontos_autor: str, pontos_reu: str, tom_defesa_reu: str,
152
+ modificacao_fato: Optional[str], abordagem_autor: str) -> str:
153
+ mod_prompt = f"\n**Modificação Fato (para AIJ):** {modificacao_fato}\n" if modificacao_fato else ""
154
+ ab_prompt = f"\n**Abordagem Autor (para AIJ):** {abordagem_autor}\n"
155
+ prompt = f"""**Persona:** Narrador Jurídico imparcial simulando AIJ (Esquecimento vs. Imprensa).
156
+ {mod_prompt}{ab_prompt}**Contexto Caso Original:** {resumo_caso}
157
+ **Petição Autor:** ```{peticao_autor}``` {("Pontos Autor: " + pontos_autor) if pontos_autor else ""}
158
+ **Contestação Réu (Tom: {tom_defesa_reu}):** ```{contestacao_reu}``` {("Pontos Réu: " + pontos_reu) if pontos_reu else ""}
159
+ **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.
160
+ **Formato Saída (marcadores exatos):**
161
+ `{Config.AIJ_MARKERS_MAP["1_abertura"]}` \n[Conteúdo]
162
+ `{Config.AIJ_MARKERS_MAP["2_dep_autor"]}` \n[Conteúdo]
163
+ `{Config.AIJ_MARKERS_MAP["3_dep_reu"]}` \n[Conteúdo]
164
+ `{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]]
165
+ `{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]]
166
+ `{Config.AIJ_MARKERS_MAP["6_aleg_autor"]}` \n[Conteúdo]
167
+ `{Config.AIJ_MARKERS_MAP["7_aleg_reu"]}` \n[Conteúdo]
168
+ `{Config.AIJ_MARKERS_MAP["8_conc_juiz"]}` \n[Conteúdo]
169
+ `{Config.AIJ_MARKERS_MAP["9_cenarios"]}` \n[a) Ponto de Virada: ... b) Estratégia Alternativa: ... c) Fator Externo: ...]"""
170
+ return GeminiAPI.chamar_api(prompt, temperature=0.8, max_output_tokens=Config.AIJ_MAX_TOKENS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  class ProcessadorTexto:
173
  @staticmethod
174
  def _extrair_secao(texto_completo: str, texto_lower: str, marcador_inicio: str, marcadores_subsequentes: List[str]) -> Optional[str]:
 
175
  start_idx = texto_lower.find(marcador_inicio.lower())
176
+ if start_idx == -1: return None
 
 
 
177
  content_start_idx = start_idx + len(marcador_inicio)
 
178
  end_idx = len(texto_completo)
179
  for next_marcador in marcadores_subsequentes:
180
  next_marker_idx_found = texto_lower.find(next_marcador.lower(), content_start_idx)
181
  if next_marker_idx_found != -1:
182
  end_idx = min(end_idx, next_marker_idx_found)
 
183
  return texto_completo[content_start_idx:end_idx].strip()
184
 
185
  @staticmethod
186
  def processar_documento(texto_completo: str, markers_map: Dict[str, str]) -> Dict[str, str]:
187
+ global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar
188
+ if markers_map == Config.PETICAO_MARKERS_MAP: _peticao_completa_exportar = texto_completo
189
+ elif markers_map == Config.CONTESTACAO_MARKERS_MAP: _contestacao_completa_exportar = texto_completo
190
+ elif markers_map == Config.AIJ_MARKERS_MAP: _aij_texto_completo_para_exportar = texto_completo
 
 
191
 
192
  if not texto_completo or "ERRO_" in texto_completo:
193
+ logger.error(f"Erro na entrada do processador: {texto_completo}")
194
+ return {key: f"Erro ao gerar/processar seção: {texto_completo}" for key in markers_map.keys()}
195
 
196
+ partes_documento = {key: f"Seção '{marker_text}' não encontrada." for key, marker_text in markers_map.items()}
 
 
 
197
  texto_lower = texto_completo.lower()
198
+ sorted_marcadores_list = list(markers_map.items())
 
199
  for i, (key, marcador_inicio) in enumerate(sorted_marcadores_list):
200
+ marcadores_subsequentes_text = [m_text for _, m_text in sorted_marcadores_list[i+1:]]
 
 
201
  conteudo_secao = ProcessadorTexto._extrair_secao(texto_completo, texto_lower, marcador_inicio, marcadores_subsequentes_text)
202
+ if conteudo_secao is not None: partes_documento[key] = conteudo_secao
203
+ else: logger.warning(f"Marcador não encontrado ao processar: '{marcador_inicio}' para '{key}'")
 
204
  return partes_documento
205
 
 
 
 
 
 
 
 
 
206
  class InterfaceProcesso:
 
207
  @staticmethod
208
  def simular_processo_completo_ui_generator(resumo_caso_input: str, pontos_autor_input: str,
209
+ pontos_reu_input: str, tom_defesa_reu_input: str,
210
+ modificacao_fato_input: Optional[str],
211
+ abordagem_autor_input: Optional[str]):
212
  global _peticao_completa_exportar, _contestacao_completa_exportar, _aij_texto_completo_para_exportar
213
+ _peticao_completa_exportar = _contestacao_completa_exportar = _aij_texto_completo_para_exportar = ""
214
+
215
+ num_aij_outputs = len(Config.AIJ_MARKERS_MAP)
216
+ total_outputs = 2 + num_aij_outputs + 1 # Pet, Cont, AIJ_parts, Status
217
+ outputs_tuple_list = [""] * total_outputs
218
 
219
+ def create_error_tuple(base_error_msg: str):
220
+ err_list = [base_error_msg] * (total_outputs -1)
221
+ err_list.append(gr.update(value=base_error_msg))
222
+ return tuple(err_list)
 
 
 
 
 
 
 
 
 
 
223
 
224
  if not GEMINI_API_KEY:
225
+ yield create_error_tuple("ERRO FATAL: API Key do Gemini não configurada.")
 
 
226
  return
 
227
  if not resumo_caso_input:
228
+ yield create_error_tuple("Erro: O resumo do caso é obrigatório.")
 
 
229
  return
230
 
231
+ outputs_tuple_list[total_outputs - 1] = gr.update(value="Iniciando: Gerando Petição Inicial...")
232
+ outputs_tuple_list[0] = "Gerando..."
233
+ yield tuple(outputs_tuple_list)
234
+ peticao_autor_txt = GeradorDocumentos.gerar_peticao_inicial(resumo_caso_input, pontos_autor_input, modificacao_fato_input, abordagem_autor_input)
235
+ outputs_tuple_list[0] = peticao_autor_txt
236
+ _peticao_completa_exportar = peticao_autor_txt
 
 
 
237
  if "ERRO_" in peticao_autor_txt:
238
+ logger.error(f"Falha Petição: {peticao_autor_txt}")
239
+ outputs_tuple_list[total_outputs-1] = gr.update(value=f"Erro Petição: {peticao_autor_txt[:100]}...")
240
+ for i in range(1, total_outputs -1) : outputs_tuple_list[i] = peticao_autor_txt
241
+ yield tuple(outputs_tuple_list)
 
 
242
  return
243
 
244
+ outputs_tuple_list[1] = "Gerando Contestação..."
245
+ outputs_tuple_list[total_outputs -1] = gr.update(value="Gerando Contestação...")
246
+ yield tuple(outputs_tuple_list)
247
+ contestacao_reu_txt = GeradorDocumentos.gerar_contestacao(resumo_caso_input, pontos_reu_input, peticao_autor_txt, tom_defesa_reu_input, modificacao_fato_input)
248
+ outputs_tuple_list[1] = contestacao_reu_txt
249
+ _contestacao_completa_exportar = contestacao_reu_txt
 
 
250
  if "ERRO_" in contestacao_reu_txt:
251
+ logger.error(f"Falha Contestação: {contestacao_reu_txt}")
252
+ outputs_tuple_list[total_outputs-1] = gr.update(value=f"Erro Contestação: {contestacao_reu_txt[:100]}...")
253
+ for i in range(2, total_outputs -1) : outputs_tuple_list[i] = contestacao_reu_txt
254
+ yield tuple(outputs_tuple_list)
 
255
  return
256
 
257
+ for i in range(num_aij_outputs): outputs_tuple_list[2+i] = "Gerando AIJ..."
258
+ outputs_tuple_list[total_outputs -1] = gr.update(value="Simulando AIJ...")
259
+ yield tuple(outputs_tuple_list)
260
+ 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)
 
 
 
 
 
261
  if "ERRO_" in aij_simulacao_txt_completa:
262
+ logger.error(f"Falha AIJ: {aij_simulacao_txt_completa}")
263
+ for i in range(num_aij_outputs): outputs_tuple_list[2+i] = aij_simulacao_txt_completa
264
+ outputs_tuple_list[total_outputs-1] = gr.update(value=f"Erro AIJ: {aij_simulacao_txt_completa[:100]}...")
265
+ yield tuple(outputs_tuple_list)
 
 
266
  return
267
 
268
+ aij_partes_dict = ProcessadorTexto.processar_aij(aij_simulacao_txt_completa)
 
 
 
 
 
269
  for i, key in enumerate(Config.AIJ_MARKERS_MAP.keys()):
270
+ outputs_tuple_list[2+i] = aij_partes_dict.get(key, f"Falha ao processar seção AIJ: {key}")
271
+ outputs_tuple_list[total_outputs-1] = gr.update(value="Simulação concluída com sucesso.")
 
 
272
  logger.info("Simulação completa e UI atualizada.")
273
+ yield tuple(outputs_tuple_list)
274
 
275
  @staticmethod
276
+ def limpar_interface_fn():
 
277
  num_aij_outputs = len(Config.AIJ_MARKERS_MAP)
278
+ return ("", "", "", "Padrão", "", "Técnica e Legalista", # Entradas
279
+ "", "", # Petição, Contestação
280
+ *[""] * num_aij_outputs, # AIJ parts
281
+ gr.update(value="Campos e saídas limpos."), # Status
282
+ "" # AIJ export box
283
+ )
284
 
285
  @staticmethod
286
+ def restaurar_padrao_fn():
287
  num_aij_outputs = len(Config.AIJ_MARKERS_MAP)
288
+ return (CasoPadrao.RESUMO, "", CasoPadrao.AUTOR_PONTOS, "Padrão", CasoPadrao.REU_PONTOS, "Técnica e Legalista",
289
+ "", "", *[""] * num_aij_outputs,
290
+ gr.update(value="Caso padrão restaurado. Modifique e simule."), ""
291
+ )
 
 
 
 
 
 
292
 
293
  @staticmethod
294
  def criar_interface() -> gr.Blocks:
295
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="amber"), title="Simulador de Fluxo Processual Avançado") as demo:
296
+ gr.Markdown("# ⚖️ Simulador Avançado de Fluxo Processual e AIJ (IA-Powered)")
297
+ gr.Markdown("Forneça um caso, modifique fatos, escolha abordagens e veja a IA simular o processo.")
298
+ if not GEMINI_API_KEY: gr.Warning("ALERTA: GEMINI_API_KEY não configurada!")
 
 
 
 
299
 
300
  with gr.Row():
301
  with gr.Column(scale=1):
302
+ gr.Markdown("## Configuração do Caso e Variações")
303
+ resumo_caso_input = gr.Textbox(lines=8, label="Resumo Detalhado do Caso Base:", value=CasoPadrao.RESUMO)
304
+ 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.")
305
+ with gr.Row():
306
+ with gr.Column():
307
+ pontos_autor_input = gr.Textbox(lines=4, label="Pontos/Provas do AUTOR (Opcional):", value=CasoPadrao.AUTOR_PONTOS)
308
+ 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")
309
+ with gr.Column():
310
+ pontos_reu_input = gr.Textbox(lines=4, label="Pontos/Provas do RÉU (Opcional):", value=CasoPadrao.REU_PONTOS)
311
+ 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")
312
+ btn_simular = gr.Button("🚀 Simular Processo com Variações", variant="primary")
 
 
 
 
 
 
 
313
  status_output = gr.Label(value="Aguardando simulação...")
 
314
  with gr.Row():
315
  btn_limpar_entradas = gr.Button("🧹 Limpar Tudo", variant="secondary", size="sm")
316
  btn_restaurar_padrao = gr.Button("🔄 Restaurar Caso Padrão", variant="secondary", size="sm")
 
320
  with gr.Tabs():
321
  with gr.TabItem("📜 Petição Inicial (Autor)"):
322
  output_peticao_inicial = gr.Markdown(show_copy_button=True)
 
 
323
  with gr.TabItem("🛡️ Contestação (Réu)"):
324
  output_contestacao = gr.Markdown(show_copy_button=True)
325
+ with gr.TabItem("🎤 Audiência (AIJ)"):
326
+ 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)
 
 
 
327
  aij_outputs_ui = {}
328
  for key, title in Config.AIJ_MARKERS_MAP.items():
 
329
  accordion_title = re.sub(r"### \d+\.\s*", "", title)
330
+ is_open = key in ["1_abertura", "9_cenarios"]
331
+ with gr.Accordion(accordion_title, open=is_open):
332
  aij_outputs_ui[key] = gr.Markdown()
333
 
334
+ all_inputs = [resumo_caso_input, modificacao_fato_input, pontos_autor_input, abordagem_autor_input, pontos_reu_input, tom_defesa_reu_input]
 
 
 
 
 
 
 
 
335
 
336
+ # Construir lista de outputs na ordem correta
337
+ outputs_simulacao_list = [output_peticao_inicial, output_contestacao]
338
+ for key_aij in Config.AIJ_MARKERS_MAP.keys(): outputs_simulacao_list.append(aij_outputs_ui[key_aij])
339
  outputs_simulacao_list.append(status_output)
340
 
341
+ # Saídas para limpar/restaurar (inclui a caixa de exportação da AIJ)
342
+ outputs_to_clear_restore = [output_peticao_inicial, output_contestacao]
343
+ for key_aij_clear in Config.AIJ_MARKERS_MAP.keys(): outputs_to_clear_restore.append(aij_outputs_ui[key_aij_clear])
344
+ outputs_to_clear_restore.extend([status_output, output_aij_export_box])
 
 
 
 
 
345
 
346
 
347
+ def update_export_box_after_sim():
348
+ global _aij_texto_completo_para_exportar
349
+ return _aij_texto_completo_para_exportar
 
 
 
 
 
350
 
351
  btn_simular.click(
352
  InterfaceProcesso.simular_processo_completo_ui_generator,
353
+ inputs=all_inputs, outputs=outputs_simulacao_list
354
+ ).then(update_export_box_after_sim, outputs=[output_aij_export_box])
 
 
355
 
356
  btn_limpar_entradas.click(
357
+ InterfaceProcesso.limpar_interface_fn,
 
 
358
  inputs=None,
359
+ outputs=all_inputs + outputs_to_clear_restore
360
  )
 
361
  btn_restaurar_padrao.click(
362
+ InterfaceProcesso.restaurar_padrao_fn,
 
 
363
  inputs=None,
364
+ outputs=all_inputs + outputs_to_clear_restore
365
  )
366
  return demo
367
 
 
368
  if __name__ == '__main__':
369
  if not GEMINI_API_KEY:
370
  logger.critical("A API Key do Gemini não está configurada. A aplicação não pode iniciar.")
 
371
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="red")) as demo_erro:
372
  gr.Markdown("# ERRO DE CONFIGURAÇÃO CRÍTICO")
373
+ gr.Markdown("A chave `GEMINI_API_KEY` não foi configurada corretamente...") # Mensagem completa
 
 
 
374
  logger.info("Lançando interface de erro.")
375
+ demo_erro.launch(debug=True)
376
  else:
377
+ logger.info("Iniciando a interface Gradio do Simulador de Fluxo Processual Avançado...")
378
  simulador_interface = InterfaceProcesso.criar_interface()
379
+ simulador_interface.queue().launch(debug=True)