diff --git a/common/chat-diff-analyzer.cpp b/common/chat-diff-analyzer.cpp
index 2f0bd14af..264ace462 100644
--- a/common/chat-diff-analyzer.cpp
+++ b/common/chat-diff-analyzer.cpp
@@ -296,7 +296,7 @@ void analyze_reasoning::compare_reasoning_presence() {
return p.literal(reasoning_content) + p.space() + p.optional(p.tag("post", (p.marker() + p.space())) + p.rest());
});
auto parser_wrapped = build_tagged_peg_parser([&](common_peg_parser_builder &p) {
- return p.tag("pre", p.marker() + p.space()) + p.literal(reasoning_content) + p.space() + p.tag("post", (p.marker() + p.space())) + p.rest();
+ return p.tag("pre", p.marker() + p.space()) + p.literal(reasoning_content) + p.tag("post", (p.space() + p.marker() + p.space())) + p.rest();
});
// try the more aggressive parse first, if it fails, fall back to the delimiter one
auto result = parser_wrapped.parse_anywhere_and_extract(comparison->output_B);
@@ -306,11 +306,11 @@ void analyze_reasoning::compare_reasoning_presence() {
if (result.result.success()) {
if (!result.tags["pre"].empty() && !result.tags["post"].empty()) {
mode = reasoning_mode::TAG_BASED;
- start = trim_leading_whitespace(result.tags["pre"]);
- end = trim_trailing_whitespace(result.tags["post"]);
+ start = result.tags["pre"];
+ end = result.tags["post"];
} else if (!result.tags["post"].empty()) {
mode = reasoning_mode::TAG_BASED;
- end = trim_trailing_whitespace(result.tags["post"]);
+ end = result.tags["post"];
}
}
}
diff --git a/tests/test-chat-auto-parser.cpp b/tests/test-chat-auto-parser.cpp
index bb23b7f2a..1d96de718 100644
--- a/tests/test-chat-auto-parser.cpp
+++ b/tests/test-chat-auto-parser.cpp
@@ -1331,7 +1331,7 @@ static void test_nemotron_reasoning_detection(testing & t) {
// Check reasoning markers
t.assert_equal("reasoning_start should be '\\n'", "\n", analysis.reasoning.start);
- t.assert_equal("reasoning_end should be ''", "", analysis.reasoning.end);
+ t.assert_equal("reasoning_end should be '\\n\\n'", "\n\n", analysis.reasoning.end);
// Check reasoning mode detection
// Nemotron uses tag-based reasoning; prefill handles the template's forced markers
diff --git a/tests/test-chat.cpp b/tests/test-chat.cpp
index 52b480c24..b0545dc95 100644
--- a/tests/test-chat.cpp
+++ b/tests/test-chat.cpp
@@ -1642,22 +1642,16 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
// Qwen3.5 (basically same as Nemotron, but keeping separate tests just in case)
auto tst = peg_tester("models/templates/Qwen3.5-4B.jinja", detailed_debug);
- tst.test("I'm\nthinkingHello, world!\nWhat's up?")
+ tst.test("I'm\nthinking\n\n\nHello, world!\nWhat's up?")
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
.enable_thinking(true)
.expect(message_assist_thoughts)
.run();
- tst.test("I'm\nthinking\n\nHello, world!\nWhat's up?")
+ tst.test("I'm\nthinking\n\n\nHello, world!\nWhat's up?")
.enable_thinking(true)
.reasoning_format(COMMON_REASONING_FORMAT_NONE)
- .expect_content("\nI'm\nthinking\n\nHello, world!\nWhat's up?")
- .run();
-
- tst.test("I'm\nthinking\n\nHello, world!\nWhat's up?")
- .enable_thinking(true)
- .reasoning_format(COMMON_REASONING_FORMAT_AUTO)
- .expect(message_assist_thoughts)
+ .expect_content("\nI'm\nthinking\n\n\nHello, world!\nWhat's up?")
.run();
tst.test(
@@ -1673,7 +1667,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
.run();
tst.test(
- "I'm\nthinking\n\n"
+ "I'm\nthinking\n\n\n"
"\n"
"\n"
"\n1\n\n"
@@ -1731,7 +1725,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
tst.test(
"I need to output the invoice details in JSON\n"
- "\n"
+ "\n\n"
R"({"amount": 123.45, "date": "2025-12-03"})")
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
.enable_thinking(true)
@@ -1751,7 +1745,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
"hello()\n"
"\n"
"\n"
- "\n"
+ "\n\n\n"
"\n"
"\n"
"\n"
@@ -1994,7 +1988,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
"hello()\n"
"\n"
"\n"
- "\n"
+ "\n\n"
"\n"
"\n"
"\n"
@@ -3463,7 +3457,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
.run();
// Tool call with reasoning (enable_thinking=true)
- tst.test("I'm\nthinking\n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}")
+ tst.test("I'm\nthinking\n\n\n\n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}")
.enable_thinking(true)
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
.tools({ special_function_tool })
@@ -3487,7 +3481,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
.run();
// Tool call with reasoning and content
- tst.test("I need to call a function"
+ tst.test("I need to call a function\n\n\n"
"Let me check the time.\n{\"name\": \"get_time\", \"arguments\": {\"city\": \"XYZCITY\"}}")
.enable_thinking(true)
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
@@ -3514,7 +3508,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
// fake tool call marker in reasoning
tst.test(
- "Let me think about \n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 2}} hmm"
+ "Let me think about \n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 2}} hmm\n\n\n"
"\n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}")
.enable_thinking(true)
.reasoning_format(COMMON_REASONING_FORMAT_AUTO)
@@ -3542,11 +3536,11 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
// Format: value
{
auto tst = peg_tester("models/templates/MiniMax-M2.jinja", detailed_debug);
- tst.test("Hello, world!\nWhat's up?").enable_thinking(true).reasoning_format(COMMON_REASONING_FORMAT_AUTO).expect(message_assist).run();
+ tst.test("\n\n\nHello, world!\nWhat's up?").enable_thinking(true).reasoning_format(COMMON_REASONING_FORMAT_AUTO).expect(message_assist).run();
- tst.test("I'm\nthinkingHello, world!\nWhat's up?").enable_thinking(true).reasoning_format(COMMON_REASONING_FORMAT_AUTO).expect(message_assist_thoughts).run();
+ tst.test("I'm\nthinking\n\n\nHello, world!\nWhat's up?").enable_thinking(true).reasoning_format(COMMON_REASONING_FORMAT_AUTO).expect(message_assist_thoughts).run();
- tst.test("Let's call a tool:\n\n\n").
+ tst.test("Let's call a tool:\n\n\n\n\n\n").
enable_thinking(true).
reasoning_format(COMMON_REASONING_FORMAT_AUTO).
tools({ empty_args_tool }).
@@ -3554,7 +3548,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
run();
tst.test(
- "\n\n\n\n\n\n1\n\n")
.tools({ special_function_tool })
.expect(message_assist_call)
@@ -3714,7 +3708,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
.enable_thinking(false)
.expect(message_assist)
.run();
- tst.test("I'm\nthinking\n\nHello, world!\nWhat's up?")
+ tst.test("I'm\nthinking\n\n\nHello, world!\nWhat's up?")
.enable_thinking(true)
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
.expect(message_assist_thoughts)
@@ -3729,7 +3723,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
.tools({ special_function_tool })
.expect(message_assist_call_content)
.run();
- tst.test("I'm\nthinking\n\n\n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n")
+ tst.test("I'm\nthinking\n\n\n\n{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n")
.enable_thinking(true)
.reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK)
.tools({ special_function_tool })
@@ -4006,7 +4000,8 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
{
auto tst = peg_tester("models/templates/StepFun3.5-Flash.jinja", detailed_debug);
- tst.test("I was thinking\nNow I'm not.").
+
+ tst.test("I was thinking\n\nNow I'm not.").
enable_thinking(true).
reasoning_format(COMMON_REASONING_FORMAT_DEEPSEEK).
expect_reasoning("I was thinking").