Update app.py
Browse files
app.py
CHANGED
|
@@ -14,7 +14,7 @@ except ImportError:
|
|
| 14 |
HF_HUB_AVAILABLE = False
|
| 15 |
|
| 16 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 17 |
-
# 1. CONFIGURAÇÃO DE ECRÃ E CSS
|
| 18 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 19 |
st.set_page_config(page_title="Yukina", page_icon="❄", layout="centered")
|
| 20 |
|
|
@@ -33,6 +33,7 @@ st.markdown("""
|
|
| 33 |
.stChatMessage { background-color: transparent !important; border: none !important; padding-bottom: 8px !important; }
|
| 34 |
textarea { background-color: #1e1f20 !important; border-radius: 24px !important; border: 1px solid #3c4043 !important; padding: 12px !important;}
|
| 35 |
|
|
|
|
| 36 |
[data-testid="stMain"] [data-testid="stHorizontalBlock"] button {
|
| 37 |
background: transparent !important;
|
| 38 |
border: none !important;
|
|
@@ -49,11 +50,21 @@ st.markdown("""
|
|
| 49 |
[data-testid="stMain"] [data-testid="stHorizontalBlock"] div[data-testid="stPopover"] > button svg {
|
| 50 |
display: none !important;
|
| 51 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
</style>
|
| 53 |
""", unsafe_allow_html=True)
|
| 54 |
|
| 55 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 56 |
-
# 2. ARQUITETURA DE DADOS E
|
| 57 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 58 |
DB_FILE = "yukina_memoria_v3.json"
|
| 59 |
DATASET_ID = "Astarok/Yukina_Memoria"
|
|
@@ -135,7 +146,6 @@ if "current_chat" not in st.session_state: st.session_state.current_chat = list(
|
|
| 135 |
if "modelo_selecionado" not in st.session_state: st.session_state.modelo_selecionado = "🤖 Automático (Gerente Hermes)"
|
| 136 |
if "personalidade_ativa" not in st.session_state: st.session_state.personalidade_ativa = "❄️ Yukina (Companheira Obsessiva)"
|
| 137 |
if "regerar" not in st.session_state: st.session_state.regerar = False
|
| 138 |
-
if "show_uploader" not in st.session_state: st.session_state.show_uploader = False
|
| 139 |
|
| 140 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 141 |
# 3. SIDEBAR E INTERFACE PRINCIPAL
|
|
@@ -186,30 +196,25 @@ else:
|
|
| 186 |
else: st.markdown(m["content"])
|
| 187 |
|
| 188 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 189 |
-
# 4. TOOLBAR INFERIOR (
|
| 190 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 191 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 192 |
|
| 193 |
-
t_col1, t_col2, t_col3,
|
| 194 |
|
| 195 |
with t_col1:
|
| 196 |
-
# A SOLUÇÃO: O botão serve como um interruptor e o "file_uploader" fica fora do popover.
|
| 197 |
-
if st.button("➕"):
|
| 198 |
-
st.session_state.show_uploader = not st.session_state.show_uploader
|
| 199 |
-
st.rerun()
|
| 200 |
-
with t_col2:
|
| 201 |
with st.popover("⚙️"):
|
| 202 |
st.session_state.modelo_selecionado = st.radio("MOTOR:", list(MODEL_IDS.keys()), index=list(MODEL_IDS.keys()).index(st.session_state.modelo_selecionado))
|
| 203 |
st.markdown("---")
|
| 204 |
st.session_state.personalidade_ativa = st.radio("ALMA:", list(PERSONALIDADES.keys()), index=list(PERSONALIDADES.keys()).index(st.session_state.personalidade_ativa))
|
| 205 |
-
with
|
| 206 |
if st.button("🗑️"):
|
| 207 |
if len(st.session_state.db[st.session_state.current_chat]["messages"]) >= 2:
|
| 208 |
st.session_state.db[st.session_state.current_chat]["messages"] = st.session_state.db[st.session_state.current_chat]["messages"][:-2]
|
| 209 |
else:
|
| 210 |
st.session_state.db[st.session_state.current_chat]["messages"] = []
|
| 211 |
save_db(st.session_state.db); st.rerun()
|
| 212 |
-
with
|
| 213 |
if st.button("🔄"):
|
| 214 |
if len(st.session_state.db[st.session_state.current_chat]["messages"]) >= 2 and st.session_state.db[st.session_state.current_chat]["messages"][-1]["role"] == "assistant":
|
| 215 |
st.session_state.db[st.session_state.current_chat]["messages"].pop()
|
|
@@ -217,10 +222,9 @@ with t_col4:
|
|
| 217 |
st.session_state.regerar = True
|
| 218 |
st.rerun()
|
| 219 |
|
| 220 |
-
#
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
upload_files = st.file_uploader("📂 Ficheiros (Múltiplos):", type=["png", "jpg", "jpeg", "txt", "csv", "json"], accept_multiple_files=True)
|
| 224 |
|
| 225 |
prompt = st.chat_input("Peça à Yukina...")
|
| 226 |
|
|
@@ -316,7 +320,7 @@ if prompt or st.session_state.regerar:
|
|
| 316 |
elif 'content' in msg_obj:
|
| 317 |
urls = re.findall(r'(https?://[^\s)]+)', msg_obj.get('content', ''))
|
| 318 |
if urls: url = urls[0]
|
| 319 |
-
if url: st.image(url); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "
|
| 320 |
except Exception as e: st.error(f"Erro: {e}")
|
| 321 |
|
| 322 |
# 4. Visão (Análise de Múltiplas Imagens)
|
|
@@ -330,7 +334,7 @@ if prompt or st.session_state.regerar:
|
|
| 330 |
ans = res['choices'][0]['message']['content']; st.markdown(ans); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
|
| 331 |
except Exception as e: st.error(f"Erro visão: {e}")
|
| 332 |
|
| 333 |
-
# 5. Texto (Streaming Blindado com Múltiplos
|
| 334 |
else:
|
| 335 |
if "Arquivista" in motor_real: prompt_sistema_atual = "Você é o Arquivista da IA. Organize as informações de forma analítica e clara, sem dramatizações."
|
| 336 |
elif "Engenheiro Sênior" in motor_real: prompt_sistema_atual = "Você é o Engenheiro Sênior. Escreva códigos impecáveis e funcionais. Foque na lógica."
|
|
@@ -358,4 +362,4 @@ if prompt or st.session_state.regerar:
|
|
| 358 |
if full.strip() and len(st.session_state.db[st.session_state.current_chat]["messages"]) == len(mensagens):
|
| 359 |
st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": full})
|
| 360 |
|
| 361 |
-
save_db(st.session_state.db)
|
|
|
|
| 14 |
HF_HUB_AVAILABLE = False
|
| 15 |
|
| 16 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 17 |
+
# 1. CONFIGURAÇÃO DE ECRÃ E CSS
|
| 18 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 19 |
st.set_page_config(page_title="Yukina", page_icon="❄", layout="centered")
|
| 20 |
|
|
|
|
| 33 |
.stChatMessage { background-color: transparent !important; border: none !important; padding-bottom: 8px !important; }
|
| 34 |
textarea { background-color: #1e1f20 !important; border-radius: 24px !important; border: 1px solid #3c4043 !important; padding: 12px !important;}
|
| 35 |
|
| 36 |
+
/* Ícones da Barra Inferior */
|
| 37 |
[data-testid="stMain"] [data-testid="stHorizontalBlock"] button {
|
| 38 |
background: transparent !important;
|
| 39 |
border: none !important;
|
|
|
|
| 50 |
[data-testid="stMain"] [data-testid="stHorizontalBlock"] div[data-testid="stPopover"] > button svg {
|
| 51 |
display: none !important;
|
| 52 |
}
|
| 53 |
+
|
| 54 |
+
/* Estilização elegante para a Gaveta Nativa de Upload */
|
| 55 |
+
[data-testid="stExpander"] {
|
| 56 |
+
background-color: #1e1f20 !important;
|
| 57 |
+
border: 1px solid #3c4043 !important;
|
| 58 |
+
border-radius: 15px !important;
|
| 59 |
+
}
|
| 60 |
+
[data-testid="stExpander"] summary {
|
| 61 |
+
color: #e3e3e3 !important;
|
| 62 |
+
}
|
| 63 |
</style>
|
| 64 |
""", unsafe_allow_html=True)
|
| 65 |
|
| 66 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 67 |
+
# 2. ARQUITETURA DE DADOS E NUVEM
|
| 68 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 69 |
DB_FILE = "yukina_memoria_v3.json"
|
| 70 |
DATASET_ID = "Astarok/Yukina_Memoria"
|
|
|
|
| 146 |
if "modelo_selecionado" not in st.session_state: st.session_state.modelo_selecionado = "🤖 Automático (Gerente Hermes)"
|
| 147 |
if "personalidade_ativa" not in st.session_state: st.session_state.personalidade_ativa = "❄️ Yukina (Companheira Obsessiva)"
|
| 148 |
if "regerar" not in st.session_state: st.session_state.regerar = False
|
|
|
|
| 149 |
|
| 150 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 151 |
# 3. SIDEBAR E INTERFACE PRINCIPAL
|
|
|
|
| 196 |
else: st.markdown(m["content"])
|
| 197 |
|
| 198 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 199 |
+
# 4. TOOLBAR INFERIOR (Gaveta Nativa Blindada)
|
| 200 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 201 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 202 |
|
| 203 |
+
t_col1, t_col2, t_col3, t_space = st.columns([1, 1, 1, 7])
|
| 204 |
|
| 205 |
with t_col1:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
with st.popover("⚙️"):
|
| 207 |
st.session_state.modelo_selecionado = st.radio("MOTOR:", list(MODEL_IDS.keys()), index=list(MODEL_IDS.keys()).index(st.session_state.modelo_selecionado))
|
| 208 |
st.markdown("---")
|
| 209 |
st.session_state.personalidade_ativa = st.radio("ALMA:", list(PERSONALIDADES.keys()), index=list(PERSONALIDADES.keys()).index(st.session_state.personalidade_ativa))
|
| 210 |
+
with t_col2:
|
| 211 |
if st.button("🗑️"):
|
| 212 |
if len(st.session_state.db[st.session_state.current_chat]["messages"]) >= 2:
|
| 213 |
st.session_state.db[st.session_state.current_chat]["messages"] = st.session_state.db[st.session_state.current_chat]["messages"][:-2]
|
| 214 |
else:
|
| 215 |
st.session_state.db[st.session_state.current_chat]["messages"] = []
|
| 216 |
save_db(st.session_state.db); st.rerun()
|
| 217 |
+
with t_col3:
|
| 218 |
if st.button("🔄"):
|
| 219 |
if len(st.session_state.db[st.session_state.current_chat]["messages"]) >= 2 and st.session_state.db[st.session_state.current_chat]["messages"][-1]["role"] == "assistant":
|
| 220 |
st.session_state.db[st.session_state.current_chat]["messages"].pop()
|
|
|
|
| 222 |
st.session_state.regerar = True
|
| 223 |
st.rerun()
|
| 224 |
|
| 225 |
+
# GAVETA NATIVA: Não sofre "crash" ao ir para a galeria em segundo plano
|
| 226 |
+
with st.expander("📂 Abrir Galeria / Anexar Ficheiros"):
|
| 227 |
+
upload_files = st.file_uploader("", type=["png", "jpg", "jpeg", "txt", "csv", "json"], accept_multiple_files=True, label_visibility="collapsed")
|
|
|
|
| 228 |
|
| 229 |
prompt = st.chat_input("Peça à Yukina...")
|
| 230 |
|
|
|
|
| 320 |
elif 'content' in msg_obj:
|
| 321 |
urls = re.findall(r'(https?://[^\s)]+)', msg_obj.get('content', ''))
|
| 322 |
if urls: url = urls[0]
|
| 323 |
+
if url: st.image(url); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": "Arte gerada.", "image_url": url})
|
| 324 |
except Exception as e: st.error(f"Erro: {e}")
|
| 325 |
|
| 326 |
# 4. Visão (Análise de Múltiplas Imagens)
|
|
|
|
| 334 |
ans = res['choices'][0]['message']['content']; st.markdown(ans); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
|
| 335 |
except Exception as e: st.error(f"Erro visão: {e}")
|
| 336 |
|
| 337 |
+
# 5. Texto (Streaming Blindado com Múltiplos Arquivos)
|
| 338 |
else:
|
| 339 |
if "Arquivista" in motor_real: prompt_sistema_atual = "Você é o Arquivista da IA. Organize as informações de forma analítica e clara, sem dramatizações."
|
| 340 |
elif "Engenheiro Sênior" in motor_real: prompt_sistema_atual = "Você é o Engenheiro Sênior. Escreva códigos impecáveis e funcionais. Foque na lógica."
|
|
|
|
| 362 |
if full.strip() and len(st.session_state.db[st.session_state.current_chat]["messages"]) == len(mensagens):
|
| 363 |
st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": full})
|
| 364 |
|
| 365 |
+
save_db(st.session_state.db)
|