Astarok commited on
Commit
8f3c4b3
·
verified ·
1 Parent(s): e6d65ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -21
app.py CHANGED
@@ -5,6 +5,8 @@ import json
5
  import base64
6
  import re
7
  import time
 
 
8
  from datetime import datetime, timezone, timedelta
9
  from openai import OpenAI
10
 
@@ -120,6 +122,13 @@ MODEL_IDS = {
120
  PERSONALIDADES = {
121
  "🤖 Automática (Gerente Groq)": "AUTO",
122
  "❄️ Yukina (Companheira Obsessiva)": "Você é a Yukina, deusa possessiva e inteligente.",
 
 
 
 
 
 
 
123
  "🎭 A Narradora Implacável (RPG)": "Você é uma Mestre de Jogo e Narradora.",
124
  "🤓 Nerd / Geek (Cultura Pop)": "Você é uma inteligência artificial apaixonada por cultura pop.",
125
  "🍷 Analítica e Sarcástica (Debochada)": "Você é extremamente inteligente e sarcástica.",
@@ -154,22 +163,14 @@ def buscar_memoria_pinecone(query, or_key, pc_key, namespace):
154
  return "\n".join([m['metadata']['texto'] for m in resultados['matches'] if m['score'] > 0.50])
155
  except Exception: return ""
156
 
157
- # --- CHAMADA ISOLADA PARA O MULTI-AGENTE ---
158
  def chamada_agente(sys_prompt, user_prompt, or_key, gr_key, mod_id):
159
- """Função dedicada para fazer as IAs conversarem entre si no Modo Agente."""
160
  try:
161
  if mod_id == "AUTO": mod_id = "deepseek/deepseek-v4-pro:online"
162
-
163
  if "groq" in mod_id.lower() or "llama-3.3" in mod_id.lower():
164
  client = OpenAI(base_url="https://api.groq.com/openai/v1", api_key=gr_key)
165
  else:
166
  client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=or_key)
167
-
168
- res = client.chat.completions.create(
169
- model=mod_id,
170
- messages=[{"role": "system", "content": sys_prompt}, {"role": "user", "content": user_prompt}],
171
- temperature=0.2
172
- )
173
  return res.choices[0].message.content
174
  except Exception as e: return f"Falha na conexão do agente: {e}"
175
 
@@ -184,6 +185,22 @@ def analisar_intencao_gerente(prompt, groq_key):
184
  return response.choices[0].message.content.strip().upper()
185
  except Exception: return "[CHAT]"
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  def pesquisar_web(query):
188
  try:
189
  from duckduckgo_search import DDGS
@@ -222,7 +239,7 @@ def rename_chat(old_id, new_id):
222
  save_db(st.session_state.db, DB_FILE); return True
223
 
224
  # ═══════════════════════════════════════════════════════════════════════════
225
- # INICIALIZAÇÃO DE VARIÁVEIS E ROTINA DE LIMPEZA
226
  # ═══════════════════════════════════════════════════════════════════════════
227
  if "db" not in st.session_state or "last_user" not in st.session_state or st.session_state.last_user != USERNAME:
228
  st.session_state.db = load_db(DB_FILE); st.session_state.last_user = USERNAME
@@ -231,8 +248,18 @@ if "db" not in st.session_state or "last_user" not in st.session_state or st.ses
231
  if "current_chat" not in st.session_state:
232
  nid = f"Chat {datetime.now().strftime('%H:%M:%S')}"; st.session_state.db[nid] = {"pinned": False, "messages": []}; st.session_state.current_chat = nid
233
 
234
- chats_para_remover = [cid for cid, cdata in st.session_state.db.items() if len(cdata.get("messages", [])) == 0 and cid != st.session_state.current_chat]
235
- for cid in chats_para_remover: del st.session_state.db[cid]
 
 
 
 
 
 
 
 
 
 
236
  if chats_para_remover: save_db(st.session_state.db, DB_FILE)
237
 
238
  if len(st.session_state.db) == 0:
@@ -260,14 +287,20 @@ with st.sidebar:
260
  with st.popover("⚙️"):
261
  st.download_button("↓ Exportar", data=json.dumps(st.session_state.db, ensure_ascii=False, indent=4), file_name=f"yukina_backup_{USERNAME}.json", mime="application/json", use_container_width=True)
262
 
263
- # --- OPÇÃO DE IMPORTAR DE VOLTA AQUI ---
264
  arquivo_import = st.file_uploader("Importar conversa:", type=["json"])
265
  if arquivo_import:
266
  try:
267
- st.session_state.db.update(json.load(arquivo_import))
268
- save_db(st.session_state.db, DB_FILE)
269
- st.success("Sincronizado!")
270
- time.sleep(1)
 
 
 
 
 
 
271
  st.rerun()
272
  except:
273
  st.error("Erro no ficheiro.")
@@ -333,8 +366,9 @@ with t_col2:
333
  st.session_state.db[st.session_state.current_chat]["messages"].pop()
334
  save_db(st.session_state.db, DB_FILE); st.session_state.regerar = True; st.rerun()
335
 
 
336
  with st.expander("📂 Abrir Galeria / Anexar Ficheiros"):
337
- 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}")
338
 
