leandroaraujodev's picture
test Adaptação Chatbot - Sicoob UI
2d1ddb2
raw
history blame
8.98 kB
import streamlit as st
import requests
from PIL import Image
import base64
import re
import streamlit_authenticator as stauth
from streamlit_feedback import streamlit_feedback
import os
import yaml
from yaml.loader import SafeLoader
from pathlib import Path
from drive_search import search_file_in_drive
from datetime import datetime
import json
class ChatbotApp:
def __init__(self):
# Configura título, ícone e layout da página
st.set_page_config(page_title="Sicoob Chatbot 🤖", page_icon="logos/sicoob-ico.ico", layout="wide")
self.backend_url = "http://localhost:5001/chat"
self.title = "Sicoob Chatbot"
self.feedback_dir = os.path.join(os.getcwd(), "./feedback")
self.description = "Este assistente virtual pode te ajudar com informações sobre carômetros da Sicoob."
self.style_dir = Path("./logos/styles")
self.load_styles()
st.session_state.first = False
if "theme" not in st.session_state:
st.session_state.theme = "light"
self.configyml = os.path.join(os.getcwd(), "./files/config.yaml")
def load_styles(self):
try:
self.base_style = (self.style_dir / "base.css").read_text()
self.light_style = (self.style_dir / "light.css").read_text()
self.dark_style = (self.style_dir / "dark.css").read_text()
except FileNotFoundError as e:
st.error(f"Error loading styles: {e}")
self.base_style = ""
self.light_style = ""
self.dark_style = ""
@st.dialog("📄 Atualizações Sicoob Chatbot", width="small")
def changelog_user(self):
with open("./logos/ChangelogUser.md", encoding="utf-8") as f:
st.markdown(f"{f.read()}", unsafe_allow_html=True)
def get_current_style(self):
theme_style = self.dark_style if st.session_state.theme == "dark" else self.light_style
return f"<style>{self.base_style}{theme_style}</style>"
def save_feedback(feedback_type, user_input, assistant_response):
feedback_dir = os.path.join(os.getcwd(), "feedback")
if not os.path.exists(feedback_dir):
os.makedirs(feedback_dir)
filename = os.path.join(feedback_dir, f"feedback_{feedback_type}.json")
try:
data = []
if os.path.exists(filename):
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
data.append({
"user_input": user_input,
"assistant_response": assistant_response,
"timestamp": datetime.now().isoformat()
})
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
st.toast(f"Feedback {feedback_type} salvo com sucesso!", icon="✅")
except Exception as e:
st.toast(f"Erro ao salvar feedback: {str(e)}", icon="❌")
def stream_chat(self, user_input):
"""
Faz a comunicação com o backend e retorna a resposta como streaming de tokens.
"""
try:
response = requests.post(
self.backend_url,
json={"message": user_input},
stream=True # Ativa o streaming
)
response.raise_for_status()
# Gera os tokens conforme chegam no streaming
for chunk in response.iter_content(chunk_size=512):
if chunk:
yield chunk.decode("utf-8")
except Exception as e:
yield f"Erro ao conectar ao servidor: {e}"
def clear_chat_history(self):
st.session_state.chat_history = []
st.toast("Histórico limpo com sucesso!", icon="✅")
def render_sidebar(self):
"""Siderbar"""
with st.sidebar:
if st.button("Light/Dark Mode"):
st.session_state.theme = "dark" if st.session_state.theme == "light" else "light"
st.rerun()
if st.session_state.theme == "light":
st.logo("./logos/sicoob-logo-horizontal-light.png", icon_image="./logos/sicoob-logo-vertical-sm.png")
else:
st.logo("./logos/sicoob-logo-horizontal-dark.png", icon_image="./logos/sicoob-logo-vertical-sm.png")
if st.button("Limpar Histórico", icon=":material/delete:"):
self.clear_chat_history()
if st.button("Atualizações", icon=":material/info:"):
self.changelog_user()
st.divider()
# st.title("Configuração de LLM")
# sidebar_option = st.radio("Selecione o LLM", ["gpt-3.5-turbo"])
# if sidebar_option != "gpt-3.5-turbo":
# raise Exception("Opção de LLM inválida!")
def render(self):
"""
Renderiza a interface do chatbot.
"""
st.markdown(self.get_current_style(), unsafe_allow_html=True)
with open(self.configyml) as file:
config = yaml.load(file, Loader=SafeLoader)
# Pre-hashing all plain text passwords once
# stauth.Hasher.hash_passwords(config['credentials'])
authenticator = stauth.Authenticate(
config['credentials'],
config['cookie']['name'],
config['cookie']['key'],
config['cookie']['expiry_days']
)
#try:
# authenticator.login()
#except Exception as e:
# st.error(e)
def add_link_to_text(text):
return re.sub(r'\|\|(.*?)\|\|', lambda match: f' <br>Fonte: <a href="{search_file_in_drive(match.group(1))}" target="_blank">{match.group(1)}</a>', text)
with open('./config.yaml', 'w', encoding='utf-8') as file:
yaml.dump(config, file, default_flow_style=False)
authentication_status = authenticator.login()
if st.session_state["authentication_status"]:
# Renderiza a barra lateral
self.render_sidebar()
# Título e descrição
st.title(self.title)
st.write(self.description)
with st.sidebar:
authenticator.logout('Sair', 'main')
# Inicializa o histórico na sessão
if "chat_history" not in st.session_state:
st.session_state.chat_history = []
# Renderiza as mensagens do histórico
for message in st.session_state.chat_history:
role, text = message.split(":", 1)
with st.chat_message(role.strip().lower()):
st.markdown(text.strip(), unsafe_allow_html=True)
# Captura o input do usuário
user_input = st.chat_input("Digite sua pergunta")
if user_input:
# Exibe a mensagem do usuário
with st.chat_message("user"):
st.write(user_input)
st.session_state.chat_history.append(f"user: {user_input}")
# Placeholder para a resposta do assistente
with st.chat_message("assistant"):
message_placeholder = st.empty()
assistant_message = ""
# Executa o streaming de tokens enquanto o backend responde
for token in self.stream_chat(user_input):
assistant_message += token
assistant_message_with_link = add_link_to_text(assistant_message)
message_placeholder.markdown(assistant_message_with_link + "▌", unsafe_allow_html=True)
message_placeholder.markdown(assistant_message_with_link, unsafe_allow_html=True)
#Adicionando histórico Streamlit
st.session_state.chat_history.append(f"assistant: {assistant_message_with_link}")
#Feedback
with st.container(border=True):
if st.button("Gostei", icon=":material/thumb_up:"):
save_feedback("positive", "Exemplo de pergunta do usuário", "Exemplo de resposta do assistente")
if st.button("Não Gostei", icon=":material/thumb_down:"):
save_feedback("negative", "Exemplo de pergunta do usuário", "Exemplo de resposta do assistente")
elif st.session_state["authentication_status"] == False:
st.error('Username/password is incorrect')
elif st.session_state["authentication_status"] == None:
st.warning('Por favor entre com seu "username" e senha.')
if __name__ == "__main__":
chatbot_app = ChatbotApp()
chatbot_app.render()