Fix .generate() support: add GenerationMixin + generation_config, document KV-cache limitation

#2
Files changed (1) hide show
  1. modeling_nanochat.py +5 -8
modeling_nanochat.py CHANGED
@@ -1,7 +1,7 @@
1
  import torch
2
  import torch.nn as nn
3
  import torch.nn.functional as F
4
- from transformers import PreTrainedModel
5
  from transformers.modeling_outputs import CausalLMOutputWithPast
6
 
7
  from .configuration_nanochat import NanochatConfig
@@ -40,16 +40,13 @@ def _apply_rotary(x, cos, sin):
40
 
41
 
42
  def _sdpa_attention(q, k, v, window_size, enable_gqa):
43
- # q/k/v are (B, H, T, D)
44
  t_q = q.size(2)
45
  t_k = k.size(2)
46
  left_window = window_size[0]
47
 
48
- # Full causal attention when the window covers full context.
49
  if (left_window < 0 or left_window >= t_q) and t_q == t_k:
50
  return F.scaled_dot_product_attention(q, k, v, is_causal=True, enable_gqa=enable_gqa)
51
 
52
- # Single-token decode path.
53
  if t_q == 1:
54
  if left_window >= 0 and left_window < t_k:
55
  start = max(0, t_k - (left_window + 1))
@@ -57,7 +54,6 @@ def _sdpa_attention(q, k, v, window_size, enable_gqa):
57
  v = v[:, :, start:, :]
58
  return F.scaled_dot_product_attention(q, k, v, is_causal=False, enable_gqa=enable_gqa)
59
 
60
- # Build explicit causal (+ optional sliding-window) mask.
61
  device = q.device
62
  row_idx = (t_k - t_q) + torch.arange(t_q, device=device).unsqueeze(1)
63
  col_idx = torch.arange(t_k, device=device).unsqueeze(0)
@@ -71,7 +67,6 @@ def _sdpa_attention(q, k, v, window_size, enable_gqa):
71
  def _flash_attn_func(q, k, v, causal=False, window_size=(-1, -1)):
72
  if not causal:
73
  raise NotImplementedError("Nanochat HF export currently supports only causal attention")
74
- # SDPA fallback mirroring nanochat.flash_attention semantics.
75
  q = q.transpose(1, 2)
76
  k = k.transpose(1, 2)
77
  v = v.transpose(1, 2)
@@ -240,7 +235,7 @@ class NanochatBackbone(nn.Module):
240
  return logits
241
 
242
 
243
- class NanochatForCausalLM(PreTrainedModel):
244
  config_class = NanochatConfig
245
  base_model_prefix = "model"
246
  main_input_name = "input_ids"
@@ -249,6 +244,8 @@ class NanochatForCausalLM(PreTrainedModel):
249
  def __init__(self, config):
250
  super().__init__(config)
251
  self.model = NanochatBackbone(config)
 
 
252
 
253
  @property
254
  def all_tied_weights_keys(self):
@@ -299,4 +296,4 @@ class NanochatForCausalLM(PreTrainedModel):
299
  )
300
 
301
  def prepare_inputs_for_generation(self, input_ids, past_key_values=None, **kwargs):
302
- return {"input_ids": input_ids}
 
1
  import torch
2
  import torch.nn as nn
3
  import torch.nn.functional as F
4
+ from transformers import PreTrainedModel, GenerationMixin, GenerationConfig
5
  from transformers.modeling_outputs import CausalLMOutputWithPast
6
 
7
  from .configuration_nanochat import NanochatConfig
 
40
 
41
 
42
  def _sdpa_attention(q, k, v, window_size, enable_gqa):
 
43
  t_q = q.size(2)
44
  t_k = k.size(2)
45
  left_window = window_size[0]
46
 
 
47
  if (left_window < 0 or left_window >= t_q) and t_q == t_k:
48
  return F.scaled_dot_product_attention(q, k, v, is_causal=True, enable_gqa=enable_gqa)
49
 
 
50
  if t_q == 1:
51
  if left_window >= 0 and left_window < t_k:
52
  start = max(0, t_k - (left_window + 1))
 
54
  v = v[:, :, start:, :]
55
  return F.scaled_dot_product_attention(q, k, v, is_causal=False, enable_gqa=enable_gqa)
56
 
 
57
  device = q.device
58
  row_idx = (t_k - t_q) + torch.arange(t_q, device=device).unsqueeze(1)
59
  col_idx = torch.arange(t_k, device=device).unsqueeze(0)
 
67
  def _flash_attn_func(q, k, v, causal=False, window_size=(-1, -1)):
68
  if not causal:
69
  raise NotImplementedError("Nanochat HF export currently supports only causal attention")
 
70
  q = q.transpose(1, 2)
71
  k = k.transpose(1, 2)
72
  v = v.transpose(1, 2)
 
235
  return logits
236
 
237
 
238
+ class NanochatForCausalLM(PreTrainedModel, GenerationMixin):
239
  config_class = NanochatConfig
240
  base_model_prefix = "model"
241
  main_input_name = "input_ids"
 
244
  def __init__(self, config):
245
  super().__init__(config)
246
  self.model = NanochatBackbone(config)
247
+ if getattr(self, "generation_config", None) is None:
248
+ self.generation_config = GenerationConfig.from_model_config(config)
249
 
250
  @property
251
  def all_tied_weights_keys(self):
 
296
  )
297
 
298
  def prepare_inputs_for_generation(self, input_ids, past_key_values=None, **kwargs):
299
+ return {"input_ids": input_ids}