GabrielGD commited on
Commit
3c97853
·
verified ·
1 Parent(s): c7b68f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +40 -149
app.py CHANGED
@@ -18,26 +18,9 @@ from ratelimiter import RateLimiter
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.")
@@ -49,36 +32,44 @@ CONFIG = {
49
  "retry_delay": 5,
50
  "timeout": 60,
51
  "temperatura": 0.2,
52
- "max_tokens": 8192
53
  }
54
 
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,7 +105,6 @@ 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,7 +132,6 @@ 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,11 +156,9 @@ 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,7 +179,6 @@ def process_file(file_obj):
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,85 +205,19 @@ 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
- # Função comentada (removida da execução, mantida para referência)
265
- """
266
- def verificar_coerencia_caso_peticao(caso, peticao):
267
- incoerencias = []
268
- caso_lower = caso.lower()
269
- peticao_lower = peticao.lower()
270
-
271
- nomes_caso = [word for word in caso.split() if word.istitle() and len(word) > 2]
272
- nomes_peticao = [word for word in peticao.split() if word.istitle() and len(word) > 2]
273
- for nome in nomes_caso:
274
- if nome.lower() not in peticao_lower:
275
- incoerencias.append(f"⚠️ Nome '{nome}' do caso não mencionado na petição (suspeita de omissão).")
276
- for frase in peticao.split("."):
277
- if frase.strip() and not any(palavra.lower() in caso_lower for palavra in frase.split() if len(palavra) > 3):
278
- incoerencias.append(f"⚠️ Frase suspeita de invenção: '{frase.strip()}' (não consta no caso).")
279
- valores_caso = re.findall(r"R\$\s*\d+(?:\.\d+)?(?:,\d+)?", caso)
280
- valores_peticao = re.findall(r"R\$\s*\d+(?:\.\d+)?(?:,\d+)?", peticao)
281
- for valor in valores_peticao:
282
- if valor not in valores_caso:
283
- incoerencias.append(f"⚠️ Valor '{valor}' na petição não consta no caso (suspeita de invenção).")
284
-
285
- return incoerencias
286
- """
287
-
288
- def validar_resposta_api(correcao_completa):
289
- """Valida a qualidade da resposta da API."""
290
- problemas_api = []
291
- if "Nota Final" not in correcao_completa:
292
- problemas_api.append("⚠️ Resposta da API sem 'Nota Final'.")
293
- if "Pontos a Melhorar" not in correcao_completa:
294
- problemas_api.append("⚠️ Resposta da API sem 'Pontos a Melhorar'.")
295
- if "Análise Específica dos Requisitos" not in correcao_completa:
296
- problemas_api.append("⚠️ Resposta da API sem 'Análise Específica dos Requisitos'.")
297
- if "### Petição Corrigida" not in correcao_completa:
298
- problemas_api.append("⚠️ Resposta da API sem 'Petição Corrigida'.")
299
- return problemas_api
300
-
301
  def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
302
  """Estrutura um prompt avançado e especializado para correção de petições."""
