import streamlit as st
import requests
import re
import streamlit_authenticator as stauth
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
from feedback_manager import FeedbackManager
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.description = "Este assistente virtual pode te ajudar com informações sobre documentos da Sicoob."
self.caption = "Confira as atualizações no botão 'Atualizações'."
self.style_dir = Path("./logos/styles")
self.load_styles()
self.feedback_manager = FeedbackManager()
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")
if "feedback_submitted" not in st.session_state:
st.session_state.feedback_submitted = set()
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 = ""
def change_style(self):
with st.sidebar:
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")
st.session_state.theme = "dark" if st.session_state.theme == "light" else "light"
@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""
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:
# st.button("Light/Dark Mode", on_click=self.change_style)
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()
st.button("Atualizações", icon=":material/info:", on_click=self.changelog_user)
st.divider()
def add_link_to_text(self, text):
"""
Adiciona links ao texto com base no padrão ||texto||.
"""
return re.sub(r'\|\|(.*?)\|\|', lambda match: f'
Fonte: {match.group(1)}', text)
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)
authenticator = stauth.Authenticate(
config['credentials'],
config['cookie']['name'],
config['cookie']['key'],
config['cookie']['expiry_days']
)
with open('./config.yaml', 'w', encoding='utf-8') as file:
yaml.dump(config, file, default_flow_style=False)
authentication_status = authenticator.login(fields={'Form name': 'Autenticação', 'Username': 'Nome de Usuário', 'Password': 'Senha', 'Login': 'Entrar'})
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:
st.caption(self.caption)
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(placeholder="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 = ""
try:
# Gerando ID único para a mensagem
message_id = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
print(f"Message ID gerado: {message_id}")
# Executa o streaming de tokens enquanto o backend responde
for token in self.stream_chat(user_input):
assistant_message += token
message_placeholder.markdown(assistant_message + "▌", unsafe_allow_html=True)
except Exception as e:
st.error(f"Erro durante o chat: {str(e)}")
print(f"Erro durante o chat: {str(e)}")
finally:
assistant_message_with_link = self.add_link_to_text(assistant_message)
message_placeholder.markdown(assistant_message_with_link, unsafe_allow_html=True)
# Feedback
self.feedback_manager.render_feedback_buttons(
message_id=message_id,
user_input=user_input,
assistant_response=assistant_message_with_link
)
# Adicionando histórico Streamlit
st.session_state.chat_history.append(f"assistant: {assistant_message_with_link}")
elif st.session_state["authentication_status"] == False:
st.error('Nome de Usuário/Senha incorreta.')
elif st.session_state["authentication_status"] == None:
st.warning('Por favor entre com seu nome de usuário e senha.')
if __name__ == "__main__":
chatbot_app = ChatbotApp()
chatbot_app.render()