]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
cli : fix reasoning responses in CLI (#18961)
authorXuan-Son Nguyen <redacted>
Tue, 20 Jan 2026 17:23:25 +0000 (18:23 +0100)
committerGitHub <redacted>
Tue, 20 Jan 2026 17:23:25 +0000 (18:23 +0100)
* cli : fix reasoning responses in CLI

* fix build

* fix build (2)

12 files changed:
common/chat-parser.cpp
common/chat-parser.h
common/chat.h
common/common.h
common/json-partial.h
tests/test-chat-parser.cpp
tests/test-chat-peg-parser.cpp
tests/test-chat.cpp
tools/cli/cli.cpp
tools/server/server-common.h
tools/server/server-task.cpp
tools/server/server-task.h

index 2f073512e0d71520bc90a92ec3ba874c52e75ef3..c2d1e30f35e4365a149c4df90c012b5018912884 100644 (file)
@@ -129,7 +129,7 @@ static void parse_json_tool_calls(
     }
 }
 
-common_chat_msg_parser::common_chat_msg_parser(const std::string & input, bool is_partial, const common_chat_syntax & syntax)
+common_chat_msg_parser::common_chat_msg_parser(const std::string & input, bool is_partial, const common_chat_parser_params & syntax)
     : input_(input), is_partial_(is_partial), syntax_(syntax)
 {
     result_.role = "assistant";
@@ -1611,7 +1611,7 @@ static void common_chat_parse(common_chat_msg_parser & builder) {
     builder.finish();
 }
 
-common_chat_msg common_chat_parse(const std::string & input, bool is_partial, const common_chat_syntax & syntax) {
+common_chat_msg common_chat_parse(const std::string & input, bool is_partial, const common_chat_parser_params & syntax) {
     if (syntax.format == COMMON_CHAT_FORMAT_PEG_SIMPLE ||
         syntax.format == COMMON_CHAT_FORMAT_PEG_NATIVE ||
         syntax.format == COMMON_CHAT_FORMAT_PEG_CONSTRUCTED) {
@@ -1635,7 +1635,7 @@ common_chat_msg common_chat_parse(const std::string & input, bool is_partial, co
     return msg;
 }
 
-common_chat_msg common_chat_peg_parse(const common_peg_arena & parser, const std::string & input, bool is_partial, const common_chat_syntax & syntax) {
+common_chat_msg common_chat_peg_parse(const common_peg_arena & parser, const std::string & input, bool is_partial, const common_chat_parser_params & syntax) {
     if (parser.empty()) {
         throw std::runtime_error("Failed to parse due to missing parser definition.");
     }
index 78c4b74c2dbe46f40b43ed3d345282c5196e83dc..3ed9c30a2b3bdafb11514523d560785b0b790507 100644 (file)
@@ -5,7 +5,7 @@
 #include "json-partial.h"
 #include "regex-partial.h"
 
-#include <nlohmann/json.hpp>
+#include <nlohmann/json_fwd.hpp>
 
 #include <optional>
 #include <string>
@@ -19,20 +19,20 @@ class common_chat_msg_partial_exception : public std::runtime_error {
 class common_chat_msg_parser {
     std::string input_;
     bool is_partial_;
-    common_chat_syntax syntax_;
+    common_chat_parser_params syntax_; // TODO: rename to params
     std::string healing_marker_;
 
     size_t pos_ = 0;
     common_chat_msg result_;
 
   public:
-    common_chat_msg_parser(const std::string & input, bool is_partial, const common_chat_syntax & syntax);
+    common_chat_msg_parser(const std::string & input, bool is_partial, const common_chat_parser_params & syntax);
     const std::string & input() const { return input_; }
     size_t pos() const { return pos_; }
     const std::string & healing_marker() const { return healing_marker_; }
     const bool & is_partial() const { return is_partial_; }
     const common_chat_msg & result() const { return result_; }
-    const common_chat_syntax & syntax() const { return syntax_; }
+    const common_chat_parser_params & syntax() const { return syntax_; }
 
     void move_to(size_t pos) {
         if (pos > input_.size()) {
index 1488017382fd551fb25ac086ee5150adaf943812..ac19348ece7968b2f63d0bafc6dcce1971e0302e 100644 (file)
@@ -145,7 +145,7 @@ struct common_chat_templates_inputs {
     std::vector<common_chat_tool> tools;
     common_chat_tool_choice tool_choice = COMMON_CHAT_TOOL_CHOICE_AUTO;
     bool parallel_tool_calls = false;
-    common_reasoning_format reasoning_format = COMMON_REASONING_FORMAT_NONE;
+    common_reasoning_format reasoning_format = COMMON_REASONING_FORMAT_NONE; // TODO: refactor this to "bool enable_thinking"
     bool enable_thinking = true;
     std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
     std::map<std::string, std::string> chat_template_kwargs;
@@ -165,14 +165,21 @@ struct common_chat_params {
     std::string                         parser;
 };
 
-struct common_chat_syntax {
+// per-message parsing syntax
+// should be derived from common_chat_params
+struct common_chat_parser_params {
     common_chat_format       format                = COMMON_CHAT_FORMAT_CONTENT_ONLY;
-    common_reasoning_format  reasoning_format      = COMMON_REASONING_FORMAT_NONE;
+    common_reasoning_format  reasoning_format      = COMMON_REASONING_FORMAT_NONE; // TODO: refactor this to "bool parse_reasoning"
     // Whether reasoning_content should be inlined in the content (e.g. for reasoning_format=deepseek in stream mode)
     bool                     reasoning_in_content  = false;
     bool                     thinking_forced_open  = false;
     bool                     parse_tool_calls      = true;
     common_peg_arena         parser                = {};
+    common_chat_parser_params() = default;
+    common_chat_parser_params(const common_chat_params & chat_params) {
+        format               = chat_params.format;
+        thinking_forced_open = chat_params.thinking_forced_open;
+    }
 };
 
 // Check if the template supplied via "--chat-template" is supported or not. Returns true if it's valid
@@ -213,10 +220,12 @@ std::string common_chat_format_example(
     const std::map<std::string, std::string> & chat_template_kwargs);
 
 const char*               common_chat_format_name(common_chat_format format);
-const char*               common_reasoning_format_name(common_reasoning_format format);
-common_reasoning_format   common_reasoning_format_from_name(const std::string & format);
-common_chat_msg           common_chat_parse(const std::string & input, bool is_partial, const common_chat_syntax & syntax);
-common_chat_msg           common_chat_peg_parse(const common_peg_arena & parser, const std::string & input, bool is_partial, const common_chat_syntax & syntax);
+common_chat_msg           common_chat_parse(const std::string & input, bool is_partial, const common_chat_parser_params & syntax);
+common_chat_msg           common_chat_peg_parse(const common_peg_arena & parser, const std::string & input, bool is_partial, const common_chat_parser_params & syntax);
+
+// used by arg and server
+const char *             common_reasoning_format_name(common_reasoning_format format);
+common_reasoning_format  common_reasoning_format_from_name(const std::string & format);
 
 common_chat_tool_choice common_chat_tool_choice_parse_oaicompat(const std::string & tool_choice);
 
index b9566df62cf126039665d2556da6443b270cf298..8247949ded14cd8c9f840bf9cb6602b90d02a6b0 100644 (file)
@@ -284,6 +284,7 @@ struct common_params_diffusion {
 };
 
 // reasoning API response format (not to be confused as chat template's reasoning format)
+// only used by server
 enum common_reasoning_format {
     COMMON_REASONING_FORMAT_NONE,
     COMMON_REASONING_FORMAT_AUTO,            // Same as deepseek, using `message.reasoning_content`
index f63356dc48f7843ce0a11e424397d442e4337fdd..be51aabfbf418342a0205bb3208c1eb3d265a75a 100644 (file)
@@ -1,5 +1,6 @@
 #pragma once
 
+// TODO: use json_fwd.hpp when possible
 #include <nlohmann/json.hpp>
 
 // Healing marker (empty if the JSON was fully parsed / wasn't healed).
index 4766518fe69550a19743934e74fc848da31f0016..6f44a2b4211a3f5e0fbad3d2287f1303d8ffa7ee 100644 (file)
@@ -54,113 +54,109 @@ static void assert_throws(const std::function<void()> & fn, const std::string &
 static void test_reasoning() {
   //common_log_set_verbosity_thold(LOG_DEFAULT_DEBUG);
   {
-    common_chat_msg_parser builder("<tnk>Cogito</tnk>Ergo sum", /* is_partial= */ false, {
-        /* .format = */ COMMON_CHAT_FORMAT_CONTENT_ONLY,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_NONE,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ false,
-    });
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    params.reasoning_format = COMMON_REASONING_FORMAT_NONE;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = false;
+    common_chat_msg_parser builder("<tnk>Cogito</tnk>Ergo sum", /* is_partial= */ false, params);
     assert_equals(false, builder.try_parse_reasoning("<tnk>", "</tnk>"));
     assert_equals("<tnk>Cogito</tnk>Ergo sum", builder.consume_rest());
   }
   {
-    common_chat_msg_parser builder("<tnk>Cogito</tnk>Ergo sum", /* is_partial= */ false, {
-        /* .format = */ COMMON_CHAT_FORMAT_CONTENT_ONLY,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ false,
-    });
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = false;
+    common_chat_msg_parser builder("<tnk>Cogito</tnk>Ergo sum", /* is_partial= */ false, params);
     assert_equals(true, builder.try_parse_reasoning("<tnk>", "</tnk>"));
     assert_equals(std::string("Cogito"), builder.result().reasoning_content);
     assert_equals("Ergo sum", builder.consume_rest());
   }
   {
-    common_chat_msg_parser builder("Cogito</tnk>Ergo sum", /* is_partial= */ false, {
-        /* .format = */ COMMON_CHAT_FORMAT_CONTENT_ONLY,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_NONE,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ false,
-    });
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    params.reasoning_format = COMMON_REASONING_FORMAT_NONE;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = false;
+    common_chat_msg_parser builder("Cogito</tnk>Ergo sum", /* is_partial= */ false, params);
     assert_equals(false, builder.try_parse_reasoning("<tnk>", "</tnk>"));
     assert_equals("Cogito</tnk>Ergo sum", builder.consume_rest());
   }
   {
-    common_chat_msg_parser builder("Cogito</tnk>Ergo sum", /* is_partial= */ false, {
-        /* .format = */ COMMON_CHAT_FORMAT_CONTENT_ONLY,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ true,
-    });
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = true;
+    common_chat_msg_parser builder("Cogito</tnk>Ergo sum", /* is_partial= */ false, params);
     assert_equals(true, builder.try_parse_reasoning("<tnk>", "</tnk>"));
     assert_equals(std::string("Cogito"), builder.result().reasoning_content);
     assert_equals("Ergo sum", builder.consume_rest());
   }
   {
-    common_chat_msg_parser builder("Cogito</tnk>Ergo sum", /* is_partial= */ false, {
-        /* .format = */ COMMON_CHAT_FORMAT_CONTENT_ONLY,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ true,
-        /* .thinking_forced_open = */ true,
-    });
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = true;
+    params.thinking_forced_open = true;
+    common_chat_msg_parser builder("Cogito</tnk>Ergo sum", /* is_partial= */ false, params);
     assert_equals(true, builder.try_parse_reasoning("<tnk>", "</tnk>"));
     assert_equals("<think>Cogito</think>", builder.result().content);
     assert_equals("Ergo sum", builder.consume_rest());
   }
   {
     const std::string variant("content_only_inline_think");
-    common_chat_syntax syntax = {
-        /* .format = */ COMMON_CHAT_FORMAT_CONTENT_ONLY,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ false,
-        /* .parse_tool_calls = */ false,
-    };
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = false;
+    params.parse_tool_calls = false;
     const std::string input = "<think>Pense</think>Bonjour";
-    auto msg = common_chat_parse(input, false, syntax);
+    auto msg = common_chat_parse(input, false, params);
     assert_equals(variant, std::string("Pense"), msg.reasoning_content);
     assert_equals(variant, std::string("Bonjour"), msg.content);
   }
   {
     const std::string variant("llama_3_inline_think");
-    common_chat_syntax syntax = {
-        /* .format = */ COMMON_CHAT_FORMAT_LLAMA_3_X,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ false,
-        /* .parse_tool_calls = */ false,
-    };
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_LLAMA_3_X;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = false;
+    params.parse_tool_calls = false;
     const std::string input = "<think>Plan</think>Réponse";
-    auto msg = common_chat_parse(input, false, syntax);
+    auto msg = common_chat_parse(input, false, params);
     assert_equals(variant, std::string("Plan"), msg.reasoning_content);
     assert_equals(variant, std::string("Réponse"), msg.content);
   }
   // Test DeepSeek V3.1 parsing - reasoning content followed by "</think>" and then regular content
   {
-    common_chat_syntax syntax = {
-        /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ true,
-        /* .parse_tool_calls = */ true,
-    };
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = true;
+    params.parse_tool_calls = true;
     const std::string variant("deepseek_v3_1_reasoning_format_deepseek");
-    common_chat_msg_parser builder("REASONING</think>ok", /* is_partial= */ false, syntax);
+    common_chat_msg_parser builder("REASONING</think>ok", /* is_partial= */ false, params);
     assert_equals(variant, true, builder.try_parse_reasoning("<think>", "</think>"));
     assert_equals(variant, std::string("REASONING"), builder.result().reasoning_content);
     assert_equals(variant, std::string("ok"), builder.consume_rest());
   }
   // Test DeepSeek V3.1 parsing - reasoning_format none - reasoning content followed by "</think>" and then regular content
   {
-    common_chat_syntax syntax = {
-        /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_NONE,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ true,
-        /* .parse_tool_calls = */ true,
-    };
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+    params.reasoning_format = COMMON_REASONING_FORMAT_NONE;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = true;
+    params.parse_tool_calls = true;
     const std::string variant("deepseek_v3_1_reasoning_format_none");
     const std::string input = "REASONING</think>ok";
-    auto msg = common_chat_parse(input, false, syntax);
+    auto msg = common_chat_parse(input, false, params);
     assert_equals(variant, std::string("REASONING</think>ok"), msg.content);
     assert_equals(variant, std::string(""), msg.reasoning_content);
   }
@@ -256,15 +252,14 @@ static void test_deepseek_v3_1_tool_calls() {
     //common_log_set_verbosity_thold(LOG_DEFAULT_DEBUG);
     // variant: happy path for when it works as the model card says it should
     const std::string variant("simple");
-    common_chat_syntax syntax = {
-        /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-        /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-        /* .reasoning_in_content = */ false,
-        /* .thinking_forced_open = */ false,
-        /* .parse_tool_calls = */ true,
-    };
+    common_chat_parser_params params;
+    params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+    params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+    params.reasoning_in_content = false;
+    params.thinking_forced_open = false;
+    params.parse_tool_calls = true;
     const std::string input = "<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>";
-    auto msg = common_chat_parse(input, false, syntax);
+    auto msg = common_chat_parse(input, false, params);
     assert_equals<std::size_t>(variant, 1, msg.tool_calls.size());
     assert_equals(variant, std::string("get_time"), msg.tool_calls[0].name);
     // JSON arguments are dumped without spaces
@@ -274,16 +269,15 @@ static void test_deepseek_v3_1_tool_calls() {
 
     // variant: simple + thinking open
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ true,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = true;
+        params.parse_tool_calls = true;
         const std::string variant("simple_thinking");
         const std::string in = "REASONING</think><|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>";
-        auto m = common_chat_parse(in, false, syntax);
+        auto m = common_chat_parse(in, false, params);
         assert_equals<std::size_t>(variant, 1, m.tool_calls.size());
         assert_equals(variant, std::string("get_time"), m.tool_calls[0].name);
         assert_equals(variant, std::string("{\"city\":\"Tokyo\"}"), m.tool_calls[0].arguments);
@@ -292,16 +286,15 @@ static void test_deepseek_v3_1_tool_calls() {
     }
     // variant: simple + multiple tool calls
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ false,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = false;
+        params.parse_tool_calls = true;
         const std::string variant("simple_multiple_tool_calls");
         const std::string in = "CONTENT<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Paris\"}<|tool▁call▁end|><|tool▁call▁begin|>get_weather<|tool▁sep|>{\"city\": \"Paris\"}<|tool▁call▁end|><|tool▁calls▁end|>";
-        auto m = common_chat_parse(in, false, syntax);
+        auto m = common_chat_parse(in, false, params);
         assert_equals<std::size_t>(variant, 2, m.tool_calls.size());
         assert_equals(variant, std::string("get_time"), m.tool_calls[0].name);
         assert_equals(variant, std::string("{\"city\":\"Paris\"}"), m.tool_calls[0].arguments);
@@ -314,16 +307,15 @@ static void test_deepseek_v3_1_tool_calls() {
 
     // variant: thinking forced open + tool call in reasoning content
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ true,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = true;
+        params.parse_tool_calls = true;
         const std::string variant("thinking_forced_open_tool_call_in_reasoning");
         const std::string in = "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time2<|tool▁sep|>{\"city\": \"Tokyo2\"}<|tool▁call▁end|><|tool▁calls▁end|>REASONING</think><|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>";
-        auto m = common_chat_parse(in, false, syntax);
+        auto m = common_chat_parse(in, false, params);
         assert_equals<std::size_t>(variant, 1, m.tool_calls.size());
         assert_equals(variant, std::string("get_time"), m.tool_calls[0].name);
         assert_equals(variant, std::string("{\"city\":\"Tokyo\"}"), m.tool_calls[0].arguments);
@@ -336,16 +328,15 @@ static void test_deepseek_v3_1_tool_calls() {
     //          to make tool calls in reasoning content according to the model card, but it does sometimes, so
     //          add the reasoning content as regular content and parse the tool calls.
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ true,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = true;
+        params.parse_tool_calls = true;
         const std::string variant("thinking_forced_open_tool_call_in_reasoning_no_closing_think_not_partial");
         const std::string in = "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>";
-        auto m = common_chat_parse(in, false, syntax);
+        auto m = common_chat_parse(in, false, params);
         assert_equals(variant, std::string("REASONING"), m.content);
         assert_equals(variant, std::string(""), m.reasoning_content);
         assert_equals<std::size_t>(variant, 1, m.tool_calls.size());
@@ -355,16 +346,15 @@ static void test_deepseek_v3_1_tool_calls() {
 
     // variant: thinking forced open + tool call in reasoning content + no closing think + partial
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ true,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = true;
+        params.parse_tool_calls = true;
         const std::string variant("thinking_forced_open_tool_call_in_reasoning_no_closing_think_partial");
         const std::string in = "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>";
-        auto m = common_chat_parse(in, /* is_partial= */ true, syntax);
+        auto m = common_chat_parse(in, /* is_partial= */ true, params);
         assert_equals(variant, std::string("REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>"), m.reasoning_content);
         assert_equals(variant, std::string(""), m.content);
         assert_equals<std::size_t>(variant, 0, m.tool_calls.size());
@@ -372,32 +362,30 @@ static void test_deepseek_v3_1_tool_calls() {
 
     // variant: thinking not forced open + reasoning + regular content + no tool calls
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ true,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = true;
+        params.parse_tool_calls = true;
         const std::string variant("thinking_forced_open_reasoning_regular_content_no_tool_calls");
         const std::string in = "REASONING</think>CONTENT";
-        auto m = common_chat_parse(in, false, syntax);
+        auto m = common_chat_parse(in, false, params);
         assert_equals<std::size_t>(variant, 0, m.tool_calls.size());
         assert_equals(variant, std::string("CONTENT"), m.content);
         assert_equals(variant, std::string("REASONING"), m.reasoning_content);
     }
     // variant: thinking not forced open + missing reasoning + no tool calls
     {
-        common_chat_syntax syntax = {
-            /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
-            /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
-            /* .reasoning_in_content = */ false,
-            /* .thinking_forced_open = */ false,
-            /* .parse_tool_calls = */ true,
-        };
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_DEEPSEEK_V3_1;
+        params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = false;
+        params.parse_tool_calls = true;
         const std::string variant("thinking_not_forced_open_missing_reasoning_no_tool_calls");
         const std::string in = "CONTENT";
-        auto m = common_chat_parse(in, false, syntax);
+        auto m = common_chat_parse(in, false, params);
         assert_equals<std::size_t>(variant, 0, m.tool_calls.size());
         assert_equals(variant, std::string("CONTENT"), m.content);
         assert_equals(variant, std::string(""), m.reasoning_content);
index d3a4cfd226e44857e11d1dafa14e0b8893d96851..f767c73c27a2440caa545a5b2ce669616106473f 100644 (file)
@@ -616,15 +616,15 @@ void test_command7_parser_compare(testing & t) {
 
     auto test_legacy = [&](const std::string & input, bool need_more_input, bool print_results) {
         // Original common_chat_combinator_parser taken from chat.cpp
+        common_chat_parser_params params;
+        params.format = COMMON_CHAT_FORMAT_GENERIC;
+        params.reasoning_format = COMMON_REASONING_FORMAT_AUTO;
+        params.reasoning_in_content = false;
+        params.thinking_forced_open = false;
         common_chat_msg_parser builder(
             input,
             /* .is_partial = */ need_more_input,
-            {
-                /* .format = */ COMMON_CHAT_FORMAT_GENERIC,
-                /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
-                /* .reasoning_in_content = */ false,
-                /* .thinking_forced_open = */ false,
-            }
+            params
         );
 
         builder.try_parse_reasoning("<|START_THINKING|>", "<|END_THINKING|>");
index e1264b8e8d18876646d26cfff3aed905daef40dc..6820acf67926f0006bc15fbab9ae55bed4a6ecd8 100644 (file)
@@ -341,10 +341,11 @@ static void test_templates(const struct common_chat_templates * tmpls, const std
         }
 
         if (expect_grammar_triggered) {
-            common_chat_syntax syntax;
-            syntax.format = data.params.format;
-            syntax.reasoning_format = reasoning_format;
-            const auto msg = common_chat_parse(data.delta, /* is_partial= */ false, syntax);
+            // TODO @ngxson : refactor common_chat_parse to avoid passing format/reasoning_format every time
+            common_chat_parser_params params;
+            params.format = data.params.format;
+            params.reasoning_format = reasoning_format;
+            const auto msg = common_chat_parse(data.delta, /* is_partial= */ false, params);
             assert_msg_equals(test_message, msg, ignore_whitespace_differences);
         }
 
@@ -556,7 +557,9 @@ struct make_peg_parser {
     }
 
     common_chat_msg parse(const std::string & msg, bool is_partial) {
-        return common_chat_peg_parse(arena_, msg, is_partial, /* syntax = */ {params_.format});
+        common_chat_parser_params parser_params;
+        parser_params.format = params_.format;
+        return common_chat_peg_parse(arena_, msg, is_partial, parser_params);
     }
 };
 
@@ -750,6 +753,25 @@ static void test_tools_oaicompat_json_conversion() {
     }
 }
 
+// for compat; ref: https://github.com/ggml-org/llama.cpp/pull/18961
+struct test_parser_params {
+    common_chat_format       format                = COMMON_CHAT_FORMAT_CONTENT_ONLY;
+    common_reasoning_format  reasoning_format      = COMMON_REASONING_FORMAT_NONE;
+    bool                     reasoning_in_content  = false;
+    bool                     thinking_forced_open  = false;
+    bool                     parse_tool_calls      = true;
+};
+
+static common_chat_msg test_chat_parse(const std::string & input, bool is_partial, const test_parser_params & syntax) {
+    common_chat_parser_params params;
+    params.format               = syntax.format;
+    params.reasoning_format     = syntax.reasoning_format;
+    params.reasoning_in_content = syntax.reasoning_in_content;
+    params.thinking_forced_open = syntax.thinking_forced_open;
+    params.parse_tool_calls     = syntax.parse_tool_calls;
+    return common_chat_parse(input, is_partial, params);
+}
+
 static void test_template_output_parsers() {
     printf("[%s]\n", __func__);
 
@@ -781,17 +803,17 @@ static void test_template_output_parsers() {
         }
 
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_COMMAND_R7B}));
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_RESPONSE|>Hello, world!\nWhat's up?<|END_RESPONSE|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_COMMAND_R7B}));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_THINKING|>I'm\nthinking<|END_THINKING|>"
                 "<|START_RESPONSE|>Hello, world!\nWhat's up?<|END_RESPONSE|>",
                 /* is_partial= */ false,
@@ -800,7 +822,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts_unparsed_deepseek,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_THINKING|>I'm\nthinking<|END_THINKING|>"
                 "<|START_RESPONSE|>Hello, world!\nWhat's up?<|END_RESPONSE|>",
                 /* is_partial= */ false,
@@ -811,13 +833,13 @@ static void test_template_output_parsers() {
                     /* .thinking_forced_open = */ false,
                 }));
         assert_msg_equals(message_assist_thoughts_unparsed_r7b,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_THINKING|>I'm\nthinking<|END_THINKING|>"
                 "<|START_RESPONSE|>Hello, world!\nWhat's up?<|END_RESPONSE|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_COMMAND_R7B}));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_THINKING|>I'm\nthinking<|END_THINKING|>"
                 "<|START_RESPONSE|>Hello, world!\nWhat's up?<|END_RESPONSE|>",
                 /* is_partial= */ false,
@@ -826,7 +848,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts_call_idx,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_THINKING|>I'm\nthinking<|END_THINKING|>"
                 "<|START_ACTION|>[\n"
                 "    {\"tool_call_id\": \"0\", \"tool_name\": \"special_function\", \"parameters\": {\"arg1\": 1}}\n"
@@ -837,7 +859,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts_no_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<|START_THINKING|>I'm\nthinking<|END_THINKING|>"
                 "<|START_ACTION|>[\n"
                 "    {\"tool_call_id\": \"0\", \"tool_name\": \"special",
@@ -877,7 +899,7 @@ static void test_template_output_parsers() {
 
         assert_equals(
             simple_assist_msg("{ \"tool_call\" : { \"name\" : \"t"),
-            common_chat_parse(
+            test_chat_parse(
                 "{ \"tool_call\" : { \"name\" : \"t",
                 /* is_partial= */ true,
                 {
@@ -889,33 +911,33 @@ static void test_template_output_parsers() {
                 }));
         assert_equals(
             message_assist_empty,
-            common_chat_parse(
+            test_chat_parse(
                 "{ \"tool_call\" : { \"name\" : \"t",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GENERIC}));
 
         assert_equals(
             simple_assist_msg("", "", "puppeteer_screenshot", "{\"name\":\"servethehome_homepage\","),
-            common_chat_parse(
+            test_chat_parse(
                 R"({"tool_call": {"name": "puppeteer_screenshot", "arguments": {"name": "servethehome_homepage",)",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GENERIC}));
 
         assert_equals(
             message_assist_call_empty_args,
-            common_chat_parse(
+            test_chat_parse(
                 "{ \"tool_call\" : { \"name\" : \"special_function\"",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GENERIC}));
         assert_equals(
             message_assist_call_cutoff_args,
-            common_chat_parse(
+            test_chat_parse(
                 "{ \"tool_call\" : { \"name\" : \"special_function\", \"arguments\" : { \"arg",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GENERIC}));
 
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "{\n"
                 "  \"response\": \"Hello, world!\\nWhat's up?\"\n"
                 "}",
@@ -951,7 +973,7 @@ static void test_template_output_parsers() {
     {
         assert_msg_equals(
             simple_assist_msg("Réponse", "raisonnement"),
-            common_chat_parse(
+            test_chat_parse(
                 message_assist_thoughts_unparsed_magistral.content,
                 /* is_partial= */ false,
                 {
@@ -988,14 +1010,14 @@ static void test_template_output_parsers() {
         // Test parsing
         assert_msg_equals(
             simple_assist_msg("", "", "python", ""),
-            common_chat_parse(
+            test_chat_parse(
                 "```json\n"
                 "<function_call> { \"name\" : \"python\"",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             simple_assist_msg("Let's call something\n"),
-            common_chat_parse(
+            test_chat_parse(
                 "Let's call something\n"
                 "<tool_call>{\"name\"",
                 /* is_partial= */ true,
@@ -1005,7 +1027,7 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(
             simple_assist_msg("Let's call something\n"),
-            common_chat_parse(
+            test_chat_parse(
                 "Let's call something\n"
                 "<tool_call>{\"name",
                 /* is_partial= */ true,
@@ -1014,7 +1036,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 // QwQ-32B's template adds a trailing <think> if add_generation_prompt
                 "I'm\nthinking</think>\n"
                 "<tool_call>{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}</tool_call>",
@@ -1027,14 +1049,14 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<tool_call>\n"
                 "{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</tool_call>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(message_assist_call_content,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?<tool_call>\n"
                 "{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</tool_call>",
@@ -1042,13 +1064,13 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<function=special_function>{\"arg1\": 1}</function>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<function name=\"special_function\">\n"
                 "{\"arg1\": 1}\n"
                 "</function>",
@@ -1056,7 +1078,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<tool>\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</tool>",
@@ -1064,7 +1086,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<tools>\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</tools>",
@@ -1072,7 +1094,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<response>\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</response>",
@@ -1080,7 +1102,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "```xml\n"
                 "<response>\n"
                 "    {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
@@ -1090,7 +1112,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "```xml\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "```",
@@ -1098,7 +1120,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "```\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "```",
@@ -1106,7 +1128,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "```\n"
                 "{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "```",
@@ -1114,7 +1136,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "```json\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "```",
@@ -1122,7 +1144,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "```json\n"
                 "\n"
                 "                    <function_call> {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}} \n"
@@ -1132,7 +1154,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<json>\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</json>",
@@ -1140,7 +1162,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<xml>\n"
                 "  {\n"
                 "    \"name\": \"special_function\", \"arguments\": {\"arg1\": 1}\n"
@@ -1150,7 +1172,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<JSON>\n"
                 "  {\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</JSON>",
@@ -1158,13 +1180,13 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "{\n  \"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
@@ -1178,7 +1200,7 @@ static void test_template_output_parsers() {
 
         assert_msg_equals(
             message_assist_multiple_calls,
-            common_chat_parse(
+            test_chat_parse(
                 "<tool_call>\n"
                 "{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}\n"
                 "</tool_call>\n"
@@ -1190,7 +1212,7 @@ static void test_template_output_parsers() {
 
         assert_msg_equals(
             message_assist_multiple_calls,
-            common_chat_parse(
+            test_chat_parse(
                 "<function=special_function>{\"arg1\": 1}</function>\n"
                 "<function=python>{\"code\":\"print('hello')\"}</function>",
                 /* is_partial= */ false,
@@ -1202,27 +1224,27 @@ static void test_template_output_parsers() {
                 "",
                 "special_function",
                 "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 "This is not a tool call:\n"
                 "{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         assert_msg_equals(message_assist_thoughts_unparsed_deepseek,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_HERMES_2_PRO}));
         // assert_msg_equals(message_assist_thoughts_unparsed_deepseek,
-        //     common_chat_parse(
+        //     test_chat_parse(
         //         "I'm\nthinking</think>Hello, world!\nWhat's up?",
         //         COMMON_CHAT_FORMAT_HERMES_2_PRO));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1230,7 +1252,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ true,
                 {
@@ -1238,7 +1260,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts_unparsed_md,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n```json\n{}```",
                 /* is_partial= */ false,
                 {
@@ -1249,7 +1271,7 @@ static void test_template_output_parsers() {
                     /* .parse_tool_calls = */ false,
                 }));
         assert_msg_equals(message_assist_thoughts_unparsed_md_partial,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n```json\n{}```",
                 /* is_partial= */ true,
                 {
@@ -1259,7 +1281,7 @@ static void test_template_output_parsers() {
                     /* .thinking_forced_open = */ false,
                 }));
         assert_msg_equals(message_assist_thoughts_unopened_unparsed,
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1267,7 +1289,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1304,7 +1326,7 @@ static void test_template_output_parsers() {
                       "</tool_call>");
         assert_msg_equals(
             simple_assist_msg("", /* reasoning_content= */ "<tool_call>nah uhg</tool_call>"),
-            common_chat_parse(
+            test_chat_parse(
                 "<think><tool_call>nah uhg</tool_call>",
                 /* is_partial= */ false,
                 {
@@ -1328,7 +1350,7 @@ static void test_template_output_parsers() {
 
         assert_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "{\"name\": \"special_function\", \"parameters\": {\"arg1\": 1}}",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LLAMA_3_X}));
@@ -1366,7 +1388,7 @@ static void test_template_output_parsers() {
         for (auto is_partial : { false, true }) {
             assert_equals(
                 message_assist_call,
-                common_chat_parse(
+                test_chat_parse(
                     "<function=special_function>{\"arg1\": 1}</function>",
                     is_partial,
                     {COMMON_CHAT_FORMAT_FUNCTIONARY_V3_1_LLAMA_3_1}));
@@ -1374,7 +1396,7 @@ static void test_template_output_parsers() {
 
         assert_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<function=special_function>{\"arg1\": 1}<",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_FUNCTIONARY_V3_1_LLAMA_3_1}));
@@ -1396,7 +1418,7 @@ static void test_template_output_parsers() {
                 "",
                 "special_function",
                 "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 "all\n"
                 "Hello, world!\n"
                 "nono\n"
@@ -1405,27 +1427,27 @@ static void test_template_output_parsers() {
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_FUNCTIONARY_V3_2}));
         assert_msg_equals(message_assist_call_python_lines,
-            common_chat_parse(
+            test_chat_parse(
                 "python\n"
                 "# This is a program:\n"
                 "print('hey')",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_FUNCTIONARY_V3_2}));
         assert_msg_equals(message_assist_call_python_lines_unclosed,
-            common_chat_parse(
+            test_chat_parse(
                 "python\n"
                 "# This is a program:\n"
                 "print('hey')",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_FUNCTIONARY_V3_2}));
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "special_function\n"
                 "{\"arg1\": 1} \n                    ",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_FUNCTIONARY_V3_2}));
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "all\n"
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
@@ -1466,7 +1488,7 @@ static void test_template_output_parsers() {
         test_templates(tmpls.get(), end_tokens, message_assist_thoughts, tools, "Hello, world!\nWhat's up?", /* expect_grammar_triggered= */ false);
         assert_msg_equals(
             simple_assist_msg("Hello, world!\nWhat's up?", "<think>I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1477,7 +1499,7 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(
             simple_assist_msg("", "I need to remember the correct syntax. It starts with <|tool▁calls▁begin|> and ends with"),
-            common_chat_parse(
+            test_chat_parse(
                 "I need to remember the correct syntax. It starts with <|tool▁calls▁begin|> and ends with",
                 /* is_partial= */ true,
                 {
@@ -1487,7 +1509,7 @@ static void test_template_output_parsers() {
                     /* .thinking_forced_open = */ true,
                 }));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1495,7 +1517,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts_unopened_unparsed,
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1503,7 +1525,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1514,7 +1536,7 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(message_assist_thoughts,
             // Latest template update (ast of 20250209) adds a trailing <think>\n if add_generation_prompt is true.
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1543,12 +1565,12 @@ static void test_template_output_parsers() {
         test_templates(tmpls.get(), end_tokens, message_assist, tools, "Hello, world!\nWhat's up?", /* expect_grammar_triggered= */ false);
         test_templates(tmpls.get(), end_tokens, message_assist_thoughts, tools, "Hello, world!\nWhat's up?", /* expect_grammar_triggered= */ false);
         assert_msg_equals(message_assist_thoughts_unparsed_deepseek,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_DEEPSEEK_R1}));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1556,7 +1578,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1567,7 +1589,7 @@ static void test_template_output_parsers() {
                 }));
 
         assert_msg_equals(message_assist_call_thoughts_unparsed,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>\n\n"
                 "<|tool▁calls▁begin|><|tool▁call▁begin|>function<|tool▁sep|>special_function\n"
                 "```json\n"
@@ -1576,7 +1598,7 @@ static void test_template_output_parsers() {
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_DEEPSEEK_R1}));
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool▁calls|>function<|tool▁sep|>special_function\n"
                 "```json\n"
                 "{\"arg1\": 1}\n"
@@ -1585,7 +1607,7 @@ static void test_template_output_parsers() {
                 {COMMON_CHAT_FORMAT_DEEPSEEK_R1}));
 
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>\n\n"
                 "<|tool▁calls▁begin|><|tool▁call▁begin|>function<|tool▁sep|>special_function\n"
                 "```json\n"
@@ -1612,20 +1634,20 @@ static void test_template_output_parsers() {
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(
             message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GRANITE}));
 
         // Test parsing content with thinking
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -1633,12 +1655,12 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts_unparsed_deepseek,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><response>Hello, world!\nWhat's up?",
                 /* is_partial= */ true,
                 {
@@ -1646,7 +1668,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><response>Hello, world!\nWhat's up?</response>",
                 /* is_partial= */ false,
                 {
@@ -1654,12 +1676,12 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(simple_assist_msg("<think>I'm\nthinking</think><response>Hello, world!\nWhat's up?</response>"),
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><response>Hello, world!\nWhat's up?</response>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(message_assist_empty,
-            common_chat_parse(
+            test_chat_parse(
                 "<think",
                 /* is_partial= */ true,
                 {
@@ -1667,12 +1689,12 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
                 }));
         assert_msg_equals(message_assist_empty,
-            common_chat_parse(
+            test_chat_parse(
                 "<think",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(message_assist_thoughts_no_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking",
                 /* is_partial= */ true,
                 {
@@ -1681,32 +1703,32 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(
             message_assist_empty,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><response",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GRANITE}));
 
         // Test parsing tool calls
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call|>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}]",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(
             message_assist_call_empty_args,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call|>[{\"name\": \"special_function\"",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(
             message_assist_call_cutoff_args,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call|>[{\"name\": \"special_function\", \"arguments\": {\"arg",
                 /* is_partial= */ true,
                 {COMMON_CHAT_FORMAT_GRANITE}));
         assert_msg_equals(
             message_assist_call_cutoff_args,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call|>[{\"name\": \"special_function\", \"arguments\": {\"arg",
                 /* is_partial= */ true,
                 {
@@ -1717,7 +1739,7 @@ static void test_template_output_parsers() {
         // Test parsing tool calls with thinking
         assert_msg_equals(
             message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><|tool_call|>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}, {",
                 /* is_partial= */ true,
                 {
@@ -1757,7 +1779,7 @@ static void test_template_output_parsers() {
         assert_equals(COMMON_CHAT_FORMAT_GPT_OSS, common_chat_templates_apply(tmpls.get(), inputs_tools).format);
 
         assert_msg_equals(simple_assist_msg("", "I'm\nthink"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthink",
                 /* is_partial= */ true,
                 {
@@ -1765,7 +1787,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>",
                 /* is_partial= */ true,
                 {
@@ -1773,7 +1795,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>final<|message|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
@@ -1782,7 +1804,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary to=functions.special_function <|constrain|>json<|message|>{\"arg1",
                 /* is_partial= */ true,
@@ -1791,7 +1813,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary to=functions.special_function<|message|>{\"arg1",
                 /* is_partial= */ true,
@@ -1800,7 +1822,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary to=functions.special_function <|constrain|>json<|message|>{\"arg1\": 1}",
                 /* is_partial= */ false,
@@ -1809,7 +1831,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>analysis to=functions.special_function <|constrain|>json<|message|>{\"arg1\": 1}",
                 /* is_partial= */ false,
@@ -1818,7 +1840,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary<|message|>Hello, world!\nWhat's up?",
                 /* is_partial= */ true,
@@ -1827,7 +1849,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking", "special_function", "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary<|message|>Hello, world!\nWhat's up?<|end|>"
                 "<|start|>assistant<|channel|>commentary to=functions.special_function <|constrain|>json<|message|>{\"arg1\": 1}",
@@ -1840,7 +1862,7 @@ static void test_template_output_parsers() {
         // Test parse_tool_calls == false
         assert_msg_equals(
             simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>final<|message|>Hello, world!\nWhat's up?",
                 /* is_partial= */ true,
@@ -1853,7 +1875,7 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(
             simple_assist_msg("", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary to=functions.special_function<|message|>{\"arg1",
                 /* is_partial= */ true,
@@ -1866,7 +1888,7 @@ static void test_template_output_parsers() {
                 }));
         assert_msg_equals(
             simple_assist_msg("", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>commentary to=functions.special_function <|constrain|>json<|message|>{\"arg1\": 1}",
                 /* is_partial= */ false,
@@ -1882,7 +1904,7 @@ static void test_template_output_parsers() {
         assert_msg_equals(
             simple_assist_msg(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>Hello, world!\nWhat's up?"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>final<|message|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
@@ -1894,7 +1916,7 @@ static void test_template_output_parsers() {
         assert_msg_equals(
             simple_assist_msg(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>Hello, world!\nWhat's up?"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant<|channel|>final<|message|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
@@ -1906,7 +1928,7 @@ static void test_template_output_parsers() {
 
         // Test tool calling in role header
         assert_msg_equals(simple_assist_msg("", "", "special_function", "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 " to=functions.special_function<|channel|>commentary <|constrain|>json<|message|>{\"arg1\": 1}",
                 /* is_partial= */ false,
                 {
@@ -1914,7 +1936,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "", "special_function", "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 " to=functions.special_function<|channel|>analysis <|constrain|>json<|message|>{\"arg1\": 1}",
                 /* is_partial= */ false,
                 {
@@ -1922,7 +1944,7 @@ static void test_template_output_parsers() {
                     /* .reasoning_format = */ COMMON_REASONING_FORMAT_AUTO,
                 }));
         assert_msg_equals(simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1\": 1}"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|channel|>analysis<|message|>I'm\nthinking<|end|>"
                 "<|start|>assistant to=functions.special_function<|channel|>analysis <|constrain|>json<|message|>{\"arg1\": 1}",
                 /* is_partial= */ false,
@@ -1944,7 +1966,7 @@ static void test_template_output_parsers() {
         // Test simple reasoning content
         assert_msg_equals(
             simple_assist_msg("Hello, world!", "I'm thinking about the answer"),
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:think>I'm thinking about the answer</seed:think>Hello, world!",
                 /* is_partial= */ false,
                 {
@@ -1959,7 +1981,7 @@ static void test_template_output_parsers() {
         msg_budget_reflect.reasoning_content = "Token usage: 45/1000\nI should continue thinking to find the best solution.";
         assert_msg_equals(
             msg_budget_reflect,
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:think>Token usage: 45/1000\nI should continue thinking to find the best solution.</seed:think>"
                 "<seed:cot_budget_reflect>Token usage: 45/1000\nI should continue thinking to find the best solution.</seed:cot_budget_reflect>"
                 "I need to calculate this step by step.",
@@ -1975,7 +1997,7 @@ static void test_template_output_parsers() {
         msg_tool_call.tool_calls.push_back({"calculate_sum", "{\"numbers\": [1, 2, 3]}", ""});
         assert_msg_equals(
             msg_tool_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:tool_call>\n"
                 "<function=calculate_sum>\n"
                 "<parameter=numbers>[1, 2, 3]</parameter>\n"
@@ -1992,7 +2014,7 @@ static void test_template_output_parsers() {
         msg_reasoning_tool.tool_calls.push_back({"calculate_sum", "{\"numbers\": [1, 2, 3]}", ""});
         assert_msg_equals(
             msg_reasoning_tool,
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:think>I need to calculate the sum of these numbers</seed:think>"
                 "<seed:tool_call>\n"
                 "<function=calculate_sum>\n"
@@ -2013,7 +2035,7 @@ static void test_template_output_parsers() {
         std::size_t previousToolCalls = 0;
         for (std::size_t i = std::string("<seed:tool_call>").length(); i < tool_msg.length() - 1; i++) {
             auto partial = tool_msg.substr(0, i);
-            auto partial_res = common_chat_parse(partial, true, { COMMON_CHAT_FORMAT_SEED_OSS, COMMON_REASONING_FORMAT_DEEPSEEK });
+            auto partial_res = test_chat_parse(partial, true, { COMMON_CHAT_FORMAT_SEED_OSS, COMMON_REASONING_FORMAT_DEEPSEEK });
             if (partial_res.tool_calls.size() < previousToolCalls) {
                 throw std::runtime_error("Tool call size decreased on partial: " + partial + " from " + std::to_string(previousToolCalls) + " to " + std::to_string(partial_res.tool_calls.size()));
             }
@@ -2026,7 +2048,7 @@ static void test_template_output_parsers() {
         msg_multi_param.tool_calls.push_back({"process_data", "{\"input\": \"test\", \"format\": \"json\"}", ""});
         assert_msg_equals(
             msg_multi_param,
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:tool_call>\n"
                 "<function=process_data>\n"
                 "<parameter=input>test</parameter>\n"
@@ -2039,7 +2061,7 @@ static void test_template_output_parsers() {
         // Test partial parsing for incomplete tool call - don't actually add the call until parsing parameters is done
         assert_msg_equals(
             simple_assist_msg("", "", "calculate_sum", "{\"numbers\":"),
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:tool_call>\n"
                 "<function=calculate_sum>\n"
                 "<parameter=numbers>[1,\n",
@@ -2049,7 +2071,7 @@ static void test_template_output_parsers() {
         // Test incomplete reasoning tag
         assert_msg_equals(
             simple_assist_msg("", "I was thinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "<seed:think>I was thinking",
                 /* is_partial= */ true,
                 {
@@ -2060,7 +2082,7 @@ static void test_template_output_parsers() {
         // Test content without reasoning
         assert_msg_equals(
             simple_assist_msg("This is a simple response without reasoning."),
-            common_chat_parse(
+            test_chat_parse(
                 "This is a simple response without reasoning.",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_SEED_OSS}));
@@ -2074,14 +2096,14 @@ static void test_template_output_parsers() {
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_NEMOTRON_V2}));
 
         // Test parsing content with thinking
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2091,14 +2113,14 @@ static void test_template_output_parsers() {
 
         // Test parsing tool calls
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<TOOLCALL>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}]</TOOLCALL>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_NEMOTRON_V2}));
 
         // Test parsing tool calls with thinking
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><TOOLCALL>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}]</TOOLCALL>",
                 /* is_partial= */ false,
                 {
@@ -2108,7 +2130,7 @@ static void test_template_output_parsers() {
 
         // Test tool calls with extra content
         assert_msg_equals(message_assist_call_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<TOOLCALL>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}]</TOOLCALL>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_NEMOTRON_V2}
@@ -2116,7 +2138,7 @@ static void test_template_output_parsers() {
 
         // Test tool calls with extra content AND thinking
         assert_msg_equals(message_assist_call_thoughts_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><TOOLCALL>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}]</TOOLCALL>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2149,7 +2171,7 @@ static void test_template_output_parsers() {
         test_templates(tmpls.get(), end_tokens, message_assist_thoughts, tools, "</think>Hello, world!\nWhat's up?", /* expect_grammar_triggered= */ false);
         assert_msg_equals(
             simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking"),
-            common_chat_parse(
+            test_chat_parse(
                 "I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2161,7 +2183,7 @@ static void test_template_output_parsers() {
         // variant: thinking forced open, reasoning_format none
         assert_msg_equals(
             simple_assist_msg("REASONING</think>ok", ""),
-            common_chat_parse(
+            test_chat_parse(
                 "REASONING</think>ok",
                 /* is_partial= */ false,
                 {
@@ -2174,7 +2196,7 @@ static void test_template_output_parsers() {
         // variant: happy path for when it works as the model card says it should
         assert_msg_equals(
             simple_assist_msg("", "", "get_time", "{\"city\":\"Tokyo\"}"),
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>",
                 /* is_partial= */ false,
                 {
@@ -2187,7 +2209,7 @@ static void test_template_output_parsers() {
         // variant: simple + thinking open
         assert_msg_equals(
             simple_assist_msg("", "REASONING", "get_time", "{\"city\":\"Tokyo\"}"),
-            common_chat_parse(
+            test_chat_parse(
                 "REASONING</think><|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>",
                 /* is_partial= */ false,
                 {
@@ -2205,7 +2227,7 @@ static void test_template_output_parsers() {
         message_assist_multiple_calls.tool_calls.push_back({"get_weather", "{\"city\":\"Paris\"}", ""});
         assert_msg_equals(
             message_assist_multiple_calls,
-            common_chat_parse(
+            test_chat_parse(
                 "CONTENT<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Paris\"}<|tool▁call▁end|><|tool▁call▁begin|>get_weather<|tool▁sep|>{\"city\": \"Paris\"}<|tool▁call▁end|><|tool▁calls▁end|>",
                 /* is_partial= */ false,
                 {
@@ -2218,7 +2240,7 @@ static void test_template_output_parsers() {
         // variant: thinking forced open + tool call in reasoning content
         assert_msg_equals(
             simple_assist_msg("", "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time2<|tool▁sep|>{\"city\": \"Tokyo2\"}<|tool▁call▁end|><|tool▁calls▁end|>REASONING", "get_time", "{\"city\":\"Tokyo\"}"),
-            common_chat_parse(
+            test_chat_parse(
                 "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time2<|tool▁sep|>{\"city\": \"Tokyo2\"}<|tool▁call▁end|><|tool▁calls▁end|>REASONING</think><|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>",
                 /* is_partial= */ false,
                 {
@@ -2234,7 +2256,7 @@ static void test_template_output_parsers() {
         //          add the reasoning content as regular content and parse the tool calls.
         assert_msg_equals(
             simple_assist_msg("REASONING", "", "get_time", "{\"city\":\"Tokyo\"}"),
-            common_chat_parse(
+            test_chat_parse(
                 "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>",
                 /* is_partial= */ false,
                 {
@@ -2247,7 +2269,7 @@ static void test_template_output_parsers() {
         // variant: thinking forced open + tool call in reasoning content + no closing think + partial
         assert_msg_equals(
             simple_assist_msg("", "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>", "", ""),
-            common_chat_parse(
+            test_chat_parse(
                 "REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\"city\": \"Tokyo\"}<|tool▁call▁end|><|tool▁calls▁end|>",
                 /* is_partial= */ true,
                 {
@@ -2260,7 +2282,7 @@ static void test_template_output_parsers() {
         // variant: thinking not forced open + missing reasoning + no tool calls
         assert_msg_equals(
             simple_assist_msg("CONTENT", ""),
-            common_chat_parse(
+            test_chat_parse(
                 "CONTENT",
                 /* is_partial= */ false,
                 {
@@ -2280,14 +2302,14 @@ static void test_template_output_parsers() {
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_APERTUS}));
 
         // Test parsing content with thinking
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<|inner_prefix|>I'm\nthinking<|inner_suffix|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2297,14 +2319,14 @@ static void test_template_output_parsers() {
 
         // Test parsing tool calls
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tools_prefix|>[{\"special_function\": {\"arg1\": 1}}]<|tools_suffix|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_APERTUS}));
 
         // Test parsing tool calls with thinking
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<|inner_prefix|>I'm\nthinking<|inner_suffix|><|tools_prefix|>[{\"special_function\": {\"arg1\": 1}}]<|tools_suffix|>",
                 /* is_partial= */ false,
                 {
@@ -2314,7 +2336,7 @@ static void test_template_output_parsers() {
 
         // Test tool calls with extra content
         assert_msg_equals(message_assist_call_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tools_prefix|>[{\"special_function\": {\"arg1\": 1}}]<|tools_suffix|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_APERTUS}
@@ -2322,7 +2344,7 @@ static void test_template_output_parsers() {
 
         // Test tool calls with extra content AND thinking
         assert_msg_equals(message_assist_call_thoughts_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<|inner_prefix|>I'm\nthinking<|inner_suffix|><|tools_prefix|>[{\"special_function\": {\"arg1\": 1}}]<|tools_suffix|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2402,7 +2424,7 @@ Hey there!<|im_end|>
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2413,7 +2435,7 @@ Hey there!<|im_end|>
         msg_single_tool_call.tool_calls.push_back({"special_function", "{\"arg1\":1}", ""});
         assert_msg_equals(
             msg_single_tool_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call_start|>[{\"name\": \"special_function\", \"arguments\": {\"arg1\": 1}}]<|tool_call_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2424,7 +2446,7 @@ Hey there!<|im_end|>
         msg_tool_call_string.tool_calls.push_back({"get_weather", "{\"location\":\"Paris\"}", ""});
         assert_msg_equals(
             msg_tool_call_string,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call_start|>[{\"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]<|tool_call_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2435,7 +2457,7 @@ Hey there!<|im_end|>
         msg_multi_args.tool_calls.push_back({"calculate", "{\"x\":10,\"y\":20,\"operation\":\"add\"}", ""});
         assert_msg_equals(
             msg_multi_args,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call_start|>[{\"name\": \"calculate\", \"arguments\": {\"x\": 10, \"y\": 20, \"operation\": \"add\"}}]<|tool_call_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2447,7 +2469,7 @@ Hey there!<|im_end|>
         msg_multiple_tools.tool_calls.push_back({"get_time", "{\"timezone\":\"UTC\"}", ""});
         assert_msg_equals(
             msg_multiple_tools,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call_start|>[{\"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}, {\"name\": \"get_time\", \"arguments\": {\"timezone\": \"UTC\"}}]<|tool_call_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2459,7 +2481,7 @@ Hey there!<|im_end|>
         msg_content_before_tool.tool_calls.push_back({"get_weather", "{\"location\":\"Paris\"}", ""});
         assert_msg_equals(
             msg_content_before_tool,
-            common_chat_parse(
+            test_chat_parse(
                 "Let me check the weather for you.<|tool_call_start|>[{\"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]<|tool_call_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2471,7 +2493,7 @@ Hey there!<|im_end|>
         msg_content_after_tool.tool_calls.push_back({"get_weather", "{\"location\":\"Paris\"}", ""});
         assert_msg_equals(
             msg_content_after_tool,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call_start|>[{\"name\": \"get_weather\", \"arguments\": {\"location\": \"Paris\"}}]<|tool_call_end|>Here's the result.",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2482,7 +2504,7 @@ Hey there!<|im_end|>
         msg_tool_call_newlines.tool_calls.push_back({"get_current_time", "{\"location\":\"Paris\"}", ""});
         assert_msg_equals(
             msg_tool_call_newlines,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_call_start|>[{\n    \"name\": \"get_current_time\",\n    \"arguments\": {\n        \"location\": \"Paris\"\n    }\n}]<|tool_call_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS}));
@@ -2502,14 +2524,14 @@ Hey there!<|im_end|>
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_MINIMAX_M2}));
 
         // Test parsing content with thinking
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2519,14 +2541,14 @@ Hey there!<|im_end|>
 
         // Test parsing tool calls
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<minimax:tool_call><invoke name=\"special_function\"><parameter name=\"arg1\">1</parameter></invoke></minimax:tool_call>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_MINIMAX_M2}));
 
         // Test parsing tool calls with thinking
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><minimax:tool_call><invoke name=\"special_function\"><parameter name=\"arg1\">1</parameter></invoke></minimax:tool_call>",
                 /* is_partial= */ false,
                 {
@@ -2536,7 +2558,7 @@ Hey there!<|im_end|>
 
         // Test tool calls with extra content
         assert_msg_equals(message_assist_call_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<minimax:tool_call><invoke name=\"special_function\"><parameter name=\"arg1\">1</parameter></invoke></minimax:tool_call>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_MINIMAX_M2}
@@ -2544,7 +2566,7 @@ Hey there!<|im_end|>
 
         // Test tool calls with extra content AND thinking
         assert_msg_equals(message_assist_call_thoughts_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><minimax:tool_call><invoke name=\"special_function\"><parameter name=\"arg1\">1</parameter></invoke></minimax:tool_call>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2555,25 +2577,25 @@ Hey there!<|im_end|>
         // Test streaming
         test_parser_with_streaming(message_assist_call_thoughts_content,
             "<think>I'm\nthinking\n</think>Hello, world!\nWhat's up?\n<minimax:tool_call><invoke name=\"special_function\"><parameter name=\"arg1\">1</parameter></invoke></minimax:tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_MINIMAX_M2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(message_assist_call_thoughts_unparsed,
             "<think>I'm\nthinking</think>\n\n<minimax:tool_call><invoke name=\"special_function\"><parameter name=\"arg1\">1</parameter></invoke></minimax:tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_MINIMAX_M2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_NONE
             }); });
         test_parser_with_streaming(message_assist_call_thoughts_content,
             "<think>I'm\nthinking\n</think>\n\nHello, world!\nWhat's up?\n\n<minimax:tool_call>\n<invoke name=\"special_function\">\n<parameter name=\"arg1\">1</parameter>\n</invoke>\n</minimax:tool_call>\n",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_MINIMAX_M2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(message_assist_call_withopt,
             "<minimax:tool_call>\n<invoke name=\"special_function_with_opt\">\n<parameter name=\"arg1\">1</parameter>\n<parameter name=\"arg2\">2</parameter>\n</invoke>\n</minimax:tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_MINIMAX_M2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_NONE
             }); });
@@ -2618,14 +2640,14 @@ Hey there!<|im_end|>
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GLM_4_5}));
 
         // Test parsing content with thinking
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "\n<think>I'm\nthinking</think>\nHello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2635,14 +2657,14 @@ Hey there!<|im_end|>
 
         // Test parsing tool calls
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GLM_4_5}), true);
 
         // Test parsing tool calls with thinking
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "\n<think>I'm\nthinking</think>\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
                 /* is_partial= */ false,
                 {
@@ -2652,7 +2674,7 @@ Hey there!<|im_end|>
 
         // Test tool calls with extra content
         assert_msg_equals(message_assist_call_content,
-            common_chat_parse(
+            test_chat_parse(
                 "\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_GLM_4_5}
@@ -2660,7 +2682,7 @@ Hey there!<|im_end|>
 
         // Test tool calls with extra content AND thinking
         assert_msg_equals(message_assist_call_thoughts_content,
-            common_chat_parse(
+            test_chat_parse(
                 "\n<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
                 /* is_partial= */ false,
                 {
@@ -2671,19 +2693,19 @@ Hey there!<|im_end|>
         // Test streaming
         test_parser_with_streaming(message_assist_call_thoughts_content,
             "\n<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_GLM_4_5,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(message_assist_call_thoughts_unparsed,
             "\n<think>I'm\nthinking</think>\n\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_GLM_4_5,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_NONE
             }); });
         test_parser_with_streaming(message_assist_call_withopt,
             "\n<think></think>\n<tool_call>special_function_with_opt\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n<arg_key>arg2</arg_key>\n<arg_value>2</arg_value>\n</tool_call>\n",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_GLM_4_5,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
@@ -2699,7 +2721,7 @@ Hey there!<|im_end|>
                 "<arg_key>score</arg_key>\n"
                 "<arg_value>95.5</arg_value>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_GLM_4_5}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_GLM_4_5}); });
         test_parser_with_streaming(
                 simple_assist_msg("", "", "web_search", "{\"query\":\"\\\"From Zero\\\" Linkin Park album tracklist complete songs\",\"limit\":3,\"type\":\"text\"}"),
                 "<tool_call>web_search\n"
@@ -2710,18 +2732,18 @@ Hey there!<|im_end|>
                 "<arg_key>type</arg_key>\n"
                 "<arg_value>text</arg_value>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_GLM_4_5}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_GLM_4_5}); });
 
         // Test interleaved thinking
         test_parser_with_streaming(simple_assist_msg("Hello, world!\n\nWhat's up?", "I'm\nthinkingThinking2", "special_function", "{\"arg1\": 1}"),
             "\n<think>I'm\nthinking</think>Hello, world!\n<think>Thinking2</think>What's up?\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_GLM_4_5,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(simple_assist_msg("\n<think>I'm\nthinking</think>Hello, world!\n<think>Thinking2</think>What's up?", "", "special_function", "{\"arg1\": 1}"),
             "\n<think>I'm\nthinking</think>Hello, world!\n<think>Thinking2</think>What's up?\n<tool_call>special_function\n<arg_key>arg1</arg_key>\n<arg_value>1</arg_value>\n</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_GLM_4_5,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_NONE
             }); });
@@ -2766,14 +2788,14 @@ Hey there!<|im_end|>
 
         // Test parsing regular content
         assert_msg_equals(message_assist,
-            common_chat_parse(
+            test_chat_parse(
                 "Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_KIMI_K2}));
 
         // Test parsing content with thinking
         assert_msg_equals(message_assist_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2783,14 +2805,14 @@ Hey there!<|im_end|>
 
         // Test parsing tool calls
         assert_msg_equals(message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_KIMI_K2}));
 
         // Test parsing tool calls with thinking
         assert_msg_equals(message_assist_call_thoughts,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>",
                 /* is_partial= */ false,
                 {
@@ -2800,7 +2822,7 @@ Hey there!<|im_end|>
 
         // Test tool calls with extra content
         assert_msg_equals(message_assist_call_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {COMMON_CHAT_FORMAT_KIMI_K2}
@@ -2808,7 +2830,7 @@ Hey there!<|im_end|>
 
         // Test tool calls with extra content AND thinking
         assert_msg_equals(message_assist_call_thoughts_content,
-            common_chat_parse(
+            test_chat_parse(
                 "<think>I'm\nthinking</think><|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>Hello, world!\nWhat's up?",
                 /* is_partial= */ false,
                 {
@@ -2819,43 +2841,43 @@ Hey there!<|im_end|>
         // Test streaming
         test_parser_with_streaming(message_assist_call_thoughts_content,
             "<think>I'm\nthinking\n</think>Hello, world!\nWhat's up?\n<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(message_assist_call_thoughts_unparsed,
             "<think>I'm\nthinking</think>\n\n<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_NONE
             }); });
         test_parser_with_streaming(message_assist_call_thoughts_content,
             "<think>I'm\nthinking\n</think>\n\nHello, world!\nWhat's up?\n\n<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": 1}<|tool_call_end|><|tool_calls_section_end|>\n",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(message_assist_call_withopt,
             "<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function_with_opt:0<|tool_call_argument_begin|>{\"arg1\": 1, \"arg2\": 2}<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_NONE
             }); });
         test_parser_with_streaming(simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking", "special_function", "{\"arg1\": \"123456\"}"),
             "<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": \"123456\"}<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking", "special_function", "{\"arg1\": [1, 2, \"345\", 6]}"),
             "<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": [1, 2, \"345\", 6]}<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
         test_parser_with_streaming(simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking", "special_function", "{\"arg1\": {\"12\": 34, \"5\": [67, 8], \"9\": \"10\"}}"),
             "<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n<|tool_calls_section_begin|><|tool_call_begin|>functions.special_function:0<|tool_call_argument_begin|>{\"arg1\": {\"12\": 34, \"5\": [67, 8], \"9\": \"10\"}}<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     /*  .format = */ COMMON_CHAT_FORMAT_KIMI_K2,
                     /*  .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
@@ -2864,19 +2886,19 @@ Hey there!<|im_end|>
                 "<|tool_calls_section_begin|><|tool_call_begin|>functions.complex_function:0<|tool_call_argument_begin|>"
                 "{\"name\": \"John Doe\", \"age\": 30, \"active\": true, \"score\": 95.5}"
                 "<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
         test_parser_with_streaming(
                 simple_assist_msg("", "", "web_search", "{\"query\":\"\\\"From Zero\\\" Linkin Park album tracklist complete songs\",\"limit\":3,\"type\":\"text\"}"),
                 "<|tool_calls_section_begin|><|tool_call_begin|>functions.web_search:0<|tool_call_argument_begin|>"
                 "{\"query\":\"\\\"From Zero\\\" Linkin Park album tracklist complete songs\",\"limit\":3,\"type\":\"text\"}"
                 "<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
         test_parser_with_streaming(
                 simple_assist_msg("", "", "read_file", "{\"args\": [{\"path\": \"src/providers/ThemeProvider.tsx\"}, {\"path\": \"src/components/Header.tsx\"}, {\"path\": \"src/components/ThemeToggle.tsx\"}, {\"path\": \"src/app/globals.css\"}, {\"path\": \"src/app/layout.tsx\"}]}"),
                 "<|tool_calls_section_begin|><|tool_call_begin|>functions.read_file:0<|tool_call_argument_begin|>"
                 "{\"args\": [{\"path\": \"src/providers/ThemeProvider.tsx\"}, {\"path\": \"src/components/Header.tsx\"}, {\"path\": \"src/components/ThemeToggle.tsx\"}, {\"path\": \"src/app/globals.css\"}, {\"path\": \"src/app/layout.tsx\"}]}"
                 "<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
         test_parser_with_streaming(
                 simple_assist_msg(
                         "Let me start by examining the relevant files to understand the current implementation.", "",
@@ -2886,7 +2908,7 @@ Hey there!<|im_end|>
                 "<|tool_calls_section_begin|><|tool_call_begin|>functions.read_file:0<|tool_call_argument_begin|>"
                 "{\"files\":[{\"path\":\"src/app/Partners.tsx\",\"line_ranges\":[\"1-100\"]}]}"
                 "<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_KIMI_K2}); });
         auto multi_tool_msg = simple_assist_msg("Let me call multiple tools.", "I'm thinking.");
         multi_tool_msg.tool_calls.push_back({ "read_file", "{\"files\": [{\"path\": \"src/app/Partners.tsx\", \"line_ranges\": [\"1-100\"]}]}", "" });
         multi_tool_msg.tool_calls.push_back({ "web_search", "{\"query\":\"\\\"From Zero\\\" Linkin Park album tracklist complete songs\",\"limit\":3,\"type\":\"text\"}", "" });
@@ -2908,7 +2930,7 @@ Hey there!<|im_end|>
                 "{\"message\":\"Hello! 👋 🌟 🚀 Testing emojis: 😀😃😄😁 and symbols: ∑∏∆∇\"}"
                 "<|tool_call_end|>"
                 "<|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     COMMON_CHAT_FORMAT_KIMI_K2,
                     COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
@@ -2917,7 +2939,7 @@ Hey there!<|im_end|>
                 "<think>I'm thinking<|tool_calls_section_begin|><|tool_call_begin|>functions.complex_function_in_think:0<|tool_call_argument_begin|>"
                 "{\"name\": \"John Doe\", \"age\": 30, \"active\": true, \"score\": 95.5}"
                 "<|tool_call_end|><|tool_calls_section_end|>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     COMMON_CHAT_FORMAT_KIMI_K2,
                     COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
@@ -2926,7 +2948,7 @@ Hey there!<|im_end|>
                 "<think>I'm thinking<|tool_calls_section_begin|><|tool_call_begin|>functions.complex_function_in_think:0<|tool_call_argument_begin|>"
                 "{\"name\": \"John Doe\", \"age\": 30, \"active\": true, \"score\": 95.5}"
                 "<|tool_call_end|><|tool_calls_section_end|>I'm still thinking</think>Hello",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {
                     COMMON_CHAT_FORMAT_KIMI_K2,
                     COMMON_REASONING_FORMAT_DEEPSEEK
             }); });
@@ -3001,7 +3023,7 @@ Hey there!<|im_end|>
         // Basic XML tool call parsing
         assert_msg_equals(
             message_assist_call,
-            common_chat_parse(
+            test_chat_parse(
                 "<tool_call>\n"
                 "  <function=special_function>\n"
                 "    <parameter=arg1>\n"
@@ -3036,7 +3058,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Special characters and Unicode
         common_chat_msg expected_special_chars;
@@ -3053,7 +3075,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Multiline content with newlines and indentation
         common_chat_msg expected_multiline;
@@ -3072,7 +3094,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // JSON object as parameter value
         common_chat_msg expected_json_param;
@@ -3090,7 +3112,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Array as parameter value
         common_chat_msg expected_array_param;
@@ -3108,7 +3130,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Empty parameter
         common_chat_msg expected_empty_param;
@@ -3125,7 +3147,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Boolean values (true/false)
         common_chat_msg expected_boolean;
@@ -3146,7 +3168,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Null value
         common_chat_msg expected_null;
@@ -3164,7 +3186,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Negative numbers and scientific notation
         common_chat_msg expected_numbers;
@@ -3188,7 +3210,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // XML-like content in parameters (should be escaped)
         common_chat_msg expected_xml_content;
@@ -3206,7 +3228,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Quotes and escape characters
         common_chat_msg expected_quotes;
@@ -3224,7 +3246,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Long parameter value (simplified)
         std::string long_text = "This is a long text parameter that should test the parser's ability to handle larger amounts of text data.";
@@ -3244,7 +3266,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Mixed content with text before and after tool call
         common_chat_msg expected_mixed_content;
@@ -3263,7 +3285,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Compact format (no extra whitespace)
         common_chat_msg expected_compact;
@@ -3275,7 +3297,7 @@ Hey there!<|im_end|>
         test_parser_with_streaming(
             expected_compact,
                 "<tool_call><function=compact_function><parameter=param>value</parameter></function></tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Function name with underscores and numbers
         common_chat_msg expected_complex_name;
@@ -3293,7 +3315,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Parameter names with underscores and numbers
         common_chat_msg expected_complex_params;
@@ -3317,7 +3339,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Very deeply nested XML content in parameter
         common_chat_msg expected_deep_xml;
@@ -3335,7 +3357,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Parameter with only whitespace
         common_chat_msg expected_whitespace_param;
@@ -3353,7 +3375,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Parameter with tabs and mixed whitespace
         common_chat_msg expected_mixed_whitespace;
@@ -3373,7 +3395,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Control characters and special Unicode
         common_chat_msg expected_control_chars;
@@ -3391,7 +3413,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Emoji and extended Unicode characters
         common_chat_msg expected_emoji;
@@ -3409,7 +3431,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Mathematical expressions and formulas
         common_chat_msg expected_math;
@@ -3427,7 +3449,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // SQL injection-like content (should be safely escaped)
         common_chat_msg expected_sql;
@@ -3445,7 +3467,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // HTML/XML injection content
         common_chat_msg expected_html;
@@ -3463,7 +3485,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Binary-like content (base64)
         common_chat_msg expected_binary;
@@ -3481,7 +3503,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
 
         // Very large numbers (should be parsed as scientific notation)
         common_chat_msg expected_large_numbers;
@@ -3499,7 +3521,7 @@ Hey there!<|im_end|>
                 "    </parameter>\n"
                 "  </function>\n"
                 "</tool_call>",
-            [&](const std::string &msg) { return common_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
+            [&](const std::string &msg) { return test_chat_parse(msg, /* is_partial= */ true, {COMMON_CHAT_FORMAT_QWEN3_CODER_XML}); });
     }
 
     {
index caad29bac77d2565c0c33d1367d43b0a25371242..0926e552e925e614f0166410a63874be2246b18e 100644 (file)
@@ -66,21 +66,25 @@ struct cli_context {
         defaults.stream = true; // make sure we always use streaming mode
         defaults.timings_per_token = true; // in order to get timings even when we cancel mid-way
         // defaults.return_progress = true; // TODO: show progress
-        defaults.oaicompat_chat_syntax.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
     }
 
     std::string generate_completion(result_timings & out_timings) {
         server_response_reader rd = ctx_server.get_response_reader();
-        auto formatted = format_chat();
+        auto chat_params = format_chat();
         {
             // TODO: reduce some copies here in the future
             server_task task = server_task(SERVER_TASK_TYPE_COMPLETION);
             task.id         = rd.get_new_id();
             task.index      = 0;
-            task.params     = defaults;         // copy
-            task.cli_prompt = formatted.prompt; // copy
-            task.cli_files  = input_files;      // copy
+            task.params     = defaults;           // copy
+            task.cli_prompt = chat_params.prompt; // copy
+            task.cli_files  = input_files;        // copy
             task.cli        = true;
+
+            // chat template settings
+            task.params.chat_parser_params = common_chat_parser_params(chat_params);
+            task.params.chat_parser_params.reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK;
+
             rd.post_task({std::move(task)});
         }
 
@@ -172,7 +176,6 @@ struct cli_context {
         inputs.use_jinja             = chat_params.use_jinja;
         inputs.parallel_tool_calls   = false;
         inputs.add_generation_prompt = true;
-        inputs.reasoning_format      = chat_params.reasoning_format;
         inputs.enable_thinking       = chat_params.enable_thinking;
 
         // Apply chat template to the list of messages
index 7f4c073874de055c9abc5f06dcafae12b44dad1e..99e9c5e6f13e2860b36431eedc128111cfae71d5 100644 (file)
@@ -274,6 +274,7 @@ std::vector<server_tokens> tokenize_input_prompts(
 // OAI utils
 //
 
+// global server parameters for chat formatting / parsing
 struct server_chat_params {
     bool use_jinja;
     bool prefill_assistant;
index 35ec7ad2adfa07b61cf5131069434bcf56973b15..2add9667d143c441f7bd7a2919bd0a186f07a152 100644 (file)
@@ -68,10 +68,10 @@ json task_params::to_json(bool only_metrics) const {
             {"stream",                    stream},
             {"n_probs",                   sampling.n_probs},
             {"min_keep",                  sampling.min_keep},
-            {"chat_format",               common_chat_format_name(oaicompat_chat_syntax.format)},
-            {"reasoning_format",          common_reasoning_format_name(oaicompat_chat_syntax.reasoning_format)},
-            {"reasoning_in_content",      oaicompat_chat_syntax.reasoning_in_content},
-            {"thinking_forced_open",      oaicompat_chat_syntax.thinking_forced_open},
+            {"chat_format",               common_chat_format_name(chat_parser_params.format)},
+            {"reasoning_format",          common_reasoning_format_name(chat_parser_params.reasoning_format)},
+            {"reasoning_in_content",      chat_parser_params.reasoning_in_content},
+            {"thinking_forced_open",      chat_parser_params.thinking_forced_open},
             {"samplers",                  samplers},
             {"speculative.n_max",         speculative.n_max},
             {"speculative.n_min",         speculative.n_min},
@@ -127,10 +127,10 @@ json task_params::to_json(bool only_metrics) const {
         {"grammar_lazy",              sampling.grammar_lazy},
         {"grammar_triggers",          grammar_triggers},
         {"preserved_tokens",          sampling.preserved_tokens},
-        {"chat_format",               common_chat_format_name(oaicompat_chat_syntax.format)},
-        {"reasoning_format",          common_reasoning_format_name(oaicompat_chat_syntax.reasoning_format)},
-        {"reasoning_in_content",      oaicompat_chat_syntax.reasoning_in_content},
-        {"thinking_forced_open",      oaicompat_chat_syntax.thinking_forced_open},
+        {"chat_format",               common_chat_format_name(chat_parser_params.format)},
+        {"reasoning_format",          common_reasoning_format_name(chat_parser_params.reasoning_format)},
+        {"reasoning_in_content",      chat_parser_params.reasoning_in_content},
+        {"thinking_forced_open",      chat_parser_params.thinking_forced_open},
         {"samplers",                  samplers},
         {"speculative.n_max",         speculative.n_max},
         {"speculative.n_min",         speculative.n_min},
@@ -291,21 +291,21 @@ task_params server_task::params_from_json_cmpl(
     {
         auto it = data.find("chat_format");
         if (it != data.end()) {
-            params.oaicompat_chat_syntax.format = static_cast<common_chat_format>(it->get<int>());
-            SRV_INF("Chat format: %s\n", common_chat_format_name(params.oaicompat_chat_syntax.format));
+            params.chat_parser_params.format = static_cast<common_chat_format>(it->get<int>());
+            SRV_INF("Chat format: %s\n", common_chat_format_name(params.chat_parser_params.format));
         } else {
-            params.oaicompat_chat_syntax.format = defaults.oaicompat_chat_syntax.format;
+            params.chat_parser_params.format = defaults.chat_parser_params.format;
         }
         common_reasoning_format reasoning_format = params_base.reasoning_format;
         if (data.contains("reasoning_format")) {
             reasoning_format = common_reasoning_format_from_name(data.at("reasoning_format").get<std::string>());
         }
-        params.oaicompat_chat_syntax.reasoning_format = reasoning_format;
-        params.oaicompat_chat_syntax.reasoning_in_content = params.stream && (reasoning_format == COMMON_REASONING_FORMAT_DEEPSEEK_LEGACY);
-        params.oaicompat_chat_syntax.thinking_forced_open = json_value(data, "thinking_forced_open", false);
-        params.oaicompat_chat_syntax.parse_tool_calls = json_value(data, "parse_tool_calls", false);
+        params.chat_parser_params.reasoning_format = reasoning_format;
+        params.chat_parser_params.reasoning_in_content = params.stream && (reasoning_format == COMMON_REASONING_FORMAT_DEEPSEEK_LEGACY);
+        params.chat_parser_params.thinking_forced_open = json_value(data, "thinking_forced_open", false);
+        params.chat_parser_params.parse_tool_calls = json_value(data, "parse_tool_calls", false);
         if (data.contains("chat_parser")) {
-            params.oaicompat_chat_syntax.parser.load(data.at("chat_parser").get<std::string>());
+            params.chat_parser_params.parser.load(data.at("chat_parser").get<std::string>());
         }
     }
 
@@ -722,7 +722,7 @@ common_chat_msg task_result_state::update_chat_msg(
     auto new_msg = common_chat_parse(
         generated_text,
         is_partial,
-        oaicompat_chat_syntax);
+        chat_parser_params);
     if (!new_msg.empty()) {
         new_msg.set_tool_call_ids(generated_tool_call_ids, gen_tool_call_id);
         chat_msg = new_msg;
index daffe0c9047a36d65695d99743a4f81635881521..6835eef507813f8bd8bf887fbadec518eaf64b5a 100644 (file)
@@ -78,7 +78,9 @@ struct task_params {
     task_response_type res_type = TASK_RESPONSE_TYPE_NONE;
     std::string        oaicompat_model;
     std::string        oaicompat_cmpl_id;
-    common_chat_syntax oaicompat_chat_syntax;
+
+    // per-request parameters for chat parsing
+    common_chat_parser_params chat_parser_params;
 
     // Embeddings
     int32_t embd_normalize = 2; // (-1=none, 0=max absolute int16, 1=taxicab, 2=Euclidean/L2, >2=p-norm)
@@ -91,7 +93,7 @@ struct task_params {
 struct task_result_state {
     // tracking diffs for partial tool calls
     std::vector<common_chat_msg_diff> diffs;
-    common_chat_syntax oaicompat_chat_syntax;
+    common_chat_parser_params chat_parser_params;
     common_chat_msg chat_msg;
     std::string generated_text; // append new chunks of generated text here
     std::vector<std::string> generated_tool_call_ids;
@@ -100,8 +102,8 @@ struct task_result_state {
     bool anthropic_thinking_block_started = false;
     bool anthropic_text_block_started = false;
 
-    task_result_state(const common_chat_syntax & oaicompat_chat_syntax)
-        : oaicompat_chat_syntax(oaicompat_chat_syntax) {}
+    task_result_state(const common_chat_parser_params & chat_parser_params)
+        : chat_parser_params(chat_parser_params) {}
 
     // parse partial tool calls and update the internal state
     common_chat_msg update_chat_msg(
@@ -230,7 +232,7 @@ struct server_task {
     // the task will be moved into queue, then onto slots
     // however, the state must be kept by caller (e.g., HTTP thread)
     task_result_state create_state() const {
-        return task_result_state(params.oaicompat_chat_syntax);
+        return task_result_state(params.chat_parser_params);
     }
 
     bool is_parent() const {