@@ -158,6 +158,40 @@ func TestVLLMBlockStoredWithLora(t *testing.T) {
158158 assert .Equal (t , [][]any {{"uuid-A" , "salt" }, nil }, blockStored .ExtraKeys )
159159}
160160
161+ func TestVLLMBlockStoredWithHMAMetadata (t * testing.T ) {
162+ adapter := NewVLLMAdapter ()
163+
164+ vllmEvent := []any {
165+ "BlockStored" ,
166+ []any {uint64 (700 ), uint64 (701 )},
167+ uint64 (699 ),
168+ []uint32 {1 , 2 , 3 , 4 },
169+ 16 ,
170+ nil ,
171+ "gpu" ,
172+ nil ,
173+ nil ,
174+ uint64 (1 ),
175+ "sliding_window" ,
176+ 128 ,
177+ }
178+
179+ rawBytes , err := msgpack .Marshal (vllmEvent )
180+ require .NoError (t , err )
181+
182+ event , err := adapter .decodeVLLMEvent (rawBytes )
183+ require .NoError (t , err )
184+
185+ blockStored , ok := event .(* kvevents.BlockStoredEvent )
186+ require .True (t , ok )
187+ assert .Equal (t , 16 , blockStored .BlockSize )
188+ require .NotNil (t , blockStored .GroupIdx )
189+ assert .Equal (t , 1 , * blockStored .GroupIdx )
190+ assert .Equal (t , kvevents .KVCacheSpecKindSlidingWindow , blockStored .KVCacheSpecKind )
191+ require .NotNil (t , blockStored .KVCacheSpecSlidingWindowSize )
192+ assert .Equal (t , 128 , * blockStored .KVCacheSpecSlidingWindowSize )
193+ }
194+
161195// TestDecodeVLLMEvent_BlockStoredMissingTrailingFields tests backward compatibility
162196// when trailing optional fields are absent (older vLLM with omit_defaults=True).
163197func TestDecodeVLLMEvent_BlockStoredMissingTrailingFields (t * testing.T ) {
@@ -236,7 +270,7 @@ func TestDecodeVLLMEvent_BlockStoredMissingTrailingFields(t *testing.T) {
236270func TestDecodeVLLMEvent_BlockStoredExtraTrailingFields (t * testing.T ) {
237271 adapter := NewVLLMAdapter ()
238272
239- // Simulate a future vLLM version with extra_keys and another unknown field
273+ // Simulate a future vLLM version with HMA metadata plus another unknown field.
240274 vllmEvent := []any {
241275 "BlockStored" ,
242276 []any {uint64 (400 ), uint64 (401 )},
@@ -247,7 +281,10 @@ func TestDecodeVLLMEvent_BlockStoredExtraTrailingFields(t *testing.T) {
247281 "gpu" ,
248282 "my-lora" ,
249283 []any {[]any {"extra" , "keys" }}, // [8] extra_keys
250- "completely-unknown-field" , // [9] future unknown — silently ignored
284+ uint64 (0 ), // [9] group_idx
285+ "full_attention" , // [10] kv_cache_spec_kind
286+ nil , // [11] kv_cache_spec_sliding_window
287+ "completely-unknown-field" , // [12] future unknown — silently ignored
251288 }
252289
253290 rawBytes , err := msgpack .Marshal (vllmEvent )
@@ -267,6 +304,9 @@ func TestDecodeVLLMEvent_BlockStoredExtraTrailingFields(t *testing.T) {
267304 assert .Equal (t , "my-lora" , * blockStored .LoraName )
268305 require .NotNil (t , blockStored .ExtraKeys )
269306 assert .Equal (t , [][]any {{"extra" , "keys" }}, blockStored .ExtraKeys )
307+ require .NotNil (t , blockStored .GroupIdx )
308+ assert .Equal (t , 0 , * blockStored .GroupIdx )
309+ assert .Equal (t , kvevents .KVCacheSpecKindFullAttention , blockStored .KVCacheSpecKind )
270310}
271311
272312// TestDecodeVLLMEvent_BlockRemovedExtraTrailingFields tests forward compatibility for BlockRemoved.
@@ -277,8 +317,8 @@ func TestDecodeVLLMEvent_BlockRemovedExtraTrailingFields(t *testing.T) {
277317 "BlockRemoved" ,
278318 []any {uint64 (500 )},
279319 "cpu" ,
280- "future-field-1" ,
281- "future-field-2" ,
320+ uint64 ( 0 ), // [3] group_idx
321+ "future-field-1" , // [4] future unknown — silently ignored
282322 }
283323
284324 rawBytes , err := msgpack .Marshal (vllmEvent )
@@ -291,6 +331,8 @@ func TestDecodeVLLMEvent_BlockRemovedExtraTrailingFields(t *testing.T) {
291331 require .True (t , ok )
292332 assert .Equal (t , []uint64 {500 }, blockRemoved .BlockHashes )
293333 assert .Equal (t , "cpu" , blockRemoved .DeviceTier )
334+ require .NotNil (t , blockRemoved .GroupIdx )
335+ assert .Equal (t , 0 , * blockRemoved .GroupIdx )
294336}
295337
296338// TestDecodeVLLMEvent_BlockRemovedMissingMedium tests backward compat for BlockRemoved.
@@ -312,6 +354,120 @@ func TestDecodeVLLMEvent_BlockRemovedMissingMedium(t *testing.T) {
312354 require .True (t , ok )
313355 assert .Equal (t , []uint64 {600 }, blockRemoved .BlockHashes )
314356 assert .Equal (t , "" , blockRemoved .DeviceTier )
357+ assert .Nil (t , blockRemoved .GroupIdx )
358+ }
359+
360+ func TestDecodeVLLMEvent_BlockRemovedWithGroupIdx (t * testing.T ) {
361+ adapter := NewVLLMAdapter ()
362+
363+ vllmEvent := []any {
364+ "BlockRemoved" ,
365+ []any {uint64 (700 )},
366+ "gpu" ,
367+ uint64 (1 ),
368+ }
369+
370+ rawBytes , err := msgpack .Marshal (vllmEvent )
371+ require .NoError (t , err )
372+
373+ event , err := adapter .decodeVLLMEvent (rawBytes )
374+ require .NoError (t , err )
375+
376+ blockRemoved , ok := event .(* kvevents.BlockRemovedEvent )
377+ require .True (t , ok )
378+ require .NotNil (t , blockRemoved .GroupIdx )
379+ assert .Equal (t , 1 , * blockRemoved .GroupIdx )
380+ }
381+
382+ func TestDecodeVLLMEvent_BlockStoredInvalidHMAMetadata (t * testing.T ) {
383+ adapter := NewVLLMAdapter ()
384+
385+ tests := []struct {
386+ name string
387+ event []any
388+ wantErr string
389+ }{
390+ {
391+ name : "negative group idx" ,
392+ event : []any {
393+ "BlockStored" ,
394+ []any {uint64 (700 )},
395+ uint64 (699 ),
396+ []uint32 {1 , 2 },
397+ 16 ,
398+ nil ,
399+ "gpu" ,
400+ nil ,
401+ nil ,
402+ int64 (- 1 ),
403+ },
404+ wantErr : "group_idx" ,
405+ },
406+ {
407+ name : "non-string spec kind" ,
408+ event : []any {
409+ "BlockStored" ,
410+ []any {uint64 (700 )},
411+ uint64 (699 ),
412+ []uint32 {1 , 2 },
413+ 16 ,
414+ nil ,
415+ "gpu" ,
416+ nil ,
417+ nil ,
418+ uint64 (0 ),
419+ uint64 (123 ),
420+ },
421+ wantErr : "kv_cache_spec_kind" ,
422+ },
423+ {
424+ name : "non-numeric sliding window" ,
425+ event : []any {
426+ "BlockStored" ,
427+ []any {uint64 (700 )},
428+ uint64 (699 ),
429+ []uint32 {1 , 2 },
430+ 16 ,
431+ nil ,
432+ "gpu" ,
433+ nil ,
434+ nil ,
435+ uint64 (0 ),
436+ "sliding_window" ,
437+ "bad-window" ,
438+ },
439+ wantErr : "kv_cache_spec_sliding_window" ,
440+ },
441+ }
442+
443+ for _ , tt := range tests {
444+ t .Run (tt .name , func (t * testing.T ) {
445+ rawBytes , err := msgpack .Marshal (tt .event )
446+ require .NoError (t , err )
447+
448+ _ , err = adapter .decodeVLLMEvent (rawBytes )
449+ require .Error (t , err )
450+ assert .Contains (t , err .Error (), tt .wantErr )
451+ })
452+ }
453+ }
454+
455+ func TestDecodeVLLMEvent_BlockRemovedInvalidGroupIdx (t * testing.T ) {
456+ adapter := NewVLLMAdapter ()
457+
458+ vllmEvent := []any {
459+ "BlockRemoved" ,
460+ []any {uint64 (700 )},
461+ "gpu" ,
462+ int64 (- 1 ),
463+ }
464+
465+ rawBytes , err := msgpack .Marshal (vllmEvent )
466+ require .NoError (t , err )
467+
468+ _ , err = adapter .decodeVLLMEvent (rawBytes )
469+ require .Error (t , err )
470+ assert .Contains (t , err .Error (), "group_idx" )
315471}
316472
317473func intPtr (v int ) * int {
0 commit comments