GabrielGD commited on
Commit
b0307cc
·
verified ·
1 Parent(s): a89d9fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +308 -76
app.py CHANGED
@@ -17,7 +17,7 @@ from datetime import datetime
17
  from ratelimiter import RateLimiter
18
  import difflib
19
  from html import escape
20
- from weasyprint import HTML
21
 
22
  load_dotenv()
23
 
@@ -32,7 +32,7 @@ CONFIG = {
32
  "retry_delay": 5,
33
  "timeout": 60,
34
  "temperatura": 0.2,
35
- "max_tokens": 12288
36
  }
37
 
38
  rate_limiter = RateLimiter(max_calls=60, period=60)
@@ -53,6 +53,124 @@ DICAS_OAB_MELHORADO = {
53
  HISTORICO = []
54
  CORRECAO_CACHE = {}
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
57
  """Estrutura o prompt apenas para a análise da petição."""
58
  base_prompt = f"""
@@ -87,7 +205,7 @@ def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
87
  5. Não penalize por informações ausentes no caso original, mas alerte se a omissão compromete a petição.
88
  6. A frase "Nestes termos, pede deferimento" com vírgula é incorreta; corrija para "Nestes termos pede deferimento" e penalize com -0,2 ponto, destacando o erro formal.
89
  7. A petição deve finalizar com "Local e data.\nADVOGADO.\nOAB." sem identificação do aluno (nome, número ou estado na OAB); se houver identificação específica (ex.: "OAB/SP" ou "12345"), zere a nota (0,0) e explique o motivo.
90
-
91
  **Instruções Especiais para Análise Avançada:**
92
  - **Competência**: Avalie o foro indicado com base no Art. 46 do CPC (domicílio do réu) ou Art. 101, I do CDC (domicílio do autor em relações de consumo); penalize erros com -0,10 ponto e justifique com jurisprudência (ex.: STJ, REsp 1.234.567).
93
  - **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.
@@ -133,61 +251,27 @@ def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
133
  return base_prompt
134
 
135
  def estruturar_prompt_peticao_corrigida(caso, peticao):
136
- """Estrutura o prompt para a petição corrigida com instruções mais enfáticas."""
137
  return f"""
138
  **Caso Fornecido:**
139
  {caso}
140
  **Petição do Aluno:**
141
  {peticao}
142
  ### Petição Corrigida
143
- Gere a petição corrigida abaixo desta seção, intitulada exatamente como '### Petição Corrigida'. Você DEVE seguir TODOS os critérios abaixo rigorosamente, sem exceção, aplicando cada correção necessária com base no caso e na petição do aluno. Não ignore nenhum item, pois todos são obrigatórios:
144
-
145
- 1. **Endereçamento correto**: Corrija o foro com base no Art. 46 do CPC (domicílio do réu) ou Art. 101, I do CDC (domicílio do autor em relações de consumo, se aplicável). Exemplo: "Excelentíssimo Senhor Juiz de Direito da Vara Cível da Comarca de [Cidade correta]".
146
- 2. **Qualificação completa**: Inclua nome, estado civil, endereço (use "XXX" para dados ausentes), conforme Art. 319, II, CPC. Não invente dados específicos.
147
- 3. **Fatos claros e fiéis ao caso**: Narre os fatos do caso em ordem cronológica, sem adicionar informações não fornecidas, conforme Art. 319, III, CPC.
148
- 4. **Fundamentação jurídica robusta**: Cite pelo menos um artigo específico do CPC, CDC ou CC (ex.: Art. 186, CC para responsabilidade civil) e, se aplicável, súmulas (ex.: Súmula 387/STJ para danos morais).
149
- 5. **Pedidos específicos e quantificados**: Detalhe todos os pedidos com clareza e valores exatos (ex.: "R$ 50.000,00 por danos morais"), conforme Art. 319, IV, CPC.
150
- 6. **Valor da causa realista**: Inclua o valor total do benefício econômico pretendido, conforme Art. 319, V, CPC (ex.: soma de danos + valor do contrato, se aplicável).
151
- 7. **Provas detalhadas**: Especifique provas como "prova documental e testemunhal", conforme Art. 319, VI, CPC.
152
- 8. **Opção por audiência explícita**: Inclua "Requer a designação de audiência de conciliação" ou "Dispensa a audiência de conciliação", conforme Art. 319, VII, CPC.
153
- 9. **Pedido de citação formal**: Use "via postal, nos termos do Art. 246, I, CPC" ou outro meio legal aplicável.
154
- 10. **Juntada da guia de custas**: Adicione "Requer a juntada da guia de custas devidamente recolhida" em todas as petições corrigidas, sem exceção.
155
- 11. **Fechamento formal**: Finalize EXATAMENTE com "Local e data.\nADVOGADO.\nOAB." sem identificação específica (ex.: "OAB/SP" ou "12345" resultam em nota zero). A frase "Nestes termos, pede deferimento" com vírgula DEVE SER corrigida para "Nestes termos pede deferimento" (sem vírgula), conforme exigência formal da OAB.
156
-
157
- **Instruções Obrigatórias**:
158
- - Não omita nenhum dos 11 itens acima. Cada um é essencial e deve ser verificado e corrigido se necessário.
159
- - Siga estritamente as regras formais da OAB, especialmente a correção da vírgula em "Nestes termos pede deferimento".
160
- - Baseie-se exclusivamente no caso fornecido e na petição do aluno, sem inventar fatos ou dados.
161
- - Gere a petição completa, incluindo todas as seções (endereçamento, qualificação, fatos, direito, pedidos, valor da causa, provas, fechamento).
162
  """
163
 
164
- def pos_corrigir_peticao(peticao_corrigida):
165
- """Pós-processamento para garantir que todas as regras sejam aplicadas."""
166
- # Garantir a correção da vírgula
167
- if "Nestes termos, pede deferimento" in peticao_corrigida:
168
- peticao_corrigida = peticao_corrigida.replace("Nestes termos, pede deferimento", "Nestes termos pede deferimento")
169
-
170
- # Garantir o pedido de juntada da guia de custas
171
- if "juntada da guia de custas" not in peticao_corrigida.lower():
172
- peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a juntada da guia de custas devidamente recolhida.\nNestes termos pede deferimento")
173
-
174
- # Garantir o fechamento formal correto
175
- if not peticao_corrigida.strip().endswith("Local e data.\nADVOGADO.\nOAB."):
176
- linhas = peticao_corrigida.split('\n')
177
- while linhas and not linhas[-1].strip(): # Remove linhas em branco no final
178
- linhas.pop()
179
- if len(linhas) >= 3:
180
- linhas[-3:] = ["Local e data.", "ADVOGADO.", "OAB."]
181
- else:
182
- linhas.extend(["Local e data.", "ADVOGADO.", "OAB."])
183
- peticao_corrigida = "\n".join(linhas)
184
-
185
- # Garantir opção por audiência explícita
186
- if "audiência de conciliação" not in peticao_corrigida.lower():
187
- peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a designação de audiência de conciliação, nos termos do Art. 319, VII, CPC.\nNestes termos pede deferimento")
188
-
189
- return peticao_corrigida
190
-
191
  def chamar_api(prompt):
192
  payload = {
193
  "contents": [{"parts": [{"text": prompt}]}],
@@ -257,10 +341,6 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
257
  if "❌" in peticao_corrigida or "⏱️" in peticao_corrigida:
258
  return aviso_validacao + analise_formatada + "\n\n" + peticao_corrigida
259
 
260
- # Pós-correção para garantir conformidade
261
- peticao_corrigida = pos_corrigir_peticao(peticao_corrigida)
262
- print("Debug: Petição corrigida após pós-processamento:\n", peticao_corrigida[:1000]) # Debug para verificar a saída
263
-
264
  correcao_completa = analise + "\n\n" + peticao_corrigida
265
  HISTORICO.append({
266
  "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
@@ -273,17 +353,178 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
273
  CORRECAO_CACHE[cache_key] = analise_formatada
274
  return aviso_validacao + analise_formatada
275
 
276
- def fornecer_dica_contextual(peticao_texto):
277
- # Example implementation of contextual tips
278
- # This function should return a string or any other type that can be displayed in the dica_contextual output
279
- if "EXCELENTÍSSIMO" not in peticao_texto.upper():
280
- return "Dica: Verifique o endereçamento da petição."
281
- elif "juntada da guia de custas" not in peticao_texto.lower():
282
- return "Dica: Não se esqueça de incluir o pedido de juntada da guia de custas."
283
- elif "Nestes termos, pede deferimento" in peticao_texto:
284
- return "Dica: Corrija a vírgula em 'Nestes termos pede deferimento'."
285
- else:
286
- return "Dica: Revise a estrutura geral da petição."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
  def interface_gradio():
289
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
@@ -447,7 +688,7 @@ def gerar_caso_ficticio():
447
  def estruturar_prompt_geracao_caso():
448
  return """
449
  Crie um caso fictício detalhado para uma petição inicial cível, similar ao exemplo:
450
- "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."
451
  O caso deve:
452
  1. Conter uma situação clara de violação de direitos que justifique uma ação cível
453
  2. Incluir valores específicos de danos materiais e/ou morais
@@ -471,8 +712,6 @@ def validar_peticao_basica(peticao):
471
  problemas.append("⚠️ Endereçamento ausente ou incompleto (Art. 319, I, CPC).")
472
  if "Art. 319" not in peticao and "CPC" not in peticao:
473
  problemas.append("⚠️ Fundamentação jurídica não menciona o CPC (Art. 319).")
474
- if "Dá-se à causa o valor" not in peticao:
475
- problemas.append("⚠️ Fundamentação jurídica não menciona o CPC (Art. 319).")
476
  if "Dá-se à causa o valor" not in peticao:
477
  problemas.append("⚠️ Valor da causa não especificado (Art. 319, V, CPC).")
478
  if "juntada da guia de custas" not in peticao.lower():
@@ -481,12 +720,5 @@ def validar_peticao_basica(peticao):
481
  problemas.append("⚠️ Fechamento formal ausente.")
482
  return problemas
483
 
484
- def calcular_progresso():
485
- total_correcoes = len(HISTORICO)
486
- if total_correcoes == 0:
487
- return "Nenhuma correção realizada até o momento."
488
- else:
489
- return f"Total de correções realizadas: {total_correcoes}"
490
-
491
  app = interface_gradio()
492
- app.launch()
 
17
  from ratelimiter import RateLimiter
18
  import difflib
19
  from html import escape
20
+ from weasyprint import HTML # Para gerar PDF com cores
21
 
22
  load_dotenv()
23
 
 
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)
 
