HiperDoubleAI / src /streamlit_app.py
Abobasnik's picture
Update src/streamlit_app.py
ff3ed46 verified
import streamlit as st
from openai import OpenAI
import requests
import os
import json
from streamlit_cookies_manager import EncryptedCookieManager
# --- ИНИЦИАЛИЗАЦИЯ ПАМЯТИ ---
cookies = EncryptedCookieManager(password="HiperDouble_Full_Control_2026")
if not cookies.ready():
st.stop()
# --- GOOGLE AUTH ---
CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID")
CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET")
host = st.context.headers.get("Host", "")
REDIRECT_URI = f"https://{host}/" if host else ""
def get_google_auth_url():
params = {"client_id": CLIENT_ID, "redirect_uri": REDIRECT_URI, "response_type": "code", "scope": "openid email profile", "access_type": "offline", "prompt": "select_account"}
return f"https://accounts.google.com/o/oauth2/v2/auth?{'&'.join([f'{k}={v}' for k, v in params.items()])}"
if "user_email" not in st.session_state:
st.session_state.user_email = cookies.get("saved_email")
st.session_state.user_name = cookies.get("saved_name", "Пользователь")
if "code" in st.query_params and not st.session_state.user_email:
try:
res = requests.post("https://oauth2.googleapis.com/token", data={
"code": st.query_params["code"], "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI, "grant_type": "authorization_code"
}).json()
token = res.get("access_token")
if token:
info = requests.get("https://www.googleapis.com/oauth2/v3/userinfo", headers={"Authorization": f"Bearer {token}"}).json()
st.session_state.user_email, st.session_state.user_name = info.get("email"), info.get("name")
cookies["saved_email"], cookies["saved_name"] = info.get("email"), info.get("name")
cookies.save(); st.rerun()
except: pass
def logout():
cookies["saved_email"], cookies["saved_name"] = "", ""
cookies.save()
st.session_state.user_email = None
st.rerun()
# --- ДИЗАЙН И СТИЛИ (CSS) ---
st.set_page_config(page_title="HiperDouble AI", page_icon="🧬", layout="wide")
st.markdown("""
<style>
html, body, [data-testid="stAppViewContainer"] {
background: radial-gradient(circle at top right, #1a0b2e, #020205);
background-attachment: fixed;
}
.fixed-header {
position: fixed; top: 0; left: 0; width: 100%;
z-index: 999; background: rgba(2, 2, 5, 0.85);
backdrop-filter: blur(15px); padding: 10px 0;
border-bottom: 1px solid rgba(115, 103, 240, 0.2);
}
.main-title {
font-size: 2.2rem; font-weight: 900; text-align: center;
background: linear-gradient(90deg, #00f2fe, #7367f0, #ff00cc, #00f2fe);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
animation: shine 4s linear infinite; margin: 0;
}
@keyframes shine { to { background-position: 200% center; } }
[data-testid="stBottom"] {
position: fixed !important; bottom: 0px !important;
background: rgba(10, 10, 20, 0.98) !important;
backdrop-filter: blur(15px); z-index: 1000 !important;
padding: 10px 5% 40px 5% !important;
border-top: 1px solid rgba(115, 103, 240, 0.4);
}
.chat-container { margin-top: 100px; }
.chat-bubble {
padding: 18px; border-radius: 20px; margin-bottom: 15px;
background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1);
color: #f0f0f0; backdrop-filter: blur(10px);
}
.user-bubble { border-left: 5px solid #ff00cc; background: rgba(255, 0, 204, 0.07); }
footer { visibility: hidden; }
</style>
""", unsafe_allow_html=True)
# --- ВХОД ---
if not st.session_state.user_email:
st.markdown('<div class="fixed-header"><p class="main-title">HiperDouble AI</p></div>', unsafe_allow_html=True)
st.markdown(f'<div style="text-align:center; margin-top:35vh;"><a href="{get_google_auth_url()}" target="_self" style="background:#4285F4; color:white; padding:15px 30px; text-decoration:none; border-radius:10px; font-weight:bold;">Войти через Google</a></div>', unsafe_allow_html=True)
st.stop()
# --- ЛОГИКА БД ---
u_id = st.session_state.user_email.replace('@','_').replace('.','_')
DB_FILE = f"chats_db_{u_id}.json"
def load_chats():
if os.path.exists(DB_FILE):
try:
with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f) or {"Чат 1": []}
except: return {"Чат 1": []}
return {"Чат 1": []}
def save_chats(chats):
with open(DB_FILE, "w", encoding="utf-8") as f: json.dump(chats, f, ensure_ascii=False, indent=4)
MODELS_CONFIG = {
"🌌 HiperAi v2.1 (Grew up)": {"engine": "groq", "key_name": "GROQ_API_KEY3", "model": "llama-3.3-70b-versatile", "identity": "HiperAI v2.1 Grew up."},
"🧠 HiperAI v2.3 (CORTEX)": {"engine": "groq", "key_name": "GROQ_API_KEY", "model": "llama-3.3-70b-versatile", "identity": "HiperAI v2.3 Cortex."},
"🔥 HiperAI v2.1 (ADULT)": {"engine": "groq", "key_name": "GROQ_API_KEY2", "model": "llama-3.1-8b-instant", "identity": "HiperAI v2.1 Adult."},
"🌐 HiperAI v2.2 (NETWORK)": {"engine": "groq", "key_name": "GROQ_API_KEY", "model": "llama-3.1-8b-instant", "identity": "HiperAI v2.2 Network."},
"👶 HiperAI v2.1 (BABY)": {"engine": "titan", "model": "titan-89m", "identity": "HiperAI v2.1 Baby."},
"🚀 HiperAI v2.0 (Test 1)": {"engine": "groq", "key_name": "GROQ_API_KEY", "model": "llama-3.3-70b-versatile", "identity": "HiperAI v2.0 Test."},
"✨ HiperAI v1.1.3 (Stable)": {"engine": "openai", "model": "gpt-4o-mini", "identity": "HiperAI v1.1.3."},
}
if "chats" not in st.session_state: st.session_state.chats = load_chats()
if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.chats.keys())[0]
if "rename_mode" not in st.session_state: st.session_state.rename_mode = None
# --- SIDEBAR (ПОЛНЫЙ ФУНКЦИОНАЛ) ---
with st.sidebar:
st.markdown(f"👤 **{st.session_state.user_name}**")
if st.button("🚪 Выйти", use_container_width=True): logout()
st.markdown("---")
sel_mod = st.selectbox("🤖 Модель:", list(MODELS_CONFIG.keys()))
cfg = MODELS_CONFIG[sel_mod]
if st.button("➕ Новый чат", use_container_width=True):
new_n = f"Чат {len(st.session_state.chats)+1}"
st.session_state.chats[new_n] = []
st.session_state.current_chat = new_n
save_chats(st.session_state.chats); st.rerun()
if st.button("🗑️ Очистить текущий", use_container_width=True):
st.session_state.chats[st.session_state.current_chat] = []
save_chats(st.session_state.chats); st.rerun()
st.markdown("---")
st.write("📂 Ваши чаты:")
for c_name in list(st.session_state.chats.keys()):
col_main, col_edit, col_del = st.columns([0.6, 0.2, 0.2])
# Кнопка выбора чата
if col_main.button(f"💬 {c_name[:12]}", key=f"sel_{c_name}", use_container_width=True):
st.session_state.current_chat = c_name
st.rerun()
# Кнопка переименования
if col_edit.button("✏️", key=f"ed_{c_name}"):
st.session_state.rename_mode = c_name
# Кнопка удаления
if col_del.button("❌", key=f"del_{c_name}"):
if len(st.session_state.chats) > 1:
del st.session_state.chats[c_name]
st.session_state.current_chat = list(st.session_state.chats.keys())[0]
save_chats(st.session_state.chats); st.rerun()
# Поле для переименования, если выбран этот чат
if st.session_state.rename_mode == c_name:
new_title = st.text_input("Новое имя:", c_name, key=f"input_{c_name}")
if st.button("OK", key=f"ok_{c_name}"):
st.session_state.chats[new_title] = st.session_state.chats.pop(c_name)
st.session_state.current_chat = new_title
st.session_state.rename_mode = None
save_chats(st.session_state.chats); st.rerun()
# --- ЧАТ ---
st.markdown('<div class="fixed-header"><p class="main-title">HiperDouble AI</p></div>', unsafe_allow_html=True)
st.markdown('<div class="chat-container">', unsafe_allow_html=True)
if st.session_state.current_chat in st.session_state.chats:
for m in reversed(st.session_state.chats[st.session_state.current_chat]):
role, style = ("Вы", "user-bubble") if m['role'] == 'user' else ("HiperAi", "")
st.markdown(f"<div class='chat-bubble {style}'><b>{role}:</b><br>{m['content']}</div>", unsafe_allow_html=True)
# Невидимый блок (180px)
st.markdown("<div style='height: 180px; width: 100%; pointer-events: none;'></div>", unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
# ВВОД
u_input = st.chat_input("Напишите HiperAI...")
if u_input:
st.session_state.chats[st.session_state.current_chat].append({"role": "user", "content": u_input})
try:
api_key = st.secrets.get(cfg["key_name"]) or os.environ.get(cfg["key_name"], "").strip()
if cfg["engine"] == "titan":
res_text = "HiperAI Titan активен."
elif cfg["engine"] == "openai":
client = OpenAI(api_key=st.secrets.get("OPENAI_API_KEY") or os.environ.get("OPENAI_API_KEY"))
r = client.chat.completions.create(model=cfg["model"], messages=[{"role":"system","content":cfg['identity']},{"role":"user","content":u_input}])
res_text = r.choices[0].message.content
else:
resp = requests.post("https://api.groq.com/openai/v1/chat/completions",
json={"model": cfg["model"], "messages": [{"role": "system", "content": cfg['identity']}] + st.session_state.chats[st.session_state.current_chat][-6:]},
headers={"Authorization": f"Bearer {api_key}"}, timeout=25).json()
res_text = resp['choices'][0]['message']['content']
except: res_text = "⚠️ Ошибка модели."
st.session_state.chats[st.session_state.current_chat].append({"role": "assistant", "content": res_text})
save_chats(st.session_state.chats); st.rerun()