Evo1-1-7B-131K / cache.py
Taykhoom's picture
Initial release
9e26fe9
Raw
History Blame Contribute Delete
4.31 kB
# Copyright (c) Together
# Apache 2.0 - Author: Michael Poli
# Adapted for the minimal Evo1 HF port.
"""Inference-time caches for Evo1 blocks.
Evo1 has two block types with different caching needs:
* `mha` blocks -> InferenceParams (standard KV cache)
* `hyena` blocks -> RecurrentInferenceParams (FIR window + IIR modal state)
Per-block dataclasses are wrapped in an HF ``Cache`` subclass (``Evo1Cache``)
so ``model.generate()`` can drive autoregressive decoding without the user
having to instantiate the two caches by hand, and so HF generation helpers
can introspect cache state (``get_seq_length``, ``get_max_cache_shape``).
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional
from torch import Tensor
from transformers.cache_utils import Cache
@dataclass
class InferenceParams:
"""KV-cache parameters for the attention blocks (mha branch)."""
max_seqlen: int
max_batch_size: int
seqlen_offset: int = 0
batch_size_offset: int = 0
key_value_memory_dict: dict = field(default_factory=dict)
lengths_per_sample: Optional[Tensor] = None
def reset(self, max_seqlen, max_batch_size):
self.max_seqlen = max_seqlen
self.max_batch_size = max_batch_size
self.seqlen_offset = 0
if self.lengths_per_sample is not None:
self.lengths_per_sample.zero_()
@dataclass
class RecurrentInferenceParams:
"""SSM-cache parameters for the Hyena blocks (hyena branch)."""
fir_filter_length: int = 3
state_dim: int = 16
seqlen_offset: int = 0
fir_state_dict: dict = field(default_factory=dict)
state_dict: dict = field(default_factory=dict)
def reset(self):
self.fir_filter_length = 3
self.state_dim = 16
self.seqlen_offset = 0
class Evo1Cache(Cache):
"""HF-compatible wrapper around the per-block inference params.
Internally holds two dataclasses keyed by block type. Exposes
``seqlen_offset`` so HF generation helpers can read the current decoded
length, and implements ``get_seq_length()`` / ``get_max_cache_shape()``
per the transformers ``Cache`` interface.
The model internals (``StripedHyena.stateful_forward``) look up caches
via ``cache["mha"]`` and ``cache["hyena"]``; ``__getitem__`` is delegated
to attribute access so the original dict-keyed API keeps working.
"""
is_compileable = False
def __init__(
self,
max_seqlen: int,
max_batch_size: int,
short_filter_length: int = 3,
state_size: int = 8,
):
# transformers >= 4.55 Cache.__init__ requires either ``layers`` or
# ``layer_class_to_replicate``. We don't use HF's per-layer cache
# model (our two block-type-specific caches handle storage), so we
# pass an empty layers list.
super().__init__(layers=[])
self.mha = InferenceParams(
max_seqlen=max_seqlen,
max_batch_size=max_batch_size,
)
self.hyena = RecurrentInferenceParams(
fir_filter_length=short_filter_length,
state_dim=state_size,
)
# --- HF Cache interface ------------------------------------------------
@property
def seqlen_offset(self) -> int:
return self.mha.seqlen_offset
def get_seq_length(self, layer_idx: int = 0) -> int:
return self.mha.seqlen_offset
def get_max_cache_shape(self) -> int:
return self.mha.max_seqlen
def get_max_length(self) -> int:
# deprecated alias kept for older transformers versions
return self.mha.max_seqlen
# --- our convenience helpers ------------------------------------------
def advance(self, n: int = 1) -> None:
self.mha.seqlen_offset += n
self.hyena.seqlen_offset += n
def set_offset(self, offset: int) -> None:
self.mha.seqlen_offset = offset
self.hyena.seqlen_offset = offset
def reset(self) -> None:
self.mha.reset(self.mha.max_seqlen, self.mha.max_batch_size)
self.hyena.reset()
# --- dict-like access so existing call sites keep working --------------
def __getitem__(self, name: str):
return getattr(self, name)
def by_block_name(self, name: str):
return getattr(self, name)