303
  base_prompt = f"""
@@ -338,7 +258,7 @@ def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
338
  - **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.
339
  - **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.
340
  - **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.
341
- - **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.
342
  - **Precedentes**: Inclua referências a súmulas ou decisões relevantes (ex.: Súmula 297/STJ para CDC) quando aplicável no caso.
343
 
344
  **Pontuação Detalhada:**
@@ -451,33 +371,9 @@ def gerar_caso_ficticio():
451
  continue
452
  return f"❌ Erro ao chamar a API: {e}"
453
  except Exception as e:
454
- logging.error(f"Erro inesperado ao gerar caso: {str(e)}")
455
  return f"❌ Erro inesperado: {str(e)}"
456
 
457
- def sanitize_input(text):
458
- """Sanitiza entradas para evitar injeção."""
459
- return escape(text.strip()) if text else ""
460
-
461
- def salvar_cache(chave, valor):
462
- """Salva correção no cache persistente."""
463
- try:
464
- with shelve.open("correcao_cache.db") as db:
465
- db[chave] = valor
466
- except Exception as e:
467
- logging.error(f"Erro ao salvar no cache: {str(e)}")
468
-
469
- def carregar_cache(chave):
470
- """Carrega correção do cache persistente."""
471
- try:
472
- with shelve.open("correcao_cache.db") as db:
473
- return db.get(chave)
474
- except Exception as e:
475
- logging.error(f"Erro ao carregar do cache: {str(e)}")
476
- return None
477
-
478
  def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
479
- """Corrige uma petição inicial cível com base em critérios da OAB."""
480
- caso = sanitize_input(caso)
481
  if not caso.strip():
482
  return "⚠️ Forneça o caso da banca."
483
  if peticao_arquivo is not None:
@@ -485,7 +381,7 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
485
  if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
486
  return peticao_conteudo
487
  else:
488
- peticao_conteudo = sanitize_input(peticao_texto) or ""
489
  if not peticao_conteudo.strip():
490
  return "⚠️ Forneça uma petição."
491
 
@@ -493,9 +389,8 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
493
  aviso_validacao = f"### Validação Inicial\n\n" + "\n".join(problemas) + "\n\n" if problemas else ""
494
 
495
  cache_key = f"{caso[:50]}|{peticao_conteudo[:50]}|{modo_treinamento}"
496
- cached_result = carregar_cache(cache_key)
497
- if cached_result:
498
- return aviso_validacao + cached_result
499
 
500
  prompt = estruturar_prompt_correcao(caso, peticao_conteudo, modo_treinamento)
501
  payload = {
@@ -521,9 +416,6 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
521
  continue
522
  return aviso_validacao + "❌ A API retornou uma resposta vazia. Tente novamente."
523
  correcao_completa = response_data["candidates"][0]["content"]["parts"][0]["text"]
524
- problemas_api = validar_resposta_api(correcao_completa)
525
- if problemas_api:
526
- return aviso_validacao + f"### Problemas na Resposta da API\n\n" + "\n".join(problemas_api) + "\n\n⚠️ Tente novamente."
527
  if "### Petição Corrigida" in correcao_completa:
528
  analise_fim = correcao_completa.index("### Petição Corrigida")
529
  analise = correcao_completa[:analise_fim].strip()
@@ -536,9 +428,9 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
536
  "peticao": peticao_conteudo,
537
  "correcao_completa": correcao_completa
538
  })
539
- if len(HISTORICO) > 10:
540
  HISTORICO.pop(0)
541
- salvar_cache(cache_key, correcao_formatada)
542
  return aviso_validacao + correcao_formatada
543
  except requests.Timeout:
544
  if tentativa < CONFIG["max_retry_attempts"] - 1:
@@ -551,7 +443,6 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
551
  continue
552
  return aviso_validacao + f"❌ Erro ao chamar a API: {e}"
553
  except Exception as e:
554
- logging.error(f"Erro inesperado ao corrigir petição: {str(e)}")
555
  return aviso_validacao + f"❌ Erro inesperado: {str(e)}"
556
 
557
  def formatar_correcao(texto):
@@ -575,6 +466,7 @@ def formatar_correcao(texto):
575
  else:
576
  texto_formatado += f"{linha}\n"
577
  if "Nota Final" in texto_formatado:
 
578
  nota_pattern = r"### Nota Final.*?(\d+[.,]\d+|\d+)"
579
  match = re.search(nota_pattern, texto_formatado, re.DOTALL)
580
  if match:
@@ -590,9 +482,9 @@ def extrair_peticao_corrigida(correcao_completa):
590
  fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
591
  peticao_texto = peticao_texto[:fim].strip()
592
  return peticao_texto
593
- return "⚠️ A seção '### Petição Corrigida' não foi encontrada na resposta da API."
 
594
  except Exception as e:
595
- logging.error(f"Erro ao extrair petição corrigida: {str(e)}")
596
  return f"❌ Erro ao extrair petição corrigida: {str(e)}"
597
 
598
  def fornecer_dica_contextual(texto):
@@ -712,7 +604,6 @@ def exportar_diferencas_html(caso, peticao_texto, correcao):
712
  HTML(string=html_content).write_pdf(temp_path)
713
  return temp_path
714
  except Exception as e:
715
- logging.error(f"Erro ao gerar PDF: {str(e)}")
716
  return f"❌ Erro ao gerar PDF: {str(e)}"
717
  finally:
718
  if 'temp_path' in locals() and os.path.exists(temp_path):
@@ -837,7 +728,7 @@ def interface_gradio():
837
  inputs=[caso_input, peticao_texto, resultado_output],
838
  outputs=[download_file]
839
  ).then(
840
- fn=lambda x: [gr.update(visible=True, value=x if not x.startswith("❌") else None), gr.update(visible=True)],
841
  outputs=[download_file, download_btn]
842
  )
843
 
 
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.")
 
32
  "retry_delay": 5,
33
  "timeout": 60,
34
  "temperatura": 0.2,
35
+ "max_tokens": 4096
36
  }
37
 
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
+ "Qualificação": "Mencione nome, estado civil (se informado), endereço (use 'Rua XXX' se não fornecido). "
44
+ "Dica: Sempre verifique se todos os dados das partes estão corretos e completos.",
45
+ "Fatos": "Descreva os fatos cronologicamente, sem inventar informações. "
46
+ "Exemplo: 'Em [data], o autor celebrou contrato com o réu para a prestação de serviços de [descrição]'.",
47
+ "Pedidos": "Seja específico nos pedidos (ex.: 'condenação ao pagamento de R$ X'). "
48
+ "Dica: Inclua pedidos de danos morais e materiais, se aplicável, com base em precedentes.",
49
+ "Custas": "Inclua 'Requer a juntada da guia de custas devidamente recolhida'. "
50
+ "Dica: Nunca se esqueça de mencionar o recolhimento das custas para evitar a extinção do processo.",
51
+ "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). "
52
+ "Exemplo: 'Conforme o artigo 186 do Código Civil, a responsabilidade civil pode ser caracterizada'.",
53
+ "Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula. "
54
+ "Dica: Evite erros de pontuação que podem comprometer a formalidade do documento.",
55
+ "Estratégia Jurídica": "Utilize precedentes e súmulas para fortalecer sua argumentação. "
56
+ "Exemplo: 'Conforme a Súmula 387 do STJ, os danos morais devem ser comprovados e quantificados'.",
57
+ "Provas": "Especifique as provas que pretende produzir. "
58
+ "Dica: Inclua testemunhas, documentos e perícias, se necessário, para comprovar os fatos alegados.",
59
+ "Contra-argumentação": "Antecipe possíveis defesas da parte contrária e prepare respostas. "
60
+ "Dica: Considere os argumentos que a outra parte pode usar e esteja preparado para refutá-los."
61
  }
62
 
63
  HISTORICO = []
64
+ CORRECAO_CACHE = {}
65
 
66
  def preprocess_image_for_ocr(img):
67
  try:
68
  gray = img.convert('L')
69
  np_img = np.array(gray)
70
+ _, thresh = cv2.threshold(np_img, 150, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
 
 
71
  return Image.fromarray(thresh)
72
  except Exception as e:
 
73
  return f"❌ Erro no pré-processamento: {str(e)}"
74
 
75
  def extract_text_from_pdf(file_path_or_obj):
 
105
  text += f"[Erro na página {i+1}: {str(e)}]\n"
106
  return text if text.strip() else "⚠️ Não foi possível extrair texto do PDF."
107
  except Exception as e:
 
108
  return f"❌ Erro ao processar o PDF: {str(e)}"
109
 
110
  def extract_text_from_docx(file_path_or_obj):
 
132
  text += "\n"
133
  return text if text.strip() else "⚠️ Não foi possível extrair texto do DOCX."
134
  except Exception as e:
 
135
  return f"❌ Erro ao processar o DOCX: {str(e)}"
136
 
137
  def extract_text_from_txt(file_path_or_obj):
 
156
  continue
157
  return "❌ Não foi possível decodificar o arquivo TXT."
158
  except Exception as e:
 
159
  return f"❌ Erro ao processar o TXT: {str(e)}"
160
 
161
  def process_file(file_obj):
 
162
  if file_obj is None:
163
  return "Nenhum arquivo enviado."
164
  try:
 
179
  else:
180
  return "❌ Erro: Objeto de arquivo inválido."
181
  except Exception as e:
 
182
  return f"❌ Erro ao processar o arquivo: {str(e)}"
183
 
184
  def calcular_pontuacao(enderecamento, fundamentos, pedidos, fechamento):
 
205
  return f"{tabela_formatada}\n\n**Nota Final:** {nota_final:.2f} / 10,00"
206
 
207
  def validar_peticao_basica(peticao):
 
208
  problemas = []
209
+ if "EXCELENTÍSSIMO" not in peticao.upper():
 
 
 
210
  problemas.append("⚠️ Endereçamento ausente ou incompleto (Art. 319, I, CPC).")
211
+ if "Art. 319" not in peticao and "CPC" not in peticao:
212
+ problemas.append("⚠️ Fundamentação jurídica não menciona o CPC (Art. 319).")
213
+ if "Dá-se à causa o valor" not in peticao:
 
 
 
 
 
 
214
  problemas.append("⚠️ Valor da causa não especificado (Art. 319, V, CPC).")
215
+ if "juntada da guia de custas" not in peticao.lower():
 
 
 
 
 
 
 
 
216
  problemas.append("⚠️ Pedido de juntada da guia de custas ausente (-1,0 ponto).")
217
+ if "Nestes termos" not in peticao:
218
+ problemas.append("⚠️ Fechamento formal ausente.")
 
 
 
 
 
 
 
 
 
 
 
219
  return problemas
220
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
222
  """Estrutura um prompt avançado e especializado para correção de petições."""
223
  base_prompt = f"""
 
