diff --git a/tests/test-chat.cpp b/tests/test-chat.cpp index e1e0a59e6..fc8df4fb5 100644 --- a/tests/test-chat.cpp +++ b/tests/test-chat.cpp @@ -5911,6 +5911,71 @@ static void test_developer_role_to_system_workaround() { } } +static void test_reasoning_budget_tokens_per_request() { + LOG_DBG("%s\n", __func__); + // Use Qwen3 template which has ... reasoning markers. + // The autoparser detects them and sets thinking_start/end_tag, which enables + // the reasoning-budget code path in oaicompat_chat_params_parse. + auto tmpls = read_templates("models/templates/Qwen-Qwen3-0.6B.jinja"); + + server_chat_params opt; + opt.tmpls = std::move(tmpls); + opt.use_jinja = true; + opt.enable_thinking = true; + opt.reasoning_budget = -1; + opt.reasoning_format = COMMON_REASONING_FORMAT_NONE; + + // Body with per-request reasoning_budget_tokens=0 (suppress thinking). + json body = { + {"messages", json::array({json{{"role", "user"}, {"content", "hello"}}})}, + {"reasoning_budget_tokens", 0}, + }; + std::vector out_files; + auto llama_params = oaicompat_chat_params_parse(body, opt, out_files); + + // The per-request value must win over the server default (-1). + if (!llama_params.contains("reasoning_budget_tokens")) { + throw std::runtime_error("reasoning_budget_tokens missing from llama_params (thinking_end_tag may be empty for this template)"); + } + int got = llama_params["reasoning_budget_tokens"].get(); + if (got != 0) { + throw std::runtime_error(std::string("Expected reasoning_budget_tokens=0, got ") + std::to_string(got)); + } +} + +static void test_reasoning_budget_message_per_request() { + LOG_DBG("%s\n", __func__); + // Same code path as test_reasoning_budget_tokens_per_request: the Qwen3 template's + // ... markers enable the reasoning-budget block in oaicompat_chat_params_parse. + auto tmpls = read_templates("models/templates/Qwen-Qwen3-0.6B.jinja"); + + server_chat_params opt; + opt.tmpls = std::move(tmpls); + opt.use_jinja = true; + opt.enable_thinking = true; + opt.reasoning_budget = -1; + opt.reasoning_format = COMMON_REASONING_FORMAT_NONE; + opt.reasoning_budget_message = "server default"; + + // Body with a per-request reasoning_budget_message override. + const std::string per_request_message = "per-request message"; + json body = { + {"messages", json::array({json{{"role", "user"}, {"content", "hello"}}})}, + {"reasoning_budget_message", per_request_message}, + }; + std::vector out_files; + auto llama_params = oaicompat_chat_params_parse(body, opt, out_files); + + // The per-request value must win over the server default. + if (!llama_params.contains("reasoning_budget_message")) { + throw std::runtime_error("reasoning_budget_message missing from llama_params (thinking_end_tag may be empty for this template)"); + } + std::string got = llama_params["reasoning_budget_message"].get(); + if (got != per_request_message) { + throw std::runtime_error("Expected reasoning_budget_message='" + per_request_message + "', got '" + got + "'"); + } +} + static void test_msg_diffs_compute() { LOG_DBG("%s\n", __func__); { @@ -6068,6 +6133,8 @@ int main(int argc, char ** argv) { test_convert_responses_to_chatcmpl(); test_developer_role_to_system_workaround(); test_template_generation_prompt(); + test_reasoning_budget_tokens_per_request(); + test_reasoning_budget_message_per_request(); test_template_output_peg_parsers(detailed_debug); std::cout << "\n[chat] All tests passed!" << '\n'; } diff --git a/tools/server/server-common.cpp b/tools/server/server-common.cpp index c76ed7cc2..78b5c6819 100644 --- a/tools/server/server-common.cpp +++ b/tools/server/server-common.cpp @@ -1117,7 +1117,8 @@ json oaicompat_chat_params_parse( // Reasoning budget: pass parameters through to sampling layer { - int reasoning_budget = json_value(body, "thinking_budget_tokens", -1); + int reasoning_budget = json_value(body, "reasoning_budget_tokens", + json_value(body, "thinking_budget_tokens", -1)); if (reasoning_budget == -1) { reasoning_budget = opt.reasoning_budget; } @@ -1126,7 +1127,7 @@ json oaicompat_chat_params_parse( llama_params["reasoning_budget_tokens"] = reasoning_budget; llama_params["reasoning_budget_start_tag"] = chat_params.thinking_start_tag; llama_params["reasoning_budget_end_tag"] = chat_params.thinking_end_tag; - llama_params["reasoning_budget_message"] = opt.reasoning_budget_message; + llama_params["reasoning_budget_message"] = json_value(body, "reasoning_budget_message", opt.reasoning_budget_message); llama_params["reasoning_control"] = json_value(body, "reasoning_control", false); } }