🤖 OpenAirAI-X Chat
Русскоязычный AI-ассистент
import gradio as gr from transformers import AutoModelForCausalLM, AutoTokenizer import torch import json from pathlib import Path from datetime import datetime import requests # === ЗАГРУЗКА МОДЕЛИ === print("🚀 Загрузка модели...") model_id = "OpenRussianAI/OpenAirAI-X" tokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForCausalLM.from_pretrained(model_id) device = "cuda" if torch.cuda.is_available() else "cpu" model = model.to(device).eval() print(f"✅ Модель загружена на {device}") # === ХРАНИЛИЩЕ ИСТОРИИ === HISTORY_DIR = Path("chat_history") HISTORY_DIR.mkdir(exist_ok=True) def get_history_path(username): return HISTORY_DIR / f"{username}.json" def load_history(username): path = get_history_path(username) if path.exists(): with open(path, "r", encoding="utf-8") as f: return json.load(f) return {} def save_history(username, history_data): path = get_history_path(username) with open(path, "w", encoding="utf-8") as f: json.dump(history_data, f, ensure_ascii=False, indent=2) def check_if_pro(token): if not token: return False try: response = requests.get( "https://huggingface.co/api/whoami-v2", headers={"Authorization": f"Bearer {token}"}, timeout=5 ) if response.status_code == 200: data = response.json() return data.get("isPro", False) or data.get("plan", {}).get("name") == "PRO" except Exception as e: print(f"Ошибка проверки PRO: {e}") return False # === ГЕНЕРАЦИЯ ОТВЕТА === def generate_response(message, history, username, current_chat_id): if not username: gr.Warning("Пожалуйста, войдите через Hugging Face") return history, gr.update(), current_chat_id if not message.strip(): return history, gr.update(), current_chat_id # Новый формат Gradio 6+: список словарей history = history + [{"role": "user", "content": message}] # Формируем промпт из всей истории prompt = "" for msg in history: if msg["role"] == "user": prompt += f"Пользователь: {msg['content']}\n" elif msg["role"] == "assistant": prompt += f"AI: {msg['content']}\n" prompt += "AI:" inputs = tokenizer(prompt, return_tensors="pt").to(device) with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=256, temperature=0.7, top_p=0.9, do_sample=True, pad_token_id=tokenizer.eos_token_id ) response = tokenizer.decode(outputs[0], skip_special_tokens=True) ai_response = response.split("AI:")[-1].strip() history = history + [{"role": "assistant", "content": ai_response}] # Сохраняем в хранилище if username and current_chat_id: history_data = load_history(username) if current_chat_id not in history_data: history_data[current_chat_id] = { "title": message[:40] + ("..." if len(message) > 40 else ""), "created": datetime.now().isoformat(), "messages": [] } history_data[current_chat_id]["messages"] = history save_history(username, history_data) return history, gr.update(value=""), current_chat_id # === УПРАВЛЕНИЕ ЧАТАМИ === def new_chat(username): if not username: return [], None, [] chat_id = datetime.now().strftime("%Y%m%d_%H%M%S") history_data = load_history(username) history_data[chat_id] = { "title": "Новый чат", "created": datetime.now().isoformat(), "messages": [] } save_history(username, history_data) return [], chat_id, get_chat_list(username) def load_chat(chat_title, username, current_chat_id): if not username or not chat_title: return [], current_chat_id history_data = load_history(username) for cid, data in history_data.items(): if data["title"] == chat_title: return data["messages"], cid return [], current_chat_id def get_chat_list(username): if not username: return [] history_data = load_history(username) sorted_chats = sorted( history_data.items(), key=lambda x: x[1].get("created", ""), reverse=True ) return [data["title"] for _, data in sorted_chats] def delete_chat(chat_title, username): if not username or not chat_title: return [], {}, None, [] history_data = load_history(username) for cid, data in list(history_data.items()): if data["title"] == chat_title: del history_data[cid] break save_history(username, history_data) return [], history_data, None, get_chat_list(username) # === CSS === CUSTOM_CSS = """ .main-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px; margin-bottom: 20px; text-align: center; } .main-header h1 { margin: 0; font-size: 2em; } .pro-badge { background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); color: #333; padding: 4px 12px; border-radius: 20px; font-weight: bold; display: inline-block; margin-left: 10px; box-shadow: 0 2px 8px rgba(255, 215, 0, 0.4); } .user-info { padding: 10px; background: white; border-radius: 8px; margin-bottom: 10px; text-align: center; font-weight: 600; } """ # === ИНТЕРФЕЙС === with gr.Blocks(css=CUSTOM_CSS, title="OpenAirAI-X Chat") as demo: # Состояния username_state = gr.State("") history_state = gr.State({}) current_chat_id_state = gr.State(None) with gr.Column(visible=False) as main_interface: gr.HTML("""
Русскоязычный AI-ассистент
Войдите через Hugging Face, чтобы начать