FlameF0X commited on
Commit
3668700
Β·
verified Β·
1 Parent(s): 561ffed

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +25 -27
app.py CHANGED
@@ -12,7 +12,7 @@ from transformers import (
12
  from transformers.modeling_outputs import CausalLMOutputWithPast
13
 
14
  # -------------------------------------------------------------------------
15
- # Model components (must match the saved checkpoint exactly)
16
  # -------------------------------------------------------------------------
17
 
18
  class FWKVConfig(PretrainedConfig):
@@ -40,7 +40,6 @@ class FWKVConfig(PretrainedConfig):
40
  self.wkv_floor = wkv_floor
41
 
42
 
43
- # ── ROSA (exact copy from training) ─────────────────────────────────────
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."""
@@ -100,7 +99,6 @@ def rosa(x: list[int]) -> list[int]:
100
  return y
101
 
102
 
103
- # ── Vectorised parallel scan ─────────────────────────────────────────────
104
  def parallel_scan_decay(a: torch.Tensor, W: torch.Tensor) -> torch.Tensor:
105
  """Hillis–Steele inclusive scan with constant per‑channel decay."""
106
  W = W.to(dtype=a.dtype) # keep precision
@@ -114,7 +112,6 @@ def parallel_scan_decay(a: torch.Tensor, W: torch.Tensor) -> torch.Tensor:
114
  return val
115
 
116
 
117
- # ── Factorised tied head ─────────────────────────────────────────────────
118
  class FactorizedTiedHead(nn.Module):
119
  def __init__(self, vocab_size: int, d_model: int, d_emb: int):
120
  super().__init__()
@@ -133,7 +130,6 @@ class FactorizedTiedHead(nn.Module):
133
  return F.linear(x_emb, self.weight)
134
 
135
 
136
- # ── FWKV Block (with parallel scan) ──────────────────────────────────────
137
  class FWKVBlock(nn.Module):
138
  def __init__(self, d: int, ffn_mult: int = 4, floor: float = 0.1):
139
  super().__init__()
@@ -175,7 +171,6 @@ class FWKVBlock(nn.Module):
175
  return x, new_state
176
 
177
 
178
- # ── Full Language Model ──────────────────────────────────────────────────
179
  class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
180
  config_class = FWKVConfig
181
 
@@ -203,7 +198,6 @@ class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
203
  **kwargs,
204
  ):
205
  if rosa_ids is None:
206
- # fallback: compute on the fly (expensive, but safe)
207
  rows = [rosa(row.tolist()) for row in input_ids.detach().cpu()]
208
  rosa_ids = torch.tensor(rows, device=input_ids.device, dtype=torch.long)
209
 
@@ -238,7 +232,7 @@ class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
238
 
239
 
240
  # -------------------------------------------------------------------------
241
- # Loading & Chat helpers
242
  # -------------------------------------------------------------------------
243
 
244
  USER_TOKEN = "<|user|>"
@@ -263,8 +257,8 @@ def load_model():
263
  model, tokenizer, load_status = load_model()
264
 
265
  @torch.no_grad()
266
- def generate_reply(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50):
267
- """Autoregressive generation with ROSA updates, returning full sequence."""
268
  device = next(model.parameters()).device
269
  eos_id = tokenizer.eos_token_id
270
 
@@ -275,6 +269,7 @@ def generate_reply(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50
275
  states = out.past_key_values
276
  logits = out.logits[0, -1]
277
  generated = list(ids)
 
278
 
279
  for _ in range(max_new_tokens):
280
  scaled = logits / max(temperature, 1e-5)
@@ -287,6 +282,9 @@ def generate_reply(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50
287
  if next_token == eos_id:
288
  break
289
 
 
 
 
290
  # ROSA for the extended sequence, use the last prediction
291
  next_rosa = rosa(generated)[-1]
292
  step_inp = torch.tensor([[next_token]], device=device)
@@ -296,16 +294,18 @@ def generate_reply(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50
296
  states = out.past_key_values
297
  logits = out.logits[0, -1]
298
 
299
- return generated
300
-
301
 
302
  def chat_function(message, history):
303
- """Gradio ChatInterface callback. history is a list of (user, assistant) pairs."""
304
- # Build the full conversation in the chat template
305
  messages = []
306
- for user_msg, asst_msg in history:
307
- messages.append({"role": "user", "content": user_msg})
308
- messages.append({"role": "assistant", "content": asst_msg})
 
 
 
 
 
309
  messages.append({"role": "user", "content": message})
310
 
311
  # Convert to token IDs
@@ -323,25 +323,23 @@ def chat_function(message, history):
323
  ids += [asst_id] + content_ids + [eos_id]
324
 
325
  # Truncate from the left if needed
326
- max_len = model.config.seq_len
327
  if len(ids) > max_len:
328
  ids = ids[-max_len:]
329
 
330
  # Cue the assistant to start replying
331
  ids.append(asst_id)
332
 
333
- # Generate the assistant's reply
334
- full_gen = generate_reply(ids, max_new_tokens=150, temperature=0.8, top_k=50)
 
 
335
 
336
- # Extract only the new assistant tokens (after the last asst_id)
337
- # Find the position of the last asst_id and take everything after it
338
- assistant_start = len(ids) - 1 # index of the asst_id we just appended
339
- reply_ids = full_gen[assistant_start + 1:] # skip the asst_id itself
340
- reply = tokenizer.decode(reply_ids, skip_special_tokens=True).strip()
341
- return reply
342
 
 
 
 
343
 
344
- # ── Gradio UI ────────────────────────────────────────────────────────────
345
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
346
  gr.Markdown(f"""
347
  # ⚑ FWKV-ROSA Chat
 
12
  from transformers.modeling_outputs import CausalLMOutputWithPast
13
 
14
  # -------------------------------------------------------------------------
15
+ # Model components (matches saved checkpoint architecture)
16
  # -------------------------------------------------------------------------
17
 
18
  class FWKVConfig(PretrainedConfig):
 
40
  self.wkv_floor = wkv_floor
41
 
42
 
 
43
  def rosa(x: list[int]) -> list[int]:
44
  """Causal copy‑signal predictor; returns y[i] = token after longest
45
  repeating suffix ending at i, or -1 if none."""
 
99
  return y
100
 
101
 
 
102
  def parallel_scan_decay(a: torch.Tensor, W: torch.Tensor) -> torch.Tensor:
103
  """Hillis–Steele inclusive scan with constant per‑channel decay."""
104
  W = W.to(dtype=a.dtype) # keep precision
 
112
  return val
113
 
114
 
 
115
  class FactorizedTiedHead(nn.Module):
116
  def __init__(self, vocab_size: int, d_model: int, d_emb: int):
117
  super().__init__()
 
130
  return F.linear(x_emb, self.weight)
131
 
132
 
 
133
  class FWKVBlock(nn.Module):
134
  def __init__(self, d: int, ffn_mult: int = 4, floor: float = 0.1):
135
  super().__init__()
 
171
  return x, new_state
172
 
173
 
 
174
  class FWKVLanguageModel(PreTrainedModel, GenerationMixin):
175
  config_class = FWKVConfig
176
 
 
198
  **kwargs,
199
  ):
