88static server_prompt make_prompt (
99 int64_t n_tokens,
1010 std::initializer_list<int64_t > checkpoints,
11- size_t checkpoint_size = 0 ) {
11+ size_t checkpoint_size = 0 ,
12+ llama_token token = 1 ) {
1213 server_prompt prompt {
13- server_tokens (llama_tokens (n_tokens, 1 ), false ),
14+ server_tokens (llama_tokens (n_tokens, token ), false ),
1415 {},
1516 };
1617
@@ -23,6 +24,30 @@ static server_prompt make_prompt(
2324 return prompt;
2425}
2526
27+ static bool cache_contains (
28+ const server_prompt_cache & cache,
29+ const server_prompt_cache_state * state) {
30+ for (const auto & cur : cache.states ) {
31+ if (&cur == state) {
32+ return true ;
33+ }
34+ }
35+
36+ return false ;
37+ }
38+
39+ static bool cache_contains_token (
40+ const server_prompt_cache & cache,
41+ llama_token token) {
42+ for (const auto & cur : cache.states ) {
43+ if (!cur.prompt .tokens .empty () && cur.prompt .tokens [0 ] == token) {
44+ return true ;
45+ }
46+ }
47+
48+ return false ;
49+ }
50+
2651int main () {
2752 {
2853 auto prompt = make_prompt (20000 , {4096 , 12000 , 18000 });
@@ -102,5 +127,78 @@ int main() {
102127 assert (state->prompt .checkpoints .size () == prompt.checkpoints .size ());
103128 }
104129
130+ {
131+ server_prompt_cache cache (4 , 10000 );
132+ auto live = make_prompt (1000 , {100 });
133+ auto cached = make_prompt (900 , {});
134+ server_tokens target (llama_tokens (950 , 1 ), false );
135+
136+ auto * cached_state = cache.alloc (cached, 512 *1024 , 0 );
137+ auto * selected = cache.find_better (live, target, true , 1 );
138+
139+ assert (selected == cached_state);
140+ }
141+
142+ {
143+ constexpr size_t mib = 1024 *1024 ;
144+
145+ server_prompt_cache cache (1 , 10000 );
146+ auto cached = make_prompt (900 , {}, 0 , 1 );
147+ auto live = make_prompt (1000 , {}, 0 , 2 );
148+ server_tokens target (llama_tokens (950 , 1 ), false );
149+
150+ auto * cached_state = cache.alloc (cached, 512 *1024 , 0 );
151+ auto * selected = cache.find_better (live, target, true , 1 );
152+ assert (selected == cached_state);
153+
154+ auto * saved_state = cache.alloc (live, 768 *1024 , 0 );
155+ assert (cache.finalize (saved_state, &target, true , selected));
156+
157+ // The selected entry is pinned during the swap. The cache may exceed
158+ // its steady-state budget until that entry is restored and removed.
159+ assert (saved_state != nullptr );
160+ assert (cache_contains (cache, cached_state));
161+ assert (cache_contains (cache, saved_state));
162+ assert (cache.size () == 1280 *1024 );
163+ assert (cache.size () > mib);
164+ }
165+
166+ {
167+ server_prompt_cache cache (2 , 10000 );
168+ auto useful = make_prompt (600 , {}, 0 , 1 );
169+ auto useless = make_prompt (600 , {}, 0 , 2 );
170+ auto current = make_prompt (600 , {}, 0 , 3 );
171+ server_tokens target (llama_tokens (700 , 1 ), false );
172+
173+ auto * useful_state = cache.alloc (useful, 512 *1024 , 0 );
174+ auto * useless_state = cache.alloc (useless, 512 *1024 , 0 );
175+ assert (useful_state != nullptr );
176+ assert (useless_state != nullptr );
177+
178+ auto * current_state = cache.alloc (current, 1280 *1024 , 0 );
179+ assert (cache.finalize (current_state, &target, false ));
180+
181+ assert (current_state != nullptr );
182+ assert (cache_contains (cache, useful_state));
183+ assert (!cache_contains_token (cache, 2 ));
184+ assert (cache_contains (cache, current_state));
185+ }
186+
187+ {
188+ server_prompt_cache cache (4 , 10000 );
189+ auto live = make_prompt (1000 , {322 });
190+ auto cached = make_prompt (900 , {322 });
191+
192+ llama_tokens target_tokens (950 , 1 );
193+ target_tokens[3 ] = 2 ;
194+ server_tokens target (target_tokens, false );
195+
196+ cache.alloc (cached, 512 *1024 , 0 );
197+
198+ // No exact recurrent state exists before the divergence at token 3.
199+ assert (live.reusable_prefix_tokens (3 , target.size (), true ) == 0 );
200+ assert (cache.find_better (live, target, true , 1 ) == nullptr );
201+ }
202+
105203 return 0 ;
106204}
0 commit comments