Update app.py
Browse files
app.py
CHANGED
|
@@ -2,7 +2,6 @@ import gradio as gr
|
|
| 2 |
import requests
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
-
import tempfile
|
| 6 |
import PyPDF2
|
| 7 |
import docx
|
| 8 |
import io
|
|
@@ -17,13 +16,12 @@ from datetime import datetime
|
|
| 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 |
|
| 24 |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
| 25 |
if not GEMINI_API_KEY:
|
| 26 |
-
raise ValueError("❌ A chave GEMINI_API_KEY não foi encontrada. Configure-a
|
| 27 |
|
| 28 |
GEMINI_API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={GEMINI_API_KEY}"
|
| 29 |
|
|
@@ -32,22 +30,22 @@ CONFIG = {
|
|
| 32 |
"retry_delay": 5,
|
| 33 |
"timeout": 60,
|
| 34 |
"temperatura": 0.2,
|
| 35 |
-
"max_tokens": 12882
|
| 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:
|
| 43 |
-
"Fatos": "Descreva os fatos cronologicamente, sem inventar informações. Exemplo: 'Em [data], o autor celebrou contrato com o réu para
|
| 44 |
-
"Pedidos": "Seja específico nos pedidos (ex.: 'condenação ao pagamento de R$ X').
|
| 45 |
-
"Custas": "Inclua 'Requer a juntada da guia de custas devidamente recolhida'.
|
| 46 |
-
"Fundamentação": "Baseie-se em artigos do CPC ou CDC e
|
| 47 |
-
"Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula.
|
| 48 |
-
"Estratégia Jurídica": "Utilize precedentes e súmulas para fortalecer
|
| 49 |
-
"Provas": "Especifique as provas
|
| 50 |
-
"Contra-argumentação": "Antecipe
|
| 51 |
}
|
| 52 |
|
| 53 |
HISTORICO = []
|
|
@@ -172,9 +170,8 @@ def process_file(file_obj):
|
|
| 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"""
|
| 177 |
-
## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB
|
| 178 |
**Caso Fornecido:**
|
| 179 |
{caso}
|
| 180 |
**Petição do Aluno:**
|
|
@@ -184,126 +181,87 @@ def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
|
|
| 184 |
1. Análise Geral (Visão geral sobre a petição - máximo 3 linhas, destacando adequação ao caso e consistência jurídica)
|
| 185 |
2. Nota Final (0 a 10 com uma casa decimal, justificando brevemente a pontuação total)
|
| 186 |
3. Pontos Positivos (Liste pelo menos 3 aspectos bem executados com justificativa legal detalhada, incluindo artigos, súmulas ou precedentes aplicáveis)
|
| 187 |
-
4. Pontos a Melhorar (Liste todos os problemas encontrados em ordem de gravidade, com explicação detalhada, base legal e impacto na nota
|
| 188 |
5. Análise Específica dos Requisitos:
|
| 189 |
-
- Endereçamento e qualificação do juízo (Art. 319, I, CPC; Art. 101, I, CDC se aplicável)
|
| 190 |
-
- Qualificação das partes (Art. 319, II, CPC)
|
| 191 |
-
- Fatos e fundamentos jurídicos (Art. 319, III, CPC; Art. 186, CC)
|
| 192 |
-
- Pedidos com especificações (Art. 319, IV, CPC)
|
| 193 |
-
- Valor da causa (Art. 319, V, CPC)
|
| 194 |
-
- Provas com que o autor pretende demonstrar
|
| 195 |
-
- Opção pela realização ou não de audiência de conciliação (Art. 319, VII, CPC)
|
| 196 |
-
- Pedido de citação do réu
|
| 197 |
-
- Juntada da guia de custas
|
| 198 |
-
- Fechamento formal da petição
|
| 199 |
|
| 200 |
**Critérios Fundamentais:**
|
| 201 |
-
1. Identificação do candidato (nome, CPF, RG ou número real na seção final)
|
| 202 |
-
2. Invenção de fatos não constantes no caso
|
| 203 |
-
3. Falta de pedido de juntada de custas
|
| 204 |
-
4. Use marcadores para dados não fornecidos (ex.: "Rua XXX"
|
| 205 |
-
5. Não penalize por informações ausentes no caso original, mas alerte se a omissão compromete a petição
|
| 206 |
-
6.
|
| 207 |
-
7.
|
| 208 |
-
|
| 209 |
-
**Instruções Especiais
|
| 210 |
-
-
|
| 211 |
-
-
|
| 212 |
-
-
|
| 213 |
-
-
|
| 214 |
-
-
|
| 215 |
-
-
|
| 216 |
-
|
| 217 |
-
**Pontuação Detalhada:**
|
| 218 |
-
Inclua uma tabela com:
|
| 219 |
-
| ITEM | PONTUAÇÃO | JUSTIFICATIVA |
|
| 220 |
-
|-------------------------------------------|-----------|---------------|
|
| 221 |
-
**Endereçamento** | | |
|
| 222 |
-
1. Correto endereçamento ao juízo competente (0,10) | / 0,10 | [Explique erro ou acerto] |
|
| 223 |
-
2. Qualificação das partes conforme Art. 319, II, CPC (0,20) | / 0,20 | [Explique erro ou acerto] |
|
| 224 |
-
**Fundamentos** | | |
|
| 225 |
-
3. Exposição clara dos fatos (0,20) | / 0,20 | [Explique erro ou acerto] |
|
| 226 |
-
4. Fundamentação jurídica adequada (0,25) | / 0,25 | [Explique erro ou acerto] |
|
| 227 |
-
5. Demonstração de ilícito e dano (0,25) | / 0,25 | [Explique erro ou acerto] |
|
| 228 |
-
**Pedidos** | | |
|
| 229 |
-
6. Pedidos claros e específicos (0,30) | / 0,30 | [Explique erro ou acerto] |
|
| 230 |
-
7. Valor da causa indicado (0,10) | / 0,10 | [Explique erro ou acerto] |
|
| 231 |
-
8. Provas pretendidas especificadas (0,10)| / 0,10 | [Explique erro ou acerto] |
|
| 232 |
-
9. Opção sobre audiência de conciliação (0,10) | / 0,10 | [Explique erro ou acerto] |
|
| 233 |
-
10. Pedido de citação do réu (0,10) | / 0,10 | [Explique erro ou acerto] |
|
| 234 |
-
11. Juntada da guia de custas (0,10) | / 0,10 | [Explique erro ou acerto] |
|
| 235 |
-
**Fechamento** | | |
|
| 236 |
-
12. Fechamento formal adequado (0,10) | / 0,10 | [Explique erro ou acerto] |
|
| 237 |
-
**Nota Final:** Calcule a soma dos pontos, aplicando penalizações cumulativas conforme os critérios fundamentais; justifique qualquer desconto adicional.
|
| 238 |
"""
|
| 239 |
if modo_treinamento:
|
| 240 |
base_prompt += f"""
|
| 241 |
-
**Modo de Treinamento
|
| 242 |
-
- Para cada item com
|
| 243 |
-
a) Explicação detalhada
|
| 244 |
-
b) Sugestão de redação corrigida
|
| 245 |
-
c) Exemplo prático
|
| 246 |
-
d) Dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)})
|
| 247 |
-
e) Estratégia jurídica avançada
|
| 248 |
-
-
|
| 249 |
-
- Sugira estratégias adicionais
|
| 250 |
"""
|
| 251 |
return base_prompt
|
| 252 |
|
| 253 |
def estruturar_prompt_peticao_corrigida(caso, peticao):
|
| 254 |
-
"""Estrutura o prompt para a petição corrigida com instruções mais enfáticas."""
|
| 255 |
return f"""
|
| 256 |
**Caso Fornecido:**
|
| 257 |
{caso}
|
| 258 |
**Petição do Aluno:**
|
| 259 |
{peticao}
|
| 260 |
### Petição Corrigida
|
| 261 |
-
Gere a petição corrigida abaixo desta seção, intitulada
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
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.
|
| 274 |
-
|
| 275 |
-
**Instruções Obrigatórias**:
|
| 276 |
-
- Não omita nenhum dos 11 itens acima. Cada um é essencial e deve ser verificado e corrigido se necessário.
|
| 277 |
-
- Siga estritamente as regras formais da OAB, especialmente a correção da vírgula em "Nestes termos pede deferimento".
|
| 278 |
-
- Baseie-se exclusivamente no caso fornecido e na petição do aluno, sem inventar fatos ou dados.
|
| 279 |
-
- Gere a petição completa, incluindo todas as seções (endereçamento, qualificação, fatos, direito, pedidos, valor da causa, provas, fechamento).
|
| 280 |
"""
|
| 281 |
|
| 282 |
def pos_corrigir_peticao(peticao_corrigida):
|
| 283 |
-
"""Pós-processamento para garantir que todas as regras sejam aplicadas."""
|
| 284 |
-
# Garantir a correção da vírgula
|
| 285 |
if "Nestes termos, pede deferimento" in peticao_corrigida:
|
| 286 |
peticao_corrigida = peticao_corrigida.replace("Nestes termos, pede deferimento", "Nestes termos pede deferimento")
|
| 287 |
-
|
| 288 |
-
# Garantir o pedido de juntada da guia de custas
|
| 289 |
if "juntada da guia de custas" not in peticao_corrigida.lower():
|
| 290 |
peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a juntada da guia de custas devidamente recolhida.\nNestes termos pede deferimento")
|
| 291 |
-
|
| 292 |
-
# Garantir o fechamento formal correto
|
| 293 |
if not peticao_corrigida.strip().endswith("Local e data.\nADVOGADO.\nOAB."):
|
| 294 |
linhas = peticao_corrigida.split('\n')
|
| 295 |
-
while linhas and not linhas[-1].strip():
|
| 296 |
linhas.pop()
|
| 297 |
if len(linhas) >= 3:
|
| 298 |
linhas[-3:] = ["Local e data.", "ADVOGADO.", "OAB."]
|
| 299 |
else:
|
| 300 |
linhas.extend(["Local e data.", "ADVOGADO.", "OAB."])
|
| 301 |
peticao_corrigida = "\n".join(linhas)
|
| 302 |
-
|
| 303 |
-
# Garantir opção por audiência explícita
|
| 304 |
if "audiência de conciliação" not in peticao_corrigida.lower():
|
| 305 |
-
peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a designação de audiência de conciliação,
|
| 306 |
-
|
| 307 |
return peticao_corrigida
|
| 308 |
|
| 309 |
def chamar_api(prompt):
|
|
@@ -317,29 +275,28 @@ def chamar_api(prompt):
|
|
| 317 |
with rate_limiter:
|
| 318 |
response = requests.post(GEMINI_API_URL, headers=headers, data=json.dumps(payload), timeout=CONFIG["timeout"])
|
| 319 |
if response.status_code != 200:
|
| 320 |
-
|
| 321 |
-
error_msg = response_data.get("error", {}).get("message", "Erro desconhecido")
|
| 322 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 323 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 324 |
continue
|
| 325 |
-
return f"❌ Erro da API
|
| 326 |
response_data = response.json()
|
| 327 |
if "candidates" not in response_data or not response_data["candidates"]:
|
| 328 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 329 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 330 |
continue
|
| 331 |
-
return "❌
|
| 332 |
return response_data["candidates"][0]["content"]["parts"][0]["text"].strip()
|
| 333 |
except requests.Timeout:
|
| 334 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 335 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 336 |
continue
|
| 337 |
-
return "⏱️
|
| 338 |
except requests.RequestException as e:
|
| 339 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 340 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 341 |
continue
|
| 342 |
-
return f"❌ Erro
|
| 343 |
except Exception as e:
|
| 344 |
return f"❌ Erro inesperado: {str(e)}"
|
| 345 |
|
|
@@ -362,22 +319,18 @@ def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treina
|
|
| 362 |
if cache_key in CORRECAO_CACHE:
|
| 363 |
return aviso_validacao + CORRECAO_CACHE[cache_key]
|
| 364 |
|
| 365 |
-
# Primeira chamada: análise
|
| 366 |
prompt_analise = estruturar_prompt_analise(caso, peticao_conteudo, modo_treinamento)
|
| 367 |
analise = chamar_api(prompt_analise)
|
| 368 |
if "❌" in analise or "⏱️" in analise:
|
| 369 |
return aviso_validacao + analise
|
| 370 |
analise_formatada = formatar_correcao(analise)
|
| 371 |
|
| 372 |
-
# Segunda chamada: petição corrigida
|
| 373 |
prompt_peticao = estruturar_prompt_peticao_corrigida(caso, peticao_conteudo)
|
| 374 |
peticao_corrigida = chamar_api(prompt_peticao)
|
| 375 |
if "❌" in peticao_corrigida or "⏱️" in peticao_corrigida:
|
| 376 |
return aviso_validacao + analise_formatada + "\n\n" + peticao_corrigida
|
| 377 |
|
| 378 |
-
# Pós-correção para garantir conformidade
|
| 379 |
peticao_corrigida = pos_corrigir_peticao(peticao_corrigida)
|
| 380 |
-
print("Debug: Petição corrigida após pós-processamento:\n", peticao_corrigida[:1000]) # Debug para verificar a saída
|
| 381 |
|
| 382 |
correcao_completa = analise + "\n\n" + peticao_corrigida
|
| 383 |
HISTORICO.append({
|
|
@@ -405,7 +358,7 @@ def formatar_correcao(texto):
|
|
| 405 |
texto_formatado += f"### ⚠️ {linha.strip()}\n\n"
|
| 406 |
elif "- Invenção de fatos" in linha:
|
| 407 |
texto_formatado += f"**🔴 {linha.strip()} (-5,0 pontos)**\n"
|
| 408 |
-
elif linha.strip().startswith('-') and
|
| 409 |
texto_formatado += f"**{linha.strip()}**\n"
|
| 410 |
elif "Explicação" in linha or "Sugestão" in linha or "Dica" in linha or "Exemplo" in linha:
|
| 411 |
texto_formatado += f"*{linha.strip()}*\n"
|
|
@@ -426,21 +379,10 @@ def extrair_peticao_corrigida(correcao_completa):
|
|
| 426 |
inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
|
| 427 |
peticao_texto = correcao_completa[inicio:].strip()
|
| 428 |
fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
else:
|
| 432 |
-
linhas = correcao_completa.split('\n')
|
| 433 |
-
inicio_peticao = -1
|
| 434 |
-
for i, linha in enumerate(linhas):
|
| 435 |
-
if "EXCELENTÍSSIMO" in linha.upper() and i > len(linhas) // 2:
|
| 436 |
-
inicio_peticao = i
|
| 437 |
-
break
|
| 438 |
-
if inicio_peticao != -1:
|
| 439 |
-
peticao_texto = "\n".join(linhas[inicio_peticao:]).strip()
|
| 440 |
-
return peticao_texto
|
| 441 |
-
return "⚠️ A seção '### Petição Corrigida' não foi encontrada na resposta da API. Verifique a resposta e tente novamente."
|
| 442 |
except Exception as e:
|
| 443 |
-
return f"❌ Erro ao extrair petição
|
| 444 |
|
| 445 |
def fornecer_dica_contextual(texto):
|
| 446 |
dicas = [f"💡 {chave}: {dica}" for chave, dica in DICAS_OAB_MELHORADO.items() if chave.lower() in texto.lower()]
|
|
@@ -530,6 +472,7 @@ Diferença Líquida: {total_adicionadas - total_removidas} linhas
|
|
| 530 |
|
| 531 |
def exportar_diferencas_html(caso, peticao_texto, correcao):
|
| 532 |
_, _, resumo_estatistico, criticas_texto, visualizacao_detalhada = comparar_peticao_melhorada(caso, peticao_texto, correcao)
|
|
|
|
| 533 |
html_content = f"""
|
| 534 |
<html>
|
| 535 |
<head>
|
|
@@ -553,21 +496,16 @@ def exportar_diferencas_html(caso, peticao_texto, correcao):
|
|
| 553 |
</body>
|
| 554 |
</html>
|
| 555 |
"""
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
except Exception as e:
|
| 562 |
-
return f"❌ Erro ao gerar PDF: {str(e)}"
|
| 563 |
-
finally:
|
| 564 |
-
if 'temp_path' in locals() and os.path.exists(temp_path):
|
| 565 |
-
os.unlink(temp_path)
|
| 566 |
|
| 567 |
def interface_gradio():
|
| 568 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 569 |
gr.Markdown("# 📝 Corretor Avançado de Petições Iniciais (Critérios OAB)")
|
| 570 |
-
gr.Markdown("⚠️ Nota: Devido a limites
|
| 571 |
with gr.Row():
|
| 572 |
with gr.Column(scale=1):
|
| 573 |
gr.Markdown("""
|
|
@@ -640,7 +578,7 @@ def interface_gradio():
|
|
| 640 |
visualizacao_detalhada_output = gr.HTML(label="📝 Alterações Detalhadas")
|
| 641 |
|
| 642 |
with gr.Row():
|
| 643 |
-
download_btn = gr.Button("📥 Baixar Diferenças em
|
| 644 |
download_file = gr.File(label="Download", visible=False)
|
| 645 |
|
| 646 |
with gr.Accordion("📜 Histórico de Correções", open=False):
|
|
@@ -683,7 +621,7 @@ def interface_gradio():
|
|
| 683 |
inputs=[caso_input, peticao_texto, resultado_output],
|
| 684 |
outputs=[download_file]
|
| 685 |
).then(
|
| 686 |
-
fn=lambda x: [gr.update(visible=True, value=x)
|
| 687 |
outputs=[download_file, download_btn]
|
| 688 |
)
|
| 689 |
|
|
@@ -706,7 +644,7 @@ def interface_gradio():
|
|
| 706 |
""")
|
| 707 |
gr.Markdown("""
|
| 708 |
### ℹ️ Modo de Treinamento Interativo
|
| 709 |
-
Ative para explicações detalhadas com exemplos
|
| 710 |
### 📋 Como usar
|
| 711 |
1. Gere ou cole o caso
|
| 712 |
2. Insira a petição
|
|
@@ -728,19 +666,13 @@ def estruturar_prompt_geracao_caso():
|
|
| 728 |
Crie um caso fictício detalhado para uma petição inicial cível, similar ao exemplo:
|
| 729 |
"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."
|
| 730 |
O caso deve:
|
| 731 |
-
1. Conter uma
|
| 732 |
2. Incluir valores específicos de danos materiais e/ou morais
|
| 733 |
-
3. Mencionar nomes completos, cidades e informações
|
| 734 |
-
4. Estabelecer relação entre as partes (contratual,
|
| 735 |
5. Ter complexidade adequada para uma petição inicial de nível OAB
|
| 736 |
-
6. Ter entre 15-25 linhas
|
| 737 |
-
7. Focar em um
|
| 738 |
-
- Responsabilidade civil por acidente de consumo
|
| 739 |
-
- Inadimplemento contratual com danos
|
| 740 |
-
- Responsabilidade civil por dano ambiental
|
| 741 |
-
- Violação de direitos da personalidade
|
| 742 |
-
- Relação de consumo com vício de produto/serviço
|
| 743 |
-
- Reparação por danos patrimoniais
|
| 744 |
Termine com: "Situação problema: Como advogado(a) de [NOME DO CLIENTE], promova a demanda cabível."
|
| 745 |
"""
|
| 746 |
|
|
|
|
| 2 |
import requests
|
| 3 |
import os
|
| 4 |
import json
|
|
|
|
| 5 |
import PyPDF2
|
| 6 |
import docx
|
| 7 |
import io
|
|
|
|
| 16 |
from ratelimiter import RateLimiter
|
| 17 |
import difflib
|
| 18 |
from html import escape
|
|
|
|
| 19 |
|
| 20 |
load_dotenv()
|
| 21 |
|
| 22 |
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
| 23 |
if not GEMINI_API_KEY:
|
| 24 |
+
raise ValueError("❌ A chave GEMINI_API_KEY não foi encontrada. Configure-a no arquivo .env ou como Secret.")
|
| 25 |
|
| 26 |
GEMINI_API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={GEMINI_API_KEY}"
|
| 27 |
|
|
|
|
| 30 |
"retry_delay": 5,
|
| 31 |
"timeout": 60,
|
| 32 |
"temperatura": 0.2,
|
| 33 |
+
"max_tokens": 12882
|
| 34 |
}
|
| 35 |
|
| 36 |
rate_limiter = RateLimiter(max_calls=60, period=60)
|
| 37 |
|
| 38 |
DICAS_OAB_MELHORADO = {
|
| 39 |
"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]'.",
|
| 40 |
+
"Qualificação": "Mencione nome, estado civil (se informado), endereço (use 'Rua XXX' se não fornecido). Dica: Verifique se todos os dados estão corretos.",
|
| 41 |
+
"Fatos": "Descreva os fatos cronologicamente, sem inventar informações. Exemplo: 'Em [data], o autor celebrou contrato com o réu para [descrição]'.",
|
| 42 |
+
"Pedidos": "Seja específico nos pedidos (ex.: 'condenação ao pagamento de R$ X'). Inclua danos morais e materiais, se aplicável.",
|
| 43 |
+
"Custas": "Inclua 'Requer a juntada da guia de custas devidamente recolhida'. Nunca omita para evitar extinção do processo.",
|
| 44 |
+
"Fundamentação": "Baseie-se em artigos do CPC ou CDC e súmulas do STJ (ex.: Súmula 387 para danos morais).",
|
| 45 |
+
"Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula. Evite erros de pontuação.",
|
| 46 |
+
"Estratégia Jurídica": "Utilize precedentes e súmulas para fortalecer a argumentação.",
|
| 47 |
+
"Provas": "Especifique as provas a produzir. Inclua testemunhas, documentos e perícias, se necessário.",
|
| 48 |
+
"Contra-argumentação": "Antecipe defesas da parte contrária e prepare respostas."
|
| 49 |
}
|
| 50 |
|
| 51 |
HISTORICO = []
|
|
|
|
| 170 |
return f"❌ Erro ao processar o arquivo: {str(e)}"
|
| 171 |
|
| 172 |
def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
|
|
|
|
| 173 |
base_prompt = f"""
|
| 174 |
+
## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB)
|
| 175 |
**Caso Fornecido:**
|
| 176 |
{caso}
|
| 177 |
**Petição do Aluno:**
|
|
|
|
| 181 |
1. Análise Geral (Visão geral sobre a petição - máximo 3 linhas, destacando adequação ao caso e consistência jurídica)
|
| 182 |
2. Nota Final (0 a 10 com uma casa decimal, justificando brevemente a pontuação total)
|
| 183 |
3. Pontos Positivos (Liste pelo menos 3 aspectos bem executados com justificativa legal detalhada, incluindo artigos, súmulas ou precedentes aplicáveis)
|
| 184 |
+
4. Pontos a Melhorar (Liste todos os problemas encontrados em ordem de gravidade, com explicação detalhada, base legal e impacto na nota)
|
| 185 |
5. Análise Específica dos Requisitos:
|
| 186 |
+
- Endereçamento e qualificação do juízo (Art. 319, I, CPC; Art. 101, I, CDC se aplicável)
|
| 187 |
+
- Qualificação das partes (Art. 319, II, CPC)
|
| 188 |
+
- Fatos e fundamentos jurídicos (Art. 319, III, CPC; Art. 186, CC)
|
| 189 |
+
- Pedidos com especificações (Art. 319, IV, CPC)
|
| 190 |
+
- Valor da causa (Art. 319, V, CPC)
|
| 191 |
+
- Provas com que o autor pretende demonstrar os fatos (Art. 319, VI, CPC)
|
| 192 |
+
- Opção pela realização ou não de audiência de conciliação (Art. 319, VII, CPC)
|
| 193 |
+
- Pedido de citação do réu
|
| 194 |
+
- Juntada da guia de custas
|
| 195 |
+
- Fechamento formal da petição
|
| 196 |
|
| 197 |
**Critérios Fundamentais:**
|
| 198 |
+
1. Identificação do candidato (nome, CPF, RG ou número real na seção final): nota máxima 3,0
|
| 199 |
+
2. Invenção de fatos não constantes no caso: -5,0 pontos
|
| 200 |
+
3. Falta de pedido de juntada de custas: -1,0 ponto
|
| 201 |
+
4. Use marcadores para dados não fornecidos (ex.: "Rua XXX"): penalize invenção de dados (-0,5 por ocorrência)
|
| 202 |
+
5. Não penalize por informações ausentes no caso original, mas alerte se a omissão compromete a petição
|
| 203 |
+
6. "Nestes termos, pede deferimento" com vírgula é incorreto: corrija e penalize com -0,2 ponto
|
| 204 |
+
7. Fechamento deve ser "Local e data.\nADVOGADO.\nOAB." sem identificação específica (ex.: "OAB/SP" ou "12345"): zere a nota (0,0) se houver
|
| 205 |
+
|
| 206 |
+
**Instruções Especiais:**
|
| 207 |
+
- Avalie o foro com base no Art. 46 do CPC ou Art. 101, I do CDC
|
| 208 |
+
- Compare fatos narrados com o caso
|
| 209 |
+
- Exija citação de pelo menos um artigo específico
|
| 210 |
+
- Verifique pedidos quantificados e fundamentados para danos
|
| 211 |
+
- Avalie formalidade e precisão da linguagem
|
| 212 |
+
- Inclua precedentes quando aplicável
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
"""
|
| 214 |
if modo_treinamento:
|
| 215 |
base_prompt += f"""
|
| 216 |
+
**Modo de Treinamento:**
|
| 217 |
+
- Para cada item com problemas, forneça:
|
| 218 |
+
a) Explicação detalhada com base legal
|
| 219 |
+
b) Sugestão de redação corrigida
|
| 220 |
+
c) Exemplo prático
|
| 221 |
+
d) Dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)})
|
| 222 |
+
e) Estratégia jurídica avançada
|
| 223 |
+
- Liste inconsistências com trechos específicos
|
| 224 |
+
- Sugira estratégias adicionais (ex.: cumulação de pedidos, Art. 327, CPC)
|
| 225 |
"""
|
| 226 |
return base_prompt
|
| 227 |
|
| 228 |
def estruturar_prompt_peticao_corrigida(caso, peticao):
|
|
|
|
| 229 |
return f"""
|
| 230 |
**Caso Fornecido:**
|
| 231 |
{caso}
|
| 232 |
**Petição do Aluno:**
|
| 233 |
{peticao}
|
| 234 |
### Petição Corrigida
|
| 235 |
+
Gere a petição corrigida abaixo desta seção, intitulada '### Petição Corrigida'. Siga TODOS os critérios abaixo:
|
| 236 |
+
1. Endereçamento correto: Art. 46 do CPC ou Art. 101, I do CDC
|
| 237 |
+
2. Qualificação completa: Nome, estado civil, endereço (use "XXX" para dados ausentes), Art. 319, II, CPC
|
| 238 |
+
3. Fatos fiéis ao caso: Ordem cronológica, sem invenção, Art. 319, III, CPC
|
| 239 |
+
4. Fundamentação jurídica: Cite pelo menos um artigo (ex.: Art. 186, CC) e súmulas, se aplicável
|
| 240 |
+
5. Pedidos específicos: Detalhe valores (ex.: "R$ 50.000,00 por danos morais"), Art. 319, IV, CPC
|
| 241 |
+
6. Valor da causa realista: Soma do benefício econômico, Art. 319, V, CPC
|
| 242 |
+
7. Provas detalhadas: "Prova documental e testemunhal", Art. 319, VI, CPC
|
| 243 |
+
8. Opção por audiência: "Requer a designação" ou "Dispensa", Art. 319, VII, CPC
|
| 244 |
+
9. Pedido de citação: "Via postal, Art. 246, I, CPC"
|
| 245 |
+
10. Juntada da guia: "Requer a juntada da guia de custas devidamente recolhida"
|
| 246 |
+
11. Fechamento: "Local e data.\nADVOGADO.\nOAB." sem identificação específica; corrija "Nestes termos, pede deferimento" para "Nestes termos pede deferimento"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
"""
|
| 248 |
|
| 249 |
def pos_corrigir_peticao(peticao_corrigida):
|
|
|
|
|
|
|
| 250 |
if "Nestes termos, pede deferimento" in peticao_corrigida:
|
| 251 |
peticao_corrigida = peticao_corrigida.replace("Nestes termos, pede deferimento", "Nestes termos pede deferimento")
|
|
|
|
|
|
|
| 252 |
if "juntada da guia de custas" not in peticao_corrigida.lower():
|
| 253 |
peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a juntada da guia de custas devidamente recolhida.\nNestes termos pede deferimento")
|
|
|
|
|
|
|
| 254 |
if not peticao_corrigida.strip().endswith("Local e data.\nADVOGADO.\nOAB."):
|
| 255 |
linhas = peticao_corrigida.split('\n')
|
| 256 |
+
while linhas and not linhas[-1].strip():
|
| 257 |
linhas.pop()
|
| 258 |
if len(linhas) >= 3:
|
| 259 |
linhas[-3:] = ["Local e data.", "ADVOGADO.", "OAB."]
|
| 260 |
else:
|
| 261 |
linhas.extend(["Local e data.", "ADVOGADO.", "OAB."])
|
| 262 |
peticao_corrigida = "\n".join(linhas)
|
|
|
|
|
|
|
| 263 |
if "audiência de conciliação" not in peticao_corrigida.lower():
|
| 264 |
+
peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a designação de audiência de conciliação, Art. 319, VII, CPC.\nNestes termos pede deferimento")
|
|
|
|
| 265 |
return peticao_corrigida
|
| 266 |
|
| 267 |
def chamar_api(prompt):
|
|
|
|
| 275 |
with rate_limiter:
|
| 276 |
response = requests.post(GEMINI_API_URL, headers=headers, data=json.dumps(payload), timeout=CONFIG["timeout"])
|
| 277 |
if response.status_code != 200:
|
| 278 |
+
error_msg = response.json().get("error", {}).get("message", "Erro desconhecido")
|
|
|
|
| 279 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 280 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 281 |
continue
|
| 282 |
+
return f"❌ Erro da API: {error_msg}"
|
| 283 |
response_data = response.json()
|
| 284 |
if "candidates" not in response_data or not response_data["candidates"]:
|
| 285 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 286 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 287 |
continue
|
| 288 |
+
return "❌ Resposta vazia da API."
|
| 289 |
return response_data["candidates"][0]["content"]["parts"][0]["text"].strip()
|
| 290 |
except requests.Timeout:
|
| 291 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 292 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 293 |
continue
|
| 294 |
+
return "⏱️ Tempo esgotado."
|
| 295 |
except requests.RequestException as e:
|
| 296 |
if tentativa < CONFIG["max_retry_attempts"] - 1:
|
| 297 |
time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
|
| 298 |
continue
|
| 299 |
+
return f"❌ Erro na requisição: {e}"
|
| 300 |
except Exception as e:
|
| 301 |
return f"❌ Erro inesperado: {str(e)}"
|
| 302 |
|
|
|
|
| 319 |
if cache_key in CORRECAO_CACHE:
|
| 320 |
return aviso_validacao + CORRECAO_CACHE[cache_key]
|
| 321 |
|
|
|
|
| 322 |
prompt_analise = estruturar_prompt_analise(caso, peticao_conteudo, modo_treinamento)
|
| 323 |
analise = chamar_api(prompt_analise)
|
| 324 |
if "❌" in analise or "⏱️" in analise:
|
| 325 |
return aviso_validacao + analise
|
| 326 |
analise_formatada = formatar_correcao(analise)
|
| 327 |
|
|
|
|
| 328 |
prompt_peticao = estruturar_prompt_peticao_corrigida(caso, peticao_conteudo)
|
| 329 |
peticao_corrigida = chamar_api(prompt_peticao)
|
| 330 |
if "❌" in peticao_corrigida or "⏱️" in peticao_corrigida:
|
| 331 |
return aviso_validacao + analise_formatada + "\n\n" + peticao_corrigida
|
| 332 |
|
|
|
|
| 333 |
peticao_corrigida = pos_corrigir_peticao(peticao_corrigida)
|
|
|
|
| 334 |
|
| 335 |
correcao_completa = analise + "\n\n" + peticao_corrigida
|
| 336 |
HISTORICO.append({
|
|
|
|
| 358 |
texto_formatado += f"### ⚠️ {linha.strip()}\n\n"
|
| 359 |
elif "- Invenção de fatos" in linha:
|
| 360 |
texto_formatado += f"**🔴 {linha.strip()} (-5,0 pontos)**\n"
|
| 361 |
+
elif linha.strip().startswith('-') and any(termo in linha for termo in ["Endereçamento", "Qualificação", "Fatos", "Pedidos", "Valor", "Provas"]):
|
| 362 |
texto_formatado += f"**{linha.strip()}**\n"
|
| 363 |
elif "Explicação" in linha or "Sugestão" in linha or "Dica" in linha or "Exemplo" in linha:
|
| 364 |
texto_formatado += f"*{linha.strip()}*\n"
|
|
|
|
| 379 |
inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
|
| 380 |
peticao_texto = correcao_completa[inicio:].strip()
|
| 381 |
fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
|
| 382 |
+
return peticao_texto[:fim].strip()
|
| 383 |
+
return "⚠️ Seção '### Petição Corrigida' não encontrada."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
except Exception as e:
|
| 385 |
+
return f"❌ Erro ao extrair petição: {str(e)}"
|
| 386 |
|
| 387 |
def fornecer_dica_contextual(texto):
|
| 388 |
dicas = [f"💡 {chave}: {dica}" for chave, dica in DICAS_OAB_MELHORADO.items() if chave.lower() in texto.lower()]
|
|
|
|
| 472 |
|
| 473 |
def exportar_diferencas_html(caso, peticao_texto, correcao):
|
| 474 |
_, _, resumo_estatistico, criticas_texto, visualizacao_detalhada = comparar_peticao_melhorada(caso, peticao_texto, correcao)
|
| 475 |
+
|
| 476 |
html_content = f"""
|
| 477 |
<html>
|
| 478 |
<head>
|
|
|
|
| 496 |
</body>
|
| 497 |
</html>
|
| 498 |
"""
|
| 499 |
+
|
| 500 |
+
nome_arquivo = f"diferencas_peticao_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html"
|
| 501 |
+
with open(nome_arquivo, 'w', encoding='utf-8') as f:
|
| 502 |
+
f.write(html_content)
|
| 503 |
+
return nome_arquivo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
|
| 505 |
def interface_gradio():
|
| 506 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 507 |
gr.Markdown("# 📝 Corretor Avançado de Petições Iniciais (Critérios OAB)")
|
| 508 |
+
gr.Markdown("⚠️ Nota: Devido a limites da API, pode haver atrasos ou erros em muitas requisições rápidas.")
|
| 509 |
with gr.Row():
|
| 510 |
with gr.Column(scale=1):
|
| 511 |
gr.Markdown("""
|
|
|
|
| 578 |
visualizacao_detalhada_output = gr.HTML(label="📝 Alterações Detalhadas")
|
| 579 |
|
| 580 |
with gr.Row():
|
| 581 |
+
download_btn = gr.Button("📥 Baixar Diferenças em HTML", visible=False)
|
| 582 |
download_file = gr.File(label="Download", visible=False)
|
| 583 |
|
| 584 |
with gr.Accordion("📜 Histórico de Correções", open=False):
|
|
|
|
| 621 |
inputs=[caso_input, peticao_texto, resultado_output],
|
| 622 |
outputs=[download_file]
|
| 623 |
).then(
|
| 624 |
+
fn=lambda x: [gr.update(visible=True, value=x), gr.update(visible=True)],
|
| 625 |
outputs=[download_file, download_btn]
|
| 626 |
)
|
| 627 |
|
|
|
|
| 644 |
""")
|
| 645 |
gr.Markdown("""
|
| 646 |
### ℹ️ Modo de Treinamento Interativo
|
| 647 |
+
Ative para explicações detalhadas com exemplos e sugestões.
|
| 648 |
### 📋 Como usar
|
| 649 |
1. Gere ou cole o caso
|
| 650 |
2. Insira a petição
|
|
|
|
| 666 |
Crie um caso fictício detalhado para uma petição inicial cível, similar ao exemplo:
|
| 667 |
"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."
|
| 668 |
O caso deve:
|
| 669 |
+
1. Conter uma violação clara de direitos que justifique uma ação cível
|
| 670 |
2. Incluir valores específicos de danos materiais e/ou morais
|
| 671 |
+
3. Mencionar nomes completos, cidades e informações para identificar as partes
|
| 672 |
+
4. Estabelecer relação entre as partes (contratual, consumo, etc.)
|
| 673 |
5. Ter complexidade adequada para uma petição inicial de nível OAB
|
| 674 |
+
6. Ter entre 15-25 linhas
|
| 675 |
+
7. Focar em um tema aleatório: responsabilidade civil por acidente de consumo, inadimplemento contratual, dano ambiental, direitos da personalidade, vício de produto/serviço, ou danos patrimoniais
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 676 |
Termine com: "Situação problema: Como advogado(a) de [NOME DO CLIENTE], promova a demanda cabível."
|
| 677 |
"""
|
| 678 |
|