200
  if rosa_ids is None:
 
201
  rows = [rosa(row.tolist()) for row in input_ids.detach().cpu()]
202
  rosa_ids = torch.tensor(rows, device=input_ids.device, dtype=torch.long)
203
 
 
232
 
233
 
234
  # -------------------------------------------------------------------------
235
+ # Loading & Streaming Helpers
236
  # -------------------------------------------------------------------------
237
 
238
  USER_TOKEN = "<|user|>"
 
257
  model, tokenizer, load_status = load_model()
258
 
259
  @torch.no_grad()
260
+ def generate_reply_stream(ids: list[int], max_new_tokens=150, temperature=0.8, top_k=50):
261
+ """Autoregressive generation with ROSA updates, yielding generated token lists step-by-step."""
262
  device = next(model.parameters()).device
263
  eos_id = tokenizer.eos_token_id
264
 
 
269
  states = out.past_key_values
270
  logits = out.logits[0, -1]
271
  generated = list(ids)
272
+ reply_tokens = []
273
 
274
  for _ in range(max_new_tokens):
275
  scaled = logits / max(temperature, 1e-5)
 
282
  if next_token == eos_id:
283
  break
284
 
285
+ reply_tokens.append(next_token)
286
+ yield reply_tokens
287
+
288
  # ROSA for the extended sequence, use the last prediction
289
  next_rosa = rosa(generated)[-1]
290
  step_inp = torch.tensor([[next_token]], device=device)
 
294
  states = out.past_key_values
295
  logits = out.logits[0, -1]
296
 
 
 
297
 
298
  def chat_function(message, history):
299
+ """Gradio ChatInterface streaming callback."""
 
300
  messages = []
301
+ for turn in history:
302
+ if isinstance(turn, (list, tuple)):
303
+ user_msg, asst_msg = turn
304
+ messages.append({"role": "user", "content": user_msg})
305
+ if asst_msg:
306
+ messages.append({"role": "assistant", "content": asst_msg})
307
+ elif isinstance(turn, dict):
308
+ messages.append(turn)
309
  messages.append({"role": "user", "content": message})
310
 
311
  # Convert to token IDs
 
323
  ids += [asst_id] + content_ids + [eos_id]
324
 
325
  # Truncate from the left if needed
326
+ max_len = model.config.seq_len if model else 1024
327
  if len(ids) > max_len:
328
  ids = ids[-max_len:]
329
 
330
  # Cue the assistant to start replying
331
  ids.append(asst_id)
332
 
333
+ # Stream generated tokens in real time
334
+ for reply_tokens in generate_reply_stream(ids, max_new_tokens=150, temperature=0.8, top_k=50):
335
+ reply = tokenizer.decode(reply_tokens, skip_special_tokens=True).strip()
336
+ yield reply
337
 
 
 
 
 
 
 
338
 
339
+ # -------------------------------------------------------------------------
340
+ # Gradio UI
341
+ # -------------------------------------------------------------------------
342
 
 
343
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
344
  gr.Markdown(f"""
345
  # ⚑ FWKV-ROSA Chat