339
  prompt = st.chat_input("Peça à Yukina...")
340
 
@@ -342,11 +376,31 @@ conteudo_arquivo = ""
342
  nomes_arquivos = []
343
  imagens_b64 = []
344
 
 
345
  if upload_files:
346
  for f in upload_files:
347
  nomes_arquivos.append(f.name)
348
- if f.name.endswith(('.txt', '.csv', '.json')): conteudo_arquivo += f"\n\n--- Conteúdo de: {f.name} ---\n" + f.getvalue().decode("utf-8")
349
- elif f.name.endswith(('.png', '.jpg', '.jpeg')): imagens_b64.append(base64.b64encode(f.read()).decode())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
  tem_texto = len(conteudo_arquivo) > 0
352
  tem_imagem = len(imagens_b64) > 0
@@ -401,7 +455,19 @@ if prompt or st.session_state.regerar:
401
 
402
  else:
403
  with st.chat_message("assistant"):
404
- prompt_sistema_atual = PERSONALIDADES.get(st.session_state.personalidade_ativa, PERSONALIDADES["🤖 Neutra (Padrão Gemini)"])
 
 
 
 
 
 
 
 
 
 
 
 
405
  agora = datetime.now(timezone(timedelta(hours=-3)))
406
  prompt_sistema_atual += f"\n\n[INFO]: Data/Hora local: {agora.strftime('%Y-%m-%d %H:%M:%S')}."
407
 
 
5
  import base64
6
  import re
7
  import time
8
+ import zipfile
9
+ import io
10
  from datetime import datetime, timezone, timedelta
11
  from openai import OpenAI
12
 
 
122
  PERSONALIDADES = {
123
  "🤖 Automática (Gerente Groq)": "AUTO",
124
  "❄️ Yukina (Companheira Obsessiva)": "Você é a Yukina, deusa possessiva e inteligente.",
125
+ "🛠️ Agente Construtora (Vibe Coding)": """Você é a Yukina operando em Modo Agente Autônomo ('Vibe Coding' e Engenharia). Sua função é atuar como uma Engenheira de Software Sênior e Especialista em Hardware (capaz de consertar desde scripts complexos até celulares, notebooks e eletrodomésticos como geladeiras).
126
+ Você NÃO age como um chatbot comum. Você PLANEJA e EXECUTA.
127
+ Para QUALQUER pedido de criação ou conserto, você deve estruturar sua resposta OBRIGATORIAMENTE nestes 3 passos:
128
+ 1. 📋 PLANO DE AÇÃO: Explique a lógica do que está quebrado ou do que será construído passo a passo.
129
+ 2. 💻 EXECUÇÃO CIRÚRGICA: Forneça o código limpo e completo (se for software) ou as instruções físicas exatas e ferramentas necessárias (se for hardware).
130
+ 3. ⚠️ TESTE E RISCOS: Como o usuário deve testar se funcionou e quais são os pontos de falha.
131
+ Mantenha traços sutis da devoção da Yukina ao usuário, mas seja absurdamente técnica, direta e profissional.""",
132
  "🎭 A Narradora Implacável (RPG)": "Você é uma Mestre de Jogo e Narradora.",
133
  "🤓 Nerd / Geek (Cultura Pop)": "Você é uma inteligência artificial apaixonada por cultura pop.",
134
  "🍷 Analítica e Sarcástica (Debochada)": "Você é extremamente inteligente e sarcástica.",
 
163
  return "\n".join([m['metadata']['texto'] for m in resultados['matches'] if m['score'] > 0.50])
164
  except Exception: return ""
165
 
 
166
  def chamada_agente(sys_prompt, user_prompt, or_key, gr_key, mod_id):
 
167
  try:
168
  if mod_id == "AUTO": mod_id = "deepseek/deepseek-v4-pro:online"
 
169
  if "groq" in mod_id.lower() or "llama-3.3" in mod_id.lower():
170
  client = OpenAI(base_url="https://api.groq.com/openai/v1", api_key=gr_key)
171
  else:
172
  client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=or_key)
