Text Generation
Transformers
Safetensors
safin1_4b
march
safin1
custom_code
conversational
File size: 16,558 Bytes
4558db3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
"""Shared Safin-1 cache utils for dense and MoE remote code."""

from __future__ import annotations


from types import MethodType
import torch
from torch import nn
from transformers.cache_utils import DynamicCache
from transformers.models.qwen3_5_moe import modeling_qwen3_5_moe as qwen35_moe_modeling


def _new_qwen35_cache(config):
    cache_cls = getattr(qwen35_moe_modeling, "Qwen3_5MoeDynamicCache", DynamicCache)
    try:
        cache = cache_cls(config=config)
    except TypeError:
        cache = cache_cls()
    return _patch_hsd_cache_get_seq_length(cache)


def _patch_hsd_cache_get_seq_length(cache):
    if cache is None:
        return cache
    _patch_hsd_cache_batch_ops(cache)
    if getattr(cache, "_historical_hsd_get_seq_length_patched", False):
        return cache
    original_get_seq_length = getattr(cache, "get_seq_length", None)
    if not callable(original_get_seq_length):
        return cache

    def get_seq_length(self, *args, **kwargs):
        try:
            return original_get_seq_length(*args, **kwargs)
        except ValueError as exc:
            message = str(exc)
            if "get_seq_length" not in message or "LinearAttention" not in message:
                raise
            value = getattr(self, "hsd_seen_expanded_tokens", None)
            if value is not None:
                return _length_value_to_int(value)
            value = getattr(self, "hsd_seen_text_tokens", None)
            if value is not None:
                return _length_value_to_int(value)
            return 0

    cache.get_seq_length = MethodType(get_seq_length, cache)
    cache._historical_hsd_get_seq_length_patched = True
    return cache


def _cache_has_previous_state(past_key_values) -> bool:
    if past_key_values is None:
        return False
    if _length_value_to_int(getattr(past_key_values, "hsd_seen_expanded_tokens", 0)) > 0:
        return True
    if _length_value_to_int(getattr(past_key_values, "hsd_seen_text_tokens", 0)) > 0:
        return True
    if _cache_text_length(past_key_values) > 0:
        return True
    has_previous_state = getattr(past_key_values, "has_previous_state", None)
    if callable(has_previous_state):
        try:
            return bool(has_previous_state())
        except TypeError:
            return bool(has_previous_state(0))
    if isinstance(has_previous_state, bool):
        return has_previous_state
    try:
        return int(past_key_values.get_seq_length()) > 0
    except Exception:
        return False


def _cache_text_length(past_key_values) -> int:
    if past_key_values is None:
        return 0
    value = getattr(past_key_values, "hsd_seen_text_tokens", None)
    if value is not None:
        return _length_value_to_int(value)
    layers = getattr(past_key_values, "layers", None)
    if layers is not None:
        for layer in layers:
            value = getattr(layer, "hsd_seen_text_tokens", None)
            if value is not None:
                return _length_value_to_int(value)
    layers = getattr(past_key_values, "_hsd_layer_states", None)
    if layers is not None:
        for layer in layers:
            value = getattr(layer, "hsd_seen_text_tokens", None)
            if value is not None:
                return _length_value_to_int(value)
    try:
        return int(past_key_values.get_seq_length())
    except Exception:
        return 0


def _cache_expanded_length(past_key_values) -> int:
    if past_key_values is None:
        return 0
    value = getattr(past_key_values, "hsd_seen_expanded_tokens", None)
    if value is not None:
        return _length_value_to_int(value)
    try:
        value = int(past_key_values.get_seq_length())
        if value > 0:
            return value
    except Exception:
        pass
    text_len = _cache_text_length(past_key_values)
    return text_len


def _length_value_to_int(value) -> int:
    if value is None:
        return 0
    if torch.is_tensor(value):
        if value.numel() == 0:
            return 0
        return int(value.detach().max().item())
    return int(value or 0)


