stanley-00 commited on
Commit
3cb64b7
·
1 Parent(s): cf8bc52

Update UI

Browse files
Files changed (2) hide show
  1. README.md +1 -1
  2. app.py +261 -142
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 6.16.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
11
  short_description: A space to play with SLM models without Inference Endpoint
 
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 6.16.0
8
+ python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  short_description: A space to play with SLM models without Inference Endpoint
app.py CHANGED
@@ -9,60 +9,104 @@ import torch
9
  import psutil
10
  import time
11
 
12
- # Define path for HF cache to clean
13
  HF_CACHE_DIR = os.path.expanduser("~/.cache/huggingface/hub")
 
14
 
15
- # List of models for autocomplete
16
  MODELS = [
17
- 'HuggingFaceTB/SmolLM2-135M', 'AxiomicLabs/GPT-X2-125M', 'Qwen/Qwen3-0.6B',
18
- 'facebook/MobileLLM-R1-140M-base', 'SupraLabs/Supra-50M-Base', 'CompactAI-O/Shard-1',
19
- 'SupraLabs/Supra-50M-Instruct', 'HuggingFaceTB/SmolLM-135M', 'facebook/opt-125m',
20
- 'AxiomicLabs/GPT-S-5M', 'openai-community/gpt2', 'LH-Tech-AI/Spark-5M-Base-v4',
21
- 'SupraLabs/Supra-Mini-v5-8M', 'EleutherAI/pythia-70m', 'SupraLabs/Supra-Mini-v4-2M',
22
- 'EleutherAI/pythia-31m', 'StentorLabs/Stentor3-50M', 'StentorLabs/Stentor3-20M',
23
- 'StentorLabs/Portimbria-150M', 'HuggingFaceTB/nanowhale-100m-base', 'EleutherAI/pythia-14m',
24
- 'Harley-ml/Tenete-8M', 'Harley-ml/Dillion-1.2M', 'MihaiPopa-1/CinnabarLM-1.4M-Base',
25
- 'MihaiPopa-1/CinnabarLM-4M-Base', 'MihaiPopa-1/PotentSulfurLM-500K-Base',
26
- 'MihaiPopa-1/CinnabarLM-1.5M-Base', 'Harley-ml/Dillionv2-1.3M', 'Eclipse-Senpai/KeyLM-75M',
27
- 'SupraLabs/Supra-Mini-v6-1M', 'AxiomicLabs/GPT-S-1.4M', 'GODELEV/Archaea-74M',
28
- 'Sandroeth/cali-0.1B', 'veyra-ai/veyra3-5m-base', 'veyra-ai/veyra-30m-base-5b-tokens',
29
- 'ThingAI/Quark-50m', 'ThingAI/Quark-135m', 'HuggingFaceTB/SmolLM2-135M-Instruct',
30
- 'Aravindan/awesome-gpt-2-coder', 'Qwen/Qwen2.5-Coder-0.5B', 'SupraLabs/Supra-50M-Reasoning'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  ]
32
 
33
  ACTIVE_SESSIONS = {}
34
  SESSION_TIMEOUT = 60
35
 
 
36
  def live_count(request: gr.Request):
37
  current_time = time.time()
38
  if request:
39
  ACTIVE_SESSIONS[request.session_hash] = current_time
40
-
41
- # Prune
42
  expired = [s for s, t in ACTIVE_SESSIONS.items() if current_time - t > SESSION_TIMEOUT]
43
  for s in expired:
44
  ACTIVE_SESSIONS.pop(s, None)
45
-
46
  return len(ACTIVE_SESSIONS)
47
 
48
- # Global class to safely manage the loaded model and tokenizer in memory
49
  class ModelManager:
50
  def __init__(self):
51
  self.model = None
52
  self.tokenizer = None
53
  self.model_id = None
54
- self.stop_generation = False # Added flag to instantly kill generation
55
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
56
 
 
57
  model_manager = ModelManager()
58
 
59
- # Custom stopping criteria to halt the generation thread when loading a new model
60
  class StopOnFlag(StoppingCriteria):
61
  def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:
