import gradio as gr import torch from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer from threading import Thread import spaces import json import time from datetime import datetime # ── Model ────────────────────────────────────────────────────────────────────── MODEL_ID = "huihui-ai/Huihui-Qwen3.5-2B-abliterated" tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) model = AutoModelForCausalLM.from_pretrained( MODEL_ID, torch_dtype=torch.bfloat16, device_map="auto", ) model.eval() # ── Presets ──────────────────────────────────────────────────────────────────── SYSTEM_PRESETS = { "🤖 Varsayılan Asistan": "You are a helpful, harmless and honest assistant.", "💻 Kod Uzmanı": "You are an expert software engineer. Write clean, efficient, well-documented code. Always explain your reasoning.", "✍️ Yaratıcı Yazar": "You are a creative writing assistant with a vivid imagination. Help craft compelling stories, characters, and narratives.", "🔬 Bilim Danışmanı": "You are a knowledgeable science advisor. Explain complex topics clearly with accurate information and real-world examples.", "🗣️ Türkçe Asistan": "Sen yardımsever, bilgili bir Türkçe asistansın. Her zaman Türkçe yanıt ver ve net, anlaşılır açıklamalar yap.", "🎯 Özel": "", } # ── GPU generation (streaming) ──────────────────────────────────────────────── @spaces.GPU def generate_stream(message, history, system_prompt, max_new_tokens, temperature, top_p, repetition_penalty): messages = [] if system_prompt.strip(): messages.append({"role": "system", "content": system_prompt}) for h in history: # Yeni Gradio: ChatMessage dict {role, content} | Eski: [user, bot] if isinstance(h, dict): if h.get("content"): messages.append({"role": h["role"], "content": h["content"]}) else: if h[0]: messages.append({"role": "user", "content": str(h[0])}) if h[1]: messages.append({"role": "assistant", "content": str(h[1])}) messages.append({"role": "user", "content": message}) text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer([text], return_tensors="pt").to(model.device) streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) gen_kwargs = dict( **inputs, streamer=streamer, max_new_tokens=max_new_tokens, temperature=temperature, top_p=top_p, repetition_penalty=repetition_penalty, do_sample=temperature > 0.01, ) thread = Thread(target=model.generate, kwargs=gen_kwargs) thread.start() partial = "" for chunk in streamer: partial += chunk yield partial thread.join() # ── Helpers ──────────────────────────────────────────────────────────────────── def update_system_prompt(preset_name): return SYSTEM_PRESETS.get(preset_name, "") def export_chat(history, system_prompt): if not history: return None data = { "exported_at": datetime.now().isoformat(), "model": MODEL_ID, "system_prompt": system_prompt, "messages": [ {"user": h.get("content") if isinstance(h, dict) and h.get("role") == "user" else (h[0] if not isinstance(h, dict) else ""), "assistant": ""} if (isinstance(h, dict) and h.get("role") == "user") else {"assistant": h.get("content") if isinstance(h, dict) else h[1]} for h in history ], } path = f"/tmp/chat_{int(time.time())}.json" with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) return path def count_tokens(text): if not text: return 0 return len(tokenizer.encode(text)) def get_stats(history): if not history: return '
Henüz mesaj yok.
' total_msgs = len(history) * 2 total_chars = sum( len(h.get("content", "") if isinstance(h, dict) else (h[0] or "") + (h[1] or "")) for h in history ) return f'
💬 {total_msgs} mesaj  •  📝 {total_chars:,} karakter
' def reset_parameters(): return 1024, 0.7, 0.9, 1.1 def update_token_count(text): n = count_tokens(text) color = "#4ade80" if n < 512 else "#fbbf24" if n < 1024 else "#f87171" return f'
Tokens: {n:,}
' # ── CSS ──────────────────────────────────────────────────────────────────────── CSS = """ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&family=Inter:wght@300;400;500&display=swap'); :root { --bg-primary: #0a0a0f; --bg-secondary: #111118; --bg-tertiary: #18181f; --bg-card: #1c1c25; --border: #2a2a38; --border-light: #35354a; --accent: #7c6af7; --accent-bright: #9d8fff; --accent-glow: rgba(124, 106, 247, 0.15); --text-primary: #e8e8f0; --text-secondary:#9090a8; --text-muted: #55556a; --user-bg: #1e1a3a; --bot-bg: #141420; --success: #4ade80; --warning: #fbbf24; --danger: #f87171; --radius: 12px; --radius-lg: 18px; } *, *::before, *::after { box-sizing: border-box; } body, .gradio-container { font-family: 'Inter', sans-serif !important; background: var(--bg-primary) !important; color: var(--text-primary) !important; min-height: 100vh; } .gradio-container::before { content: ''; position: fixed; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(ellipse at 20% 20%, rgba(124,106,247,0.06) 0%, transparent 50%), radial-gradient(ellipse at 80% 80%, rgba(99,179,237,0.04) 0%, transparent 50%); pointer-events: none; z-index: 0; animation: bgShift 20s ease-in-out infinite alternate; } @keyframes bgShift { from { transform: translate(0,0) rotate(0deg); } to { transform: translate(2%,2%) rotate(3deg); } } #header-block { background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 28px 36px; margin-bottom: 20px; position: relative; overflow: hidden; } #header-block::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: linear-gradient(90deg, transparent, var(--accent), var(--accent-bright), transparent); } #header-block h1 { font-family: 'Syne', sans-serif !important; font-size: 1.9rem !important; font-weight: 800 !important; letter-spacing: -0.03em !important; background: linear-gradient(135deg, #fff 0%, var(--accent-bright) 60%, #63b3ed 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin: 0 0 6px 0 !important; } #header-block p { color: var(--text-secondary) !important; font-size: 0.88rem !important; margin: 0 !important; font-weight: 300; letter-spacing: 0.01em; } #chatbot { background: var(--bg-secondary) !important; border: 1px solid var(--border) !important; border-radius: var(--radius-lg) !important; font-family: 'Inter', sans-serif !important; font-size: 0.92rem !important; } #chatbot .message.user { background: var(--user-bg) !important; border: 1px solid rgba(124,106,247,0.2) !important; border-radius: 14px 14px 4px 14px !important; color: var(--text-primary) !important; font-size: 0.9rem !important; padding: 12px 16px !important; max-width: 82% !important; margin-left: auto !important; } #chatbot .message.bot { background: var(--bot-bg) !important; border: 1px solid var(--border) !important; border-radius: 14px 14px 14px 4px !important; color: var(--text-primary) !important; font-size: 0.9rem !important; padding: 12px 16px !important; max-width: 88% !important; line-height: 1.65 !important; } #chatbot code { font-family: 'JetBrains Mono', monospace !important; background: rgba(124,106,247,0.12) !important; color: var(--accent-bright) !important; padding: 2px 6px !important; border-radius: 5px !important; font-size: 0.83em !important; } #chatbot pre { background: #0d0d14 !important; border: 1px solid var(--border-light) !important; border-radius: 10px !important; padding: 16px !important; overflow-x: auto !important; margin: 10px 0 !important; } #chatbot pre code { background: transparent !important; color: #c9d1d9 !important; padding: 0 !important; font-size: 0.85rem !important; line-height: 1.6 !important; } #msg-input textarea { background: var(--bg-tertiary) !important; border: 1px solid var(--border) !important; border-radius: var(--radius) !important; color: var(--text-primary) !important; font-family: 'Inter', sans-serif !important; font-size: 0.92rem !important; padding: 12px 16px !important; resize: none !important; transition: border-color 0.2s ease !important; } #msg-input textarea:focus { border-color: var(--accent) !important; box-shadow: 0 0 0 3px var(--accent-glow) !important; } #send-btn { background: linear-gradient(135deg, var(--accent), #5b4fcf) !important; color: #fff !important; border: none !important; border-radius: var(--radius) !important; font-family: 'Syne', sans-serif !important; font-weight: 600 !important; font-size: 0.88rem !important; letter-spacing: 0.03em !important; padding: 10px 22px !important; cursor: pointer !important; transition: all 0.2s ease !important; box-shadow: 0 4px 15px rgba(124,106,247,0.3) !important; height: 100% !important; } #send-btn:hover { transform: translateY(-1px) !important; box-shadow: 0 6px 20px rgba(124,106,247,0.45) !important; } label span { color: var(--text-secondary) !important; font-size: 0.82rem !important; font-weight: 500 !important; letter-spacing: 0.04em !important; text-transform: uppercase !important; font-family: 'Syne', sans-serif !important; } input[type=range] { accent-color: var(--accent) !important; } #stats-bar { background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 8px; padding: 8px 14px; font-size: 0.78rem; color: var(--text-muted); font-family: 'JetBrains Mono', monospace; letter-spacing: 0.02em; margin: 6px 0; } #token-info { font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; color: var(--text-muted); text-align: right; padding: 4px 8px; } #system-prompt textarea { font-family: 'JetBrains Mono', monospace !important; font-size: 0.82rem !important; background: var(--bg-tertiary) !important; border: 1px solid var(--border) !important; color: var(--text-secondary) !important; border-radius: var(--radius) !important; line-height: 1.6 !important; } .status-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 8px #4ade80; animation: pulse 2s ease-in-out infinite; margin-right: 6px; vertical-align: middle; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: var(--bg-secondary); } ::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 10px; } ::-webkit-scrollbar-thumb:hover { background: var(--accent); } """ # ── UI ──────────────────────────────────────────────────────────────────────── with gr.Blocks(css=CSS, title="Huihui-Qwen3.5 Chat", theme=gr.themes.Base()) as demo: with gr.Group(elem_id="header-block"): gr.HTML("""

⚡ Huihui-Qwen3.5-2B

abliterated  ·  ZeroGPU  ·  Streaming  ·  Markdown  ·  Export  |  huihui-ai/Huihui-Qwen3.5-2B-abliterated

""") with gr.Row(equal_height=False): # ── Left: Chat ────────────────────────────────────────────────── with gr.Column(scale=7): chatbot = gr.Chatbot( elem_id="chatbot", height=540, show_label=False, ) stats_html = gr.HTML('
Henüz mesaj yok.
') with gr.Row(): msg = gr.Textbox( elem_id="msg-input", placeholder="Mesajınızı yazın... (Enter = gönder, Shift+Enter = yeni satır)", lines=3, max_lines=8, show_label=False, scale=9, ) send_btn = gr.Button("Gönder ↑", elem_id="send-btn", variant="primary", scale=1) with gr.Row(): clear_btn = gr.Button("🗑 Sohbeti Temizle", variant="secondary", scale=1) token_info = gr.HTML('
Tokens: —
', scale=1) # ── Right: Settings ──────────────────────────────────────────── with gr.Column(scale=3): with gr.Tabs(): with gr.Tab("🎛 Sistem"): preset_dd = gr.Dropdown( choices=list(SYSTEM_PRESETS.keys()), value="🤖 Varsayılan Asistan", label="Hazır Şablonlar", interactive=True, ) system_prompt = gr.Textbox( elem_id="system-prompt", value=SYSTEM_PRESETS["🤖 Varsayılan Asistan"], label="System Prompt", lines=6, placeholder="Modele kimliğini ve davranışını tanımlayın...", ) with gr.Tab("⚙️ Parametreler"): max_new_tokens = gr.Slider( 64, 4096, value=1024, step=64, label="Max Yeni Token", info="Uzun yanıtlar için artırın" ) temperature = gr.Slider( 0.01, 2.0, value=0.7, step=0.05, label="Temperature", info="Yüksek = yaratıcı, Düşük = tutarlı" ) top_p = gr.Slider( 0.1, 1.0, value=0.9, step=0.05, label="Top-p (nucleus sampling)", ) repetition_penalty = gr.Slider( 1.0, 1.5, value=1.1, step=0.02, label="Tekrar Cezası", info="Yüksek = daha az tekrar" ) reset_params_btn = gr.Button("↺ Varsayılana Dön", variant="secondary") with gr.Tab("📤 Export"): gr.Markdown("Sohbet geçmişini JSON formatında indirin.") export_btn = gr.Button("💾 JSON İndir", variant="primary") export_file = gr.File(label="İndirme", visible=False) gr.HTML("""
Model   Huihui-Qwen3.5-2B-abliterated
Runtime  ZeroGPU (A100)
Streaming TextIteratorStreamer
Format   ChatML
""") # ── Event Handlers ───────────────────────────────────────────────────────── def user_turn(message, history): if not message.strip(): return "", history history = history or [] history.append({"role": "user", "content": message}) return "", history def bot_turn(history, sys_prompt, max_tok, temp, tp, rep_pen): if not history: yield history, get_stats([]) return # Son mesaj user mı kontrol et last = history[-1] last_role = last.get("role") if isinstance(last, dict) else None if last_role != "user": yield history, get_stats(history) return user_msg = last.get("content", "") if isinstance(last, dict) else last[0] prev_history = history[:-1] # Bot placeholder ekle history.append({"role": "assistant", "content": ""}) for partial in generate_stream(user_msg, prev_history, sys_prompt, max_tok, temp, tp, rep_pen): history[-1]["content"] = partial yield history, get_stats(history) # Wiring preset_dd.change(update_system_prompt, inputs=preset_dd, outputs=system_prompt) msg.submit(user_turn, [msg, chatbot], [msg, chatbot]).then( bot_turn, [chatbot, system_prompt, max_new_tokens, temperature, top_p, repetition_penalty], [chatbot, stats_html] ) send_btn.click(user_turn, [msg, chatbot], [msg, chatbot]).then( bot_turn, [chatbot, system_prompt, max_new_tokens, temperature, top_p, repetition_penalty], [chatbot, stats_html] ) clear_btn.click( lambda: ([], '
Henüz mesaj yok.
'), outputs=[chatbot, stats_html] ) msg.change(update_token_count, inputs=msg, outputs=token_info) reset_params_btn.click( reset_parameters, outputs=[max_new_tokens, temperature, top_p, repetition_penalty] ) export_btn.click( export_chat, inputs=[chatbot, system_prompt], outputs=export_file ).then(lambda: gr.update(visible=True), outputs=export_file) demo.queue(max_size=10).launch()