GabrielGD commited on
Commit
a564bb4
·
verified ·
1 Parent(s): ae819b3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -400
app.py CHANGED
@@ -32,194 +32,31 @@ CONFIG = {
32
  "retry_delay": 5,
33
  "timeout": 60,
34
  "temperatura": 0.2,
35
- "max_tokens": 12288 # Aumentado para 8000 para garantir que a resposta completa caiba
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):
76
- try:
77
- if isinstance(file_path_or_obj, str):
78
- with open(file_path_or_obj, 'rb') as f:
79
- text = ""
80
- with pdfplumber_open(f) as pdf:
81
- for page in pdf.pages:
82
- text += (page.extract_text() or "") + "\n"
83
- if not text.strip():
84
- images = pdfplumber_open(f).pages
85
- for i, image in enumerate(images):
86
- try:
87
- img = image.to_image()
88
- preprocessed_img = preprocess_image_for_ocr(img)
89
- text += pytesseract.image_to_string(preprocessed_img) + "\n"
90
- except Exception as e:
91
- text += f"[Erro na página {i+1}: {str(e)}]\n"
92
- else:
93
- text = ""
94
- with pdfplumber_open(file_path_or_obj) as pdf:
95
- for page in pdf.pages:
96
- text += (page.extract_text() or "") + "\n"
97
- if not text.strip():
98
- images = pdfplumber_open(file_path_or_obj).pages
99
- for i, image in enumerate(images):
100
- try:
101
- img = image.to_image()
102
- preprocessed_img = preprocess_image_for_ocr(img)
103
- text += pytesseract.image_to_string(preprocessed_img) + "\n"
104
- except Exception as e:
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):
111
- try:
112
- if isinstance(file_path_or_obj, str):
113
- with open(file_path_or_obj, 'rb') as f:
114
- doc = docx.Document(f)
115
- text = ""
116
- for para in doc.paragraphs:
117
- text += para.text + "\n"
118
- for table in doc.tables:
119
- for row in table.rows:
120
- for cell in row.cells:
121
- text += cell.text + " "
122
- text += "\n"
123
- else:
124
- doc = docx.Document(file_path_or_obj)
125
- text = ""
126
- for para in doc.paragraphs:
127
- text += para.text + "\n"
128
- for table in doc.tables:
129
- for row in table.rows:
130
- for cell in row.cells:
131
- text += cell.text + " "
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):
138
- try:
139
- if isinstance(file_path_or_obj, str):
140
- with open(file_path_or_obj, 'rb') as f:
141
- content = f.read()
142
- encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
143
- for encoding in encodings:
144
- try:
145
- return content.decode(encoding)
146
- except UnicodeDecodeError:
147
- continue
148
- return "❌ Não foi possível decodificar o arquivo TXT."
149
- else:
150
- content = file_path_or_obj.read()
151
- encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
152
- for encoding in encodings:
153
- try:
154
- return content.decode(encoding)
155
- except UnicodeDecodeError:
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:
165
- if hasattr(file_obj, 'name'):
166
- file_path = file_obj.name
167
- filename = os.path.basename(file_path).lower()
168
- file_size = os.path.getsize(file_path)
169
- if file_size > 10 * 1024 * 1024:
170
- return "⚠️ O arquivo é muito grande. Máx. 10 MB."
171
- if filename.endswith('.pdf'):
172
- return extract_text_from_pdf(file_path)
173
- elif filename.endswith('.docx'):
174
- return extract_text_from_docx(file_path)
175
- elif filename.endswith('.txt'):
176
- return extract_text_from_txt(file_path)
177
- else:
178
- return "⚠️ Formato de arquivo não suportado. Use PDF, DOCX ou TXT."
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):
185
- tabela = [
186
- ("**Endereçamento**", ""),
187
- ("1. Correto endereçamento ao juízo competente", f"{enderecamento[0]:.2f} / 0,10"),
188
- ("2. Qualificação das partes (Art. 319, II, CPC)", f"{enderecamento[1]:.2f} / 0,20"),
189
- ("**Fundamentos**", ""),
190
- ("3. Exposição clara dos fatos (Art. 319, III, CPC)", f"{fundamentos[0]:.2f} / 0,20"),
191
- ("4. Fundamentação jurídica adequada", f"{fundamentos[1]:.2f} / 0,25"),
192
- ("5. Demonstração de ilícito e dano (Art. 186, CC)", f"{fundamentos[2]:.2f} / 0,25"),
193
- ("**Pedidos**", ""),
194
- ("6. Pedidos claros e específicos (Art. 319, IV, CPC)", f"{pedidos[0]:.2f} / 0,30"),
195
- ("7. Valor da causa indicado (Art. 319, V, CPC)", f"{pedidos[1]:.2f} / 0,10"),
196
- ("8. Provas pretendidas (Art. 319, VI, CPC)", f"{pedidos[2]:.2f} / 0,10"),
197
- ("9. Opção sobre audiência (Art. 319, VII, CPC)", f"{pedidos[3]:.2f} / 0,10"),
198
- ("10. Pedido de citação do réu", f"{pedidos[4]:.2f} / 0,10"),
199
- ("11. Juntada da guia de custas", f"{pedidos[5]:.2f} / 0,10"),
200
- ("**Fechamento**", ""),
201
- ("12. Fechamento formal adequado", f"{fechamento[0]:.2f} / 0,10"),
202
- ]
203
- nota_final = sum(enderecamento) + sum(fundamentos) + sum(pedidos) + sum(fechamento)
204
- tabela_formatada = "\n".join([f"| {linha[0].ljust(60)} | {linha[1].rjust(10)} |" for linha in tabela])
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"""
224
  ## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB com Análise Especializada)
225
  **Caso Fornecido:**
@@ -282,9 +119,30 @@ def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
282
  **Fechamento** | | |
283
  12. Fechamento formal adequado (0,10) | / 0,10 | [Explique erro ou acerto] |
284
  **Nota Final:** Calcule a soma dos pontos, aplicando penalizações cumulativas conforme os critérios fundamentais; justifique qualquer desconto adicional.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
 
 
 
 
 
 
 
286
  ### Petição Corrigida
287
- Certifique-se de incluir esta seção exatamente como '### Petição Corrigida'. 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:
288
  1. Endereçamento correto (com foro justificado por CPC ou CDC).
289
  2. Qualificação completa (Art. 319, II, CPC; use "XXX" para dados ausentes).
290
  3. Fatos claros e fiéis ao caso (Art. 319, III, CPC).
@@ -297,48 +155,11 @@ def estruturar_prompt_correcao(caso, peticao, modo_treinamento=False):
297
  10. Juntada da guia de custas com redação precisa.
298
  11. Fechamento formal com "Local e data.\nADVOGADO.\nOAB." sem identificação específica.
299
  """