def _cache_lengths(
    past_key_values,
    *,
    base_attr: str,
    per_row_attr: str,
    batch_size: int,
    device: torch.device,
) -> torch.Tensor:
    if past_key_values is None:
        return torch.zeros(batch_size, dtype=torch.long, device=device)
    for source in (past_key_values,):
        value = getattr(source, per_row_attr, None)
        if value is not None:
            tensor = torch.as_tensor(value, dtype=torch.long, device=device).reshape(-1)
            if tensor.numel() == batch_size:
                return tensor
            if tensor.numel() == 1:
                return tensor.expand(batch_size)
    for attr_name in ("layers", "_hsd_layer_states"):
        layers = getattr(past_key_values, attr_name, None)
        if layers is None:
            continue
        for layer in layers:
            value = getattr(layer, per_row_attr, None)
            if value is not None:
                tensor = torch.as_tensor(value, dtype=torch.long, device=device).reshape(-1)
                if tensor.numel() == batch_size:
                    return tensor
                if tensor.numel() == 1:
                    return tensor.expand(batch_size)
            value = getattr(layer, base_attr, None)
            if value is not None:
                scalar = _length_value_to_int(value)
                return torch.full((batch_size,), scalar, dtype=torch.long, device=device)
    value = getattr(past_key_values, base_attr, None)
    if value is not None:
        scalar = _length_value_to_int(value)
        return torch.full((batch_size,), scalar, dtype=torch.long, device=device)
    scalar = _cache_text_length(past_key_values) if base_attr == "hsd_seen_text_tokens" else _cache_expanded_length(past_key_values)
    return torch.full((batch_size,), scalar, dtype=torch.long, device=device)


def _cache_text_lengths(past_key_values, batch_size: int, device: torch.device) -> torch.Tensor:
    return _cache_lengths(
        past_key_values,
        base_attr="hsd_seen_text_tokens",
        per_row_attr="hsd_seen_text_tokens_per_row",
        batch_size=batch_size,
        device=device,
    )


def _cache_expanded_lengths(past_key_values, batch_size: int, device: torch.device) -> torch.Tensor:
    return _cache_lengths(
        past_key_values,
        base_attr="hsd_seen_expanded_tokens",
        per_row_attr="hsd_seen_expanded_tokens_per_row",
        batch_size=batch_size,
        device=device,
    )


def _set_root_cache_lengths(
    past_key_values,
    *,
    text_lengths: torch.Tensor,
    expanded_lengths: torch.Tensor,
) -> None:
    if past_key_values is None:
        return
    text_cpu = text_lengths.detach().to(dtype=torch.long)
    expanded_cpu = expanded_lengths.detach().to(dtype=torch.long)
    past_key_values.hsd_seen_text_tokens_per_row = text_cpu
    past_key_values.hsd_seen_expanded_tokens_per_row = expanded_cpu
    past_key_values.hsd_seen_text_tokens = _length_value_to_int(text_cpu)
    past_key_values.hsd_seen_expanded_tokens = _length_value_to_int(expanded_cpu)


class _RowFullAttentionCache:
    def __init__(self, layer_idx: int, keys: torch.Tensor | None = None, values: torch.Tensor | None = None):
        self.layer_idx = int(layer_idx)
        self.keys = keys
        self.values = values

    def get_seq_length(self, layer_idx: int = 0) -> int:
        # This adapter represents one compacted row of one full-attention layer.
        return int(self.keys.shape[-2]) if torch.is_tensor(self.keys) else 0

    def get_mask_sizes(self, query_length: int | torch.Tensor, layer_idx: int = 0) -> tuple[int, int]:
        length = int(query_length.numel()) if torch.is_tensor(query_length) else int(query_length)
        return self.get_seq_length() + length, 0

    def update(self, key_states: torch.Tensor, value_states: torch.Tensor, layer_idx: int, *args, **kwargs):
        if int(layer_idx) != self.layer_idx:
            raise ValueError(f"row full-attention cache received layer {layer_idx}, expected {self.layer_idx}.")
        if self.keys is None or self.values is None or self.keys.numel() == 0:
            self.keys = key_states
            self.values = value_states
        else:
            self.keys = torch.cat([self.keys.to(key_states.device), key_states], dim=-2)
            self.values = torch.cat([self.values.to(value_states.device), value_states], dim=-2)
        return self.keys, self.values


