GabrielGD commited on
Commit
65d0ba0
·
verified ·
1 Parent(s): 9d5615f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -161
app.py CHANGED
@@ -204,8 +204,7 @@ def process_file(file_obj):
204
  logger.warning("--- Nenhum arquivo fornecido para processamento.")
205
  return "⚠️ Nenhum arquivo enviado."
206
  try:
207
- # O file_obj do Gradio já é um objeto de arquivo temporário
208
- file_path = file_obj.name # Obter o path temporário
209
  filename = os.path.basename(file_path).lower()
210
  logger.info(f"--- Processando arquivo: {filename} (path: {file_path})")
211
 
@@ -219,7 +218,6 @@ def process_file(file_obj):
219
  logger.warning(f"--- Não foi possível verificar o tamanho do arquivo: {size_e}")
220
 
221
  result_text = ""
222
- # Passar o file_path para as funções de extração
223
  if filename.endswith('.pdf'):
224
  logger.info("--- Chamando extract_text_from_pdf")
225
  result_text = extract_text_from_pdf(file_path)
@@ -611,33 +609,113 @@ def estruturar_prompt_peticao_corrigida(caso, peticao_original, tipo_peticao):
611
  prompt += "\n\n**Instrução Final:** Gere **APENAS** o texto completo da peça corrigida abaixo, começando pelo endereçamento e terminando com 'OAB/...'. Não inclua NENHUM comentário, introdução ou explicação ANTES ou DEPOIS do texto da peça."
612
  return prompt
613
 
614
- def pos_corrigir_peticao(peticao_corrigida, tipo_peticao):
615
- if "Nestes termos, pede deferimento" in peticao_corrigida:
616
- peticao_corrigida = peticao_corrigida.replace("Nestes termos, pede deferimento", "Nestes termos pede deferimento")
 
 
 
 
 
 
 
 
 
617
 
618
- if tipo_peticao == "Petição Inicial":
619
- if "juntada da guia de custas" not in peticao_corrigida.lower() and "gratuidade de justiça" not in peticao_corrigida.lower():
620
- partes = peticao_corrigida.split("Nestes termos pede deferimento")
621
- if len(partes) == 2:
622
- peticao_corrigida = partes[0] + "Requer, ainda, a juntada da guia de custas devidamente recolhida.\nNestes termos pede deferimento" + partes[1]
623
- else:
624
- peticao_corrigida = peticao_corrigida.replace("Local, data.", "Requer, ainda, a juntada da guia de custas devidamente recolhida.\n\nLocal, data.")
625
-
626
- if "audiência de conciliação" not in peticao_corrigida.lower() and "audiência de mediação" not in peticao_corrigida.lower():
627
- partes = peticao_corrigida.split("Nestes termos pede deferimento")
628
- if len(partes) == 2:
629
- peticao_corrigida = partes[0] + "Opta o Autor pela realização da audiência de conciliação ou mediação (Art. 319, VII, CPC).\nNestes termos pede deferimento" + partes[1]
630
- else:
631
- peticao_corrigida = peticao_corrigida.replace("Local, data.", "Opta o Autor pela realização da audiência de conciliação ou mediação (Art. 319, VII, CPC).\n\nLocal, data.")
632
-
633
- linhas = [l.strip() for l in peticao_corrigida.strip().split('\n') if l.strip()]
634
- fechamento_padrao = ["Local, data.", "ADVOGADO(A)", "OAB/..."]
635
- while linhas and any(term in linhas[-1] for term in ["ADVOGADO", "OAB", "Local", "data"]):
636
- linhas.pop()
637
- linhas.extend(fechamento_padrao)
638
- peticao_corrigida = "\n".join(linhas)
639
-
640
- return peticao_corrigida
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
 
642
  # --- Função Principal de Correção ---
643
  def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
@@ -655,7 +733,7 @@ def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=Non
655
  peticao_conteudo = ""
656
  if peticao_arquivo is not None:
657
  logger.info("--- Processando arquivo de petição...")
658
- peticao_conteudo = process_file(peticao_arquivo) # process_file lida com o objeto Gradio
659
  if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
660
  logger.error(f"--- Erro ao processar arquivo: {peticao_conteudo}")
661
  return peticao_conteudo, "", "", "", "", ""
@@ -667,7 +745,6 @@ def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=Non
667
  logger.warning("--- Nenhuma petição fornecida (nem texto, nem arquivo).")
668
  return "⚠️ Forneça a petição (em texto ou arquivo).", "", "", "", "", ""
669
 
