File size: 10,385 Bytes
0ba222a
d488a46
a4fbbd9
06d2c4c
19ae06c
5844efd
 
3dd96ad
ff3ed46
1330dfd
 
5844efd
3dd96ad
9cb2585
 
 
 
 
 
2576262
9cb2585
 
2576262
2e6042e
 
9cb2585
 
 
1330dfd
2576262
1330dfd
 
9cb2585
 
2576262
 
 
 
 
9cb2585
140ce7b
2576262
08f0468
140ce7b
08f0468
140ce7b
3dd96ad
57f8dff
 
 
 
 
08f0468
c7d6c6c
2a59b1d
3dd96ad
 
 
 
 
8e1d8b2
3298092
3dd96ad
3298092
2576262
3dd96ad
3298092
 
2a59b1d
2576262
8e1d8b2
3dd96ad
 
1330dfd
35c78d9
3dd96ad
86eeb9f
8e1d8b2
2576262
 
1330dfd
2576262
3dd96ad
57f8dff
 
9e51f4f
3dd96ad
2576262
3dd96ad
 
2576262
c7d6c6c
ff3ed46
8e1d8b2
 
c7d6c6c
2576262
8e1d8b2
2576262
8e1d8b2
2576262
 
2a59b1d
2576262
8e1d8b2
1330dfd
2576262
 
 
 
 
 
 
 
 
 
 
 
ff3ed46
2576262
ff3ed46
2576262
 
 
 
ff3ed46
2576262
 
ff3ed46
2576262
ff3ed46
 
 
 
 
 
 
 
 
8e1d8b2
ff3ed46
 
8e1d8b2
ff3ed46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2576262
3dd96ad
 
57f8dff
3dd96ad
c7d6c6c
3dd96ad
2576262
 
8e1d8b2
ff3ed46
3dd96ad
 
8e1d8b2
3dd96ad
8e1d8b2
da9580c
67cb4c8
 
c43d6b3
8e1d8b2
 
 
 
 
 
 
 
 
 
 
 
2576262
c7d6c6c
67cb4c8
2576262
c7d6c6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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()