GabrielGD commited on
Commit
f260604
·
verified ·
1 Parent(s): 3a114cb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -73
app.py CHANGED
@@ -37,17 +37,15 @@ CONFIG = {
37
  "max_retry_attempts": 3,
38
  "retry_delay": 5,
39
  "timeout": 180,
40
- "temperatura": 0.3, # Default temp for general IA correction
41
  "max_tokens": 8192
42
  }
43
 
44
- # Lower temperature for more deterministic OAB-based generation/correction
45
  CONFIG_OAB = {
46
  "temperatura": 0.2,
47
  "max_tokens": 8192
48
  }
49
 
50
-
51
  rate_limiter = RateLimiter(max_calls=30, period=60)
52
 
53
  TIPOS_DE_PETICAO = [
@@ -89,7 +87,6 @@ DICAS_OAB_MELHORADO = {
89
  HISTORICO = []
90
  CORRECAO_CACHE = {}
91
 
92
- # --- Funções de Extração de Texto ---
93
  def preprocess_image_for_ocr(img):
94
  try:
95
  gray = img.convert('L')
@@ -111,7 +108,7 @@ def extract_text_from_pdf(file_path_or_obj):
111
  page_text = page.extract_text()
112
  if page_text:
113
  page_full_text += page_text + "\n"
114
- text += page_text + "\n" # Keep track of non-OCR text separately
115
  else:
116
  logger.warning(f"Página {i+1} sem texto extraível, tentando OCR.")
117
  try:
@@ -120,7 +117,7 @@ def extract_text_from_pdf(file_path_or_obj):
120
  page_ocr_text = pytesseract.image_to_string(preprocessed_img, lang='por')
121
  if page_ocr_text.strip():
122
  page_full_text += page_ocr_text + "\n"
123
- ocr_text += page_ocr_text + "\n" # Keep track of OCR text separately
124
  else:
125
  logger.warning(f"OCR falhou na página {i+1}.")
126
  page_full_text += f"[OCR falhou na página {i+1}]\n"
@@ -162,7 +159,6 @@ def extract_text_from_pdf(file_path_or_obj):
162
  logger.error(f"Erro no fallback com PyPDF2: {str(fallback_e)}")
163
  return f"❌ Erro crítico ao processar o PDF: {str(e)}"
164
 
165
-
166
  def extract_text_from_docx(file_path_or_obj):
167
  try:
168
  doc = docx.Document(file_path_or_obj)
@@ -235,8 +231,6 @@ def process_file(file_obj):
235
  logger.error(f"Erro geral ao processar o arquivo '{getattr(file_obj, 'name', 'N/A')}': {str(e)}", exc_info=True)
236
  return f"❌ Erro inesperado ao processar o arquivo: {str(e)}"
237
 
238
-
239
- # --- Funções da API Gemini ---
240
  def chamar_api(prompt, use_oab_config=False):
241
  config_to_use = CONFIG_OAB if use_oab_config else CONFIG
242
  payload = {
@@ -311,12 +305,6 @@ def chamar_api(prompt, use_oab_config=False):
311
 
312
  return "❌ Falha API após todas tentativas."
313
 
314
-
315
- # --- Funções de Correção Geral (IA) ---
316
- # (Inclui as funções restauradas: get_instrucoes_gerais_avaliacao, estruturar_prompt_analise,
317
- # estruturar_prompt_peticao_corrigida, pos_corrigir_peticao, corrigir_peticao_geral_ia,
318
- # formatar_correcao, avaliar_criterios_peticao (renomeada para avaliacao_criterios_ia), etc.)
319
-
320
  def get_instrucoes_gerais_avaliacao(modo_treinamento=False):
321
  base = """
322
  **Estrutura da Avaliação (Geral - IA):**
@@ -394,7 +382,7 @@ def estruturar_prompt_analise_geral(caso, peticao, tipo_peticao, modo_treinament
394
  - **Pedidos:** Acolhimento preliminares; IMPROCEDÊNCIA TOTAL; Condenação custas/honorários.
395
  - Indicação de provas. Fechamento formal.
396
  """
397
- # Add other petition types...
398
 
399
  prompt_especifico += "\n\n**Instruções Finais:** Analise a peça do aluno com base no caso e nos critérios gerais e específicos. Use seu conhecimento jurídico para simular um examinador da OAB. Forneça feedback construtivo e detalhado. Gere a resposta **exclusivamente** no formato solicitado (Análise Geral, Nota, Pontos Positivos, etc.)."
400
  return prompt_especifico
@@ -451,32 +439,32 @@ def pos_corrigir_peticao(peticao_corrigida, tipo_peticao):
451
  if tipo_peticao in ["Petição Inicial", "Ação de Alimentos Gravídicos"]:
452
  if "juntada da guia de custas" not in peticao_corrigida.lower() and "gratuidade de justiça" not in peticao_corrigida.lower():
453
  partes = peticao_corrigida.split("Nestes termos pede deferimento")
454
- prefixo = partes[0] if len(partes) > 1 else peticao_corrigida
455
- sufixo = "Nestes termos pede deferimento" + partes[1] if len(partes) > 1 else ""
456
- peticao_corrigida = prefixo.strip() + "\nRequer, ainda, a juntada da guia de custas devidamente recolhida.\n" + sufixo
457
 
458
  if "audiência de conciliação" not in peticao_corrigida.lower() and "audiência de mediação" not in peticao_corrigida.lower():
459
  partes = peticao_corrigida.split("Nestes termos pede deferimento")
460
- prefixo = partes[0] if len(partes) > 1 else peticao_corrigida
461
- sufixo = "Nestes termos pede deferimento" + partes[1] if len(partes) > 1 else ""
462
- peticao_corrigida = prefixo.strip() + "\nOpta o Autor pela realização da audiência de conciliação ou mediação (Art. 319, VII, CPC).\n" + sufixo
463
 
464
  linhas = [l.strip() for l in peticao_corrigida.strip().split('\n') if l.strip()]
465
  fechamento_padrao = ["\nLocal, data.", "ADVOGADO(A)", "OAB/..."]
466
- # Improved removal of existing closing lines
467
- while linhas and any(term.lower() in linhas[-1].lower() for term in ["advogado", "oab", "local", "data", "pede deferimento"]):
468
- # Check if it's the standard closing part or just content
469
- is_standard_closing = any(std_close in linhas[-1] for std_close in fechamento_padrao)
470
- if not is_standard_closing and "pede deferimento" in linhas[-1].lower() and len(linhas) > 3: # Avoid removing the actual request line
 
 
471
  break
472
  linhas.pop()
473
 
474
- # Add standard closing cleanly
475
- peticao_corrigida = "\n".join(linhas).strip() + "\n" + "\n".join(fechamento_padrao)
476
 
477
  return peticao_corrigida.strip()
478
 
479
-
480
  def formatar_correcao(texto):
481
  texto = texto.replace("### Análise Geral", "## 📝 Análise Geral")
482
  texto = texto.replace("### Nota Final", "## ⭐ Nota Final")
@@ -512,15 +500,6 @@ def formatar_correcao(texto):
512
  texto_final = re.sub(nota_pattern, f"\\1**{nota}/10**", texto_final, count=1, flags=re.IGNORECASE)
513
  return texto_final
514
 
515
- def avaliar_criterios_ia(caso, peticao_original, tipo_peticao):
516
- # This function remains from the previous step, evaluating based on IA criteria
517
- # Ensure it's called by corrigir_peticao_geral_ia if needed
518
- logger.info(f"Iniciando avaliação por critérios (IA) para: {tipo_peticao}")
519
- # ... (rest of the function logic as provided in the previous answer) ...
520
- # It needs: obter_criterios_avaliacao, estruturar_prompt_avaliacao_criterios, chamar_api
521
- # parse the JSON response and format to Markdown.
522
- return "Avaliação por Critérios (IA) - Implementação Pendente ou Removida" # Placeholder
523
-
524
  def corrigir_peticao_geral_ia(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
525
  logger.info(f"Iniciando correção GERAL (IA) para tipo: {tipo_peticao}")
526
  start_time = time.time()
@@ -537,7 +516,7 @@ def corrigir_peticao_geral_ia(caso, tipo_peticao, peticao_texto=None, peticao_ar
537
  peticao_conteudo = peticao_texto
538
  else: return "⚠️ Forneça a petição.", "", "", "", "", "", ""
539
 
540
- troll_triggers = [ "escreva a petição", "faça a petição"] # Shortened
541
  if any(trigger in peticao_conteudo.lower() for trigger in troll_triggers):
542
  return random.choice(["Nope! 😉", "Faça você e eu corrijo!"]), "", "", "", "", "", ""
543
 
@@ -565,8 +544,7 @@ def corrigir_peticao_geral_ia(caso, tipo_peticao, peticao_texto=None, peticao_ar
565
  else:
566
  peticao_corrigida_final = pos_corrigir_peticao(peticao_corrigida_bruta, tipo_peticao)
567
 
568
- # avaliacao_criterios_md = avaliar_criterios_ia(caso, peticao_conteudo, tipo_peticao) # Keep this call commented/removed if not needed now
569
- avaliacao_criterios_md = "" # Set to empty for now
570
 
571
  visualizacao_detalhada_html, resumo_diff, criticas_diff = calcular_diff_interno(peticao_conteudo, peticao_corrigida_final)
572
 
@@ -583,11 +561,7 @@ def corrigir_peticao_geral_ia(caso, tipo_peticao, peticao_texto=None, peticao_ar
583
  return ( correcao_completa_formatada, peticao_conteudo, peticao_corrigida_final,
584
  resumo_diff, criticas_diff, avaliacao_criterios_md, visualizacao_detalhada_html )
585
 
586
-
587
-
588
  # --- Funções de Correção Padrão OAB ---
589
- # (Inclui as funções: parse_oab_pdf, estruturar_prompt_geracao_modelo_oab,
590
- # gerar_peticao_modelo_oab, estruturar_prompt_correcao_pela_oab, corrigir_pela_oab)
591
  def parse_oab_pdf(file_obj):
592
  if file_obj is None: return None, None, None, "⚠️ Nenhum PDF OAB."
593
  try:
@@ -602,13 +576,11 @@ def parse_oab_pdf(file_obj):
602
  line_strip = line.strip()
603
  line_lower = line_strip.lower()
604
 
605
- # Section Start Markers (allow variations)
606
  if line_strip.startswith("Enunciado"): current_section = "enunciado"; continue
607
  if line_strip.startswith("Gabarito comentado"): current_section = "gabarito"; continue
608
  if line_strip.startswith("Distribuição dos Pontos"): current_section = "distribuicao"; continue
609
- # Break Markers (end of relevant sections)
610
  if line_strip.startswith("PADRÃO DE RESPOSTA – QUESTÃO"): current_section = None; break
611
- if line_strip.startswith("Padrão de Resposta"): continue # Skip sub-headers
612
 
613
  if current_section == "enunciado": enunciado += line + "\n"
614
  elif current_section == "gabarito": gabarito += line + "\n"
@@ -618,10 +590,9 @@ def parse_oab_pdf(file_obj):
618
  gabarito = gabarito.strip()
619
  distribuicao = distribuicao.strip()
620
 
621
- # Basic check if sections were captured
622
  if not enunciado or not gabarito or not distribuicao:
623
  logger.warning(f"Parse OAB PDF falhou. E:{bool(enunciado)}, G:{bool(gabarito)}, D:{bool(distribuicao)}")
624
- return None, None, None, f"⚠️ Falha separar seções. Verifique o texto completo:\n\n{full_text[:500]}..." # Show preview
625
 
626
  logger.info("PDF OAB parseado.")
627
  return enunciado, gabarito, distribuicao, "✅ PDF OAB carregado e seções extraídas."
@@ -629,7 +600,6 @@ def parse_oab_pdf(file_obj):
629
  logger.error(f"Erro parsear PDF OAB: {str(e)}", exc_info=True)
630
  return None, None, None, f"❌ Erro processar PDF OAB: {str(e)}"
631
 
632
-
633
  def estruturar_prompt_geracao_modelo_oab(enunciado, gabarito, distribuicao):
634
  prompt = f"""
635
  **Tarefa:** Elabore a peça processual completa e adequada para o **Enunciado**, seguindo **estritamente** as diretrizes do **Gabarito Comentado** e os critérios da **Distribuição dos Pontos**. Crie uma peça modelo perfeita para este caso OAB.
@@ -666,7 +636,7 @@ def gerar_peticao_modelo_oab(enunciado, gabarito, distribuicao):
666
 
667
  logger.info("Gerando peça modelo OAB...")
668
  prompt = estruturar_prompt_geracao_modelo_oab(enunciado, gabarito, distribuicao)
669
- peticao_modelo = chamar_api(prompt, use_oab_config=True) # Use OAB config
670
 
671
  if peticao_modelo.startswith("❌") or peticao_modelo.startswith("⏱️"):
672
  logger.error(f"Erro API gerar modelo OAB: {peticao_modelo}")
@@ -704,7 +674,7 @@ def estruturar_prompt_correcao_pela_oab(peticao_usuario, enunciado, gabarito, di
704
  1. **Avaliação Pontual:** Para **cada item** na **Distribuição**, verifique se foi abordado pelo Usuário conforme o Gabarito. Atribua a pontuação correspondente.
705
  2. **Justificativa:** Justifique brevemente a pontuação de cada item.
706
  3. **Feedback:** Aponte erros/omissões do Usuário vs Gabarito/Distribuição.
707
- 4. **Peça Corrigida (Opcional - Foco na Avaliação):** Se possível, sugira trechos corrigidos para alinhar ao padrão, mas priorize a avaliação pontual.
708
  5. **Output Markdown:**
709
 
710
  ```markdown
@@ -738,7 +708,7 @@ def corrigir_pela_oab(peticao_usuario, enunciado, gabarito, distribuicao):
738
 
739
  logger.info("Corrigindo pela OAB...")
740
  prompt = estruturar_prompt_correcao_pela_oab(peticao_usuario, enunciado, gabarito, distribuicao)
741
- correcao_oab = chamar_api(prompt, use_oab_config=True) # Use OAB config
742
 
743
  if correcao_oab.startswith("❌") or correcao_oab.startswith("⏱️"):
744
  logger.error(f"Erro API corrigir OAB: {correcao_oab}")
@@ -747,9 +717,7 @@ def corrigir_pela_oab(peticao_usuario, enunciado, gabarito, distribuicao):
747
  logger.info("Correção/Avaliação OAB concluída.")
748
  return correcao_oab
749
 
750
-
751
- # --- Funções Auxiliares (Diff, Histórico, Salvar/Carregar, Gerar Caso Fictício) ---
752
- # (Keep these largely as they were in the previous correct version)
753
  def calcular_diff_interno(texto_original, texto_corrigido):
754
  if not texto_original or not texto_corrigido or texto_corrigido.startswith("⚠️") or texto_corrigido.startswith("❌"): return "","",""
755
  orig_lines = texto_original.splitlines(); corr_lines = texto_corrigido.splitlines()
@@ -774,7 +742,6 @@ def calcular_diff_interno(texto_original, texto_corrigido):
774
 
775
  def calcular_progresso():
776
  if not HISTORICO: return "📊 Progresso vazio."
777
- # Simplified progress focusing on General IA for now
778
  notas_geral = []
779
  tipos_geral = set()
780
  count_geral = 0
@@ -851,15 +818,71 @@ def gerar_caso_ficticio(tipo_peticao="Petição Inicial"):
851
  logger.info("Caso gerado.")
852
  return caso
853
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
854
 
855
- # --- Interface Gradio (Reestruturada com Abas) ---
856
  def interface_gradio():
857
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="orange")) as demo:
858
  gr.Markdown("# ⚖️ Corretor Jurídico OAB Master ⚖️")
859
  gr.Markdown("Use a **Correção Geral (IA)** para feedback amplo ou a **Correção Baseada no Padrão OAB** para avaliação específica.")
860
 
861
  with gr.Tabs() as main_tabs:
862
- # --- ABA: Correção Geral (IA) ---
863
  with gr.TabItem("🤖 Correção Geral (IA)", id=0):
864
  with gr.Row():
865
  with gr.Column(scale=1):
@@ -905,7 +928,6 @@ def interface_gradio():
905
  processing_message_geral = gr.Markdown("", visible=False)
906
  with gr.Tabs():
907
  with gr.TabItem("📊 Análise IA"): resultado_analise_output_geral = gr.Markdown()
908
- # with gr.TabItem("💎 Critérios IA"): avaliacao_criterios_output_geral = gr.Markdown() # Optional tab
909
  with gr.TabItem("✨ Correção IA"): resultado_peticao_corrigida_output_geral = gr.Textbox(lines=15, show_copy_button=True)
910
  with gr.TabItem("⚖️ Diff"):
911
  with gr.Row():
@@ -913,7 +935,6 @@ def interface_gradio():
913
  with gr.Column(scale=3): visualizacao_detalhada_output_geral = gr.HTML()
914
  criticas_output_geral = gr.Markdown()
915
 
916
- # --- ABA: Correção Padrão OAB ---
917
  with gr.TabItem("📄 Correção Padrão OAB", id=1):
918
  gr.Markdown("Forneça o material oficial (PDF ou texto) e sua peça.")
919
  with gr.Row():
@@ -941,13 +962,11 @@ def interface_gradio():
941
 
942
  clear_btn = gr.Button("🗑️ Limpar Tudo")
943
 
944
- # --- Lógica Eventos ---
945
  caso_btn_geral.click(gerar_caso_ficticio, [tipo_peticao_input_geral], [caso_input_geral])
946
  peticao_texto_input_geral.change(fornecer_dica_contextual, [peticao_texto_input_geral, tipo_peticao_input_geral], dica_contextual_output_geral)
947
  tipo_peticao_input_geral.change(fornecer_dica_contextual, [peticao_texto_input_geral, tipo_peticao_input_geral], dica_contextual_output_geral)
948
  peticao_arquivo_input_geral.change(process_file, [peticao_arquivo_input_geral], arquivo_preview_output_geral)
949
 
950
- # Correção Geral IA (Texto)
951
  btn_corrigir_geral.click( lambda: gr.update(value="⏳ Processando IA...", visible=True), None, processing_message_geral).then(
952
  corrigir_peticao_geral_ia,
953
  [caso_input_geral, tipo_peticao_input_geral, peticao_texto_input_geral, gr.State(None), modo_treinamento_geral],
@@ -955,7 +974,6 @@ def interface_gradio():
955
  resumo_alteracoes_output_geral, criticas_output_geral, gr.Markdown(visible=False), visualizacao_detalhada_output_geral]
956
  ).then( lambda: gr.update(visible=False), None, processing_message_geral )
957
 
958
- # Correção Geral IA (Arquivo)
959
  btn_corrigir_arquivo_geral.click( lambda: gr.update(value="⏳ Processando IA...", visible=True), None, processing_message_geral).then(
960
  corrigir_peticao_geral_ia,
961
  [caso_input_geral, tipo_peticao_input_geral, gr.State(None), peticao_arquivo_input_geral, modo_treinamento_geral],
@@ -963,7 +981,6 @@ def interface_gradio():
963
  resumo_alteracoes_output_geral, criticas_output_geral, gr.Markdown(visible=False), visualizacao_detalhada_output_geral]
964
  ).then( lambda: gr.update(visible=False), None, processing_message_geral )
965
 
966
- # Salvar/Carregar Geral
967
  salvar_btn_geral.click( lambda: gr.update(value="⏳ Salvando...", visible=True), None, processing_message_geral).then(
968
  salvar_progresso, [caso_input_geral, peticao_texto_input_geral, tipo_peticao_input_geral],
969
  [gr.Textbox(visible=False), config_status_geral, progresso_download_file_geral]
@@ -974,7 +991,6 @@ def interface_gradio():
974
  [tipo_peticao_input_geral, caso_input_geral, peticao_texto_input_geral, config_status_geral]
975
  ).then( lambda: gr.update(visible=False), None, processing_message_geral )
976
 
977
- # Padrão OAB
978
  oab_pdf_input.change(parse_oab_pdf, [oab_pdf_input], [oab_enunciado_input, oab_gabarito_input, oab_distribuicao_input, parse_status_output])
979
  btn_gerar_modelo_oab.click( lambda: gr.update(value="⏳ Gerando Modelo...", visible=True), None, processing_message_oab).then(
980
  gerar_peticao_modelo_oab, [oab_enunciado_input, oab_gabarito_input, oab_distribuicao_input], [resultado_modelo_oab_output]
@@ -983,10 +999,9 @@ def interface_gradio():
983
  corrigir_pela_oab, [peticao_usuario_oab_input, oab_enunciado_input, oab_gabarito_input, oab_distribuicao_input], [resultado_correcao_oab_output]
984
  ).then( lambda: gr.update(visible=False), None, processing_message_oab )
985
 
986
- # Limpar Tudo
987
- clear_btn.click( fn=lambda: [ # Reset values for all relevant components across tabs
988
- "Petição Inicial", False, None, None, None, None, "", "", "", "", "", "", "", None, None, # Geral IA Tab
989
- None, "", None, None, None, "", "", "", "" # OAB Tab
990
  ], inputs=None, outputs=[
991
  tipo_peticao_input_geral, modo_treinamento_geral, caso_input_geral, peticao_texto_input_geral, peticao_arquivo_input_geral,
992
  arquivo_preview_output_geral, processing_message_geral, resultado_analise_output_geral,
@@ -999,7 +1014,6 @@ def interface_gradio():
999
 
1000
  return demo
1001
 
1002
- # --- Execução ---
1003
  if __name__ == "__main__":
1004
  app = interface_gradio()
1005
  app.launch()
 
37
  "max_retry_attempts": 3,
38
  "retry_delay": 5,
39
  "timeout": 180,
40
+ "temperatura": 0.3,
41
  "max_tokens": 8192
42
  }
43
 
 
44
  CONFIG_OAB = {
45
  "temperatura": 0.2,
46
  "max_tokens": 8192
47
  }
48
 
 
49
  rate_limiter = RateLimiter(max_calls=30, period=60)
50
 
51
  TIPOS_DE_PETICAO = [
 
87
  HISTORICO = []
88
  CORRECAO_CACHE = {}
89
 
 
90
  def preprocess_image_for_ocr(img):
91
  try:
92
  gray = img.convert('L')
 
108
  page_text = page.extract_text()
109
  if page_text:
110
  page_full_text += page_text + "\n"
111
+ text += page_text + "\n"
112
  else:
113
  logger.warning(f"Página {i+1} sem texto extraível, tentando OCR.")
114
  try:
 
117
  page_ocr_text = pytesseract.image_to_string(preprocessed_img, lang='por')
118
  if page_ocr_text.strip():
119
  page_full_text += page_ocr_text + "\n"
120
+ ocr_text += page_ocr_text + "\n"
121
  else:
122
  logger.warning(f"OCR falhou na página {i+1}.")
123
  page_full_text += f"[OCR falhou na página {i+1}]\n"
 
159
  logger.error(f"Erro no fallback com PyPDF2: {str(fallback_e)}")
160
  return f"❌ Erro crítico ao processar o PDF: {str(e)}"
161
 
 
162
  def extract_text_from_docx(file_path_or_obj):
163
  try:
164
  doc = docx.Document(file_path_or_obj)
 
231
  logger.error(f"Erro geral ao processar o arquivo '{getattr(file_obj, 'name', 'N/A')}': {str(e)}", exc_info=True)
232
  return f"❌ Erro inesperado ao processar o arquivo: {str(e)}"
233
 
 
 
234
  def chamar_api(prompt, use_oab_config=False):
235
  config_to_use = CONFIG_OAB if use_oab_config else CONFIG
236
  payload = {
 
305
 
306
  return "❌ Falha API após todas tentativas."
307
 
 
 
 
 
 
 
308
  def get_instrucoes_gerais_avaliacao(modo_treinamento=False):
309
  base = """
310
  **Estrutura da Avaliação (Geral - IA):**
 
382
  - **Pedidos:** Acolhimento preliminares; IMPROCEDÊNCIA TOTAL; Condenação custas/honorários.
383
  - Indicação de provas. Fechamento formal.
384
  """
385
+ # Add other types...
386
 
387
  prompt_especifico += "\n\n**Instruções Finais:** Analise a peça do aluno com base no caso e nos critérios gerais e específicos. Use seu conhecimento jurídico para simular um examinador da OAB. Forneça feedback construtivo e detalhado. Gere a resposta **exclusivamente** no formato solicitado (Análise Geral, Nota, Pontos Positivos, etc.)."
388
  return prompt_especifico
 
439
  if tipo_peticao in ["Petição Inicial", "Ação de Alimentos Gravídicos"]:
440
  if "juntada da guia de custas" not in peticao_corrigida.lower() and "gratuidade de justiça" not in peticao_corrigida.lower():
441
  partes = peticao_corrigida.split("Nestes termos pede deferimento")
442
+ prefixo = partes[0].strip() if len(partes) > 1 else peticao_corrigida.strip()
443
+ sufixo = "\nNestes termos pede deferimento" + partes[1] if len(partes) > 1 else ""
444
+ peticao_corrigida = prefixo + "\nRequer, ainda, a juntada da guia de custas devidamente recolhida." + sufixo
445
 
446
  if "audiência de conciliação" not in peticao_corrigida.lower() and "audiência de mediação" not in peticao_corrigida.lower():
447
  partes = peticao_corrigida.split("Nestes termos pede deferimento")
448
+ prefixo = partes[0].strip() if len(partes) > 1 else peticao_corrigida.strip()
449
+ sufixo = "\nNestes termos pede deferimento" + partes[1] if len(partes) > 1 else ""
450
+ peticao_corrigida = prefixo + "\nOpta o Autor pela realização da audiência de conciliação ou mediação (Art. 319, VII, CPC)." + sufixo
451
 
452
  linhas = [l.strip() for l in peticao_corrigida.strip().split('\n') if l.strip()]
453
  fechamento_padrao = ["\nLocal, data.", "ADVOGADO(A)", "OAB/..."]
454
+
455
+ # Remove potential multiple closing lines before adding the standard one
456
+ while linhas and any(term.lower() in linhas[-1].lower() for term in ["advogado", "oab", "local", "data", "pede deferimento", "termos em que"]):
457
+ # More robust check to avoid removing essential content like the final request
458
+ last_line_lower = linhas[-1].lower()
459
+ is_closing_line = any(std_close.strip().lower() in last_line_lower for std_close in fechamento_padrao) or "pede deferimento" in last_line_lower or "termos em que" in last_line_lower
460
+ if not is_closing_line and len(linhas[-1]) > 30: # Heuristic: unlikely to be just closing
461
  break
462
  linhas.pop()
463
 
464
+ peticao_corrigida = "\n".join(linhas).strip() + "".join(fechamento_padrao) # Join directly
 
465
 
466
  return peticao_corrigida.strip()
467
 
 
468
  def formatar_correcao(texto):
469
  texto = texto.replace("### Análise Geral", "## 📝 Análise Geral")
470
  texto = texto.replace("### Nota Final", "## ⭐ Nota Final")
 
500
  texto_final = re.sub(nota_pattern, f"\\1**{nota}/10**", texto_final, count=1, flags=re.IGNORECASE)
501
  return texto_final
502
 
 
 
 
 
 
 
 
 
 
503
  def corrigir_peticao_geral_ia(caso, tipo_peticao, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
504
  logger.info(f"Iniciando correção GERAL (IA) para tipo: {tipo_peticao}")
505
  start_time = time.time()
 
516
  peticao_conteudo = peticao_texto
517
  else: return "⚠️ Forneça a petição.", "", "", "", "", "", ""
518
 
519
+ troll_triggers = [ "escreva a petição", "faça a petição"]
520
  if any(trigger in peticao_conteudo.lower() for trigger in troll_triggers):
521
  return random.choice(["Nope! 😉", "Faça você e eu corrijo!"]), "", "", "", "", "", ""
522
 
 
544
  else:
545
  peticao_corrigida_final = pos_corrigir_peticao(peticao_corrigida_bruta, tipo_peticao)
546
 
547
+ avaliacao_criterios_md = ""
 
548
 
549
  visualizacao_detalhada_html, resumo_diff, criticas_diff = calcular_diff_interno(peticao_conteudo, peticao_corrigida_final)
550
 
 
561
  return ( correcao_completa_formatada, peticao_conteudo, peticao_corrigida_final,
562
  resumo_diff, criticas_diff, avaliacao_criterios_md, visualizacao_detalhada_html )
563
 
 
 
564
  # --- Funções de Correção Padrão OAB ---
 
 
565
  def parse_oab_pdf(file_obj):
566
  if file_obj is None: return None, None, None, "⚠️ Nenhum PDF OAB."
567
  try:
 
576
  line_strip = line.strip()
577
  line_lower = line_strip.lower()
578
 
 
579
  if line_strip.startswith("Enunciado"): current_section = "enunciado"; continue
580
  if line_strip.startswith("Gabarito comentado"): current_section = "gabarito"; continue
581
  if line_strip.startswith("Distribuição dos Pontos"): current_section = "distribuicao"; continue
 
582
  if line_strip.startswith("PADRÃO DE RESPOSTA – QUESTÃO"): current_section = None; break
583
+ if line_strip.startswith("Padrão de Resposta"): continue
584
 
585
  if current_section == "enunciado": enunciado += line + "\n"
586
  elif current_section == "gabarito": gabarito += line + "\n"
 
590
  gabarito = gabarito.strip()
591
  distribuicao = distribuicao.strip()
592
 
 
593
  if not enunciado or not gabarito or not distribuicao:
594
  logger.warning(f"Parse OAB PDF falhou. E:{bool(enunciado)}, G:{bool(gabarito)}, D:{bool(distribuicao)}")
595
+ return None, None, None, f"⚠️ Falha separar seções. Verifique o texto completo:\n\n{full_text[:500]}..."
596
 
597
  logger.info("PDF OAB parseado.")
598
  return enunciado, gabarito, distribuicao, "✅ PDF OAB carregado e seções extraídas."
 
600
  logger.error(f"Erro parsear PDF OAB: {str(e)}", exc_info=True)
601
  return None, None, None, f"❌ Erro processar PDF OAB: {str(e)}"
602
 
 
603
  def estruturar_prompt_geracao_modelo_oab(enunciado, gabarito, distribuicao):
604
  prompt = f"""
605
  **Tarefa:** Elabore a peça processual completa e adequada para o **Enunciado**, seguindo **estritamente** as diretrizes do **Gabarito Comentado** e os critérios da **Distribuição dos Pontos**. Crie uma peça modelo perfeita para este caso OAB.
 
636
 
637
  logger.info("Gerando peça modelo OAB...")
638
  prompt = estruturar_prompt_geracao_modelo_oab(enunciado, gabarito, distribuicao)
639
+ peticao_modelo = chamar_api(prompt, use_oab_config=True)
640
 
641
  if peticao_modelo.startswith("❌") or peticao_modelo.startswith("⏱️"):
642
  logger.error(f"Erro API gerar modelo OAB: {peticao_modelo}")
 
674
  1. **Avaliação Pontual:** Para **cada item** na **Distribuição**, verifique se foi abordado pelo Usuário conforme o Gabarito. Atribua a pontuação correspondente.
675
  2. **Justificativa:** Justifique brevemente a pontuação de cada item.
676
  3. **Feedback:** Aponte erros/omissões do Usuário vs Gabarito/Distribuição.
677
+ 4. **Peça Corrigida (Opcional):** Se possível, sugira trechos corrigidos para alinhar ao padrão.
678
  5. **Output Markdown:**
679
 
680
  ```markdown
 
708
 
709
  logger.info("Corrigindo pela OAB...")
710
  prompt = estruturar_prompt_correcao_pela_oab(peticao_usuario, enunciado, gabarito, distribuicao)
711
+ correcao_oab = chamar_api(prompt, use_oab_config=True)
712
 
713
  if correcao_oab.startswith("❌") or correcao_oab.startswith("⏱️"):
714
  logger.error(f"Erro API corrigir OAB: {correcao_oab}")
 
717
  logger.info("Correção/Avaliação OAB concluída.")
718
  return correcao_oab
719
 
720
+ # --- Funções Auxiliares (Diff, Histórico, Salvar/Carregar, Gerar Caso Fictício, Dicas) ---
 
 
721
  def calcular_diff_interno(texto_original, texto_corrigido):
722
  if not texto_original or not texto_corrigido or texto_corrigido.startswith("⚠️") or texto_corrigido.startswith("❌"): return "","",""
723
  orig_lines = texto_original.splitlines(); corr_lines = texto_corrigido.splitlines()
 
742
 
743
  def calcular_progresso():
744
  if not HISTORICO: return "📊 Progresso vazio."
 
745
  notas_geral = []
746
  tipos_geral = set()
747
  count_geral = 0
 
818
  logger.info("Caso gerado.")
819
  return caso
820
 
821
+ def fornecer_dica_contextual(texto_peticao, tipo_peticao):
822
+ if not texto_peticao or not texto_peticao.strip():
823
+ return "Digite algo na petição para receber dicas..."
824
+ dicas_relevantes = []
825
+ texto_lower = texto_peticao.lower()
826
+ for chave, dica in DICAS_OAB_MELHORADO.items():
827
+ if chave in ["Clareza e Concisão", "Fundamentação Legal", "Coerência Argumentativa", "Revisão Ortográfica e Gramatical", "Formatação"]:
828
+ dicas_relevantes.append(f"💡 **{chave}:** {dica}")
829
+ keywords_por_tipo = {
830
+ "Petição Inicial": ["endereçamento", "qualificação", "fatos", "fundamentos", "pedidos", "valor da causa", "provas", "audiência", "citação", "custas"],
831
+ "Contestação": ["preliminar", "mérito", "impugnação", "fatos", "improcedência", "provas", "honorários"],
832
+ "Reconvenção": ["reconvinte", "reconvindo", "conexão", "pedidos reconvencionais"],
833
+ "Impugnação à Contestação": ["réplica", "preliminar", "fatos novos", "documentos", "ratifica"],
834
+ "Embargos de Declaração": ["omissão", "contradição", "obscuridade", "erro material", "decisão embargada"],
835
+ "Recurso de Apelação": ["sentença", "reforma", "anulação", "dialeticidade", "preparo", "tribunal", "razões"],
836
+ "Agravo de Instrumento": ["decisão interlocutória", "tribunal", "cabimento", "urgência", "efeito suspensivo", "peças obrigatórias"],
837
+ "Petição de Tutela Provisória": ["urgência", "evidência", "liminar", "periculum", "fumus boni", "probabilidade", "risco"],
838
+ "Ação de Alimentos Gravídicos": ["gravídicos", "nascituro", "indícios de paternidade", "binômio", "conversão", "lei 11.804"],
839
+ }
840
+ if tipo_peticao in keywords_por_tipo:
841
+ for keyword in keywords_por_tipo[tipo_peticao]:
842
+ if keyword in texto_lower:
843
+ for chave, dica in DICAS_OAB_MELHORADO.items():
844
+ chave_lower = chave.lower()
845
+ tipo_simples = tipo_peticao.split()[0].lower() if tipo_peticao else ""
846
+ if f"({tipo_simples})" in chave_lower or keyword in chave_lower:
847
+ dica_formatada = f"💡 **{chave}:** {dica}"
848
+ if dica_formatada not in dicas_relevantes:
849
+ dicas_relevantes.append(dica_formatada)
850
+ if not dicas_relevantes:
851
+ return "💡 Continue escrevendo ou selecione o tipo de peça para dicas mais específicas."
852
+ return "\n\n".join(dicas_relevantes[:5])
853
+
854
+ def estruturar_prompt_geracao_caso(tipo_peticao="Petição Inicial"):
855
+ prompt = f"""
856
+ 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).
857
+ **Instruções Gerais para o Caso:**
858
+ 1. **Complexidade Adequada:** (Ideal: 15-25 linhas).
859
+ 2. **Dados Essenciais:** Nomes, locais, datas, valores, relação jurídica, conflito.
860
+ 3. **Coerência:** Fatos lógicos para a peça indicada.
861
+ 4. **Foco no Tipo de Peça:** Cenário exige **{tipo_peticao}**.
862
+ **Instruções Específicas para Gerar Caso para "{tipo_peticao}":**
863
+ """
864
+ if tipo_peticao == "Petição Inicial" or tipo_peticao == "Ação de Alimentos Gravídicos":
865
+ prompt += "* Descreva violação de direitos, busca inicial da tutela. Elementos para danos. Dados para qualificação. Temas: consumo, contrato, cobrança, despejo, família (alimentos gravídicos - incluir indícios paternidade, necessidade mãe/nascituro)."
866
+ elif tipo_peticao == "Contestação":
867
+ prompt += "* Resumo da Inicial recebida. Versão dos fatos do RÉU. Elementos para Preliminares (Art. 337), Impugnação Específica, Defesa de Mérito (direta/indireta). Dados qualificação réu."
868
+ # ... (Add other types as before) ...
869
+ else: prompt += "* Crie um cenário jurídico cível com conflito claro."
870
+
871
+ prompt += """
872
+ **Finalize o caso com:**
873
+ "**Situação-Problema:** Na qualidade de advogado(a) de [Nome do Cliente], elabore a peça processual cabível ({tipo_peticao})."
874
+ **Importante:** Evite temas excessivamente específicos. Foco em Civil/Processo Civil comum.
875
+ """
876
+ return prompt
877
+
878
 
879
+ # --- Interface Gradio ---
880
  def interface_gradio():
881
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="orange")) as demo:
882
  gr.Markdown("# ⚖️ Corretor Jurídico OAB Master ⚖️")
883
  gr.Markdown("Use a **Correção Geral (IA)** para feedback amplo ou a **Correção Baseada no Padrão OAB** para avaliação específica.")
884
 
885
  with gr.Tabs() as main_tabs:
 
886
  with gr.TabItem("🤖 Correção Geral (IA)", id=0):
887
  with gr.Row():
888
  with gr.Column(scale=1):
 
928
  processing_message_geral = gr.Markdown("", visible=False)
929
  with gr.Tabs():
930
  with gr.TabItem("📊 Análise IA"): resultado_analise_output_geral = gr.Markdown()
 
931
  with gr.TabItem("✨ Correção IA"): resultado_peticao_corrigida_output_geral = gr.Textbox(lines=15, show_copy_button=True)
932
  with gr.TabItem("⚖️ Diff"):
933
  with gr.Row():
 
935
  with gr.Column(scale=3): visualizacao_detalhada_output_geral = gr.HTML()
936
  criticas_output_geral = gr.Markdown()
937
 
 
938
  with gr.TabItem("📄 Correção Padrão OAB", id=1):
939
  gr.Markdown("Forneça o material oficial (PDF ou texto) e sua peça.")
940
  with gr.Row():
 
962
 
963
  clear_btn = gr.Button("🗑️ Limpar Tudo")
964
 
 
965
  caso_btn_geral.click(gerar_caso_ficticio, [tipo_peticao_input_geral], [caso_input_geral])
966
  peticao_texto_input_geral.change(fornecer_dica_contextual, [peticao_texto_input_geral, tipo_peticao_input_geral], dica_contextual_output_geral)
967
  tipo_peticao_input_geral.change(fornecer_dica_contextual, [peticao_texto_input_geral, tipo_peticao_input_geral], dica_contextual_output_geral)
968
  peticao_arquivo_input_geral.change(process_file, [peticao_arquivo_input_geral], arquivo_preview_output_geral)
969
 
 
970
  btn_corrigir_geral.click( lambda: gr.update(value="⏳ Processando IA...", visible=True), None, processing_message_geral).then(
971
  corrigir_peticao_geral_ia,
972
  [caso_input_geral, tipo_peticao_input_geral, peticao_texto_input_geral, gr.State(None), modo_treinamento_geral],
 
974
  resumo_alteracoes_output_geral, criticas_output_geral, gr.Markdown(visible=False), visualizacao_detalhada_output_geral]
975
  ).then( lambda: gr.update(visible=False), None, processing_message_geral )
976
 
 
977
  btn_corrigir_arquivo_geral.click( lambda: gr.update(value="⏳ Processando IA...", visible=True), None, processing_message_geral).then(
978
  corrigir_peticao_geral_ia,
979
  [caso_input_geral, tipo_peticao_input_geral, gr.State(None), peticao_arquivo_input_geral, modo_treinamento_geral],
 
981
  resumo_alteracoes_output_geral, criticas_output_geral, gr.Markdown(visible=False), visualizacao_detalhada_output_geral]
982
  ).then( lambda: gr.update(visible=False), None, processing_message_geral )
983
 
 
984
  salvar_btn_geral.click( lambda: gr.update(value="⏳ Salvando...", visible=True), None, processing_message_geral).then(
985
  salvar_progresso, [caso_input_geral, peticao_texto_input_geral, tipo_peticao_input_geral],
986
  [gr.Textbox(visible=False), config_status_geral, progresso_download_file_geral]
 
991
  [tipo_peticao_input_geral, caso_input_geral, peticao_texto_input_geral, config_status_geral]
992
  ).then( lambda: gr.update(visible=False), None, processing_message_geral )
993
 
 
994
  oab_pdf_input.change(parse_oab_pdf, [oab_pdf_input], [oab_enunciado_input, oab_gabarito_input, oab_distribuicao_input, parse_status_output])
995
  btn_gerar_modelo_oab.click( lambda: gr.update(value="⏳ Gerando Modelo...", visible=True), None, processing_message_oab).then(
996
  gerar_peticao_modelo_oab, [oab_enunciado_input, oab_gabarito_input, oab_distribuicao_input], [resultado_modelo_oab_output]
 
999
  corrigir_pela_oab, [peticao_usuario_oab_input, oab_enunciado_input, oab_gabarito_input, oab_distribuicao_input], [resultado_correcao_oab_output]
1000
  ).then( lambda: gr.update(visible=False), None, processing_message_oab )
1001
 
1002
+ clear_btn.click( fn=lambda: [
1003
+ "Petição Inicial", False, None, None, None, None, "", "", "", "", "", "", "", None, None,
1004
+ None, "", None, None, None, "", "", "", ""
 
1005
  ], inputs=None, outputs=[
1006
  tipo_peticao_input_geral, modo_treinamento_geral, caso_input_geral, peticao_texto_input_geral, peticao_arquivo_input_geral,
1007
  arquivo_preview_output_geral, processing_message_geral, resultado_analise_output_geral,
 
1014
 
1015
  return demo
1016
 
 
1017
  if __name__ == "__main__":
1018
  app = interface_gradio()
1019
  app.launch()