300
- if modo_treinamento:
301
- base_prompt += f"""
302
- **Modo de Treinamento Interativo Avançado:**
303
- - Para cada item com pontuação inferior ao máximo, forneça:
304
- a) Explicação detalhada (mínimo 3 frases) com base legal (ex.: Art. 319, CPC) e análise do impacto processual.
305
- b) Sugestão de redação corrigida baseada no caso, com linguagem técnica exemplar.
306
- 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.’").
307
- d) Dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)}) ou personalizada, com enfoque pedagógico.
308
- 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").
309
- - Verifique a coerência entre os fatos do caso e a petição, listando todas as inconsistências com trechos específicos.
310
- - Sugira estratégias adicionais baseadas no caso (ex.: cumulação de pedidos, Art. 327, CPC; ou danos morais com Súmula 387/STJ).
311
- - 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).
312
- - Avalie a viabilidade processual da petição (ex.: risco de indeferimento por inépcia, Art. 330, CPC).
313
- """
314
- return base_prompt
315
-
316
- def estruturar_prompt_geracao_caso():
317
- return """
318
- Crie um caso fictício detalhado para uma petição inicial cível, similar ao exemplo:
319
- "Ana, modelo profissional, residente em Manaus, viajou para São Paulo, para o casamento de sua filha. Para lavar, pintar seus cabelos e realizar um penteado para o casamento, Ana procurou os serviços de João Macedo, cabeleireiro e dono do salão de beleza 'Hair', sediado na cidade de São Paulo, que lhe cobrou R$500,00 (quinhentos reais) pela prestação do serviço. Após lavar os cabelos de Ana, João aplicou-lhe uma tintura da marca francesa ABC, importada pela empresa Brasil Connection Ltda, sediada na cidade de Curitiba (PR). Meia hora após a aplicação da tintura, Ana sofreu uma reação alérgica, que demandou atendimento médico-hospitalar, no valor de R$1.000,00 (um mil reais), bem como dois dias de absoluto repouso que impossibilitou sua presença no casamento de sua filha. Além disso, perdeu grande parte de seu cabelo, tendo permanecido com manchas em seu rosto por dois meses, perdendo um ensaio fotográfico, para o qual já havia sido contratada, pelo valor de R$50.000,00 (cinquenta mil reais). Posteriormente constatou-se que a tintura utilizada continha substâncias químicas extremamente perigosas à vida e à saúde das pessoas e a fabricante ABC já havia sido condenada pela justiça francesa a encerrar a fabricação e comercialização do produto. Indignada com os danos sofridos, Ana procura um advogado para pleitear o devido ressarcimento."
320
- O caso deve:
321
- 1. Conter uma situação clara de violação de direitos que justifique uma ação cível
322
- 2. Incluir valores específicos de danos materiais e/ou morais
323
- 3. Mencionar nomes completos, cidades e informações suficientes para identificar as partes
324
- 4. Estabelecer relação entre as partes (contratual, extracontratual, consumo, etc.)
325
- 5. Ter complexidade adequada para uma petição inicial de nível OAB
326
- 6. Ter entre 15-25 linhas de texto
327
- 7. Focar em um dos seguintes temas (escolha APENAS UM aleatoriamente):
328
- - Responsabilidade civil por acidente de consumo
329
- - Inadimplemento contratual com danos
330
- - Responsabilidade civil por dano ambiental
331
- - Violação de direitos da personalidade
332
- - Relação de consumo com vício de produto/serviço
333
- - Reparação por danos patrimoniais
334
- Termine com: "Situação problema: Como advogado(a) de [NOME DO CLIENTE], promova a demanda cabível."
335
- """
336
 
