Commit 42916d83f for llama.cpp
commit 42916d83f4a225e56709f873aa8050ac11f5b6a4
Author: Will <willie37555@gmail.com>
Date: Wed Sep 23 21:28:49 2026 +0800
server: fix token counting API crash on sleep (#29309)
* server: wake up sleeping server correctly
* server: wake up sleeping server correctly (local aliases removed)
diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp
index b6835e434..274af9b05 100644
--- a/tools/server/server-context.cpp
+++ b/tools/server/server-context.cpp
@@ -4944,7 +4944,7 @@ void server_routes::init_routes() {
};
this->post_chat_completions_tok = [this](const server_http_req & req) {
- return handle_count_tokens(ctx_server.vocab, ctx_server.mctx, ctx_server.init_opt, req, TASK_RESPONSE_TYPE_OAI_CHAT);
+ return handle_count_tokens(req, TASK_RESPONSE_TYPE_OAI_CHAT);
};
this->post_control = [this](const server_http_req & req) {
@@ -5003,7 +5003,7 @@ void server_routes::init_routes() {
};
this->post_responses_tok_oai = [this](const server_http_req & req) {
- return handle_count_tokens(ctx_server.vocab, ctx_server.mctx, ctx_server.init_opt, req, TASK_RESPONSE_TYPE_OAI_RESP);
+ return handle_count_tokens(req, TASK_RESPONSE_TYPE_OAI_RESP);
};
this->post_transcriptions_oai = [this](const server_http_req & req) {
@@ -5053,7 +5053,7 @@ void server_routes::init_routes() {
};
this->post_anthropic_count_tokens = [this](const server_http_req & req) {
- return handle_count_tokens(ctx_server.vocab, ctx_server.mctx, ctx_server.init_opt, req, TASK_RESPONSE_TYPE_ANTHROPIC);
+ return handle_count_tokens(req, TASK_RESPONSE_TYPE_ANTHROPIC);
};
// same with handle_chat_completions, but without inference part
@@ -5485,7 +5485,7 @@ std::unique_ptr<server_res_generator> server_routes::handle_embeddings_impl(cons
return res;
}
-std::unique_ptr<server_res_generator> server_routes::handle_count_tokens(const llama_vocab * vocab, mtmd_context * mctx, const mtmd_helper_init_opt & init_opt, const server_http_req & req, task_response_type res_type) {
+std::unique_ptr<server_res_generator> server_routes::handle_count_tokens(const server_http_req & req, task_response_type res_type) {
auto res = create_response();
std::vector<raw_buffer> files;
json body = json::parse(req.body);
@@ -5519,13 +5519,13 @@ std::unique_ptr<server_res_generator> server_routes::handle_count_tokens(const l
// TODO @ngxson : refactor this code block, move this to server-common and reuse it in other places
size_t n_tokens;
- if (mctx != nullptr) {
+ if (ctx_server.mctx != nullptr) {
if (!prompt.is_string()) {
throw std::runtime_error("for mtmd, input prompt must be a string.");
}
- n_tokens = process_mtmd_prompt(mctx, prompt.get<std::string>(), files, init_opt, true).size();
+ n_tokens = process_mtmd_prompt(ctx_server.mctx, prompt.get<std::string>(), files, ctx_server.init_opt, true).size();
} else {
- n_tokens = tokenize_mixed(vocab, prompt, true, true).size();
+ n_tokens = tokenize_mixed(ctx_server.vocab, prompt, true, true).size();
}
json response = {{"input_tokens", static_cast<int64_t>(n_tokens)}};
diff --git a/tools/server/server-context.h b/tools/server/server-context.h
index 0acbbffa9..7265ccad1 100644
--- a/tools/server/server-context.h
+++ b/tools/server/server-context.h
@@ -169,7 +169,7 @@ private:
std::unique_ptr<server_res_generator> handle_slots_restore(const server_http_req & req, int id_slot);
std::unique_ptr<server_res_generator> handle_slots_erase(const server_http_req &, int id_slot);
std::unique_ptr<server_res_generator> handle_embeddings_impl(const server_http_req & req, task_response_type res_type);
- std::unique_ptr<server_res_generator> handle_count_tokens(const llama_vocab * vocab, mtmd_context * mctx, const mtmd_helper_init_opt & init_opt, const server_http_req & req, task_response_type res_type);
+ std::unique_ptr<server_res_generator> handle_count_tokens(const server_http_req & req, task_response_type res_type);
// using unique_ptr to allow late initialization of const
std::unique_ptr<const server_context_meta> meta;
diff --git a/tools/server/tests/unit/test_sleep.py b/tools/server/tests/unit/test_sleep.py
index 515f7077d..4edb26450 100644
--- a/tools/server/tests/unit/test_sleep.py
+++ b/tools/server/tests/unit/test_sleep.py
@@ -125,3 +125,43 @@ def test_server_sleep_metrics_buckets():
assert res.status_code == 200
assert is_sleeping(server) == False
assert get_metric(fetch_metrics(server), "predicted_tokens_seconds") == 0
+
+
+def test_server_sleep_token_counting_wake():
+ global server
+ server.sleep_idle_seconds = 1
+ server.start()
+
+ wait_for_sleep(server)
+ assert is_sleeping(server)
+
+ res = server.make_request("POST", "/chat/completions/input_tokens", data={
+ "messages": [
+ {"role": "user", "content": "Hello world"}
+ ]
+ })
+ assert res.status_code == 200
+ assert res.body["input_tokens"] > 0
+ assert is_sleeping(server) == False
+
+ wait_for_sleep(server)
+ assert is_sleeping(server)
+
+ res = server.make_request("POST", "/v1/responses/input_tokens", data={
+ "input": "Hello world"
+ })
+ assert res.status_code == 200
+ assert res.body["input_tokens"] > 0
+ assert is_sleeping(server) == False
+
+ wait_for_sleep(server)
+ assert is_sleeping(server)
+
+ res = server.make_request("POST", "/v1/messages/count_tokens", data={
+ "messages": [
+ {"role": "user", "content": "Hello world"}
+ ]
+ })
+ assert res.status_code == 200
+ assert res.body["input_tokens"] > 0
+ assert is_sleeping(server) == False