Text Generation
Transformers
Safetensors
Korean
English
aether_v2_7way
foundation-model
sovereign-ai
fully-open
open-source
mixture-of-experts
Mixture of Experts
heterogeneous-attention
latin-square
from-scratch
reproducible
pretrained
korean
vidraft
aether
conversational
custom_code
Instructions to use FINAL-Bench/Aether-7B-5Attn with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use FINAL-Bench/Aether-7B-5Attn with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="FINAL-Bench/Aether-7B-5Attn", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("FINAL-Bench/Aether-7B-5Attn", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use FINAL-Bench/Aether-7B-5Attn with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "FINAL-Bench/Aether-7B-5Attn" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FINAL-Bench/Aether-7B-5Attn", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/FINAL-Bench/Aether-7B-5Attn
- SGLang
How to use FINAL-Bench/Aether-7B-5Attn with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "FINAL-Bench/Aether-7B-5Attn" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FINAL-Bench/Aether-7B-5Attn", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "FINAL-Bench/Aether-7B-5Attn" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FINAL-Bench/Aether-7B-5Attn", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use FINAL-Bench/Aether-7B-5Attn with Docker Model Runner:
docker model run hf.co/FINAL-Bench/Aether-7B-5Attn
fix: make model loadable via AutoModelForCausalLM (add auto_map, flatten module files to repo root, inherit GenerationMixin)
Browse files
nsa.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NSA: Native Sparse Attention (PRODUCTION - causal-safe)
|
| 3 |
+
========================================================
|
| 4 |
+
|
| 5 |
+
DeepSeek 2502.11089 (ACL 2025 Best Paper)
|
| 6 |
+
Hardware-Aligned and Natively Trainable Sparse Attention.
|
| 7 |
+
|
| 8 |
+
3 branches with learnable gating:
|
| 9 |
+
1. Compressed: block-mean K/V + causal block mask (FIX 5/7)
|
| 10 |
+
2. Selected: top-k blocks (fallback to full causal)
|
| 11 |
+
3. Sliding: local window with causal mask
|
| 12 |
+
|
| 13 |
+
Implementation notes:
|
| 14 |
+
- NSACompressBranch: added causal block mask (q at pos t can only see blocks fully <= t-1)
|
| 15 |
+
- Class renamed NSAttention -> NSAAttention (modeling compat)
|
| 16 |
+
- forward signature: layer_idx kwarg + (Tensor, past_kv) tuple return
|
| 17 |
+
|
| 18 |
+
NSAAttention is the public name imported by modeling_aether_v2_7way.py.
|
| 19 |
+
"""
|
| 20 |
+
from __future__ import annotations
|
| 21 |
+
import math
|
| 22 |
+
from typing import Optional, Tuple
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
import torch.nn as nn
|
| 26 |
+
import torch.nn.functional as F
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class NSACompressBranch(nn.Module):
|
| 30 |
+
"""Block-compressed long-range attention WITH causal block mask."""
|
| 31 |
+
|
| 32 |
+
def __init__(self, config):
|
| 33 |
+
super().__init__()
|
| 34 |
+
self.h = config.hidden_size
|
| 35 |
+
self.num_heads = config.num_attention_heads
|
| 36 |
+
self.num_kv_heads = config.num_key_value_heads
|
| 37 |
+
self.head_dim = config.head_dim
|
| 38 |
+
# accept either nsa_compressed_block_size or nsa_compress_block_size
|
| 39 |
+
self.block_size = getattr(config, "nsa_compressed_block_size",
|
| 40 |
+
getattr(config, "nsa_compress_block_size", 32))
|
| 41 |
+
|
| 42 |
+
self.q_proj = nn.Linear(self.h, self.num_heads * self.head_dim, bias=False)
|
| 43 |
+
self.k_proj = nn.Linear(self.h, self.num_kv_heads * self.head_dim, bias=False)
|
| 44 |
+
self.v_proj = nn.Linear(self.h, self.num_kv_heads * self.head_dim, bias=False)
|
| 45 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.h, bias=False)
|
| 46 |
+
|
| 47 |
+
self.block_compress_k = nn.Linear(self.head_dim, self.head_dim, bias=False)
|
| 48 |
+
self.block_compress_v = nn.Linear(self.head_dim, self.head_dim, bias=False)
|
| 49 |
+
|
| 50 |
+
def compress_blocks(self, x: torch.Tensor) -> torch.Tensor:
|
| 51 |
+
"""x: [B, S, H, D] -> [B, num_blocks, H, D] via block-mean (truncate non-aligned)."""
|
| 52 |
+
B, S, H, D = x.shape
|
| 53 |
+
num_blocks = S // self.block_size
|
| 54 |
+
if num_blocks == 0:
|
| 55 |
+
return x[:, :0] # empty
|
| 56 |
+
x = x[:, : num_blocks * self.block_size]
|
| 57 |
+
x = x.view(B, num_blocks, self.block_size, H, D)
|
| 58 |
+
return x.mean(dim=2)
|
| 59 |
+
|
| 60 |
+
def forward(self, hidden_states: torch.Tensor, past_key_value=None, cache_idx: int = 0) -> torch.Tensor:
|
| 61 |
+
B, S, _ = hidden_states.shape
|
| 62 |
+
|
| 63 |
+
q = self.q_proj(hidden_states).view(B, S, self.num_heads, self.head_dim)
|
| 64 |
+
k = self.k_proj(hidden_states).view(B, S, self.num_kv_heads, self.head_dim)
|
| 65 |
+
v = self.v_proj(hidden_states).view(B, S, self.num_kv_heads, self.head_dim)
|
| 66 |
+
|
| 67 |
+
# AetherCache: cache raw K,V (projection becomes O(1)); block-compress is a cheap reduce
|
| 68 |
+
if past_key_value is not None:
|
| 69 |
+
kc, vc = past_key_value.update(k.transpose(1, 2), v.transpose(1, 2), cache_idx)
|
| 70 |
+
k = kc.transpose(1, 2)
|
| 71 |
+
v = vc.transpose(1, 2)
|
| 72 |
+
S_kv = k.shape[1]
|
| 73 |
+
|
| 74 |
+
# Compress K, V into blocks
|
| 75 |
+
k_compressed = self.compress_blocks(k) # [B, num_blocks, kv_h, D]
|
| 76 |
+
v_compressed = self.compress_blocks(v)
|
| 77 |
+
num_blocks = k_compressed.shape[1]
|
| 78 |
+
|
| 79 |
+
if num_blocks == 0:
|
| 80 |
+
# Sequence shorter than block_size — fallback to identity (no compress contribution)
|
| 81 |
+
out = torch.zeros(B, S, self.h, device=hidden_states.device, dtype=hidden_states.dtype)
|
| 82 |
+
return out
|
| 83 |
+
|
| 84 |
+
k_compressed = self.block_compress_k(k_compressed)
|
| 85 |
+
v_compressed = self.block_compress_v(v_compressed)
|
| 86 |
+
|
| 87 |
+
# GQA repeat
|
| 88 |
+
repeat = self.num_heads // self.num_kv_heads
|
| 89 |
+
k_compressed = k_compressed.repeat_interleave(repeat, dim=2)
|
| 90 |
+
v_compressed = v_compressed.repeat_interleave(repeat, dim=2)
|
| 91 |
+
|
| 92 |
+
# [B, H, S, D]
|
| 93 |
+
q = q.transpose(1, 2)
|
| 94 |
+
k_c = k_compressed.transpose(1, 2) # [B, H, num_blocks, D]
|
| 95 |
+
v_c = v_compressed.transpose(1, 2)
|
| 96 |
+
|
| 97 |
+
scores = torch.matmul(q, k_c.transpose(-2, -1)) / math.sqrt(self.head_dim)
|
| 98 |
+
# ★ FIX 5/7: causal block mask
|
| 99 |
+
# query at position t can attend to block i iff (i+1)*bs <= t (block fully completed by t-1)
|
| 100 |
+
# equivalently: block_end_inclusive = (i+1)*bs - 1 < t → i*bs + bs - 1 < t → i < (t - bs + 1) / bs
|
| 101 |
+
# simpler: mask True where block start > t (block has not started)... but we need stricter:
|
| 102 |
+
# block i is "in the past" iff its END (i+1)*bs - 1 < t, i.e. (i+1)*bs <= t.
|
| 103 |
+
q_pos = torch.arange(S_kv - S, S_kv, device=q.device).unsqueeze(1) # AetherCache: absolute [S, 1]
|
| 104 |
+
block_end = (torch.arange(num_blocks, device=q.device) + 1) * self.block_size # [num_blocks]
|
| 105 |
+
block_end = block_end.unsqueeze(0) # [1, num_blocks]
|
| 106 |
+
# mask: True = blocked (future block)
|
| 107 |
+
mask = block_end > q_pos # [S, num_blocks], True if block_end > q_pos (block not fully past)
|
| 108 |
+
mask = mask.unsqueeze(0).unsqueeze(0) # [1, 1, S, num_blocks]
|
| 109 |
+
scores = scores.masked_fill(mask, float("-inf"))
|
| 110 |
+
|
| 111 |
+
# If a query has no past block, all -inf -> nan softmax. Set first block prob to 0 by giving it 0 logit.
|
| 112 |
+
# Cheap guard: rows that are all -inf get a 0-tensor output.
|
| 113 |
+
all_neg_inf = mask.all(dim=-1, keepdim=True) # [1,1,S,1]
|
| 114 |
+
# set those rows to a small finite value so softmax produces uniform; we'll zero output below
|
| 115 |
+
scores = torch.where(all_neg_inf.expand_as(scores), torch.zeros_like(scores), scores)
|
| 116 |
+
|
| 117 |
+
attn = F.softmax(scores, dim=-1)
|
| 118 |
+
out = torch.matmul(attn, v_c) # [B, H, S, D]
|
| 119 |
+
# Zero out positions that have no past block (would be invalid)
|
| 120 |
+
out = out.masked_fill(all_neg_inf, 0.0)
|
| 121 |
+
out = out.transpose(1, 2).reshape(B, S, -1)
|
| 122 |
+
return self.o_proj(out)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
class NSASelectBranch(nn.Module):
|
| 126 |
+
"""Top-k block selection. Currently falls back to full causal attention."""
|
| 127 |
+
|
| 128 |
+
def __init__(self, config):
|
| 129 |
+
super().__init__()
|
| 130 |
+
self.h = config.hidden_size
|
| 131 |
+
self.num_heads = config.num_attention_heads
|
| 132 |
+
self.num_kv_heads = config.num_key_value_heads
|
| 133 |
+
self.head_dim = config.head_dim
|
| 134 |
+
self.block_size = getattr(config, "nsa_compressed_block_size",
|
| 135 |
+
getattr(config, "nsa_compress_block_size", 32))
|
| 136 |
+
self.top_k = getattr(config, "nsa_selected_top_k",
|
| 137 |
+
getattr(config, "nsa_select_top_k", 16))
|
| 138 |
+
|
| 139 |
+
self.q_proj = nn.Linear(self.h, self.num_heads * self.head_dim, bias=False)
|
| 140 |
+
self.k_proj = nn.Linear(self.h, self.num_kv_heads * self.head_dim, bias=False)
|
| 141 |
+
self.v_proj = nn.Linear(self.h, self.num_kv_heads * self.head_dim, bias=False)
|
| 142 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.h, bias=False)
|
| 143 |
+
|
| 144 |
+
self.block_score = nn.Linear(self.head_dim, 1, bias=False)
|
| 145 |
+
|
| 146 |
+
def forward(self, hidden_states: torch.Tensor, past_key_value=None, cache_idx: int = 0) -> torch.Tensor:
|
| 147 |
+
B, S, _ = hidden_states.shape
|
| 148 |
+
|
| 149 |
+
q = self.q_proj(hidden_states).view(B, S, self.num_heads, self.head_dim)
|
| 150 |
+
k = self.k_proj(hidden_states).view(B, S, self.num_kv_heads, self.head_dim)
|
| 151 |
+
v = self.v_proj(hidden_states).view(B, S, self.num_kv_heads, self.head_dim)
|
| 152 |
+
|
| 153 |
+
# AetherCache: standard KV cache
|
| 154 |
+
if past_key_value is not None:
|
| 155 |
+
kc, vc = past_key_value.update(k.transpose(1, 2), v.transpose(1, 2), cache_idx)
|
| 156 |
+
k = kc.transpose(1, 2)
|
| 157 |
+
v = vc.transpose(1, 2)
|
| 158 |
+
|
| 159 |
+
# GQA repeat
|
| 160 |
+
repeat = self.num_heads // self.num_kv_heads
|
| 161 |
+
k_full = k.repeat_interleave(repeat, dim=2)
|
| 162 |
+
v_full = v.repeat_interleave(repeat, dim=2)
|
| 163 |
+
|
| 164 |
+
# Use SDPA causal (FIX 5/7: strict causal) — causal only when prefill
|
| 165 |
+
q = q.transpose(1, 2)
|
| 166 |
+
k_full = k_full.transpose(1, 2)
|
| 167 |
+
v_full = v_full.transpose(1, 2)
|
| 168 |
+
out = F.scaled_dot_product_attention(q, k_full, v_full, dropout_p=0.0, is_causal=(S > 1))
|
| 169 |
+
out = out.transpose(1, 2).reshape(B, S, -1)
|
| 170 |
+
return self.o_proj(out)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class NSASlidingBranch(nn.Module):
|
| 174 |
+
"""Local sliding window with causal mask."""
|
| 175 |
+
|
| 176 |
+
def __init__(self, config):
|
| 177 |
+
super().__init__()
|
| 178 |
+
self.h = config.hidden_size
|
| 179 |
+
self.num_heads = config.num_attention_heads
|
| 180 |
+
self.num_kv_heads = config.num_key_value_heads
|
| 181 |
+
self.head_dim = config.head_dim
|
| 182 |
+
self.window_size = getattr(config, "nsa_sliding_size",
|
| 183 |
+
getattr(config, "nsa_sliding_window", 512))
|
| 184 |
+
|
| 185 |
+
self.q_proj = nn.Linear(self.h, self.num_heads * self.head_dim, bias=False)
|
| 186 |
+
self.k_proj = nn.Linear(self.h, self.num_kv_heads * self.head_dim, bias=False)
|
| 187 |
+
self.v_proj = nn.Linear(self.h, self.num_kv_heads * self.head_dim, bias=False)
|
| 188 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.h, bias=False)
|
| 189 |
+
|
| 190 |
+
def forward(self, hidden_states: torch.Tensor, past_key_value=None, cache_idx: int = 0) -> torch.Tensor:
|
| 191 |
+
B, S, _ = hidden_states.shape
|
| 192 |
+
|
| 193 |
+
q = self.q_proj(hidden_states).view(B, S, self.num_heads, self.head_dim)
|
| 194 |
+
k = self.k_proj(hidden_states).view(B, S, self.num_kv_heads, self.head_dim)
|
| 195 |
+
v = self.v_proj(hidden_states).view(B, S, self.num_kv_heads, self.head_dim)
|
| 196 |
+
|
| 197 |
+
# AetherCache: standard KV cache
|
| 198 |
+
if past_key_value is not None:
|
| 199 |
+
kc, vc = past_key_value.update(k.transpose(1, 2), v.transpose(1, 2), cache_idx)
|
| 200 |
+
k = kc.transpose(1, 2)
|
| 201 |
+
v = vc.transpose(1, 2)
|
| 202 |
+
S_kv = k.shape[1]
|
| 203 |
+
|
| 204 |
+
repeat = self.num_heads // self.num_kv_heads
|
| 205 |
+
k = k.repeat_interleave(repeat, dim=2)
|
| 206 |
+
v = v.repeat_interleave(repeat, dim=2)
|
| 207 |
+
|
| 208 |
+
q = q.transpose(1, 2)
|
| 209 |
+
k = k.transpose(1, 2)
|
| 210 |
+
v = v.transpose(1, 2)
|
| 211 |
+
|
| 212 |
+
# Sliding window + causal (AetherCache: absolute positions)
|
| 213 |
+
q_abs = torch.arange(S_kv - S, S_kv, device=q.device)
|
| 214 |
+
k_abs = torch.arange(S_kv, device=q.device)
|
| 215 |
+
mask = (k_abs[None, :] > q_abs[:, None]) | (k_abs[None, :] < q_abs[:, None] - self.window_size + 1)
|
| 216 |
+
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
|
| 217 |
+
scores = scores.masked_fill(mask, float("-inf"))
|
| 218 |
+
attn = F.softmax(scores, dim=-1)
|
| 219 |
+
out = torch.matmul(attn, v)
|
| 220 |
+
out = out.transpose(1, 2).reshape(B, S, -1)
|
| 221 |
+
return self.o_proj(out)
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
class NSAAttention(nn.Module):
|
| 225 |
+
"""
|
| 226 |
+
Native Sparse Attention (DeepSeek 2502.11089) — modeling-compat wrapper.
|
| 227 |
+
|
| 228 |
+
Public name: NSAAttention (matches modeling_aether_v2_7way.py import).
|
| 229 |
+
Accepts layer_idx kwarg (stored, currently unused).
|
| 230 |
+
Returns (Tensor, past_key_value=None) tuple to match other attention modules.
|
| 231 |
+
"""
|
| 232 |
+
|
| 233 |
+
def __init__(self, config, layer_idx: int = 0):
|
| 234 |
+
super().__init__()
|
| 235 |
+
self.layer_idx = layer_idx
|
| 236 |
+
self.compressed = NSACompressBranch(config)
|
| 237 |
+
self.selected = NSASelectBranch(config)
|
| 238 |
+
self.sliding = NSASlidingBranch(config)
|
| 239 |
+
|
| 240 |
+
# Learnable gate (sigmoid normalized)
|
| 241 |
+
self.gate_c = nn.Parameter(torch.zeros(1))
|
| 242 |
+
self.gate_s = nn.Parameter(torch.zeros(1))
|
| 243 |
+
self.gate_w = nn.Parameter(torch.zeros(1))
|
| 244 |
+
|
| 245 |
+
self.norm = nn.LayerNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 246 |
+
|
| 247 |
+
def forward(
|
| 248 |
+
self,
|
| 249 |
+
hidden_states: torch.Tensor,
|
| 250 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 251 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 252 |
+
past_key_value=None,
|
| 253 |
+
use_cache: bool = False,
|
| 254 |
+
**kwargs,
|
| 255 |
+
) -> Tuple[torch.Tensor, Optional[object]]:
|
| 256 |
+
# AetherCache: branches are stateless fns of the full sequence -> cache the layer input
|
| 257 |
+
# and recompute over (history + new), returning only the new positions. Prefill unchanged.
|
| 258 |
+
cidx = int(getattr(self, "cache_idx", self.layer_idx))
|
| 259 |
+
# compress uses the layer's own slot (keeps get_seq_length() valid); others get high slots
|
| 260 |
+
out_c = self.compressed(hidden_states, past_key_value, cidx)
|
| 261 |
+
out_s = self.selected(hidden_states, past_key_value, 200 + 2 * cidx)
|
| 262 |
+
out_w = self.sliding(hidden_states, past_key_value, 200 + 2 * cidx + 1)
|
| 263 |
+
|
| 264 |
+
g_c = torch.sigmoid(self.gate_c)
|
| 265 |
+
g_s = torch.sigmoid(self.gate_s)
|
| 266 |
+
g_w = torch.sigmoid(self.gate_w)
|
| 267 |
+
|
| 268 |
+
out = g_c * out_c + g_s * out_s + g_w * out_w
|
| 269 |
+
out = self.norm(out)
|
| 270 |
+
return out, past_key_value
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
# Backward compat alias (in case of NSAttention import)
|
| 274 |
+
NSAttention = NSAAttention
|
| 275 |
+
|
| 276 |
+
__all__ = ["NSAAttention", "NSAttention", "NSACompressBranch", "NSASelectBranch", "NSASlidingBranch"]
|