-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
148 lines (125 loc) · 5.86 KB
/
Copy pathapp.py
File metadata and controls
148 lines (125 loc) · 5.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import time
import streamlit as st
import os
from io import BytesIO
from streamlit_mic_recorder import mic_recorder
from src.engine import RAGParaBot
from dotenv import load_dotenv
# Cargar variables de entorno
load_dotenv()
# =========================
# CONFIGURACIÓN
# =========================
CONFIG = {
'PAGE_TITLE': "ParaBot - Asistente Paralímpico",
'PDF_FOLDER': './pdfs',
'INDEX_FILE': 'index_faiss.bin',
'CHUNKS_FILE': 'chunks_text.pkl',
'CHUNK_SIZE': 600,
'CHUNK_OVERLAP': 100,
'EMBEDDING_MODEL_NAME': 'sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2'
}
PASS_ADMIN = os.getenv("ADMIN_PASSWORD", "admin123")
st.set_page_config(page_title=CONFIG['PAGE_TITLE'], page_icon="🏅", layout="wide")
# =========================
# ESTILOS (CSS) - Preservando tu diseño de accesibilidad
# =========================
st.markdown("""
<style>
button:focus-visible, [data-testid="stBaseButton-secondary"]:focus-visible,
input:focus-visible, div[role="button"]:focus-visible {
outline: 3px solid #0072CE !important;
outline-offset: 2px !important;
}
.stApp { background-color: #f8f9fa; color: #333; }
h1 { color: #0072CE; font-size: 3rem !important; font-weight: 800 !important; text-align: center; }
.stChatMessage { padding: 1rem; border-radius: 15px; margin-bottom: 10px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
div[data-testid="stChatMessage"]:nth-child(odd) { background-color: #e3f2fd; border-left: 5px solid #0072CE; color: black; }
div[data-testid="stChatMessage"]:nth-child(even) { background-color: #ffffff; border-left: 5px solid #2e7d32; color: black; }
.subtitulo-accesible { text-align: center; color: #262730; font-size: 1.2rem !important; margin-bottom: 20px; }
@media (prefers-color-scheme: dark) {
.stApp { background-color: #0e1117 !important; color: white !important; }
h1 { color: #4da6ff !important; }
div[data-testid="stChatMessage"]:nth-child(odd) { background-color: #192841 !important; border-left: 5px solid #4da6ff !important; color: white !important; }
.subtitulo-accesible { color: #ffffff !important; }
}
</style>
""", unsafe_allow_html=True)
# =========================
# INICIALIZACIÓN
# =========================
@st.cache_resource
def get_bot():
bot = RAGParaBot(CONFIG)
bot.load_engine()
return bot
bot = get_bot()
if "messages" not in st.session_state: st.session_state.messages = []
if "es_admin" not in st.session_state: st.session_state.es_admin = False
# =========================
# BARRA LATERAL
# =========================
with st.sidebar:
st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/IPC_logo_%282019%29.svg/500px-IPC_logo_%282019%29.svg.png", width=120)
st.markdown("### ♿ Accesibilidad")
modo_audio = st.toggle("🔊 Leer respuestas (Audio)", value=True)
modo_lectura_facil = st.toggle("🔎 Lectura Fácil", value=False)
st.markdown("---")
if st.session_state.es_admin:
st.success("🔒 Modo Admin")
pdfs_subidos = st.file_uploader("Subir PDFs", type="pdf", accept_multiple_files=True)
if pdfs_subidos and st.button("🔄 Procesar"):
if not os.path.exists(CONFIG['PDF_FOLDER']): os.makedirs(CONFIG['PDF_FOLDER'])
for archivo in pdfs_subidos:
with open(os.path.join(CONFIG['PDF_FOLDER'], archivo.name), "wb") as f: f.write(archivo.getbuffer())
if bot.process_pdfs():
st.success("¡Base de datos actualizada!")
time.sleep(1)
st.rerun()
else:
with st.expander("⚙️ Admin"):
password = st.text_input("Clave", type="password")
if st.button("Entrar"):
if password == PASS_ADMIN:
st.session_state.es_admin = True
st.rerun()
# =========================
# CHAT INTERFACE
# =========================
st.title("🤖 ParaBot")
st.markdown("<p class='subtitulo-accesible'>Asistente Inteligente del Comité Paralímpico</p>", unsafe_allow_html=True)
# Audio Input
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
audio_grabado = mic_recorder(start_prompt="🎙️ TOCAR PARA HABLAR", stop_prompt="✅ ENVIAR", key='recorder')
# Mostrar historial
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
if "fuentes" in msg: st.markdown(msg["fuentes"])
# Procesar entrada
pregunta = st.chat_input("Escribe tu pregunta...")
if audio_grabado:
with st.spinner("Transcribiendo..."):
pregunta = bot.speech_to_text(BytesIO(audio_grabado['bytes']))
if pregunta:
st.session_state.messages.append({"role": "user", "content": pregunta})
with st.chat_message("user"): st.markdown(pregunta)
with st.chat_message("assistant"):
with st.spinner("🧠 Consultando documentos..."):
context, fuentes_dict = bot.query(pregunta)
# Construcción del prompt
instr = "Explica de forma simple y corta." if modo_lectura_facil else "Responde como experto del Comité Paralímpico usando el contexto."
prompt = f"{instr}\n\nCONTEXTO:\n{context}\n\nPREGUNTA: {pregunta}"
respuesta = bot.generate_response(prompt)
# Formatear fuentes
fuentes_txt = ""
if fuentes_dict:
fuentes_txt = "\n\n📚 **Fuentes:**\n" + "\n".join([f"- 📄 {k} (Pág. {', '.join(map(str, v))})" for k, v in fuentes_dict.items()])
st.markdown(respuesta)
if fuentes_txt: st.markdown(fuentes_txt)
# Guardar y Audio
st.session_state.messages.append({"role": "assistant", "content": respuesta, "fuentes": fuentes_txt})
if modo_audio:
audio = bot.text_to_speech(respuesta)
st.audio(audio, format="audio/mp3", autoplay=True)