53
  HISTORICO = []
54
  CORRECAO_CACHE = {}
55
 
56
+ def preprocess_image_for_ocr(img):
57
+ try:
58
+ gray = img.convert('L')
59
+ np_img = np.array(gray)
60
+ _, thresh = cv2.threshold(np_img, 150, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
61
+ return Image.fromarray(thresh)
62
+ except Exception as e:
63
+ return f"❌ Erro no pré-processamento: {str(e)}"
64
+
65
+ def extract_text_from_pdf(file_path_or_obj):
66
+ try:
67
+ if isinstance(file_path_or_obj, str):
68
+ with open(file_path_or_obj, 'rb') as f:
69
+ text = ""
70
+ with pdfplumber_open(f) as pdf:
71
+ for page in pdf.pages:
72
+ text += (page.extract_text() or "") + "\n"
73
+ if not text.strip():
74
+ images = pdfplumber_open(f).pages
75
+ for i, image in enumerate(images):
76
+ try:
77
+ img = image.to_image()
78
+ preprocessed_img = preprocess_image_for_ocr(img)
79
+ text += pytesseract.image_to_string(preprocessed_img) + "\n"
80
+ except Exception as e:
81
+ text += f"[Erro na página {i+1}: {str(e)}]\n"
82
+ else:
83
+ text = ""
84
+ with pdfplumber_open(file_path_or_obj) as pdf:
85
+ for page in pdf.pages:
86
+ text += (page.extract_text() or "") + "\n"
87
+ if not text.strip():
88
+ images = pdfplumber_open(file_path_or_obj).pages
89
+ for i, image in enumerate(images):
90
+ try:
91
+ img = image.to_image()
92
+ preprocessed_img = preprocess_image_for_ocr(img)
93
+ text += pytesseract.image_to_string(preprocessed_img) + "\n"
94
+ except Exception as e:
95
+ text += f"[Erro na página {i+1}: {str(e)}]\n"
96
+ return text if text.strip() else "⚠️ Não foi possível extrair texto do PDF."
97
+ except Exception as e:
98
+ return f"❌ Erro ao processar o PDF: {str(e)}"
99
+
100
+ def extract_text_from_docx(file_path_or_obj):
101
+ try:
102
+ if isinstance(file_path_or_obj, str):
103
+ with open(file_path_or_obj, 'rb') as f:
104
+ doc = docx.Document(f)
105
+ text = ""
106
+ for para in doc.paragraphs:
107
+ text += para.text + "\n"
108
+ for table in doc.tables:
109
+ for row in table.rows:
110
+ for cell in row.cells:
111
+ text += cell.text + " "
112
+ text += "\n"
113
+ else:
114
+ doc = docx.Document(file_path_or_obj)
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
+ return text if text.strip() else "⚠️ Não foi possível extrair texto do DOCX."
124
+ except Exception as e:
125
+ return f"❌ Erro ao processar o DOCX: {str(e)}"
126
+
127
+ def extract_text_from_txt(file_path_or_obj):
128
+ try:
129
+ if isinstance(file_path_or_obj, str):
130
+ with open(file_path_or_obj, 'rb') as f:
131
+ content = f.read()
132
+ encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
133
+ for encoding in encodings:
134
+ try:
135
+ return content.decode(encoding)
136
+ except UnicodeDecodeError:
137
+ continue
138
+ return "❌ Não foi possível decodificar o arquivo TXT."
139
+ else:
140
+ content = file_path_or_obj.read()
141
+ encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
142
+ for encoding in encodings:
143
+ try:
144
+ return content.decode(encoding)
145
+ except UnicodeDecodeError:
146
+ continue
147
+ return "❌ Não foi possível decodificar o arquivo TXT."
148
+ except Exception as e:
149
+ return f"❌ Erro ao processar o TXT: {str(e)}"
150
+
151
+ def process_file(file_obj):
152
+ if file_obj is None:
153
+ return "Nenhum arquivo enviado."
154
+ try:
155
+ if hasattr(file_obj, 'name'):
156
+ file_path = file_obj.name
157
+ filename = os.path.basename(file_path).lower()
158
+ file_size = os.path.getsize(file_path)
159
+ if file_size > 10 * 1024 * 1024:
160
+ return "⚠️ O arquivo é muito grande. Máx. 10 MB."
161
+ if filename.endswith('.pdf'):
162
+ return extract_text_from_pdf(file_path)
163
+ elif filename.endswith('.docx'):
164
+ return extract_text_from_docx(file_path)
165
+ elif filename.endswith('.txt'):
166
+ return extract_text_from_txt(file_path)
167
+ else:
168
+ return "⚠️ Formato de arquivo não suportado. Use PDF, DOCX ou TXT."
169
+ else:
170
+ return "❌ Erro: Objeto de arquivo inválido."
171
+ except Exception as e:
172
+ return f"❌ Erro ao processar o arquivo: {str(e)}"
173
+
174
  def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
175
  """Estrutura o prompt apenas para a análise da petição."""
176
  base_prompt = f"""
 
205
  5. Não penalize por informações ausentes no caso original, mas alerte se a omissão compromete a petição.
206
  6. A frase "Nestes termos, pede deferimento" com vírgula é incorreta; corrija para "Nestes termos pede deferimento" e penalize com -0,2 ponto, destacando o erro formal.
207
  7. A petição deve finalizar com "Local e data.\nADVOGADO.\nOAB." sem identificação do aluno (nome, número ou estado na OAB); se houver identificação específica (ex.: "OAB/SP" ou "12345"), zere a nota (0,0) e explique o motivo.
208
+
209
  **Instruções Especiais para Análise Avançada:**
210
  - **Competência**: Avalie o foro indicado com base no Art. 46 do CPC (domicílio do réu) ou Art. 101, I do CDC (domicílio do autor em relações de consumo); penalize erros com -0,10 ponto e justifique com jurisprudência (ex.: STJ, REsp 1.234.567).
211
  - **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.
 
251
  return base_prompt
252
 
253
  def estruturar_prompt_peticao_corrigida(caso, peticao):
254
+ """Estrutura o prompt apenas para a petição corrigida."""
255
  return f"""
256
  **Caso Fornecido:**
257
  {caso}
258
  **Petição do Aluno:**
259
  {peticao}
260
  ### Petição Corrigida
261
+ 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:
262
+ 1. Endereçamento correto (com foro justificado por CPC ou CDC).
263
+ 2. Qualificação completa (Art. 319, II, CPC; use "XXX" para dados ausentes).
264
+ 3. Fatos claros e fiéis ao caso (Art. 319, III, CPC).
265
+ 4. Fundamentação jurídica robusta (cite CPC, CDC, CC e súmulas aplicáveis, como Súmula 387/STJ para danos morais).
266
+ 5. Pedidos específicos e quantificados (Art. 319, IV, CPC).
267
+ 6. Valor da causa realista (Art. 319, V, CPC).
268
+ 7. Provas detalhadas (Art. 319, VI, CPC; ex.: "prova documental e testemunhal").
269
+ 8. Opção por audiência explícita (Art. 319, VII, CPC).
270
+ 9. Pedido de citação formal (ex.: "via postal, nos termos do Art. 246, I, CPC").
271
+ 10. Juntada da guia de custas com redação precisa.
272
+ 11. Fechamento formal com "Local e data.\nADVOGADO.\nOAB." sem identificação específica.
 
 
 
 
 
 
 
273
  """
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  def chamar_api(prompt):
276
  payload = {
277
  "contents": [{"parts": [{"text": prompt}]}],
 
341
  if "❌" in peticao_corrigida or "⏱️" in peticao_corrigida:
342
  return aviso_validacao + analise_formatada + "\n\n" + peticao_corrigida
343
 
 
 
 
 
344
  correcao_completa = analise + "\n\n" + peticao_corrigida
345
  HISTORICO.append({
346
  "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
 
353
  CORRECAO_CACHE[cache_key] = analise_formatada
354
  return aviso_validacao + analise_formatada
355
 
356
+ def formatar_correcao(texto):
357
+ linhas = texto.split('\n')
358
+ texto_formatado = ""
359
+ for linha in linhas:
360
+ if "Análise Geral" in linha:
361
+ texto_formatado += f"### ℹ️ {linha.strip()}\n\n"
362
+ elif "Nota Final" in linha:
363
+ texto_formatado += f"### ⭐ {linha.strip()}\n\n"
364
+ elif "Pontos Positivos" in linha:
365
+ texto_formatado += f"### ✅ {linha.strip()}\n\n"
366
+ elif "Pontos a Melhorar" in linha:
367
+ texto_formatado += f"### ⚠️ {linha.strip()}\n\n"
368
+ elif "- Invenção de fatos" in linha:
369
+ texto_formatado += f"**🔴 {linha.strip()} (-5,0 pontos)**\n"
370
+ elif linha.strip().startswith('-') and len(linha) > 2 and any(termo in linha for termo in ["Endereçamento", "Qualificação", "Fatos", "Pedidos", "Valor", "Provas"]):
371
+ texto_formatado += f"**{linha.strip()}**\n"
372
+ elif "Explicação" in linha or "Sugestão" in linha or "Dica" in linha or "Exemplo" in linha:
373
+ texto_formatado += f"*{linha.strip()}*\n"
374
+ else:
375
+ texto_formatado += f"{linha}\n"
376
+ if "Nota Final" in texto_formatado:
377
+ import re
378
+ nota_pattern = r"### Nota Final.*?(\d+[.,]\d+|\d+)"
379
+ match = re.search(nota_pattern, texto_formatado, re.DOTALL)
380
+ if match:
381
+ nota = match.group(1)
382
+ texto_formatado = texto_formatado.replace(f"### ⭐ Nota Final\n\n{nota}", f"### ⭐ Nota Final: {nota}/10\n\n")
383
+ return texto_formatado
384
+
385
+ def extrair_peticao_corrigida(correcao_completa):
386
+ try:
387
+ if "### Petição Corrigida" in correcao_completa:
388
+ inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
389
+ peticao_texto = correcao_completa[inicio:].strip()
390
+ fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
391
+ peticao_texto = peticao_texto[:fim].strip()
392
+ return peticao_texto
393
+ else:
394
+ linhas = correcao_completa.split('\n')
395
+ inicio_peticao = -1
396
+ for i, linha in enumerate(linhas):
397
+ if "EXCELENTÍSSIMO" in linha.upper() and i > len(linhas) // 2:
398
+ inicio_peticao = i
399
+ break
400
+ if inicio_peticao != -1:
401
+ peticao_texto = "\n".join(linhas[inicio_peticao:]).strip()
402
+ return peticao_texto
403
+ return "⚠️ A seção '### Petição Corrigida' não foi encontrada na resposta da API. Verifique a resposta e tente novamente."
404
+ except Exception as e:
405
+ return f"❌ Erro ao extrair petição corrigida: {str(e)}"
406
+
407
+ def fornecer_dica_contextual(texto):
408
+ dicas = [f"💡 {chave}: {dica}" for chave, dica in DICAS_OAB_MELHORADO.items() if chave.lower() in texto.lower()]
409
+ return "\n".join(dicas) if dicas else "💡 Escreva mais para receber dicas."
410
+
411
+ def calcular_progresso():
412
+ if not HISTORICO:
413
+ return "📊 Nenhum progresso registrado."
414
+ notas = []
415
+ for entry in HISTORICO:
416
+ for line in entry["correcao_completa"].split("\n"):
417
+ if "Nota Final" in line:
418
+ try:
419
+ nota = float(line.split()[-1].split("/")[0].replace(",", "."))
420
+ notas.append(nota)
421
+ except:
422
+ continue
423
+ media = sum(notas) / len(notas) if notas else 0
424
+ tendencia = "⬆️ Melhorando" if len(notas) > 1 and notas[-1] > notas[-2] else "⬇️ Estável ou piorando"
425
+ return f"📊 Progresso: Média {media:.1f}/10 | Tentativas: {len(notas)} | Tendência: {tendencia}"
426
+
427
+ def comparar_peticao_melhorada(caso, peticao_texto, correcao):
428
+ if not caso:
429
+ return "⚠️ Forneça um caso antes de comparar.", "", "", "", ""
430
+ peticao = peticao_texto if peticao_texto else (HISTORICO[-1]["peticao"] if HISTORICO else "")
431
+ if not peticao:
432
+ return "⚠️ Forneça uma petição para comparar ou corrija uma antes.", "", "", "", ""
433
+ if not correcao:
434
+ return "⚠️ Corrija a petição antes de comparar.", "", "", "", ""
435
+ ultima_correcao = HISTORICO[-1]["correcao_completa"] if HISTORICO else ""
436
+ if not ultima_correcao:
437
+ return "⚠️ Nenhuma correção disponível no histórico.", "", "", "", ""
438
+
439
+ peticao_corrigida = extrair_peticao_corrigida(ultima_correcao)
440
+ if "⚠️" in peticao_corrigida or "❌" in peticao_corrigida:
441
+ return peticao_corrigida, "", "", "", ""
442
+
443
+ orig_lines = peticao.split('\n')
444
+ corr_lines = peticao_corrigida.split('\n')
445
+ start_orig = next((i for i, line in enumerate(orig_lines) if "AÇÃO" in line.upper() or "EXCELENTÍSSIMO" in line.upper()), 0)
446
+ start_corr = next((i for i, line in enumerate(corr_lines) if "AÇÃO" in line.upper() or "EXCELENTÍSSIMO" in line.upper()), 0)
447
+
448
+ orig_body = "\n".join(orig_lines[start_orig:]).strip()
449
+ corr_body = "\n".join(corr_lines[start_corr:]).strip()
450
+
451
+ diff = difflib.ndiff(orig_body.splitlines(), corr_body.splitlines())
452
+
453
+ resultado_diff = []
454
+ criticas = []
455
+ for linha in diff:
456
+ if linha.startswith(' '):
457
+ texto = linha[2:]
458
+ resultado_diff.append(f"<div class='linha-igual'>{escape(texto)}</div>")
459
+ elif linha.startswith('- '):
460
+ texto = linha[2:]
461
+ if any(kw in texto.lower() for kw in ["invenção", "custas", "art. 319"]):
462
+ criticas.append(f"🔴 REMOVIDO: {texto} (inconsistente ou incorreto)")
463
+ resultado_diff.append(f"<div class='linha-removida'><del>{escape(texto)}</del></div>")
464
+ elif linha.startswith('+ '):
465
+ texto = linha[2:]
466
+ if any(kw in texto.lower() for kw in ["custas", "art. 319"]):
467
+ criticas.append(f"🟢 ADICIONADO: {texto} (essencial para conformidade)")
468
+ resultado_diff.append(f"<div class='linha-adicionada'><ins>{escape(texto)}</ins></div>")
469
+ elif linha.startswith('? '):
470
+ continue
471
+
472
+ total_removidas = len([linha for linha in resultado_diff if "linha-removida" in linha])
473
+ total_adicionadas = len([linha for linha in resultado_diff if "linha-adicionada" in linha])
474
+
475
+ resumo_estatistico = f"""
476
+ Linhas Removidas: {total_removidas}
477
+ Linhas Adicionadas: {total_adicionadas}
478
+ Diferença Líquida: {total_adicionadas - total_removidas} linhas
479
+ """
480
+
481
+ criticas_texto = "\n".join(criticas) if criticas else "Nenhuma mudança crítica detectada."
482
+
483
+ visualizacao_detalhada = f"""
484
+ <style>
485
+ .linha-igual {{ color: #000000; margin: 2px 0; }}
486
+ .linha-removida {{ color: #ffffff; background-color: #b30000; margin: 2px 0; padding: 2px; }}
487
+ .linha-adicionada {{ color: #ffffff; background-color: #006600; margin: 2px 0; padding: 2px; }}
488
+ </style>
489
+ {"".join(resultado_diff)}
490
+ """
491
+ return peticao, peticao_corrigida, resumo_estatistico, criticas_texto, visualizacao_detalhada
492
+
493
+ def exportar_diferencas_html(caso, peticao_texto, correcao):
494
+ _, _, resumo_estatistico, criticas_texto, visualizacao_detalhada = comparar_peticao_melhorada(caso, peticao_texto, correcao)
495
+ html_content = f"""
496
+ <html>
497
+ <head>
498
+ <title>Diferenças na Petição</title>
499
+ <style>
500
+ body {{ font-family: Arial, sans-serif; margin: 20px; }}
501
+ .linha-igual {{ color: #000000; margin: 2px 0; }}
502
+ .linha-removida {{ color: #ffffff; background-color: #b30000; margin: 2px 0; padding: 2px; }}
503
+ .linha-adicionada {{ color: #ffffff; background-color: #006600; margin: 2px 0; padding: 2px; }}
504
+ h2 {{ color: #333; }}
505
+ pre {{ white-space: pre-wrap; }}
506
+ </style>
507
+ </head>
508
+ <body>
509
+ <h2>Estatísticas</h2>
510
+ <pre>{resumo_estatistico}</pre>
511
+ <h2>Mudanças Críticas</h2>
512
+ <pre>{criticas_texto}</pre>
513
+ <h2>Alterações Detalhadas</h2>
514
+ {visualizacao_detalhada}
515
+ </body>
516
+ </html>
517
+ """
518
+ try:
519
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
520
+ temp_path = tmp.name
521
+ HTML(string=html_content).write_pdf(temp_path)
522
+ return temp_path
523
+ except Exception as e:
524
+ return f"❌ Erro ao gerar PDF: {str(e)}"
525
+ finally:
526
+ if 'temp_path' in locals() and os.path.exists(temp_path):
527
+ os.unlink(temp_path)
528
 
529
  def interface_gradio():
530
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
688
  def estruturar_prompt_geracao_caso():
689
  return """
690
  Crie um caso fictício detalhado para uma petição inicial cível, similar ao exemplo:
691
+ "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."
692
  O caso deve:
693
  1. Conter uma situação clara de violação de direitos que justifique uma ação cível
694
  2. Incluir valores específicos de danos materiais e/ou morais
 
712
  problemas.append("⚠️ Endereçamento ausente ou incompleto (Art. 319, I, CPC).")
713
  if "Art. 319" not in peticao and "CPC" not in peticao:
714
  problemas.append("⚠️ Fundamentação jurídica não menciona o CPC (Art. 319).")
 
 
715
  if "Dá-se à causa o valor" not in peticao:
716
  problemas.append("⚠️ Valor da causa não especificado (Art. 319, V, CPC).")
717
  if "juntada da guia de custas" not in peticao.lower():
 
720
  problemas.append("⚠️ Fechamento formal ausente.")
721
  return problemas
722
 
 
 
 
 
 
 
 
723
  app = interface_gradio()
724
+ app.launch()