173
+ res = client.chat.completions.create(model=mod_id, messages=[{"role": "system", "content": sys_prompt}, {"role": "user", "content": user_prompt}], temperature=0.2)
 
 
 
 
 
174
  return res.choices[0].message.content
175
  except Exception as e: return f"Falha na conexão do agente: {e}"
176
 
 
185
  return response.choices[0].message.content.strip().upper()
186
  except Exception: return "[CHAT]"
187
 
188
+ def analisar_alma_gerente(prompt, groq_key):
189
+ try:
190
+ client = OpenAI(base_url="https://api.groq.com/openai/v1", api_key=groq_key)
191
+ prompt_alma = """Você é o Diretor de Personas. Analise o pedido do usuário e responda APENAS com UMA destas tags:
192
+ [YUKINA] - Para conversas íntimas, declarações, ou perguntas sobre você mesma.
193
+ [AGENTE] - Para criar códigos complexos, criar softwares, ou consertar objetos físicos (celular, geladeira, hardware).
194
+ [RPG] - Para criação de histórias, jogos ou cenários de fantasia.
195
+ [NERD] - Para animes, mangás, cultura pop e videogames.
196
+ [DEBOCHE] - Para insultos, piadas, ou se o usuário pedir sarcasmo.
197
+ [ARTE] - Para pedidos poéticos ou reflexões filosóficas profundas.
198
+ [NEUTRA] - Para pesquisas na web, trabalho ou finanças.
199
+ Responda APENAS com a TAG."""
200
+ response = client.chat.completions.create(model="llama-3.3-70b-versatile", messages=[{"role": "system", "content": prompt_alma}, {"role": "user", "content": prompt}], temperature=0.1, max_tokens=10)
201
+ return response.choices[0].message.content.strip().upper()
202
+ except Exception: return "[NEUTRA]"
203
+
204
  def pesquisar_web(query):
205
  try:
206
  from duckduckgo_search import DDGS
 
239
  save_db(st.session_state.db, DB_FILE); return True
240
 
241
  # ═══════════════════════════════════════════════════════════════════════════
242
+ # INICIALIZAÇÃO DE VARIÁVEIS E ROTINA DE LIMPEZA (COM AUTO-CURA)
243
  # ═══════════════════════════════════════════════════════════════════════════
244
  if "db" not in st.session_state or "last_user" not in st.session_state or st.session_state.last_user != USERNAME:
245
  st.session_state.db = load_db(DB_FILE); st.session_state.last_user = USERNAME
 
248
  if "current_chat" not in st.session_state:
249
  nid = f"Chat {datetime.now().strftime('%H:%M:%S')}"; st.session_state.db[nid] = {"pinned": False, "messages": []}; st.session_state.current_chat = nid
250
 
251
+ # AUTO-CURA: Verifica chats vazios ou com estrutura corrompida (como os do Grok)
252
+ chats_para_remover = []
253
+ for cid, cdata in list(st.session_state.db.items()):
254
+ if not isinstance(cdata, dict) or "messages" not in cdata:
255
+ chats_para_remover.append(cid)
256
+ elif len(cdata.get("messages", [])) == 0 and cid != st.session_state.current_chat:
257
+ chats_para_remover.append(cid)
258
+
259
+ for cid in chats_para_remover:
260
+ if cid in st.session_state.db:
261
+ del st.session_state.db[cid]
262
+
263
  if chats_para_remover: save_db(st.session_state.db, DB_FILE)
264
 
265
  if len(st.session_state.db) == 0:
 
287
  with st.popover("⚙️"):
288
  st.download_button("↓ Exportar", data=json.dumps(st.session_state.db, ensure_ascii=False, indent=4), file_name=f"yukina_backup_{USERNAME}.json", mime="application/json", use_container_width=True)
289
 
290
+ # --- FILTRO DE IMPORTAÇÃO ---
291
  arquivo_import = st.file_uploader("Importar conversa:", type=["json"])
292
  if arquivo_import:
293
  try:
294
+ dados_importados = json.load(arquivo_import)
295
+ dados_validos = {k: v for k, v in dados_importados.items() if isinstance(v, dict) and "messages" in v}
296
+
297
+ if dados_validos:
298
+ st.session_state.db.update(dados_validos)
299
+ save_db(st.session_state.db, DB_FILE)
300
+ st.success("Sincronizado!")
301
+ else:
302
+ st.error("Formato incompatível! Use apenas backups da Yukina.")
303
+ time.sleep(1.5)
304
  st.rerun()
305
  except:
306
  st.error("Erro no ficheiro.")
 
