chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from sglang.multimodal_gen.runtime.layers.kvcache.causal_attention_cache import (
|
||||
CausalAttentionKVView,
|
||||
CausalSelfAttentionKVCache,
|
||||
CrossAttentionKVCache,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CausalAttentionKVView",
|
||||
"CausalSelfAttentionKVCache",
|
||||
"CrossAttentionKVCache",
|
||||
]
|
||||
@@ -0,0 +1,413 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class CausalAttentionKVView:
|
||||
k: torch.Tensor
|
||||
v: torch.Tensor
|
||||
local_start_index: int
|
||||
local_end_index: int
|
||||
visible_local_end: int
|
||||
visible_global_end: int
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class CausalSelfAttentionKVCache:
|
||||
"""one transformer block's causal self-attn K/V cache and write cursors"""
|
||||
|
||||
k: torch.Tensor
|
||||
v: torch.Tensor
|
||||
# the right bound of the valid global token range
|
||||
# e.g., 12000 means [0, 12000) has been generated and cached
|
||||
global_end_index: torch.Tensor
|
||||
# the right bound of the valid local token range within the buffer (when cache is unfilled)
|
||||
local_end_index: torch.Tensor
|
||||
global_end_index_int: int | None = None
|
||||
local_end_index_int: int | None = None
|
||||
cache_size: int = 0
|
||||
sink_tokens: int = 0
|
||||
attention_window_size: int = 0
|
||||
allow_growth: bool = False
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.cache_size == 0:
|
||||
self.cache_size = self.k.shape[1]
|
||||
if self.attention_window_size == 0:
|
||||
self.attention_window_size = self.cache_size
|
||||
|
||||
def reset_indices(self) -> None:
|
||||
self.global_end_index.zero_()
|
||||
self.local_end_index.zero_()
|
||||
if self.global_end_index_int is not None:
|
||||
self.global_end_index_int = 0
|
||||
if self.local_end_index_int is not None:
|
||||
self.local_end_index_int = 0
|
||||
|
||||
def _read_indices(self) -> tuple[int, int]:
|
||||
global_end_index = self.global_end_index_int
|
||||
local_end_index = self.local_end_index_int
|
||||
if global_end_index is None or local_end_index is None:
|
||||
global_end_index = int(self.global_end_index.item())
|
||||
local_end_index = int(self.local_end_index.item())
|
||||
self.global_end_index_int = global_end_index
|
||||
self.local_end_index_int = local_end_index
|
||||
return global_end_index, local_end_index
|
||||
|
||||
def _write_indices(self, *, global_end_index: int, local_end_index: int) -> None:
|
||||
if (
|
||||
self.global_end_index_int == global_end_index
|
||||
and self.local_end_index_int == local_end_index
|
||||
):
|
||||
return
|
||||
if self.global_end_index_int is not None:
|
||||
self.global_end_index_int = global_end_index
|
||||
if self.local_end_index_int is not None:
|
||||
self.local_end_index_int = local_end_index
|
||||
self.global_end_index.fill_(global_end_index)
|
||||
self.local_end_index.fill_(local_end_index)
|
||||
|
||||
def _grow_to_fit(self, required_tokens: int) -> None:
|
||||
if required_tokens <= self.cache_size:
|
||||
return
|
||||
old_cache_size = self.cache_size
|
||||
new_cache_size = max(required_tokens, old_cache_size * 2)
|
||||
|
||||
new_k = self.k.new_zeros(
|
||||
self.k.shape[0],
|
||||
new_cache_size,
|
||||
self.k.shape[2],
|
||||
self.k.shape[3],
|
||||
)
|
||||
new_v = self.v.new_zeros(
|
||||
self.v.shape[0],
|
||||
new_cache_size,
|
||||
self.v.shape[2],
|
||||
self.v.shape[3],
|
||||
)
|
||||
new_k[:, :old_cache_size] = self.k
|
||||
new_v[:, :old_cache_size] = self.v
|
||||
self.k = new_k
|
||||
self.v = new_v
|
||||
self.cache_size = new_cache_size
|
||||
if self.attention_window_size == old_cache_size:
|
||||
self.attention_window_size = new_cache_size
|
||||
|
||||
def can_direct_current_attention(self, num_new_tokens: int) -> bool:
|
||||
return (
|
||||
self.sink_tokens == 0
|
||||
and self.cache_size == num_new_tokens
|
||||
and self.attention_window_size == num_new_tokens
|
||||
)
|
||||
|
||||
def update_and_get_attention_kv(
|
||||
self,
|
||||
*,
|
||||
key: torch.Tensor,
|
||||
value: torch.Tensor,
|
||||
current_chunk_start: int,
|
||||
cache_head_start: int | None = None,
|
||||
recent_window_tokens: int | None = None,
|
||||
debug_name: str = "causal KV cache",
|
||||
) -> CausalAttentionKVView:
|
||||
"""write fresh kv into the cache, returns the part of view visible to the current chunk
|
||||
|
||||
Args:
|
||||
current_chunk_start: the global position of the start of the chunk
|
||||
cache_head_start: first cache head for key/value when they only
|
||||
carry a local slice of the cache heads; other heads are left untouched
|
||||
recent_window_tokens: recent-window attention size. ``None``
|
||||
returns the full visible attention window. ``0`` keeps only sink
|
||||
tokens plus the current chunk. A positive value keeps sink tokens,
|
||||
up to that many tokens before the current chunk, and the current
|
||||
chunk. Negative values are invalid.
|
||||
|
||||
"""
|
||||
num_new_tokens = key.shape[1]
|
||||
num_input_heads = key.shape[2]
|
||||
num_cache_heads = self.k.shape[2]
|
||||
cache_head_slice = None
|
||||
if num_cache_heads != num_input_heads:
|
||||
if cache_head_start is None:
|
||||
raise ValueError(
|
||||
f"{debug_name} requires cache_head_start when cache heads "
|
||||
f"({num_cache_heads}) differ from input heads ({num_input_heads})."
|
||||
)
|
||||
cache_head_slice = slice(
|
||||
cache_head_start, cache_head_start + num_input_heads
|
||||
)
|
||||
current_chunk_end = current_chunk_start + num_new_tokens
|
||||
kv_cache_size = self.cache_size
|
||||
sink_tokens = self.sink_tokens
|
||||
global_end_index, local_end_index_prev = self._read_indices()
|
||||
|
||||
# local_start(/end)_index: the local position of the start/end of current chunk
|
||||
# updated_local_end: the updated local end
|
||||
# updated_global_end: the updated global end
|
||||
|
||||
# the global position of the start of the buffer
|
||||
window_start = global_end_index - local_end_index_prev
|
||||
|
||||
if current_chunk_end <= global_end_index:
|
||||
# the window stays as previous
|
||||
# cache layout:
|
||||
# [sink tokens, recent window tokens, current chunk tokens, uninitialized tokens (optional)]
|
||||
local_start_index = current_chunk_start - window_start
|
||||
local_end_index = local_start_index + num_new_tokens
|
||||
|
||||
# the local end and global end remains unchanged (since the chunk hasn't proceed)
|
||||
updated_local_end = local_end_index_prev
|
||||
updated_global_end = global_end_index
|
||||
else:
|
||||
# the chunk window has proceed, append new tokens, and evict earliest (if have to)
|
||||
appended_tokens = current_chunk_end - global_end_index
|
||||
if self.allow_growth:
|
||||
self._grow_to_fit(local_end_index_prev + appended_tokens)
|
||||
kv_cache_size = self.cache_size
|
||||
if local_end_index_prev + appended_tokens > kv_cache_size:
|
||||
# the new tokens can't fit in the remaining space (after local_end_index_prev), start evicting:
|
||||
# before:
|
||||
# [sink tokens, evicted tokens, rolled tokens, remaining space]
|
||||
# ^ end of previous chunk
|
||||
# after:
|
||||
# [sink tokens, rolled tokens, remaining space ]
|
||||
|
||||
# 1. keep sink tokens ([0: sink_tokens]) untouched
|
||||
# 2. evict obsolete tokens in: [sink_tokens:sink_tokens + num_evicted_tokens]
|
||||
num_evicted_tokens = (
|
||||
local_end_index_prev + appended_tokens - kv_cache_size
|
||||
)
|
||||
|
||||
# number of tokens to move
|
||||
num_rolled_tokens = max(
|
||||
0,
|
||||
local_end_index_prev - num_evicted_tokens - sink_tokens,
|
||||
)
|
||||
if num_rolled_tokens > 0:
|
||||
if cache_head_slice is None:
|
||||
self.k[:, sink_tokens : sink_tokens + num_rolled_tokens] = (
|
||||
self.k[
|
||||
:,
|
||||
sink_tokens
|
||||
+ num_evicted_tokens : sink_tokens
|
||||
+ num_evicted_tokens
|
||||
+ num_rolled_tokens,
|
||||
].clone()
|
||||
)
|
||||
self.v[:, sink_tokens : sink_tokens + num_rolled_tokens] = (
|
||||
self.v[
|
||||
:,
|
||||
sink_tokens
|
||||
+ num_evicted_tokens : sink_tokens
|
||||
+ num_evicted_tokens
|
||||
+ num_rolled_tokens,
|
||||
].clone()
|
||||
)
|
||||
else:
|
||||
self.k[
|
||||
:,
|
||||
sink_tokens : sink_tokens + num_rolled_tokens,
|
||||
cache_head_slice,
|
||||
:,
|
||||
] = self.k[
|
||||
:,
|
||||
sink_tokens
|
||||
+ num_evicted_tokens : sink_tokens
|
||||
+ num_evicted_tokens
|
||||
+ num_rolled_tokens,
|
||||
cache_head_slice,
|
||||
:,
|
||||
].clone()
|
||||
self.v[
|
||||
:,
|
||||
sink_tokens : sink_tokens + num_rolled_tokens,
|
||||
cache_head_slice,
|
||||
:,
|
||||
] = self.v[
|
||||
:,
|
||||
sink_tokens
|
||||
+ num_evicted_tokens : sink_tokens
|
||||
+ num_evicted_tokens
|
||||
+ num_rolled_tokens,
|
||||
cache_head_slice,
|
||||
:,
|
||||
].clone()
|
||||
|
||||
# if we move the minimum number of tokens, the right bound of the append token would be aligned with end of the buffer
|
||||
local_end_index = kv_cache_size
|
||||
else:
|
||||
# enough space, directly append new tokens after end of previous chunk
|
||||
local_end_index = local_end_index_prev + appended_tokens
|
||||
local_start_index = local_end_index - num_new_tokens
|
||||
updated_local_end = local_end_index
|
||||
# after filling in the proceeded new chunk, the global end aligns with the global end of the current chunk
|
||||
updated_global_end = current_chunk_end
|
||||
|
||||
if (
|
||||
local_start_index < 0
|
||||
or local_end_index > kv_cache_size
|
||||
or local_end_index - local_start_index != num_new_tokens
|
||||
):
|
||||
raise RuntimeError(
|
||||
f"Invalid {debug_name} write range: "
|
||||
f"local=[{local_start_index}, {local_end_index}), "
|
||||
f"global_end={global_end_index}, "
|
||||
f"prev_local_end={local_end_index_prev}, "
|
||||
f"kv_cache_size={kv_cache_size}, "
|
||||
f"num_new_tokens={num_new_tokens}, "
|
||||
f"current_start={current_chunk_start}, current_end={current_chunk_end}"
|
||||
)
|
||||
|
||||
if self.k.requires_grad:
|
||||
self.k = self.k.detach()
|
||||
if self.v.requires_grad:
|
||||
self.v = self.v.detach()
|
||||
attn_start_index = max(0, updated_local_end - self.attention_window_size)
|
||||
|
||||
# write fresh kv and return visible view
|
||||
if cache_head_slice is None:
|
||||
self.k[:, local_start_index:local_end_index] = key
|
||||
self.v[:, local_start_index:local_end_index] = value
|
||||
visible_k, visible_v = self._visible_attention_kv(
|
||||
local_start_index=local_start_index,
|
||||
updated_local_end=updated_local_end,
|
||||
attn_start_index=attn_start_index,
|
||||
recent_window_tokens=recent_window_tokens,
|
||||
)
|
||||
else:
|
||||
self.k[:, local_start_index:local_end_index, cache_head_slice, :] = key
|
||||
self.v[:, local_start_index:local_end_index, cache_head_slice, :] = value
|
||||
visible_k, visible_v = self._visible_attention_kv(
|
||||
local_start_index=local_start_index,
|
||||
updated_local_end=updated_local_end,
|
||||
attn_start_index=attn_start_index,
|
||||
recent_window_tokens=recent_window_tokens,
|
||||
cache_head_slice=cache_head_slice,
|
||||
)
|
||||
|
||||
self._write_indices(
|
||||
global_end_index=updated_global_end,
|
||||
local_end_index=updated_local_end,
|
||||
)
|
||||
return CausalAttentionKVView(
|
||||
k=visible_k,
|
||||
v=visible_v,
|
||||
local_start_index=local_start_index,
|
||||
local_end_index=local_end_index,
|
||||
visible_local_end=updated_local_end,
|
||||
visible_global_end=updated_global_end,
|
||||
)
|
||||
|
||||
def _visible_attention_kv(
|
||||
self,
|
||||
*,
|
||||
local_start_index: int,
|
||||
updated_local_end: int,
|
||||
attn_start_index: int,
|
||||
recent_window_tokens: int | None,
|
||||
cache_head_slice: slice | None = None,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
"""Return the visible KV slice for the current attention call.
|
||||
|
||||
When ``recent_window_tokens`` is ``None``, the returned token range is
|
||||
the standard sliding window::
|
||||
|
||||
[attn_start_index, updated_local_end)
|
||||
|
||||
When recent-window selection is enabled, ``recent_window_tokens`` must be
|
||||
non-negative and the returned token ranges are::
|
||||
|
||||
sink_end = min(self.sink_tokens, updated_local_end)
|
||||
recent_start = max(sink_end, local_start_index - recent_window_tokens)
|
||||
[0, sink_end) + [recent_start, updated_local_end)
|
||||
|
||||
Thus ``0`` keeps only sink tokens plus the current chunk.
|
||||
``cache_head_slice`` applies the same token ranges to a subset of KV
|
||||
heads.
|
||||
"""
|
||||
if recent_window_tokens is None:
|
||||
if cache_head_slice is None:
|
||||
return (
|
||||
self.k[:, attn_start_index:updated_local_end],
|
||||
self.v[:, attn_start_index:updated_local_end],
|
||||
)
|
||||
return (
|
||||
self.k[:, attn_start_index:updated_local_end, cache_head_slice, :],
|
||||
self.v[:, attn_start_index:updated_local_end, cache_head_slice, :],
|
||||
)
|
||||
if recent_window_tokens < 0:
|
||||
raise ValueError("recent_window_tokens must be non-negative or None")
|
||||
|
||||
sink_end = min(self.sink_tokens, updated_local_end)
|
||||
recent_start = max(sink_end, local_start_index - recent_window_tokens)
|
||||
if recent_start <= sink_end:
|
||||
if cache_head_slice is None:
|
||||
return self.k[:, :updated_local_end], self.v[:, :updated_local_end]
|
||||
return (
|
||||
self.k[:, :updated_local_end, cache_head_slice, :],
|
||||
self.v[:, :updated_local_end, cache_head_slice, :],
|
||||
)
|
||||
if sink_end <= 0:
|
||||
if cache_head_slice is None:
|
||||
return (
|
||||
self.k[:, recent_start:updated_local_end],
|
||||
self.v[:, recent_start:updated_local_end],
|
||||
)
|
||||
return (
|
||||
self.k[:, recent_start:updated_local_end, cache_head_slice, :],
|
||||
self.v[:, recent_start:updated_local_end, cache_head_slice, :],
|
||||
)
|
||||
|
||||
if cache_head_slice is None:
|
||||
return (
|
||||
torch.cat(
|
||||
[
|
||||
self.k[:, :sink_end],
|
||||
self.k[:, recent_start:updated_local_end],
|
||||
],
|
||||
dim=1,
|
||||
),
|
||||
torch.cat(
|
||||
[
|
||||
self.v[:, :sink_end],
|
||||
self.v[:, recent_start:updated_local_end],
|
||||
],
|
||||
dim=1,
|
||||
),
|
||||
)
|
||||
return (
|
||||
torch.cat(
|
||||
[
|
||||
self.k[:, :sink_end, cache_head_slice, :],
|
||||
self.k[:, recent_start:updated_local_end, cache_head_slice, :],
|
||||
],
|
||||
dim=1,
|
||||
),
|
||||
torch.cat(
|
||||
[
|
||||
self.v[:, :sink_end, cache_head_slice, :],
|
||||
self.v[:, recent_start:updated_local_end, cache_head_slice, :],
|
||||
],
|
||||
dim=1,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class CrossAttentionKVCache:
|
||||
"""one transformer block's cross-attn condition K/V cache"""
|
||||
|
||||
k: torch.Tensor
|
||||
v: torch.Tensor
|
||||
is_init: bool = False
|
||||
|
||||
def store(self, k: torch.Tensor, v: torch.Tensor) -> None:
|
||||
self.k = k.detach()
|
||||
self.v = v.detach()
|
||||
self.is_init = True
|
||||
|
||||
def reset(self) -> None:
|
||||
self.is_init = False
|
||||
Reference in New Issue
Block a user