FlameF0X commited on
Commit
ec1c2cd
·
verified ·
1 Parent(s): f856672

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -33
app.py CHANGED
@@ -12,11 +12,8 @@ from transformers import (
12
  )
13
  from transformers.modeling_outputs import CausalLMOutputWithPast
14
 
15
- # -------------------------------------------------------------------------
16
- # Model components (matches saved checkpoint architecture)
17
- # -------------------------------------------------------------------------
18
-
19
  class FWKVConfig(PretrainedConfig):
 
20
  model_type = "fwkv"
21
 
22
  def __init__(
@@ -40,7 +37,6 @@ class FWKVConfig(PretrainedConfig):
40
  self.seq_len = seq_len
41
  self.wkv_floor = wkv_floor
42
 
43
-
44
  def rosa(x: list[int]) -> list[int]:
45
  """Causal copy‑signal predictor; returns y[i] = token after longest
46
  repeating suffix ending at i, or -1 if none."""
@@ -99,7 +95,6 @@ def rosa(x: list[int]) -> list[int]:
99
  v = link[v]
100
  return y
101
 
102
-
103
  def parallel_scan_decay(a: torch.Tensor, W: torch.Tensor) -> torch.Tensor:
104
  """Hillis–Steele inclusive scan with constant per‑channel decay."""
105
  W = W.to(dtype=a.dtype) # keep precision
@@ -112,8 +107,8 @@ def parallel_scan_decay(a: torch.Tensor, W: torch.Tensor) -> torch.Tensor:
112
  d *= 2
113
  return val
114
 
115
-
116
  class FactorizedTiedHead(nn.Module):
 
117
  def __init__(self, vocab_size: int, d_model: int, d_emb: int):
118
  super().__init__()
119
  self.d_model = d_model
@@ -130,8 +125,8 @@ class FactorizedTiedHead(nn.Module):
130
  def logits(self, x_emb):
131
  return F.linear(x_emb, self.weight)
132
 
133
-
134
  class FWKVBlock(nn.Module):
 
135
  def __init__(self, d: int, ffn_mult: int = 4, floor: float = 0.1):
136
  super().__init__()
137
  self.floor = floor
@@ -171,8 +166,8 @@ class FWKVBlock(nn.Module):
171
  x = self.norm_ffn(x + self.ffn(x))
172
  return x, new_state
173
 
174
-
175
  class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
 
176
  config_class = FWKVConfig
177
 
178
  def __init__(self, config):
@@ -231,11 +226,6 @@ class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
231
  return {"input_ids": input_ids, "rosa_ids": rosa_ids,
232
  "past_key_values": past_key_values, "use_cache": True}
233
 
234
-
235
- # -------------------------------------------------------------------------
236
- # Loading & Streaming Helpers
237
- # -------------------------------------------------------------------------
238
-
239
  USER_TOKEN = "<|user|>"
240
  ASSISTANT_TOKEN = "<|assistant|>"
241
 
@@ -259,11 +249,11 @@ model, tokenizer, load_status = load_model()
259
 
260
  @torch.no_grad()
261
  def generate_reply_stream(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50):
262
- """Autoregressive generation with ROSA updates, yielding generated token lists and speed (tok/s) step-by-step."""
263
  device = next(model.parameters()).device
264
  eos_id = tokenizer.eos_token_id
265
 
266
- # initial forward over the whole prompt
267
  inp = torch.tensor([ids], device=device)
268
  rosa_ids = torch.tensor([rosa(ids)], device=device)
269
  out = model(input_ids=inp, rosa_ids=rosa_ids, use_cache=True)
@@ -273,6 +263,8 @@ def generate_reply_stream(ids: list[int], max_new_tokens=150, temperature=0.8, t
273
  reply_tokens = []
274
 
275
  start_time = time.perf_counter()
 
 
276
 
277
  for _ in range(max_new_tokens):
278
  scaled = logits / max(temperature, 1e-5)
@@ -286,11 +278,33 @@ def generate_reply_stream(ids: list[int], max_new_tokens=150, temperature=0.8, t
286
  break
287
 
288
  reply_tokens.append(next_token)
289
- elapsed = time.perf_counter() - start_time
290
- tps = len(reply_tokens) / elapsed if elapsed > 0 else 0.0
291
- yield reply_tokens, tps
 
 
292
 
293
- # ROSA for the extended sequence, use the last prediction
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  next_rosa = rosa(generated)[-1]
295
  step_inp = torch.tensor([[next_token]], device=device)
296
  step_rosa = torch.tensor([[next_rosa]], device=device)
@@ -299,9 +313,8 @@ def generate_reply_stream(ids: list[int], max_new_tokens=150, temperature=0.8, t
299
  states = out.past_key_values
300
  logits = out.logits[0, -1]
301
 
302
-
303
  def chat_function(message, history):
304
- """Gradio ChatInterface streaming callback with tok/s throughput display."""
305
  messages = []
306
  for turn in history:
307
  if isinstance(turn, (list, tuple)):
@@ -313,7 +326,7 @@ def chat_function(message, history):
313
  messages.append(turn)
314
  messages.append({"role": "user", "content": message})
315
 
316
- # Convert to token IDs
317
  user_id = tokenizer.convert_tokens_to_ids(USER_TOKEN)
318
  asst_id = tokenizer.convert_tokens_to_ids(ASSISTANT_TOKEN)
319
  eos_id = tokenizer.eos_token_id
@@ -327,23 +340,22 @@ def chat_function(message, history):
327
  elif role == "assistant":
328
  ids += [asst_id] + content_ids + [eos_id]
329
 
330
- # Truncate from the left if needed
331
  max_len = model.config.seq_len if model else 1024
332
  if len(ids) > max_len:
333
  ids = ids[-max_len:]
334
 
335
- # Cue the assistant to start replying
336
  ids.append(asst_id)
337
 
338
- # Stream generated tokens and tok/s throughput in real time
339
- for reply_tokens, tps in generate_reply_stream(ids, max_new_tokens=150, temperature=0.8, top_k=50):
340
  reply = tokenizer.decode(reply_tokens, skip_special_tokens=True).strip()
341
- yield f"{reply}\n\n⚡ *{tps:.1f} tok/s*"
342
-
343
-
344
- # -------------------------------------------------------------------------
345
- # Gradio UI
346
- # -------------------------------------------------------------------------
347
 
348
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
349
  gr.Markdown(f"""
 
12
  )
13
  from transformers.modeling_outputs import CausalLMOutputWithPast
14
 
 
 
 
 
15
  class FWKVConfig(PretrainedConfig):
16
+ """Configuration class for FWKV-ROSA model architecture."""
17
  model_type = "fwkv"
18
 
19
  def __init__(
 
37
  self.seq_len = seq_len
38
  self.wkv_floor = wkv_floor
39
 
 
40
  def rosa(x: list[int]) -> list[int]:
41
  """Causal copy‑signal predictor; returns y[i] = token after longest
42
  repeating suffix ending at i, or -1 if none."""
 
95
  v = link[v]
96
  return y
97
 
 
98
  def parallel_scan_decay(a: torch.Tensor, W: torch.Tensor) -> torch.Tensor:
99
  """Hillis–Steele inclusive scan with constant per‑channel decay."""
100
  W = W.to(dtype=a.dtype) # keep precision
 
107
  d *= 2
108
  return val
109
 
 
110
  class FactorizedTiedHead(nn.Module):
111
+ """Factorized embedding projection and tied output head."""
112
  def __init__(self, vocab_size: int, d_model: int, d_emb: int):
113
  super().__init__()
114
  self.d_model = d_model
 
125
  def logits(self, x_emb):
126
  return F.linear(x_emb, self.weight)
127
 
 
128
  class FWKVBlock(nn.Module):
129
+ """FWKV layer with linear time attention-style recurrent mechanism."""
130
  def __init__(self, d: int, ffn_mult: int = 4, floor: float = 0.1):
131
  super().__init__()
132
  self.floor = floor
 
166
  x = self.norm_ffn(x + self.ffn(x))
167
  return x, new_state
168
 
 
169
  class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
170
+ """Full causal language model utilizing FWKV recurrent layers and ROSA embeddings."""
171
  config_class = FWKVConfig
172
 
173
  def __init__(self, config):
 
226
  return {"input_ids": input_ids, "rosa_ids": rosa_ids,
227
  "past_key_values": past_key_values, "use_cache": True}
228
 
 
 
 
 
 
229
  USER_TOKEN = "<|user|>"
230
  ASSISTANT_TOKEN = "<|assistant|>"
231
 
 
249
 
250
  @torch.no_grad()
251
  def generate_reply_stream(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50):
252
+ """Autoregressive generation with ROSA updates, yielding token lists and comprehensive throughput metrics."""
253
  device = next(model.parameters()).device
254
  eos_id = tokenizer.eos_token_id
255
 
256
+ # Initial forward pass over the prompt
257
  inp = torch.tensor([ids], device=device)
258
  rosa_ids = torch.tensor([rosa(ids)], device=device)
259
  out = model(input_ids=inp, rosa_ids=rosa_ids, use_cache=True)
 
263
  reply_tokens = []
264
 
265
  start_time = time.perf_counter()
266
+ prev_step_time = start_time
267
+ instant_tps_list = []
268
 
269
  for _ in range(max_new_tokens):
270
  scaled = logits / max(temperature, 1e-5)
 
278
  break
279
 
280
  reply_tokens.append(next_token)
281
+ now = time.perf_counter()
282
+
283
+ # Calculate per-step instant duration and speed
284
+ step_duration = now - prev_step_time
285
+ prev_step_time = now
286
 
287
+ if step_duration > 0:
288
+ instant_tps = 1.0 / step_duration
289
+ instant_tps_list.append(instant_tps)
290
+
291
+ # Compute aggregate throughput metrics
292
+ total_elapsed = now - start_time
293
+ avg_tps = len(reply_tokens) / total_elapsed if total_elapsed > 0 else 0.0
294
+ current_tps = instant_tps_list[-1] if instant_tps_list else avg_tps
295
+ min_tps = min(instant_tps_list) if instant_tps_list else avg_tps
296
+ max_tps = max(instant_tps_list) if instant_tps_list else avg_tps
297
+
298
+ stats = {
299
+ "current": current_tps,
300
+ "avg": avg_tps,
301
+ "min": min_tps,
302
+ "max": max_tps,
303
+ }
304
+
305
+ yield reply_tokens, stats
306
+
307
+ # ROSA prediction for the next step
308
  next_rosa = rosa(generated)[-1]
309
  step_inp = torch.tensor([[next_token]], device=device)
310
  step_rosa = torch.tensor([[next_rosa]], device=device)
 
313
  states = out.past_key_values
314
  logits = out.logits[0, -1]
315
 
 
316
  def chat_function(message, history):
317
+ """Gradio ChatInterface streaming handler formatted with speed stats (Live, Avg, Min, Max)."""
318
  messages = []
319
  for turn in history:
320
  if isinstance(turn, (list, tuple)):
 
326
  messages.append(turn)
327
  messages.append({"role": "user", "content": message})
328
 
329
+ # Encode token sequence according to model chat template
330
  user_id = tokenizer.convert_tokens_to_ids(USER_TOKEN)
331
  asst_id = tokenizer.convert_tokens_to_ids(ASSISTANT_TOKEN)
332
  eos_id = tokenizer.eos_token_id
 
340
  elif role == "assistant":
341
  ids += [asst_id] + content_ids + [eos_id]
342
 
343
+ # Truncate left if context exceeds model max sequence length
344
  max_len = model.config.seq_len if model else 1024
345
  if len(ids) > max_len:
346
  ids = ids[-max_len:]
347
 
348
+ # Prompt assistant response
349
  ids.append(asst_id)
350
 
351
+ # Stream generated output with full throughput statistics
352
+ for reply_tokens, stats in generate_reply_stream(ids, max_new_tokens=150, temperature=0.8, top_k=50):
353
  reply = tokenizer.decode(reply_tokens, skip_special_tokens=True).strip()
354
+ metrics_bar = (
355
+ f"⚡ **{stats['current']:.1f} tok/s** "
356
+ f"*(Avg: **{stats['avg']:.1f}** | Min: **{stats['min']:.1f}** | Max: **{stats['max']:.1f}** tok/s)*"
357
+ )
358
+ yield f"{reply}\n\n{metrics_bar}"
 
359
 
360
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
361
  gr.Markdown(f"""