Astarok commited on
Commit
b565984
·
verified ·
1 Parent(s): 9db2198

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -45
app.py CHANGED
@@ -7,7 +7,6 @@ import re
7
  import time
8
  from datetime import datetime
9
 
10
- # Importação do motor de Nuvem do Hugging Face
11
  try:
12
  from huggingface_hub import HfApi, hf_hub_download
13
  HF_HUB_AVAILABLE = True
@@ -57,7 +56,7 @@ st.markdown("""
57
  # 2. ARQUITETURA DE DADOS E MEMÓRIA EM NUVEM
58
  # ═══════════════════════════════════════════════════════════════════════════
59
  DB_FILE = "yukina_memoria_v3.json"
60
- DATASET_ID = "Astarok/Yukina_Memoria" # Seu banco de dados privado na nuvem
61
 
62
  MODEL_IDS = {
63
  "🤖 Automático (Gerente Hermes)": "AUTO",
@@ -108,47 +107,28 @@ def analisar_intencao_gerente(prompt, api_key):
108
 
109
  def load_db():
110
  hf_token = os.getenv("HF_TOKEN")
111
-
112
- # Tenta baixar da Nuvem primeiro (Quando o servidor reinicia)
113
  if hf_token and HF_HUB_AVAILABLE:
114
  try:
115
  api = HfApi(token=hf_token)
116
  api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
117
  path = hf_hub_download(repo_id=DATASET_ID, filename=DB_FILE, repo_type="dataset", token=hf_token)
118
- with open(path, "r", encoding="utf-8") as f:
119
- return json.load(f)
120
- except Exception:
121
- pass # Se o arquivo não existir na nuvem ainda, ignora silenciosamente
122
-
123
- # Se falhar ou não tiver net, usa o arquivo local da máquina
124
  if os.path.exists(DB_FILE):
125
  try:
126
- with open(DB_FILE, "r", encoding="utf-8") as f:
127
- return json.load(f)
128
- except:
129
- pass
130
-
131
  return {"Nova Conversa": {"pinned": False, "messages": []}}
132
 
133
  def save_db(db):
134
- # Salva na máquina local (rápido)
135
- with open(DB_FILE, "w", encoding="utf-8") as f:
136
- json.dump(db, f, ensure_ascii=False, indent=4)
137
-
138
- # Envia o backup pra nuvem silenciosamente no background
139
  hf_token = os.getenv("HF_TOKEN")
140
  if hf_token and HF_HUB_AVAILABLE:
141
  try:
142
  api = HfApi(token=hf_token)
143
  api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
144
- api.upload_file(
145
- path_or_fileobj=DB_FILE,
146
- path_in_repo=DB_FILE,
147
- repo_id=DATASET_ID,
148
- repo_type="dataset"
149
- )
150
- except Exception:
151
- pass # Se a internet oscilar, ele ignora para não travar o seu chat
152
 
153
  if "db" not in st.session_state: st.session_state.db = load_db()
154
  if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.db.keys())[0]
@@ -205,7 +185,7 @@ else:
205
  else: st.markdown(m["content"])
206
 
207
  # ═══════════════════════════════════════════════════════════════════════════
208
- # 4. TOOLBAR INFERIOR (Ícones Horizontais e Limpos)
209
  # ═══════════════════════════════════════════════════════════════════════════
210
  st.markdown("<br>", unsafe_allow_html=True)
211
 
@@ -213,7 +193,8 @@ t_col1, t_col2, t_col3, t_col4, t_space = st.columns([1, 1, 1, 1, 6])
213
 
214
  with t_col1:
215
  with st.popover("➕"):
216
- upload_file = st.file_uploader("Upload", type=["png", "jpg", "jpeg", "txt", "csv", "json"], label_visibility="collapsed")
 
217
  with t_col2:
218
  with st.popover("⚙️"):
219
  st.session_state.modelo_selecionado = st.radio("MOTOR:", list(MODEL_IDS.keys()), index=list(MODEL_IDS.keys()).index(st.session_state.modelo_selecionado))
@@ -236,17 +217,35 @@ with t_col4:
236
 
237
  prompt = st.chat_input("Peça à Yukina...")
238
 
 
239
  conteudo_arquivo = ""
240
- if upload_file is not None and upload_file.name.endswith(('.txt', '.csv', '.json')):
241
- conteudo_arquivo = upload_file.getvalue().decode("utf-8")
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  if prompt or st.session_state.regerar:
244
  if st.session_state.regerar and len(st.session_state.db[st.session_state.current_chat]["messages"]) > 0:
245
  texto_anterior = st.session_state.db[st.session_state.current_chat]["messages"][-1]["content"]
246
- prompt = texto_anterior.split("\n\n")[-1] if "📄 **Arquivo enviado:**" in texto_anterior else texto_anterior
 
247
  st.session_state.regerar = False
248
  else:
249
- mensagem_display = f"📄 **Arquivo enviado:** `{upload_file.name}`\n\n{prompt}" if conteudo_arquivo else prompt
 
 
 
 
 
250
  st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "user", "content": mensagem_display})
251
  with st.chat_message("user"): st.markdown(mensagem_display)
252
 
@@ -261,10 +260,10 @@ if prompt or st.session_state.regerar:
261
  if "Automático" in motor_real:
262
  with st.spinner("Roteando..."):
263
  tag_decisao = analisar_intencao_gerente(prompt, or_key)
264
- if upload_file is not None and upload_file.name.endswith(('.txt', '.csv', '.json')): motor_real = "12. Arquivista (Mistral Nemo)"
265
  elif "[IMAGEM]" in tag_decisao: motor_real = "9. Imagem (Flux 2 Pro)"
266
  elif "[VIDEO]" in tag_decisao: motor_real = "16. Vídeo (Kling V1.5)"
267
- elif "[VISAO]" in tag_decisao or (upload_file is not None and upload_file.name.endswith(('.png', '.jpg', '.jpeg'))): motor_real = "7. Visão Omni (MiMo V2)"
268
  elif "[CODIGO]" in tag_decisao: motor_real = "14. Engenheiro Sênior (DeepSeek V4)"
269
  elif "[ARQUIVISTA]" in tag_decisao: motor_real = "12. Arquivista (Mistral Nemo)"
270
  elif "[PESQUISA]" in tag_decisao: motor_real = "2. Pesquisa (Groq Llama 3.3)"
@@ -295,13 +294,13 @@ if prompt or st.session_state.regerar:
295
  elif "Pesquisa" in motor_real:
296
  with st.spinner("Pesquisando..."):
297
  historico = [{"role": m["role"], "content": m["content"]} for m in mensagens if "image_url" not in m and "video_url" not in m]
298
- if conteudo_arquivo: historico[-1]["content"] = f"DOCUMENTO:\n{conteudo_arquivo}\n\nPEDIDO:\n{prompt}"
299
  try:
300
  res = requests.post("https://api.groq.com/openai/v1/chat/completions", headers={"Authorization": f"Bearer {gr_key}", "Content-Type": "application/json"}, json={"model": modelo_id, "messages": [{"role": "system", "content": prompt_sistema_atual}] + historico, "max_tokens": 4000}).json()
301
  ans = res['choices'][0]['message']['content']; st.markdown(ans); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
302
  except Exception as e: st.error(f"Erro Groq: {e}")
303
 
304
- # 3. Imagem
305
  elif "Imagem" in motor_real:
306
  with st.spinner("Desenhando..."):
307
  try:
@@ -315,22 +314,24 @@ if prompt or st.session_state.regerar:
315
  if url: st.image(url); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": "Arte gerada.", "image_url": url})
316
  except Exception as e: st.error(f"Erro: {e}")
317
 
318
- # 4. Visão
319
- elif "Visão" in motor_real and upload_file is not None and upload_file.name.endswith(('.png', '.jpg', '.jpeg')):
320
- with st.spinner("Analisando imagem..."):
321
- img_b64 = base64.b64encode(upload_file.read()).decode()
 
 
322
  try:
323
- res = requests.post("https://openrouter.ai/api/v1/chat/completions", headers={"Authorization": f"Bearer {or_key}", "Content-Type": "application/json"}, json={"model": modelo_id, "messages": [{"role": "system", "content": prompt_sistema_atual}, {"role": "user", "content": [{"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_b64}"}}]} ], "max_tokens": 4000}).json()
324
  ans = res['choices'][0]['message']['content']; st.markdown(ans); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
325
  except Exception as e: st.error(f"Erro visão: {e}")
326
 
327
- # 5. Texto (Streaming Blindado)
328
  else:
329
  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."
330
  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."
331
 
332
  historico = [{"role": m["role"], "content": m["content"]} for m in mensagens if "image_url" not in m and "video_url" not in m]
333
- if conteudo_arquivo and len(historico) > 0: historico[-1]["content"] = f"DOCUMENTO:\n{conteudo_arquivo}\n\nO QUE FAZER:\n{prompt}"
334
 
335
  ph = st.empty(); full = ""
336
 
 
7
  import time
8
  from datetime import datetime
9
 
 
10
  try:
11
  from huggingface_hub import HfApi, hf_hub_download
12
  HF_HUB_AVAILABLE = True
 
56
  # 2. ARQUITETURA DE DADOS E MEMÓRIA EM NUVEM
57
  # ═══════════════════════════════════════════════════════════════════════════
58
  DB_FILE = "yukina_memoria_v3.json"
59
+ DATASET_ID = "Astarok/Yukina_Memoria"
60
 
61
  MODEL_IDS = {
62
  "🤖 Automático (Gerente Hermes)": "AUTO",
 
107
 
108
  def load_db():
109
  hf_token = os.getenv("HF_TOKEN")
 
 
110
  if hf_token and HF_HUB_AVAILABLE:
111
  try:
112
  api = HfApi(token=hf_token)
113
  api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
114
  path = hf_hub_download(repo_id=DATASET_ID, filename=DB_FILE, repo_type="dataset", token=hf_token)
115
+ with open(path, "r", encoding="utf-8") as f: return json.load(f)
116
+ except Exception: pass
 
 
 
 
117
  if os.path.exists(DB_FILE):
118
  try:
119
+ with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f)
120
+ except: pass
 
 
 
121
  return {"Nova Conversa": {"pinned": False, "messages": []}}
122
 
123
  def save_db(db):
124
+ with open(DB_FILE, "w", encoding="utf-8") as f: json.dump(db, f, ensure_ascii=False, indent=4)
 
 
 
 
125
  hf_token = os.getenv("HF_TOKEN")
126
  if hf_token and HF_HUB_AVAILABLE:
127
  try:
128
  api = HfApi(token=hf_token)
129
  api.create_repo(repo_id=DATASET_ID, repo_type="dataset", private=True, exist_ok=True)
130
+ api.upload_file(path_or_fileobj=DB_FILE, path_in_repo=DB_FILE, repo_id=DATASET_ID, repo_type="dataset")
131
+ except Exception: pass
 
 
 
 
 
 
132
 
133
  if "db" not in st.session_state: st.session_state.db = load_db()
134
  if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.db.keys())[0]
 
185
  else: st.markdown(m["content"])
186
 
187
  # ═══════════════════════════════════════════════════════════════════════════
188
+ # 4. TOOLBAR INFERIOR (Múltiplos Arquivos)
189
  # ═══════════════════════════════════════════════════════════════════════════
190
  st.markdown("<br>", unsafe_allow_html=True)
191
 
 
193
 
194
  with t_col1:
195
  with st.popover("➕"):
196
+ # A MÁGICA DOS MÚLTIPLOS ARQUIVOS (accept_multiple_files=True)
197
+ upload_files = st.file_uploader("Upload", type=["png", "jpg", "jpeg", "txt", "csv", "json"], accept_multiple_files=True, label_visibility="collapsed")
198
  with t_col2:
199
  with st.popover("⚙️"):
200
  st.session_state.modelo_selecionado = st.radio("MOTOR:", list(MODEL_IDS.keys()), index=list(MODEL_IDS.keys()).index(st.session_state.modelo_selecionado))
 
217
 
218
  prompt = st.chat_input("Peça à Yukina...")
219
 
220
+ # Processamento de Múltiplos Arquivos em Lote
221
  conteudo_arquivo = ""
222
+ nomes_arquivos = []
223
+ imagens_b64 = []
224
+
225
+ if upload_files:
226
+ for f in upload_files:
227
+ nomes_arquivos.append(f.name)
228
+ if f.name.endswith(('.txt', '.csv', '.json')):
229
+ conteudo_arquivo += f"\n\n--- Conteúdo de: {f.name} ---\n" + f.getvalue().decode("utf-8")
230
+ elif f.name.endswith(('.png', '.jpg', '.jpeg')):
231
+ imagens_b64.append(base64.b64encode(f.read()).decode())
232
+
233
+ tem_texto = len(conteudo_arquivo) > 0
234
+ tem_imagem = len(imagens_b64) > 0
235
 
236
  if prompt or st.session_state.regerar:
237
  if st.session_state.regerar and len(st.session_state.db[st.session_state.current_chat]["messages"]) > 0:
238
  texto_anterior = st.session_state.db[st.session_state.current_chat]["messages"][-1]["content"]
239
+ # Filtro inteligente para pegar o prompt caso tenham havido arquivos na mensagem original
240
+ prompt = texto_anterior.split("\n\n\n", 1)[-1] if "📄 **Arquivos enviados:**" in texto_anterior else texto_anterior
241
  st.session_state.regerar = False
242
  else:
243
+ if nomes_arquivos:
244
+ arquivos_str = ", ".join([f"`{n}`" for n in nomes_arquivos])
245
+ mensagem_display = f"📄 **Arquivos enviados:** {arquivos_str}\n\n\n{prompt}"
246
+ else:
247
+ mensagem_display = prompt
248
+
249
  st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "user", "content": mensagem_display})
250
  with st.chat_message("user"): st.markdown(mensagem_display)
251
 
 
260
  if "Automático" in motor_real:
261
  with st.spinner("Roteando..."):
262
  tag_decisao = analisar_intencao_gerente(prompt, or_key)
263
+ if tem_texto: motor_real = "12. Arquivista (Mistral Nemo)"
264
  elif "[IMAGEM]" in tag_decisao: motor_real = "9. Imagem (Flux 2 Pro)"
265
  elif "[VIDEO]" in tag_decisao: motor_real = "16. Vídeo (Kling V1.5)"
266
+ elif "[VISAO]" in tag_decisao or tem_imagem: motor_real = "7. Visão Omni (MiMo V2)"
267
  elif "[CODIGO]" in tag_decisao: motor_real = "14. Engenheiro Sênior (DeepSeek V4)"
268
  elif "[ARQUIVISTA]" in tag_decisao: motor_real = "12. Arquivista (Mistral Nemo)"
269
  elif "[PESQUISA]" in tag_decisao: motor_real = "2. Pesquisa (Groq Llama 3.3)"
 
294
  elif "Pesquisa" in motor_real:
295
  with st.spinner("Pesquisando..."):
296
  historico = [{"role": m["role"], "content": m["content"]} for m in mensagens if "image_url" not in m and "video_url" not in m]
297
+ if tem_texto: historico[-1]["content"] = f"DOCUMENTO(S) ANEXADOS:\n{conteudo_arquivo}\n\nPEDIDO DO USUÁRIO:\n{prompt}"
298
  try:
299
  res = requests.post("https://api.groq.com/openai/v1/chat/completions", headers={"Authorization": f"Bearer {gr_key}", "Content-Type": "application/json"}, json={"model": modelo_id, "messages": [{"role": "system", "content": prompt_sistema_atual}] + historico, "max_tokens": 4000}).json()
300
  ans = res['choices'][0]['message']['content']; st.markdown(ans); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
301
  except Exception as e: st.error(f"Erro Groq: {e}")
302
 
303
+ # 3. Imagem (Flux)
304
  elif "Imagem" in motor_real:
305
  with st.spinner("Desenhando..."):
306
  try:
 
314
  if url: st.image(url); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": "Arte gerada.", "image_url": url})
315
  except Exception as e: st.error(f"Erro: {e}")
316
 
317
+ # 4. Visão (Análise de Múltiplas Imagens)
318
+ elif "Visão" in motor_real and tem_imagem:
319
+ with st.spinner("Analisando imagens..."):
320
+ content_list = [{"type": "text", "text": prompt}]
321
+ for img_b64 in imagens_b64:
322
+ content_list.append({"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{img_b64}"}})
323
  try:
324
+ res = requests.post("https://openrouter.ai/api/v1/chat/completions", headers={"Authorization": f"Bearer {or_key}", "Content-Type": "application/json"}, json={"model": modelo_id, "messages": [{"role": "system", "content": prompt_sistema_atual}, {"role": "user", "content": content_list} ], "max_tokens": 4000}).json()
325
  ans = res['choices'][0]['message']['content']; st.markdown(ans); st.session_state.db[st.session_state.current_chat]["messages"].append({"role": "assistant", "content": ans})
326
  except Exception as e: st.error(f"Erro visão: {e}")
327
 
328
+ # 5. Texto (Streaming Blindado com Múltiplos Arquivos)
329
  else:
330
  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."
331
  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."
332
 
333
  historico = [{"role": m["role"], "content": m["content"]} for m in mensagens if "image_url" not in m and "video_url" not in m]
334
+ if tem_texto and len(historico) > 0: historico[-1]["content"] = f"DOCUMENTO(S):\n{conteudo_arquivo}\n\nO QUE FAZER:\n{prompt}"
335
 
336
  ph = st.empty(); full = ""
337