Spaces:
Sleeping
Sleeping
Pecximenes
commited on
Commit
路
0746d2c
1
Parent(s):
70a5baf
Adding feedback's and login's features
Browse files- interface/app.py +24 -14
- interface/auxiliary_functions.py +60 -7
- interface/chatbot.py +97 -43
- interface/login.py +110 -0
- interface/main.py +0 -122
- requirements.txt +4 -2
interface/app.py
CHANGED
@@ -9,6 +9,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
9 |
|
10 |
# Importando m贸dulos necess谩rios
|
11 |
from interface.chatbot import Chatbot
|
|
|
12 |
|
13 |
|
14 |
def display_message(message):
|
@@ -33,21 +34,30 @@ def display_page_message(message):
|
|
33 |
|
34 |
def main():
|
35 |
app = Chatbot()
|
36 |
-
|
37 |
-
with st.sidebar:
|
38 |
-
app.create_sidebar()
|
39 |
-
|
40 |
-
app.mount_chatbot()
|
41 |
-
|
42 |
-
if app.response:
|
43 |
-
app.add_to_history(app.response, role="user")
|
44 |
-
app.generate_awnser(app.response)
|
45 |
-
app.response = "" # Zerando a vari谩vel ap贸s uso
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
|
52 |
|
53 |
if __name__ == "__main__":
|
|
|
9 |
|
10 |
# Importando m贸dulos necess谩rios
|
11 |
from interface.chatbot import Chatbot
|
12 |
+
from interface.login import Login
|
13 |
|
14 |
|
15 |
def display_message(message):
|
|
|
34 |
|
35 |
def main():
|
36 |
app = Chatbot()
|
37 |
+
login = Login()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
+
if not st.session_state.user_authorized:
|
40 |
+
login.mount_login_page()
|
41 |
+
|
42 |
+
else:
|
43 |
+
with st.sidebar:
|
44 |
+
app.create_sidebar() # Corrigido para chamar o m茅todo que cria o sidebar
|
45 |
+
|
46 |
+
app.mount_chatbot() # Recebe o input de texto feito pelo usu谩rio
|
47 |
+
|
48 |
+
if app.response:
|
49 |
+
app.add_to_history(app.response, role="user")
|
50 |
+
app.generate_answer(app.response)
|
51 |
+
app.response = "" # Zerando a vari谩vel ap贸s uso
|
52 |
+
st.session_state.is_feedback_active = True # Ativando o feedback
|
53 |
+
|
54 |
+
for message in st.session_state.chat_history:
|
55 |
+
display_message(message)
|
56 |
+
if message["type"] == "page" and message == st.session_state.chat_history[-1]:
|
57 |
+
app.display_suggestions(app.links)
|
58 |
+
|
59 |
+
if st.session_state.is_feedback_active:
|
60 |
+
app.display_feedback()
|
61 |
|
62 |
|
63 |
if __name__ == "__main__":
|
interface/auxiliary_functions.py
CHANGED
@@ -1,9 +1,13 @@
|
|
1 |
-
# Fun莽玫es utilit谩rias
|
2 |
import re
|
3 |
import requests
|
4 |
from bs4 import BeautifulSoup
|
5 |
from urllib.parse import urlparse, unquote
|
6 |
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
def extract_content(text, tag):
|
9 |
"""Extrai conte煤do de um texto com base na tag fornecida."""
|
@@ -12,29 +16,78 @@ def extract_content(text, tag):
|
|
12 |
return match.group(1).strip() if match else None
|
13 |
|
14 |
def fetch_webpage_content(url):
|
15 |
-
"""Obt茅m o conte煤do HTML de uma URL e retorna o conte煤do principal."""
|
16 |
try:
|
17 |
response = requests.get(url)
|
18 |
response.raise_for_status()
|
|
|
|
|
19 |
soup = BeautifulSoup(response.text, 'html.parser')
|
|
|
|
|
20 |
main_content = soup.find('div', id='main')
|
21 |
|
22 |
if main_content:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
body_tag = soup.find('body')
|
24 |
if body_tag:
|
25 |
body_tag.clear()
|
26 |
body_tag.append(main_content)
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
29 |
except requests.RequestException as e:
|
30 |
-
|
|
|
|
|
31 |
|
32 |
def extract_links(html_content):
|
33 |
"""Extrai todos os links (URLs) de um conte煤do HTML."""
|
34 |
soup = BeautifulSoup(html_content, 'html.parser')
|
35 |
return [a_tag['href'] for a_tag in soup.find_all('a', href=True)]
|
36 |
|
|
|
37 |
def url_to_suggestion(url):
|
38 |
-
"""Converte uma URL longa em uma sugest茫o amig谩vel."""
|
39 |
path = unquote(urlparse(url).path).strip('/').split('/')
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import re
|
2 |
import requests
|
3 |
from bs4 import BeautifulSoup
|
4 |
from urllib.parse import urlparse, unquote
|
5 |
|
6 |
+
import streamlit as st
|
7 |
+
from openai import OpenAI
|
8 |
+
from qdrant_client import QdrantClient
|
9 |
+
import edgedb
|
10 |
+
import os
|
11 |
|
12 |
def extract_content(text, tag):
|
13 |
"""Extrai conte煤do de um texto com base na tag fornecida."""
|
|
|
16 |
return match.group(1).strip() if match else None
|
17 |
|
18 |
def fetch_webpage_content(url):
|
19 |
+
"""Obt茅m o conte煤do HTML de uma URL e retorna o conte煤do principal, desativando links espec铆ficos, mas mantendo os links originais para sugest玫es."""
|
20 |
try:
|
21 |
response = requests.get(url)
|
22 |
response.raise_for_status()
|
23 |
+
|
24 |
+
# Usando BeautifulSoup para processar o HTML
|
25 |
soup = BeautifulSoup(response.text, 'html.parser')
|
26 |
+
|
27 |
+
# Localiza o conte煤do principal pelo ID 'main'
|
28 |
main_content = soup.find('div', id='main')
|
29 |
|
30 |
if main_content:
|
31 |
+
# Armazenar os links originais para sugest玫es
|
32 |
+
original_links = [link['href'] for link in main_content.find_all('a', class_='govbr-card-content')]
|
33 |
+
|
34 |
+
# Desativar links com a classe "govbr-card-content" (apenas visualmente)
|
35 |
+
for link in main_content.find_all('a', class_='govbr-card-content'):
|
36 |
+
link['href'] = 'javascript:void(0);' # Impede a navega莽茫o
|
37 |
+
link['style'] = 'pointer-events: none; cursor: not-allowed;' # Estilo para desativar visualmente
|
38 |
+
|
39 |
+
# Remover elementos espec铆ficos (exemplo: div com a classe 'outstanding-header')
|
40 |
+
for unwanted_element in main_content.find_all('div', class_='outstanding-header'):
|
41 |
+
unwanted_element.decompose() # Remove o elemento do DOM
|
42 |
+
|
43 |
+
# Limpa o conte煤do do <body> e insere o conte煤do principal
|
44 |
body_tag = soup.find('body')
|
45 |
if body_tag:
|
46 |
body_tag.clear()
|
47 |
body_tag.append(main_content)
|
48 |
+
|
49 |
+
# Retorna o conte煤do modificado e os links originais
|
50 |
+
return str(soup), original_links
|
51 |
+
|
52 |
+
# Mensagem de erro caso n茫o encontre o conte煤do principal
|
53 |
+
return "<html><body><p>Could not find main content on the page.</p></body></html>", []
|
54 |
+
|
55 |
except requests.RequestException as e:
|
56 |
+
# Retorna uma mensagem de erro em caso de falha na requisi莽茫o
|
57 |
+
return f"<html><body><p>Error fetching the webpage: {str(e)}</p></body></html>", []
|
58 |
+
|
59 |
|
60 |
def extract_links(html_content):
|
61 |
"""Extrai todos os links (URLs) de um conte煤do HTML."""
|
62 |
soup = BeautifulSoup(html_content, 'html.parser')
|
63 |
return [a_tag['href'] for a_tag in soup.find_all('a', href=True)]
|
64 |
|
65 |
+
|
66 |
def url_to_suggestion(url):
|
67 |
+
"""Converte uma URL longa em uma sugest茫o amig谩vel e natural."""
|
68 |
path = unquote(urlparse(url).path).strip('/').split('/')
|
69 |
+
|
70 |
+
# Garantir que h谩 pelo menos duas partes significativas no caminho
|
71 |
+
if len(path) > 2:
|
72 |
+
# Remover h铆fens e capitalizar cada palavra
|
73 |
+
section = path[-2].replace('-', ' ').title()
|
74 |
+
subsection = path[-1].replace('-', ' ').title()
|
75 |
+
|
76 |
+
# Formatar a sugest茫o de forma mais natural
|
77 |
+
return f"Acesso 脿 se莽茫o '{section}' sobre '{subsection}'"
|
78 |
+
return None
|
79 |
+
|
80 |
+
@st.cache_resource
|
81 |
+
def connect_to_services():
|
82 |
+
oa_client = OpenAI(
|
83 |
+
api_key=os.environ.get("OPENAI_API_KEY")
|
84 |
+
)
|
85 |
+
|
86 |
+
qdrant_client = QdrantClient(
|
87 |
+
url=os.environ.get("QDRANT_URL"),
|
88 |
+
api_key=os.environ.get("QDRANT_KEY")
|
89 |
+
)
|
90 |
+
|
91 |
+
edgedb_client = edgedb.create_client()
|
92 |
+
|
93 |
+
return oa_client, qdrant_client, edgedb_client
|
interface/chatbot.py
CHANGED
@@ -14,8 +14,7 @@ from interface.auxiliary_functions import (
|
|
14 |
url_to_suggestion
|
15 |
)
|
16 |
|
17 |
-
|
18 |
-
class Chatbot():
|
19 |
def __init__(self):
|
20 |
# Configura莽茫o inicial do hist贸rico de chat
|
21 |
if "chat_history" not in st.session_state:
|
@@ -29,55 +28,33 @@ class Chatbot():
|
|
29 |
# Conte煤do dos bot玫es do sidebar
|
30 |
if "topics" not in st.session_state:
|
31 |
st.session_state["topics"] = [
|
32 |
-
"
|
33 |
"D煤vidas no reconhecimento facial.",
|
34 |
"Como recuperar minha conta gov.br",
|
35 |
-
"D煤vidas para aumentar o n铆vel com a
|
36 |
]
|
37 |
|
|
|
|
|
|
|
38 |
# Pergunta do usu谩rio no chatbot
|
39 |
self.response = ""
|
40 |
self.links = []
|
41 |
-
|
42 |
-
|
43 |
-
def display_suggestions(self, links):
|
44 |
-
suggestions = [(link, url_to_suggestion(link)) for link in links if url_to_suggestion(link)]
|
45 |
-
st.subheader("Sugest玫es de Passos:")
|
46 |
-
if suggestions:
|
47 |
-
st.write("Voc锚 tamb茅m pode se interessar em:")
|
48 |
-
for link, suggestion in suggestions:
|
49 |
-
st.write(f"- {suggestion}")
|
50 |
-
else:
|
51 |
-
st.write("N茫o h谩 sugest玫es dispon铆veis no momento.")
|
52 |
-
|
53 |
-
|
54 |
-
def mount_chatbot(self):
|
55 |
-
# Exibi莽茫o do t铆tulo e subt铆tulo
|
56 |
-
st.title("Bem-vindo 脿 ajuda do gov.br")
|
57 |
-
st.caption("馃挰 Qual a sua dificuldade hoje? Estou aqui para ajudar!")
|
58 |
|
59 |
-
# Exibi莽茫o do espa莽o para mandar mensagem
|
60 |
-
if user_query := st.chat_input(placeholder="Digite sua mensagem"):
|
61 |
-
st.session_state.chat_history.append({"type": "text","role": "user", "content": user_query})
|
62 |
-
|
63 |
-
#TODO Aplicar o llm_rag nessa parte
|
64 |
-
full_link = self.generate_awnser(user_query)
|
65 |
-
|
66 |
-
|
67 |
def create_sidebar(self):
|
|
|
68 |
st.image('https://www.gov.br/++theme++padrao_govbr/img/govbr-logo-large.png', width=200)
|
69 |
st.header("T贸picos frequentes")
|
70 |
-
|
71 |
-
for topic in st.session_state
|
72 |
if st.button(topic, key=topic):
|
73 |
self.response = topic
|
74 |
|
75 |
-
|
76 |
# Espa莽os em branco para organiza莽茫o
|
77 |
for _ in range(5):
|
78 |
st.write("")
|
79 |
-
|
80 |
-
# Bot茫o centralizado
|
81 |
col1, col2, col3 = st.columns([1, 2, 1])
|
82 |
with col2:
|
83 |
if st.button("LIMPAR HIST脫RICO"):
|
@@ -87,21 +64,52 @@ class Chatbot():
|
|
87 |
"content": "Como eu posso ajudar?",
|
88 |
"links": []
|
89 |
}]
|
|
|
90 |
|
91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
def add_to_history(self, message, role="user", type="text"):
|
|
|
93 |
st.session_state.chat_history.append({
|
94 |
"role": role,
|
95 |
"type": type,
|
96 |
"content": message
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
#
|
102 |
response_text = send_message(input_message, pipeline)
|
103 |
|
104 |
-
# Armazena a resposta do
|
105 |
self.add_to_history(response_text, role="assistant")
|
106 |
|
107 |
# Extra莽茫o do caminho da resposta do bot
|
@@ -109,11 +117,57 @@ class Chatbot():
|
|
109 |
if path:
|
110 |
base_url = "https://www.gov.br/governodigital/pt-br/"
|
111 |
full_url = base_url + path
|
|
|
|
|
|
|
|
|
112 |
with st.spinner("Obtendo conte煤do da p谩gina..."):
|
113 |
-
content = fetch_webpage_content(full_url)
|
114 |
|
|
|
115 |
self.add_to_history(content, role="assistant", type="page")
|
116 |
|
117 |
-
|
118 |
extracted_links = extract_links(content)
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
url_to_suggestion
|
15 |
)
|
16 |
|
17 |
+
class Chatbot:
|
|
|
18 |
def __init__(self):
|
19 |
# Configura莽茫o inicial do hist贸rico de chat
|
20 |
if "chat_history" not in st.session_state:
|
|
|
28 |
# Conte煤do dos bot玫es do sidebar
|
29 |
if "topics" not in st.session_state:
|
30 |
st.session_state["topics"] = [
|
31 |
+
"N铆veis da conta govbr.",
|
32 |
"D煤vidas no reconhecimento facial.",
|
33 |
"Como recuperar minha conta gov.br",
|
34 |
+
"D煤vidas para aumentar o n铆vel com a CIN."
|
35 |
]
|
36 |
|
37 |
+
if "is_feedback_active" not in st.session_state:
|
38 |
+
st.session_state.is_feedback_active = False
|
39 |
+
|
40 |
# Pergunta do usu谩rio no chatbot
|
41 |
self.response = ""
|
42 |
self.links = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
def create_sidebar(self):
|
45 |
+
"""Cria o sidebar com t贸picos e op莽玫es."""
|
46 |
st.image('https://www.gov.br/++theme++padrao_govbr/img/govbr-logo-large.png', width=200)
|
47 |
st.header("T贸picos frequentes")
|
48 |
+
|
49 |
+
for topic in st.session_state["topics"]:
|
50 |
if st.button(topic, key=topic):
|
51 |
self.response = topic
|
52 |
|
|
|
53 |
# Espa莽os em branco para organiza莽茫o
|
54 |
for _ in range(5):
|
55 |
st.write("")
|
56 |
+
|
57 |
+
# Bot茫o centralizado para limpar hist贸rico
|
58 |
col1, col2, col3 = st.columns([1, 2, 1])
|
59 |
with col2:
|
60 |
if st.button("LIMPAR HIST脫RICO"):
|
|
|
64 |
"content": "Como eu posso ajudar?",
|
65 |
"links": []
|
66 |
}]
|
67 |
+
st.session_state.is_feedback_active = False
|
68 |
|
69 |
|
70 |
+
def display_suggestions(self, links):
|
71 |
+
"""Exibe as sugest玫es de links no chat."""
|
72 |
+
if links:
|
73 |
+
st.subheader("Voc锚 pode acessar a p谩gina na web em:")
|
74 |
+
st.write(links[0]) # O primeiro link ser谩 o da p谩gina principal
|
75 |
+
|
76 |
+
st.subheader("Pr贸ximos Passos:")
|
77 |
+
suggestions = [(link, url_to_suggestion(link)) for link in links[1:] if url_to_suggestion(link)]
|
78 |
+
|
79 |
+
if suggestions:
|
80 |
+
st.write("Voc锚 tamb茅m pode se interessar em:")
|
81 |
+
for link, suggestion in suggestions:
|
82 |
+
st.write(f"- [{suggestion}]({link})") # Exibe os links clic谩veis no formato Markdown
|
83 |
+
else:
|
84 |
+
st.write("N茫o h谩 sugest玫es de navega莽茫o adicionais.")
|
85 |
+
else:
|
86 |
+
st.write("N茫o h谩 links dispon铆veis para exibi莽茫o.")
|
87 |
+
|
88 |
+
def mount_chatbot(self):
|
89 |
+
"""Configura o chatbot com t铆tulo, subt铆tulo e espa莽o de entrada."""
|
90 |
+
st.title("Bem-vindo 脿 ajuda do gov.br")
|
91 |
+
st.caption("馃挰 Qual a sua dificuldade hoje? Estou aqui para ajudar!")
|
92 |
+
|
93 |
+
# Exibi莽茫o do espa莽o para mandar mensagem
|
94 |
+
if user_query := st.chat_input(placeholder="Digite sua mensagem"):
|
95 |
+
st.session_state.chat_history.append({"type": "text", "role": "user", "content": user_query})
|
96 |
+
self.generate_answer(user_query)
|
97 |
+
st.session_state.is_feedback_active = True # Ativando o feedback
|
98 |
+
|
99 |
def add_to_history(self, message, role="user", type="text"):
|
100 |
+
"""Adiciona uma mensagem ao hist贸rico de chat."""
|
101 |
st.session_state.chat_history.append({
|
102 |
"role": role,
|
103 |
"type": type,
|
104 |
"content": message
|
105 |
+
})
|
106 |
+
|
107 |
+
def generate_answer(self, input_message):
|
108 |
+
"""Gera uma resposta para a consulta do usu谩rio."""
|
109 |
+
# Chama o pipeline para obter a resposta
|
110 |
response_text = send_message(input_message, pipeline)
|
111 |
|
112 |
+
# Armazena a resposta do agente com a tag <path>
|
113 |
self.add_to_history(response_text, role="assistant")
|
114 |
|
115 |
# Extra莽茫o do caminho da resposta do bot
|
|
|
117 |
if path:
|
118 |
base_url = "https://www.gov.br/governodigital/pt-br/"
|
119 |
full_url = base_url + path
|
120 |
+
|
121 |
+
# Armazena o link principal
|
122 |
+
self.links = [full_url]
|
123 |
+
|
124 |
with st.spinner("Obtendo conte煤do da p谩gina..."):
|
125 |
+
content, original_links = fetch_webpage_content(full_url) # Agora retornando tamb茅m os links desativados
|
126 |
|
127 |
+
# Adiciona o conte煤do da p谩gina ao hist贸rico do chatbot
|
128 |
self.add_to_history(content, role="assistant", type="page")
|
129 |
|
130 |
+
# Extrai links ativos do conte煤do da p谩gina e armazena para sugest玫es
|
131 |
extracted_links = extract_links(content)
|
132 |
+
|
133 |
+
# Combinar links ativos e links inibidos
|
134 |
+
all_links = extracted_links + original_links
|
135 |
+
self.links.extend(all_links)
|
136 |
+
|
137 |
+
def display_feedback(self):
|
138 |
+
user_input = st.session_state.chat_history[-2]["content"]
|
139 |
+
bot_output = st.session_state.chat_history[-1]["content"]
|
140 |
+
|
141 |
+
with st.expander("Avalia莽茫o do atendimento"):
|
142 |
+
# st.write(f'O que achou da resposta para a pergunta "{user_input}"?')
|
143 |
+
st.write(f'O que achou da resposta para a pergunta?')
|
144 |
+
|
145 |
+
rate = st.feedback("stars")
|
146 |
+
# rate = st.feedback("faces")
|
147 |
+
|
148 |
+
text_feedback = st.text_input("Coment谩rios extras:")
|
149 |
+
|
150 |
+
# Bot茫o para confirmar a avalia莽茫o
|
151 |
+
if rate is not None:
|
152 |
+
if st.button("Enviar Avalia莽茫o"):
|
153 |
+
try:
|
154 |
+
feedback_rate = rate + 1
|
155 |
+
# TODO Colocar nessa parte a estrutura para adicionar o feedback_data ao banco de dados
|
156 |
+
# st.session_state.services["edgedb_client"].query('''
|
157 |
+
# INSERT Feedback {
|
158 |
+
# rating := <int16>$rating,
|
159 |
+
# content := <str>$content,
|
160 |
+
# message := <str>$content
|
161 |
+
# }
|
162 |
+
# ''',
|
163 |
+
# message_id=bot_output,
|
164 |
+
# rating=feedback_rate,
|
165 |
+
# content=text_feedback,
|
166 |
+
# )
|
167 |
+
# st.session_state.chat_history
|
168 |
+
st.success(f"Avalia莽茫o enviada!")
|
169 |
+
except Exception as e:
|
170 |
+
print(e)
|
171 |
+
st.error("Erro ao enviar avalia莽茫o!")
|
172 |
+
|
173 |
+
st.session_state.is_feedback_active = False # Desativando o feedback
|
interface/login.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import sys
|
3 |
+
import os
|
4 |
+
import bcrypt
|
5 |
+
|
6 |
+
# Add the parent directory to the Python path
|
7 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
8 |
+
|
9 |
+
from auxiliary_functions import connect_to_services
|
10 |
+
|
11 |
+
|
12 |
+
class Login:
|
13 |
+
def __init__(self):
|
14 |
+
if "username" not in st.session_state:
|
15 |
+
st.session_state.username = ""
|
16 |
+
|
17 |
+
if "user_authorized" not in st.session_state:
|
18 |
+
st.session_state.user_authorized = False
|
19 |
+
|
20 |
+
if "services" not in st.session_state:
|
21 |
+
oa_client, qdrant_client, edgedb_client = connect_to_services()
|
22 |
+
st.session_state.services = {
|
23 |
+
"oa_client": oa_client,
|
24 |
+
"qdrant_client": qdrant_client,
|
25 |
+
"edgedb_client": edgedb_client
|
26 |
+
}
|
27 |
+
|
28 |
+
|
29 |
+
self.username = ""
|
30 |
+
self.password = ""
|
31 |
+
|
32 |
+
|
33 |
+
def mount_login_page(self):
|
34 |
+
# Exibi莽茫o do t铆tulo e subt铆tulo
|
35 |
+
st.title("Bem-vindo 脿 ajuda do gov.br")
|
36 |
+
with st.form("login_form"):
|
37 |
+
st.title("Login")
|
38 |
+
|
39 |
+
username = st.text_input('E-mail:')
|
40 |
+
password = st.text_input('Senha:', type='password')
|
41 |
+
submitted = st.form_submit_button("Entrar")
|
42 |
+
st.text("N茫o possui uma conta?")
|
43 |
+
create_account = st.form_submit_button("Criar uma conta")
|
44 |
+
|
45 |
+
if create_account:
|
46 |
+
if not username:
|
47 |
+
st.warning("Digite um email para cadastrar a conta.")
|
48 |
+
|
49 |
+
if not password:
|
50 |
+
st.warning("Digite uma senha para cadastrar a conta.")
|
51 |
+
|
52 |
+
if username and password:
|
53 |
+
if "@" not in username:
|
54 |
+
st.warning("Digite um email v谩lido para cadastrar a conta.")
|
55 |
+
|
56 |
+
else:
|
57 |
+
user = self.validate_credentials(username, password, return_just_user=True)
|
58 |
+
if user:
|
59 |
+
st.warning("J谩 existe uma conta usando este email")
|
60 |
+
else:
|
61 |
+
# Gerando senha encriptada
|
62 |
+
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
63 |
+
hashed_password_str = hashed_password.decode('utf-8')
|
64 |
+
|
65 |
+
st.session_state.services["edgedb_client"].query('''
|
66 |
+
INSERT User {
|
67 |
+
username := <str>$username,
|
68 |
+
password := <str>$password
|
69 |
+
}
|
70 |
+
''',
|
71 |
+
username=username, \
|
72 |
+
password=hashed_password_str, \
|
73 |
+
)
|
74 |
+
|
75 |
+
st.success("Conta cadastrada com sucesso.")
|
76 |
+
|
77 |
+
if submitted:
|
78 |
+
if username and password:
|
79 |
+
checked = self.validate_credentials(username, password)
|
80 |
+
if checked:
|
81 |
+
st.success("Usu谩rio logado!")
|
82 |
+
st.session_state.user_authorized = True
|
83 |
+
st.session_state.username = username
|
84 |
+
st.rerun()
|
85 |
+
|
86 |
+
else:
|
87 |
+
st.error("Nome de usu谩rio ou senha inv谩lidos.")
|
88 |
+
|
89 |
+
else:
|
90 |
+
st.warning("Digite o nome do usu谩rio e a senha.")
|
91 |
+
|
92 |
+
|
93 |
+
def validate_credentials(self, username, password, return_just_user=False):
|
94 |
+
user = st.session_state.services["edgedb_client"].query('''
|
95 |
+
SELECT User {
|
96 |
+
username,
|
97 |
+
password
|
98 |
+
} FILTER .username = <str>$username
|
99 |
+
''', username=username)
|
100 |
+
|
101 |
+
if return_just_user:
|
102 |
+
if not user:
|
103 |
+
return False
|
104 |
+
|
105 |
+
return True
|
106 |
+
|
107 |
+
if not user or not bcrypt.checkpw(password.encode('utf-8'), user[0].password.encode('utf-8')):
|
108 |
+
return False
|
109 |
+
|
110 |
+
return True
|
interface/main.py
DELETED
@@ -1,122 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import streamlit.components.v1 as components
|
3 |
-
import re
|
4 |
-
import sys
|
5 |
-
import os
|
6 |
-
import requests
|
7 |
-
from bs4 import BeautifulSoup
|
8 |
-
import difflib
|
9 |
-
from urllib.parse import urlparse, unquote
|
10 |
-
from pipelines.message import send_message
|
11 |
-
from agent import pipeline, knowledge, graph, graph_data
|
12 |
-
|
13 |
-
# Configura莽茫o do caminho
|
14 |
-
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
15 |
-
|
16 |
-
# Substitui o caminho do gr谩fico pelo conte煤do do graph_data
|
17 |
-
graph = graph.replace('"../memory/graph_data.json"', f'{graph_data}')
|
18 |
-
|
19 |
-
# Vari谩veis globais
|
20 |
-
extracted_links = []
|
21 |
-
|
22 |
-
# Configura莽茫o inicial do hist贸rico de chat
|
23 |
-
if "chat_history" not in st.session_state:
|
24 |
-
st.session_state.chat_history = []
|
25 |
-
|
26 |
-
# Fun莽玫es utilit谩rias
|
27 |
-
def extract_content(text, tag):
|
28 |
-
"""Extrai conte煤do de um texto com base na tag fornecida."""
|
29 |
-
pattern = rf'<{tag}>(.*?)</{tag}>'
|
30 |
-
match = re.search(pattern, text, re.DOTALL)
|
31 |
-
return match.group(1).strip() if match else None
|
32 |
-
|
33 |
-
def fetch_webpage_content(url):
|
34 |
-
"""Obt茅m o conte煤do HTML de uma URL e retorna o conte煤do principal."""
|
35 |
-
try:
|
36 |
-
response = requests.get(url)
|
37 |
-
response.raise_for_status()
|
38 |
-
soup = BeautifulSoup(response.text, 'html.parser')
|
39 |
-
main_content = soup.find('div', id='main')
|
40 |
-
|
41 |
-
if main_content:
|
42 |
-
body_tag = soup.find('body')
|
43 |
-
if body_tag:
|
44 |
-
body_tag.clear()
|
45 |
-
body_tag.append(main_content)
|
46 |
-
return str(soup)
|
47 |
-
return "<html><body><p>Could not find main content on the page.</p></body></html>"
|
48 |
-
except requests.RequestException as e:
|
49 |
-
return f"<html><body><p>Error fetching the webpage: {str(e)}</p></body></html>"
|
50 |
-
|
51 |
-
def extract_links(html_content):
|
52 |
-
"""Extrai todos os links (URLs) de um conte煤do HTML."""
|
53 |
-
soup = BeautifulSoup(html_content, 'html.parser')
|
54 |
-
return [a_tag['href'] for a_tag in soup.find_all('a', href=True)]
|
55 |
-
|
56 |
-
def url_to_suggestion(url):
|
57 |
-
"""Converte uma URL longa em uma sugest茫o amig谩vel."""
|
58 |
-
path = unquote(urlparse(url).path).strip('/').split('/')
|
59 |
-
return path[-1].replace('-', ' ').replace('_', ' ').capitalize() if path else url
|
60 |
-
|
61 |
-
def display_suggestions(links):
|
62 |
-
"""Exibe sugest玫es para o usu谩rio com base nos links extra铆dos."""
|
63 |
-
suggestions = [(link, url_to_suggestion(link)) for link in links if url_to_suggestion(link)]
|
64 |
-
st.subheader("Sugest玫es de Passos:")
|
65 |
-
if suggestions:
|
66 |
-
st.write("Voc锚 tamb茅m pode se interessar em:")
|
67 |
-
for link, suggestion in suggestions:
|
68 |
-
st.write(f"- {suggestion}")
|
69 |
-
else:
|
70 |
-
st.write("N茫o h谩 sugest玫es dispon铆veis no momento.")
|
71 |
-
|
72 |
-
def display_knowledge_tree(structure):
|
73 |
-
"""Exibe a estrutura de conhecimento na barra lateral do Streamlit."""
|
74 |
-
with st.sidebar.expander("Knowledge Structure", expanded=True):
|
75 |
-
components.html(graph, height=600, scrolling=True)
|
76 |
-
st.markdown(structure)
|
77 |
-
|
78 |
-
def navigate_page(full_url):
|
79 |
-
"""Fun莽茫o para navegar para uma nova p谩gina e atualizar o conte煤do."""
|
80 |
-
global extracted_links
|
81 |
-
with st.spinner("Obtendo conte煤do da p谩gina..."):
|
82 |
-
content = fetch_webpage_content(full_url)
|
83 |
-
extracted_links = extract_links(content)
|
84 |
-
st.session_state.chat_history.append({"type": "page", "content": content, "links": extracted_links})
|
85 |
-
st.subheader("Conte煤do da P谩gina Web:")
|
86 |
-
components.html(content, height=600, scrolling=True)
|
87 |
-
display_suggestions(extracted_links)
|
88 |
-
st.subheader("URL Completa:")
|
89 |
-
st.write(full_url)
|
90 |
-
|
91 |
-
def main():
|
92 |
-
st.title("Alfred - Assistente de IA para Servi莽os gov.br")
|
93 |
-
st.write("Qual a sua dificuldade hoje? Estou aqui para ajudar!")
|
94 |
-
display_knowledge_tree(knowledge)
|
95 |
-
|
96 |
-
# Exibe o hist贸rico do chat
|
97 |
-
for message in st.session_state.chat_history:
|
98 |
-
if message["type"] == "text":
|
99 |
-
st.markdown(message["content"])
|
100 |
-
elif message["type"] == "page":
|
101 |
-
st.subheader("Conte煤do da P谩gina Web:")
|
102 |
-
components.html(message["content"], height=400, scrolling=True)
|
103 |
-
|
104 |
-
with st.form(key='user_input_form', clear_on_submit=True):
|
105 |
-
user_query = st.text_input("Digite sua mensagem aqui:")
|
106 |
-
submit_button = st.form_submit_button(label='Enviar')
|
107 |
-
if submit_button and user_query:
|
108 |
-
response_text = send_message(user_query, pipeline)
|
109 |
-
st.write("Bot:", response_text)
|
110 |
-
st.session_state.chat_history.append({"type": "text", "content": f"**Voc锚:** {user_query}"})
|
111 |
-
|
112 |
-
# Extra莽茫o do caminho da resposta do bot
|
113 |
-
path = extract_content(response_text, "path")
|
114 |
-
if path:
|
115 |
-
base_url = "https://www.gov.br/governodigital/pt-br/"
|
116 |
-
full_url = base_url + path
|
117 |
-
navigate_page(full_url)
|
118 |
-
|
119 |
-
st.session_state.chat_history.append({"type": "text", "content": f"**Bot:** {response_text}"})
|
120 |
-
|
121 |
-
if __name__ == "__main__":
|
122 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
@@ -13,6 +13,8 @@ docker==7.1.0
|
|
13 |
paramiko==3.4.0
|
14 |
duckduckgo_search==6.1.12
|
15 |
inputimeout==1.0.4
|
16 |
-
streamlit==1.
|
17 |
beautifulsoup4==4.12.3
|
18 |
-
bs4==0.0.2
|
|
|
|
|
|
13 |
paramiko==3.4.0
|
14 |
duckduckgo_search==6.1.12
|
15 |
inputimeout==1.0.4
|
16 |
+
streamlit==1.39.0
|
17 |
beautifulsoup4==4.12.3
|
18 |
+
bs4==0.0.2
|
19 |
+
qdrant-client==1.12.0
|
20 |
+
edgedb==2.1.0
|