Spaces:
Running
Running
| 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() | |