Spaces:
Running on Zero
Running on Zero
| import os | |
| import json | |
| from concurrent.futures import ThreadPoolExecutor | |
| import gradio as gr | |
| import spaces | |
| from huggingface_hub import InferenceClient | |
| def _ensure_gpu_functions_present_for_zerogpu(): | |
| """ZeroGPU startup probe: required for zero-a10g spaces detection.""" | |
| return None | |
| MODEL_MATRIX = [ | |
| { | |
| "key": "gemma4-31b", | |
| "label": "Gemma 4 31B-IT", | |
| "model_id": "unsloth/gemma-4-31B-it-unsloth-bnb-4bit", | |
| }, | |
| { | |
| "key": "gemma4-26b", | |
| "label": "Gemma 4 26B-IT", | |
| "model_id": "unsloth/gemma-4-26B-A4B-it", | |
| }, | |
| { | |
| "key": "qwen3.6-35b", | |
| "label": "Qwen 3.6 35B-IT", | |
| "model_id": "unsloth/Qwen3.6-35B-A3B-GGUF", | |
| }, | |
| { | |
| "key": "qwen3.6-27b", | |
| "label": "Qwen 3.6 27B", | |
| "model_id": "unsloth/Qwen3.6-27B-GGUF", | |
| }, | |
| ] | |
| HF_TOKEN = os.getenv("HF_API_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN") or os.getenv("HF_TOKEN") | |
| HF_TIMEOUT = int(os.getenv("HF_TIMEOUT", "120")) | |
| _SYSTEM_PROMPT = ( | |
| "You are a useful assistant. Answer briefly and directly unless the user asks for " | |
| "long-form detail. Keep responses focused and practical." | |
| ) | |
| def _init_client() -> InferenceClient: | |
| return InferenceClient(token=HF_TOKEN) | |
| def _extract_chat_text(response): | |
| if response is None: | |
| return "No response from model." | |
| if isinstance(response, dict): | |
| if "choices" in response and response["choices"]: | |
| choice = response["choices"][0] | |
| if isinstance(choice, dict) and "message" in choice and isinstance(choice["message"], dict): | |
| return (choice["message"].get("content") or "").strip() | |
| if isinstance(choice, dict) and "text" in choice: | |
| return str(choice["text"]).strip() | |
| if "generated_text" in response: | |
| return str(response["generated_text"]).strip() | |
| if "text" in response: | |
| return str(response["text"]).strip() | |
| if hasattr(response, "choices"): | |
| choices = getattr(response, "choices") | |
| if choices: | |
| first = choices[0] | |
| if hasattr(first, "message") and getattr(first.message, "content", None) is not None: | |
| return str(first.message.content).strip() | |
| if hasattr(first, "text"): | |
| return str(first.text).strip() | |
| text = str(response) | |
| if text and text != "{}": | |
| return text.strip() | |
| return "Model returned an empty response." | |
| def _build_messages(history, user_message): | |
| messages = [{"role": "system", "content": _SYSTEM_PROMPT}] | |
| for pair in history: | |
| if not pair: | |
| continue | |
| user_turn, assistant_turn = pair | |
| if user_turn: | |
| messages.append({"role": "user", "content": str(user_turn)}) | |
| if assistant_turn: | |
| messages.append({"role": "assistant", "content": str(assistant_turn)}) | |
| messages.append({"role": "user", "content": user_message}) | |
| return messages | |
| def _chat_completion(model_id, messages, max_new_tokens, temperature, top_p): | |
| client = _init_client() | |
| try: | |
| response = client.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=max_new_tokens, | |
| temperature=temperature, | |
| top_p=top_p, | |
| timeout=HF_TIMEOUT, | |
| ) | |
| return _extract_chat_text(response) | |
| except Exception as chat_error: | |
| fallback_prompt = "\n".join([f"{m['role']}: {m['content']}" for m in messages]) + "\nassistant:" | |
| try: | |
| response = client.text_generation( | |
| prompt=fallback_prompt, | |
| max_new_tokens=max_new_tokens, | |
| temperature=temperature, | |
| top_p=top_p, | |
| timeout=HF_TIMEOUT, | |
| ) | |
| return str(response).strip() | |
| except Exception as text_error: | |
| return ( | |
| f"Could not query model '{model_id}'." | |
| f" Chat error: {chat_error.__class__.__name__}" | |
| f"\nFallback error: {text_error.__class__.__name__}" | |
| ) | |
| def _safe_append(history, user_message, assistant_message): | |
| updated = list(history or []) | |
| updated.append((user_message, assistant_message)) | |
| return updated | |
| def _query_model(model_id, messages, max_new_tokens, temperature, top_p): | |
| return _chat_completion(model_id, messages, max_new_tokens, temperature, top_p) | |
| def run_compare( | |
| user_message, | |
| hist1, | |
| hist2, | |
| hist3, | |
| hist4, | |
| temperature, | |
| top_p, | |
| max_tokens, | |
| ): | |
| if not user_message or not str(user_message).strip(): | |
| return hist1, hist2, hist3, hist4, "", "", "", "" | |
| user_message = str(user_message).strip() | |
| m1, m2, m3, m4 = MODEL_MATRIX | |
| model_ids = [m1["model_id"], m2["model_id"], m3["model_id"], m4["model_id"]] | |
| histories = [ | |
| list(hist1 or []), | |
| list(hist2 or []), | |
| list(hist3 or []), | |
| list(hist4 or []), | |
| ] | |
| messages = [ | |
| _build_messages(histories[i], user_message) | |
| for i in range(4) | |
| ] | |
| with ThreadPoolExecutor(max_workers=4) as executor: | |
| futures = [ | |
| executor.submit(_query_model, model_ids[idx], messages[idx], max_tokens, temperature, top_p) | |
| for idx in range(4) | |
| ] | |
| answers = [f.result() for f in futures] | |
| new_hist1 = _safe_append(histories[0], user_message, answers[0]) | |
| new_hist2 = _safe_append(histories[1], user_message, answers[1]) | |
| new_hist3 = _safe_append(histories[2], user_message, answers[2]) | |
| new_hist4 = _safe_append(histories[3], user_message, answers[3]) | |
| return ( | |
| new_hist1, | |
| new_hist2, | |
| new_hist3, | |
| new_hist4, | |
| m1["label"], | |
| m2["label"], | |
| m3["label"], | |
| m4["label"], | |
| ) | |
| def run_clear(): | |
| return [], [], [], [], "", "", "", "" | |
| css = """ | |
| .gradio-container { max-width: 100%; } | |
| .column { | |
| border: 1px solid #d7dee8; | |
| border-radius: 12px; | |
| padding: 10px; | |
| background: linear-gradient(160deg, #f8fbff, #f2f4ff); | |
| } | |
| """ | |
| with gr.Blocks(css=css) as demo: | |
| gr.Markdown( | |
| "# Side-by-side Unsloth model comparison\n" | |
| "All generations run through Hugging Face hosted inference (zero local GPU)." | |
| ) | |
| with gr.Row(): | |
| status1 = gr.Textbox(label="Gemma4 31B-IT", value="", interactive=False) | |
| status2 = gr.Textbox(label="Gemma4 26B-IT", value="", interactive=False) | |
| status3 = gr.Textbox(label="Qwen 3.6 35B-IT", value="", interactive=False) | |
| status4 = gr.Textbox(label="Qwen 3.6 27B", value="", interactive=False) | |
| with gr.Row(equal_height=True): | |
| with gr.Column(elem_classes=["column"]): | |
| gr.Markdown("### Gemma4 31B-IT") | |
| chat1 = gr.Chatbot(height=470, label="Gemma4 31B-IT") | |
| with gr.Column(elem_classes=["column"]): | |
| gr.Markdown("### Gemma4 26B-IT") | |
| chat2 = gr.Chatbot(height=470, label="Gemma4 26B-IT") | |
| with gr.Column(elem_classes=["column"]): | |
| gr.Markdown("### Qwen 3.6 35B-IT") | |
| chat3 = gr.Chatbot(height=470, label="Qwen 3.6 35B-IT") | |
| with gr.Column(elem_classes=["column"]): | |
| gr.Markdown("### Qwen 3.6 27B") | |
| chat4 = gr.Chatbot(height=470, label="Qwen 3.6 27B") | |
| with gr.Accordion("Advanced generation settings", open=False): | |
| temperature = gr.Slider(0.1, 1.2, value=0.7, step=0.05, label="Temperature") | |
| top_p = gr.Slider(0.2, 1.0, value=0.9, step=0.05, label="Top-p") | |
| max_tokens = gr.Slider(64, 2048, value=512, step=16, label="Max new tokens") | |
| with gr.Row(): | |
| prompt = gr.Textbox( | |
| label="User prompt", | |
| placeholder="Ask the same question to all models...", | |
| lines=2, | |
| scale=3, | |
| ) | |
| send = gr.Button("Compare", variant="primary") | |
| clear = gr.Button("Clear") | |
| send.click( | |
| run_compare, | |
| inputs=[ | |
| prompt, | |
| chat1, | |
| chat2, | |
| chat3, | |
| chat4, | |
| temperature, | |
| top_p, | |
| max_tokens, | |
| ], | |
| outputs=[chat1, chat2, chat3, chat4, status1, status2, status3, status4], | |
| ) | |
| prompt.submit( | |
| run_compare, | |
| inputs=[ | |
| prompt, | |
| chat1, | |
| chat2, | |
| chat3, | |
| chat4, | |
| temperature, | |
| top_p, | |
| max_tokens, | |
| ], | |
| outputs=[chat1, chat2, chat3, chat4, status1, status2, status3, status4], | |
| ) | |
| clear.click( | |
| run_clear, | |
| outputs=[chat1, chat2, chat3, chat4, status1, status2, status3, status4], | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue() | |
| demo.launch() | |