258
  - **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.
259
  - **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.
260
  - **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.
261
+ - **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) con -0,05 ponto por ocorrência.
262
  - **Precedentes**: Inclua referências a súmulas ou decisões relevantes (ex.: Súmula 297/STJ para CDC) quando aplicável no caso.
263
 
264
  **Pontuação Detalhada:**
 
371
  continue
372
  return f"❌ Erro ao chamar a API: {e}"
373
  except Exception as e:
 
374
  return f"❌ Erro inesperado: {str(e)}"
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
 
 
377
  if not caso.strip():
378
  return "⚠️ Forneça o caso da banca."
379
  if peticao_arquivo is not None:
 
381
  if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
382
  return peticao_conteudo
383
  else:
384
+ peticao_conteudo = peticao_texto or ""
385
  if not peticao_conteudo.strip():
386
  return "⚠️ Forneça uma petição."
387
 
 
389
  aviso_validacao = f"### Validação Inicial\n\n" + "\n".join(problemas) + "\n\n" if problemas else ""
390
 
391
  cache_key = f"{caso[:50]}|{peticao_conteudo[:50]}|{modo_treinamento}"
392
+ if cache_key in CORRECAO_CACHE:
393
+ return aviso_validacao + CORRECAO_CACHE[cache_key]
 
