Instructions to use ModelCloud/Ripple-65B-Preview-tf5-patch with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ModelCloud/Ripple-65B-Preview-tf5-patch with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ModelCloud/Ripple-65B-Preview-tf5-patch", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download modeling_ricursive.py from ModelCloud/Ripple-65B-Preview-tf5-patch: direct link, hf CLI and curl.
- Browser
- Download file 30.8 kB
-
https://huggingface.co/ModelCloud/Ripple-65B-Preview-tf5-patch/resolve/main/modeling_ricursive.py
- Command line
-
hf download hf://ModelCloud/Ripple-65B-Preview-tf5-patch/modeling_ricursive.py
-
curl -L -o modeling_ricursive.py https://huggingface.co/ModelCloud/Ripple-65B-Preview-tf5-patch/resolve/main/modeling_ricursive.py
30.8 kB
| """HuggingFace modeling implementation for the Ricursive transformer architecture. | |
| Designed for inference with vLLM (via --trust-remote-code). Key differences from | |
| standard Llama/Mistral: | |
| - PeriNorm blocks: 4 layer norms per block (pre/post attention + pre/post FFN) | |
| - Gated attention: elementwise sigmoid gate on attention output | |
| - Mixed local/global layers: local = sliding window + RoPE, global = full attention + no RoPE | |
| - Per-head Q/K RMSNorm | |
| Transformers 5.x compatibility (2026-11-05) | |
| ------------------------------------------ | |
| `generate()` used to break on the very first prefill because | |
| `prepare_inputs_for_generation` sliced `input_ids` with a `None` | |
| `cache_position`, and the causal/sliding-window mask builders assumed the | |
| transformers 4.x cache API. The fixes are applied inline below: | |
| * `prepare_inputs_for_generation` rebuilds a missing `cache_position`, slices | |
| only when the cache is warm, derives per-row `position_ids` from the padding | |
| mask, and moves inputs to the model device. | |
| * `_update_causal_mask` sizes the key axis from `cache_position` instead of the | |
| removed `get_max_cache_shape()`, emits `0.0` for visible keys, and never | |
| returns an all-masked single-token decode row. | |
| * `_apply_sliding_window_mask` anchors key positions at the first key the layer | |
| actually holds (the cache evicts from the left) and is skipped when the | |
| window cannot bind. | |
| * `RicursiveForCausalLM` inherits `GenerationMixin` explicitly; transformers 5 | |
| no longer injects it into remote-code classes. | |
| """ | |
| from typing import Optional, Tuple, Union | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers import PreTrainedModel | |
| from transformers.cache_utils import Cache, DynamicCache | |
| from transformers.modeling_outputs import ( | |
| BaseModelOutputWithPast, | |
| CausalLMOutputWithPast, | |
| ) | |
| try: # transformers >= 4.26, re-exported from here in 5.x | |
| from transformers.generation.utils import GenerationMixin | |
| except ImportError: # pragma: no cover - very old transformers | |
| from transformers.generation import GenerationMixin | |
| from .configuration_ricursive import RicursiveConfig | |
| # Set when the transformers-5.x `generate()` fixes above are present natively. | |
| # Downstream loaders that monkeypatch older checkpoints check this marker and | |
| # stand down instead of re-installing the equivalent runtime patch. | |
| RICURSIVE_TF5_COMPAT = "2026-11-05" | |
| class RicursiveRMSNorm(nn.Module): | |
| def __init__(self, hidden_size: int, eps: float = 1e-6): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(hidden_size)) | |
| self.eps = eps | |
| def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: | |
| input_dtype = hidden_states.dtype | |
| hidden_states = hidden_states.float() | |
| variance = hidden_states.pow(2).mean(-1, keepdim=True) | |
| hidden_states = hidden_states * torch.rsqrt(variance + self.eps) | |
| return self.weight * hidden_states.to(input_dtype) | |
| class RicursiveRotaryEmbedding(nn.Module): | |
| def __init__(self, dim: int, theta: float = 10000.0): | |
| super().__init__() | |
| self.dim = dim | |
| self.theta = theta | |
| # NOTE: inv_freq is computed on-the-fly in forward() rather than stored | |
| # as a buffer, because non-persistent buffers get corrupted during | |
| # from_pretrained() loading. | |
| def forward(self, x: torch.Tensor, position_ids: torch.Tensor): | |
| inv_freq = 1.0 / ( | |
| self.theta ** (torch.arange(0, self.dim, 2, device=x.device).float() / self.dim) | |
| ) | |
| inv_freq_expanded = inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1) | |
| position_ids_expanded = position_ids[:, None, :].float() | |
| device_type = x.device.type | |
| with torch.autocast(device_type=device_type, enabled=False): | |
| freqs = (inv_freq_expanded @ position_ids_expanded).transpose(1, 2) | |
| emb = torch.cat((freqs, freqs), dim=-1) | |
| cos = emb.cos() | |
| sin = emb.sin() | |
| return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype) | |
| def rotate_half(x: torch.Tensor) -> torch.Tensor: | |
| x1 = x[..., : x.shape[-1] // 2] | |
| x2 = x[..., x.shape[-1] // 2 :] | |
| return torch.cat((-x2, x1), dim=-1) | |
| def apply_rotary_pos_emb( | |
| q: torch.Tensor, | |
| k: torch.Tensor, | |
| cos: torch.Tensor, | |
| sin: torch.Tensor, | |
| ) -> Tuple[torch.Tensor, torch.Tensor]: | |
| cos = cos.unsqueeze(1) # (B, 1, T, dim) | |
| sin = sin.unsqueeze(1) # (B, 1, T, dim) | |
| # OLMo-core applies RoPE in full precision (float32) for numerical stability | |
| orig_dtype = q.dtype | |
| q_float, k_float = q.float(), k.float() | |
| cos_float, sin_float = cos.float(), sin.float() | |
| q_embed = (q_float * cos_float) + (rotate_half(q_float) * sin_float) | |
| k_embed = (k_float * cos_float) + (rotate_half(k_float) * sin_float) | |
| return q_embed.to(orig_dtype), k_embed.to(orig_dtype) | |
| def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor: | |
| if n_rep == 1: | |
| return hidden_states | |
| batch, num_kv_heads, slen, head_dim = hidden_states.shape | |
| hidden_states = hidden_states[:, :, None, :, :].expand( | |
| batch, num_kv_heads, n_rep, slen, head_dim | |
| ) | |
| return hidden_states.reshape(batch, num_kv_heads * n_rep, slen, head_dim) | |
| class RicursiveAttention(nn.Module): | |
| def __init__(self, config: RicursiveConfig, layer_idx: int): | |
| super().__init__() | |
| self.config = config | |
| self.layer_idx = layer_idx | |
| self.layer_type = config.layer_types[layer_idx] | |
| self.hidden_size = config.hidden_size | |
| self.num_heads = config.num_attention_heads | |
| self.num_kv_heads = config.num_key_value_heads | |
| self.head_dim = config.head_dim | |
| self.num_kv_groups = self.num_heads // self.num_kv_heads | |
| self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False) | |
| self.k_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False) | |
| self.v_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False) | |
| self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False) | |
| if config.use_valres: | |
| self.value_res_lambda = nn.Parameter(torch.zeros(1)) | |
| # Per-head Q/K norms (RMSNorm on head_dim) | |
| self.q_norm = RicursiveRMSNorm(self.head_dim, eps=config.rms_norm_eps) | |
| self.k_norm = RicursiveRMSNorm(self.head_dim, eps=config.rms_norm_eps) | |
| # Gated attention | |
| if config.use_gated_attention: | |
| self.g_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=False) | |
| # RoPE only for local (sliding_attention) layers | |
| if self.layer_type == "sliding_attention": | |
| self.rotary_emb = RicursiveRotaryEmbedding(self.head_dim, theta=config.rope_theta) | |
| def _apply_sliding_window_mask( | |
| self, | |
| attention_mask: Optional[torch.Tensor], | |
| q_len: int, | |
| kv_len: int, | |
| dtype: torch.dtype, | |
| device: torch.device, | |
| cache_position: Optional[torch.LongTensor], | |
| ) -> Optional[torch.Tensor]: | |
| if self.layer_type != "sliding_attention": | |
| return attention_mask | |
| # transformers >= 5 builds a hybrid cache: a sliding layer holds at most | |
| # `sliding_window` keys while a full-attention layer keeps growing, so the | |
| # shared mask produced by `_update_causal_mask` can be longer than this | |
| # layer's key tensor. Keep only the keys this layer actually holds. | |
| if attention_mask is not None and attention_mask.shape[-1] > kv_len: | |
| attention_mask = attention_mask[..., -kv_len:] | |
| if cache_position is not None: | |
| query_positions = cache_position.to(device=device).reshape(-1) | |
| else: | |
| query_positions = torch.arange(kv_len - q_len, kv_len, device=device) | |
| window = self.config.sliding_window | |
| if window is None: | |
| return attention_mask | |
| if kv_len <= window: | |
| # The window cannot exclude any key, so the result is either the | |
| # unchanged (already causal) mask or, without one, plain causal | |
| # attention -- which SDPA applies through `is_causal`. | |
| if attention_mask is None: | |
| return None | |
| if q_len == 1: | |
| return attention_mask | |
| # Keys are contiguous and end at the newest query position. After the cache | |
| # evicts from the left they no longer start at position 0, so anchor the | |
| # range at the first key the layer holds instead of at zero. | |
| key_positions = torch.arange(kv_len, device=device) + ( | |
| query_positions[-1] - kv_len + 1 | |
| ) | |
| allowed = ( | |
| (key_positions[None, :] <= query_positions[:, None]) | |
| & (key_positions[None, :] >= query_positions[:, None] - (window - 1)) | |
| )[None, None, :, :] | |
| if attention_mask is None: | |
| return allowed | |
| if attention_mask.dtype == torch.bool: | |
| return attention_mask & allowed | |
| return attention_mask.masked_fill(~allowed, torch.finfo(dtype).min) | |
| def forward( | |
| self, | |
| hidden_states: torch.Tensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| position_ids: Optional[torch.Tensor] = None, | |
| past_key_value: Optional[Cache] = None, | |
| output_attentions: bool = False, | |
| use_cache: bool = False, | |
| cache_position: Optional[torch.LongTensor] = None, | |
| value_residual: Optional[torch.Tensor] = None, | |
| **kwargs, | |
| ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: | |
| bsz, q_len, _ = hidden_states.shape | |
| # Project Q, K, V | |
| q = self.q_proj(hidden_states) | |
| k = self.k_proj(hidden_states) | |
| value_states = hidden_states | |
| if value_residual is not None: | |
| if not self.config.use_valres: | |
| raise RuntimeError("value_residual was supplied but config.use_valres is false") | |
| blend = torch.sigmoid(self.value_res_lambda[0]).to(dtype=hidden_states.dtype) | |
| value_states = torch.lerp( | |
| value_residual.to(dtype=hidden_states.dtype), hidden_states, blend | |
| ) | |
| v = self.v_proj(value_states) | |
| # Reshape to (B, T, n_heads, head_dim) then transpose to (B, n_heads, T, head_dim) | |
| q = q.view(bsz, q_len, self.num_heads, self.head_dim) | |
| k = k.view(bsz, q_len, self.num_kv_heads, self.head_dim) | |
| v = v.view(bsz, q_len, self.num_kv_heads, self.head_dim) | |
| # Per-head Q/K norm (applied before transpose, on head_dim) | |
| q = self.q_norm(q) | |
| k = self.k_norm(k) | |
| # Transpose to (B, n_heads, T, head_dim) for attention | |
| q = q.transpose(1, 2) | |
| k = k.transpose(1, 2) | |
| v = v.transpose(1, 2) | |
| # RoPE only for local layers | |
| if self.layer_type == "sliding_attention": | |
| cos, sin = self.rotary_emb(v, position_ids) | |
| q, k = apply_rotary_pos_emb(q, k, cos, sin) | |
| # KV cache | |
| if past_key_value is not None: | |
| k, v = past_key_value.update(k, v, self.layer_idx) | |
| # GQA: expand KV heads | |
| k = repeat_kv(k, self.num_kv_groups) | |
| v = repeat_kv(v, self.num_kv_groups) | |
| attention_mask = self._apply_sliding_window_mask( | |
| attention_mask=attention_mask, | |
| q_len=q.shape[2], | |
| kv_len=k.shape[2], | |
| dtype=q.dtype, | |
| device=q.device, | |
| cache_position=cache_position, | |
| ) | |
| # Attention (use is_causal when no explicit mask for correct causal behavior) | |
| # Cast to plain bool to avoid SymBool issues with torch.compile | |
| is_causal = bool(attention_mask is None and q.shape[2] > 1) | |
| attn_output = F.scaled_dot_product_attention( | |
| q, k, v, attn_mask=attention_mask, dropout_p=0.0, is_causal=is_causal | |
| ) | |
| # (B, n_heads, T, head_dim) -> (B, T, n_heads * head_dim) | |
| attn_output = attn_output.transpose(1, 2).contiguous() | |
| # Gated attention: output * sigmoid(g_proj(input)) | |
| if self.config.use_gated_attention: | |
| gate = self.g_proj(hidden_states) | |
| if self.config.gate_full_precision: | |
| gate = gate.float() | |
| gate = torch.sigmoid(gate).to(attn_output.dtype) | |
| attn_output = attn_output.view(bsz, q_len, -1) * gate | |
| else: | |
| attn_output = attn_output.view(bsz, q_len, -1) | |
| # Output projection | |
| attn_output = self.o_proj(attn_output) | |
| return attn_output, None, past_key_value | |
| class RicursiveMLP(nn.Module): | |
| def __init__(self, config: RicursiveConfig): | |
| super().__init__() | |
| self.clamp_threshold = config.ffn_clamp_threshold | |
| self.gate_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False) | |
| self.up_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False) | |
| self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| gate = self.gate_proj(x) | |
| up = self.up_proj(x) | |
| if self.clamp_threshold: | |
| gate = gate.clamp(min=None, max=self.clamp_threshold) | |
| up = up.clamp(min=-self.clamp_threshold, max=self.clamp_threshold) | |
| return self.down_proj(F.silu(gate) * up) | |
| class RicursiveDecoderLayer(nn.Module): | |
| """PeriNorm decoder layer with 4 layer norms.""" | |
| def __init__(self, config: RicursiveConfig, layer_idx: int): | |
| super().__init__() | |
| self.self_attn = RicursiveAttention(config, layer_idx) | |
| self.mlp = RicursiveMLP(config) | |
| # PeriNorm: 4 norms | |
| self.input_layernorm = RicursiveRMSNorm(config.hidden_size, eps=config.rms_norm_eps) | |
| self.post_attention_layernorm = RicursiveRMSNorm( | |
| config.hidden_size, eps=config.rms_norm_eps | |
| ) | |
| self.pre_feedforward_layernorm = RicursiveRMSNorm( | |
| config.hidden_size, eps=config.rms_norm_eps | |
| ) | |
| self.post_feedforward_layernorm = RicursiveRMSNorm( | |
| config.hidden_size, eps=config.rms_norm_eps | |
| ) | |
| def forward( | |
| self, | |
| hidden_states: torch.Tensor, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| position_ids: Optional[torch.Tensor] = None, | |
| past_key_value: Optional[Cache] = None, | |
| output_attentions: bool = False, | |
| use_cache: bool = False, | |
| cache_position: Optional[torch.LongTensor] = None, | |
| value_residual: Optional[torch.Tensor] = None, | |
| **kwargs, | |
| ) -> Tuple[torch.Tensor, ...]: | |
| # Attention sub-block (PeriNorm: pre-norm -> attn -> post-norm -> residual) | |
| normed = self.input_layernorm(hidden_states) | |
| attn_output, attn_weights, present_key_value = self.self_attn( | |
| normed, | |
| attention_mask=attention_mask, | |
| position_ids=position_ids, | |
| past_key_value=past_key_value, | |
| output_attentions=output_attentions, | |
| use_cache=use_cache, | |
| cache_position=cache_position, | |
| value_residual=value_residual, | |
| **kwargs, | |
| ) | |
| hidden_states = hidden_states + self.post_attention_layernorm(attn_output) | |
| # FFN sub-block (PeriNorm: pre-norm -> ffn -> post-norm -> residual) | |
| normed = self.pre_feedforward_layernorm(hidden_states) | |
| ff_output = self.mlp(normed) | |
| hidden_states = hidden_states + self.post_feedforward_layernorm(ff_output) | |
| outputs = (hidden_states,) | |
| if output_attentions: | |
| outputs += (attn_weights,) | |
| if use_cache: | |
| outputs += (present_key_value,) | |
| return outputs | |
| class RicursivePreTrainedModel(PreTrainedModel): | |
| config_class = RicursiveConfig | |
| base_model_prefix = "model" | |
| supports_gradient_checkpointing = True | |
| _no_split_modules = ["RicursiveDecoderLayer"] | |
| _supports_sdpa = True | |
| _supports_cache_class = True | |
| class RicursiveModel(RicursivePreTrainedModel): | |
| def __init__(self, config: RicursiveConfig): | |
| super().__init__(config) | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) | |
| self.layers = nn.ModuleList( | |
| [RicursiveDecoderLayer(config, i) for i in range(config.num_hidden_layers)] | |
| ) | |
| self.norm = RicursiveRMSNorm(config.hidden_size, eps=config.rms_norm_eps) | |
| self.post_init() | |
| def forward( | |
| self, | |
| input_ids: Optional[torch.LongTensor] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| position_ids: Optional[torch.LongTensor] = None, | |
| past_key_values: Optional[Cache] = None, | |
| inputs_embeds: Optional[torch.FloatTensor] = None, | |
| use_cache: Optional[bool] = None, | |
| output_attentions: Optional[bool] = None, | |
| output_hidden_states: Optional[bool] = None, | |
| return_dict: Optional[bool] = None, | |
| cache_position: Optional[torch.LongTensor] = None, | |
| **kwargs, | |
| ) -> Union[Tuple, BaseModelOutputWithPast]: | |
| output_attentions = ( | |
| output_attentions if output_attentions is not None else self.config.output_attentions | |
| ) | |
| output_hidden_states = ( | |
| output_hidden_states | |
| if output_hidden_states is not None | |
| else self.config.output_hidden_states | |
| ) | |
| use_cache = use_cache if use_cache is not None else self.config.use_cache | |
| return_dict = return_dict if return_dict is not None else self.config.use_return_dict | |
| if inputs_embeds is None: | |
| inputs_embeds = self.embed_tokens(input_ids) | |
| # embed_scale: non-folded checkpoints carry the scale (e.g. sqrt(hidden_size)) in the | |
| # config and apply it here at runtime; folded checkpoints set embed_scale=None and bake | |
| # the scale into embed_tokens.weight, so this is a no-op. This mirrors the SGLang native | |
| # port (sglang_ricursive) so served and trained forwards agree. It must run BEFORE the | |
| # value-residual seed below, whose source is the (scaled) layer-0 embeddings. | |
| embed_scale = getattr(self.config, "embed_scale", None) | |
| if embed_scale is not None: | |
| inputs_embeds = inputs_embeds * embed_scale | |
| if use_cache and past_key_values is None: | |
| past_key_values = DynamicCache() | |
| if cache_position is None: | |
| past_seen_tokens = ( | |
| past_key_values.get_seq_length() if past_key_values is not None else 0 | |
| ) | |
| cache_position = torch.arange( | |
| past_seen_tokens, | |
| past_seen_tokens + inputs_embeds.shape[1], | |
| device=inputs_embeds.device, | |
| ) | |
| if position_ids is None: | |
| position_ids = cache_position.unsqueeze(0) | |
| # Build causal mask | |
| causal_mask = self._update_causal_mask( | |
| attention_mask, inputs_embeds, cache_position, past_key_values | |
| ) | |
| hidden_states = inputs_embeds | |
| value_residual = hidden_states if self.config.use_valres else None | |
| all_hidden_states = () if output_hidden_states else None | |
| all_self_attns = () if output_attentions else None | |
| for layer in self.layers: | |
| if output_hidden_states: | |
| all_hidden_states += (hidden_states,) | |
| layer_outputs = layer( | |
| hidden_states, | |
| attention_mask=causal_mask, | |
| position_ids=position_ids, | |
| past_key_value=past_key_values, | |
| output_attentions=output_attentions, | |
| use_cache=use_cache, | |
| cache_position=cache_position, | |
| value_residual=value_residual, | |
| **kwargs, | |
| ) | |
| hidden_states = layer_outputs[0] | |
| if output_attentions: | |
| all_self_attns += (layer_outputs[1],) | |
| hidden_states = self.norm(hidden_states) | |
| if output_hidden_states: | |
| all_hidden_states += (hidden_states,) | |
| next_cache = past_key_values if use_cache else None | |
| if not return_dict: | |
| return tuple( | |
| v | |
| for v in [hidden_states, next_cache, all_hidden_states, all_self_attns] | |
| if v is not None | |
| ) | |
| return BaseModelOutputWithPast( | |
| last_hidden_state=hidden_states, | |
| past_key_values=next_cache, | |
| hidden_states=all_hidden_states, | |
| attentions=all_self_attns, | |
| ) | |
| def _update_causal_mask( | |
| self, | |
| attention_mask: Optional[torch.Tensor], | |
| input_tensor: torch.Tensor, | |
| cache_position: torch.Tensor, | |
| past_key_values: Optional[Cache], | |
| ) -> Optional[torch.Tensor]: | |
| # When there's no explicit attention_mask (no padding) and we're doing | |
| # standard prefill or cached decoding, return None and let SDPA use | |
| # its efficient is_causal path. This also avoids numerical issues | |
| # from float min mask values with large activations. | |
| if attention_mask is None: | |
| return None | |
| if torch.all(attention_mask == 1): | |
| return None | |
| # Only build an explicit mask when we have a padding mask | |
| dtype, device = input_tensor.dtype, input_tensor.device | |
| sequence_length = input_tensor.shape[1] | |
| if cache_position is None: | |
| query_positions = torch.arange(sequence_length, device=device) | |
| else: | |
| query_positions = cache_position.to(device=device).reshape(-1) | |
| # Size the key axis from `cache_position`, which is exactly what the | |
| # attention layer uses to size its key tensor. | |
| # `past_key_values.get_max_cache_shape()` was removed in transformers 5, | |
| # and its replacement reports the *capacity* (e.g. `sliding_window`), | |
| # which disagrees with the live key count whenever the cache is not full. | |
| target_length = int(query_positions[-1].item()) + 1 | |
| target_length = max(target_length, attention_mask.shape[-1]) | |
| # A shorter padding mask leaves the trailing keys unspecified; treat them as | |
| # visible -- the causal term below still applies. | |
| if attention_mask.shape[-1] < target_length: | |
| pad = attention_mask.new_ones( | |
| attention_mask.shape[0], target_length - attention_mask.shape[-1] | |
| ) | |
| attention_mask = torch.cat([pad, attention_mask], dim=-1) | |
| key_positions = torch.arange(target_length, device=device) | |
| allowed = key_positions[None, :] <= query_positions[:, None] | |
| allowed = allowed[None, None, :, :] & attention_mask[:, None, None, :].to(torch.bool) | |
| causal_mask = torch.zeros( | |
| (attention_mask.shape[0], 1, sequence_length, target_length), | |
| dtype=dtype, | |
| device=device, | |
| ) | |
| # `0.0` for visible keys (not `1.0`, which would bias every score) and the | |
| # dtype minimum elsewhere. Building the mask from `allowed` also keeps a | |
| # single-token decode step from coming out fully masked. | |
| return causal_mask.masked_fill(~allowed, torch.finfo(dtype).min) | |
| def _cache_seq_length(past_key_values: Optional[Cache]) -> int: | |
| """Number of cached keys, across transformers 4.x and 5.x cache objects.""" | |
| if past_key_values is None: | |
| return 0 | |
| getter = getattr(past_key_values, "get_seq_length", None) | |
| if callable(getter): | |
| return int(getter()) | |
| try: | |
| return int(past_key_values[0][0].shape[-2]) | |
| except Exception: # pragma: no cover - unknown cache implementations | |
| return 0 | |
| class RicursiveForCausalLM(RicursivePreTrainedModel, GenerationMixin): | |
| _tied_weights_keys = [] | |
| # transformers 5 no longer injects `GenerationMixin` into remote-code classes, | |
| # so inherit it explicitly above. Without it `generate` is missing entirely and | |
| # `PreTrainedModel.__init__` never builds `generation_config`. | |
| # | |
| # This attribute marks the native transformers-5 compatibility fixes so that | |
| # downstream loaders can skip their runtime monkeypatch. | |
| _ricursive_tf5_native_fix = RICURSIVE_TF5_COMPAT | |
| def __init__(self, config: RicursiveConfig): | |
| super().__init__(config) | |
| self.model = RicursiveModel(config) | |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) | |
| self.post_init() | |
| def get_input_embeddings(self): | |
| return self.model.embed_tokens | |
| def set_input_embeddings(self, value): | |
| self.model.embed_tokens = value | |
| def get_output_embeddings(self): | |
| return self.lm_head | |
| def set_output_embeddings(self, new_embeddings): | |
| self.lm_head = new_embeddings | |
| def forward( | |
| self, | |
| input_ids: Optional[torch.LongTensor] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| position_ids: Optional[torch.LongTensor] = None, | |
| past_key_values: Optional[Cache] = None, | |
| inputs_embeds: Optional[torch.FloatTensor] = None, | |
| labels: Optional[torch.LongTensor] = None, | |
| use_cache: Optional[bool] = None, | |
| output_attentions: Optional[bool] = None, | |
| output_hidden_states: Optional[bool] = None, | |
| return_dict: Optional[bool] = None, | |
| cache_position: Optional[torch.LongTensor] = None, | |
| **kwargs, | |
| ) -> Union[Tuple, CausalLMOutputWithPast]: | |
| return_dict = return_dict if return_dict is not None else self.config.use_return_dict | |
| outputs = self.model( | |
| input_ids=input_ids, | |
| attention_mask=attention_mask, | |
| position_ids=position_ids, | |
| past_key_values=past_key_values, | |
| inputs_embeds=inputs_embeds, | |
| use_cache=use_cache, | |
| output_attentions=output_attentions, | |
| output_hidden_states=output_hidden_states, | |
| return_dict=return_dict, | |
| cache_position=cache_position, | |
| **kwargs, | |
| ) | |
| hidden_states = outputs[0] | |
| logits = self.lm_head(hidden_states) | |
| loss = None | |
| if labels is not None: | |
| shift_logits = logits[..., :-1, :].contiguous() | |
| shift_labels = labels[..., 1:].contiguous() | |
| loss = F.cross_entropy( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), | |
| ) | |
| if not return_dict: | |
| output = (logits,) + outputs[1:] | |
| return (loss,) + output if loss is not None else output | |
| return CausalLMOutputWithPast( | |
| loss=loss, | |
| logits=logits, | |
| past_key_values=outputs.past_key_values, | |
| hidden_states=outputs.hidden_states, | |
| attentions=outputs.attentions, | |
| ) | |
| def prepare_inputs_for_generation( | |
| self, | |
| input_ids: torch.LongTensor, | |
| past_key_values: Optional[Cache] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| inputs_embeds: Optional[torch.FloatTensor] = None, | |
| cache_position: Optional[torch.LongTensor] = None, | |
| use_cache: Optional[bool] = None, | |
| position_ids: Optional[torch.LongTensor] = None, | |
| **kwargs, | |
| ): | |
| # transformers 5.x does not move remote-code inputs onto the model device for | |
| # this model, so a plain CPU `input_ids` reaches the embedding lookup and | |
| # raises. Its own generation helper performs this move, so mirror it here. | |
| device = getattr(self, "device", None) | |
| if device is not None and getattr(device, "type", None) != "meta": | |
| if input_ids is not None and input_ids.device != device: | |
| input_ids = input_ids.to(device) | |
| if attention_mask is not None and attention_mask.device != device: | |
| attention_mask = attention_mask.to(device) | |
| if inputs_embeds is not None and inputs_embeds.device != device: | |
| inputs_embeds = inputs_embeds.to(device) | |
| # transformers derives `position_ids` from the *caller's* tensors before | |
| # this hook runs, so a CPU prompt yields CPU positions even after the | |
| # move above. RoPE then multiplies them against CUDA frequencies and fails. | |
| if position_ids is not None and position_ids.device != device: | |
| position_ids = position_ids.to(device) | |
| past_length = _cache_seq_length(past_key_values) | |
| if cache_position is None: | |
| # With a non-empty cache and the full sequence supplied, only the trailing | |
| # (uncached) tokens are new. Otherwise every token in `input_ids` is new. | |
| if past_length > 0 and input_ids.shape[1] > 1: | |
| new_len = input_ids.shape[1] - past_length | |
| cache_position = torch.arange( | |
| past_length, past_length + new_len, device=input_ids.device | |
| ) | |
| else: | |
| cache_position = torch.arange( | |
| past_length, past_length + input_ids.shape[1], device=input_ids.device | |
| ) | |
| # Only slice when the cache is warm and `input_ids` still carries cached | |
| # tokens. Slicing unconditionally turned a `None` cache position into | |
| # `input_ids[:, None]` and injected a bogus axis. | |
| sliced = past_length > 0 and input_ids.shape[1] > cache_position.numel() | |
| if sliced: | |
| input_ids = input_ids[:, cache_position] | |
| if inputs_embeds is not None and cache_position.numel() and int(cache_position[0]) == 0: | |
| model_inputs = {"inputs_embeds": inputs_embeds} | |
| else: | |
| model_inputs = {"input_ids": input_ids} | |
| # Derive per-row positions from the padding mask. Using `cache_position` for | |
| # every row is only correct when nothing is padded: with left padding the real | |
| # tokens of a short row sit at higher indices, so a shared position vector | |
| # shifts its RoPE phases and the row decodes to garbage. | |
| if position_ids is not None: | |
| if sliced: | |
| position_ids = position_ids[:, cache_position] | |
| elif attention_mask is not None and attention_mask.dim() == 2: | |
| position_ids = attention_mask.long().cumsum(-1) - 1 | |
| position_ids = position_ids.masked_fill(attention_mask == 0, 0) | |
| position_ids = position_ids[:, -input_ids.shape[1]:] | |
| else: | |
| position_ids = cache_position.unsqueeze(0) | |
| model_inputs.update( | |
| { | |
| "position_ids": position_ids, | |
| "past_key_values": past_key_values, | |
| "use_cache": use_cache, | |
| "attention_mask": attention_mask, | |
| "cache_position": cache_position, | |
| } | |
| ) | |
| return model_inputs | |