Update app.py
Browse files
app.py
CHANGED
|
@@ -6,6 +6,7 @@ import base64
|
|
| 6 |
import re
|
| 7 |
import time
|
| 8 |
from datetime import datetime
|
|
|
|
| 9 |
|
| 10 |
try:
|
| 11 |
from huggingface_hub import HfApi, hf_hub_download
|
|
@@ -47,15 +48,13 @@ st.markdown("""
|
|
| 47 |
transform: scale(1.15);
|
| 48 |
transition: 0.2s ease-in-out;
|
| 49 |
}
|
| 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;
|
|
@@ -70,7 +69,7 @@ DB_FILE = "yukina_memoria_v3.json"
|
|
| 70 |
DATASET_ID = "Astarok/Yukina_Memoria"
|
| 71 |
|
| 72 |
MODEL_IDS = {
|
| 73 |
-
"🤖 Automático (Gerente
|
| 74 |
"1. Gerente (Hermes 2 Pro)": "nousresearch/hermes-2-pro-llama-3-8b",
|
| 75 |
"2. Pesquisa (Groq Llama 3.3)": "llama-3.3-70b-versatile",
|
| 76 |
"3. Lógica Free (Ring 1T)": "inclusionai/ring-2.6-1t:free",
|
|
@@ -97,23 +96,32 @@ Personalidade pública: orgulhosa, dramática e otimista. Adora exigir aplausos.
|
|
| 97 |
"🤖 Neutra (Padrão Gemini)": """Você é uma assistente virtual neutra, direta, prestativa e objetiva. Sua função é fornecer respostas claras, estruturadas e precisas, sem inserir emoções, personalidades fictícias ou dramatizações. Vá direto ao ponto e foque apenas na informação solicitada pelo usuário."""
|
| 98 |
}
|
| 99 |
|
| 100 |
-
def analisar_intencao_gerente(prompt,
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
return "[CHAT]"
|
| 118 |
|
| 119 |
def load_db():
|
|
@@ -124,28 +132,33 @@ def load_db():
|
|
| 124 |
api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
|
| 125 |
path = hf_hub_download(repo_id=DATASET_ID, filename=DB_FILE, repo_type="dataset", token=hf_token)
|
| 126 |
with open(path, "r", encoding="utf-8") as f: return json.load(f)
|
| 127 |
-
except Exception:
|
|
|
|
| 128 |
if os.path.exists(DB_FILE):
|
| 129 |
try:
|
| 130 |
with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f)
|
| 131 |
-
except:
|
|
|
|
| 132 |
return {"Nova Conversa": {"pinned": False, "messages": []}}
|
| 133 |
|
| 134 |
def save_db(db):
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
api = HfApi(token=hf_token)
|
| 140 |
api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
|
| 141 |
api.upload_file(path_or_fileobj=DB_FILE, path_in_repo=DB_FILE, repo_id=DATASET_ID, repo_type="dataset")
|
| 142 |
-
|
|
|
|
| 143 |
|
|
|
|
| 144 |
if "db" not in st.session_state: st.session_state.db = load_db()
|
| 145 |
if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.db.keys())[0]
|
| 146 |
-
if "modelo_selecionado" not in st.session_state: st.session_state.modelo_selecionado = "🤖 Automático (Gerente
|
| 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
|
|
@@ -154,7 +167,7 @@ with st.sidebar:
|
|
| 154 |
c_title, c_add, c_set = st.columns([5, 1, 1])
|
| 155 |
with c_title: st.markdown("<h4 style='color: #ededed; margin-bottom: 0;'>Bate-papos</h4>", unsafe_allow_html=True)
|
| 156 |
with c_add:
|
| 157 |
-
if st.button("➕"):
|
| 158 |
nid = f"Chat {datetime.now().strftime('%H:%M:%S')}"
|
| 159 |
st.session_state.db[nid] = {"pinned": False, "messages": []}; st.session_state.current_chat = nid; save_db(st.session_state.db); st.rerun()
|
| 160 |
with c_set:
|
|
@@ -183,11 +196,17 @@ with st.sidebar:
|
|
| 183 |
if st.button("🗑 Apagar", key=f"del_{c_id}"):
|
| 184 |
if len(st.session_state.db) > 1: del st.session_state.db[c_id]; st.session_state.current_chat = list(st.session_state.db.keys())[0]; save_db(st.session_state.db); st.rerun()
|
| 185 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
st.markdown("<h3 style='margin-top: -10px; color: #ededed;'>❄ Yukina</h3>", unsafe_allow_html=True)
|
| 187 |
mensagens = st.session_state.db[st.session_state.current_chat]["messages"]
|
| 188 |
|
| 189 |
if len(mensagens) == 0:
|
| 190 |
-
st.markdown("<br><br><h3 style='color: #888; font-weight: 400;'>Olá
|
| 191 |
else:
|
| 192 |
for m in mensagens:
|
| 193 |
with st.chat_message(m["role"]):
|
|
@@ -196,35 +215,31 @@ else:
|
|
| 196 |
else: st.markdown(m["content"])
|
| 197 |
|
| 198 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 199 |
-
# 4. TOOLBAR INFERIOR
|
| 200 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 201 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 202 |
|
| 203 |
-
|
|
|
|
| 204 |
|
| 205 |
with t_col1:
|
| 206 |
-
|
| 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
|
| 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()
|
| 221 |
save_db(st.session_state.db)
|
| 222 |
st.session_state.regerar = True
|
| 223 |
st.rerun()
|
| 224 |
|
| 225 |
-
# GAVETA NATIVA:
|
| 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 |
|
|
@@ -268,7 +283,7 @@ if prompt or st.session_state.regerar:
|
|
| 268 |
|
| 269 |
if "Automático" in motor_real:
|
| 270 |
with st.spinner("Roteando..."):
|
| 271 |
-
tag_decisao = analisar_intencao_gerente(prompt,
|
| 272 |
if tem_texto: motor_real = "12. Arquivista (Mistral Nemo)"
|
| 273 |
elif "[IMAGEM]" in tag_decisao: motor_real = "9. Imagem (Flux 2 Pro)"
|
| 274 |
elif "[VIDEO]" in tag_decisao: motor_real = "16. Vídeo (Kling V1.5)"
|
|
@@ -297,19 +312,26 @@ if prompt or st.session_state.regerar:
|
|
| 297 |
v_url = check.get('video_url') or check.get('output', {}).get('url')
|
| 298 |
st.video(v_url); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": "Vídeo materializado.", "video_url": v_url}); status = "completed"; break
|
| 299 |
if status != "completed": st.error("Tempo limite.")
|
| 300 |
-
except Exception as e: st.error(f"Erro: {e}")
|
| 301 |
|
| 302 |
-
# 2. Pesquisa (Groq)
|
| 303 |
elif "Pesquisa" in motor_real:
|
| 304 |
with st.spinner("Pesquisando..."):
|
| 305 |
historico = [{"role": m["role"], "content": m["content"]} for m in mensagens if "image_url" not in m and "video_url" not in m]
|
| 306 |
if tem_texto: historico[-1]["content"] = f"DOCUMENTO(S) ANEXADOS:\n{conteudo_arquivo}\n\nPEDIDO DO USUÁRIO:\n{prompt}"
|
| 307 |
try:
|
| 308 |
-
|
| 309 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
except Exception as e: st.error(f"Erro Groq: {e}")
|
| 311 |
|
| 312 |
-
# 3. Imagem (Flux)
|
| 313 |
elif "Imagem" in motor_real:
|
| 314 |
with st.spinner("Desenhando..."):
|
| 315 |
try:
|
|
@@ -320,21 +342,33 @@ if prompt or st.session_state.regerar:
|
|
| 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:
|
| 324 |
-
|
|
|
|
|
|
|
| 325 |
|
| 326 |
-
# 4. Visão (Análise de Múltiplas Imagens)
|
| 327 |
elif "Visão" in motor_real and tem_imagem:
|
| 328 |
with st.spinner("Analisando imagens..."):
|
| 329 |
content_list = [{"type": "text", "text": prompt}]
|
| 330 |
for img_b64 in imagens_b64:
|
| 331 |
content_list.append({"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{img_b64}"}})
|
| 332 |
try:
|
| 333 |
-
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
except Exception as e: st.error(f"Erro visão: {e}")
|
| 336 |
|
| 337 |
-
# 5. Texto (Streaming Blindado com
|
| 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."
|
|
@@ -345,21 +379,28 @@ if prompt or st.session_state.regerar:
|
|
| 345 |
ph = st.empty(); full = ""
|
| 346 |
|
| 347 |
try:
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
finally:
|
| 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)
|
|
|
|
|
|
| 6 |
import re
|
| 7 |
import time
|
| 8 |
from datetime import datetime
|
| 9 |
+
from openai import OpenAI
|
| 10 |
|
| 11 |
try:
|
| 12 |
from huggingface_hub import HfApi, hf_hub_download
|
|
|
|
| 48 |
transform: scale(1.15);
|
| 49 |
transition: 0.2s ease-in-out;
|
| 50 |
}
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
/* Estilização elegante para a Gaveta Nativa de Upload */
|
| 53 |
[data-testid="stExpander"] {
|
| 54 |
background-color: #1e1f20 !important;
|
| 55 |
border: 1px solid #3c4043 !important;
|
| 56 |
border-radius: 15px !important;
|
| 57 |
+
margin-bottom: 10px;
|
| 58 |
}
|
| 59 |
[data-testid="stExpander"] summary {
|
| 60 |
color: #e3e3e3 !important;
|
|
|
|
| 69 |
DATASET_ID = "Astarok/Yukina_Memoria"
|
| 70 |
|
| 71 |
MODEL_IDS = {
|
| 72 |
+
"🤖 Automático (Gerente Groq)": "AUTO",
|
| 73 |
"1. Gerente (Hermes 2 Pro)": "nousresearch/hermes-2-pro-llama-3-8b",
|
| 74 |
"2. Pesquisa (Groq Llama 3.3)": "llama-3.3-70b-versatile",
|
| 75 |
"3. Lógica Free (Ring 1T)": "inclusionai/ring-2.6-1t:free",
|
|
|
|
| 96 |
"🤖 Neutra (Padrão Gemini)": """Você é uma assistente virtual neutra, direta, prestativa e objetiva. Sua função é fornecer respostas claras, estruturadas e precisas, sem inserir emoções, personalidades fictícias ou dramatizações. Vá direto ao ponto e foque apenas na informação solicitada pelo usuário."""
|
| 97 |
}
|
| 98 |
|
| 99 |
+
def analisar_intencao_gerente(prompt, groq_key):
|
| 100 |
+
"""Utiliza a Groq para roteamento ultra-rápido da intenção."""
|
| 101 |
+
try:
|
| 102 |
+
client = OpenAI(base_url="https://api.groq.com/openai/v1", api_key=groq_key)
|
| 103 |
+
prompt_gerente = """Você é o Gerente de Roteamento da IA Yukina. Analise o pedido e responda APENAS com UMA destas tags:
|
| 104 |
+
[IMAGEM] - Criar, desenhar ou gerar uma imagem/foto.
|
| 105 |
+
[VIDEO] - Criar ou gerar um vídeo.
|
| 106 |
+
[VISAO] - Analisar ou ver uma imagem.
|
| 107 |
+
[CODIGO] - Escrever programação, scripts, Python.
|
| 108 |
+
[ARQUIVISTA] - Resumir textos, planilhas, analisar dados anexados.
|
| 109 |
+
[PESQUISA] - Perguntas de conhecimentos gerais rápidas e buscas recentes.
|
| 110 |
+
[CHAT] - Conversas, RPG ou ordens simples.
|
| 111 |
+
Responda APENAS com a TAG."""
|
| 112 |
+
|
| 113 |
+
response = client.chat.completions.create(
|
| 114 |
+
model="llama-3.3-70b-versatile",
|
| 115 |
+
messages=[
|
| 116 |
+
{"role": "system", "content": prompt_gerente},
|
| 117 |
+
{"role": "user", "content": prompt}
|
| 118 |
+
],
|
| 119 |
+
temperature=0.1,
|
| 120 |
+
max_tokens=10
|
| 121 |
+
)
|
| 122 |
+
return response.choices[0].message.content.strip().upper()
|
| 123 |
+
except Exception as e:
|
| 124 |
+
print(f"Erro no roteamento do Gerente: {e}")
|
| 125 |
return "[CHAT]"
|
| 126 |
|
| 127 |
def load_db():
|
|
|
|
| 132 |
api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
|
| 133 |
path = hf_hub_download(repo_id=DATASET_ID, filename=DB_FILE, repo_type="dataset", token=hf_token)
|
| 134 |
with open(path, "r", encoding="utf-8") as f: return json.load(f)
|
| 135 |
+
except Exception as e:
|
| 136 |
+
print(f"Aviso HuggingFace Load: {e}")
|
| 137 |
if os.path.exists(DB_FILE):
|
| 138 |
try:
|
| 139 |
with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f)
|
| 140 |
+
except Exception as e:
|
| 141 |
+
print(f"Erro ao ler DB local: {e}")
|
| 142 |
return {"Nova Conversa": {"pinned": False, "messages": []}}
|
| 143 |
|
| 144 |
def save_db(db):
|
| 145 |
+
try:
|
| 146 |
+
with open(DB_FILE, "w", encoding="utf-8") as f: json.dump(db, f, ensure_ascii=False, indent=4)
|
| 147 |
+
hf_token = os.getenv("HF_TOKEN")
|
| 148 |
+
if hf_token and HF_HUB_AVAILABLE:
|
| 149 |
api = HfApi(token=hf_token)
|
| 150 |
api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
|
| 151 |
api.upload_file(path_or_fileobj=DB_FILE, path_in_repo=DB_FILE, repo_id=DATASET_ID, repo_type="dataset")
|
| 152 |
+
except Exception as e:
|
| 153 |
+
print(f"Erro ao salvar DB: {e}")
|
| 154 |
|
| 155 |
+
# Inicialização de Variáveis de Estado
|
| 156 |
if "db" not in st.session_state: st.session_state.db = load_db()
|
| 157 |
if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.db.keys())[0]
|
| 158 |
+
if "modelo_selecionado" not in st.session_state: st.session_state.modelo_selecionado = "🤖 Automático (Gerente Groq)"
|
| 159 |
if "personalidade_ativa" not in st.session_state: st.session_state.personalidade_ativa = "❄️ Yukina (Companheira Obsessiva)"
|
| 160 |
if "regerar" not in st.session_state: st.session_state.regerar = False
|
| 161 |
+
if "uploader_key" not in st.session_state: st.session_state.uploader_key = 0
|
| 162 |
|
| 163 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 164 |
# 3. SIDEBAR E INTERFACE PRINCIPAL
|
|
|
|
| 167 |
c_title, c_add, c_set = st.columns([5, 1, 1])
|
| 168 |
with c_title: st.markdown("<h4 style='color: #ededed; margin-bottom: 0;'>Bate-papos</h4>", unsafe_allow_html=True)
|
| 169 |
with c_add:
|
| 170 |
+
if st.button("➕", help="Nova conversa"):
|
| 171 |
nid = f"Chat {datetime.now().strftime('%H:%M:%S')}"
|
| 172 |
st.session_state.db[nid] = {"pinned": False, "messages": []}; st.session_state.current_chat = nid; save_db(st.session_state.db); st.rerun()
|
| 173 |
with c_set:
|
|
|
|
| 196 |
if st.button("🗑 Apagar", key=f"del_{c_id}"):
|
| 197 |
if len(st.session_state.db) > 1: del st.session_state.db[c_id]; st.session_state.current_chat = list(st.session_state.db.keys())[0]; save_db(st.session_state.db); st.rerun()
|
| 198 |
|
| 199 |
+
# MUDANÇA: Configurações de IA movidas para a sidebar para melhor responsividade
|
| 200 |
+
st.markdown("---")
|
| 201 |
+
st.markdown("<h4 style='color: #ededed;'>Núcleo da IA</h4>", unsafe_allow_html=True)
|
| 202 |
+
st.session_state.modelo_selecionado = st.selectbox("Motor:", list(MODEL_IDS.keys()), index=list(MODEL_IDS.keys()).index(st.session_state.modelo_selecionado))
|
| 203 |
+
st.session_state.personalidade_ativa = st.selectbox("Alma:", list(PERSONALIDADES.keys()), index=list(PERSONALIDADES.keys()).index(st.session_state.personalidade_ativa))
|
| 204 |
+
|
| 205 |
st.markdown("<h3 style='margin-top: -10px; color: #ededed;'>❄ Yukina</h3>", unsafe_allow_html=True)
|
| 206 |
mensagens = st.session_state.db[st.session_state.current_chat]["messages"]
|
| 207 |
|
| 208 |
if len(mensagens) == 0:
|
| 209 |
+
st.markdown("<br><br><h3 style='color: #888; font-weight: 400;'>Olá!</h3><h1 style='color: #fff; font-size: 32px;'>Como você quer que eu aja hoje?</h1>", unsafe_allow_html=True)
|
| 210 |
else:
|
| 211 |
for m in mensagens:
|
| 212 |
with st.chat_message(m["role"]):
|
|
|
|
| 215 |
else: st.markdown(m["content"])
|
| 216 |
|
| 217 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 218 |
+
# 4. TOOLBAR INFERIOR
|
| 219 |
# ═══════════════════════════════════════════════════════════════════════════
|
| 220 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 221 |
|
| 222 |
+
# Barra simplificada apenas com utilitários da conversa atual
|
| 223 |
+
t_col1, t_col2, t_space = st.columns([1, 1, 8])
|
| 224 |
|
| 225 |
with t_col1:
|
| 226 |
+
if st.button("🗑️", help="Apagar última mensagem"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
if len(st.session_state.db[st.session_state.current_chat]["messages"]) >= 2:
|
| 228 |
st.session_state.db[st.session_state.current_chat]["messages"] = st.session_state.db[st.session_state.current_chat]["messages"][:-2]
|
| 229 |
else:
|
| 230 |
st.session_state.db[st.session_state.current_chat]["messages"] = []
|
| 231 |
save_db(st.session_state.db); st.rerun()
|
| 232 |
+
with t_col2:
|
| 233 |
+
if st.button("🔄", help="Regerar resposta"):
|
| 234 |
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":
|
| 235 |
st.session_state.db[st.session_state.current_chat]["messages"].pop()
|
| 236 |
save_db(st.session_state.db)
|
| 237 |
st.session_state.regerar = True
|
| 238 |
st.rerun()
|
| 239 |
|
| 240 |
+
# GAVETA NATIVA: Chave dinâmica para esvaziar arquivos após o envio
|
| 241 |
with st.expander("📂 Abrir Galeria / Anexar Ficheiros"):
|
| 242 |
+
upload_files = st.file_uploader("", type=["png", "jpg", "jpeg", "txt", "csv", "json"], accept_multiple_files=True, label_visibility="collapsed", key=f"uploader_{st.session_state.uploader_key}")
|
| 243 |
|
| 244 |
prompt = st.chat_input("Peça à Yukina...")
|
| 245 |
|
|
|
|
| 283 |
|
| 284 |
if "Automático" in motor_real:
|
| 285 |
with st.spinner("Roteando..."):
|
| 286 |
+
tag_decisao = analisar_intencao_gerente(prompt, gr_key)
|
| 287 |
if tem_texto: motor_real = "12. Arquivista (Mistral Nemo)"
|
| 288 |
elif "[IMAGEM]" in tag_decisao: motor_real = "9. Imagem (Flux 2 Pro)"
|
| 289 |
elif "[VIDEO]" in tag_decisao: motor_real = "16. Vídeo (Kling V1.5)"
|
|
|
|
| 312 |
v_url = check.get('video_url') or check.get('output', {}).get('url')
|
| 313 |
st.video(v_url); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": "Vídeo materializado.", "video_url": v_url}); status = "completed"; break
|
| 314 |
if status != "completed": st.error("Tempo limite.")
|
| 315 |
+
except Exception as e: st.error(f"Erro no Vídeo: {e}")
|
| 316 |
|
| 317 |
+
# 2. Pesquisa (Groq com biblioteca oficial)
|
| 318 |
elif "Pesquisa" in motor_real:
|
| 319 |
with st.spinner("Pesquisando..."):
|
| 320 |
historico = [{"role": m["role"], "content": m["content"]} for m in mensagens if "image_url" not in m and "video_url" not in m]
|
| 321 |
if tem_texto: historico[-1]["content"] = f"DOCUMENTO(S) ANEXADOS:\n{conteudo_arquivo}\n\nPEDIDO DO USUÁRIO:\n{prompt}"
|
| 322 |
try:
|
| 323 |
+
client_groq = OpenAI(base_url="https://api.groq.com/openai/v1", api_key=gr_key)
|
| 324 |
+
res = client_groq.chat.completions.create(
|
| 325 |
+
model=modelo_id,
|
| 326 |
+
messages=[{"role": "system", "content": prompt_sistema_atual}] + historico,
|
| 327 |
+
max_tokens=4000
|
| 328 |
+
)
|
| 329 |
+
ans = res.choices[0].message.content
|
| 330 |
+
st.markdown(ans)
|
| 331 |
+
st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
|
| 332 |
except Exception as e: st.error(f"Erro Groq: {e}")
|
| 333 |
|
| 334 |
+
# 3. Imagem (Flux) - Mantido via requests para compatibilidade com "modalities" específicas do OpenRouter
|
| 335 |
elif "Imagem" in motor_real:
|
| 336 |
with st.spinner("Desenhando..."):
|
| 337 |
try:
|
|
|
|
| 342 |
elif 'content' in msg_obj:
|
| 343 |
urls = re.findall(r'(https?://[^\s)]+)', msg_obj.get('content', ''))
|
| 344 |
if urls: url = urls[0]
|
| 345 |
+
if url:
|
| 346 |
+
st.image(url)
|
| 347 |
+
st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": "Arte gerada.", "image_url": url})
|
| 348 |
+
except Exception as e: st.error(f"Erro na Imagem: {e}")
|
| 349 |
|
| 350 |
+
# 4. Visão (Análise de Múltiplas Imagens via biblioteca oficial)
|
| 351 |
elif "Visão" in motor_real and tem_imagem:
|
| 352 |
with st.spinner("Analisando imagens..."):
|
| 353 |
content_list = [{"type": "text", "text": prompt}]
|
| 354 |
for img_b64 in imagens_b64:
|
| 355 |
content_list.append({"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{img_b64}"}})
|
| 356 |
try:
|
| 357 |
+
client_or_vision = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=or_key)
|
| 358 |
+
res = client_or_vision.chat.completions.create(
|
| 359 |
+
model=modelo_id,
|
| 360 |
+
messages=[
|
| 361 |
+
{"role": "system", "content": prompt_sistema_atual},
|
| 362 |
+
{"role": "user", "content": content_list}
|
| 363 |
+
],
|
| 364 |
+
max_tokens=4000
|
| 365 |
+
)
|
| 366 |
+
ans = res.choices[0].message.content
|
| 367 |
+
st.markdown(ans)
|
| 368 |
+
st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
|
| 369 |
except Exception as e: st.error(f"Erro visão: {e}")
|
| 370 |
|
| 371 |
+
# 5. Texto (Streaming Blindado com biblioteca oficial)
|
| 372 |
else:
|
| 373 |
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."
|
| 374 |
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."
|
|
|
|
| 379 |
ph = st.empty(); full = ""
|
| 380 |
|
| 381 |
try:
|
| 382 |
+
client_stream = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=or_key)
|
| 383 |
+
resp = client_stream.chat.completions.create(
|
| 384 |
+
model=modelo_id,
|
| 385 |
+
messages=[{"role": "system", "content": prompt_sistema_atual}] + historico,
|
| 386 |
+
stream=True,
|
| 387 |
+
temperature=0.8,
|
| 388 |
+
max_tokens=4096
|
| 389 |
+
)
|
| 390 |
+
|
| 391 |
+
for chunk in resp:
|
| 392 |
+
if chunk.choices and chunk.choices[0].delta.content:
|
| 393 |
+
full += chunk.choices[0].delta.content
|
| 394 |
+
ph.markdown(full + "▌")
|
| 395 |
+
ph.markdown(full)
|
| 396 |
+
|
| 397 |
+
except Exception as e:
|
| 398 |
+
st.error(f"Oscilação de rede ou erro na API: {e}")
|
| 399 |
finally:
|
| 400 |
if full.strip() and len(st.session_state.db[st.session_state.current_chat]["messages"]) == len(mensagens):
|
| 401 |
st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": full})
|
| 402 |
|
| 403 |
+
# Incrementa a chave do uploader para resetar a gaveta de arquivos
|
| 404 |
+
st.session_state.uploader_key += 1
|
| 405 |
save_db(st.session_state.db)
|
| 406 |
+
st.rerun() # Força o rerun para limpar a gaveta visualmente
|