Spaces:
Sleeping
Sleeping
import streamlit as st | |
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 | |
from supabase import create_client, Client | |
import types | |
import pandas as pd | |
import plotly.express as px | |
from typing import Dict, Any | |
# --- Configuração Inicial --- | |
st.set_page_config( | |
page_title="AiiT Services - Plataforma de IA", | |
page_icon="🤖", | |
layout="wide", | |
initial_sidebar_state="expanded" | |
) | |
# --- Estilos Personalizados --- | |
st.markdown(""" | |
<style> | |
:root { | |
--primary: #7f5af0; /* Roxo neon vibrante */ | |
--secondary: #2cb67d; /* Verde menta brilhante */ | |
--accent: #f25f4c; /* Laranja coral vibrante */ | |
--light: #fdfdfd; /* Branco suave */ | |
--dark: #16161a; /* Preto carvão elegante */ | |
--success: #2cb67d; /* Verde sucesso */ | |
--info: #3d5afe; /* Azul elétrico */ | |
} | |
.main { | |
background-color: #f5f7ff; /* Cor de fundo geral */ | |
} | |
.stApp { | |
background-image: linear-gradient(120deg, #0f0c29, #302b63, #24243e); /* Fundo roxo/azul degradê */ | |
} | |
.header { | |
color: var(--light); | |
border-bottom: 2px solid var(--primary); | |
padding-bottom: 10px; | |
} | |
.card { | |
background: var(--light); | |
border-radius: 12px; | |
box-shadow: 0 4px 20px rgba(127, 90, 240, 0.2); | |
padding: 20px; | |
margin-bottom: 20px; | |
transition: transform 0.3s, box-shadow 0.3s; | |
} | |
.card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 6px 25px rgba(127, 90, 240, 0.4); | |
} | |
.feature-icon { | |
font-size: 2.8rem; | |
margin-bottom: 15px; | |
color: var(--primary); | |
} | |
.btn-primary { | |
background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
border: none; | |
color: white; | |
border-radius: 10px; | |
padding: 10px 20px; | |
font-weight: 600; | |
transition: all 0.3s; | |
} | |
.btn-primary:hover { | |
transform: scale(1.05); | |
box-shadow: 0 6px 15px rgba(44, 182, 125, 0.5); | |
} | |
.result-box { | |
background: linear-gradient(135deg, #e0f7fa, #b2ebf2); | |
border-radius: 12px; | |
padding: 20px; | |
margin-top: 20px; | |
} | |
.demo-box { | |
background: linear-gradient(135deg, #f1f8e9, #dcedc8); | |
border-radius: 12px; | |
padding: 20px; | |
margin-top: 20px; | |
} | |
.use-case-card { | |
background: var(--light); | |
border-radius: 12px; | |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); | |
padding: 20px; | |
margin-bottom: 20px; | |
border-left: 4px solid var(--accent); | |
transition: all 0.3s; | |
} | |
.use-case-card:hover { | |
transform: translateX(5px); | |
box-shadow: 0 6px 20px rgba(242, 95, 76, 0.2); | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# --- Monkey-patch para compatibilidade --- | |
st.context = types.SimpleNamespace(cookies={}) | |
import streamlit_authenticator as stauth | |
# --- Configuração de 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' | |
) | |
# --- Configuração do Supabase --- | |
SUPABASE_URL = os.getenv("SUPABASE_URL") | |
SUPABASE_KEY = os.getenv("SUPABASE_KEY") | |
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) if SUPABASE_URL and SUPABASE_KEY else None | |
# --- Funções de Banco de Dados --- | |
def connect_db(): | |
"""Conecta ao Supabase""" | |
if not supabase: | |
st.error("❌ Configuração do Supabase ausente") | |
return None | |
return supabase | |
def init_db(): | |
"""Inicializa a tabela de usuários no Supabase""" | |
try: | |
sb = connect_db() | |
if not sb: | |
return | |
_ = sb.table("users").select("username").limit(1).execute() | |
except Exception as e: | |
logging.error(f"Erro ao inicializar banco de dados: {e}") | |
def load_users_from_db(): | |
"""Carrega usuários do Supabase""" | |
try: | |
sb = connect_db() | |
if not sb: | |
return None | |
resp = sb.table("users").select("username, name, password, role").execute() | |
rows = resp.data if hasattr(resp, 'data') else [] | |
users = { | |
row['username']: { | |
'name': row['name'], | |
'password': row['password'], | |
'role': row['role'] | |
} | |
for row in rows | |
} | |
return { | |
'credentials': {'usernames': users}, | |
'cookie': {'name': 'ai_app_cookie', 'key': 'random_key_123', 'expiry_days': 30}, | |
'preauthorized': {'emails': []} | |
} | |
except Exception as e: | |
logging.error(f"Erro ao carregar usuários: {e}") | |
return None | |
def admin_panel(authenticator): | |
sb = connect_db() | |
if not sb or st.session_state.get('role') != 'admin': | |
st.warning("⚠️ Apenas administradores têm acesso") | |
return | |
st.sidebar.title("⚙️ Painel Admin") | |
with st.sidebar.expander("➕ Adicionar Usuário"): | |
with st.form("add_user_form"): | |
u = st.text_input("Username") | |
n = st.text_input("Nome") | |
p = st.text_input("Senha", type="password") | |
r = st.selectbox("Role", ["user", "admin"]) | |
if st.form_submit_button("Adicionar", use_container_width=True): | |
try: | |
hashed = stauth.Hasher([p]).generate()[0] | |
sb.table("users").insert({"username": u, "name": n, "password": hashed, "role": r}).execute() | |
st.success(f"✅ Usuário '{u}' criado!") | |
except Exception as e: | |
st.error(f"❌ Erro ao adicionar usuário: {str(e)}") | |
with st.sidebar.expander("🗑️ Remover Usuário"): | |
with st.form("remove_user_form"): | |
ur = st.text_input("Username para remover") | |
if st.form_submit_button("Remover", use_container_width=True): | |
try: | |
sb.table("users").delete().eq("username", ur).execute() | |
st.success(f"✅ Usuário '{ur}' removido!") | |
except Exception as e: | |
st.error(f"❌ Erro ao remover usuário: {str(e)}") | |
with st.sidebar.expander("🔄 Atualizar Role"): | |
with st.form("update_role_form"): | |
uu = st.text_input("Username para atualizar") | |
nr = st.selectbox("Novo Role", ["user", "admin"]) | |
if st.form_submit_button("Atualizar", use_container_width=True): | |
try: | |
sb.table("users").update({"role": nr}).eq("username", uu).execute() | |
st.success(f"✅ Role de '{uu}' alterado para '{nr}'") | |
except Exception as e: | |
st.error(f"❌ Erro ao atualizar role: {str(e)}") | |
st.sidebar.subheader("👥 Usuários Cadastrados") | |
try: | |
resp = sb.table("users").select("username,name,role").execute() | |
for row in resp.data or []: | |
st.sidebar.info(f"**{row['username']}** ({row['name']}) - `{row['role']}`") | |
except Exception as e: | |
st.error(f"❌ Erro ao listar usuários: {str(e)}") | |
# --- 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) | |
model_loaders = { | |
'sentiment_analysis': lambda: pipeline("sentiment-analysis", model="cardiffnlp/twitter-roberta-base-sentiment-latest", device=device), | |
'text_classification': lambda: pipeline("text-classification", model="distilbert-base-uncased-finetuned-sst-2-english", device=device), | |
'summarization': lambda: pipeline("summarization", model="facebook/bart-large-cnn", device=device, max_length=150, min_length=30), | |
'question_answering': lambda: pipeline("question-answering", model="deepset/roberta-base-squad2", device=device), | |
'translation': lambda: pipeline("translation", model="Helsinki-NLP/opus-mt-tc-big-en-pt", device=device), | |
'text_generation': lambda: pipeline("text-generation", model="gpt2", device=device, pad_token_id=50256), | |
'ner': lambda: pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english", device=device, aggregation_strategy="simple"), | |
'image_classification': lambda: pipeline("image-classification", model="google/vit-base-patch16-224", device=device), | |
'object_detection': lambda: pipeline("object-detection", model="facebook/detr-resnet-50", device=device), | |
'image_segmentation': lambda: pipeline("image-segmentation", model="facebook/detr-resnet-50-panoptic", device=device), | |
'facial_recognition': lambda: pipeline("image-classification", model="mo-thecreator/vit-Facial-Expression-Recognition", device=device), | |
'speech_to_text': lambda: pipeline("automatic-speech-recognition", model="openai/whisper-base", device=device), | |
'audio_classification': lambda: pipeline("audio-classification", model="superb/hubert-base-superb-er", device=device), | |
'text_to_image': lambda: StableDiffusionPipeline.from_pretrained( | |
"runwayml/stable-diffusion-v1-5", torch_dtype=torch.float32, use_safetensors=True, safety_checker=None, cache_dir=cache_dir | |
).to(device) | |
} | |
if model_key in model_loaders: | |
try: | |
return model_loaders[model_key]() | |
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 | |
# --- Funções de Processamento --- | |
def validate_audio_file(file: UploadedFile) -> bool: | |
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: | |
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): | |
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}") | |
return None | |
def process_image_file(image_file): | |
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}") | |
return None | |
# --- Funções de Exibição de Resultados --- | |
def display_results(result, model_key, input_text=None): | |
st.markdown("""<div class='result-box'>""", unsafe_allow_html=True) | |
if model_key == 'summarization': | |
st.subheader("📝 Resumo Gerado") | |
if input_text: | |
with st.expander("🔍 Texto Original"): | |
st.write(input_text) | |
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") | |
df = pd.DataFrame(result) | |
df['score'] = df['score'].apply(lambda x: f"{x:.2%}") | |
fig = px.bar(df, x='label', y='score', color='label', | |
labels={'score': 'Confiança', 'label': 'Categoria'}, | |
title="Distribuição de Probabilidades") | |
st.plotly_chart(fig, use_container_width=True) | |
for _, row in df.iterrows(): | |
st.progress(float(row['score'].strip('%'))/100, text=f"{row['label']} ({row['score']})") | |
elif model_key == 'ner': | |
st.subheader("🔍 Entidades Reconhecidas") | |
entities = [] | |
for entity in result: | |
entities.append({ | |
"Entidade": entity['word'], | |
"Tipo": entity['entity_group'], | |
"Confiança": f"{entity['score']:.2%}" | |
}) | |
st.table(pd.DataFrame(entities)) | |
elif model_key == 'text_generation': | |
st.subheader("🧠 Texto Gerado") | |
st.write(result[0]['generated_text']) | |
elif model_key == 'image_classification': | |
st.subheader("🏷️ Classificação") | |
top5 = result[:5] | |
labels = [res['label'] for res in top5] | |
scores = [res['score'] for res in top5] | |
fig = px.bar(x=scores, y=labels, orientation='h', | |
labels={'x': 'Confiança', 'y': 'Categoria'}, | |
title="Top 5 Classificações") | |
st.plotly_chart(fig, use_container_width=True) | |
elif model_key == 'object_detection': | |
st.subheader("📦 Objetos Detectados") | |
objects = [] | |
for obj in result: | |
objects.append({ | |
"Objeto": obj['label'], | |
"Confiança": f"{obj['score']:.2%}" | |
}) | |
st.table(pd.DataFrame(objects)) | |
elif model_key == 'image_segmentation': | |
st.subheader("🧩 Segmentação") | |
st.image(result[0]['mask'], caption="Máscara de segmentação", use_column_width=True) | |
elif model_key == 'facial_recognition': | |
st.subheader("😊 Reconhecimento Facial") | |
df = pd.DataFrame(result[:3]) | |
df['score'] = df['score'].apply(lambda x: f"{x:.2%}") | |
st.table(df[['label', 'score']]) | |
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") | |
df = pd.DataFrame(result[:3]) | |
df['score'] = df['score'].apply(lambda x: f"{x:.2%}") | |
st.table(df[['label', 'score']]) | |
elif model_key == 'text_to_image': | |
st.subheader("🎨 Imagem Gerada") | |
st.image(result[0], caption="Imagem gerada a partir do texto", use_column_width=True) | |
st.markdown("</div>", unsafe_allow_html=True) | |
# --- Funções de Casos de Uso --- | |
def get_use_cases(): | |
"""Retorna os casos de uso detalhados para cada modelo""" | |
return { | |
'sentiment_analysis': { | |
'title': "📊 Análise de Sentimento em Redes Sociais", | |
'description': "Identifique automaticamente o sentimento (positivo, negativo ou neutro) em comentários sobre sua marca.", | |
'example': "Monitorar menções ao seu produto no Twitter para detectar insatisfação de clientes e responder rapidamente.", | |
'benefit': "Redução de 40% no tempo de resposta a crises e aumento de 25% na satisfação do cliente.", | |
'demo_input': "Adorei o novo design do produto! Mas a bateria poderia durar mais.", | |
'demo_type': 'text', | |
'model_key': 'sentiment_analysis' | |
}, | |
'text_classification': { | |
'title': "🗂️ Classificação Automática de Tickets", | |
'description': "Classifique automaticamente tickets de suporte em categorias para roteamento eficiente.", | |
'example': "Direcionar tickets técnicos para a equipe de engenharia e pedidos de reembolso para o financeiro.", | |
'benefit': "Redução de 60% no tempo de resolução e aumento de 30% na precisão do encaminhamento.", | |
'demo_input': "Meu pedido nº 12345 não chegou. Preciso de ajuda para rastrear.", | |
'demo_type': 'text', | |
'model_key': 'text_classification' | |
}, | |
'summarization': { | |
'title': "📑 Resumo Automático de Relatórios", | |
'description': "Condense relatórios longos em resumos executivos de fácil compreensão.", | |
'example': "Transformar relatórios financeiros trimestrais de 50 páginas em resumos de 1 página para a diretoria.", | |
'benefit': "Economia de 15 horas/mês na análise de documentos e tomada de decisão mais ágil.", | |
'demo_input': """A empresa reportou crescimento de 15% na receita no último trimestre, alcançando $120 milhões. | |
O aumento foi impulsionado pelo lançamento do novo produto X2, que contribuiu com $18 milhões em vendas. | |
Os custos operacionais aumentaram 5% devido a investimentos em logística.""", | |
'demo_type': 'text', | |
'model_key': 'summarization' | |
}, | |
'question_answering': { | |
'title': "❓ FAQ Inteligente para Suporte", | |
'description': "Sistema automático de perguntas e respostas baseado em seus manuais e documentos.", | |
'example': "Chatbot que responde perguntas técnicas sobre produtos diretamente do manual do usuário.", | |
'benefit': "Redução de 50% no volume de chamadas para o suporte técnico.", | |
'demo_input': { | |
'context': "O produto X possui garantia de 24 meses. Para ativar, registre no site com a nota fiscal. Assistência técnica em 48h úteis.", | |
'question': "Qual é o prazo da garantia?" | |
}, | |
'demo_type': 'qa', | |
'model_key': 'question_answering' | |
}, | |
'translation': { | |
'title': "🌍 Tradução de Conteúdo para Expansão Global", | |
'description': "Traduza automaticamente conteúdo de marketing para novos mercados.", | |
'example': "Traduzir descrições de produtos do inglês para português para lançamento no Brasil.", | |
'benefit': "Redução de 70% nos custos com tradução humana e velocidade 10x maior.", | |
'demo_input': "Our premium subscription offers ad-free experience and exclusive content.", | |
'demo_type': 'text', | |
'model_key': 'translation' | |
}, | |
'ner': { | |
'title': "🔍 Extração de Informações em Contratos", | |
'description': "Identifique automaticamente partes, valores e prazos em documentos legais.", | |
'example': "Extrair nomes das partes, valores e datas de vencimento em contratos de fornecedores.", | |
'benefit': "Redução de 80% no tempo de revisão de contratos e menor risco de erros humanos.", | |
'demo_input': "Acordo entre ABC Ltda (CNPJ 12.345/0001-01) e XYZ S.A. (CNPJ 98.765/0001-02) assinado em 15/01/2023.", | |
'demo_type': 'text', | |
'model_key': 'ner' | |
}, | |
'text_generation': { | |
'title': "✍️ Geração de Conteúdo para Marketing", | |
'description': "Crie textos criativos para campanhas, anúncios e redes sociais.", | |
'example': "Gerar variações de copywriting para campanhas de email marketing.", | |
'benefit': "Aumento de 3x na produção de conteúdo e redução de custos com redatores.", | |
'demo_input': "Escreva um anúncio criativo para um novo smartphone com câmera de 108MP:", | |
'demo_type': 'text', | |
'model_key': 'text_generation' | |
}, | |
'image_classification': { | |
'title': "🖼️ Controle de Qualidade Automatizado", | |
'description': "Classifique automaticamente produtos em linhas de produção.", | |
'example': "Identificar defeitos em peças manufaturadas usando imagens da linha de produção.", | |
'benefit': "Aumento de 25% na detecção de defeitos e redução de 40% em retrabalhos.", | |
'demo_input': None, | |
'demo_type': 'image', | |
'model_key': 'image_classification' | |
}, | |
'object_detection': { | |
'title': "📦 Monitoramento de Estoque Inteligente", | |
'description': "Conte e identifique itens em prateleiras usando imagens.", | |
'example': "Monitorar níveis de estoque em tempo real através de imagens de prateleiras.", | |
'benefit': "Redução de 30% em rupturas de estoque e inventário 50% mais preciso.", | |
'demo_input': None, | |
'demo_type': 'image', | |
'model_key': 'object_detection' | |
}, | |
'image_segmentation': { | |
'title': "🏥 Análise de Imagens Médicas", | |
'description': "Segmentar áreas de interesse em exames de imagem.", | |
'example': "Identificar tumores em ressonâncias magnéticas para auxílio diagnóstico.", | |
'benefit': "Aumento de 20% na precisão diagnóstica e redução de 30% no tempo de análise.", | |
'demo_input': None, | |
'demo_type': 'image', | |
'model_key': 'image_segmentation' | |
}, | |
'facial_recognition': { | |
'title': "😊 Análise de Satisfação do Cliente", | |
'description': "Medir emoções de clientes em pontos de venda.", | |
'example': "Analisar expressões faciais de clientes ao interagir com produtos em lojas.", | |
'benefit': "Insights valiosos sobre preferências e pontos de dor dos clientes.", | |
'demo_input': None, | |
'demo_type': 'image', | |
'model_key': 'facial_recognition' | |
}, | |
'speech_to_text': { | |
'title': "🎙️ Transcrição de Reuniões Automática", | |
'description': "Converta gravações de reuniões em atas textuais automaticamente.", | |
'example': "Transcrever reuniões de diretoria para criar registros acionáveis.", | |
'benefit': "Economia de 5 horas/semana em documentação e maior precisão nos registros.", | |
'demo_input': None, | |
'demo_type': 'audio', | |
'model_key': 'speech_to_text' | |
}, | |
'audio_classification': { | |
'title': "📞 Análise de Emoções em Call Center", | |
'description': "Classifique o tom emocional de chamadas de suporte.", | |
'example': "Identificar clientes frustrados para escalonamento prioritário.", | |
'benefit': "Aumento de 35% na satisfação do cliente e redução de 25% no churn.", | |
'demo_input': None, | |
'demo_type': 'audio', | |
'model_key': 'audio_classification' | |
}, | |
'text_to_image': { | |
'title': "🎨 Geração de Conteúdo Visual para Marketing", | |
'description': "Crie imagens personalizadas para campanhas a partir de descrições textuais.", | |
'example': "Gerar banners promocionais para campanhas de redes sociais.", | |
'benefit': "Redução de 90% no tempo de produção visual e custos com designers.", | |
'demo_input': "Uma paisagem futurista com carros voadores sobre uma cidade high-tech à noite", | |
'demo_type': 'text', | |
'model_key': 'text_to_image' | |
} | |
} | |
def handle_use_case_demo(models, use_case_key, use_case): | |
""" | |
Executa e exibe a demonstração de um caso de uso específico | |
Args: | |
models: Dicionário de modelos carregados | |
use_case_key: Chave identificadora do caso de uso | |
use_case: Dicionário com configurações do caso de uso | |
""" | |
if use_case['demo_input'] is None: | |
st.warning("⚠️ Demonstração requer upload de arquivo") | |
return | |
st.markdown("""<div class='demo-box'>""", unsafe_allow_html=True) | |
st.subheader("🚀 Demonstração Prática") | |
try: | |
# Carrega o modelo se necessário | |
model = models.get(use_case_key) | |
if model is None: | |
with st.spinner(f"Carregando modelo {use_case_key.replace('_', ' ').title()}..."): | |
model = load_model(use_case_key) | |
models[use_case_key] = model | |
if model is None: | |
st.error("Falha ao carregar o modelo para demonstração") | |
return | |
# Processa conforme o tipo de demonstração | |
if use_case['demo_type'] == 'text': | |
with st.spinner("Analisando texto..."): | |
result = model(use_case['demo_input']) | |
display_demo_results(result, use_case_key, use_case['demo_input']) | |
elif use_case['demo_type'] == 'qa': | |
with st.spinner("Processando pergunta..."): | |
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"❌ Falha na demonstração: {str(e)}") | |
logging.error(f"Erro no caso de uso {use_case_key}: {str(e)}") | |
finally: | |
st.markdown("</div>", unsafe_allow_html=True) | |
def display_demo_results(result, model_key, input_text=None): | |
""" | |
Exibe os resultados da demonstração formatados | |
Args: | |
result: Resultado retornado pelo modelo | |
model_key: Identificador do modelo | |
input_text: Texto original (opcional) | |
""" | |
with st.expander("🔍 Detalhes da Análise", expanded=True): | |
if input_text: | |
st.markdown("**Entrada:**") | |
st.write(input_text) | |
st.markdown("**Resultados:**") | |
if model_key in ['sentiment_analysis', 'text_classification']: | |
for item in result: | |
label = item['label'] | |
score = item['score'] | |
st.progress(float(score), | |
text=f"{label} ({score:.2%})") | |
elif model_key == 'summarization': | |
st.markdown("**Resumo Gerado:**") | |
st.info(result[0]['summary_text']) | |
elif model_key == 'translation': | |
st.markdown("**Tradução:**") | |
st.success(result[0]['translation_text']) | |
elif model_key == 'ner': | |
st.markdown("**Entidades Identificadas:**") | |
for entity in result: | |
st.write(f"- {entity['word']} ({entity['entity_group']})") | |
elif model_key == 'text_generation': | |
st.markdown("**Texto Gerado:**") | |
st.write(result[0]['generated_text']) | |
elif model_key == 'text_to_image': | |
st.image(result[0], caption="Imagem gerada pelo modelo") | |
def render_use_cases(models): | |
""" | |
Renderiza todos os casos de uso na interface | |
Args: | |
models: Dicionário de modelos carregados | |
""" | |
st.header("📂 Casos de Uso Práticos") | |
st.markdown("Explore exemplos reais de aplicação dos nossos modelos de IA") | |
use_cases = get_use_cases() | |
for key, case in use_cases.items(): | |
with st.container(): | |
st.subheader(case['title']) | |
st.markdown(f"**{case['description']}**") | |
with st.expander("ℹ️ Detalhes do Caso de Uso", expanded=False): | |
st.markdown(f"📌 **Exemplo Prático:** {case['example']}") | |
st.markdown(f"✅ **Benefício:** {case['benefit']}") | |
# Seção de demonstração interativa | |
if case.get('demo_input') is not None: | |
if st.button( | |
"▶ Executar Demonstração", | |
key=f"demo_{key}", | |
type="primary", | |
help=f"Testar {case['title'].split(' ')[-1]} com dados de exemplo" | |
): | |
handle_use_case_demo(models, key, case) | |
else: | |
st.warning("Demonstração requer upload de arquivo específico") | |
st.divider() | |
# --- Layout e Componentes --- | |
def load_branding(username): | |
branding = { | |
'admin': {'logo': None, 'title': 'Bem-vindo, Administrador!', 'color': '#2c3e50'}, | |
'cliente': {'logo': None, 'title': 'Bem-vindo, Cliente!', 'color': '#3498db'}, | |
'empresa1': {'logo': None, 'title': 'Bem-vindo, Empresa Um!', 'color': '#e74c3c'} | |
} | |
return branding.get(username, {'logo': None, 'title': 'Bem-vindo!', 'color': '#3498db'}) | |
def feature_card(title, description, icon): | |
return f""" | |
<div class="card"> | |
<div class="feature-icon">{icon}</div> | |
<h3>{title}</h3> | |
<p>{description}</p> | |
</div> | |
""" | |
def render_login(authenticator): | |
st.title("AiiT Services - Plataforma de IA") | |
st.markdown(""" | |
<div style="text-align:center; margin-bottom:30px;"> | |
<h2 style="color:#2c3e50;">Inteligência Artificial Multi-Modal</h2> | |
<p style="font-size:1.1rem;">Soluções avançadas de IA para análise de texto, imagem e áudio</p> | |
</div> | |
""", unsafe_allow_html=True) | |
cols = st.columns(3) | |
with cols[0]: | |
st.markdown(feature_card("Texto Inteligente", "Análise, classificação e geração de texto", "📝"), unsafe_allow_html=True) | |
with cols[1]: | |
st.markdown(feature_card("Visão Computacional", "Reconhecimento de imagens e objetos", "🖼️"), unsafe_allow_html=True) | |
with cols[2]: | |
st.markdown(feature_card("Processamento de Áudio", "Transcrição e análise de sentimentos em áudio", "🎵"), unsafe_allow_html=True) | |
authenticator.login(location='main') | |
def render_main_interface(authenticator, config, models): | |
name = st.session_state["name"] | |
username = st.session_state["username"] | |
role = st.session_state.get("role") or config['credentials']['usernames'][username]['role'] | |
branding = load_branding(username) | |
st.sidebar.title(f"👤 {name}") | |
authenticator.logout("Sair", "sidebar") | |
if role == "admin": | |
admin_panel(authenticator) | |
st.title(f"{branding['title']}") | |
st.markdown(f"<style>.sidebar .sidebar-content {{background: {branding['color']};}}</style>", unsafe_allow_html=True) | |
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") | |
] | |
} | |
tab1, tab2 = st.tabs(["🧠 Explorar Modelos", "💼 Casos de Uso"]) | |
with tab1: | |
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]], | |
index=0 | |
) | |
model_key = next(key for name, key in model_categories[selected_category] if name == selected_model) | |
st.header(f"{selected_model}") | |
st.markdown(f"<div class='card'>", unsafe_allow_html=True) | |
if model_key not in models: | |
models[model_key] = load_model(model_key) | |
if not models[model_key]: | |
st.error("❌ Modelo não disponível") | |
return | |
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 durante a execução: {str(e)}") | |
st.markdown("</div>", unsafe_allow_html=True) | |
with tab2: | |
st.header("💼 Casos de Uso Práticos") | |
st.markdown("Explore aplicações reais de nossos modelos de IA") | |
use_cases = get_use_cases() | |
cols = st.columns(2) | |
for idx, (key, case) in enumerate(use_cases.items()): | |
with cols[idx % 2]: | |
st.markdown(f""" | |
<div class="use-case-card"> | |
<h4>{case['title']}</h4> | |
<p><strong>Descrição:</strong> {case['description']}</p> | |
<p><strong>Benefício:</strong> {case['benefit']}</p> | |
<button class="btn-primary" onclick="window['useCase_{key}'].click()">Ver Demonstração</button> | |
</div> | |
""", unsafe_allow_html=True) | |
if st.button("Ver Demonstração", key=f"useCase_{key}"): | |
handle_use_case_demo(models, key, case) | |
# --- Handlers para Tipos de Modelos --- | |
def handle_text_models(models, model_key, model_name): | |
examples = { | |
'sentiment_analysis': "A entrega foi super rápida, adorei!", | |
'text_classification': "Estou insatisfeito com o produto", | |
'summarization': "A empresa XYZ reportou crescimento de 15% no último trimestre...", | |
'translation': "Our product ensures high performance", | |
'ner': "Microsoft assinou contrato com a empresa XYZ em Nova York", | |
'text_generation': "Um futuro onde a tecnologia conecta todos" | |
} | |
col1, col2 = st.columns([0.85, 0.15]) | |
with col1: | |
input_text = st.text_area( | |
f"Digite o texto para {model_name.lower()}:", | |
height=200, | |
placeholder="Cole ou digite seu texto aqui..." | |
) | |
with col2: | |
st.write("") | |
st.write("") | |
if st.button("📋 Exemplo", use_container_width=True): | |
input_text = examples.get(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", use_container_width=True): | |
if input_text.strip(): | |
with st.spinner("Processando..."): | |
try: | |
result = models[model_key](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)}") | |
else: | |
st.warning("⚠️ Por favor, insira um texto válido") | |
def handle_qa_model(models, model_key): | |
col1, col2 = st.columns(2) | |
with col1: | |
context = st.text_area( | |
"Contexto:", | |
height=200, | |
placeholder="Texto contendo a informação...", | |
value="O produto X tem garantia de 2 anos e pode ser configurado via aplicativo em 5 minutos." | |
) | |
with col2: | |
question = st.text_area( | |
"Pergunta:", | |
height=150, | |
placeholder="Faça sua pergunta sobre o contexto...", | |
value="Qual é o tempo de garantia do produto X?" | |
) | |
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", use_container_width=True): | |
if context.strip() and question.strip(): | |
with st.spinner("Buscando resposta..."): | |
try: | |
result = models[model_key](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"**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)}") | |
else: | |
st.warning("⚠️ Preencha o contexto e a pergunta") | |
def handle_image_models(models, model_key, model_name): | |
uploaded_file = st.file_uploader( | |
"Carregue uma imagem", | |
type=["jpg", "png", "jpeg", "bmp"], | |
help="Formatos suportados: JPG, PNG, JPEG, BMP" | |
) | |
if uploaded_file: | |
if not validate_image_file(uploaded_file): | |
st.error("⚠️ Formato 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, use_column_width=True) | |
with col2: | |
st.subheader("📊 Resultados") | |
if st.button(f"🚀 Executar {model_name}", type="primary", use_container_width=True): | |
if image: | |
with st.spinner("Analisando imagem..."): | |
try: | |
result = models[model_key](image) | |
display_results(result, model_key) | |
except Exception as e: | |
st.error(f"❌ Erro ao processar imagem: {str(e)}") | |
def handle_audio_models(models, model_key): | |
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" | |
) | |
if uploaded_file: | |
if not validate_audio_file(uploaded_file): | |
st.error("⚠️ Formato de arquivo não suportado") | |
return | |
st.audio(uploaded_file, format="audio/wav") | |
if st.button(f"🚀 Executar {model_name}", type="primary", use_container_width=True): | |
with st.spinner("Processando áudio..."): | |
try: | |
audio_array = process_audio_file(uploaded_file) | |
if audio_array is not None: | |
result = models[model_key](audio_array) | |
display_results(result, model_key) | |
except Exception as e: | |
st.error(f"❌ Erro ao processar áudio: {str(e)}") | |
def handle_generative_models(models, model_key): | |
prompt = st.text_area( | |
"Descrição da imagem:", | |
height=150, | |
placeholder="Descreva a imagem que deseja gerar...", | |
value="Uma paisagem tropical ao pôr do sol" | |
) | |
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", use_container_width=True): | |
if prompt.strip(): | |
with st.spinner("Criando imagem..."): | |
try: | |
result = models[model_key]( | |
prompt, | |
height=height, | |
width=width, | |
num_images_per_prompt=num_images, | |
guidance_scale=guidance_scale | |
).images | |
display_results(result, model_key) | |
except Exception as e: | |
st.error(f"❌ Erro ao gerar imagem: {str(e)}") | |
else: | |
st.warning("⚠️ Insira uma descrição para a imagem") | |
# --- Função Principal --- | |
def main(): | |
init_db() | |
config = load_users_from_db() | |
if not config: | |
st.error("⚠️ Sistema de autenticação não configurado") | |
return | |
authenticator = stauth.Authenticate( | |
config['credentials'], | |
config['cookie']['name'], | |
config['cookie']['key'], | |
config['cookie']['expiry_days'] | |
) | |
if 'authentication_status' not in st.session_state: | |
st.session_state.authentication_status = None | |
if st.session_state.get("authentication_status"): | |
models = st.session_state.setdefault("models", {}) | |
render_main_interface(authenticator, config, models) | |
# Moved inside the authenticated block | |
tab1, tab2 = st.tabs(["Modelos", "Casos de Uso"]) | |
with tab2: | |
render_use_cases(models) # Renderiza todos os casos de uso | |
else: | |
render_login(authenticator) | |
if st.session_state.get("authentication_status") is False: | |
st.error("❌ Credenciais inválidas") | |
elif st.session_state.get("authentication_status") is None: | |
st.info("🔑 Faça login para acessar a plataforma") | |
if __name__ == "__main__": | |
main() |