62
  return model_manager.stop_generation
63
 
 
64
  def get_system_stats(request: gr.Request = None):
65
- """Returns a dictionary of current system metrics with formatted strings."""
66
  mem = psutil.virtual_memory()
67
  disk = psutil.disk_usage('/')
68
  return (
@@ -72,12 +116,9 @@ def get_system_stats(request: gr.Request = None):
72
  f"Active\t: \t{len(ACTIVE_SESSIONS) if request is None else live_count(request)} session(s)"
73
  )
74
 
 
75
  def load_new_model(model_id):
76
- """Loads the model and tokenizer dynamically into the global manager."""
77
- # Stop any ongoing generation immediately
78
  model_manager.stop_generation = True
79
-
80
- # Clear old model from memory
81
  model_manager.model = None
82
  model_manager.tokenizer = None
83
  model_manager.model_id = None
@@ -85,90 +126,137 @@ def load_new_model(model_id):
85
  gc.collect()
86
  if torch.cuda.is_available():
87
  torch.cuda.empty_cache()
88
-
89
  try:
90
- # Load explicitly for streaming purposes instead of pipeline
91
  tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
92
- model = AutoModelForCausalLM.from_pretrained(model_id, trust_remote_code=True)
93
-
 
94
  model_manager.tokenizer = tokenizer
95
  model_manager.model = model
96
  model_manager.model_id = model_id
97
-
98
- yield f"Successfully loaded {model_id} on {model_manager.device.upper()}"
99
  except Exception as e:
100
  yield f"Error loading model: {str(e)}"
101
 
102
 
103
- def run_inference(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample, gpu):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  if gpu:
105
- yield from run_inference_gpu(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample)
 
 
 
106
  else:
107
- yield from run_inference_raw(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample)
108
 
109
 
110
- @spaces.GPU
111
  def run_inference_gpu(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample):
112
- max_retries = 5
113
- for attempt in range(max_retries):
114
- try:
115
- if model_manager.model is not None:
116
- model_manager.model = model_manager.model.to("cuda")
117
- break
118
- except RuntimeError as e:
119
- if "CUDA" in str(e) and attempt < max_retries - 1:
120
- yield f"Waiting for Hugging Face ZeroGPU allocation (Attempt {attempt+1}/{max_retries})...", "Queueing..."
121
- time.sleep(2)
122
- else:
123
- yield f"ZeroGPU initialization failed: {str(e)}. Try clicking Generate again.", "GPU Unavailable"
124
- return
125
 
126
  yield from run_inference_raw(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample, use_cuda=True)
127
 
 
128
  def run_inference_raw(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample, use_cuda=False):
129
- """Generates text via streaming generator."""
130
  if model_manager.model is None or model_manager.tokenizer is None:
131
  yield "Please load a model first.", "Model not loaded"
132
  return
133
-
134
- # Reset the stop flag for the new generation run
135
  model_manager.stop_generation = False
136
-
137
  tokenizer = model_manager.tokenizer
138
  model = model_manager.model
139
  model_id = model_manager.model_id
140
-
141
  is_supra_reasoning = "Supra-50M-Reasoning" in model_id if model_id else False
142
-
143
- if is_supra_reasoning:
144
- SYSTEM_PROMPT = "Your role as an assistant involves thoroughly exploring questions through a systematic long thinking process before providing the final precise and accurate solutions."
145
- prompt_to_encode = (
146
- f"[SYSTEM]: {SYSTEM_PROMPT}\n\n"
147
- f"[USER]: {user_prompt}\n\n"
148
- f"[ASSISTANT]: <|begin_of_thought|>\n"
149
- )
150
- skip_special = False
151
- else:
152
- prompt_to_encode = user_prompt
153
- skip_special = True
154
 
155
- # Tokenize input
156
- inputs = tokenizer([prompt_to_encode], return_tensors="pt")
157
-
 
 
158
  if use_cuda:
159
  inputs = {k: v.to("cuda") for k, v in inputs.items()}
160
  else:
161
  model = model.to("cpu")
162
  inputs = {k: v.to("cpu") for k, v in inputs.items()}
