|
14 | 14 | from vllm.v1.core.kv_cache_metrics import KVCacheMetricsCollector |
15 | 15 | from vllm.v1.core.kv_cache_utils import ( |
16 | 16 | BlockHash, |
17 | | - BlockHashList, |
18 | | - BlockHashListWithBlockSize, |
19 | 17 | BlockHashWithGroupId, |
20 | 18 | ExternalBlockHash, |
21 | 19 | FreeKVCacheBlockQueue, |
|
25 | 23 | get_group_id, |
26 | 24 | make_block_hash_with_group_id, |
27 | 25 | maybe_convert_block_hash, |
| 26 | + resolve_block_hashes, |
28 | 27 | ) |
29 | 28 | from vllm.v1.request import Request |
30 | 29 |
|
@@ -261,17 +260,7 @@ def cache_full_blocks( |
261 | 260 | return |
262 | 261 | new_full_blocks = blocks[num_cached_blocks:num_full_blocks] |
263 | 262 | assert block_mask is None or len(block_mask) == len(new_full_blocks) |
264 | | - if block_size == self.hash_block_size: |
265 | | - # Common case. |
266 | | - block_hashes: BlockHashList = request.block_hashes |
267 | | - else: |
268 | | - # block_size is a multiple of hash_block_size. This happens when |
269 | | - # different KV cache groups have different block sizes. |
270 | | - assert block_size % self.hash_block_size == 0 |
271 | | - block_hashes = BlockHashListWithBlockSize( |
272 | | - request.block_hashes, self.hash_block_size, block_size |
273 | | - ) |
274 | | - assert len(block_hashes) >= num_full_blocks |
| 263 | + block_hashes = resolve_block_hashes(request, self.hash_block_size, block_size) |
275 | 264 |
|
276 | 265 | new_block_hashes = block_hashes[num_cached_blocks:] |
277 | 266 | new_hashes: list[ExternalBlockHash] | None = ( |
@@ -338,23 +327,117 @@ def cache_full_blocks( |
338 | 327 | extra_keys_list.append(extra_keys) |
339 | 328 |
|
340 | 329 | self.kv_event_queue.append( |
341 | | - BlockStored( |
| 330 | + self._build_block_stored_event( |
| 331 | + request, |
342 | 332 | block_hashes=new_hashes, |
343 | 333 | parent_block_hash=parent_block_hash, |
344 | | - token_ids=request.all_token_ids[start_token_idx:end_token_idx], |
| 334 | + start_token_idx=start_token_idx, |
| 335 | + end_token_idx=end_token_idx, |
345 | 336 | block_size=block_size, |
346 | | - lora_id=request.lora_request.adapter_id |
347 | | - if request.lora_request |
348 | | - else None, |
349 | | - medium=MEDIUM_GPU, |
350 | | - lora_name=request.lora_request.name |
351 | | - if request.lora_request |
352 | | - else None, |
353 | | - extra_keys=extra_keys_list if extra_keys_list else None, |
354 | | - group_idx=kv_cache_group_id, |
| 337 | + kv_cache_group_id=kv_cache_group_id, |
| 338 | + extra_keys_list=extra_keys_list, |
355 | 339 | ) |
356 | 340 | ) |
357 | 341 |
|
| 342 | + def _build_block_stored_event( |
| 343 | + self, |
| 344 | + request: Request, |
| 345 | + block_hashes: list[ExternalBlockHash] | None, |
| 346 | + parent_block_hash: ExternalBlockHash | None, |
| 347 | + start_token_idx: int, |
| 348 | + end_token_idx: int, |
| 349 | + block_size: int, |
| 350 | + kv_cache_group_id: int, |
| 351 | + extra_keys_list: list[tuple[Any, ...] | None], |
| 352 | + ) -> BlockStored: |
| 353 | + """Build a ``BlockStored`` KV event for ``request``. |
| 354 | +
|
| 355 | + Shared by ``cache_full_blocks`` (newly cached blocks) and |
| 356 | + ``emit_cached_block_events`` (prefix-cache-reused blocks) so both emit |
| 357 | + identical event shapes for downstream consumers. |
| 358 | + """ |
| 359 | + return BlockStored( |
| 360 | + block_hashes=block_hashes, |
| 361 | + parent_block_hash=parent_block_hash, |
| 362 | + token_ids=request.all_token_ids[start_token_idx:end_token_idx], |
| 363 | + block_size=block_size, |
| 364 | + lora_id=request.lora_request.adapter_id if request.lora_request else None, |
| 365 | + medium=MEDIUM_GPU, |
| 366 | + lora_name=request.lora_request.name if request.lora_request else None, |
| 367 | + extra_keys=extra_keys_list if extra_keys_list else None, |
| 368 | + group_idx=kv_cache_group_id, |
| 369 | + ) |
| 370 | + |
| 371 | + def emit_cached_block_events( |
| 372 | + self, |
| 373 | + request: Request, |
| 374 | + num_cached_blocks: int, |
| 375 | + block_size: int, |
| 376 | + kv_cache_group_id: int, |
| 377 | + ) -> None: |
| 378 | + """Generate BlockStored events for blocks reused from prefix cache. |
| 379 | +
|
| 380 | + Unlike cache_full_blocks(), this does NOT modify block state — |
| 381 | + the blocks are already cached. It only generates events so that |
| 382 | + external consumers (e.g. gateway) can learn about reused blocks. |
| 383 | +
|
| 384 | + Args: |
| 385 | + request: The request whose prefix cache blocks were reused. |
| 386 | + num_cached_blocks: Number of blocks that were cache hits. |
| 387 | + block_size: Number of tokens per block. |
| 388 | + kv_cache_group_id: The KV cache group ID. |
| 389 | + """ |
| 390 | + if not self.enable_kv_cache_events or num_cached_blocks == 0: |
| 391 | + return |
| 392 | + |
| 393 | + block_hashes = resolve_block_hashes(request, self.hash_block_size, block_size) |
| 394 | + |
| 395 | + # Collect external hashes and extra_keys for cached blocks. |
| 396 | + cached_hashes: list[ExternalBlockHash] = [] |
| 397 | + extra_keys_list: list[tuple[Any, ...] | None] = [] |
| 398 | + curr_mm_idx = 0 |
| 399 | + for i in range(num_cached_blocks): |
| 400 | + block_start = i * block_size |
| 401 | + block_end = block_start + block_size |
| 402 | + cached_hashes.append(maybe_convert_block_hash(block_hashes[i])) |
| 403 | + extra_keys, curr_mm_idx = generate_block_hash_extra_keys( |
| 404 | + request, block_start, block_end, curr_mm_idx |
| 405 | + ) |
| 406 | + extra_keys_list.append(extra_keys) |
| 407 | + |
| 408 | + if not cached_hashes: |
| 409 | + return |
| 410 | + |
| 411 | + # Prefix-cache hits always form a contiguous prefix starting at block 0, |
| 412 | + # so the first (and thus the whole group's) parent block hash is None. |
| 413 | + parent_block_hash: ExternalBlockHash | None = None |
| 414 | + start_token_idx = 0 |
| 415 | + end_token_idx = num_cached_blocks * block_size |
| 416 | + |
| 417 | + logger.debug( |
| 418 | + "EmitCachedBlock event: block_size=%d, " |
| 419 | + "num_cached_blocks=%d, parent_block_hash=%s, " |
| 420 | + "token_ids_len=%d, group_idx=%s", |
| 421 | + block_size, |
| 422 | + num_cached_blocks, |
| 423 | + parent_block_hash, |
| 424 | + len(request.all_token_ids[start_token_idx:end_token_idx]), |
| 425 | + kv_cache_group_id, |
| 426 | + ) |
| 427 | + |
| 428 | + self.kv_event_queue.append( |
| 429 | + self._build_block_stored_event( |
| 430 | + request, |
| 431 | + block_hashes=cached_hashes, |
| 432 | + parent_block_hash=parent_block_hash, |
| 433 | + start_token_idx=start_token_idx, |
| 434 | + end_token_idx=end_token_idx, |
| 435 | + block_size=block_size, |
| 436 | + kv_cache_group_id=kv_cache_group_id, |
| 437 | + extra_keys_list=extra_keys_list, |
| 438 | + ) |
| 439 | + ) |
| 440 | + |
358 | 441 | def cache_partial_block( |
359 | 442 | self, |
360 | 443 | request: Request, |
|
0 commit comments