394
 
395
  prompt = estruturar_prompt_correcao(caso, peticao_conteudo, modo_treinamento)
396
  payload = {
 
416
  continue
417
  return aviso_validacao + "❌ A API retornou uma resposta vazia. Tente novamente."
418
  correcao_completa = response_data["candidates"][0]["content"]["parts"][0]["text"]
 
 
 
419
  if "### Petição Corrigida" in correcao_completa:
420
  analise_fim = correcao_completa.index("### Petição Corrigida")
421
  analise = correcao_completa[:analise_fim].strip()
 
428
  "peticao": peticao_conteudo,
429
  "correcao_completa": correcao_completa
430
  })
431
+ if len(HISTORICO) > 10: # Limite de histórico
432
  HISTORICO.pop(0)
433
+ CORRECAO_CACHE[cache_key] = correcao_formatada
434
  return aviso_validacao + correcao_formatada
435
  except requests.Timeout:
436
  if tentativa < CONFIG["max_retry_attempts"] - 1:
 
443
  continue
444
  return aviso_validacao + f"❌ Erro ao chamar a API: {e}"
445
  except Exception as e:
 
446
  return aviso_validacao + f"❌ Erro inesperado: {str(e)}"
447
 
448
  def formatar_correcao(texto):
 
466
  else:
467
  texto_formatado += f"{linha}\n"
468
  if "Nota Final" in texto_formatado:
469
+ import re
470
  nota_pattern = r"### Nota Final.*?(\d+[.,]\d+|\d+)"
471
  match = re.search(nota_pattern, texto_formatado, re.DOTALL)
472
  if match:
 
482
  fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
483
  peticao_texto = peticao_texto[:fim].strip()
484
  return peticao_texto
485
+ else:
486
+ return "⚠️ A seção '### Petição Corrigida' não foi encontrada na resposta da API. Verifique a resposta e tente novamente."
487
  except Exception as e:
 
488
  return f"❌ Erro ao extrair petição corrigida: {str(e)}"
489
 
490
  def fornecer_dica_contextual(texto):
 
604
  HTML(string=html_content).write_pdf(temp_path)
605
  return temp_path
606
  except Exception as e:
 
607
  return f"❌ Erro ao gerar PDF: {str(e)}"
608
  finally:
609
  if 'temp_path' in locals() and os.path.exists(temp_path):
 
728
  inputs=[caso_input, peticao_texto, resultado_output],
729
  outputs=[download_file]
730
  ).then(
731
+ 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)],
732
  outputs=[download_file, download_btn]
733
  )
734