337
- def gerar_caso_ficticio():
338
- prompt = estruturar_prompt_geracao_caso()
339
  payload = {
340
  "contents": [{"parts": [{"text": prompt}]}],
341
- "generationConfig": {"temperature": 0.7, "maxOutputTokens": CONFIG["max_tokens"], "topP": 0.95}
342
  }
343
  headers = {"Content-Type": "application/json"}
344
  for tentativa in range(CONFIG["max_retry_attempts"]):
@@ -358,8 +179,7 @@ def gerar_caso_ficticio():
358
  time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
359
  continue
360
  return "❌ A API retornou uma resposta vazia. Tente novamente."
361
- caso_gerado = response_data["candidates"][0]["content"]["parts"][0]["text"].strip()
362
- return f"## 📝 Caso Fictício Gerado\n\n{caso_gerado}"
363
  except requests.Timeout:
364
  if tentativa < CONFIG["max_retry_attempts"] - 1:
365
  time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
@@ -392,66 +212,34 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
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 = {
397
- "contents": [{"parts": [{"text": prompt}]}],
398
- "generationConfig": {"temperature": CONFIG["temperatura"], "maxOutputTokens": CONFIG["max_tokens"], "topP": 0.95}
399
- }
400
- headers = {"Content-Type": "application/json"}
401
- for tentativa in range(CONFIG["max_retry_attempts"]):
402
- try:
403
- with rate_limiter:
404
- response = requests.post(GEMINI_API_URL, headers=headers, data=json.dumps(payload), timeout=CONFIG["timeout"])
405
- if response.status_code != 200:
406
- response_data = response.json()
407
- error_msg = response_data.get("error", {}).get("message", "Erro desconhecido")
408
- if tentativa < CONFIG["max_retry_attempts"] - 1:
409
- time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
410
- continue
411
- return aviso_validacao + f"❌ Erro da API (Tentativa {tentativa+1}/{CONFIG['max_retry_attempts']}): {error_msg}"
412
- response_data = response.json()
413
- if "candidates" not in response_data or not response_data["candidates"]:
414
- if tentativa < CONFIG["max_retry_attempts"] - 1:
415
- time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
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
- # Debug: Inspeciona a resposta se a seção esperada não estiver presente
420
- if "### Petição Corrigida" not in correcao_completa:
421
- print("Debug: Resposta da API não contém '### Petição Corrigida'. Resposta completa (primeiros 1000 caracteres):")
422
- print(correcao_completa[:1000])
423
- if "### Petição Corrigida" in correcao_completa:
424
- analise_fim = correcao_completa.index("### Petição Corrigida")
425
- analise = correcao_completa[:analise_fim].strip()
426
- else:
427
- analise = correcao_completa.strip()
428
- correcao_formatada = formatar_correcao(analise)
429
- HISTORICO.append({
430
- "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
431
- "caso": caso,
432
- "peticao": peticao_conteudo,
433
- "correcao_completa": correcao_completa
434
- })
435
- if len(HISTORICO) > 10: # Limite de histórico
436
- HISTORICO.pop(0)
437
- CORRECAO_CACHE[cache_key] = correcao_formatada
438
- return aviso_validacao + correcao_formatada
439
- except requests.Timeout:
440
- if tentativa < CONFIG["max_retry_attempts"] - 1:
441
- time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
442
- continue
443
- return aviso_validacao + "⏱️ A resposta demorou muito. Tente novamente mais tarde."
444
- except requests.RequestException as e:
445
- if tentativa < CONFIG["max_retry_attempts"] - 1:
446
- time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
447
- continue
448
- return aviso_validacao + f"❌ Erro ao chamar a API: {e}"
449
- except Exception as e:
450
- return aviso_validacao + f"❌ Erro inesperado: {str(e)}"
451
 
