GabrielGD commited on
Commit
3a1c0e0
·
verified ·
1 Parent(s): 35eaa11

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -121
app.py CHANGED
@@ -18,9 +18,26 @@ from ratelimiter import RateLimiter
18
  import difflib
19
  from html import escape
20
  from weasyprint import HTML # Para gerar PDF com cores
 
 
 
 
 
 
21
 
22
  load_dotenv()
23
 
 
 
 
 
 
 
 
 
 
 
 
24
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
25
  if not GEMINI_API_KEY:
26
  raise ValueError("❌ A chave GEMINI_API_KEY não foi encontrada. Configure-a como Secret no Hugging Face Spaces.")
@@ -38,47 +55,30 @@ CONFIG = {
38
  rate_limiter = RateLimiter(max_calls=60, period=60)
39
 
40
  DICAS_OAB_MELHORADO = {
41
- "Endereçamento": "Em relações de consumo, prefira o foro do domicílio do autor (Art. 101, I, CDC). "
42
- "Exemplo: 'Excelentíssimo Senhor Juiz de Direito da Vara Cível da Comarca de [Cidade do Autor]'.",
43
-
44
- "Qualificação": "Mencione nome, estado civil (se informado), endereço (use 'Rua XXX' se não fornecido). "
45
- "Dica: Sempre verifique se todos os dados das partes estão corretos e completos.",
46
-
47
- "Fatos": "Descreva os fatos cronologicamente, sem inventar informações. "
48
- "Exemplo: 'Em [data], o autor celebrou contrato com o réu para a prestação de serviços de [descrição]'.",
49
-
50
- "Pedidos": "Seja específico nos pedidos (ex.: 'condenação ao pagamento de R$ X'). "
51
- "Dica: Inclua pedidos de danos morais e materiais, se aplicável, com base em precedentes.",
52
-
53
- "Custas": "Inclua 'Requer a juntada da guia de custas devidamente recolhida'. "
54
- "Dica: Nunca se esqueça de mencionar o recolhimento das custas para evitar a extinção do processo.",
55
-
56
- "Fundamentação": "Baseie-se em artigos do CPC ou CDC e, se possível, em súmulas do STJ (ex.: Súmula 387 para danos morais). "
57
- "Exemplo: 'Conforme o artigo 186 do Código Civil, a responsabilidade civil pode ser caracterizada'.",
58
-
59
- "Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula. "
60
- "Dica: Evite erros de pontuação que podem comprometer a formalidade do documento.",
61
-
62
- "Estratégia Jurídica": "Utilize precedentes e súmulas para fortalecer sua argumentação. "
63
- "Exemplo: 'Conforme a Súmula 387 do STJ, os danos morais devem ser comprovados e quantificados'.",
64
-
65
- "Provas": "Especifique as provas que pretende produzir. "
66
- "Dica: Inclua testemunhas, documentos e perícias, se necessário, para comprovar os fatos alegados.",
67
-
68
- "Contra-argumentação": "Antecipe possíveis defesas da parte contrária e prepare respostas. "
69
- "Dica: Considere os argumentos que a outra parte pode usar e esteja preparado para refutá-los."
70
  }
71
 
72
  HISTORICO = []
73
- CORRECAO_CACHE = {}
74
 
75
  def preprocess_image_for_ocr(img):
76
  try:
77
  gray = img.convert('L')
78
  np_img = np.array(gray)
79
- _, thresh = cv2.threshold(np_img, 150, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
 
 
80
  return Image.fromarray(thresh)
81
  except Exception as e:
 
82
  return f"❌ Erro no pré-processamento: {str(e)}"
83
 
84
  def extract_text_from_pdf(file_path_or_obj):
@@ -114,6 +114,7 @@ def extract_text_from_pdf(file_path_or_obj):
114
  text += f"[Erro na página {i+1}: {str(e)}]\n"
115
  return text if text.strip() else "⚠️ Não foi possível extrair texto do PDF."
116
  except Exception as e:
 
117
  return f"❌ Erro ao processar o PDF: {str(e)}"
118
 
119
  def extract_text_from_docx(file_path_or_obj):
@@ -141,6 +142,7 @@ def extract_text_from_docx(file_path_or_obj):
141
  text += "\n"
142
  return text if text.strip() else "⚠️ Não foi possível extrair texto do DOCX."
143
  except Exception as e:
 
144
  return f"❌ Erro ao processar o DOCX: {str(e)}"
145
 
146
  def extract_text_from_txt(file_path_or_obj):
@@ -165,9 +167,11 @@ def extract_text_from_txt(file_path_or_obj):
165
  continue
166
  return "❌ Não foi possível decodificar o arquivo TXT."
167
  except Exception as e:
 
168
  return f"❌ Erro ao processar o TXT: {str(e)}"
169
 
170
  def process_file(file_obj):
 
171
  if file_obj is None:
172
  return "Nenhum arquivo enviado."
173
  try:
@@ -188,6 +192,7 @@ def process_file(file_obj):
188
  else:
189
  return "❌ Erro: Objeto de arquivo inválido."
190
  except Exception as e:
 
191
  return f"❌ Erro ao processar o arquivo: {str(e)}"
192
 
193
  def calcular_pontuacao(enderecamento, fundamentos, pedidos, fechamento):
@@ -214,105 +219,175 @@ def calcular_pontuacao(enderecamento, fundamentos, pedidos, fechamento):
214
  return f"{tabela_formatada}\n\n**Nota Final:** {nota_final:.2f} / 10,00"
215
 
216
  def validar_peticao_basica(peticao):
 
217
  problemas = []
218
- if "EXCELENTÍSSIMO" not in peticao.upper():
 
 
 
219
  problemas.append("⚠️ Endereçamento ausente ou incompleto (Art. 319, I, CPC).")
220
- if "Art. 319" not in peticao and "CPC" not in peticao:
221
- problemas.append("⚠️ Fundamentação jurídica não menciona o CPC (Art. 319).")
222
- if "Dá-se à causa o valor" not in peticao:
 
 
 
 
 
 
223
  problemas.append("⚠️ Valor da causa não especificado (Art. 319, V, CPC).")
224
- if "juntada da guia de custas" not in peticao.lower():
 
 
 
 
 
 
 
 
225
  problemas.append("⚠️ Pedido de juntada da guia de custas ausente (-1,0 ponto).")
226
- if "Nestes termos" not in peticao:
227
- problemas.append("⚠️ Fechamento formal ausente.")
 
 
 
 
 
 
 
 
 
 
 
228
  return problemas
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
 
231
  base_prompt = f"""
232
- ## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB)
233
  **Caso Fornecido:**
234
  {caso}
235
  **Petição do Aluno:**
236
  {peticao}
237
  **Estrutura da Avaliação:**
238
  ### Análise da Petição
239
- 1. Análise Geral (Visão geral sobre a petição - máximo 3 linhas)
240
- 2. Nota Final (0 a 10 com uma casa decimal)
241
- 3. Pontos Positivos (Liste pelo menos 3 aspectos bem executados com justificativa legal)
242
- 4. Pontos a Melhorar (Liste os problemas encontrados em ordem de gravidade com explicação e base legal)
243
  5. Análise Específica dos Requisitos:
244
- - Endereçamento e qualificação do juízo (Art. 319, I, CPC; Art. 101, I, CDC se aplicável)
245
- - Qualificação das partes (Art. 319, II, CPC)
246
- - Fatos e fundamentos jurídicos (Art. 319, III, CPC; Art. 186, CC)
247
- - Pedidos com especificações (Art. 319, IV, CPC)
248
- - Valor da causa (Art. 319, V, CPC)
249
- - Provas com que o autor pretende demonstrar a verdade dos fatos (Art. 319, VI, CPC)
250
- - Opção pela realização ou não de audiência de conciliação (Art. 319, VII, CPC)
251
- - Pedido de citação do réu
252
- - Juntada da guia de custas
253
- - Fechamento formal da petição
 
254
  **Critérios Fundamentais:**
255
- 1. Identificação do candidato (nome ou número real na seção final) resulta em nota máxima de 3,0
256
- 2. Invenção de fatos não constantes no caso resulta em penalização grave (-5,0 pontos)
257
- 3. Falta de pedido de juntada de custas resulta em perda de 1,0 ponto
258
- 4. Use marcadores para dados não fornecidos (ex: "Rua XXX", "CPF XXX")
259
- 5. Não penalize por informações ausentes no caso original
260
- 6. A frase "Nestes termos, pede deferimento" com vírgula é incorreta; corrija para "Nestes termos pede deferimento" e destaque o erro na análise
261
- 7. A petição deve finalizar com "Local e data.\nADVOGADO.\nOAB." sem identificação do aluno (nome, número ou estado na OAB); se houver identificação específica (ex.: número ou estado), zere a nota (0,0)
262
- **Instruções Especiais para Endereçamento:**
263
- - CPC (Art. 46): foro competente é o domicílio do réu; CDC (Art. 101, I): em relação de consumo, foro do autor é correto.
264
- - Avalie o contexto: priorize o foro do autor em casos consumeristas.
 
 
 
 
 
 
265
  **Pontuação Detalhada:**
266
  Inclua uma tabela com:
267
- | ITEM | PONTUAÇÃO |
268
- |-------------------------------------------|-----------|
269
- **Endereçamento** | |
270
- 1. Correto endereçamento ao juízo competente (0,10) | / 0,10 |
271
- 2. Qualificação das partes conforme Art. 319, II, CPC (0,20) | / 0,20 |
272
- **Fundamentos** | |
273
- 3. Exposição clara dos fatos (0,20) | / 0,20 |
274
- 4. Fundamentação jurídica adequada (0,25) | / 0,25 |
275
- 5. Demonstração de ilícito e dano (0,25) | / 0,25 |
276
- **Pedidos** | |
277
- 6. Pedidos claros e específicos (0,30) | / 0,30 |
278
- 7. Valor da causa indicado (0,10) | / 0,10 |
279
- 8. Provas pretendidas especificadas (0,10) | / 0,10 |
280
- 9. Opção sobre audiência de conciliação (0,10) | / 0,10 |
281
- 10. Pedido de citação do réu (0,10) | / 0,10 |
282
- 11. Juntada da guia de custas (0,10) | / 0,10 |
283
- **Fechamento** | |
284
- 12. Fechamento formal adequado (0,10) | / 0,10 |
285
- **Nota Final:**
286
- Calcule a nota final somando os pontos, exceto se houver identificação específica na OAB, que zera a nota.
287
- ---
288
 
289
  ### Petição Corrigida
290
- Forneça a petição do aluno reescrita com todas as correções aplicadas, incluindo partes omitidas (como pedidos ou custas) e ajustando erros identificados nos "Pontos a Melhorar" e "Análise Específica". Use apenas informações do caso e fundamente com leis brasileiras relevantes (ex.: CPC, CDC, Súmula 387 do STJ para danos morais). Estruture a petição corrigida com:
291
- 1. Endereçamento correto
292
- 2. Qualificação completa (Art. 319, II, CPC)
293
- 3. Fatos claros (Art. 319, III, CPC)
294
- 4. Fundamentação jurídica (ex.: CPC, CDC, Art. 186, CC)
295
- 5. Pedidos específicos (Art. 319, IV, CPC)
296
- 6. Valor da causa (Art. 319, V, CPC)
297
- 7. Provas (Art. 319, VI, CPC)
298
- 8. Opção por audiência (Art. 319, VII, CPC)
299
- 9. Pedido de citação
300
- 10. Juntada da guia de custas
301
- 11. Fechamento formal com "Local e data.\nADVOGADO.\nOAB." sem identificação específica
302
  """
303
  if modo_treinamento:
304
  base_prompt += f"""
305
  **Modo de Treinamento Interativo Avançado:**
306
  - Para cada item com pontuação inferior ao máximo, forneça:
307
- a) Uma explicação detalhada (mínimo 2 frases) descrevendo o erro e sua base legal (ex.: Art. 319, CPC).
308
- b) Uma sugestão de redação corrigida baseada no caso.
309
- c) Um exemplo prático de redação correta em outro contexto (ex.: "Em uma ação de dano moral, poderia ser: 'Requer a condenação ao pagamento de R$ 10.000,00 por danos morais, conforme Súmula 387 do STJ.'").
310
- d) Uma dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)}) ou personalizada.
311
- e) Uma estratégia jurídica avançada que poderia ser aplicada ao caso.
312
- - Verifique a coerência entre os fatos do caso e a petição, destacando inconsistências.
313
- - Sugira estratégias jurídicas adicionais baseadas no caso (ex.: citar Art. 186, CC ou Súmula 387, STJ para danos morais).
314
- - Não resuma; detalhe cada erro de forma pedagógica e clara.
315
- - Inclua referências a artigos de lei, jurisprudência e doutrina relevantes.
316
  """
317
  return base_prompt
318
 
@@ -374,9 +449,33 @@ def gerar_caso_ficticio():
374
  continue
375
  return f"❌ Erro ao chamar a API: {e}"
376
  except Exception as e:
 
377
  return f"❌ Erro inesperado: {str(e)}"
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
 
 
380
  if not caso.strip():
381
  return "⚠️ Forneça o caso da banca."
382
  if peticao_arquivo is not None:
@@ -384,16 +483,20 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
384
  if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
385
  return peticao_conteudo
386
  else:
387
- peticao_conteudo = peticao_texto or ""
388
  if not peticao_conteudo.strip():
389
  return "⚠️ Forneça uma petição."
390
 
391
  problemas = validar_peticao_basica(peticao_conteudo)
 
392
  aviso_validacao = f"### Validação Inicial\n\n" + "\n".join(problemas) + "\n\n" if problemas else ""
 
 
393
 
394
  cache_key = f"{caso[:50]}|{peticao_conteudo[:50]}|{modo_treinamento}"
395
- if cache_key in CORRECAO_CACHE:
396
- return aviso_validacao + CORRECAO_CACHE[cache_key]
 
397
 
398
  prompt = estruturar_prompt_correcao(caso, peticao_conteudo, modo_treinamento)
399
  payload = {
@@ -419,6 +522,9 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
419
  continue
420
  return aviso_validacao + "❌ A API retornou uma resposta vazia. Tente novamente."
421
  correcao_completa = response_data["candidates"][0]["content"]["parts"][0]["text"]
 
 
 
422
  if "### Petiç��o Corrigida" in correcao_completa:
423
  analise_fim = correcao_completa.index("### Petição Corrigida")
424
  analise = correcao_completa[:analise_fim].strip()
@@ -431,9 +537,9 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
431
  "peticao": peticao_conteudo,
432
  "correcao_completa": correcao_completa
433
  })
434
- if len(HISTORICO) > 10: # Limite de histórico
435
  HISTORICO.pop(0)
436
- CORRECAO_CACHE[cache_key] = correcao_formatada
437
  return aviso_validacao + correcao_formatada
438
  except requests.Timeout:
439
  if tentativa < CONFIG["max_retry_attempts"] - 1:
@@ -446,6 +552,7 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
446
  continue
447
  return aviso_validacao + f"❌ Erro ao chamar a API: {e}"
448
  except Exception as e:
 
449
  return aviso_validacao + f"❌ Erro inesperado: {str(e)}"
450
 
451
  def formatar_correcao(texto):
@@ -469,7 +576,6 @@ def formatar_correcao(texto):
469
  else:
470
  texto_formatado += f"{linha}\n"
471
  if "Nota Final" in texto_formatado:
472
- import re
473
  nota_pattern = r"### Nota Final.*?(\d+[.,]\d+|\d+)"
474
  match = re.search(nota_pattern, texto_formatado, re.DOTALL)
475
  if match:
@@ -479,20 +585,15 @@ def formatar_correcao(texto):
479
 
480
  def extrair_peticao_corrigida(correcao_completa):
481
  try:
482
- # Verifica se a seção "### Petição Corrigida" está presente na resposta
483
  if "### Petição Corrigida" in correcao_completa:
484
  inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
485
  peticao_texto = correcao_completa[inicio:].strip()
486
- # Encontra o final da petição corrigida, caso haja outra seção marcada com "###"
487
  fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
488
  peticao_texto = peticao_texto[:fim].strip()
489
  return peticao_texto
490
- else:
491
- # Se a seção não for encontrada, tenta extrair a petição corrigida de outra forma
492
- # Aqui, você pode adicionar lógica adicional para tentar localizar a petição corrigida
493
- # de outras maneiras, se necessário.
494
- return "⚠️ A seção '### Petição Corrigida' não foi encontrada na resposta da API. Verifique a resposta e tente novamente."
495
  except Exception as e:
 
496
  return f"❌ Erro ao extrair petição corrigida: {str(e)}"
497
 
498
  def fornecer_dica_contextual(texto):
@@ -527,7 +628,6 @@ def comparar_peticao_melhorada(caso, peticao_texto, correcao):
527
  if not ultima_correcao:
528
  return "⚠️ Nenhuma correção disponível no histórico.", "", "", "", ""
529
 
530
- # Tenta extrair a petição corrigida da última correção
531
  peticao_corrigida = extrair_peticao_corrigida(ultima_correcao)
532
  if "⚠️" in peticao_corrigida or "❌" in peticao_corrigida:
533
  return peticao_corrigida, "", "", "", ""
@@ -613,6 +713,7 @@ def exportar_diferencas_html(caso, peticao_texto, correcao):
613
  HTML(string=html_content).write_pdf(temp_path)
614
  return temp_path
615
  except Exception as e:
 
616
  return f"❌ Erro ao gerar PDF: {str(e)}"
617
  finally:
618
  if 'temp_path' in locals() and os.path.exists(temp_path):
@@ -737,7 +838,7 @@ def interface_gradio():
737
  inputs=[caso_input, peticao_texto, resultado_output],
738
  outputs=[download_file]
739
  ).then(
740
- fn=lambda x: [gr.update(visible=True, value=x) if isinstance(x, str) and not x.startswith("❌") else gr.update(visible=True, value=None, label=f"Erro: {x}" if x else "Erro desconhecido"), gr.update(visible=True)],
741
  outputs=[download_file, download_btn]
742
  )
