Update app.py
Browse files
app.py
CHANGED
|
@@ -12,7 +12,7 @@ from transformers import (
|
|
| 12 |
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 13 |
|
| 14 |
# -------------------------------------------------------------------------
|
| 15 |
-
# Model components (
|
| 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 &
|
| 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
|
| 267 |
-
"""Autoregressive generation with ROSA updates,
|
| 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.
|
| 304 |
-
# Build the full conversation in the chat template
|
| 305 |
messages = []
|
| 306 |
-
for
|
| 307 |
-
|
| 308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 334 |
-
|
|
|
|
|
|
|
| 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
|