pm-assistant / modules /utils.py
igorsteixeira's picture
fix: 14 bug fixes across 10 modules
95064ac
Raw
History Blame Contribute Delete
5.82 kB
import logging
import re
import base64
import unicodedata
import streamlit as st
from modules.config import client_anthropic
# --------------- Prompt Enhancer (shared) ---------------
_ENHANCER_SYSTEM = (
"Voce e um especialista em engenharia de prompts. "
"Sua tarefa e aprimorar o prompt do usuario para obter a melhor resposta possivel "
"de um assistente de IA chamado J.A.R.V.I.S. (assistente de PM para squad de Risco e Seguranca do banco Cora).\n\n"
"CONTEXTO IMPORTANTE: O J.A.R.V.I.S. ja possui templates internos para PRDs, Tech Specs, "
"apresentacoes e outros documentos. Ele tambem carrega contexto automaticamente (OKRs, roadmap, "
"metricas, dados do squad). Voce NAO precisa sugerir templates, formatos ou estruturas — "
"foque APENAS em melhorar a clareza, especificidade e intencao do pedido do usuario.\n\n"
"Regras:\n"
"1. Mantenha a INTENCAO original do usuario intacta.\n"
"2. Adicione clareza, especificidade e detalhes relevantes (tom, audiencia, foco, exclusoes).\n"
"3. NAO sugira templates, formatos, estruturas de documento ou modelos — o sistema ja tem.\n"
"4. NAO adicione instrucoes tecnicas sobre como o modelo deve responder (ex: 'use markdown').\n"
"5. Se o prompt for trivial (saudacao, agradecimento, pergunta simples de 1 linha), "
"retorne-o como esta — nao force complexidade.\n"
"6. Responda APENAS com o prompt aprimorado, sem explicacoes ou meta-comentarios.\n"
"7. Mantenha o idioma original (portugues brasileiro).\n"
"8. Nao adicione emojis a menos que o original tenha."
)
_TRIVIAL_RE = re.compile(
r"^(oi|ola|hey|obrigad[oa]|valeu|ok|sim|nao|bom dia|boa tarde|boa noite|beleza|blz|fala|e ai)[!?.]*$",
re.IGNORECASE,
)
def aprimorar_prompt(texto: str) -> str:
"""Envia o prompt do usuario ao Claude Opus para aprimoramento."""
if _TRIVIAL_RE.match(texto.strip()):
return texto
try:
resp = client_anthropic.messages.create(
model="claude-opus-4-6",
max_tokens=2048,
system=_ENHANCER_SYSTEM,
messages=[{"role": "user", "content": texto}],
)
aprimorado = resp.content[0].text.strip()
return aprimorado if aprimorado else texto
except Exception as e:
logging.warning(f"[Enhancer] Falha ao aprimorar prompt: {e}")
return texto
def sanitizar(nome):
n = unicodedata.normalize("NFKD", nome).encode("ascii", "ignore").decode("utf-8")
return re.sub(r"[^a-zA-Z0-9._-]", "_", n)
def extrair_texto_arquivo(uploaded_file) -> str:
nome = uploaded_file.name.lower()
texto = ""
if nome.endswith((".png", ".jpg", ".jpeg")):
dados = base64.standard_b64encode(uploaded_file.read()).decode("utf-8")
media_type = "image/png" if nome.endswith(".png") else "image/jpeg"
resp = client_anthropic.messages.create(
model=st.session_state.modelo, max_tokens=2048,
messages=[{"role": "user", "content": [
{"type": "image", "source": {"type": "base64", "media_type": media_type, "data": dados}},
{"type": "text", "text": "Extraia e descreva todo o conteudo desta imagem detalhadamente."}
]}]
)
texto = resp.content[0].text
elif nome.endswith(".pdf"):
try:
import PyPDF2
reader = PyPDF2.PdfReader(uploaded_file)
pages_text = []
for i, page in enumerate(reader.pages, 1):
page_content = (page.extract_text() or "").strip()
if page_content:
pages_text.append(f"--- PAGINA {i} ---\n{page_content}")
texto = "\n\n".join(pages_text)
except Exception as e:
logging.warning(f"[Utils] Falha ao extrair PDF '{nome}': {e}")
texto = ""
elif nome.endswith(".docx"):
try:
import docx
doc = docx.Document(uploaded_file)
texto = "\n".join(p.text for p in doc.paragraphs if p.text.strip())
except Exception as e:
logging.warning(f"[Utils] Falha ao extrair DOCX '{nome}': {e}")
texto = ""
elif nome.endswith(".csv"):
try:
import pandas as pd
texto = pd.read_csv(uploaded_file).to_markdown()
except Exception as e:
logging.warning(f"[Utils] Falha ao ler CSV '{nome}' como markdown: {e}")
texto = uploaded_file.read().decode("utf-8", errors="ignore")
elif nome.endswith(".pptx"):
try:
from pptx import Presentation
prs = Presentation(uploaded_file)
slides_text = []
for i, slide in enumerate(prs.slides, 1):
parts = []
for shape in slide.shapes:
if shape.has_text_frame:
for para in shape.text_frame.paragraphs:
t = para.text.strip()
if t:
parts.append(t)
if shape.has_table:
table = shape.table
for row in table.rows:
row_text = " | ".join(cell.text.strip() for cell in row.cells)
if row_text.strip(" |"):
parts.append(row_text)
slides_text.append(f"--- SLIDE {i} ---\n" + ("\n".join(parts) if parts else "[Slide sem texto extraivel — pode conter imagem, grafico ou SmartArt]"))
texto = "\n\n".join(slides_text)
except Exception as e:
logging.warning(f"[Utils] Falha ao extrair PPTX '{nome}': {e}")
texto = ""
elif nome.endswith(".txt"):
texto = uploaded_file.read().decode("utf-8", errors="ignore")
return texto.strip()