|
1 | 1 | import ctypes |
2 | 2 | import multiprocessing |
| 3 | +from types import SimpleNamespace |
| 4 | +from unittest.mock import Mock |
3 | 5 |
|
4 | 6 | import numpy as np |
5 | 7 | from scipy.special import log_softmax |
|
15 | 17 | MODEL = "./vendor/llama.cpp/models/ggml-vocab-llama-spm.gguf" |
16 | 18 |
|
17 | 19 |
|
| 20 | +class EvalCalled(Exception): |
| 21 | + pass |
| 22 | + |
| 23 | + |
18 | 24 | def test_llama_cpp_version(): |
19 | 25 | assert llama_cpp.__version__ |
20 | 26 |
|
@@ -232,3 +238,75 @@ def test_real_llama_embeddings(llama_cpp_model_path): |
232 | 238 | ) |
233 | 239 | # Smoke test for now |
234 | 240 | model.embed("Hello World") |
| 241 | + |
| 242 | + |
| 243 | +def test_kv_cache_seq_rm_returns_bool(monkeypatch): |
| 244 | + context = internals.LlamaContext.__new__(internals.LlamaContext) |
| 245 | + context.memory = object() |
| 246 | + calls = [] |
| 247 | + |
| 248 | + def fake_llama_memory_seq_rm(memory, seq_id, p0, p1): |
| 249 | + calls.append((memory, seq_id, p0, p1)) |
| 250 | + return True |
| 251 | + |
| 252 | + monkeypatch.setattr(llama_cpp, "llama_memory_seq_rm", fake_llama_memory_seq_rm) |
| 253 | + |
| 254 | + assert context.kv_cache_seq_rm(-1, 4, -1) is True |
| 255 | + assert calls == [(context.memory, 0, 4, -1)] |
| 256 | + |
| 257 | + |
| 258 | +def make_test_llama(kv_cache_seq_rm_return): |
| 259 | + llama = llama_cpp.Llama.__new__(llama_cpp.Llama) |
| 260 | + llama.n_tokens = 3 |
| 261 | + llama.n_batch = 8 |
| 262 | + llama._n_ctx = 32 |
| 263 | + llama._n_vocab = 8 |
| 264 | + llama._logits_all = False |
| 265 | + llama._seed = 1337 |
| 266 | + llama.last_n_tokens_size = 64 |
| 267 | + llama.verbose = False |
| 268 | + llama.input_ids = np.array([1, 2, 3, 0, 0, 0], dtype=np.intc) |
| 269 | + llama.scores = np.zeros((6, 8), dtype=np.single) |
| 270 | + llama._ctx = SimpleNamespace( |
| 271 | + kv_cache_seq_rm=Mock(return_value=kv_cache_seq_rm_return) |
| 272 | + ) |
| 273 | + llama._sampler = None |
| 274 | + llama.eval_tokens_seen = None |
| 275 | + llama.reset_calls = 0 |
| 276 | + |
| 277 | + def reset(): |
| 278 | + llama.reset_calls += 1 |
| 279 | + llama.n_tokens = 0 |
| 280 | + |
| 281 | + def eval_tokens(tokens): |
| 282 | + llama.eval_tokens_seen = list(tokens) |
| 283 | + raise EvalCalled |
| 284 | + |
| 285 | + llama.reset = reset |
| 286 | + llama.eval = eval_tokens |
| 287 | + llama._init_sampler = lambda **kwargs: object() |
| 288 | + return llama |
| 289 | + |
| 290 | + |
| 291 | +def test_generate_reuses_prefix_when_partial_removal_supported(): |
| 292 | + llama = make_test_llama(True) |
| 293 | + |
| 294 | + with pytest.raises(EvalCalled): |
| 295 | + next(llama.generate([1, 2, 3, 4])) |
| 296 | + |
| 297 | + llama._ctx.kv_cache_seq_rm.assert_called_once_with(-1, 3, -1) |
| 298 | + assert llama.reset_calls == 0 |
| 299 | + assert llama.n_tokens == 3 |
| 300 | + assert llama.eval_tokens_seen == [4] |
| 301 | + |
| 302 | + |
| 303 | +def test_generate_falls_back_to_reset_when_partial_removal_rejected(): |
| 304 | + llama = make_test_llama(False) |
| 305 | + |
| 306 | + with pytest.raises(EvalCalled): |
| 307 | + next(llama.generate([1, 2, 3, 4])) |
| 308 | + |
| 309 | + llama._ctx.kv_cache_seq_rm.assert_called_once_with(-1, 3, -1) |
| 310 | + assert llama.reset_calls == 1 |
| 311 | + assert llama.n_tokens == 0 |
| 312 | + assert llama.eval_tokens_seen == [1, 2, 3, 4] |
0 commit comments