452
  def formatar_correcao(texto):
453
  linhas = texto.split('\n')
454
- texto_formatado = "" # Corrigido para "texto_formatado" consistentemente
455
  for linha in linhas:
456
  if "Análise Geral" in linha:
457
  texto_formatado += f"### ℹ️ {linha.strip()}\n\n"
@@ -480,7 +268,6 @@ def formatar_correcao(texto):
480
 
481
  def extrair_peticao_corrigida(correcao_completa):
482
  try:
483
- # Tenta encontrar a seção exata
484
  if "### Petição Corrigida" in correcao_completa:
485
  inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
486
  peticao_texto = correcao_completa[inicio:].strip()
@@ -488,11 +275,10 @@ def extrair_peticao_corrigida(correcao_completa):
488
  peticao_texto = peticao_texto[:fim].strip()
489
  return peticao_texto
490
  else:
491
- # Fallback: tenta identificar o início da petição corrigida por padrões comuns
492
  linhas = correcao_completa.split('\n')
493
  inicio_peticao = -1
494
  for i, linha in enumerate(linhas):
495
- if "EXCELENTÍSSIMO" in linha.upper() and i > len(linhas) // 2: # Assume que a petição corrigida vem após a análise
496
  inicio_peticao = i
497
  break
498
  if inicio_peticao != -1:
@@ -502,127 +288,7 @@ def extrair_peticao_corrigida(correcao_completa):
502
  except Exception as e:
503
  return f"❌ Erro ao extrair petição corrigida: {str(e)}"
504
 