743
 
@@ -774,4 +875,4 @@ def interface_gradio():
774
  return demo
775
 
776
  app = interface_gradio()
777
- app.launch()
 
18
  import difflib
19
  from html import escape
20
  from weasyprint import HTML # Para gerar PDF com cores
21
+ import shelve
22
+ import logging
23
+ import re
24
+
25
+ # Configuração de logging
26
+ logging.basicConfig(filename='app.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
27
 
28
  load_dotenv()
29
 
30
+ # Verificação de dependências
31
+ def verificar_dependencias():
32
+ dependencias = {
33
+ "gradio": gr, "requests": requests, "pdfplumber": pdfplumber_open,
34
+ "pytesseract": pytesseract, "cv2": cv2, "weasyprint": HTML
35
+ }
36
+ for nome, modulo in dependencias.items():
37
+ if modulo is None:
38
+ raise ImportError(f"❌ Módulo {nome} não encontrado. Instale com 'pip install {nome}'.")
39
+ verificar_dependencias()
40
+
41
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
42
  if not GEMINI_API_KEY:
43
  raise ValueError("❌ A chave GEMINI_API_KEY não foi encontrada. Configure-a como Secret no Hugging Face Spaces.")
 
55
  rate_limiter = RateLimiter(max_calls=60, period=60)
56
 
57
  DICAS_OAB_MELHORADO = {
58
+ "Endereçamento": "Em relações de consumo, prefira o foro do domicílio do autor (Art. 101, I, CDC). Exemplo: 'Excelentíssimo Senhor Juiz de Direito da Vara Cível da Comarca de [Cidade do Autor]'.",
59
+ "Qualificação": "Mencione nome, estado civil (se informado), endereço (use 'Rua XXX' se não fornecido). Dica: Sempre verifique se todos os dados das partes estão corretos e completos.",
60
+ "Fatos": "Descreva os fatos cronologicamente, sem inventar informações. Exemplo: 'Em [data], o autor celebrou contrato com o réu para a prestação de serviços de [descrição]'.",
61
+ "Pedidos": "Seja específico nos pedidos (ex.: 'condenação ao pagamento de R$ X'). Dica: Inclua pedidos de danos morais e materiais, se aplicável, com base em precedentes.",
62
+ "Custas": "Inclua 'Requer a juntada da guia de custas devidamente recolhida'. Dica: Nunca se esqueça de mencionar o recolhimento das custas para evitar a extinção do processo.",
63
+ "Fundamentação": "Baseie-se em artigos do CPC ou CDC e, se possível, em súmulas do STJ (ex.: Súmula 387 para danos morais). Exemplo: 'Conforme o artigo 186 do Código Civil, a responsabilidade civil pode ser caracterizada'.",
64
+ "Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula. Dica: Evite erros de pontuação que podem comprometer a formalidade do documento.",
65
+ "Estratégia Jurídica": "Utilize precedentes e súmulas para fortalecer sua argumentação. Exemplo: 'Conforme a Súmula 387 do STJ, os danos morais devem ser comprovados e quantificados'.",
66
+ "Provas": "Especifique as provas que pretende produzir. Dica: Inclua testemunhas, documentos e perícias, se necessário, para comprovar os fatos alegados.",
67
+ "Contra-argumentação": "Antecipe possíveis defesas da parte contrária e prepare respostas. Dica: Considere os argumentos que a outra parte pode usar e esteja preparado para refutá-los."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
69
 
70
  HISTORICO = []
 
71
 
72
  def preprocess_image_for_ocr(img):
73
  try:
74
  gray = img.convert('L')
75
  np_img = np.array(gray)
76
+ clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
77
+ enhanced = clahe.apply(np_img)
78
+ _, thresh = cv2.threshold(enhanced, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
79
  return Image.fromarray(thresh)
80
  except Exception as e:
81
+ logging.error(f"Erro no pré-processamento de imagem: {str(e)}")
82
  return f"❌ Erro no pré-processamento: {str(e)}"
83
 
84
  def extract_text_from_pdf(file_path_or_obj):
 
114
  text += f"[Erro na página {i+1}: {str(e)}]\n"
115
  return text if text.strip() else "⚠️ Não foi possível extrair texto do PDF."
116
  except Exception as e:
117
+ logging.error(f"Erro ao processar PDF: {str(e)}")
118
  return f"❌ Erro ao processar o PDF: {str(e)}"
119
 
120
  def extract_text_from_docx(file_path_or_obj):
 
142
  text += "\n"
143
  return text if text.strip() else "⚠️ Não foi possível extrair texto do DOCX."
144
  except Exception as e:
145
+ logging.error(f"Erro ao processar DOCX: {str(e)}")
146
  return f"❌ Erro ao processar o DOCX: {str(e)}"
147
 
148
  def extract_text_from_txt(file_path_or_obj):
 
167
  continue
168
  return "❌ Não foi possível decodificar o arquivo TXT."
169
  except Exception as e:
170
+ logging.error(f"Erro ao processar TXT: {str(e)}")
171
  return f"❌ Erro ao processar o TXT: {str(e)}"
172
 
173
  def process_file(file_obj):
174
+ """Processa arquivos PDF, DOCX ou TXT e extrai texto."""
175
  if file_obj is None:
176
  return "Nenhum arquivo enviado."
177
  try:
 
192
  else:
193
  return "❌ Erro: Objeto de arquivo inválido."
194
  except Exception as e:
195
+ logging.error(f"Erro ao processar arquivo: {str(e)}")
196
  return f"❌ Erro ao processar o arquivo: {str(e)}"
197
 
198
  def calcular_pontuacao(enderecamento, fundamentos, pedidos, fechamento):
 
219
  return f"{tabela_formatada}\n\n**Nota Final:** {nota_final:.2f} / 10,00"
220
 
221
  def validar_peticao_basica(peticao):
222
+ """Valida os elementos básicos da petição com base no Art. 319 do CPC e critérios OAB."""
223
  problemas = []
224
+ peticao_upper = peticao.upper()
225
+ peticao_lower = peticao.lower()
226
+
227
+ if "EXCELENTÍSSIMO" not in peticao_upper or "JUIZ" not in peticao_upper:
228
  problemas.append("⚠️ Endereçamento ausente ou incompleto (Art. 319, I, CPC).")
229
+ if "AUTOR" not in peticao_upper or "RÉU" not in peticao_upper:
230
+ problemas.append("⚠️ Qualificação incompleta: 'autor' ou 'réu' não mencionados (Art. 319, II, CPC).")
231
+ if "FATO" not in peticao_upper and "FUNDAMENTO" not in peticao_upper:
232
+ problemas.append("⚠️ Exposição de fatos ou fundamentos ausente (Art. 319, III, CPC).")
233
+ if "ART." not in peticao_upper or "CPC" not in peticao_upper:
234
+ problemas.append("⚠️ Fundamentação jurídica não menciona artigos ou CPC (Art. 319, III, CPC).")
235
+ if "REQUER" not in peticao_upper and "PEDE" not in peticao_upper:
236
+ problemas.append("⚠️ Pedidos não especificados ou ausentes (Art. 319, IV, CPC).")
237
+ if "DÁ-SE À CAUSA O VALOR" not in peticao_upper:
238
  problemas.append("⚠️ Valor da causa não especificado (Art. 319, V, CPC).")
239
+ if "R$" not in peticao_upper and "VALOR" not in peticao_upper:
240
+ problemas.append("⚠️ Valor da causa sem quantia monetária explícita (Art. 319, V, CPC).")
241
+ if "PROVA" not in peticao_upper and "TESTEMUNHA" not in peticao_upper and "DOCUMENTO" not in peticao_upper:
242
+ problemas.append("⚠️ Provas pretendidas não especificadas (Art. 319, VI, CPC).")
243
+ if "AUDIÊNCIA" not in peticao_upper and "CONCILIAÇÃO" not in peticao_upper:
244
+ problemas.append("⚠️ Opção sobre audiência de conciliação não mencionada (Art. 319, VII, CPC).")
245
+ if "CITAÇÃO" not in peticao_upper:
246
+ problemas.append("⚠️ Pedido de citação do réu ausente.")
247
+ if "juntada da guia de custas" not in peticao_lower:
248
  problemas.append("⚠️ Pedido de juntada da guia de custas ausente (-1,0 ponto).")
249
+ if "NESTES TERMOS" not in peticao_upper:
250
+ problemas.append("⚠️ Fechamento formal 'Nestes termos pede deferimento' ausente.")
251
+ if "NESTES TERMOS," in peticao_upper:
252
+ problemas.append("⚠️ Uso incorreto de vírgula em 'Nestes termos, pede deferimento'.")
253
+ if "ADVOGADO" not in peticao_upper or "OAB" not in peticao_upper:
254
+ problemas.append("⚠️ Fechamento sem 'ADVOGADO' ou 'OAB'.")
255
+ if "LOCAL E DATA" not in peticao_upper:
256
+ problemas.append("⚠️ Fechamento sem 'Local e data'.")
257
+ if any(id in peticao_upper for id in ["CPF", "RG", "MATRÍCULA"]):
258
+ problemas.append("⚠️ Identificação indevida do candidato detectada (nota máxima 3,0).")
259
+ if "OAB/" in peticao_upper or any(char.isdigit() for char in peticao.split("OAB")[-1] if "OAB" in peticao_upper):
260
+ problemas.append("⚠️ Identificação específica na OAB (ex.: número ou estado) detectada (nota 0,0).")
261
+
262
  return problemas
263
 
264
+ def verificar_coerencia_caso_peticao(caso, peticao):
265
+ """Verifica coerência entre o caso e a petição, detectando omissões e invenções."""
266
+ incoerencias = []
267
+ caso_lower = caso.lower()
268
+ peticao_lower = peticao.lower()
269
+
270
+ nomes_caso = [word for word in caso.split() if word.istitle() and len(word) > 2]
271
+ nomes_peticao = [word for word in peticao.split() if word.istitle() and len(word) > 2]
272
+ for nome in nomes_caso:
273
+ if nome.lower() not in peticao_lower:
274
+ incoerencias.append(f"⚠️ Nome '{nome}' do caso não mencionado na petição (suspeita de omissão).")
275
+ for frase in peticao.split("."):
276
+ if frase.strip() and not any(palavra.lower() in caso_lower for palavra in frase.split() if len(palavra) > 3):
277
+ incoerencias.append(f"⚠️ Frase suspeita de invenção: '{frase.strip()}' (não consta no caso).")
278
+ valores_caso = re.findall(r"R\$\s*\d+(?:\.\d+)?(?:,\d+)?", caso)
279
+ valores_peticao = re.findall(r"R\$\s*\d+(?:\.\d+)?(?:,\d+)?", peticao)
280
+ for valor in valores_peticao:
281
+ if valor not in valores_caso:
282
+ incoerencias.append(f"⚠️ Valor '{valor}' na petição não consta no caso (suspeita de invenção).")
283
+
284
+ return incoerencias
285
+
286
+ def validar_resposta_api(correcao_completa):
287
+ """Valida a qualidade da resposta da API."""
288
+ problemas_api = []
289
+ if "Nota Final" not in correcao_completa:
290
+ problemas_api.append("⚠️ Resposta da API sem 'Nota Final'.")
291
+ if "Pontos a Melhorar" not in correcao_completa:
292
+ problemas_api.append("⚠️ Resposta da API sem 'Pontos a Melhorar'.")
293
+ if "Análise Específica dos Requisitos" not in correcao_completa:
294
+ problemas_api.append("⚠️ Resposta da API sem 'Análise Específica dos Requisitos'.")
295
+ if "### Petição Corrigida" not in correcao_completa:
296
+ problemas_api.append("⚠️ Resposta da API sem 'Petição Corrigida'.")
297
+ return problemas_api
298
+
299
  def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
300
+ """Estrutura um prompt avançado e especializado para correção de petições."""
301
  base_prompt = f"""
302
+ ## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB com Análise Especializada)
303
  **Caso Fornecido:**
304
  {caso}
305
  **Petição do Aluno:**
306
  {peticao}
307
  **Estrutura da Avaliação:**
308
  ### Análise da Petição
309
+ 1. Análise Geral (Visão geral sobre a petição - máximo 3 linhas, destacando adequação ao caso e consistência jurídica)
310
+ 2. Nota Final (0 a 10 com uma casa decimal, justificando brevemente a pontuação total)
311
+ 3. Pontos Positivos (Liste pelo menos 3 aspectos bem executados com justificativa legal detalhada, incluindo artigos, súmulas ou precedentes aplicáveis)
312
+ 4. Pontos a Melhorar (Liste todos os problemas encontrados em ordem de gravidade, com explicação detalhada, base legal e impacto na nota; não omita erros menores)
313
  5. Análise Específica dos Requisitos:
314
+ - Endereçamento e qualificação do juízo (Art. 319, I, CPC; Art. 101, I, CDC se aplicável): Verifique a competência territorial e material com precisão.
315
+ - Qualificação das partes (Art. 319, II, CPC): Avalie nomes, estado civil, endereço e consistência com o caso.
316
+ - Fatos e fundamentos jurídicos (Art. 319, III, CPC; Art. 186, CC): Compare os fatos narrados com o caso e analise a pertinência dos fundamentos legais.
317
+ - Pedidos com especificações (Art. 319, IV, CPC): Examine clareza, especificidade e adequação dos pedidos ao caso.
318
+ - Valor da causa (Art. 319, V, CPC): Confirme se o valor é realista e compatível com os danos alegados.
319
+ - Provas com que o autor pretende demonstrar a verdade dos fatos (Art. 319, VI, CPC): Avalie a suficiência e relevância das provas indicadas.
320
+ - Opção pela realização ou não de audiência de conciliação (Art. 319, VII, CPC): Verifique a menção explícita e sua coerência com o caso.
321
+ - Pedido de citação do réu: Analise se está claro e conforme os meios legais (ex.: Art. 246, CPC).
322
+ - Juntada da guia de custas: Confirme a presença literal do pedido e sua redação formal.
323
+ - Fechamento formal da petição: Certifique-se da estrutura exata "Local e data.\nADVOGADO.\nOAB." sem variações ou identificação indevida.
324
+
325
  **Critérios Fundamentais:**
326
+ 1. Identificação do candidato (nome, CPF, RG ou número real na seção final) resulta em nota máxima de 3,0; forneça exemplo do trecho problemático.
327
+ 2. Invenção de fatos não constantes no caso resulta em penalização grave (-5,0 pontos); identifique cada fato inventado e compare com o caso.
328
+ 3. Falta de pedido de juntada de custas resulta em perda de 1,0 ponto; verifique a ausência ou redação inadequada.
329
+ 4. Use marcadores para dados não fornecidos (ex.: "Rua XXX", "CPF XXX"); penalize invenção de dados específicos (-0,5 por ocorrência).
330
+ 5. Não penalize por informações ausentes no caso original, mas alerte se a omissão compromete a petição.
331
+ 6. A frase "Nestes termos, pede deferimento" com vírgula é incorreta; corrija para "Nestes termos pede deferimento" e penalize com -0,2 ponto, destacando o erro formal.
332
+ 7. A petição deve finalizar com "Local e data.\nADVOGADO.\nOAB." sem identificação do aluno (nome, número ou estado na OAB); se houver identificação específica (ex.: "OAB/SP" ou "12345"), zere a nota (0,0) e explique o motivo.
333
+
334
+ **Instruções Especiais para Análise Avançada:**
335
+ - **Competência**: Avalie o foro indicado com base no Art. 46 do CPC (domicílio do réu) ou Art. 101, I do CDC (domicílio do autor em relações de consumo); penalize erros com -0,10 ponto e justifique com jurisprudência (ex.: STJ, REsp 1.234.567).
336
+ - **Consistência Fática**: Compare cada fato narrado na petição com o caso; destaque omissões ou distorções com base no Art. 319, III, CPC.
337
+ - **Fundamentação Jurídica**: Exija citação de pelo menos um artigo específico (ex.: Art. 186, CC para responsabilidade civil) e avalie a pertinência; penalize generalidades com -0,15 ponto por item vago.
338
+ - **Danos Morais e Materiais**: Se o caso sugerir danos, verifique se os pedidos estão quantificados e fundamentados (ex.: Súmula 387/STJ para danos morais); penalize omissões com -0,20 ponto.
339
+ - **Linguagem Técnica**: Avalie a formalidade e precisão do vocabulário jurídico; penalize informalidades ou erros gramaticais (ex.: "vossa excelência" em minúsculas) com -0,05 ponto por ocorrência.
340
+ - **Precedentes**: Inclua referências a súmulas ou decisões relevantes (ex.: Súmula 297/STJ para CDC) quando aplicável no caso.
341
+
342
  **Pontuação Detalhada:**
343
  Inclua uma tabela com:
344
+ | ITEM | PONTUAÇÃO | JUSTIFICATIVA |
345
+ |-------------------------------------------|-----------|---------------|
346
+ **Endereçamento** | | |
347
+ 1. Correto endereçamento ao juízo competente (0,10) | / 0,10 | [Explique erro ou acerto] |
348
+ 2. Qualificação das partes conforme Art. 319, II, CPC (0,20) | / 0,20 | [Explique erro ou acerto] |
349
+ **Fundamentos** | | |
350
+ 3. Exposição clara dos fatos (0,20) | / 0,20 | [Explique erro ou acerto] |
351
+ 4. Fundamentação jurídica adequada (0,25) | / 0,25 | [Explique erro ou acerto] |
352
+ 5. Demonstração de ilícito e dano (0,25) | / 0,25 | [Explique erro ou acerto] |
353
+ **Pedidos** | | |
354
+ 6. Pedidos claros e específicos (0,30) | / 0,30 | [Explique erro ou acerto] |
355
+ 7. Valor da causa indicado (0,10) | / 0,10 | [Explique erro ou acerto] |
356
+ 8. Provas pretendidas especificadas (0,10)| / 0,10 | [Explique erro ou acerto] |
357
+ 9. Opção sobre audiência de conciliação (0,10) | / 0,10 | [Explique erro ou acerto] |
358
+ 10. Pedido de citação do réu (0,10) | / 0,10 | [Explique erro ou acerto] |
359
+ 11. Juntada da guia de custas (0,10) | / 0,10 | [Explique erro ou acerto] |
360
+ **Fechamento** | | |
361
+ 12. Fechamento formal adequado (0,10) | / 0,10 | [Explique erro ou acerto] |
362
+ **Nota Final:** Calcule a soma dos pontos, aplicando penalizações cumulativas conforme os critérios fundamentais; justifique qualquer desconto adicional.
 
 
363
 
364
  ### Petição Corrigida
365
+ Forneça a petição do aluno reescrita com todas as correções aplicadas, incluindo partes omitidas (como pedidos ou custas) e ajustando erros identificados. Siga rigorosamente:
366
+ 1. Endereçamento correto (com foro justificado por CPC ou CDC).
367
+ 2. Qualificação completa (Art. 319, II, CPC; use "XXX" para dados ausentes).
368
+ 3. Fatos claros e fiéis ao caso (Art. 319, III, CPC).
369
+ 4. Fundamentação jurídica robusta (cite CPC, CDC, CC e súmulas aplicáveis, como Súmula 387/STJ para danos morais).
370
+ 5. Pedidos específicos e quantificados (Art. 319, IV, CPC).
371
+ 6. Valor da causa realista (Art. 319, V, CPC).
372
+ 7. Provas detalhadas (Art. 319, VI, CPC; ex.: "prova documental e testemunhal").
373
+ 8. Opção por audiência explícita (Art. 319, VII, CPC).
374
+ 9. Pedido de citação formal (ex.: "via postal, nos termos do Art. 246, I, CPC").
375
+ 10. Juntada da guia de custas com redação precisa.
376
+ 11. Fechamento formal com "Local e data.\nADVOGADO.\nOAB." sem identificação específica.
377
  """
378
  if modo_treinamento:
379
  base_prompt += f"""
380
  **Modo de Treinamento Interativo Avançado:**
381
  - Para cada item com pontuação inferior ao máximo, forneça:
382
+ a) Explicação detalhada (mínimo 3 frases) com base legal (ex.: Art. 319, CPC) e análise do impacto processual.
383
+ b) Sugestão de redação corrigida baseada no caso, com linguagem técnica exemplar.
384
+ c) Exemplo prático em outro contexto (ex.: "Em ação consumerista, ‘Requer a inversão do ônus da prova, nos termos do Art. 6º, VIII, CDC.’").
385
+ d) Dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)}) ou personalizada, com enfoque pedagógico.
386
+ e) Estratégia jurídica avançada (ex.: "Cite o Art. 944, CC para quantificar danos ou peça tutela antecipada com base no Art. 300, CPC").
387
+ - Verifique a coerência entre os fatos do caso e a petição, listando todas as inconsistências com trechos específicos.
388
+ - Sugira estratégias adicionais baseadas no caso (ex.: cumulação de pedidos, Art. 327, CPC; ou danos morais com Súmula 387/STJ).
389
+ - Detalhe cada erro pedagogicamente, com referências a leis, súmulas (ex.: Súmula 54/STJ para lucros cessantes), e doutrina (ex.: Nelson Nery Jr. sobre Art. 319, CPC).
390
+ - Avalie a viabilidade processual da petição (ex.: risco de indeferimento por inépcia, Art. 330, CPC).
391
  """
392
  return base_prompt
393
 
 
449
  continue
450
  return f"❌ Erro ao chamar a API: {e}"
451
  except Exception as e:
452
+ logging.error(f"Erro inesperado ao gerar caso: {str(e)}")
453
  return f"❌ Erro inesperado: {str(e)}"
454
 
455
+ def sanitize_input(text):
456
+ """Sanitiza entradas para evitar injeção."""
457
+ return escape(text.strip()) if text else ""
458
+
459
+ def salvar_cache(chave, valor):
460
+ """Salva correção no cache persistente."""
461
+ try:
462
+ with shelve.open("correcao_cache.db") as db:
463
+ db[chave] = valor
464
+ except Exception as e:
465
+ logging.error(f"Erro ao salvar no cache: {str(e)}")
466
+
467
+ def carregar_cache(chave):
468
+ """Carrega correção do cache persistente."""
469
+ try:
470
+ with shelve.open("correcao_cache.db") as db:
471
+ return db.get(chave)
472
+ except Exception as e:
473
+ logging.error(f"Erro ao carregar do cache: {str(e)}")
474
+ return None
475
+
476
  def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
477
+ """Corrige uma petição inicial cível com base em critérios da OAB."""
478
+ caso = sanitize_input(caso)
479
  if not caso.strip():
480
  return "⚠️ Forneça o caso da banca."
481
  if peticao_arquivo is not None:
 
483
  if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
484
  return peticao_conteudo
485
  else:
486
+ peticao_conteudo = sanitize_input(peticao_texto) or ""
487
  if not peticao_conteudo.strip():
488
  return "⚠️ Forneça uma petição."
489
 
490
  problemas = validar_peticao_basica(peticao_conteudo)
491
+ incoerencias = verificar_coerencia_caso_peticao(caso, peticao_conteudo)
492
  aviso_validacao = f"### Validação Inicial\n\n" + "\n".join(problemas) + "\n\n" if problemas else ""
493
+ if incoerencias:
494
+ aviso_validacao += f"### Verificação de Coerência\n\n" + "\n".join(incoerencias) + "\n\n"
495
 
496
  cache_key = f"{caso[:50]}|{peticao_conteudo[:50]}|{modo_treinamento}"
497
+ cached_result = carregar_cache(cache_key)
498
+ if cached_result:
499
+ return aviso_validacao + cached_result
500
 
501
  prompt = estruturar_prompt_correcao(caso, peticao_conteudo, modo_treinamento)
502
  payload = {
 
522
  continue
523
  return aviso_validacao + "❌ A API retornou uma resposta vazia. Tente novamente."
524
  correcao_completa = response_data["candidates"][0]["content"]["parts"][0]["text"]
525
+ problemas_api = validar_resposta_api(correcao_completa)
526
+ if problemas_api:
527
+ return aviso_validacao + f"### Problemas na Resposta da API\n\n" + "\n".join(problemas_api) + "\n\n⚠️ Tente novamente."
528
  if "### Petiç��o Corrigida" in correcao_completa:
529
  analise_fim = correcao_completa.index("### Petição Corrigida")
530
  analise = correcao_completa[:analise_fim].strip()
 
537
  "peticao": peticao_conteudo,
538
  "correcao_completa": correcao_completa
539
  })
540
+ if len(HISTORICO) > 10:
541
  HISTORICO.pop(0)
542
+ salvar_cache(cache_key, correcao_formatada)
543
  return aviso_validacao + correcao_formatada
544
  except requests.Timeout:
545
  if tentativa < CONFIG["max_retry_attempts"] - 1:
 
552
  continue
553
  return aviso_validacao + f"❌ Erro ao chamar a API: {e}"
554
  except Exception as e:
555
+ logging.error(f"Erro inesperado ao corrigir petição: {str(e)}")
556
  return aviso_validacao + f"❌ Erro inesperado: {str(e)}"
557
 
558
  def formatar_correcao(texto):
 
576
  else:
577
  texto_formatado += f"{linha}\n"
578
  if "Nota Final" in texto_formatado:
 
579
  nota_pattern = r"### Nota Final.*?(\d+[.,]\d+|\d+)"
580
  match = re.search(nota_pattern, texto_formatado, re.DOTALL)
581
  if match:
 
585
 
586
  def extrair_peticao_corrigida(correcao_completa):
587
  try:
 
588
  if "### Petição Corrigida" in correcao_completa:
589
  inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
590
  peticao_texto = correcao_completa[inicio:].strip()
 
591
  fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
592
  peticao_texto = peticao_texto[:fim].strip()
593
  return peticao_texto
594
+ return "⚠️ A seção '### Petição Corrigida' não foi encontrada na resposta da API."
 
 
 
 
595
  except Exception as e:
596
+ logging.error(f"Erro ao extrair petição corrigida: {str(e)}")
597
  return f"❌ Erro ao extrair petição corrigida: {str(e)}"
598
 
599
  def fornecer_dica_contextual(texto):
 
628
  if not ultima_correcao:
629
  return "⚠️ Nenhuma correção disponível no histórico.", "", "", "", ""
630
 
 
631
  peticao_corrigida = extrair_peticao_corrigida(ultima_correcao)
632
  if "⚠️" in peticao_corrigida or "❌" in peticao_corrigida:
633
  return peticao_corrigida, "", "", "", ""
 
713
  HTML(string=html_content).write_pdf(temp_path)
714
  return temp_path
715
  except Exception as e:
716
+ logging.error(f"Erro ao gerar PDF: {str(e)}")
717
  return f"❌ Erro ao gerar PDF: {str(e)}"
718
  finally:
719
  if 'temp_path' in locals() and os.path.exists(temp_path):
 
838
  inputs=[caso_input, peticao_texto, resultado_output],
839
  outputs=[download_file]
840
  ).then(
841
+ fn=lambda x: [gr.update(visible=True, value=x if not x.startswith("❌") else None), gr.update(visible=True)],
842
  outputs=[download_file, download_btn]
843
  )
844
 
 
875
  return demo
876
 
877
  app = interface_gradio()
878
+ app.launch()