def _full_attention_layer_idx(layer: nn.Module) -> int:
    attn = getattr(layer, "self_attn", None)
    return int(getattr(attn, "layer_idx", getattr(layer, "_historical_hsd_layer_idx", getattr(layer, "layer_idx", 0))))


def _slice_row_full_attention_cache(past_key_values, layer_idx: int, batch_idx: int, batch_size: int) -> _RowFullAttentionCache | None:
    if past_key_values is None:
        return None
    layers = getattr(past_key_values, "layers", None)
    if layers is None or len(layers) <= layer_idx:
        return _RowFullAttentionCache(layer_idx)
    layer_cache = layers[layer_idx]
    keys = getattr(layer_cache, "keys", None)
    values = getattr(layer_cache, "values", None)
    if not torch.is_tensor(keys) or not torch.is_tensor(values) or keys.numel() == 0:
        return _RowFullAttentionCache(layer_idx)
    lengths = getattr(layer_cache, "hsd_text_cache_lengths_per_row", None)
    if torch.is_tensor(lengths) and lengths.numel() > batch_idx:
        row_len = int(lengths.reshape(-1)[batch_idx].item())
    else:
        row_len = int(keys.shape[-2])
    return _RowFullAttentionCache(
        layer_idx,
        keys[batch_idx : batch_idx + 1, :, :row_len].contiguous(),
        values[batch_idx : batch_idx + 1, :, :row_len].contiguous(),
    )


def _merge_row_full_attention_caches(past_key_values, layer_idx: int, row_caches: list[_RowFullAttentionCache | None]) -> None:
    if past_key_values is None:
        return
    layers = getattr(past_key_values, "layers", None)
    if layers is None or len(layers) <= layer_idx:
        return
    keyed = [cache for cache in row_caches if cache is not None and torch.is_tensor(cache.keys)]
    if not keyed:
        return
    sample_keys = keyed[0].keys
    sample_values = keyed[0].values
    max_len = max(int(cache.keys.shape[-2]) if cache is not None and torch.is_tensor(cache.keys) else 0 for cache in row_caches)
    batch_size = len(row_caches)
    merged_keys = sample_keys.new_zeros(batch_size, sample_keys.shape[1], max_len, sample_keys.shape[-1])
    merged_values = sample_values.new_zeros(batch_size, sample_values.shape[1], max_len, sample_values.shape[-1])
    lengths = torch.zeros(batch_size, dtype=torch.long, device=merged_keys.device)
    for batch_idx, cache in enumerate(row_caches):
        if cache is None or not torch.is_tensor(cache.keys) or not torch.is_tensor(cache.values):
            continue
        row_len = int(cache.keys.shape[-2])
        merged_keys[batch_idx : batch_idx + 1, :, :row_len] = cache.keys.to(merged_keys.dtype)
        merged_values[batch_idx : batch_idx + 1, :, :row_len] = cache.values.to(merged_values.dtype)
        lengths[batch_idx] = row_len
    layer_cache = layers[layer_idx]
    layer_cache.keys = merged_keys
    layer_cache.values = merged_values
    layer_cache.is_initialized = True
    layer_cache.dtype = merged_keys.dtype
    layer_cache.device = merged_keys.device
    layer_cache.hsd_text_cache_lengths_per_row = lengths


def _reorder_batch_tensor(value, beam_idx: torch.LongTensor, *, min_ndim=1):
    """Select rows of a declared batch tensor, including expansion and pruning."""
    if torch.is_tensor(value) and value.ndim >= min_ndim:
        return value.index_select(0, beam_idx.to(value.device))
    if isinstance(value, list):
        return [_reorder_batch_tensor(item, beam_idx, min_ndim=min_ndim) for item in value]
    if isinstance(value, tuple):
        return tuple(_reorder_batch_tensor(item, beam_idx, min_ndim=min_ndim) for item in value)
    return value


_PER_ROW_LENGTHS = ("hsd_seen_text_tokens_per_row", "hsd_seen_expanded_tokens_per_row",
                    "hsd_text_cache_lengths_per_row")
_ANCHOR_BATCH_FIELDS = ("hsd_anchor_states", "hsd_anchor_keys", "hsd_anchor_boundaries",
                       "hsd_anchor_valid_mask", "hsd_anchor_persistent_memory_mask")


