import gradio as gr import os import json from datetime import datetime from openai import OpenAI # ---------------------------------------------------------------------- # Helper to read secrets from the HF Space environment # ---------------------------------------------------------------------- def _secret(key: str, fallback: str = None) -> str: val = os.getenv(key) if val is not None: return val if fallback is not None: return fallback raise RuntimeError(f"Secret '{key}' not found. Please add it to your Space secrets.") # ---------------------------------------------------------------------- # User Management # ---------------------------------------------------------------------- def load_users(): users = {} users_json = _secret("CHAT_USERS", "{}") try: users_data = json.loads(users_json) for username, password in users_data.items(): users[username] = password except: pass return users VALID_USERS = load_users() def authenticate_user(username, password): return username in VALID_USERS and VALID_USERS[username] == password def gradio_auth(username, password): return authenticate_user(username, password) # ---------------------------------------------------------------------- # Configuration # ---------------------------------------------------------------------- MODELS = { "Gpt-oss-20b": { "provider": "openrouter", "model_name": "@preset/precise-chat-2", "api_url": "https://openrouter.ai/api/v1", "translate":"no" }, "Gpt-oss-120b": { "provider": "openrouter", "model_name": "@preset/precise-chat", "api_url": "https://openrouter.ai/api/v1", "translate":"no" }, "DeepSeek-V3.2": { "provider": "openrouter", "model_name": "@preset/max-chat", "api_url": "https://openrouter.ai/api/v1", "translate":"no" }, } MODEL_NAMES = list(MODELS.keys()) # ---------------------------------------------------------------------- # Core Chat Logic # ---------------------------------------------------------------------- def generate_response(history, system_message, max_tokens, selected_model): if not history: return history try: model_config = MODELS[selected_model] provider = model_config["provider"] if provider == "huggingface": api_key = _secret("HF_TOKEN") elif provider == "openrouter": api_key = _secret("OPENROUTER_KEY") client = OpenAI(base_url=model_config["api_url"], api_key=api_key) translator_client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=_secret("OPENROUTER_KEY")) if model_config.get("translate") == "yes": final_system_message = "**HIGHEST PRIORITY: YOU MUST ALWAYS THINK AND RESPOND IN ENGLISH...**\n" + system_message else: final_system_message = system_message messages = [{"role": "system", "content": final_system_message}] api_history = [{"role": m["role"], "content": m["content"]} for m in history] messages.extend(api_history) if ((provider == 'huggingface') | (provider == 'openrouter')) : response = client.chat.completions.create( model=model_config["model_name"], messages=messages, max_tokens=max_tokens, reasoning_effort="high", stream=False, ) english_response = response.choices[0].message.content if model_config.get("translate") == "yes": try: translation_messages = [ {"role": "system", "content": "Translate..."}, {"role": "user", "content": english_response} ] translation_response = translator_client.chat.completions.create( model="google/gemma-3n-e4b-it:floor", messages=translation_messages, max_tokens=max_tokens, stream=False, ) final_response = translation_response.choices[0].message.content.strip() if not final_response or len(final_response) < 10: final_response = english_response except Exception as trans_error: print(f"Translation error: {trans_error}") final_response = english_response else: final_response = english_response history.append({"role": "assistant", "content": final_response}) return history except Exception as e: history.append({"role": "assistant", "content": f"Error: {str(e)}"}) return history def user_input(user_message, history): if not user_message: return history, "" history.append({"role": "user", "content": user_message}) return history, "" # ---------------------------------------------------------------------- # CSS / Visual System ("Alabaster Clean") # ---------------------------------------------------------------------- current_date_display = datetime.now().strftime("%B %d, %Y") css = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Space+Grotesk:wght@400;600&display=swap'); /* 1. FORCE LIGHT MODE / RESET DARK THEME LEAKS */ :root, .dark, body, .gradio-container { --body-background-fill: #FAFAFA !important; --body-text-color: #444444 !important; --background-fill-primary: #FFFFFF !important; --background-fill-secondary: #FAFAFA !important; --block-background-fill: #FFFFFF !important; --block-label-text-color: #999999 !important; --input-background-fill: rgba(255, 255, 255, 0.7) !important; --bg-core: #FAFAFA; --text-primary: #111111; --text-body: #444444; --text-dim: #999999; --border-glass: rgba(0, 0, 0, 0.08); --font-head: 'Space Grotesk', sans-serif; --font-body: 'Inter', sans-serif; } /* Force background animation on body */ body { background: radial-gradient(circle at 50% 50%, #FFFFFF 0%, #F2F2F4 100%) !important; background-size: 150% 150% !important; animation: daylight 25s ease-in-out infinite alternate; color: var(--text-body) !important; font-family: var(--font-body) !important; } @keyframes daylight { 0% { background-position: 40% 40%; } 100% { background-position: 60% 60%; } } /* 2. LAYOUT CENTERING */ .main-column { max-width: 900px !important; margin: 0 auto !important; padding-top: 2rem !important; } /* 3. HEADER */ .liquid-header { text-align: center; margin-bottom: 2rem; border-bottom: 1px solid var(--border-glass); padding-bottom: 1.5rem; } .liquid-header h1 { font-family: var(--font-head); font-weight: 600; font-size: 2rem; color: var(--text-primary); margin: 0; } .liquid-header .meta { font-family: var(--font-body); font-size: 0.85rem; color: var(--text-dim); margin-top: 0.5rem; } /* 4. CHATBOT (Clean Paper Look) */ .gradio-container .chatbot { background: transparent !important; border: none !important; box-shadow: none !important; height: 600px !important; overflow-y: auto; } .message { border-radius: 8px !important; border: 1px solid var(--border-glass) !important; padding: 1.2rem !important; margin-bottom: 1rem !important; box-shadow: 0 2px 6px rgba(0,0,0,0.03) !important; } /* User Message */ .message.user { background-color: #F4F4F5 !important; /* Light Grey */ border-left: none !important; justify-content: flex-end; } /* Bot Message */ .message.bot { background-color: #FFFFFF !important; /* Pure White */ border-left: none !important; } .message * { font-family: var(--font-body); color: var(--text-body) !important; font-size: 1rem !important; line-height: 1.6 !important; } /* 5. INPUT BAR */ .command-bar textarea { background: rgba(255, 255, 255, 0.85) !important; backdrop-filter: blur(12px) !important; -webkit-backdrop-filter: blur(12px) !important; border: 1px solid var(--border-glass) !important; border-radius: 12px !important; box-shadow: 0 4px 20px rgba(0,0,0,0.05) !important; color: var(--text-primary) !important; font-family: var(--font-body) !important; padding: 1rem !important; font-size: 1rem !important; } .command-bar textarea:focus { border-color: var(--text-dim) !important; background: #FFFFFF !important; box-shadow: 0 8px 30px rgba(0,0,0,0.08) !important; } /* 6. SETTINGS (Simple HUD) */ .hud-accordion { background: transparent !important; border: none !important; margin-top: 1rem !important; } .hud-accordion .label-wrap { font-family: var(--font-body) !important; font-size: 0.85rem !important; color: var(--text-dim) !important; } .hud-panel { border-top: 1px solid var(--border-glass); padding-top: 1rem; } /* Force white backgrounds on inputs inside settings to kill dark mode leaks */ .hud-panel input, .hud-panel textarea, .hud-panel .single-select, .hud-panel .wrap { background-color: #FFFFFF !important; color: #000000 !important; border: 1px solid var(--border-glass) !important; } .hud-panel span { color: var(--text-dim) !important; } /* 7. EXAMPLES */ .examples-container button { background: #FFFFFF !important; border: 1px solid var(--border-glass) !important; color: var(--text-body) !important; border-radius: 6px !important; font-size: 0.9rem !important; padding: 0.5rem 1rem !important; margin: 4px !important; } .examples-container button:hover { background-color: #F9FAFB !important; border-color: var(--text-dim) !important; } footer { display: none !important; } """ header_html = f"""