Commit 56fd8a375 for llama.cpp
commit 56fd8a375f42dd89cfbbba744f097648214fdd61
Author: Roshan Kumar <52444089+roshankumarg529@users.noreply.github.com>
Date: Sun Nov 15 18:23:05 2026 +0200
server : ignore empty stop strings (#32093)
diff --git a/tests/test-server-component.cpp b/tests/test-server-component.cpp
index 3494cdf04..580203be8 100644
--- a/tests/test-server-component.cpp
+++ b/tests/test-server-component.cpp
@@ -718,6 +718,41 @@ static void test_dry_sequence_breakers(testing & t) {
}
}
+static void test_stop_strings(testing & t) {
+ common_params params_base;
+
+ const auto eval = [&](const json & data) {
+ return server_schema::eval_llama_cmpl_schema(nullptr, params_base, {}, data);
+ };
+
+ {
+ json data;
+ data["stop"] = "";
+ t.assert_true("empty string is ignored", eval(data).antiprompt.empty());
+ }
+
+ {
+ json data;
+ data["stop"] = json::array({""});
+ t.assert_true("empty string in array is ignored", eval(data).antiprompt.empty());
+ }
+
+ {
+ json data;
+ data["stop"] = json::array({"", "zzz"});
+ const std::vector<std::string> expected = {"zzz"};
+ t.assert_true("only non-empty strings are kept", eval(data).antiprompt == expected);
+ }
+
+ {
+ params_base.antiprompt = {"User:"};
+ json data;
+ data["stop"] = json::array({""});
+ const std::vector<std::string> expected = {"User:"};
+ t.assert_true("falls back to CLI defaults", eval(data).antiprompt == expected);
+ }
+}
+
//
// glob_match
//
@@ -842,6 +877,7 @@ int main(int argc, char ** argv) {
t.test("decision_depth", test_decision_depth);
t.test("glob_match", test_glob_match);
t.test("dry_sequence_breakers", test_dry_sequence_breakers);
+ t.test("stop_strings", test_stop_strings);
t.test("speech_convert", test_speech_convert);
t.test("pcm_f32_to_s16", test_pcm_f32_to_s16);
diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp
index 4015e23ea..7c2c35c35 100644
--- a/tools/server/server-context.cpp
+++ b/tools/server/server-context.cpp
@@ -692,6 +692,10 @@ struct server_slot {
size_t stop_pos = std::string::npos;
for (const std::string & word : task->params.antiprompt) {
+ if (word.empty()) {
+ continue;
+ }
+
size_t pos;
if (is_full_stop) {
diff --git a/tools/server/server-schema.cpp b/tools/server/server-schema.cpp
index 76d634d58..84e872f21 100644
--- a/tools/server/server-schema.cpp
+++ b/tools/server/server-schema.cpp
@@ -458,9 +458,9 @@ std::vector<std::unique_ptr<field>> make_llama_cmpl_schema(const common_params &
const auto & stop = data.at("stop");
if (stop.is_array()) {
for (const auto & word : stop) {
- if (!word.empty()) ctx.params.antiprompt.push_back(word);
+ if (!word.empty() && word != "") ctx.params.antiprompt.push_back(word);
}
- } else if (stop.is_string()) {
+ } else if (stop.is_string() && stop != "") {
ctx.params.antiprompt.push_back(stop.get<std::string>());
}
// fall back to CLI defaults if the request provided no effective stop strings
diff --git a/tools/server/tests/unit/test_completion.py b/tools/server/tests/unit/test_completion.py
index 0c5beb23e..618345585 100644
--- a/tools/server/tests/unit/test_completion.py
+++ b/tools/server/tests/unit/test_completion.py
@@ -118,6 +118,20 @@ def test_completion_connection_reuse():
conn.close()
+def test_completion_empty_reverse_prompt():
+ global server
+ server.reverse_prompt = ""
+ server.start()
+ res = server.make_request("POST", "/completion", data={
+ "n_predict": 8,
+ "prompt": "I believe the meaning of life is",
+ })
+ assert res.status_code == 200
+ assert res.body["stop_type"] == "limit"
+ assert res.body["timings"]["predicted_n"] == 8
+ assert match_regex("(going|bed)+", res.body["content"])
+
+
def test_completion_with_openai_library():
global server
server.start()
diff --git a/tools/server/tests/utils.py b/tools/server/tests/utils.py
index 56c88b354..f18b566aa 100644
--- a/tools/server/tests/utils.py
+++ b/tools/server/tests/utils.py
@@ -77,6 +77,7 @@ class ServerProcess:
n_ga: int | None = None
n_ga_w: int | None = None
n_predict: int | None = None
+ reverse_prompt: str | None = None
n_prompts: int | None = 0
slot_save_path: str | None = None
id_slot: int | None = None
@@ -248,6 +249,8 @@ class ServerProcess:
server_args.extend(["-fa", self.fa])
if self.n_predict:
server_args.extend(["--n-predict", self.n_predict])
+ if self.reverse_prompt is not None:
+ server_args.extend(["--reverse-prompt", self.reverse_prompt])
if self.slot_save_path:
server_args.extend(["--slot-save-path", self.slot_save_path])
if self.n_ga: