Spaces:
Running
Running
import streamlit as st | |
import streamlit_authenticator as stauth | |
import sqlite3 | |
import os | |
import jwt | |
from datetime import datetime, timedelta | |
from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer | |
import torch | |
from PIL import Image | |
import io | |
import librosa | |
import numpy as np | |
import logging | |
import tempfile | |
from streamlit.runtime.uploaded_file_manager import UploadedFile | |
from diffusers import StableDiffusionPipeline | |
import sentry_sdk | |
import streamlit.components.v1 as components | |
import mimetypes | |
mimetypes.add_type('application/javascript', '.js') | |
mimetypes.add_type('text/css', '.css') | |
# Configurar página | |
st.set_page_config( | |
page_title="Aplicação de IA Multi-Modal", | |
page_icon="🤖", | |
layout="wide" | |
) | |
# Configurar logging e Sentry | |
sentry_sdk.init(os.getenv('SENTRY_DSN', ''), traces_sample_rate=1.0) | |
logging.basicConfig( | |
filename='private/app_errors.log', | |
level=logging.ERROR, | |
format='%(asctime)s - %(levelname)s - %(message)s' | |
) | |
# Configurar banco de dados | |
def init_db(): | |
"""Inicializa o banco de dados SQLite com tabela de usuários""" | |
db_path = os.getenv('DB_PATH', 'private/users.db') | |
conn = sqlite3.connect(db_path) | |
cursor = conn.cursor() | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS users ( | |
username TEXT PRIMARY KEY, | |
name TEXT NOT NULL, | |
password TEXT NOT NULL, | |
role TEXT NOT NULL DEFAULT 'user' | |
) | |
''') | |
# Adicionar admin padrão (remover em produção) | |
try: | |
hashed_password = stauth.Hasher(['admin123']).generate()[0] | |
cursor.execute(''' | |
INSERT OR IGNORE INTO users (username, name, password, role) | |
VALUES (?, ?, ?, ?) | |
''', ('admin', 'Administrador', hashed_password, 'admin')) | |
conn.commit() | |
except Exception as e: | |
logging.error(f"Erro ao inicializar banco de dados: {e}") | |
sentry_sdk.capture_exception(e) | |
st.error(f"Erro ao inicializar banco de dados: {str(e)}") | |
conn.close() | |
def load_users_from_db(): | |
"""Carrega usuários do banco de dados SQLite""" | |
try: | |
db_path = os.getenv('DB_PATH', 'private/users.db') | |
conn = sqlite3.connect(db_path) | |
cursor = conn.cursor() | |
cursor.execute("SELECT username, name, password, role FROM users") | |
users = {row[0]: {'name': row[1], 'password': row[2], 'role': row[3]} for row in cursor.fetchall()} | |
conn.close() | |
config = { | |
'credentials': {'usernames': users}, | |
'cookie': {'name': 'ai_app_cookie', 'key': 'random_key_123', 'expiry_days': 30}, | |
'preauthorized': {'emails': []} | |
} | |
return config | |
except Exception as e: | |
st.error(f"Erro ao carregar usuários: {str(e)}") | |
logging.error(f"Erro ao carregar usuários: {e}") | |
sentry_sdk.capture_exception(e) | |
return None | |
# Cache para modelos | |
def load_model(model_key): | |
"""Carrega modelo específico com cache persistente""" | |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
cache_dir = "model_cache" | |
os.makedirs(cache_dir, exist_ok=True) | |
logging.info(f"Carregando modelo {model_key} em {device} com cache em {cache_dir}") | |
try: | |
if model_key == 'sentiment_analysis': | |
return pipeline("sentiment-analysis", model="cardiffnlp/twitter-roberta-base-sentiment-latest", device=device, cache_dir=cache_dir) | |
elif model_key == 'text_classification': | |
return pipeline("text-classification", model="distilbert-base-uncased-finetuned-sst-2-english", device=device, cache_dir=cache_dir) | |
elif model_key == 'summarization': | |
return pipeline("summarization", model="facebook/bart-large-cnn", device=device, max_length=150, min_length=30, cache_dir=cache_dir) | |
elif model_key == 'question_answering': | |
return pipeline("question-answering", model="deepset/roberta-base-squad2", device=device, cache_dir=cache_dir) | |
elif model_key == 'translation': | |
return pipeline("translation", model="Helsinki-NLP/opus-mt-tc-big-en-pt", device=device, cache_dir=cache_dir) | |
elif model_key == 'text_generation': | |
tokenizer = AutoTokenizer.from_pretrained("gpt2", cache_dir=cache_dir) | |
model = AutoModelForCausalLM.from_pretrained("gpt2", cache_dir=cache_dir) | |
model.config.pad_token_id = model.config.eos_token_id | |
return pipeline("text-generation", model=model, tokenizer=tokenizer, device=device) | |
elif model_key == 'ner': | |
return pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english", device=device, aggregation_strategy="simple", cache_dir=cache_dir) | |
elif model_key == 'image_classification': | |
return pipeline("image-classification", model="google/vit-base-patch16-224", device=device, cache_dir=cache_dir) | |
elif model_key == 'object_detection': | |
return pipeline("object-detection", model="facebook/detr-resnet-50", device=device, cache_dir=cache_dir) | |
elif model_key == 'image_segmentation': | |
return pipeline("image-segmentation", model="facebook/detr-resnet-50-panoptic", device=device, cache_dir=cache_dir) | |
elif model_key == 'facial_recognition': | |
return pipeline("image-classification", model="mo-thecreator/vit-Facial-Expression-Recognition", device=device, cache_dir=cache_dir) | |
elif model_key == 'speech_to_text': | |
return pipeline("automatic-speech-recognition", model="openai/whisper-base", device=device, cache_dir=cache_dir) | |
elif model_key == 'audio_classification': | |
return pipeline("audio-classification", model="superb/hubert-base-superb-er", device=device, cache_dir=cache_dir) | |
elif model_key == 'text_to_image': | |
return StableDiffusionPipeline.from_pretrained( | |
"runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16, use_safetensors=True, safety_checker=None, variant="fp16", cache_dir=cache_dir | |
) | |
except Exception as e: | |
st.error(f"Erro ao carregar modelo {model_key}: {str(e)}") | |
logging.error(f"Erro ao carregar modelo {model_key}: {e}") | |
sentry_sdk.capture_exception(e) | |
return None | |
def validate_audio_file(file: UploadedFile) -> bool: | |
"""Valida o arquivo de áudio""" | |
valid_extensions = ['.wav', '.mp3', '.flac', '.m4a'] | |
return any(file.name.lower().endswith(ext) for ext in valid_extensions) | |
def validate_image_file(file: UploadedFile) -> bool: | |
"""Valida o arquivo de imagem""" | |
valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp'] | |
if not any(file.name.lower().endswith(ext) for ext in valid_extensions): | |
return False | |
try: | |
Image.open(file).verify() | |
return True | |
except Exception: | |
return False | |
def process_audio_file(audio_file): | |
"""Processa arquivo de áudio para o formato correto""" | |
try: | |
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(audio_file.name)[1]) as tmp_file: | |
tmp_file.write(audio_file.read()) | |
tmp_file_path = tmp_file.name | |
audio_array, sample_rate = librosa.load(tmp_file_path, sr=16000) | |
os.unlink(tmp_file_path) | |
return audio_array | |
except Exception as e: | |
st.error(f"Erro ao processar áudio: {str(e)}") | |
logging.error(f"Erro no processamento de áudio: {e}") | |
sentry_sdk.capture_exception(e) | |
return None | |
def process_image_file(image_file): | |
"""Processa arquivo de imagem""" | |
try: | |
image = Image.open(image_file) | |
if image.mode != 'RGB': | |
image = image.convert('RGB') | |
return image | |
except Exception as e: | |
st.error(f"Erro ao processar imagem: {str(e)}") | |
logging.error(f"Erro no processamento de imagem: {e}") | |
sentry_sdk.capture_exception(e) | |
return None | |
def display_results(result, model_key, input_text=None): | |
"""Exibe resultados formatados de acordo com o tipo de modelo""" | |
if model_key == 'summarization': | |
st.subheader("📝 Resumo Gerado") | |
if input_text: | |
st.markdown("**Texto Original:**") | |
st.write(input_text) | |
st.markdown("**Resumo:**") | |
st.info(result[0]['summary_text']) | |
elif model_key == 'translation': | |
st.subheader("🌍 Tradução") | |
st.success(result[0]['translation_text']) | |
elif model_key in ['sentiment_analysis', 'text_classification']: | |
st.subheader("📊 Resultados") | |
for res in result: | |
label = res['label'] | |
score = res['score'] | |
st.progress(float(score), text=f"{label} ({score:.2%})") | |
elif model_key == 'ner': | |
st.subheader("🔍 Entidades Reconhecidas") | |
for entity in result: | |
st.write(f"- **{entity['word']}**: {entity['entity_group']} (confiança: {entity['score']:.2%})") | |
elif model_key == 'text_generation': | |
st.subheader("🧠 Texto Gerado") | |
st.write(result[0]['generated_text']) | |
elif model_key == 'image_classification': | |
st.subheader("🏷️ Classificação") | |
for res in result[:5]: | |
st.write(f"- **{res['label']}**: {res['score']:.2%}") | |
elif model_key == 'object_detection': | |
st.subheader("📦 Objetos Detectados") | |
for obj in result: | |
st.write(f"- {obj['label']} (confiança: {obj['score']:.2%})") | |
elif model_key == 'image_segmentation': | |
st.subheader("🧩 Segmentação") | |
st.image(result[0]['mask'], caption="Máscara de segmentação") | |
elif model_key == 'facial_recognition': | |
st.subheader("😊 Reconhecimento Facial") | |
top_result = result[0] | |
st.write(f"**Emoção predominante**: {top_result['label']} (confiança: {top_result['score']:.2%})") | |
elif model_key == 'speech_to_text': | |
st.subheader("🔈 Transcrição") | |
st.success(result['text']) | |
elif model_key == 'audio_classification': | |
st.subheader("🎧 Classificação de Áudio") | |
top_emotion = result[0] | |
st.write(f"**Emoção detectada**: {top_emotion['label']} (confiança: {top_emotion['score']:.2%})") | |
elif model_key == 'text_to_image': | |
st.subheader("🎨 Imagem Gerada") | |
st.image(result[0], caption="Imagem gerada a partir do texto") | |
def load_branding(username): | |
"""Carrega branding personalizado por usuário""" | |
branding = { | |
'admin': {'logo': 'logos/admin_logo.png', 'title': 'Bem-vindo, Administrador!'}, | |
'cliente': {'logo': 'logos/cliente_logo.png', 'title': 'Bem-vindo, Cliente!'}, | |
'empresa1': {'logo': 'logos/empresa1_logo.png', 'title': 'Bem-vindo, Empresa Um!'} | |
} | |
return branding.get(username, {'logo': None, 'title': 'Bem-vindo!'}) | |
def admin_panel(authenticator): | |
"""Painel administrativo para gerenciar usuários""" | |
if st.session_state.get('role') == 'admin': | |
st.sidebar.subheader("Painel Admin") | |
with st.sidebar.form("add_user_form"): | |
username = st.text_input("Username") | |
name = st.text_input("Nome") | |
password = st.text_input("Senha", type="password") | |
role = st.selectbox("Role", ["user", "admin"]) | |
if st.form_submit_button("Adicionar Usuário"): | |
hashed_password = stauth.Hasher([password]).generate()[0] | |
db_path = os.getenv('DB_PATH', 'private/users.db') | |
conn = sqlite3.connect(db_path) | |
cursor = conn.cursor() | |
try: | |
cursor.execute('INSERT INTO users (username, name, password, role) VALUES (?, ?, ?, ?)', | |
(username, name, hashed_password, role)) | |
conn.commit() | |
st.success(f"Usuário {username} adicionado!") | |
except sqlite3.IntegrityError: | |
st.error("Usuário já existe.") | |
except Exception as e: | |
st.error(f"Erro ao adicionar usuário: {str(e)}") | |
logging.error(f"Erro ao adicionar usuário: {e}") | |
sentry_sdk.capture_exception(e) | |
conn.close() | |
with st.sidebar.form("remove_user_form"): | |
username_to_remove = st.text_input("Username para Remover") | |
if st.form_submit_button("Remover Usuário"): | |
db_path = os.getenv('DB_PATH', 'private/users.db') | |
conn = sqlite3.connect(db_path) | |
cursor = conn.cursor() | |
try: | |
cursor.execute('DELETE FROM users WHERE username = ?', (username_to_remove,)) | |
conn.commit() | |
if cursor.rowcount > 0: | |
st.success(f"Usuário {username_to_remove} removido!") | |
else: | |
st.error("Usuário não encontrado.") | |
except Exception as e: | |
st.error(f"Erro ao remover usuário: {str(e)}") | |
logging.error(f"Erro ao remover usuário: {e}") | |
sentry_sdk.capture_exception(e) | |
conn.close() | |
def get_use_cases(): | |
"""Retorna os casos de uso para cada modelo""" | |
return { | |
'sentiment_analysis': { | |
'title': "Análise de Sentimento", | |
'description': "Analisa o sentimento (positivo, negativo, neutro) em comentários, avaliações ou postagens de clientes em redes sociais.", | |
'example': "Uma empresa de varejo monitora menções da marca no Twitter/X, identificando feedback negativo para responder proativamente ou destacando comentários positivos em campanhas de marketing.", | |
'benefit': "Melhoria na gestão de reputação online e resposta rápida a crises de imagem.", | |
'demo_input': "A entrega foi super rápida, adorei!", | |
'demo_type': 'text' | |
}, | |
# Outros casos de uso mantidos idênticos ao original | |
'text_classification': { | |
'title': "Classificação de Texto", | |
'description': "Classifica e-mails recebidos como positivos ou negativos para priorizar respostas ou identificar reclamações.", | |
'example': "Um call center categoriza e-mails de clientes, direcionando mensagens negativas para equipes de suporte prioritário.", | |
'benefit': "Otimização do tempo da equipe de atendimento e melhoria na experiência do cliente.", | |
'demo_input': "Estou insatisfeito com o produto", | |
'demo_type': 'text' | |
}, | |
'summarization': { | |
'title': "Resumo de Texto", | |
'description': "Gera resumos concisos de documentos longos, como relatórios financeiros ou atas de reuniões.", | |
'example': "Uma consultoria financeira resume relatórios anuais de empresas em poucos parágrafos para facilitar a análise de investidores.", | |
'benefit': "Economia de tempo na leitura de documentos extensos e tomada de decisão mais rápida.", | |
'demo_input': "A empresa XYZ reportou um crescimento de 15% no último trimestre, impulsionado por novas parcerias estratégicas e expansão no mercado asiático. No entanto, desafios logísticos aumentaram os custos operacionais em 5%. A diretoria planeja investir em automação para mitigar esses custos no próximo ano.", | |
'demo_type': 'text' | |
}, | |
'question_answering': { | |
'title': "Perguntas e Respostas", | |
'description': "Responde perguntas específicas com base em manuais, FAQs ou documentos internos.", | |
'example': "Um chatbot de suporte técnico responde perguntas como 'Como configurar o produto X?' extraindo respostas diretamente do manual do produto.", | |
'benefit': "Redução do tempo de suporte e maior autonomia para os usuários finais.", | |
'demo_input': { | |
'context': "O produto X tem garantia de 2 anos e pode ser configurado via aplicativo móvel em 5 minutos.", | |
'question': "Qual é o tempo de garantia do produto X?" | |
}, | |
'demo_type': 'qa' | |
}, | |
'translation': { | |
'title': "Tradução (EN→PT)", | |
'description': "Traduz conteúdo de marketing, manuais ou comunicações de inglês para português.", | |
'example': "Uma empresa de software traduz descrições de produtos para lançar no mercado brasileiro.", | |
'benefit': "Expansão de mercado com conteúdo adaptado e redução de custos com tradutores humanos.", | |
'demo_input': "Our product ensures high performance", | |
'demo_type': 'text' | |
}, | |
'ner': { | |
'title': "Reconhecimento de Entidades", | |
'description': "Identifica entidades como nomes de pessoas, organizações e locais em contratos ou documentos legais.", | |
'example': "Um escritório de advocacia extrai automaticamente nomes de partes envolvidas em contratos, agilizando revisões.", | |
'benefit': "Redução de erros manuais e maior eficiência na análise de documentos.", | |
'demo_input': "Microsoft assinou um contrato com a empresa XYZ em Nova York.", | |
'demo_type': 'text' | |
}, | |
'text_generation': { | |
'title': "Geração de Texto", | |
'description': "Gera textos criativos para campanhas de marketing, postagens em redes sociais ou roteiros.", | |
'example': "Uma agência de publicidade cria slogans ou descrições de produtos a partir de prompts iniciais.", | |
'benefit': "Aceleração do processo criativo e geração de ideias inovadoras.", | |
'demo_input': "Um futuro onde a tecnologia conecta todos", | |
'demo_type': 'text' | |
}, | |
'image_classification': { | |
'title': "Classificação de Imagem", | |
'description': "Identifica defeitos ou classifica produtos em linhas de produção com base em imagens.", | |
'example': "Uma fábrica de eletrônicos classifica imagens de circuitos como 'Defeituoso' ou 'Aprovado' para controle de qualidade.", | |
'benefit': "Redução de erros humanos e aumento da eficiência na inspeção.", | |
'demo_input': None, | |
'demo_type': 'image' | |
}, | |
'object_detection': { | |
'title': "Detecção de Objetos", | |
'description': "Detecta objetos como pessoas, veículos ou itens em imagens de câmeras de segurança.", | |
'example': "Um sistema de segurança identifica veículos em um estacionamento para monitoramento automático.", | |
'benefit': "Maior segurança e automação de processos de monitoramento.", | |
'demo_input': None, | |
'demo_type': 'image' | |
}, | |
'image_segmentation': { | |
'title': "Segmentação de Imagem", | |
'description': "Segmenta diferentes partes de uma imagem, como órgãos em exames médicos.", | |
'example': "Um hospital segmenta tumores em imagens de ressonância magnética, facilitando diagnósticos.", | |
'benefit': "Apoio a diagnósticos médicos com maior precisão e rapidez.", | |
'demo_input': None, | |
'demo_type': 'image' | |
}, | |
'facial_recognition': { | |
'title': "Reconhecimento Facial", | |
'description': "Identifica emoções faciais em vídeos ou fotos de clientes em lojas ou eventos.", | |
'example': "Uma loja de varejo analisa expressões faciais de clientes para avaliar a satisfação durante interações com produtos.", | |
'benefit': "Melhoria na experiência do cliente com base em dados emocionais.", | |
'demo_input': None, | |
'demo_type': 'image' | |
}, | |
'speech_to_text': { | |
'title': "Transcrição de Áudio", | |
'description': "Converte gravações de reuniões ou entrevistas em texto para documentação.", | |
'example': "Uma empresa transcreve automaticamente reuniões para criar atas ou resumos.", | |
'benefit': "Economia de tempo na documentação e maior acessibilidade de conteúdo.", | |
'demo_input': None, | |
'demo_type': 'audio' | |
}, | |
'audio_classification': { | |
'title': "Classificação de Áudio", | |
'description': "Classifica emoções em chamadas de suporte para avaliar a qualidade do atendimento.", | |
'example': "Um call center analisa chamadas para identificar emoções como 'Frustração' ou 'Satisfação' dos clientes.", | |
'benefit': "Melhoria na formação de equipes e na experiência do cliente.", | |
'demo_input': None, | |
'demo_type': 'audio' | |
}, | |
'text_to_image': { | |
'title': "Texto para Imagem", | |
'description': "Gera imagens personalizadas a partir de descrições textuais para campanhas publicitárias ou design de produtos.", | |
'example': "Uma agência de design cria mockups de produtos com base em prompts como 'Um smartphone futurista em um fundo azul neon'.", | |
'benefit': "Redução de custos com designers gráficos e maior agilidade na criação de conteúdo visual.", | |
'demo_input': "Uma paisagem tropical ao pôr do sol", | |
'demo_type': 'text' | |
} | |
} | |
def handle_use_case_demo(models, use_case_key, use_case): | |
"""Executa a demonstração de um caso de uso com entrada pré-definida""" | |
if use_case['demo_input'] is None: | |
st.warning("⚠️ Demonstração não disponível. Este modelo requer upload de imagem ou áudio.") | |
return | |
st.subheader("📊 Demonstração") | |
try: | |
model = models.get(use_case_key) or load_model(use_case_key) | |
if use_case['demo_type'] == 'text': | |
with st.spinner("Processando demonstração..."): | |
result = model(use_case['demo_input']) | |
display_results(result, use_case_key, input_text=use_case['demo_input']) | |
elif use_case['demo_type'] == 'qa': | |
with st.spinner("Processando demonstração..."): | |
result = model( | |
question=use_case['demo_input']['question'], | |
context=use_case['demo_input']['context'] | |
) | |
st.success("🔍 Resposta encontrada:") | |
st.markdown(f"**Contexto:** {use_case['demo_input']['context']}") | |
st.markdown(f"**Pergunta:** {use_case['demo_input']['question']}") | |
st.markdown(f"**Resposta:** {result['answer']}") | |
st.markdown(f"**Confiança:** {result['score']:.2%}") | |
except Exception as e: | |
st.error(f"Erro ao executar demonstração: {str(e)}") | |
logging.error(f"Erro na demonstração do caso de uso {use_case_key}: {e}") | |
sentry_sdk.capture_exception(e) | |
def main(): | |
# Inicializar banco de dados | |
init_db() | |
# Carregar configuração de autenticação | |
config = load_users_from_db() | |
if not config: | |
st.error("Falha ao carregar autenticação. Contate o administrador.") | |
return | |
authenticator = stauth.Authenticate( | |
config['credentials'], | |
config['cookie']['name'], | |
config['cookie']['key'], | |
config['cookie']['expiry_days'], | |
config['preauthorized'] | |
) | |
# Tela de login | |
name, authentication_status, username = authenticator.login('Login', 'main') | |
if authentication_status: | |
# Gerenciar sessão | |
if "user_id" not in st.session_state: | |
st.session_state.user_id = username | |
st.session_state.role = config['credentials']['usernames'][username]['role'] | |
# Carregar branding | |
branding = load_branding(username) | |
if branding['logo'] and os.path.exists(branding['logo']): | |
st.image(branding['logo'], width=150) | |
st.title(branding['title']) | |
authenticator.logout('Logout', 'sidebar') | |
admin_panel(authenticator) | |
# Tour guiado | |
if st.sidebar.button("Iniciar Tour"): | |
components.html(""" | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/shepherd.min.js"></script> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/shepherd.css"/> | |
<script> | |
const tour = new Shepherd.Tour({ | |
defaultStepOptions: { scrollTo: true, cancelIcon: { enabled: true } }, | |
steps: [ | |
{ id: 'login', title: 'Login', text: 'Faça login com suas credenciais.', attachTo: { element: '#login', on: 'bottom' } }, | |
{ id: 'tab1', title: 'Explorar Modelos', text: 'Teste modelos de IA.', attachTo: { element: '#tab1', on: 'bottom' } }, | |
{ id: 'tab2', title: 'Casos de Uso', text: 'Explore aplicações práticas.', attachTo: { element: '#tab2', on: 'bottom' } } | |
] | |
}); | |
tour.start(); | |
</script> | |
""", height=0) | |
# Carregar modelos sob demanda | |
models = {} | |
model_categories = { | |
"📝 Processamento de Texto": [ | |
("Análise de Sentimento", "sentiment_analysis"), | |
("Classificação de Texto", "text_classification"), | |
("Resumo de Texto", "summarization"), | |
("Perguntas e Respostas", "question_answering"), | |
("Tradução (EN→PT)", "translation"), | |
("Reconhecimento de Entidades", "ner"), | |
("Geração de Texto", "text_generation") | |
], | |
"🖼️ Processamento de Imagem": [ | |
("Classificação de Imagem", "image_classification"), | |
("Detecção de Objetos", "object_detection"), | |
("Segmentação de Imagem", "image_segmentation"), | |
("Reconhecimento Facial", "facial_recognition") | |
], | |
"🎵 Processamento de Áudio": [ | |
("Transcrição de Áudio", "speech_to_text"), | |
("Classificação de Emoções", "audio_classification") | |
], | |
"✨ Modelos Generativos": [ | |
("Texto para Imagem", "text_to_image") | |
] | |
} | |
# Abas para navegação | |
tab1, tab2 = st.tabs(["Explorar Modelos", "Casos de Uso"]) | |
with tab1: | |
st.sidebar.title("⚙️ Configurações") | |
selected_category = st.sidebar.selectbox( | |
"Categoria", | |
list(model_categories.keys()), | |
index=0 | |
) | |
selected_model = st.sidebar.selectbox( | |
"Modelo", | |
[name for name, key in model_categories[selected_category]], | |
format_func=lambda x: x, | |
index=0 | |
) | |
model_key = next(key for name, key in model_categories[selected_category] if name == selected_model) | |
# Carregar modelo com barra de progresso | |
if model_key not in models: | |
with st.spinner(f"Carregando modelo {selected_model}..."): | |
progress = st.progress(0) | |
models[model_key] = load_model(model_key) | |
progress.progress(100) | |
st.header(f"{selected_model}") | |
with st.expander("ℹ️ Sobre este modelo"): | |
model_info = { | |
'sentiment_analysis': "Analisa o sentimento expresso em um texto (positivo/negativo/neutro).", | |
'text_classification': "Classifica textos em categorias pré-definidas.", | |
'summarization': "Gera um resumo conciso de um texto longo.", | |
'question_answering': "Responde perguntas baseadas em um contexto fornecido.", | |
'translation': "Traduz texto de inglês para português.", | |
'ner': "Identifica e classifica entidades nomeadas (pessoas, lugares, organizações).", | |
'text_generation': "Gera texto criativo continuando a partir de um prompt.", | |
'image_classification': "Identifica objetos e cenas em imagens.", | |
'object_detection': "Detecta e localiza múltiplos objetos em uma imagem.", | |
'image_segmentation': "Segmenta diferentes elementos em uma imagem.", | |
'facial_recognition': "Reconhece características faciais e emoções.", | |
'speech_to_text': "Transcreve fala em texto.", | |
'audio_classification': "Classifica emoções em arquivos de áudio.", | |
'text_to_image': "Gera imagens a partir de descrições textuais." | |
} | |
st.info(model_info.get(model_key, "Informações detalhadas sobre este modelo.")) | |
try: | |
if model_key in ['sentiment_analysis', 'text_classification', 'summarization', | |
'translation', 'text_generation', 'ner']: | |
handle_text_models(models, model_key, selected_model) | |
elif model_key == 'question_answering': | |
handle_qa_model(models, model_key) | |
elif model_key in ['image_classification', 'object_detection', | |
'image_segmentation', 'facial_recognition']: | |
handle_image_models(models, model_key, selected_model) | |
elif model_key in ['speech_to_text', 'audio_classification']: | |
handle_audio_models(models, model_key) | |
elif model_key == 'text_to_image': | |
handle_generative_models(models, model_key) | |
except Exception as e: | |
st.error(f"Erro inesperado durante a execução: {str(e)}") | |
logging.error(f"Erro durante a execução do modelo {model_key}: {e}") | |
sentry_sdk.capture_exception(e) | |
with tab2: | |
st.header("Casos de Uso") | |
st.markdown("Explore casos práticos de aplicação dos modelos para resolver problemas reais.") | |
use_cases = get_use_cases() | |
selected_use_case = st.selectbox( | |
"Selecione um caso de uso", | |
list(use_cases.keys()), | |
format_func=lambda x: use_cases[x]['title'] | |
) | |
use_case = use_cases[selected_use_case] | |
st.subheader(use_case['title']) | |
with st.expander("ℹ️ Detalhes do Caso de Uso"): | |
st.markdown(f"**Descrição**: {use_case['description']}") | |
st.markdown(f"**Exemplo Prático**: {use_case['example']}") | |
st.markdown(f"**Benefício**: {use_case['benefit']}") | |
if use_case['demo_input'] is not None: | |
if st.button("🚀 Executar Demonstração", key=f"demo_{selected_use_case}"): | |
handle_use_case_demo(models, selected_use_case, use_case) | |
def handle_text_models(models, model_key, model_name): | |
"""Manipula modelos de texto""" | |
input_text = st.text_area( | |
f"Digite o texto para {model_name.lower()}:", | |
height=200, | |
placeholder="Cole ou digite seu texto aqui...", | |
key=f"text_input_{model_key}" | |
) | |
advanced_params = {} | |
if model_key == 'summarization': | |
with st.expander("⚙️ Parâmetros Avançados"): | |
advanced_params['max_length'] = st.slider("Comprimento máximo", 50, 300, 150) | |
advanced_params['min_length'] = st.slider("Comprimento mínimo", 10, 100, 30) | |
if model_key == 'text_generation': | |
with st.expander("⚙️ Parâmetros Avançados"): | |
advanced_params['max_length'] = st.slider("Comprimento do texto", 50, 500, 100) | |
advanced_params['temperature'] = st.slider("Criatividade", 0.1, 1.0, 0.7) | |
advanced_params['num_return_sequences'] = st.slider("Número de resultados", 1, 5, 1) | |
if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_{model_key}"): | |
if input_text.strip(): | |
with st.spinner("Processando..."): | |
try: | |
model = models.get(model_key) or load_model(model_key) | |
if model_key == 'ner': | |
result = model(input_text) | |
elif model_key == 'text_generation': | |
result = model( | |
input_text, | |
max_new_tokens=advanced_params.get('max_length', 100), | |
do_sample=True, | |
temperature=advanced_params.get('temperature', 0.7), | |
top_k=50, | |
top_p=0.95, | |
num_return_sequences=advanced_params.get('num_return_sequences', 1) | |
) | |
else: | |
result = model(input_text, **advanced_params) | |
display_results(result, model_key, input_text=input_text) | |
except Exception as e: | |
st.error(f"Erro ao processar texto: {str(e)}") | |
logging.error(f"Erro no modelo {model_key}: {e}") | |
sentry_sdk.capture_exception(e) | |
else: | |
st.warning("⚠️ Por favor, insira um texto válido.") | |
def handle_qa_model(models, model_key): | |
"""Manipula modelo de Q&A""" | |
col1, col2 = st.columns(2) | |
with col1: | |
context = st.text_area( | |
"Contexto:", | |
height=200, | |
placeholder="Cole o texto que contém a informação...", | |
key="qa_context" | |
) | |
with col2: | |
question = st.text_area( | |
"Pergunta:", | |
height=150, | |
placeholder="Faça sua pergunta sobre o contexto...", | |
key="qa_question" | |
) | |
with st.expander("⚙️ Parâmetros Avançados"): | |
confidence_threshold = st.slider("Limite de confiança", 0.0, 1.0, 0.5, 0.01) | |
if st.button("🚀 Executar Pergunta e Resposta", type="primary", key="btn_qa"): | |
if context.strip() and question.strip(): | |
with st.spinner("Buscando resposta..."): | |
try: | |
model = models.get(model_key) or load_model(model_key) | |
result = model(question=question, context=context) | |
if result['score'] < confidence_threshold: | |
st.warning(f"⚠️ Confiança baixa na resposta ({result['score']:.2%})") | |
st.success("🔍 Resposta encontrada:") | |
st.markdown(f"**Contexto:** {context}") | |
st.markdown(f"**Pergunta:** {question}") | |
st.markdown(f"**Resposta:** {result['answer']}") | |
st.markdown(f"**Confiança:** {result['score']:.2%}") | |
except Exception as e: | |
st.error(f"Erro ao processar Q&A: {str(e)}") | |
logging.error(f"Erro no modelo Q&A: {e}") | |
sentry_sdk.capture_exception(e) | |
else: | |
st.warning("⚠️ Por favor, forneça tanto o contexto quanto a pergunta.") | |
def handle_image_models(models, model_key, model_name): | |
"""Manipula modelos de imagem""" | |
uploaded_file = st.file_uploader( | |
"Carregue uma imagem", | |
type=["jpg", "png", "jpeg", "bmp"], | |
help="Formatos suportados: JPG, PNG, JPEG, BMP", | |
key=f"img_upload_{model_key}" | |
) | |
if uploaded_file is not None: | |
if not validate_image_file(uploaded_file): | |
st.error("⚠️ Formato de arquivo inválido ou arquivo corrompido.") | |
return | |
col1, col2 = st.columns(2) | |
with col1: | |
st.subheader("🖼️ Imagem Original") | |
image = process_image_file(uploaded_file) | |
if image: | |
st.image(image) | |
with col2: | |
st.subheader("📊 Resultados") | |
if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_img_{model_key}"): | |
if image: | |
with st.spinner("Analisando imagem..."): | |
try: | |
model = models.get(model_key) or load_model(model_key) | |
result = model(image) | |
display_results(result, model_key) | |
except Exception as e: | |
st.error(f"Erro ao processar imagem: {str(e)}") | |
logging.error(f"Erro no modelo {model_key}: {e}") | |
sentry_sdk.capture_exception(e) | |
def handle_audio_models(models, model_key): | |
"""Manipula modelos de áudio""" | |
model_name = "Transcrição de Áudio" if model_key == 'speech_to_text' else "Classificação de Áudio" | |
uploaded_file = st.file_uploader( | |
f"Carregue um arquivo de áudio para {model_name}", | |
type=["wav", "mp3", "flac", "m4a"], | |
help="Formatos suportados: WAV, MP3, FLAC, M4A", | |
key=f"audio_upload_{model_key}" | |
) | |
if uploaded_file is not None: | |
if not validate_audio_file(uploaded_file): | |
st.error("⚠️ Formato de arquivo inválido ou não suportado.") | |
return | |
st.audio(uploaded_file, format="audio/wav") | |
if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_audio_{model_key}"): | |
with st.spinner("Processando áudio..."): | |
try: | |
audio_array = process_audio_file(uploaded_file) | |
if audio_array is not None: | |
model = models.get(model_key) or load_model(model_key) | |
result = model(audio_array) | |
display_results(result, model_key) | |
else: | |
st.error("Não foi possível processar o arquivo de áudio.") | |
except Exception as e: | |
st.error(f"Erro ao processar áudio: {str(e)}") | |
logging.error(f"Erro no modelo {model_key}: {e}") | |
sentry_sdk.capture_exception(e) | |
def handle_generative_models(models, model_key): | |
"""Manipula modelos generativos""" | |
prompt = st.text_area( | |
"Descrição da imagem:", | |
height=150, | |
placeholder="Descreva a imagem que deseja gerar...", | |
key="text_to_image_prompt" | |
) | |
with st.expander("⚙️ Parâmetros Avançados"): | |
cols = st.columns(2) | |
with cols[0]: | |
width = st.slider("Largura", 256, 1024, 512, 64) | |
with cols[1]: | |
height = st.slider("Altura", 256, 1024, 512, 64) | |
num_images = st.slider("Número de imagens", 1, 4, 1) | |
guidance_scale = st.slider("Escala de orientação", 1.0, 20.0, 7.5) | |
if st.button("🚀 Gerar Imagem", type="primary", key="btn_text_to_image"): | |
if prompt.strip(): | |
with st.spinner("Criando imagem..."): | |
try: | |
model = models.get(model_key) or load_model(model_key) | |
result = model( | |
prompt, | |
height=height, | |
width=width, | |
num_images_per_prompt=num_images, | |
guidance_scale=guidance_scale | |
) | |
display_results(result, model_key) | |
except Exception as e: | |
st.error(f"Erro ao gerar imagem: {str(e)}") | |
logging.error(f"Erro no modelo text-to-image: {e}") | |
sentry_sdk.capture_exception(e) | |
else: | |
st.warning("⚠️ Por favor, insira uma descrição para a imagem.") | |
if __name__ == "__main__": | |
main() |