From 8375f795d096e8996d438cca8704e3d0f6bbf477 Mon Sep 17 00:00:00 2001 From: Sage Ahrac Date: Mon, 17 Nov 2025 18:01:08 +0200 Subject: [PATCH 1/3] wip Signed-off-by: Sage Ahrac --- tests/integration/prompt_to_block_test.go | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/integration/prompt_to_block_test.go diff --git a/tests/integration/prompt_to_block_test.go b/tests/integration/prompt_to_block_test.go new file mode 100644 index 00000000..315f5265 --- /dev/null +++ b/tests/integration/prompt_to_block_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2025 The llm-d Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//nolint:testpackage // need to test internal types +package integration_test + +import ( + "testing" + + "github.com/llm-d/llm-d-kv-cache-manager/examples/testdata" + "github.com/llm-d/llm-d-kv-cache-manager/pkg/kvcache/kvblock" + "github.com/llm-d/llm-d-kv-cache-manager/pkg/tokenization" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPromptToBlockHashesWithPrecomputedValues(t *testing.T) { + if testing.Short() { + t.Skip("Skipping precomputed hash comparison test in short mode") + } + + // Setup tokenizer + config := &tokenization.HFTokenizerConfig{ + TokenizersCacheDir: t.TempDir(), + } + tokenizer, err := tokenization.NewCachedHFTokenizer(config) + require.NoError(t, err) + + // Setup processor with config that should match vLLM settings + processorConfig := &kvblock.TokenProcessorConfig{ + BlockSize: 16, // vLLM default + HashSeed: "", // Match vLLM's default + } + processor := kvblock.NewChunkedTokenDatabase(processorConfig) + + // Tokenize the testdata prompt + tokenIds, _, err := tokenizer.Encode(testdata.Prompt, testdata.ModelName) + require.NoError(t, err, "tokenization should succeed") + require.NotEmpty(t, tokenIds, "prompt should produce tokens") + + // Generate block keys with hashes + blockKeys := processor.TokensToKVBlockKeys(tokenIds, testdata.ModelName) + require.NotEmpty(t, blockKeys, "should generate block keys") + + // Extract hashes for comparison + actualHashes := make([]uint64, len(blockKeys)) + for i, key := range blockKeys { + actualHashes[i] = key.ChunkHash + } + + // Compare with precomputed hashes from vLLM + assert.Equal(t, testdata.PromptHashes, actualHashes, + "computed hashes should match precomputed vLLM hashes") + + // Additional validations + assert.Equal(t, len(testdata.PromptHashes), len(actualHashes), + "number of hash blocks should match expected count") + + for i, expectedHash := range testdata.PromptHashes { + if i < len(actualHashes) { + assert.Equal(t, expectedHash, actualHashes[i], + "hash at position %d should match vLLM precomputed value", i) + } + } +} From d6b98f32536d27d20dc4241be28e3db0234e1f73 Mon Sep 17 00:00:00 2001 From: Sage Ahrac Date: Tue, 18 Nov 2025 15:33:41 +0200 Subject: [PATCH 2/3] add tests Signed-off-by: Sage Ahrac --- tests/integration/prompt_to_block_test.go | 103 ++++++++++++++++++---- 1 file changed, 86 insertions(+), 17 deletions(-) diff --git a/tests/integration/prompt_to_block_test.go b/tests/integration/prompt_to_block_test.go index 315f5265..31140a7d 100644 --- a/tests/integration/prompt_to_block_test.go +++ b/tests/integration/prompt_to_block_test.go @@ -18,20 +18,51 @@ limitations under the License. package integration_test import ( + _ "embed" + "encoding/json" "testing" - "github.com/llm-d/llm-d-kv-cache-manager/examples/testdata" "github.com/llm-d/llm-d-kv-cache-manager/pkg/kvcache/kvblock" "github.com/llm-d/llm-d-kv-cache-manager/pkg/tokenization" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +//go:embed testdata/kv_event.json +var kvEventJSON []byte + +//go:embed testdata/kv_event_with_lora.json +var kvEventWithLoraJSON []byte + +type KVEventData struct { + Prompt string `json:"prompt"` + ModelName string `json:"model_name"` + LoraPath *string `json:"lora_path"` + LoraName *string `json:"lora_name"` + EventType string `json:"event_type"` + BlockHashes []uint64 `json:"block_hashes"` + ParentBlockHash *uint64 `json:"parent_block_hash"` + TokenIds []int `json:"token_ids"` + BlockSize int `json:"block_size"` + Medium string `json:"medium"` + HashSeed string `json:"hash_seed"` +} + +func parseTestData(t *testing.T, jsonData []byte) *KVEventData { + var eventData KVEventData + err := json.Unmarshal(jsonData, &eventData) + require.NoError(t, err, "failed to parse test data JSON") + return &eventData +} + func TestPromptToBlockHashesWithPrecomputedValues(t *testing.T) { if testing.Short() { t.Skip("Skipping precomputed hash comparison test in short mode") } + // Parse embedded test data + testData := parseTestData(t, kvEventJSON) + // Setup tokenizer config := &tokenization.HFTokenizerConfig{ TokenizersCacheDir: t.TempDir(), @@ -39,20 +70,20 @@ func TestPromptToBlockHashesWithPrecomputedValues(t *testing.T) { tokenizer, err := tokenization.NewCachedHFTokenizer(config) require.NoError(t, err) - // Setup processor with config that should match vLLM settings + // Setup processor with config from test data processorConfig := &kvblock.TokenProcessorConfig{ - BlockSize: 16, // vLLM default - HashSeed: "", // Match vLLM's default + BlockSize: testData.BlockSize, + HashSeed: testData.HashSeed, } processor := kvblock.NewChunkedTokenDatabase(processorConfig) - // Tokenize the testdata prompt - tokenIds, _, err := tokenizer.Encode(testdata.Prompt, testdata.ModelName) + // Tokenize the prompt from test data + tokenIds, _, err := tokenizer.Encode(testData.Prompt, testData.ModelName) require.NoError(t, err, "tokenization should succeed") require.NotEmpty(t, tokenIds, "prompt should produce tokens") // Generate block keys with hashes - blockKeys := processor.TokensToKVBlockKeys(tokenIds, testdata.ModelName) + blockKeys := processor.TokensToKVBlockKeys(tokenIds, testData.ModelName) require.NotEmpty(t, blockKeys, "should generate block keys") // Extract hashes for comparison @@ -61,18 +92,56 @@ func TestPromptToBlockHashesWithPrecomputedValues(t *testing.T) { actualHashes[i] = key.ChunkHash } - // Compare with precomputed hashes from vLLM - assert.Equal(t, testdata.PromptHashes, actualHashes, + // Compare with precomputed hashes from test data + assert.Equal(t, testData.BlockHashes, actualHashes, "computed hashes should match precomputed vLLM hashes") +} + +func TestPromptToBlockHashesWithLoraAdapter(t *testing.T) { + if testing.Short() { + t.Skip("Skipping precomputed hash comparison test in short mode") + } - // Additional validations - assert.Equal(t, len(testdata.PromptHashes), len(actualHashes), - "number of hash blocks should match expected count") + // Parse embedded test data with LoRA + testData := parseTestData(t, kvEventWithLoraJSON) - for i, expectedHash := range testdata.PromptHashes { - if i < len(actualHashes) { - assert.Equal(t, expectedHash, actualHashes[i], - "hash at position %d should match vLLM precomputed value", i) - } + // Setup tokenizer + config := &tokenization.HFTokenizerConfig{ + TokenizersCacheDir: t.TempDir(), } + tokenizer, err := tokenization.NewCachedHFTokenizer(config) + require.NoError(t, err) + + // Setup processor with config from test data + processorConfig := &kvblock.TokenProcessorConfig{ + BlockSize: testData.BlockSize, + HashSeed: testData.HashSeed, + } + processor := kvblock.NewChunkedTokenDatabase(processorConfig) + + // Build model name with LoRA adapter + modelNameWithLora := testData.ModelName + if testData.LoraName != nil { + // The model name should include LoRA information for proper hashing + modelNameWithLora = testData.ModelName + "+" + *testData.LoraName + } + + // Tokenize the prompt from test data + tokenIds, _, err := tokenizer.Encode(testData.Prompt, testData.ModelName) + require.NoError(t, err, "tokenization should succeed") + require.NotEmpty(t, tokenIds, "prompt should produce tokens") + + // Generate block keys with hashes using LoRA-aware model name + blockKeys := processor.TokensToKVBlockKeys(tokenIds, modelNameWithLora) + require.NotEmpty(t, blockKeys, "should generate block keys") + + // Extract hashes for comparison + actualHashes := make([]uint64, len(blockKeys)) + for i, key := range blockKeys { + actualHashes[i] = key.ChunkHash + } + + // Compare with precomputed hashes from test data + assert.Equal(t, testData.BlockHashes, actualHashes, + "computed hashes with LoRA should match precomputed vLLM hashes") } From 69f35e1cd0288457cbac8a54905b6818db6eb818 Mon Sep 17 00:00:00 2001 From: Sage Ahrac Date: Tue, 18 Nov 2025 15:37:21 +0200 Subject: [PATCH 3/3] testdata Signed-off-by: Sage Ahrac --- tests/integration/testdata/kv_event.json | 440 ++++++++++++++++++ .../testdata/kv_event_with_lora.json | 440 ++++++++++++++++++ 2 files changed, 880 insertions(+) create mode 100644 tests/integration/testdata/kv_event.json create mode 100644 tests/integration/testdata/kv_event_with_lora.json diff --git a/tests/integration/testdata/kv_event.json b/tests/integration/testdata/kv_event.json new file mode 100644 index 00000000..b71d9e9e --- /dev/null +++ b/tests/integration/testdata/kv_event.json @@ -0,0 +1,440 @@ +{ + "prompt": "Anarchism is a political philosophy and movement that is sceptical of authority and rejects all involuntary, coercive forms of hierarchy. Anarchism calls for the abolition of the state, which it holds to be unnecessary, undesirable, and harmful. As a historically left-wing movement, placed on the farthest left of the political spectrum, it is usually described alongside communalism and libertarian Marxism as the libertarian wing (libertarian socialism) of the socialist movement, and has a strong historical association with anti-capitalism and socialism.\n Humans lived in societies without formal hierarchies long before the establishment of formal states, realms, or empires. With the rise of organised hierarchical bodies, scepticism toward authority also rose. Although traces of anarchist thought are found throughout history, modern anarchism emerged from the Enlightenment. During the latter half of the 19th and the first decades of the 20th century, the anarchist movement flourished in most parts of the world and had a significant role in workers' struggles for emancipation. Various anarchist schools of thought formed during this period. Anarchists have taken part in several revolutions, most notably in the Paris Commune, the Russian Civil War and the Spanish Civil War, whose end marked the end of the classical era of anarchism. In the last decades of the 20th and into the 21st century, the anarchist movement has been resurgent once more.\n Anarchism employs a diversity of tactics in order to meet its ideal ends which can be broadly separated into revolutionary and evolutionary tactics; there is significant overlap between the two, which are merely descriptive. Revolutionary tactics aim to bring down authority and state, having taken a violent turn in the past, while evolutionary tactics aim to prefigure what an anarchist society would be like. Anarchist thought, criticism, and praxis have played a part in diverse areas of human society. Criticism of anarchism include claims that it is internally inconsistent, violent, or utopian.", + "model_name": "Qwen/Qwen3-0.6B", + "lora_path": "charent/self_cognition_Alice", + "lora_name": null, + "event_type": "BlockStored", + "block_hashes": [ + 17705339885301241772, + 13360521540053240825, + 1652464311825922890, + 12860800319228078273, + 3450323724325539335, + 3228310428562727993, + 6148655630810427752, + 7247902503758347614, + 13398480267478892921, + 16001393832065633136, + 5465193924169978467, + 10089890520785644009, + 422920441552050324, + 15899806193804522047, + 8393642862922256935, + 6252039273099455613, + 4984351404723017020, + 17333094528838329214, + 18308444338606176786, + 337201363510575849, + 9210643397516082020, + 293096018719580475, + 18388723977999946127, + 5490930151665639282, + 1902323794085395034 + ], + "parent_block_hash": null, + "token_ids": [ + 2082, + 1113, + 2142, + 374, + 264, + 4948, + 19128, + 323, + 7203, + 429, + 374, + 66351, + 938, + 315, + 11198, + 323, + 59415, + 678, + 90505, + 11, + 54263, + 533, + 7586, + 315, + 28922, + 13, + 1527, + 1113, + 2142, + 6738, + 369, + 279, + 75345, + 315, + 279, + 1584, + 11, + 892, + 432, + 9982, + 311, + 387, + 25165, + 11, + 76244, + 11, + 323, + 27756, + 13, + 1634, + 264, + 34801, + 2115, + 28380, + 7203, + 11, + 9099, + 389, + 279, + 3041, + 60843, + 2115, + 315, + 279, + 4948, + 19745, + 11, + 432, + 374, + 5990, + 7481, + 16263, + 56837, + 2142, + 323, + 56025, + 82715, + 438, + 279, + 56025, + 20013, + 320, + 2740, + 529, + 8821, + 50518, + 8, + 315, + 279, + 40189, + 7203, + 11, + 323, + 702, + 264, + 3746, + 13656, + 15010, + 448, + 7147, + 97131, + 2142, + 323, + 50518, + 624, + 262, + 64994, + 12163, + 304, + 33675, + 2041, + 15908, + 12412, + 1113, + 550, + 1293, + 1573, + 279, + 21269, + 315, + 15908, + 5302, + 11, + 76463, + 11, + 476, + 976, + 18968, + 13, + 3085, + 279, + 10000, + 315, + 38333, + 69894, + 12866, + 11, + 66351, + 41814, + 8841, + 11198, + 1083, + 16009, + 13, + 10328, + 34383, + 315, + 77331, + 3381, + 525, + 1730, + 6814, + 3840, + 11, + 6481, + 43465, + 2142, + 21992, + 504, + 279, + 91831, + 13, + 11954, + 279, + 15271, + 4279, + 315, + 279, + 220, + 16, + 24, + 339, + 323, + 279, + 1156, + 10793, + 315, + 279, + 220, + 17, + 15, + 339, + 9294, + 11, + 279, + 77331, + 7203, + 19828, + 3304, + 304, + 1429, + 5479, + 315, + 279, + 1879, + 323, + 1030, + 264, + 5089, + 3476, + 304, + 7337, + 6, + 27870, + 369, + 90125, + 48586, + 13, + 39641, + 77331, + 8682, + 315, + 3381, + 14122, + 2337, + 419, + 4168, + 13, + 1527, + 1113, + 1671, + 614, + 4429, + 949, + 304, + 3807, + 92474, + 11, + 1429, + 34046, + 304, + 279, + 12095, + 6804, + 2886, + 11, + 279, + 8522, + 16398, + 5004, + 323, + 279, + 15154, + 16398, + 5004, + 11, + 6693, + 835, + 12864, + 279, + 835, + 315, + 279, + 28824, + 11385, + 315, + 43465, + 2142, + 13, + 758, + 279, + 1537, + 10793, + 315, + 279, + 220, + 17, + 15, + 339, + 323, + 1119, + 279, + 220, + 17, + 16, + 267, + 9294, + 11, + 279, + 77331, + 7203, + 702, + 1012, + 592, + 85053, + 3055, + 803, + 624, + 262, + 1527, + 1113, + 2142, + 50142, + 264, + 19492, + 315, + 25342, + 304, + 1973, + 311, + 3367, + 1181, + 10507, + 10335, + 892, + 646, + 387, + 42929, + 18663, + 1119, + 29091, + 323, + 40893, + 25342, + 26, + 1052, + 374, + 5089, + 27248, + 1948, + 279, + 1378, + 11, + 892, + 525, + 16234, + 52844, + 13, + 66579, + 25342, + 9213, + 311, + 4446, + 1495, + 11198, + 323, + 1584, + 11, + 3432, + 4429, + 264, + 16401, + 2484, + 304, + 279, + 3267, + 11, + 1393, + 40893, + 25342, + 9213, + 311, + 855, + 17781, + 1128, + 458, + 77331, + 8232, + 1035, + 387, + 1075, + 13, + 1527, + 1113, + 380, + 3381, + 11, + 18817, + 11, + 323, + 548, + 7184, + 614, + 6342, + 264, + 949, + 304, + 16807, + 5671, + 315, + 3738, + 8232, + 13, + 33207, + 41814, + 315, + 43465, + 2142, + 2924, + 8186, + 429, + 432, + 374, + 33067, + 39140, + 11, + 16401, + 11, + 476, + 8621 + ], + "block_size": 16, + "medium": "GPU", + "hash_seed": "42" +} \ No newline at end of file diff --git a/tests/integration/testdata/kv_event_with_lora.json b/tests/integration/testdata/kv_event_with_lora.json new file mode 100644 index 00000000..3831ac78 --- /dev/null +++ b/tests/integration/testdata/kv_event_with_lora.json @@ -0,0 +1,440 @@ +{ + "prompt": "Anarchism is a political philosophy and movement that is sceptical of authority and rejects all involuntary, coercive forms of hierarchy. Anarchism calls for the abolition of the state, which it holds to be unnecessary, undesirable, and harmful. As a historically left-wing movement, placed on the farthest left of the political spectrum, it is usually described alongside communalism and libertarian Marxism as the libertarian wing (libertarian socialism) of the socialist movement, and has a strong historical association with anti-capitalism and socialism.\n Humans lived in societies without formal hierarchies long before the establishment of formal states, realms, or empires. With the rise of organised hierarchical bodies, scepticism toward authority also rose. Although traces of anarchist thought are found throughout history, modern anarchism emerged from the Enlightenment. During the latter half of the 19th and the first decades of the 20th century, the anarchist movement flourished in most parts of the world and had a significant role in workers' struggles for emancipation. Various anarchist schools of thought formed during this period. Anarchists have taken part in several revolutions, most notably in the Paris Commune, the Russian Civil War and the Spanish Civil War, whose end marked the end of the classical era of anarchism. In the last decades of the 20th and into the 21st century, the anarchist movement has been resurgent once more.\n Anarchism employs a diversity of tactics in order to meet its ideal ends which can be broadly separated into revolutionary and evolutionary tactics; there is significant overlap between the two, which are merely descriptive. Revolutionary tactics aim to bring down authority and state, having taken a violent turn in the past, while evolutionary tactics aim to prefigure what an anarchist society would be like. Anarchist thought, criticism, and praxis have played a part in diverse areas of human society. Criticism of anarchism include claims that it is internally inconsistent, violent, or utopian.", + "model_name": "Qwen/Qwen3-0.6B", + "lora_path": "charent/self_cognition_Alice", + "lora_name": "Alice", + "event_type": "BlockStored", + "block_hashes": [ + 9643097150530800701, + 11170191873018355659, + 6527130464540768062, + 5266282249679428495, + 841584138981164017, + 2167863935422872178, + 10047306778055275762, + 9832566306835575616, + 13152907232236974528, + 16406591261347721627, + 7927681126437370465, + 14102553466237340935, + 12688422695725635668, + 60694021523729386, + 1853251211306187455, + 15753609530196991075, + 8654018951238099208, + 18153899847325680392, + 9504216802721532657, + 9589072771426370715, + 17521139068014238504, + 17360094881635091534, + 18205002777866313257, + 16504313431429417684, + 8548534288577952324 + ], + "parent_block_hash": null, + "token_ids": [ + 2082, + 1113, + 2142, + 374, + 264, + 4948, + 19128, + 323, + 7203, + 429, + 374, + 66351, + 938, + 315, + 11198, + 323, + 59415, + 678, + 90505, + 11, + 54263, + 533, + 7586, + 315, + 28922, + 13, + 1527, + 1113, + 2142, + 6738, + 369, + 279, + 75345, + 315, + 279, + 1584, + 11, + 892, + 432, + 9982, + 311, + 387, + 25165, + 11, + 76244, + 11, + 323, + 27756, + 13, + 1634, + 264, + 34801, + 2115, + 28380, + 7203, + 11, + 9099, + 389, + 279, + 3041, + 60843, + 2115, + 315, + 279, + 4948, + 19745, + 11, + 432, + 374, + 5990, + 7481, + 16263, + 56837, + 2142, + 323, + 56025, + 82715, + 438, + 279, + 56025, + 20013, + 320, + 2740, + 529, + 8821, + 50518, + 8, + 315, + 279, + 40189, + 7203, + 11, + 323, + 702, + 264, + 3746, + 13656, + 15010, + 448, + 7147, + 97131, + 2142, + 323, + 50518, + 624, + 262, + 64994, + 12163, + 304, + 33675, + 2041, + 15908, + 12412, + 1113, + 550, + 1293, + 1573, + 279, + 21269, + 315, + 15908, + 5302, + 11, + 76463, + 11, + 476, + 976, + 18968, + 13, + 3085, + 279, + 10000, + 315, + 38333, + 69894, + 12866, + 11, + 66351, + 41814, + 8841, + 11198, + 1083, + 16009, + 13, + 10328, + 34383, + 315, + 77331, + 3381, + 525, + 1730, + 6814, + 3840, + 11, + 6481, + 43465, + 2142, + 21992, + 504, + 279, + 91831, + 13, + 11954, + 279, + 15271, + 4279, + 315, + 279, + 220, + 16, + 24, + 339, + 323, + 279, + 1156, + 10793, + 315, + 279, + 220, + 17, + 15, + 339, + 9294, + 11, + 279, + 77331, + 7203, + 19828, + 3304, + 304, + 1429, + 5479, + 315, + 279, + 1879, + 323, + 1030, + 264, + 5089, + 3476, + 304, + 7337, + 6, + 27870, + 369, + 90125, + 48586, + 13, + 39641, + 77331, + 8682, + 315, + 3381, + 14122, + 2337, + 419, + 4168, + 13, + 1527, + 1113, + 1671, + 614, + 4429, + 949, + 304, + 3807, + 92474, + 11, + 1429, + 34046, + 304, + 279, + 12095, + 6804, + 2886, + 11, + 279, + 8522, + 16398, + 5004, + 323, + 279, + 15154, + 16398, + 5004, + 11, + 6693, + 835, + 12864, + 279, + 835, + 315, + 279, + 28824, + 11385, + 315, + 43465, + 2142, + 13, + 758, + 279, + 1537, + 10793, + 315, + 279, + 220, + 17, + 15, + 339, + 323, + 1119, + 279, + 220, + 17, + 16, + 267, + 9294, + 11, + 279, + 77331, + 7203, + 702, + 1012, + 592, + 85053, + 3055, + 803, + 624, + 262, + 1527, + 1113, + 2142, + 50142, + 264, + 19492, + 315, + 25342, + 304, + 1973, + 311, + 3367, + 1181, + 10507, + 10335, + 892, + 646, + 387, + 42929, + 18663, + 1119, + 29091, + 323, + 40893, + 25342, + 26, + 1052, + 374, + 5089, + 27248, + 1948, + 279, + 1378, + 11, + 892, + 525, + 16234, + 52844, + 13, + 66579, + 25342, + 9213, + 311, + 4446, + 1495, + 11198, + 323, + 1584, + 11, + 3432, + 4429, + 264, + 16401, + 2484, + 304, + 279, + 3267, + 11, + 1393, + 40893, + 25342, + 9213, + 311, + 855, + 17781, + 1128, + 458, + 77331, + 8232, + 1035, + 387, + 1075, + 13, + 1527, + 1113, + 380, + 3381, + 11, + 18817, + 11, + 323, + 548, + 7184, + 614, + 6342, + 264, + 949, + 304, + 16807, + 5671, + 315, + 3738, + 8232, + 13, + 33207, + 41814, + 315, + 43465, + 2142, + 2924, + 8186, + 429, + 432, + 374, + 33067, + 39140, + 11, + 16401, + 11, + 476, + 8621 + ], + "block_size": 16, + "medium": "GPU", + "hash_seed": "42" +} \ No newline at end of file