def _hsd_cache_objects(cache):
    seen = set()
    for obj in [cache, *(getattr(cache, "_hsd_layer_states", None) or []), *(getattr(cache, "layers", None) or [])]:
        if obj is not None and id(obj) not in seen:
            seen.add(id(obj))
            yield obj


def _hsd_cache_batch_size(cache):
    for obj in _hsd_cache_objects(cache):
        for name in (*_PER_ROW_LENGTHS, "hsd_anchor_states", "conv_states", "recurrent_states", "keys"):
            value = getattr(obj, name, None)
            if torch.is_tensor(value) and value.ndim > 0:
                return int(value.shape[0])
    for name in ("_hsd_conv_states", "_hsd_recurrent_states"):
        for value in getattr(cache, name, []) or []:
            if torch.is_tensor(value) and value.ndim > 0:
                return int(value.shape[0])
    return None


def _validate_cache_indices(cache, indices):
    if not torch.is_tensor(indices) or indices.ndim != 1 or indices.dtype not in (torch.int32, torch.int64):
        raise ValueError("cache row indices must be a one-dimensional integer tensor")
    batch_size = _hsd_cache_batch_size(cache)
    if indices.numel() and (int(indices.min()) < 0 or
                           (batch_size is not None and int(indices.max()) >= batch_size)):
        raise IndexError(f"cache row indices are out of range for batch size {batch_size}")


def _reorder_hsd_sidecar_cache(past_key_values, beam_idx: torch.LongTensor):
    """Reorder MARCH sidecars only; upstream owns native KV/conv/recurrent layers."""
    if past_key_values is None:
        return past_key_values
    _validate_cache_indices(past_key_values, beam_idx)
    for obj in _hsd_cache_objects(past_key_values):
        for name in _PER_ROW_LENGTHS:
            if hasattr(obj, name):
                setattr(obj, name, _reorder_batch_tensor(getattr(obj, name), beam_idx))
        for name in _ANCHOR_BATCH_FIELDS:
            if hasattr(obj, name):
                # One-dimensional boundaries/masks are shared across the batch.
                # Their length can coincide with B; that does not make them batch tensors.
                setattr(obj, name, _reorder_batch_tensor(getattr(obj, name), beam_idx, min_ndim=2))
        for base in ("hsd_seen_text_tokens", "hsd_seen_expanded_tokens"):
            lengths = getattr(obj, base + "_per_row", None)
            if torch.is_tensor(lengths):
                setattr(obj, base, _length_value_to_int(lengths))
    for name in ("_hsd_conv_states", "_hsd_recurrent_states"):
        states = getattr(past_key_values, name, None)
        if states is not None:
            setattr(past_key_values, name, _reorder_batch_tensor(states, beam_idx))
    return past_key_values


def _patch_hsd_cache_batch_ops(cache):
    """Give all HF cache row operations one native reorder and one sidecar reorder.

    LinearAttentionLayer lacks the upstream repeat/select methods. Implement
    those through its native reorder_cache, which also handles size changes.
    """
    if getattr(cache, "_march_batch_ops_patched", False):
        return cache
    original = getattr(cache, "reorder_cache", None)
    if not callable(original):
        return cache

    def reorder_cache(self, indices):
        _validate_cache_indices(self, indices)
        # Select sidecars before native layers change B, so validation sees old B.
        _reorder_hsd_sidecar_cache(self, indices)
        return original(indices)

    def batch_select_indices(self, indices):
        return self.reorder_cache(indices)

    def batch_repeat_interleave(self, repeats):
        if not isinstance(repeats, int) or isinstance(repeats, bool) or repeats < 1:
            raise ValueError("cache repeats must be a positive integer")
        batch_size = _hsd_cache_batch_size(self)
        if batch_size is None:
            return None
        indices = torch.arange(batch_size, dtype=torch.long).repeat_interleave(repeats)
        return self.reorder_cache(indices)

    cache.reorder_cache = MethodType(reorder_cache, cache)
    cache.batch_select_indices = MethodType(batch_select_indices, cache)
    cache.batch_repeat_interleave = MethodType(batch_repeat_interleave, cache)
    cache._march_batch_ops_patched = True
    return cache