505
- def fornecer_dica_contextual(texto):
506
- dicas = [f"💡 {chave}: {dica}" for chave, dica in DICAS_OAB_MELHORADO.items() if chave.lower() in texto.lower()]
507
- return "\n".join(dicas) if dicas else "💡 Escreva mais para receber dicas."
508
-
509
- def calcular_progresso():
510
- if not HISTORICO:
511
- return "📊 Nenhum progresso registrado."
512
- notas = []
513
- for entry in HISTORICO:
514
- for line in entry["correcao_completa"].split("\n"):
515
- if "Nota Final" in line:
516
- try:
517
- nota = float(line.split()[-1].split("/")[0].replace(",", "."))
518
- notas.append(nota)
519
- except:
520
- continue
521
- media = sum(notas) / len(notas) if notas else 0
522
- tendencia = "⬆️ Melhorando" if len(notas) > 1 and notas[-1] > notas[-2] else "⬇️ Estável ou piorando"
523
- return f"📊 Progresso: Média {media:.1f}/10 | Tentativas: {len(notas)} | Tendência: {tendencia}"
524
-
525
- def comparar_peticao_melhorada(caso, peticao_texto, correcao):
526
- if not caso:
527
- return "⚠️ Forneça um caso antes de comparar.", "", "", "", ""
528
- peticao = peticao_texto if peticao_texto else (HISTORICO[-1]["peticao"] if HISTORICO else "")
529
- if not peticao:
530
- return "⚠️ Forneça uma petição para comparar ou corrija uma antes.", "", "", "", ""
531
- if not correcao:
532
- return "⚠️ Corrija a petição antes de comparar.", "", "", "", ""
533
- ultima_correcao = HISTORICO[-1]["correcao_completa"] if HISTORICO else ""
534
- if not ultima_correcao:
535
- return "⚠️ Nenhuma correção disponível no histórico.", "", "", "", ""
536
-
537
- peticao_corrigida = extrair_peticao_corrigida(ultima_correcao)
538
- if "⚠️" in peticao_corrigida or "❌" in peticao_corrigida:
539
- return peticao_corrigida, "", "", "", ""
540
-
541
- orig_lines = peticao.split('\n')
542
- corr_lines = peticao_corrigida.split('\n')
543
- start_orig = next((i for i, line in enumerate(orig_lines) if "AÇÃO" in line.upper() or "EXCELENTÍSSIMO" in line.upper()), 0)
544
- start_corr = next((i for i, line in enumerate(corr_lines) if "AÇÃO" in line.upper() or "EXCELENTÍSSIMO" in line.upper()), 0)
545
-
546
- orig_body = "\n".join(orig_lines[start_orig:]).strip()
547
- corr_body = "\n".join(corr_lines[start_corr:]).strip()
548
-
549
- diff = difflib.ndiff(orig_body.splitlines(), corr_body.splitlines())
550
-
551
- resultado_diff = []
552
- criticas = []
553
- for linha in diff:
554
- if linha.startswith(' '):
555
- texto = linha[2:]
556
- resultado_diff.append(f"<div class='linha-igual'>{escape(texto)}</div>")
557
- elif linha.startswith('- '):
558
- texto = linha[2:]
559
- if any(kw in texto.lower() for kw in ["invenção", "custas", "art. 319"]):
560
- criticas.append(f"🔴 REMOVIDO: {texto} (inconsistente ou incorreto)")
561
- resultado_diff.append(f"<div class='linha-removida'><del>{escape(texto)}</del></div>")
562
- elif linha.startswith('+ '):
563
- texto = linha[2:]
564
- if any(kw in texto.lower() for kw in ["custas", "art. 319"]):
565
- criticas.append(f"🟢 ADICIONADO: {texto} (essencial para conformidade)")
566
- resultado_diff.append(f"<div class='linha-adicionada'><ins>{escape(texto)}</ins></div>")
567
- elif linha.startswith('? '):
568
- continue
569
-
570
- total_removidas = len([linha for linha in resultado_diff if "linha-removida" in linha])
571
- total_adicionadas = len([linha for linha in resultado_diff if "linha-adicionada" in linha])
572
-
573
- resumo_estatistico = f"""
574
- Linhas Removidas: {total_removidas}
575
- Linhas Adicionadas: {total_adicionadas}
576
- Diferença Líquida: {total_adicionadas - total_removidas} linhas
577
- """
578
-
579
- criticas_texto = "\n".join(criticas) if criticas else "Nenhuma mudança crítica detectada."
580
-
581
- visualizacao_detalhada = f"""
582
- <style>
583
- .linha-igual {{ color: #000000; margin: 2px 0; }}
584
- .linha-removida {{ color: #ffffff; background-color: #b30000; margin: 2px 0; padding: 2px; }}
585
- .linha-adicionada {{ color: #ffffff; background-color: #006600; margin: 2px 0; padding: 2px; }}
586
- </style>
587
- {"".join(resultado_diff)}
588
- """
589
- return peticao, peticao_corrigida, resumo_estatistico, criticas_texto, visualizacao_detalhada
590
-
591
- def exportar_diferencas_html(caso, peticao_texto, correcao):
592
- _, _, resumo_estatistico, criticas_texto, visualizacao_detalhada = comparar_peticao_melhorada(caso, peticao_texto, correcao)
593
- html_content = f"""
594
- <html>
595
- <head>
596
- <title>Diferenças na Petição</title>
597
- <style>
598
- body {{ font-family: Arial, sans-serif; margin: 20px; }}
599
- .linha-igual {{ color: #000000; margin: 2px 0; }}
600
- .linha-removida {{ color: #ffffff; background-color: #b30000; margin: 2px 0; padding: 2px; }}
601
- .linha-adicionada {{ color: #ffffff; background-color: #006600; margin: 2px 0; padding: 2px; }}
602
- h2 {{ color: #333; }}
603
- pre {{ white-space: pre-wrap; }}
604
- </style>
605
- </head>
606
- <body>
607
- <h2>Estatísticas</h2>
608
- <pre>{resumo_estatistico}</pre>
609
- <h2>Mudanças Críticas</h2>
610
- <pre>{criticas_texto}</pre>
611
- <h2>Alterações Detalhadas</h2>
612
- {visualizacao_detalhada}
613
- </body>
614
- </html>
615
- """
616
- try:
617
- with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
618
- temp_path = tmp.name
619
- HTML(string=html_content).write_pdf(temp_path)
620
- return temp_path
621
- except Exception as e:
622
- return f"❌ Erro ao gerar PDF: {str(e)}"
623
- finally:
624
- if 'temp_path' in locals() and os.path.exists(temp_path):
625
- os.unlink(temp_path)
626
 