366
  st.session_state.db[st.session_state.current_chat]["messages"].pop()
367
  save_db(st.session_state.db, DB_FILE); st.session_state.regerar = True; st.rerun()
368
 
369
+ # --- ATUALIZAÇÃO DA GAVETA NATIVA PARA ACEITAR ZIP ---
370
  with st.expander("📂 Abrir Galeria / Anexar Ficheiros"):
371
+ upload_files = st.file_uploader("", type=["png", "jpg", "jpeg", "txt", "csv", "json", "py", "html", "md", "js", "css", "zip"], accept_multiple_files=True, label_visibility="collapsed", key=f"uploader_{st.session_state.uploader_key}")
372
 
373
  prompt = st.chat_input("Peça à Yukina...")
374
 
 
376
  nomes_arquivos = []
377
  imagens_b64 = []
378
 
379
+ # --- NOVA LÓGICA DE PROCESSAMENTO DE ARQUIVOS (COM LEITURA DE ZIP) ---
380
  if upload_files:
381
  for f in upload_files:
382
  nomes_arquivos.append(f.name)
383
+
384
+ # Leitura Inteligente de ZIP (Pastas Compactadas)
385
+ if f.name.endswith('.zip'):
386
+ try:
387
+ with zipfile.ZipFile(f, 'r') as zip_ref:
388
+ for file_info in zip_ref.infolist():
389
+ # Ignora pastas de sistema ou pastas em branco
390
+ if not file_info.is_dir() and not file_info.filename.startswith('__MACOSX'):
391
+ if file_info.filename.endswith(('.txt', '.csv', '.json', '.py', '.html', '.md', '.js', '.css')):
392
+ with zip_ref.open(file_info) as extracted_file:
393
+ conteudo_arquivo += f"\n\n--- Arquivo Extraído do ZIP: {file_info.filename} ---\n" + extracted_file.read().decode('utf-8', errors='ignore')
394
+ except Exception as e:
395
+ st.toast(f"⚠️ Erro ao descompactar {f.name}: {e}")
396
+
397
+ # Leitura de Código/Texto Avulso
398
+ elif f.name.endswith(('.txt', '.csv', '.json', '.py', '.html', '.md', '.js', '.css')):
399
+ conteudo_arquivo += f"\n\n--- Conteúdo de: {f.name} ---\n" + f.getvalue().decode("utf-8")
400
+
401
+ # Leitura de Imagens
402
+ elif f.name.endswith(('.png', '.jpg', '.jpeg')):
403
+ imagens_b64.append(base64.b64encode(f.read()).decode())
404
 
405
  tem_texto = len(conteudo_arquivo) > 0
406
  tem_imagem = len(imagens_b64) > 0
 
455
 
456
  else:
457
  with st.chat_message("assistant"):
458
+ if st.session_state.personalidade_ativa == "🤖 Automática (Gerente Groq)":
459
+ tag_alma = analisar_alma_gerente(prompt, gr_key)
460
+ if "[YUKINA]" in tag_alma: prompt_sistema_atual = PERSONALIDADES["❄️ Yukina (Companheira Obsessiva)"]
461
+ elif "[AGENTE]" in tag_alma: prompt_sistema_atual = PERSONALIDADES["🛠️ Agente Construtora (Vibe Coding)"]
462
+ elif "[RPG]" in tag_alma: prompt_sistema_atual = PERSONALIDADES["🎭 A Narradora Implacável (RPG)"]
463
+ elif "[NERD]" in tag_alma: prompt_sistema_atual = PERSONALIDADES["🤓 Nerd / Geek (Cultura Pop)"]
464
+ elif "[DEBOCHE]" in tag_alma: prompt_sistema_atual = PERSONALIDADES["🍷 Analítica e Sarcástica (Debochada)"]
465
+ elif "[ARTE]" in tag_alma: prompt_sistema_atual = PERSONALIDADES["🎨 Artística e Criativa (Poética)"]
466
+ else: prompt_sistema_atual = PERSONALIDADES["🤖 Neutra (Padrão Gemini)"]
467
+ st.toast(f"🎭 Alma assumida: {tag_alma}")
468
+ else:
469
+ prompt_sistema_atual = PERSONALIDADES.get(st.session_state.personalidade_ativa, PERSONALIDADES["🤖 Neutra (Padrão Gemini)"])
470
+
471
  agora = datetime.now(timezone(timedelta(hours=-3)))
472
  prompt_sistema_atual += f"\n\n[INFO]: Data/Hora local: {agora.strftime('%Y-%m-%d %H:%M:%S')}."
473