|
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): |
|
|
|
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"<style>{self.base_style}{theme_style}</style>" |
|
|
|
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 |
|
) |
|
response.raise_for_status() |
|
|
|
|
|
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.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' <br>Fonte: <a href="{search_file_in_drive(match.group(1))}" target="_blank">{match.group(1)}</a>', 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"]: |
|
|
|
self.render_sidebar() |
|
|
|
|
|
st.title(self.title) |
|
st.write(self.description) |
|
with st.sidebar: |
|
st.caption(self.caption) |
|
authenticator.logout('Sair', 'main') |
|
|
|
if "chat_history" not in st.session_state: |
|
st.session_state.chat_history = [] |
|
|
|
|
|
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) |
|
|
|
|
|
user_input = st.chat_input(placeholder="Digite sua pergunta...") |
|
if user_input: |
|
|
|
with st.chat_message("user"): |
|
st.write(user_input) |
|
st.session_state.chat_history.append(f"user: {user_input}") |
|
|
|
with st.chat_message("assistant"): |
|
message_placeholder = st.empty() |
|
assistant_message = "" |
|
try: |
|
|
|
message_id = datetime.now().strftime("%Y%m%d_%H%M%S_%f") |
|
print(f"Message ID gerado: {message_id}") |
|
|
|
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) |
|
|
|
self.feedback_manager.render_feedback_buttons( |
|
message_id=message_id, |
|
user_input=user_input, |
|
assistant_response=assistant_message_with_link |
|
) |
|
|
|
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() |
|
|