Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions tests/v1/core/test_kv_cache_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,9 +496,9 @@ def test_generate_block_hash_extra_keys_cache_salt():

# salt is added for the first token
extra_keys, _ = generate_block_hash_extra_keys(request, 0, 1, 0)
assert extra_keys == ("salt",)
assert extra_keys == (("cache_salt", "salt"),)
extra_keys, _ = generate_block_hash_extra_keys(request, 0, 10, 0)
assert extra_keys == ("salt",)
assert extra_keys == (("cache_salt", "salt"),)

# no salt added for other tokens
extra_keys, _ = generate_block_hash_extra_keys(request, 1, 2, 0)
Expand All @@ -519,7 +519,7 @@ def test_generate_block_hash_extra_keys_cache_salt():

# Test with no extra keys
extra_keys, next_mm_idx = generate_block_hash_extra_keys(request_mm, 0, 5, 0)
assert extra_keys == (("hash1", 0), "salt")
assert extra_keys == (("hash1", 0), ("cache_salt", "salt"))
assert next_mm_idx == 1


Expand Down Expand Up @@ -607,13 +607,39 @@ def test_generate_block_hash_extra_keys_lora():
)

extra_keys, _ = generate_block_hash_extra_keys(request, 0, 3, 0)
assert extra_keys == ("test_lora_adapter",)
assert extra_keys == (("lora", "test_lora_adapter"),)

request.lora_request = None
extra_keys, _ = generate_block_hash_extra_keys(request, 0, 3, 0)
assert extra_keys is None


def test_generate_block_hash_extra_keys_lora_cache_salt_no_collision():
"""A LoRA adapter name must not collide with an identically valued
``cache_salt`` from a base-model request (see issue #44701)."""
shared = "COLLIDE"

lora_request = make_request(
request_id="0",
prompt_token_ids=[_ for _ in range(6)],
)
lora_request.lora_request = LoRARequest(
lora_name=shared, lora_int_id=1, lora_path="/path/to/lora"
)
lora_keys, _ = generate_block_hash_extra_keys(lora_request, 0, 3, 0)

salt_request = make_request(
request_id="1",
prompt_token_ids=[_ for _ in range(6)],
mm_positions=None,
mm_hashes=None,
cache_salt=shared,
)
salt_keys, _ = generate_block_hash_extra_keys(salt_request, 0, 3, 0)

assert lora_keys != salt_keys


@pytest.mark.parametrize("hash_fn", [sha256, sha256_cbor])
def test_hash_block_tokens(hash_fn):
parent_block_hash = BlockHash(b"123")
Expand Down
12 changes: 10 additions & 2 deletions tests/v1/core/test_prefix_caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,11 @@ def test_cache_key_salting():
block_hashes = req0.block_hashes
assert len(block_hashes) == 3
assert block_hashes[0] == sha256(
(kv_cache_utils.NONE_HASH, tuple(token_ids[:block_size]), ("salt1",))
(
kv_cache_utils.NONE_HASH,
tuple(token_ids[:block_size]),
(("cache_salt", "salt1"),),
)
)
assert block_hashes[1] == sha256(
(block_hashes[0], tuple(token_ids[block_size : block_size * 2]), None)
Expand Down Expand Up @@ -1703,7 +1707,11 @@ def test_cache_key_salting():
block_hashes = req2.block_hashes
assert len(block_hashes) == 3
assert block_hashes[0] == sha256(
(kv_cache_utils.NONE_HASH, tuple(token_ids[:block_size]), ("salt2",))
(
kv_cache_utils.NONE_HASH,
tuple(token_ids[:block_size]),
(("cache_salt", "salt2"),),
)
)
assert block_hashes[1] == sha256(
(block_hashes[0], tuple(token_ids[block_size : block_size * 2]), None)
Expand Down
22 changes: 15 additions & 7 deletions vllm/v1/core/kv_cache_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,19 +459,21 @@ def _gen_mm_extra_hash_keys(
return extra_keys, curr_mm_idx


def _gen_lora_extra_hash_keys(request: Request) -> list[str]:
def _gen_lora_extra_hash_keys(request: Request) -> list[tuple[str, str]]:
"""Generate extra keys related to LoRA for block hash computation.

Args:
request: The request object.

Returns:
Return LoRA name of the request if it is a LoRA request. Return empty
list otherwise.
Return a domain-tagged LoRA name of the request if it is a LoRA
request. Return empty list otherwise. The ``"lora"`` tag keeps the
adapter name in its own key namespace so it cannot collide with an
identically valued key from another source (e.g. ``cache_salt``).
"""
if not request.lora_request:
return []
return [request.lora_request.lora_name]
return [("lora", request.lora_request.lora_name)]


def _gen_prompt_embeds_extra_hash_keys(
Expand Down Expand Up @@ -520,14 +522,20 @@ def generate_block_hash_extra_keys(
mm_extra_keys, new_start_mm_idx = _gen_mm_extra_hash_keys(
request, start_token_idx, end_token_idx, start_mm_idx
)
lora_extra_keys: list[str] = _gen_lora_extra_hash_keys(request)
cache_salt_keys: list[str] = (
[request.cache_salt] if (start_token_idx == 0 and request.cache_salt) else []
lora_extra_keys: list[tuple[str, str]] = _gen_lora_extra_hash_keys(request)
cache_salt_keys: list[tuple[str, str]] = (
[("cache_salt", request.cache_salt)]
if (start_token_idx == 0 and request.cache_salt)
else []
)
prompt_embeds_keys = _gen_prompt_embeds_extra_hash_keys(
request, start_token_idx, end_token_idx
)

# NOTE: the concatenation order below is load-bearing. It is part of the
# block hash, so reordering these lists changes every resulting hash and
# silently invalidates the entire prefix cache. Keep the order stable (do
# not, e.g., sort for determinism) unless a cache reset is intended.
extra_keys: list[Any] = (
lora_extra_keys + mm_extra_keys + cache_salt_keys + prompt_embeds_keys
)
Expand Down
Loading