Spaces:
Running on Zero
Running on Zero
| import os | |
| from threading import Thread | |
| from typing import Iterator | |
| import spaces | |
| import gradio as gr | |
| import torch | |
| from transformers import ( | |
| AutoModelForCausalLM, | |
| AutoTokenizer, | |
| BitsAndBytesConfig, | |
| TextIteratorStreamer, | |
| ) | |
| # ============================================================ | |
| # CONFIG | |
| # ============================================================ | |
| MODEL_ID = "CreitinGameplays/GLM-4.7-Flash-Fable-5-Distill" | |
| # Keep these conservative for ZeroGPU. | |
| MAX_NEW_TOKENS = 1024 | |
| DEFAULT_MAX_NEW_TOKENS = 4096 | |
| # Smaller context = much faster prompt processing. | |
| MAX_INPUT_TOKEN_LENGTH = 6112 | |
| DEFAULT_SYSTEM_PROMPT = ( | |
| "Answer clearly and directly. " | |
| "Use two or three sentences unless the user asks " | |
| "for a longer response." | |
| ) | |
| # ============================================================ | |
| # TOKENIZER | |
| # ============================================================ | |
| print(f"Loading tokenizer: {MODEL_ID}") | |
| tokenizer = AutoTokenizer.from_pretrained( | |
| MODEL_ID, | |
| padding_side="left", | |
| ) | |
| if hasattr(tokenizer, "use_default_system_prompt"): | |
| tokenizer.use_default_system_prompt = False | |
| # ============================================================ | |
| # MODEL | |
| # ============================================================ | |
| print(f"Loading model: {MODEL_ID}") | |
| quantization_config = BitsAndBytesConfig( | |
| load_in_8bit=True, | |
| ) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_ID, | |
| quantization_config=quantization_config, | |
| torch_dtype=torch.bfloat16, | |
| device_map={"": "cuda"}, | |
| low_cpu_mem_usage=True, | |
| ) | |
| model.eval() | |
| print("Model loaded.") | |
| print(f"CUDA: {torch.cuda.is_available()}") | |
| # ============================================================ | |
| # CONVERSATION | |
| # ============================================================ | |
| def build_conversation( | |
| message: str, | |
| chat_history, | |
| system_prompt: str, | |
| ): | |
| messages = [] | |
| if system_prompt and system_prompt.strip(): | |
| messages.append( | |
| { | |
| "role": "system", | |
| "content": system_prompt.strip(), | |
| } | |
| ) | |
| if chat_history: | |
| for item in chat_history: | |
| if not isinstance(item, (list, tuple)): | |
| continue | |
| if len(item) != 2: | |
| continue | |
| user_message, assistant_message = item | |
| if user_message: | |
| messages.append( | |
| { | |
| "role": "user", | |
| "content": str(user_message), | |
| } | |
| ) | |
| if assistant_message: | |
| messages.append( | |
| { | |
| "role": "assistant", | |
| "content": str(assistant_message), | |
| } | |
| ) | |
| messages.append( | |
| { | |
| "role": "user", | |
| "content": message, | |
| } | |
| ) | |
| return messages | |
| # ============================================================ | |
| # DYNAMIC GPU DURATION | |
| # ============================================================ | |
| def get_gpu_duration( | |
| message: str, | |
| chat_history, | |
| system_prompt: str, | |
| max_new_tokens: int, | |
| temperature: float, | |
| top_p: float, | |
| top_k: int, | |
| repetition_penalty: float, | |
| ): | |
| """ | |
| Estimate a GPU reservation based on requested output length. | |
| The goal is to avoid reserving an unnecessarily large GPU | |
| window while still giving longer answers enough time. | |
| """ | |
| tokens = int(max_new_tokens) | |
| if tokens <= 128: | |
| return 30 | |
| if tokens <= 256: | |
| return 45 | |
| if tokens <= 384: | |
| return 60 | |
| # Do not request enormous durations by default. | |
| return 60 | |
| # ============================================================ | |
| # GENERATION | |
| # ============================================================ | |
| def generate( | |
| message: str, | |
| chat_history, | |
| system_prompt: str = DEFAULT_SYSTEM_PROMPT, | |
| max_new_tokens: int = DEFAULT_MAX_NEW_TOKENS, | |
| temperature: float = 0.5, | |
| top_p: float = 0.95, | |
| top_k: int = 40, | |
| repetition_penalty: float = 1.0, | |
| ) -> Iterator[str]: | |
| if not message or not message.strip(): | |
| yield "Please enter a message." | |
| return | |
| # Hard cap to protect ZeroGPU runtime. | |
| max_new_tokens = min( | |
| int(max_new_tokens), | |
| MAX_NEW_TOKENS, | |
| ) | |
| # -------------------------------------------------------- | |
| # Build conversation | |
| # -------------------------------------------------------- | |
| conversation = build_conversation( | |
| message=message, | |
| chat_history=chat_history, | |
| system_prompt=system_prompt, | |
| ) | |
| # -------------------------------------------------------- | |
| # Tokenize with native model chat template | |
| # -------------------------------------------------------- | |
| input_ids = tokenizer.apply_chat_template( | |
| conversation, | |
| tokenize=True, | |
| add_generation_prompt=True, | |
| return_tensors="pt", | |
| ) | |
| # -------------------------------------------------------- | |
| # Context limit | |
| # -------------------------------------------------------- | |
| if input_ids.shape[1] > MAX_INPUT_TOKEN_LENGTH: | |
| input_ids = input_ids[ | |
| :, | |
| -MAX_INPUT_TOKEN_LENGTH: | |
| ] | |
| gr.Warning( | |
| f"Conversation trimmed to " | |
| f"{MAX_INPUT_TOKEN_LENGTH} tokens." | |
| ) | |
| # -------------------------------------------------------- | |
| # CUDA | |
| # -------------------------------------------------------- | |
| input_ids = input_ids.to("cuda") | |
| # -------------------------------------------------------- | |
| # Stream generation | |
| # -------------------------------------------------------- | |
| streamer = TextIteratorStreamer( | |
| tokenizer, | |
| timeout=30.0, | |
| skip_prompt=True, | |
| skip_special_tokens=True, | |
| ) | |
| generation_kwargs = { | |
| "input_ids": input_ids, | |
| "streamer": streamer, | |
| "max_new_tokens": max_new_tokens, | |
| "do_sample": True, | |
| "temperature": float(temperature), | |
| "top_p": float(top_p), | |
| "top_k": int(top_k), | |
| "repetition_penalty": float(repetition_penalty), | |
| "num_beams": 1, | |
| "use_cache": True, | |
| } | |
| thread = Thread( | |
| target=model.generate, | |
| kwargs=generation_kwargs, | |
| daemon=True, | |
| ) | |
| thread.start() | |
| generated_text = "" | |
| try: | |
| for text in streamer: | |
| generated_text += text | |
| yield generated_text | |
| except Exception as exc: | |
| print(f"Generation error: {exc}") | |
| raise | |
| finally: | |
| thread.join(timeout=1.0) | |
| # ============================================================ | |
| # UI | |
| # ============================================================ | |
| chat_interface = gr.ChatInterface( | |
| fn=generate, | |
| additional_inputs=[ | |
| gr.Textbox( | |
| label="System prompt", | |
| lines=4, | |
| value=DEFAULT_SYSTEM_PROMPT, | |
| ), | |
| gr.Slider( | |
| label="Max new tokens", | |
| minimum=16, | |
| maximum=MAX_NEW_TOKENS, | |
| step=16, | |
| value=DEFAULT_MAX_NEW_TOKENS, | |
| ), | |
| gr.Slider( | |
| label="Temperature", | |
| minimum=0.1, | |
| maximum=1.5, | |
| step=0.1, | |
| value=0.5, | |
| ), | |
| gr.Slider( | |
| label="Top-p", | |
| minimum=0.1, | |
| maximum=1.0, | |
| step=0.05, | |
| value=0.95, | |
| ), | |
| gr.Slider( | |
| label="Top-k", | |
| minimum=0, | |
| maximum=100, | |
| step=1, | |
| value=40, | |
| ), | |
| gr.Slider( | |
| label="Repetition penalty", | |
| minimum=1.0, | |
| maximum=1.5, | |
| step=0.05, | |
| value=1.0, | |
| ), | |
| ], | |
| examples=[ | |
| ["Hello there!"], | |
| [ | |
| "Explain Python in three sentences." | |
| ], | |
| [ | |
| "Explain the plot of Cinderella briefly." | |
| ], | |
| [ | |
| "Write a short paragraph about open-source AI." | |
| ], | |
| ], | |
| ) | |
| # ============================================================ | |
| # APP | |
| # ============================================================ | |
| with gr.Blocks( | |
| title="GLM-4.7-Flash Fable Chat", | |
| ) as demo: | |
| gr.Markdown( | |
| """ | |
| # GLM-4.7-Flash Fable Chat | |
| Fast ZeroGPU chat interface. | |
| """ | |
| ) | |
| chat_interface.render() | |
| # ============================================================ | |
| # LAUNCH | |
| # ============================================================ | |
| if __name__ == "__main__": | |
| demo.queue( | |
| max_size=10, | |
| ).launch( | |
| show_error=True, | |
| ) |