670
- # Verificação Anti-Troll
671
  troll_triggers = [
672
  "escreva a petição", "faça a petição", "redija a petição",
673
  "elabore a petição", "crie a petição", "gere a petição",
@@ -691,16 +768,15 @@ def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=Non
691
 
692
  aviso_validacao = ""
693
 
694
- # Cache Check
695
  cache_key = f"{tipo_peticao}|{caso[:100]}|{peticao_conteudo[:100]}|{modo_treinamento}"
696
  if cache_key in CORRECAO_CACHE:
697
  logger.info("--- Correção encontrada no cache.")
698
  cached_data = CORRECAO_CACHE[cache_key]
699
  diff_html_cache, _, _ = calcular_diff_interno(cached_data["peticao_original"], cached_data["peticao_corrigida"])
700
  logger.info("<<< CORRIGIR PETICAO FINALIZADO (CACHE HIT)")
 
701
  return cached_data["analise_formatada"], cached_data["peticao_original"], cached_data["peticao_corrigida"], cached_data["resumo_diff"], cached_data["criticas_diff"], diff_html_cache
702
 
703
- # Chamada para Análise
704
  logger.info("--- Gerando prompt de análise...")
705
  prompt_analise = estruturar_prompt_analise(caso, peticao_conteudo, tipo_peticao, modo_treinamento)
706
  logger.info("--- Enviando para análise da API...")
@@ -711,7 +787,6 @@ def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=Non
711
  logger.info("--- Análise recebida da API.")
712
  analise_formatada = formatar_correcao(analise)
713
 
714
- # Chamada para Gerar Petição Corrigida
715
  logger.info("--- Gerando prompt para peça corrigida...")
716
  prompt_peticao = estruturar_prompt_peticao_corrigida(caso, peticao_conteudo, tipo_peticao)
717
  logger.info("--- Enviando para geração da peça corrigida API...")
@@ -740,12 +815,10 @@ def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=Non
740
  peticao_corrigida_final = pos_corrigir_peticao(peticao_extraida, tipo_peticao)
741
  logger.info("--- Pós-correção aplicada.")
742
 
743
- # Cálculo de Diferenças
744
  logger.info("--- Calculando diferenças...")
745
  diff_html, resumo_diff, criticas_diff = calcular_diff_interno(peticao_conteudo, peticao_corrigida_final)
746
  logger.info("--- Diferenças calculadas.")
747
 
748
- # Histórico e Cache
749
  correcao_completa_formatada = aviso_validacao + analise_formatada
750
  resultado_historico = {
751
  "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
@@ -771,9 +844,10 @@ def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=Non
771
 
772
  end_time = time.time()
773
  logger.info(f"<<< CORRIGIR PETICAO FINALIZADO em {end_time - start_time:.2f} segundos.")
 
774
  return correcao_completa_formatada, peticao_conteudo, peticao_corrigida_final, resumo_diff, criticas_diff, diff_html
775
 
776
- # --- Funções Auxiliares (Formatação, Dicas, Diff, etc.) ---
777
  def formatar_correcao(texto):
778
  texto = texto.replace("### Análise Geral", "## 📝 Análise Geral")
779
  texto = texto.replace("### Nota Final", "## ⭐ Nota Final")
@@ -1038,94 +1112,7 @@ def carregar_progresso(arquivo_entrada):
1038
  logger.info("<<< CARREGAR PROGRESSO FINALIZADO COM ERRO")
1039
  return "", "", "", f"❌ Erro inesperado ao carregar o progresso: {str(e)}"
1040
 
1041
- def estruturar_prompt_geracao_caso(tipo_peticao="Petição Inicial"):
1042
- prompt = f"""
1043
- Crie um caso fictício **detalhado e realista** para um exercício prático de elaboração de uma peça do tipo **"{tipo_peticao}"**, simulando um cenário de Exame de Ordem (OAB).
1044
-
1045
- **Instruções Gerais para o Caso:**
1046
- 1. **Complexidade Adequada:** Deve ter informações suficientes para redigir a peça solicitada, mas sem ser excessivamente longo ou complicado. (Ideal: 15-25 linhas).
1047
- 2. **Dados Essenciais:** Inclua nomes completos (fictícios) das partes envolvidas, locais (cidades/estados fictícios ou genéricos), datas relevantes, valores monetários (se aplicável), e uma descrição clara da relação jurídica e do conflito.
1048
- 3. **Coerência:** Os fatos devem ser lógicos e justificar a peça processual indicada.
1049
- 4. **Foco no Tipo de Peça:** O cenário deve ser construído para exigir especificamente a elaboração de um(a) **{tipo_peticao}**.
1050
-
1051
- **Instruções Específicas para Gerar Caso para "{tipo_peticao}":**
1052
- """
1053
- if tipo_peticao == "Petição Inicial":
1054
- prompt += """
1055
- * Descreva uma situação onde direitos foram violados (contrato descumprido, dano sofrido, etc.) e o cliente (autor) busca a tutela jurisdicional pela primeira vez.
1056
- * Inclua elementos que permitam calcular danos materiais e/ou justificar danos morais.
1057
- * Forneça dados para qualificação das partes (autor e réu).
1058
- * **Exemplo de Tema:** Acidente de consumo, inadimplemento contratual, cobrança indevida, ação de despejo, etc.
1059
- """
1060
- elif tipo_peticao == "Contestação":
1061
- prompt += """
1062
- * **Apresente PRIMEIRO um resumo da Petição Inicial** que o réu (seu cliente) recebeu (quem processou, o que alegou, o que pediu).
1063
- * **Depois, apresente a versão dos fatos do RÉU**, incluindo informações que permitam:
1064
- * Alegar preliminares (ex: incompetência do juízo, ilegitimidade da parte, etc.).
1065
- * Impugnar os fatos da inicial.
1066
- * Apresentar defesa de mérito (ex: pagamento já realizado, culpa exclusiva da vítima, prescrição, etc.).
1067
- * Forneça dados para qualificação do réu.
1068
- """
1069
- elif tipo_peticao == "Reconvenção":
1070
- prompt += """
1071
- * Siga a estrutura da **Contestação**: Apresente o resumo da Petição Inicial do autor original.
1072
- * Apresente a versão dos fatos do Réu (seu cliente) para a defesa.
1073
- * **ADICIONE fatos e fundamentos que permitam ao RÉU fazer um pedido PRÓPRIO contra o AUTOR ORIGINAL**, que seja CONEXO com a ação principal ou com a defesa.
1074
- * **Exemplo:** Numa ação de cobrança, o réu contesta e reconvém pedindo indenização por danos que o autor lhe causou no mesmo negócio.
1075
- """
1076
- elif tipo_peticao == "Impugnação à Contestação":
1077
- prompt += """
1078
- * Apresente um resumo da Petição Inicial original do seu cliente (Autor).
1079
- * **Apresente um resumo da Contestação** apresentada pelo réu, destacando:
1080
- * As preliminares arguidas pelo réu (se houver).
1081
- * Os fatos novos (impeditivos, modificativos, extintivos) alegados pelo réu.
1082
- * Os documentos juntados pelo réu (se houver menção a algum relevante).
1083
- * O objetivo é fornecer elementos para o Autor rebater a contestação.
1084
- """
1085
- elif tipo_peticao == "Embargos de Declaração":
1086
- prompt += """
1087
- * Descreva brevemente o caso original (quem processou quem e por quê).
1088
- * **Apresente o DISPOSITIVO ou um TRECHO FUNDAMENTAL de uma DECISÃO JUDICIAL (sentença, acórdão, etc.)** proferida no caso.
1089
- * **INDIQUE CLARAMENTE qual o vício (omissão, contradição, obscuridade ou erro material) presente NESSA DECISÃO**, fornecendo contexto para que o aluno possa identificar e fundamentar os Embargos.
1090
- * **Exemplo:** "A sentença julgou procedente o pedido X, mas foi omissa quanto ao pedido Y que também constava na inicial." OU "O acórdão afirmou X na fundamentação, mas concluiu Y no dispositivo, gerando contradição."
1091
- """
1092
- elif tipo_peticao == "Recurso de Apelação":
1093
- prompt += """
1094
- * Descreva o caso original (Autor vs. Réu, causa de pedir, pedidos).
1095
- * **Apresente um resumo da SENTENÇA proferida pelo juiz de primeira instância**, indicando se foi procedente, improcedente, parcialmente procedente, e os principais fundamentos do juiz.
1096
- * **FORNEÇA ELEMENTOS que indiquem um ERRO na sentença (error in judicando ou in procedendo)**, que justifiquem a reforma ou anulação pelo Tribunal.
1097
- * **Exemplo:** "A sentença julgou improcedente o pedido por falta de provas, ignorando o documento X juntado aos autos." OU "O juiz indeferiu a oitiva da testemunha chave Y, cerceando a defesa do réu, e julgou procedente."
1098
- * Indique quem é o cliente (apelante).
1099
- """
1100
- elif tipo_peticao == "Agravo de Instrumento":
1101
- prompt += """
1102
- * Descreva o caso original em andamento.
1103
- * **Apresente uma DECISÃO INTERLOCUTÓRIA específica** proferida pelo juiz (NÃO a sentença final).
1104
- * **A decisão DEVE se enquadrar numa das hipóteses do Art. 1015 do CPC** (ou justificar a taxatividade mitigada).
1105
- * **FORNEÇA ELEMENTOS que indiquem um ERRO nessa decisão interlocutória** E, se possível, a **URGÊNCIA** em revertê-la (risco de dano).
1106
- * **Exemplo:** "O juiz indeferiu o pedido de gratuidade de justiça, embora o autor tenha comprovado baixa renda." OU "O juiz deferiu a liminar de busca e apreensão do bem X sem a devida comprovação da mora."
1107
- * Indique quem é o cliente (agravante).
1108
- """
1109
- elif tipo_peticao == "Petição de Tutela Provisória":
1110
- prompt += """
1111
- * Descreva uma situação de conflito onde uma das partes precisa de uma medida **urgente** (risco iminente) ou onde seu direito é **evidente**.
1112
- * **FORNEÇA ELEMENTOS claros que demonstrem:**
1113
- * **Para Urgência:** A probabilidade do direito (fumus boni iuris) E o perigo de dano ou risco ao resultado útil do processo (periculum in mora).
1114
- * **Para Evidência:** Uma das hipóteses do Art. 311 do CPC (ex: defesa protelatória, tese firmada em repetitivo, etc.).
1115
- * Indique qual medida específica o cliente deseja (ex: bloqueio de bens, retirada de nome do SPC, etc.).
1116
- * Especifique se a tutela é pedida ANTES do processo principal (antecedente) ou DENTRO de um processo já existente (incidental).
1117
- """
1118
- else:
1119
- prompt += "* Crie um cenário jurídico cível com um conflito claro entre duas ou mais partes."
1120
-
1121
- prompt += """
1122
- **Finalize o caso com:**
1123
- "**Situação-Problema:** Na qualidade de advogado(a) de [Nome do Cliente], elabore a peça processual cabível ({tipo_peticao})."
1124
-
1125
- **Importante:** Evite criar casos que dependam de conhecimento jurídico EXTREMAMENTE específico ou de nicho. Foque em temas comuns do direito civil e processual civil. Seja criativo!
1126
- """
1127
- return prompt
1128
-
1129
  def gerar_caso_ficticio(tipo_peticao="Petição Inicial"):
1130
  logger.info(f">>> GERAR CASO FICTICIO INICIADO para: {tipo_peticao}")
1131
  if not tipo_peticao:
@@ -1170,6 +1157,9 @@ def interface_gradio():
1170
  gr.Markdown("# ⚖️ Corretor Jurídico OAB Master ⚖️")
1171
  gr.Markdown("Analise e aprimore suas peças processuais! Selecione o tipo, forneça o caso e sua peça (texto ou arquivo).")
1172
 
 
 
 
1173
  with gr.Row():
1174
  with gr.Column(scale=1):
1175
  gr.Markdown("### Configurações")
@@ -1206,6 +1196,7 @@ def interface_gradio():
1206
  salvar_btn = gr.Button("💾 Salvar Caso/Peça Atual")
1207
  carregar_btn = gr.Button("📂 Carregar Caso/Peça de Arquivo")
1208
  arquivo_progresso_upload = gr.File(label="Selecione o arquivo .json para carregar", file_types=[".json"], scale=2)
 
1209
  progresso_download_file = gr.File(label="Baixar Progresso Salvo", interactive=False)
1210
 
1211
  with gr.Column(scale=3):
@@ -1247,16 +1238,17 @@ def interface_gradio():
1247
 
1248
  clear_btn = gr.Button("🗑️ Limpar Tudo")
1249
 
1250
- # --- FUNÇÕES WRAPPER (com logs) ---
1251
  def corrigir_peticao_wrapper_texto(caso, tipo_peticao, peticao_texto, peticao_arquivo_state):
1252
  logger.info(">>> WRAPPER TEXTO INICIADO")
1253
  is_treinamento = modo_treinamento.value
1254
  logger.info(f"--- Modo Treinamento (Texto): {is_treinamento}")
1255
  try:
 
1256
  resultado = corrigir_peticao(caso, tipo_peticao, peticao_texto, peticao_arquivo_state, is_treinamento)
1257
  except Exception as e:
1258
  logger.error(f"Erro dentro do wrapper texto chamando corrigir_peticao: {e}", exc_info=True)
1259
- # Retornar tupla com erro para todos os outputs esperados
1260
  return f"❌ Erro interno no processamento: {e}", "", "", "", "", ""
1261
  logger.info("<<< WRAPPER TEXTO FINALIZADO")
1262
  return resultado
@@ -1266,16 +1258,18 @@ def interface_gradio():
1266
  is_treinamento = modo_treinamento.value
1267
  logger.info(f"--- Modo Treinamento (Arquivo): {is_treinamento}")
1268
  try:
 
1269
  resultado = corrigir_peticao(caso, tipo_peticao, peticao_texto_state, peticao_arquivo, is_treinamento)
1270
  except Exception as e:
1271
  logger.error(f"Erro dentro do wrapper arquivo chamando corrigir_peticao: {e}", exc_info=True)
1272
- # Retornar tupla com erro para todos os outputs esperados
1273
  return f"❌ Erro interno no processamento: {e}", "", "", "", "", ""
1274
  logger.info("<<< WRAPPER ARQUIVO FINALIZADO")
1275
  return resultado
1276
  # --- FIM DAS FUNÇÕES WRAPPER ---
1277
 
1278
- # --- LÓGICA DOS BOTÕES E EVENTOS (COM CORREÇÃO DO CLEAR_BTN) ---
 
1279
 
1280
  caso_btn.click(
1281
  fn=gerar_caso_ficticio,
@@ -1302,7 +1296,7 @@ def interface_gradio():
1302
  show_progress="minimal"
1303
  )
1304
 
1305
- # Botão Corrigir (Texto) - Usando wrapper
1306
  btn_corrigir.click(
1307
  fn=lambda: gr.update(value="⏳ Processando... Gerando análise e correção. Isso pode levar um minuto...", visible=True),
1308
  inputs=None,
@@ -1310,9 +1304,9 @@ def interface_gradio():
1310
  show_progress=False
1311
  ).then(
1312
  fn=corrigir_peticao_wrapper_texto,
1313
- # Inputs SEM o componente modo_treinamento direto
1314
  inputs=[caso_input, tipo_peticao_input, peticao_texto_input, gr.State(None)],
1315
- outputs=[resultado_analise_output, gr.Textbox(visible=False), resultado_peticao_corrigida_output,
 
1316
  resumo_alteracoes_output, criticas_output, visualizacao_detalhada_output],
1317
  show_progress="full"
1318
  ).then(
@@ -1321,7 +1315,7 @@ def interface_gradio():
1321
  outputs=[processing_message]
1322
  )
1323
 
1324
- # Botão Corrigir (Arquivo) - Usando wrapper
1325
  btn_corrigir_arquivo.click(
1326
  fn=lambda: gr.update(value="⏳ Processando arquivo... Gerando análise e correção. Isso pode levar um minuto...", visible=True),
1327
  inputs=None,
@@ -1329,9 +1323,9 @@ def interface_gradio():
1329
  show_progress=False
1330
  ).then(
1331
  fn=corrigir_peticao_wrapper_arquivo,
1332
- # Inputs SEM o componente modo_treinamento direto
1333
  inputs=[caso_input, tipo_peticao_input, gr.State(None), peticao_arquivo_input],
1334
- outputs=[resultado_analise_output, gr.Textbox(visible=False), resultado_peticao_corrigida_output,
 
1335
  resumo_alteracoes_output, criticas_output, visualizacao_detalhada_output],
1336
  show_progress="full"
1337
  ).then(
@@ -1340,53 +1334,49 @@ def interface_gradio():
1340
  outputs=[processing_message]
1341
  )
1342
 
1343
- # Botão Limpar - CORRIGIDO para usar gr.update()
1344
  clear_btn.click(
1345
  fn=lambda: [
1346
  gr.update(value="Petição Inicial"),
1347
- gr.update(value=False), # <--- Usa gr.update()
 
 
1348
  gr.update(value=None),
 
 
 
 
 
 
 
 
1349
  gr.update(value=None),
1350
- gr.update(value=None), # Limpa o gr.File
1351
- gr.update(value=""), # Limpa o preview
1352
- gr.update(value="", visible=False),# Esconde msg processamento
1353
- gr.update(value=""), # Limpa análise
1354
- gr.update(value=""), # Limpa peça corrigida
1355
- gr.update(value=""), # Limpa resumo diff
1356
- gr.update(value=""), # Limpa críticas diff
1357
- gr.update(value=""), # Limpa HTML diff
1358
- gr.update(value="*Dicas aparecerão aqui enquanto você digita...*"), # Reseta dicas
1359
- gr.update(value=None), # Limpa upload progresso
1360
- gr.update(value=None, visible=False) # Limpa e esconde download progresso
1361
  ],
1362
- inputs=None, # Nenhum input para a função lambda de limpeza
1363
- outputs=[ # Lista de componentes a serem atualizados
1364
  tipo_peticao_input, modo_treinamento, caso_input, peticao_texto_input, peticao_arquivo_input,
1365
  arquivo_preview_output, processing_message, resultado_analise_output,
1366
  resultado_peticao_corrigida_output, resumo_alteracoes_output, criticas_output,
1367
  visualizacao_detalhada_output, dica_contextual_output, arquivo_progresso_upload,
1368
- progresso_download_file
1369
  ]
1370
  )
1371
 
1372
  progresso_btn.click(fn=calcular_progresso, outputs=progresso_output)
1373
  historico_btn.click(fn=mostrar_historico, outputs=historico_output)
1374
 
1375
- # Salvar/Carregar Progresso (mantidos como antes, mas wrapper garante que não há bool direto)
1376
  salvar_btn.click(
1377
  fn=lambda: gr.update(value="⏳ Salvando progresso atual...", visible=True),
1378
  inputs=None, outputs=processing_message, show_progress=False
1379
  ).then(
1380
  fn=salvar_progresso,
1381
  inputs=[caso_input, peticao_texto_input, tipo_peticao_input],
1382
- # Saída para gr.File deve ser o path
1383
  outputs=[gr.Textbox(visible=False), config_status, progresso_download_file],
1384
  show_progress="minimal"
1385
- ).then(
1386
- # Atualiza a visibilidade do componente de download
1387
- fn=lambda file_path: gr.update(visible=bool(file_path), value=file_path),
1388
- inputs=[progresso_download_file],
1389
- outputs=[progresso_download_file]
1390
  ).then(
1391
  lambda: gr.update(value="", visible=False), None, processing_message
1392
  )
@@ -1397,7 +1387,6 @@ def interface_gradio():
1397
  ).then(
1398
  fn=carregar_progresso,
1399
  inputs=[arquivo_progresso_upload],
1400
- # Outputs mapeados corretamente para os componentes
1401
  outputs=[tipo_peticao_input, caso_input, peticao_texto_input, config_status],
1402
  show_progress="minimal"
1403
  ).then(
@@ -1416,6 +1405,5 @@ if __name__ == "__main__":
1416
  logger.warning(f"Tesseract não encontrado ou erro ao verificar versão: {e}. OCR de PDFs pode falhar. Verifique packages.txt.")
1417
 
1418
  app = interface_gradio()
1419
- # app.queue() # Descomente se precisar lidar com múltiplas requisições
1420
- app.launch() # share=True geralmente não necessário no HF e pode expor secrets se mal configurado
1421
  logger.info("Aplicação Gradio encerrada.")
 
204
  logger.warning("--- Nenhum arquivo fornecido para processamento.")
205
  return "⚠️ Nenhum arquivo enviado."
206
  try:
207
+ file_path = file_obj.name
 
208
  filename = os.path.basename(file_path).lower()
209
  logger.info(f"--- Processando arquivo: {filename} (path: {file_path})")
210
 
 
218
  logger.warning(f"--- Não foi possível verificar o tamanho do arquivo: {size_e}")
219
 
220
  result_text = ""
 
221
  if filename.endswith('.pdf'):
222
  logger.info("--- Chamando extract_text_from_pdf")
223
  result_text = extract_text_from_pdf(file_path)
 
609
  prompt += "\n\n**Instrução Final:** Gere **APENAS** o texto completo da peça corrigida abaixo, começando pelo endereçamento e terminando com 'OAB/...'. Não inclua NENHUM comentário, introdução ou explicação ANTES ou DEPOIS do texto da peça."
610
  return prompt
611
 
612
+ # --- Função de Chamada da API ---
613
+ def chamar_api(prompt):
614
+ payload = {
615
+ "contents": [{"parts": [{"text": prompt}]}],
616
+ "generationConfig": {
617
+ "temperature": CONFIG["temperatura"],
618
+ "maxOutputTokens": CONFIG["max_tokens"],
619
+ "topP": 0.95,
620
+ }
621
+ }
622
+ headers = {"Content-Type": "application/json"}
623
+ logger.info(f"Chamando API Gemini com {len(prompt)} caracteres de prompt. Temperatura: {CONFIG['temperatura']}, Max Tokens: {CONFIG['max_tokens']}")
624
 
625
+ for tentativa in range(CONFIG["max_retry_attempts"]):
626
+ try:
627
+ with rate_limiter:
628
+ logger.debug(f"Tentativa {tentativa + 1}/{CONFIG['max_retry_attempts']}...")
629
+ response = requests.post(GEMINI_API_URL, headers=headers, data=json.dumps(payload), timeout=CONFIG["timeout"])
630
+
631
+ logger.debug(f"Status Code recebido: {response.status_code}")
632
+
633
+ if response.status_code == 429:
634
+ retry_after = int(response.headers.get("Retry-After", CONFIG["retry_delay"] * (tentativa + 1)))
635
+ logger.warning(f"Erro 429 (Too Many Requests). Tentando novamente em {retry_after}s...")
636
+ if tentativa < CONFIG["max_retry_attempts"] - 1:
637
+ time.sleep(retry_after)
638
+ continue
639
+ else:
640
+ return "❌ Erro 429: Muitas requisições. Tente novamente mais tarde."
641
+
642
+ if response.status_code != 200:
643
+ error_info = {}
644
+ error_msg = f"Erro desconhecido (Status {response.status_code})"
645
+ try:
646
+ error_info = response.json()
647
+ error_msg = error_info.get("error", {}).get("message", error_msg)
648
+ except json.JSONDecodeError:
649
+ error_msg = f"{error_msg}. Resposta não é JSON: {response.text[:200]}"
650
+ logger.error(f"Erro da API (Status {response.status_code}): {error_msg}")
651
+ if tentativa < CONFIG["max_retry_attempts"] - 1:
652
+ logger.info(f"Tentando novamente em {CONFIG['retry_delay'] * (tentativa + 1)}s...")
653
+ time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
654
+ continue
655
+ return f"❌ Erro da API ({response.status_code}): {error_msg}"
656
+
657
+ response_data = response.json()
658
+
659
+ if not response_data.get("candidates"):
660
+ prompt_feedback = response_data.get("promptFeedback", {})
661
+ block_reason = prompt_feedback.get("blockReason")
662
+ safety_ratings = prompt_feedback.get("safetyRatings")
663
+ logger.error(f"Resposta da API sem 'candidates'. Block Reason: {block_reason}, Safety Ratings: {safety_ratings}")
664
+ if block_reason:
665
+ return f"❌ A solicitação foi bloqueada pela API. Motivo: {block_reason}. Tente reformular o caso ou a petição."
666
+ else:
667
+ return "❌ Resposta vazia ou inválida da API (sem 'candidates'). Verifique o log para detalhes."
668
+
669
+ candidate = response_data["candidates"][0]
670
+ finish_reason = candidate.get("finishReason")
671
+ if finish_reason not in [None, "STOP", "MAX_TOKENS"]:
672
+ logger.error(f"Geração interrompida pela API. Motivo: {finish_reason}")
673
+ safety_ratings_candidate = candidate.get("safetyRatings", [])
674
+ harm_categories = [r['category'] for r in safety_ratings_candidate if r['probability'] not in ['NEGLIGIBLE', 'LOW']]
675
+ if harm_categories:
676
+ return f"❌ A resposta foi bloqueada por segurança pela API. Motivo: {finish_reason} (Categorias: {', '.join(harm_categories)}). Revise o conteúdo."
677
+ else:
678
+ return f"❌ A geração da resposta foi interrompida pela API. Motivo: {finish_reason}. Tente novamente ou simplifique a solicitação."
679
+
680
+ if "content" not in candidate or "parts" not in candidate["content"] or not candidate["content"]["parts"]:
681
+ logger.error("Estrutura de resposta inesperada da API: Faltando content/parts.")
682
+ if tentativa < CONFIG["max_retry_attempts"] - 1:
683
+ logger.info(f"Tentando novamente em {CONFIG['retry_delay'] * (tentativa + 1)}s...")
684
+ time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
685
+ continue
686
+ return "❌ Resposta inválida da API (estrutura inesperada)."
687
+
688
+ texto_gerado = candidate["content"]["parts"][0]["text"].strip()
689
+ logger.info(f"API retornou com sucesso. Tamanho da resposta: {len(texto_gerado)} caracteres.")
690
+ return texto_gerado
691
+
692
+ except requests.Timeout:
693
+ logger.warning(f"Timeout na tentativa {tentativa + 1}.")
694
+ if tentativa < CONFIG["max_retry_attempts"] - 1:
695
+ logger.info(f"Tentando novamente em {CONFIG['retry_delay'] * (tentativa + 1)}s...")
696
+ time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
697
+ continue
698
+ return "⏱️ Tempo esgotado após múltiplas tentativas. Verifique a conexão ou aumente o timeout."
699
+ except requests.RequestException as e:
700
+ logger.error(f"Erro de rede/requisição na tentativa {tentativa + 1}: {e}")
701
+ if tentativa < CONFIG["max_retry_attempts"] - 1:
702
+ logger.info(f"Tentando novamente em {CONFIG['retry_delay'] * (tentativa + 1)}s...")
703
+ time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
704
+ continue
705
+ return f"❌ Erro na requisição: {e}"
706
+ except json.JSONDecodeError as e:
707
+ logger.error(f"Erro ao decodificar JSON da resposta da API: {e}")
708
+ logger.debug(f"Resposta que causou erro de JSON: {response.text[:500]}")
709
+ return f"❌ Erro ao processar a resposta da API (JSON inválido)."
710
+ except Exception as e:
711
+ logger.exception(f"Erro inesperado na chamada da API na tentativa {tentativa + 1}: {str(e)}")
712
+ if tentativa < CONFIG["max_retry_attempts"] - 1:
713
+ logger.info(f"Tentando novamente em {CONFIG['retry_delay'] * (tentativa + 1)}s...")
714
+ time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
715
+ continue
716
+ return f"❌ Erro inesperado: {str(e)}"
717
+
718
+ return "❌ Falha ao chamar a API após todas as tentativas."
719
 
720
  # --- Função Principal de Correção ---
721
  def corrigir_peticao(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
 
733
  peticao_conteudo = ""
734
  if peticao_arquivo is not None:
735
  logger.info("--- Processando arquivo de petição...")
736
+ peticao_conteudo = process_file(peticao_arquivo)
737
  if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
738
  logger.error(f"--- Erro ao processar arquivo: {peticao_conteudo}")
739
  return peticao_conteudo, "", "", "", "", ""
 
745
  logger.warning("--- Nenhuma petição fornecida (nem texto, nem arquivo).")
746
  return "⚠️ Forneça a petição (em texto ou arquivo).", "", "", "", "", ""
747
 
 
748
  troll_triggers = [
749
  "escreva a petição", "faça a petição", "redija a petição",
750
  "elabore a petição", "crie a petição", "gere a petição",
 
768
 
769
  aviso_validacao = ""
770
 
 
771
  cache_key = f"{tipo_peticao}|{caso[:100]}|{peticao_conteudo[:100]}|{modo_treinamento}"
772
  if cache_key in CORRECAO_CACHE:
773
  logger.info("--- Correção encontrada no cache.")
774
  cached_data = CORRECAO_CACHE[cache_key]
775
  diff_html_cache, _, _ = calcular_diff_interno(cached_data["peticao_original"], cached_data["peticao_corrigida"])
776
  logger.info("<<< CORRIGIR PETICAO FINALIZADO (CACHE HIT)")
777
+ # Retorna 6 elementos para corresponder aos outputs
778
  return cached_data["analise_formatada"], cached_data["peticao_original"], cached_data["peticao_corrigida"], cached_data["resumo_diff"], cached_data["criticas_diff"], diff_html_cache
779
 
 
780
  logger.info("--- Gerando prompt de análise...")
781
  prompt_analise = estruturar_prompt_analise(caso, peticao_conteudo, tipo_peticao, modo_treinamento)
782
  logger.info("--- Enviando para análise da API...")
 
787
  logger.info("--- Análise recebida da API.")
788
  analise_formatada = formatar_correcao(analise)
789
 
 
790
  logger.info("--- Gerando prompt para peça corrigida...")
791
  prompt_peticao = estruturar_prompt_peticao_corrigida(caso, peticao_conteudo, tipo_peticao)
792
  logger.info("--- Enviando para geração da peça corrigida API...")
 
815
  peticao_corrigida_final = pos_corrigir_peticao(peticao_extraida, tipo_peticao)
816
  logger.info("--- Pós-correção aplicada.")
817
 
 
818
  logger.info("--- Calculando diferenças...")
819
  diff_html, resumo_diff, criticas_diff = calcular_diff_interno(peticao_conteudo, peticao_corrigida_final)
820
  logger.info("--- Diferenças calculadas.")
821
 
 
822
  correcao_completa_formatada = aviso_validacao + analise_formatada
823
  resultado_historico = {
824
  "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
 
844
 
845
  end_time = time.time()
846
  logger.info(f"<<< CORRIGIR PETICAO FINALIZADO em {end_time - start_time:.2f} segundos.")
847
+ # Retorna 6 elementos para corresponder aos outputs
848
  return correcao_completa_formatada, peticao_conteudo, peticao_corrigida_final, resumo_diff, criticas_diff, diff_html
849
 
850
+ # --- Funções Auxiliares ---
851
  def formatar_correcao(texto):
852
  texto = texto.replace("### Análise Geral", "## 📝 Análise Geral")
853
  texto = texto.replace("### Nota Final", "## ⭐ Nota Final")
 
1112
  logger.info("<<< CARREGAR PROGRESSO FINALIZADO COM ERRO")
1113
  return "", "", "", f"❌ Erro inesperado ao carregar o progresso: {str(e)}"
1114
 
1115
+ # --- Geração de Caso Fictício ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1116
  def gerar_caso_ficticio(tipo_peticao="Petição Inicial"):
1117
  logger.info(f">>> GERAR CASO FICTICIO INICIADO para: {tipo_peticao}")
1118
  if not tipo_peticao:
 
1157
  gr.Markdown("# ⚖️ Corretor Jurídico OAB Master ⚖️")
1158
  gr.Markdown("Analise e aprimore suas peças processuais! Selecione o tipo, forneça o caso e sua peça (texto ou arquivo).")
1159
 
1160
+ # Definir o Textbox oculto aqui para que as saídas dos botões possam referenciá-lo
1161
+ peticao_original_hidden = gr.Textbox(visible=False, label="Original Hidden")
1162
+
1163
  with gr.Row():
1164
  with gr.Column(scale=1):
1165
  gr.Markdown("### Configurações")
 
1196
  salvar_btn = gr.Button("💾 Salvar Caso/Peça Atual")
1197
  carregar_btn = gr.Button("📂 Carregar Caso/Peça de Arquivo")
1198
  arquivo_progresso_upload = gr.File(label="Selecione o arquivo .json para carregar", file_types=[".json"], scale=2)
1199
+ # Definido como interactive=False desde o início
1200
  progresso_download_file = gr.File(label="Baixar Progresso Salvo", interactive=False)
1201
 
1202
  with gr.Column(scale=3):
 
1238
 
1239
  clear_btn = gr.Button("🗑️ Limpar Tudo")
1240
 
1241
+ # --- FUNÇÕES WRAPPER (com try-except) ---
1242
  def corrigir_peticao_wrapper_texto(caso, tipo_peticao, peticao_texto, peticao_arquivo_state):
1243
  logger.info(">>> WRAPPER TEXTO INICIADO")
1244
  is_treinamento = modo_treinamento.value
1245
  logger.info(f"--- Modo Treinamento (Texto): {is_treinamento}")
1246
  try:
1247
+ # A função corrigir_peticao retorna 6 valores
1248
  resultado = corrigir_peticao(caso, tipo_peticao, peticao_texto, peticao_arquivo_state, is_treinamento)
1249
  except Exception as e:
1250
  logger.error(f"Erro dentro do wrapper texto chamando corrigir_peticao: {e}", exc_info=True)
1251
+ # Retornar tupla com erro para todos os outputs esperados (6 outputs)
1252
  return f"❌ Erro interno no processamento: {e}", "", "", "", "", ""
1253
  logger.info("<<< WRAPPER TEXTO FINALIZADO")
1254
  return resultado
 
1258
  is_treinamento = modo_treinamento.value
1259
  logger.info(f"--- Modo Treinamento (Arquivo): {is_treinamento}")
1260
  try:
1261
+ # A função corrigir_peticao retorna 6 valores
1262
  resultado = corrigir_peticao(caso, tipo_peticao, peticao_texto_state, peticao_arquivo, is_treinamento)
1263
  except Exception as e:
1264
  logger.error(f"Erro dentro do wrapper arquivo chamando corrigir_peticao: {e}", exc_info=True)
1265
+ # Retornar tupla com erro para todos os outputs esperados (6 outputs)
1266
  return f"❌ Erro interno no processamento: {e}", "", "", "", "", ""
1267
  logger.info("<<< WRAPPER ARQUIVO FINALIZADO")
1268
  return resultado
1269
  # --- FIM DAS FUNÇÕES WRAPPER ---
1270
 
1271
+
1272
+ # --- LÓGICA DOS BOTÕES E EVENTOS ---
1273
 
1274
  caso_btn.click(
1275
  fn=gerar_caso_ficticio,
 
1296
  show_progress="minimal"
1297
  )
1298
 
1299
+ # Botão Corrigir (Texto) - Usando wrapper e output corrigido
1300
  btn_corrigir.click(
1301
  fn=lambda: gr.update(value="⏳ Processando... Gerando análise e correção. Isso pode levar um minuto...", visible=True),
1302
  inputs=None,
 
1304
  show_progress=False
1305
  ).then(
1306
  fn=corrigir_peticao_wrapper_texto,
 
1307
  inputs=[caso_input, tipo_peticao_input, peticao_texto_input, gr.State(None)],
1308
+ # 6 outputs correspondendo aos 6 valores retornados por corrigir_peticao
1309
+ outputs=[resultado_analise_output, peticao_original_hidden, resultado_peticao_corrigida_output,
1310
  resumo_alteracoes_output, criticas_output, visualizacao_detalhada_output],
1311
  show_progress="full"
1312
  ).then(
 
1315
  outputs=[processing_message]
1316
  )
1317
 
1318
+ # Botão Corrigir (Arquivo) - Usando wrapper e output corrigido
1319
  btn_corrigir_arquivo.click(
1320
  fn=lambda: gr.update(value="⏳ Processando arquivo... Gerando análise e correção. Isso pode levar um minuto...", visible=True),
1321
  inputs=None,
 
1323
  show_progress=False
1324
  ).then(
1325
  fn=corrigir_peticao_wrapper_arquivo,
 
1326
  inputs=[caso_input, tipo_peticao_input, gr.State(None), peticao_arquivo_input],
1327
+ # 6 outputs correspondendo aos 6 valores retornados por corrigir_peticao
1328
+ outputs=[resultado_analise_output, peticao_original_hidden, resultado_peticao_corrigida_output,
1329
  resumo_alteracoes_output, criticas_output, visualizacao_detalhada_output],
1330
  show_progress="full"
1331
  ).then(
 
1334
  outputs=[processing_message]
1335
  )
1336
 
1337
+ # Botão Limpar - Usando gr.update()
1338
  clear_btn.click(
1339
  fn=lambda: [
1340
  gr.update(value="Petição Inicial"),
1341
+ gr.update(value=False),
1342
+ gr.update(value=None),
1343
+ gr.update(value=None),
1344
  gr.update(value=None),
1345
+ gr.update(value=""),
1346
+ gr.update(value="", visible=False),
1347
+ gr.update(value=""),
1348
+ gr.update(value=""),
1349
+ gr.update(value=""),
1350
+ gr.update(value=""),
1351
+ gr.update(value=""),
1352
+ gr.update(value="*Dicas aparecerão aqui enquanto você digita...*"),
1353
  gr.update(value=None),
1354
+ gr.update(value=None) # Limpa o valor do arquivo de download
 
 
 
 
 
 
 
 
 
 
1355
  ],
1356
+ inputs=None,
1357
+ outputs=[
1358
  tipo_peticao_input, modo_treinamento, caso_input, peticao_texto_input, peticao_arquivo_input,
1359
  arquivo_preview_output, processing_message, resultado_analise_output,
1360
  resultado_peticao_corrigida_output, resumo_alteracoes_output, criticas_output,
1361
  visualizacao_detalhada_output, dica_contextual_output, arquivo_progresso_upload,
1362
+ progresso_download_file # Mantém o componente na lista de outputs
1363
  ]
1364
  )
1365
 
1366
  progresso_btn.click(fn=calcular_progresso, outputs=progresso_output)
1367
  historico_btn.click(fn=mostrar_historico, outputs=historico_output)
1368
 
1369
+ # Salvar/Carregar Progresso - Lógica de visibilidade do botão de download removida
1370
  salvar_btn.click(
1371
  fn=lambda: gr.update(value="⏳ Salvando progresso atual...", visible=True),
1372
  inputs=None, outputs=processing_message, show_progress=False
1373
  ).then(
1374
  fn=salvar_progresso,
1375
  inputs=[caso_input, peticao_texto_input, tipo_peticao_input],
1376
+ # Saída para gr.File (terceiro output) atualiza o valor do componente progresso_download_file
1377
  outputs=[gr.Textbox(visible=False), config_status, progresso_download_file],
1378
  show_progress="minimal"
1379
+ # REMOVIDO o .then() que mexia com a visibilidade
 
 
 
 
1380
  ).then(
1381
  lambda: gr.update(value="", visible=False), None, processing_message
1382
  )
 
1387
  ).then(
1388
  fn=carregar_progresso,
1389
  inputs=[arquivo_progresso_upload],
 
1390
  outputs=[tipo_peticao_input, caso_input, peticao_texto_input, config_status],
1391
  show_progress="minimal"
1392
  ).then(
 
1405
  logger.warning(f"Tesseract não encontrado ou erro ao verificar versão: {e}. OCR de PDFs pode falhar. Verifique packages.txt.")
1406
 
1407
  app = interface_gradio()
1408
+ app.launch()
 
1409
  logger.info("Aplicação Gradio encerrada.")