627
  def interface_gradio():
628
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
32
  "retry_delay": 5,
33
  "timeout": 60,
34
  "temperatura": 0.2,
35
+ "max_tokens": 8000 # Mantido em 8000, mas dividido em duas chamadas
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). Exemplo: 'Excelentíssimo Senhor Juiz de Direito da Vara Cível da Comarca de [Cidade do Autor]'.",
42
+ "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.",
43
+ "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]'.",
44
+ "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.",
45
+ "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.",
46
+ "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'.",
47
+ "Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula. Dica: Evite erros de pontuação que podem comprometer a formalidade do documento.",
48
+ "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'.",
49
+ "Provas": "Especifique as provas que pretende produzir. Dica: Inclua testemunhas, documentos e perícias, se necessário, para comprovar os fatos alegados.",
50
+ "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."
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
  HISTORICO = []
54
  CORRECAO_CACHE = {}
55
 
56
+ # Funções auxiliares (processamento de arquivos) permanecem iguais, omitidas aqui por brevidade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
59
+ """Estrutura o prompt apenas para a análise da petição."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  base_prompt = f"""
61
  ## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB com Análise Especializada)
62
  **Caso Fornecido:**
 
119
  **Fechamento** | | |
120
  12. Fechamento formal adequado (0,10) | / 0,10 | [Explique erro ou acerto] |
121
  **Nota Final:** Calcule a soma dos pontos, aplicando penalizações cumulativas conforme os critérios fundamentais; justifique qualquer desconto adicional.
122
+ """
123
+ if modo_treinamento:
124
+ base_prompt += f"""
125
+ **Modo de Treinamento Interativo Avançado:**
126
+ - Para cada item com pontuação inferior ao máximo, forneça:
127
+ a) Explicação detalhada (máximo 2 frases curtas) com base legal (ex.: Art. 319, CPC) e impacto processual.
128
+ b) Sugestão de redação corrigida baseada no caso, com linguagem técnica.
129
+ c) Exemplo prático em outro contexto (máximo 1 frase).
130
+ d) Dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)}) ou personalizada, curta.
131
+ e) Estratégia jurídica avançada (máximo 1 frase).
132
+ - Verifique a coerência entre os fatos do caso e a petição, listando inconsistências com trechos específicos.
133
+ - Sugira estratégias adicionais baseadas no caso (ex.: cumulação de pedidos, Art. 327, CPC).
134
+ """
135
+ return base_prompt
136
 
137
+ def estruturar_prompt_peticao_corrigida(caso, peticao):
138
+ """Estrutura o prompt apenas para a petição corrigida."""
139
+ return f"""
140
+ **Caso Fornecido:**
141
+ {caso}
142
+ **Petição do Aluno:**
143
+ {peticao}
144
  ### Petição Corrigida
145
+ Certifique-se de incluir esta seção exatamente como '### Petição Corrigida'. 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 com base nos seguintes critérios:
146
  1. Endereçamento correto (com foro justificado por CPC ou CDC).
147
  2. Qualificação completa (Art. 319, II, CPC; use "XXX" para dados ausentes).
148
  3. Fatos claros e fiéis ao caso (Art. 319, III, CPC).
 
155
  10. Juntada da guia de custas com redação precisa.
156
  11. Fechamento formal com "Local e data.\nADVOGADO.\nOAB." sem identificação específica.
157
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ def chamar_api(prompt):
 
160
  payload = {
161
  "contents": [{"parts": [{"text": prompt}]}],
162
+ "generationConfig": {"temperature": CONFIG["temperatura"], "maxOutputTokens": CONFIG["max_tokens"], "topP": 0.95}
163
  }
164
  headers = {"Content-Type": "application/json"}
165
  for tentativa in range(CONFIG["max_retry_attempts"]):
 
179
  time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
180
  continue
181
  return "❌ A API retornou uma resposta vazia. Tente novamente."
182
+ return response_data["candidates"][0]["content"]["parts"][0]["text"].strip()
 
183
  except requests.Timeout:
184
  if tentativa < CONFIG["max_retry_attempts"] - 1:
185
  time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
 
212
  if cache_key in CORRECAO_CACHE:
213
  return aviso_validacao + CORRECAO_CACHE[cache_key]
214
 
215
+ # Primeira chamada: análise
216
+ prompt_analise = estruturar_prompt_analise(caso, peticao_conteudo, modo_treinamento)
217
+ analise = chamar_api(prompt_analise)
218
+ if "❌" in analise or "⏱️" in analise:
219
+ return aviso_validacao + analise
220
+ analise_formatada = formatar_correcao(analise)
221
+
222
+ # Segunda chamada: petição corrigida
223
+ prompt_peticao = estruturar_prompt_peticao_corrigida(caso, peticao_conteudo)
224
+ peticao_corrigida = chamar_api(prompt_peticao)
225
+ if "❌" in peticao_corrigida or "⏱️" in peticao_corrigida:
226
+ return aviso_validacao + analise_formatada + "\n\n" + peticao_corrigida
227
+
228
+ correcao_completa = analise + "\n\n" + peticao_corrigida
229
+ HISTORICO.append({
230
+ "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
231
+ "caso": caso,
232
+ "peticao": peticao_conteudo,
233
+ "correcao_completa": correcao_completa
234
+ })
235
+ if len(HISTORICO) > 10:
236
+ HISTORICO.pop(0)
237
+ CORRECAO_CACHE[cache_key] = analise_formatada
238
+ return aviso_validacao + analise_formatada
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
  def formatar_correcao(texto):
241
  linhas = texto.split('\n')
242
+ texto_formatado = ""
243
  for linha in linhas:
244
  if "Análise Geral" in linha:
245
  texto_formatado += f"### ℹ️ {linha.strip()}\n\n"
 
268
 
269
  def extrair_peticao_corrigida(correcao_completa):
270
  try:
 
271
  if "### Petição Corrigida" in correcao_completa:
272
  inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
273
  peticao_texto = correcao_completa[inicio:].strip()
 
275
  peticao_texto = peticao_texto[:fim].strip()
276
  return peticao_texto
277
  else:
 
278
  linhas = correcao_completa.split('\n')
279
  inicio_peticao = -1
280
  for i, linha in enumerate(linhas):
281
+ if "EXCELENTÍSSIMO" in linha.upper() and i > len(linhas) // 2:
282
  inicio_peticao = i
283
  break
284
  if inicio_peticao != -1:
 
288
  except Exception as e:
289
  return f"❌ Erro ao extrair petição corrigida: {str(e)}"
290
 
291
+ # Outras funções (comparar_peticao_melhorada, interface_gradio, etc.) permanecem iguais, omitidas por brevidade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
  def interface_gradio():
294
  with gr.Blocks(theme=gr.themes.Soft()) as demo: