Commit 5d806aa25 for llama.cpp

commit 5d806aa2575e01e126651fd69ab1ab6cefff861d
Author: Foad Abo Dahood <32059146+masterFoad@users.noreply.github.com>
Date:   Tue Sep 8 16:01:03 2026 +0300

    server : apply checkpoint min-step eviction only when the checkpoint list is full (#28302)

    The spacing eviction in create_checkpoint() keeps the oldest checkpoint and
    erases every later one within checkpoint_min_step of it. For prompts shorter
    than checkpoint_min_step this drops the checkpoint at n_tokens - 4 that the
    next request resumes from, so hybrid/recurrent models re-prefill from the
    previous checkpoint instead. Apply the spacing rule only once the list is at
    n_ctx_checkpoints, and replace an existing checkpoint at the same n_tokens
    instead of appending a duplicate.

diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp
index f78cfb36d..fe068d3e9 100644
--- a/tools/server/server-context.cpp
+++ b/tools/server/server-context.cpp
@@ -2311,8 +2311,11 @@ private:

         // evict checkpoints within min-step of a previous checkpoint, unless they were
         // created by the current task
+        // only when the list is full, otherwise short prompts keep just the oldest checkpoint
         int64_t last = -1;
-        for (auto it = slot.prompt.checkpoints.begin(); it != slot.prompt.checkpoints.end(); ) {
+        for (auto it = slot.prompt.checkpoints.begin();
+                slot.prompt.checkpoints.size() + 1 >= (size_t) params_base.n_ctx_checkpoints &&
+                it != slot.prompt.checkpoints.end(); ) {
             if (it->id_task != id_task && last >= 0 && it->n_tokens <= last + params_base.checkpoint_min_step) {
                 SLT_TRC(slot, "erasing context checkpoint too close to an earlier one (pos_min = %d, pos_max = %d, n_tokens = %" PRId64 ", size = %.3f MiB)\n",
                         it->pos_min, it->pos_max, it->n_tokens, (float) it->size() / 1024 / 1024);
@@ -2335,6 +2338,19 @@ private:
             slot.prompt.checkpoints.erase(slot.prompt.checkpoints.begin());
         }

+        // replace an existing checkpoint at the same n_tokens instead of appending a duplicate
+        {
+            const int64_t n_tokens_new = slot.prompt.n_tokens() - n_tokens_cur;
+            for (auto it = slot.prompt.checkpoints.begin(); it != slot.prompt.checkpoints.end(); ) {
+                if (it->n_tokens == n_tokens_new) {
+                    SLT_TRC(slot, "superseding context checkpoint at n_tokens = %" PRId64 "\n", it->n_tokens);
+                    it = slot.prompt.checkpoints.erase(it);
+                } else {
+                    ++it;
+                }
+            }
+        }
+
         auto & cur = slot.prompt.checkpoints.emplace_back();

         cur.id_task = id_task;