163
-
164
- # Set up the streamer
165
- streamer = TextIteratorStreamer(tokenizer, timeout=60.0, skip_prompt=True, skip_special_tokens=skip_special)
166
-
167
- # Adjust variables based on the do_sample logic
168
  if not do_sample:
169
- temperature = 1.0 # Temperature is ignored if do_sample=False, but setting it > 0 avoids config errors
170
 
171
- # Generation arguments
172
  generate_kwargs = dict(
173
  **inputs,
174
  streamer=streamer,
@@ -179,50 +267,46 @@ def run_inference_raw(user_prompt, max_tokens, temperature, top_k, top_p, rep_pe
179
  repetition_penalty=float(rep_penalty),
180
  no_repeat_ngram_size=int(ngram_size),
181
  do_sample=do_sample,
182
- pad_token_id=tokenizer.eos_token_id, # Prevents padding warnings
183
- stopping_criteria=StoppingCriteriaList([StopOnFlag()]) # Attach the stopping criteria
184
  )
185
 
186
  start_time = time.time()
187
- # Start generation in a separate background thread
188
  thread = Thread(target=model.generate, kwargs=generate_kwargs)
189
  thread.start()
190
-
191
  if is_supra_reasoning:
192
- # Use plain text formatting rather than markdown symbols inside gr.Textbox
193
- base_display = f"Prompt: {user_prompt}\n\n----------------------------------------\n\n"
194
  generated_text = ""
195
  else:
196
  base_display = ""
197
- generated_text = user_prompt
198
 
199
- # Yield output iteratively for the streaming effect
200
  token_count = 0
201
  for new_text in streamer:
202
- # Immediately break out of the UI update loop if a new model is loaded
203
  if model_manager.stop_generation:
204
  break
205
-
206
  generated_text += new_text
207
  token_count += 1
208
  duration = time.time() - start_time
209
  tps = token_count / duration if duration > 0 else 0
210
-
211
  display_text = generated_text
212
-
213
  if is_supra_reasoning:
214
  display_text = display_text.replace("<s>", "").replace("</s>", "")
215
- if not display_text.startswith("🧠 Thinking Process:"):
216
- display_text = "🧠 Thinking Process:\n" + display_text
217
-
218
- display_text = display_text.replace("<|begin_of_thought|>", "🧠 Thinking Process:\n")
219
  display_text = display_text.replace("<|end_of_thought|>", "\n\n")
220
- display_text = display_text.replace("<|begin_of_solution|>", "Final Answer:\n\n")
221
  display_text = display_text.replace("<|end_of_solution|>", "")
222
 
223
  device_label = "CUDA" if use_cuda else "CPU"
224
  yield base_display + display_text, f"Speed: {tps:.2f} tokens/sec ({device_label})"
225
 
 
226
  def clean_cache():
227
  if os.path.exists(HF_CACHE_DIR):
228
  shutil.rmtree(HF_CACHE_DIR)
@@ -230,74 +314,109 @@ def clean_cache():
230
  return "Cache cleaned successfully!"
231
  return "Cache directory not found."
232
 
233
- # Gradio Interface
234
- with gr.Blocks(title="Small MF Model Tester", theme=gr.themes.Soft()) as app:
235
-
236
- gr.Markdown("# 🚀 Small Model Evaluation Hub with Streaming")
237
 
238
  with gr.Row():
239
- # Left column: Settings & Monitoring
240
- with gr.Column(scale=1):
241
-
242
- with gr.Accordion("System Monitoring", open=True):
243
- stats_output = gr.Textbox(label="Live System Stats", show_label=False)
244
  gr.Timer(2).tick(get_system_stats, None, stats_output)
245
 
246
  with gr.Group():
247
- gr.Markdown("### Select or Paste custom model id here")
 
 
 
248
  with gr.Row():
249
- model_id_input = gr.Dropdown(choices=MODELS, label="Model", allow_custom_value=True, show_label=False, scale=3)
250
- load_btn = gr.Button("Load", variant="secondary", scale=1)
251
- use_gpu = gr.Checkbox(label="Use GPU?", value=False)
252
- clean_btn = gr.Button("Clean HF Cache", variant="stop", size="sm")
253
-
254
- with gr.Accordion("Generation Configuration", open=False):
255
- do_sample_input = gr.Checkbox(label="Enable Sampling (do_sample)", value=True, info="Uncheck for greedy decoding")
256
- max_tokens_input = gr.Slider(minimum=10, maximum=2048, value=256, step=1, label="Max Output Tokens")
257
- temperature_input = gr.Slider(minimum=0.1, maximum=2.0, value=0.7, step=0.1, label="Temperature", info="Higher = more creative")
258
-
259
- top_k_input = gr.Slider(minimum=0, maximum=100, value=50, step=1, label="Top-K", info="0 = disabled")
260
- top_p_input = gr.Slider(minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-P (Nucleus)", info="1.0 = disabled")
261
- rep_penalty_input = gr.Slider(minimum=1.0, maximum=2.0, value=1.1, step=0.05, label="Repetition Penalty", info="1.0 = disabled")
262
- ngram_size_input = gr.Slider(minimum=0, maximum=10, value=0, step=1, label="No Repeat N-Gram Size", info="0 = disabled")
263
-
264
- # Right column: Interaction
265
- with gr.Column(scale=2):
 
 
 
 
 
 
 
266
  user_prompt = gr.Textbox(
267
- label="Prompt",
268
- value="Once upon a time in a digital kingdom,",
269
- placeholder="Enter your prompt here...",
270
- lines=5
 
 
 
 
 
 
 
 
 
 
271
  )
272
- run_btn = gr.Button("Generate text", variant="primary", size="lg")
273
- status_output = gr.Markdown("Status: *Waiting to load model...*")
274
- output_text = gr.Textbox(label="Result", lines=15, buttons=["copy"], autoscroll=True)
275
-
276
- # Events
 
 
 
 
 
 
277
  load_btn.click(
278
- fn=load_new_model,
279
- inputs=[model_id_input],
280
  outputs=[status_output]
281
  )
282
-
283
- # We use `.click` targeting a generator function, which Gradio naturally treats as a streaming output
 
 
 
 
 
284
  run_btn.click(
285
  fn=run_inference,
286
  inputs=[
287
- user_prompt,
288
- max_tokens_input,
289
- temperature_input,
290
- top_k_input,
291
- top_p_input,
292
- rep_penalty_input,
 
 
 
 
 
293
  ngram_size_input,
294
  do_sample_input,
295
  use_gpu
296
  ],
297
  outputs=[output_text, status_output]
298
  )
299
-
300
  clean_btn.click(fn=clean_cache, outputs=[status_output])
301
 
302
  if __name__ == "__main__":
303
- app.launch()
 
9
  import psutil
10
  import time
11
 
 
12
  HF_CACHE_DIR = os.path.expanduser("~/.cache/huggingface/hub")
13
+ DEFAULT_MODEL = "HuggingFaceTB/SmolLM2-135M-Instruct"
14
 
 
15
  MODELS = [
16
+ "HuggingFaceTB/SmolLM2-135M-Instruct",
17
+ "HuggingFaceTB/SmolLM2-135M",
18
+ "HuggingFaceTB/SmolLM2-360M-Instruct",
19
+ "HuggingFaceTB/SmolLM2-1.7B-Instruct",
20
+ "HuggingFaceTB/SmolLM-135M",
21
+ "Qwen/Qwen3-0.6B",
22
+ "Qwen/Qwen2.5-Coder-0.5B",
23
+ "Qwen/Qwen2.5-0.5B",
24
+ "Qwen/Qwen2.5-1.5B",
25
+ "Qwen/Qwen2.5-3B",
26
+ "Qwen/Qwen3-1.7B",
27
+ "facebook/MobileLLM-R1-140M-base",
28
+ "facebook/opt-125m",
29
+ "facebook/opt-350m",
30
+ "microsoft/phi-2",
31
+ "microsoft/Phi-3.5-mini-instruct",
32
+ "microsoft/Phi-3-mini-4k-instruct",
33
+ "openai-community/gpt2",
34
+ "openai-community/gpt2-medium",
35
+ "openai-community/gpt2-large",
36
+ "EleutherAI/pythia-70m",
37
+ "EleutherAI/pythia-160m",
38
+ "EleutherAI/pythia-410m",
39
+ "EleutherAI/gpt-neo-125M",
40
+ "EleutherAI/gpt-neo-1.3B",
41
+ "TinyLlama/TinyLlama-1.1B-Chat-v1.0",
42
+ "stabilityai/StableLM-3b-4e1t",
43
+ "stabilityai/StableLM-Zephyr-3B",
44
+ "NousResearch/Hermes-3-Llama-3.1-8B",
45
+ "meta-llama/Llama-3.2-1B",
46
+ "meta-llama/Llama-3.2-3B",
47
+ "THUDM/glm-4-1b-flash",
48
+ "2Butch/MiniCPM-1B-sft-bf16",
49
+ "SupraLabs/Supra-50M-Base",
50
+ "SupraLabs/Supra-50M-Instruct",
51
+ "SupraLabs/Supra-50M-Reasoning",
52
+ "GODELEV/Archaea-74M",
53
+ "Sandroeth/cali-0.1B",
54
+ "ThingAI/Quark-50m",
55
+ "ThingAI/Quark-135m",
56
+ "Aravindan/awesome-gpt-2-coder",
57
+ "LiquidAI/LFM2-1.2B",
58
+ "LiquidAI/LFM2-2.6B",
59
+ "LiquidAI/LFM-350M",
60
+ "LiquidAI/LFM-700M",
61
+ "LiquidAI/LFM2.5-230M",
62
+ "LiquidAI/LFM2.5-350M",
63
+ "LiquidAI/LFM2.5-1.2B-Instruct",
64
+ "LiquidAI/LFM2.5-1.2B-Thinking",
65
+ "LiquidAI/LFM2.5-8B-A1B",
66
+ ]
67
+
68
+ TASK_MODES = ["Completion", "Chat", "Q&A", "Translation"]
69
+
70
+ LANGUAGES = [
71
+ "English", "Spanish", "French", "German", "Italian", "Portuguese",
72
+ "Chinese", "Japanese", "Korean", "Arabic", "Russian", "Hindi",
73
+ "Dutch", "Turkish", "Polish", "Czech", "Romanian", "Greek",
74
+ "Thai", "Vietnamese", "Indonesian", "Malay", "Finnish", "Swedish",
75
+ "Norwegian", "Danish"
76
  ]
77
 
78
  ACTIVE_SESSIONS = {}
79
  SESSION_TIMEOUT = 60
80
 
81
+
82
  def live_count(request: gr.Request):
83
  current_time = time.time()
84
  if request:
85
  ACTIVE_SESSIONS[request.session_hash] = current_time
 
 
86
  expired = [s for s, t in ACTIVE_SESSIONS.items() if current_time - t > SESSION_TIMEOUT]
87
  for s in expired:
88
  ACTIVE_SESSIONS.pop(s, None)
 
89
  return len(ACTIVE_SESSIONS)
90
 
91
+
92
  class ModelManager:
93
  def __init__(self):
94
  self.model = None
95
  self.tokenizer = None
96
  self.model_id = None
97
+ self.stop_generation = False
98
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
99
 
100
+
101
  model_manager = ModelManager()
102
 
103
+
104
  class StopOnFlag(StoppingCriteria):
105
  def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:
106
  return model_manager.stop_generation
107
 
108
+
109
  def get_system_stats(request: gr.Request = None):
 
110
  mem = psutil.virtual_memory()
111
  disk = psutil.disk_usage('/')
112
  return (
 
116
  f"Active\t: \t{len(ACTIVE_SESSIONS) if request is None else live_count(request)} session(s)"
117
  )
118
 
119
+
120
  def load_new_model(model_id):
 
 
121
  model_manager.stop_generation = True
 
 
122
  model_manager.model = None
123
  model_manager.tokenizer = None
124
  model_manager.model_id = None
 
126
  gc.collect()
127
  if torch.cuda.is_available():
128
  torch.cuda.empty_cache()
 
129
  try:
 
130
  tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
131
+ model = AutoModelForCausalLM.from_pretrained(
132
+ model_id, trust_remote_code=True, dtype=torch.float16
133
+ )
134
  model_manager.tokenizer = tokenizer
135
  model_manager.model = model
136
  model_manager.model_id = model_id
137
+ yield f"Loaded **{model_id}** on {model_manager.device.upper()}"
 
138
  except Exception as e:
139
  yield f"Error loading model: {str(e)}"
140
 
141
 
142
+ def update_mode_ui(mode):
143
+ show_sys = mode == "Chat"
144
+ show_ctx = mode == "Q&A"
145
+ show_src = mode == "Translation"
146
+ show_tgt = mode == "Translation"
147
+
148
+ if mode == "Chat":
149
+ prompt_label = "User Message"
150
+ prompt_placeholder = "Type your message..."
151
+ elif mode == "Q&A":
152
+ prompt_label = "Question"
153
+ prompt_placeholder = "Enter your question..."
154
+ elif mode == "Translation":
155
+ prompt_label = "Text to Translate"
156
+ prompt_placeholder = "Enter text to translate..."
157
+ else:
158
+ prompt_label = "Prompt"
159
+ prompt_placeholder = "Enter your prompt here..."
160
+
161
+ return (
162
+ gr.update(visible=show_sys),
163
+ gr.update(label=prompt_label, placeholder=prompt_placeholder),
164
+ gr.update(visible=show_ctx),
165
+ gr.update(visible=show_src),
166
+ gr.update(visible=show_tgt),
167
+ )
168
+
169
+
170
+ def format_prompt(mode, prompt, system_prompt="", context="", src_lang="", tgt_lang=""):
171
+ if mode == "Completion":
172
+ return prompt
173
+
174
+ elif mode == "Chat":
175
+ if model_manager.tokenizer and hasattr(model_manager.tokenizer, "apply_chat_template"):
176
+ try:
177
+ messages = []
178
+ if system_prompt:
179
+ messages.append({"role": "system", "content": system_prompt})
180
+ messages.append({"role": "user", "content": prompt})
181
+ return model_manager.tokenizer.apply_chat_template(
182
+ messages, tokenize=False, add_generation_prompt=True
183
+ )
184
+ except Exception:
185
+ pass
186
+ parts = []
187
+ if system_prompt:
188
+ parts.append(f"[SYSTEM]: {system_prompt}")
189
+ parts.append(f"[USER]: {prompt}")
190
+ parts.append("[ASSISTANT]:")
191
+ return "\n\n".join(parts)
192
+
193
+ elif mode == "Q&A":
194
+ if context and context.strip():
195
+ return f"Context:\n{context.strip()}\n\nQuestion: {prompt.strip()}\n\nAnswer:"
196
+ return f"Question: {prompt.strip()}\n\nAnswer:"
197
+
198
+ elif mode == "Translation":
199
+ src = src_lang or "English"
200
+ tgt = tgt_lang or "Spanish"
201
+ return f"Translate the following text from {src} to {tgt}:\n\n{prompt.strip()}\n\nTranslation:"
202
+
203
+ return prompt
204
+
205
+
206
+ def estimate_duration(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample):
207
+ return min(max(int(max_tokens) // 20, 15), 60)
208
+
209
+
210
+ def run_inference(mode, prompt, system_prompt, context, src_lang, tgt_lang,
211
+ max_tokens, temperature, top_k, top_p, rep_penalty,
212
+ ngram_size, do_sample, gpu):
213
+ formatted = format_prompt(mode, prompt, system_prompt, context, src_lang, tgt_lang)
214
  if gpu:
215
+ try:
216
+ yield from run_inference_gpu(formatted, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample)
217
+ except Exception as e:
218
+ yield f"GPU error: {e}\n\nClick **Generate** to retry.", "GPU Unavailable"
219
  else:
220
+ yield from run_inference_raw(formatted, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample)
221
 
222
 
223
+ @spaces.GPU(duration=estimate_duration)
224
  def run_inference_gpu(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample):
225
+ if model_manager.model is not None:
226
+ model_manager.model = model_manager.model.to("cuda")
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  yield from run_inference_raw(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample, use_cuda=True)
229
 
230
+
231
  def run_inference_raw(user_prompt, max_tokens, temperature, top_k, top_p, rep_penalty, ngram_size, do_sample, use_cuda=False):
 
232
  if model_manager.model is None or model_manager.tokenizer is None:
233
  yield "Please load a model first.", "Model not loaded"
234
  return
235
+
 
236
  model_manager.stop_generation = False
237
+
238
  tokenizer = model_manager.tokenizer
239
  model = model_manager.model
240
  model_id = model_manager.model_id
241
+
242
  is_supra_reasoning = "Supra-50M-Reasoning" in model_id if model_id else False
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
+ if is_supra_reasoning and "<|begin_of_thought|>" not in user_prompt:
245
+ user_prompt += "<|begin_of_thought|>\n"
246
+
247
+ inputs = tokenizer([user_prompt], return_tensors="pt")
248
+
249
  if use_cuda:
250
  inputs = {k: v.to("cuda") for k, v in inputs.items()}
251
  else:
252
  model = model.to("cpu")
253
  inputs = {k: v.to("cpu") for k, v in inputs.items()}
254
+
255
+ streamer = TextIteratorStreamer(tokenizer, timeout=60.0, skip_prompt=True, skip_special_tokens=True)
256
+
 
 
257
  if not do_sample:
258
+ temperature = 1.0
259
 
 
260
  generate_kwargs = dict(
261
  **inputs,
262
  streamer=streamer,
 
267
  repetition_penalty=float(rep_penalty),
268
  no_repeat_ngram_size=int(ngram_size),
269
  do_sample=do_sample,
270
+ pad_token_id=tokenizer.eos_token_id,
271
+ stopping_criteria=StoppingCriteriaList([StopOnFlag()])
272
  )
273
 
274
  start_time = time.time()
 
275
  thread = Thread(target=model.generate, kwargs=generate_kwargs)
276
  thread.start()
277
+
278
  if is_supra_reasoning:
279
+ base_display = f"Prompt: {user_prompt}\n\n{'─' * 40}\n\n"
 
280
  generated_text = ""
281
  else:
282
  base_display = ""
283
+ generated_text = ""
284
 
 
285
  token_count = 0
286
  for new_text in streamer:
 
287
  if model_manager.stop_generation:
288
  break
289
+
290
  generated_text += new_text
291
  token_count += 1
292
  duration = time.time() - start_time
293
  tps = token_count / duration if duration > 0 else 0
294
+
295
  display_text = generated_text
296
+
297
  if is_supra_reasoning:
298
  display_text = display_text.replace("<s>", "").replace("</s>", "")
299
+ if not display_text.startswith("Thinking Process:"):
300
+ display_text = "Thinking Process:\n" + display_text
301
+ display_text = display_text.replace("<|begin_of_thought|>", "Thinking Process:\n")
 
302
  display_text = display_text.replace("<|end_of_thought|>", "\n\n")
303
+ display_text = display_text.replace("<|begin_of_solution|>", "Final Answer:\n\n")
304
  display_text = display_text.replace("<|end_of_solution|>", "")
305
 
306
  device_label = "CUDA" if use_cuda else "CPU"
307
  yield base_display + display_text, f"Speed: {tps:.2f} tokens/sec ({device_label})"
308
 
309
+
310
  def clean_cache():
311
  if os.path.exists(HF_CACHE_DIR):
312
  shutil.rmtree(HF_CACHE_DIR)
 
314
  return "Cache cleaned successfully!"
315
  return "Cache directory not found."
316
 
317
+
318
+ with gr.Blocks(title="SLM Model Tester") as app:
319
+
320
+ gr.Markdown("# SLM Model Evaluation Hub")
321
 
322
  with gr.Row():
323
+ with gr.Column(scale=1, min_width=300):
324
+
325
+ with gr.Accordion("System", open=False):
326
+ stats_output = gr.Textbox(label="Stats", show_label=False, max_lines=5)
 
327
  gr.Timer(2).tick(get_system_stats, None, stats_output)
328
 
329
  with gr.Group():
330
+ model_id_input = gr.Dropdown(
331
+ choices=MODELS, label="Model", allow_custom_value=True,
332
+ value=DEFAULT_MODEL
333
+ )
334
  with gr.Row():
335
+ load_btn = gr.Button("Load Model", variant="primary", scale=2)
336
+ clean_btn = gr.Button("Clear Cache", variant="stop", scale=1)
337
+
338
+ with gr.Group():
339
+ mode_input = gr.Dropdown(
340
+ choices=TASK_MODES, value="Completion", label="Mode"
341
+ )
342
+ use_gpu = gr.Checkbox(label="Use GPU", value=True)
343
+
344
+ with gr.Accordion("Parameters", open=False):
345
+ do_sample_input = gr.Checkbox(label="Sampling", value=True, info="Uncheck for greedy")
346
+ max_tokens_input = gr.Slider(minimum=10, maximum=2048, value=256, step=1, label="Max Tokens")
347
+ temperature_input = gr.Slider(minimum=0.1, maximum=2.0, value=0.7, step=0.1, label="Temperature")
348
+ top_k_input = gr.Slider(minimum=0, maximum=100, value=50, step=1, label="Top-K")
349
+ top_p_input = gr.Slider(minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-P")
350
+ rep_penalty_input = gr.Slider(minimum=1.0, maximum=2.0, value=1.1, step=0.05, label="Rep. Penalty")
351
+ ngram_size_input = gr.Slider(minimum=0, maximum=10, value=0, step=1, label="N-Gram Size")
352
+
353
+ with gr.Column(scale=3):
354
+ system_prompt_input = gr.Textbox(
355
+ label="System Prompt", value="You are a helpful assistant.",
356
+ lines=2, visible=False
357
+ )
358
+
359
  user_prompt = gr.Textbox(
360
+ label="Prompt",
361
+ value="Once upon a time in a digital kingdom,",
362
+ placeholder="Enter your prompt here...",
363
+ lines=8
364
+ )
365
+
366
+ context_input = gr.Textbox(
367
+ label="Context", placeholder="Paste reference context here...",
368
+ lines=5, visible=False
369
+ )
370
+
371
+ src_lang_input = gr.Dropdown(
372
+ choices=LANGUAGES, value="English", label="Translate from",
373
+ visible=False
374
  )
375
+ tgt_lang_input = gr.Dropdown(
376
+ choices=LANGUAGES, value="Spanish", label="Translate to",
377
+ visible=False
378
+ )
379
+
380
+ run_btn = gr.Button("Generate", variant="primary", size="lg")
381
+ status_output = gr.Markdown("*Ready*")
382
+ output_text = gr.Textbox(
383
+ label="Output", lines=15, buttons=["copy"], autoscroll=True
384
+ )
385
+
386
  load_btn.click(
387
+ fn=load_new_model,
388
+ inputs=[model_id_input],
389
  outputs=[status_output]
390
  )
391
+
392
+ mode_input.change(
393
+ fn=update_mode_ui,
394
+ inputs=[mode_input],
395
+ outputs=[system_prompt_input, user_prompt, context_input, src_lang_input, tgt_lang_input]
396
+ )
397
+
398
  run_btn.click(
399
  fn=run_inference,
400
  inputs=[
401
+ mode_input,
402
+ user_prompt,
403
+ system_prompt_input,
404
+ context_input,
405
+ src_lang_input,
406
+ tgt_lang_input,
407
+ max_tokens_input,
408
+ temperature_input,
409
+ top_k_input,
410
+ top_p_input,
411
+ rep_penalty_input,
412
  ngram_size_input,
413
  do_sample_input,
414
  use_gpu
415
  ],
416
  outputs=[output_text, status_output]
417
  )
418
+
419
  clean_btn.click(fn=clean_cache, outputs=[status_output])
420
 
421
  if __name__ == "__main__":
422
+ app.launch(theme=gr.themes.Soft())