| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | #include <exception>
|
| | #include <iostream>
|
| | #include <string>
|
| |
|
| | #include "chat-parser.h"
|
| | #include "common.h"
|
| | #include "log.h"
|
| | #include "regex-partial.h"
|
| |
|
| | template <class T>
|
| | static void assert_equals(const std::string_view label, const T & expected, const T & actual) {
|
| | if (expected != actual) {
|
| | std::cerr << label << std::endl;
|
| | std::cerr << "Expected: " << expected << std::endl;
|
| | std::cerr << "Actual: " << actual << std::endl;
|
| | std::cerr << std::flush;
|
| | throw std::runtime_error("Test failed");
|
| | }
|
| | }
|
| |
|
| | template <class T>
|
| | static void assert_equals(const T & expected, const T & actual) {
|
| | assert_equals("", expected, actual);
|
| | }
|
| | static void assert_equals(const char * expected, const std::string & actual) {
|
| | return assert_equals<std::string>(expected, actual);
|
| | }
|
| |
|
| | static void assert_throws(const std::function<void()> & fn, const std::string & expected_exception_pattern = "") {
|
| | try {
|
| | fn();
|
| | } catch (const std::exception & e) {
|
| | if (expected_exception_pattern.empty()) {
|
| | return;
|
| | }
|
| | std::regex expected_exception_regex(expected_exception_pattern);
|
| | std::string actual_message = e.what();
|
| | if (std::regex_search(actual_message, expected_exception_regex)) {
|
| | return;
|
| | }
|
| | throw std::runtime_error("Exception doesn't match expected pattern: " + actual_message + " (pattern: " + expected_exception_pattern + ")");
|
| | throw std::runtime_error("Exception of unexpected type: " + std::string(e.what()));
|
| | }
|
| | throw std::runtime_error("Exception was expected but not thrown");
|
| | }
|
| |
|
| | static void test_reasoning() {
|
| |
|
| | {
|
| | 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", false, params);
|
| | assert_equals(false, builder.try_parse_reasoning("<tnk>", "</tnk>"));
|
| | assert_equals("<tnk>Cogito</tnk>Ergo sum", builder.consume_rest());
|
| | }
|
| | {
|
| | 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", 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_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", false, params);
|
| | assert_equals(false, builder.try_parse_reasoning("<tnk>", "</tnk>"));
|
| | assert_equals("Cogito</tnk>Ergo sum", builder.consume_rest());
|
| | }
|
| | {
|
| | 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", 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_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", 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_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, 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_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, params);
|
| | assert_equals(variant, std::string("Plan"), msg.reasoning_content);
|
| | assert_equals(variant, std::string("Réponse"), msg.content);
|
| | }
|
| |
|
| | {
|
| | 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", 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());
|
| | }
|
| |
|
| | {
|
| | 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, params);
|
| | assert_equals(variant, std::string("REASONING</think>ok"), msg.content);
|
| | assert_equals(variant, std::string(""), msg.reasoning_content);
|
| | }
|
| | }
|
| |
|
| | static void test_regex() {
|
| | auto test_throws = [](const std::string & input, const std::string & regex, const std::string & expected_exception_pattern = "") {
|
| | common_chat_msg_parser builder(input, false, {});
|
| | assert_throws([&]() { builder.consume_regex(common_regex(regex)); }, expected_exception_pattern);
|
| | };
|
| |
|
| | test_throws("Hello, world!", "abc", "^abc$");
|
| | test_throws("Hello, world!", "e", "^e$");
|
| |
|
| | {
|
| | common_chat_msg_parser builder("Hello, world!", false, {});
|
| | builder.consume_regex(common_regex("Hello"));
|
| | assert_equals(", world!", builder.consume_rest());
|
| | }
|
| |
|
| | {
|
| |
|
| | common_chat_msg_parser builder("Hello,", false, {});
|
| | assert_equals(false, builder.try_consume_regex(common_regex("Hello, world!")).has_value());
|
| | }
|
| | {
|
| | common_chat_msg_parser builder("Hello,", false, {});
|
| | auto res = builder.try_consume_regex(common_regex("H(el)l(?:o, world!)?"));
|
| | assert_equals(true, res.has_value());
|
| |
|
| | assert_equals<size_t>(2, res->groups.size());
|
| | assert_equals("Hell", builder.str(res->groups[0]));
|
| | assert_equals("el", builder.str(res->groups[1]));
|
| |
|
| | assert_equals<size_t>(4, builder.pos());
|
| | assert_equals("o,", builder.consume_rest());
|
| | }
|
| | {
|
| |
|
| | common_chat_msg_parser builder("Hello,", true, {});
|
| | assert_throws([&]() {
|
| | builder.try_consume_regex(common_regex("Hello, world!"));
|
| | }, "^Hello, world!$");
|
| | }
|
| |
|
| |
|
| | for (const auto is_partial : {false, true}) {
|
| | common_chat_msg_parser builder("Hello,", is_partial, {});
|
| | assert_equals(false, builder.try_consume_regex(common_regex("a(b|c)(d|e)f")).has_value());
|
| | }
|
| | for (const auto is_partial : {false, true}) {
|
| | common_chat_msg_parser builder("Hello,", is_partial, {});
|
| | assert_equals(false, builder.try_consume_literal("Oh"));
|
| | }
|
| | }
|
| |
|
| | const std::vector<std::string> barely_healable_jsons = {
|
| | "{",
|
| | "{\"",
|
| | "{\"\\",
|
| | "{\"n",
|
| | "{\"name\"",
|
| | "{\"name\":",
|
| | "{\"name\":\"",
|
| | "{\"name\":\"\\",
|
| | "{\"name\":\"python",
|
| | "{\"name\":\"python\\",
|
| | "{\",",
|
| | "{\":",
|
| | "{\"[",
|
| | "{\"]",
|
| | "{\"{",
|
| | "{\"}",
|
| | "{\"1",
|
| | "{\"name\":\",",
|
| | "{\"name\":\":",
|
| | "{\"name\":\"[",
|
| | "{\"name\":\"]",
|
| | "{\"name\":\"{",
|
| | "{\"name\":\"}",
|
| | "{\"name\":\"1",
|
| | };
|
| |
|
| | static void test(const std::string & input, bool is_partial, const std::vector<std::vector<std::string>> & args_paths, const std::vector<std::vector<std::string>> & content_paths, const std::string & expected) {
|
| | common_chat_msg_parser builder(input, is_partial, {});
|
| | auto js = builder.try_consume_json_with_dumped_args(args_paths, content_paths);
|
| | assert_equals(true, js.has_value());
|
| | assert_equals(is_partial, js->is_partial);
|
| | assert_equals(expected, args_paths.size() == 1 && args_paths[0].empty() ? js->value.get<std::string>() : js->value.dump());
|
| | }
|
| |
|
| | static void test_deepseek_v3_1_tool_calls() {
|
| |
|
| |
|
| | const std::string variant("simple");
|
| | 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, params);
|
| | assert_equals<std::size_t>(variant, 1, msg.tool_calls.size());
|
| | assert_equals(variant, std::string("get_time"), msg.tool_calls[0].name);
|
| |
|
| | assert_equals(variant, std::string("{\"city\":\"Tokyo\"}"), msg.tool_calls[0].arguments);
|
| | assert_equals(variant, std::string(""), msg.content);
|
| | assert_equals(variant, std::string(""), msg.reasoning_content);
|
| |
|
| |
|
| | {
|
| | 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, 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);
|
| | assert_equals(variant, std::string(""), m.content);
|
| | assert_equals(variant, std::string("REASONING"), m.reasoning_content);
|
| | }
|
| |
|
| | {
|
| | 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, 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);
|
| | assert_equals(variant, std::string("get_weather"), m.tool_calls[1].name);
|
| | assert_equals(variant, std::string("{\"city\":\"Paris\"}"), m.tool_calls[1].arguments);
|
| | assert_equals(variant, std::string("CONTENT"), m.content);
|
| | assert_equals(variant, std::string(""), m.reasoning_content);
|
| | }
|
| |
|
| |
|
| |
|
| | {
|
| | 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, 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);
|
| | assert_equals(variant, std::string(""), m.content);
|
| | assert_equals(variant, std::string("REASONING<|tool▁calls▁begin|><|tool▁call▁begin|>get_time2<|tool▁sep|>{\"city\": \"Tokyo2\"}<|tool▁call▁end|><|tool▁calls▁end|>REASONING"), m.reasoning_content);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | {
|
| | 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, 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());
|
| | assert_equals(variant, std::string("get_time"), m.tool_calls[0].name);
|
| | assert_equals(variant, std::string("{\"city\":\"Tokyo\"}"), m.tool_calls[0].arguments);
|
| | }
|
| |
|
| |
|
| | {
|
| | 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, 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());
|
| | }
|
| |
|
| |
|
| | {
|
| | 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, 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);
|
| | }
|
| |
|
| | {
|
| | 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, 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);
|
| | }
|
| | }
|
| |
|
| | static void test_with_args(const std::string & input, const std::string & expected, bool parse_as_partial = true, bool is_partial = true) {
|
| | common_chat_msg_parser builder(input, parse_as_partial, {});
|
| | auto js = builder.try_consume_json_with_dumped_args({{"args"}}, {});
|
| | assert_equals(true, js.has_value());
|
| | assert_equals(is_partial, js->is_partial);
|
| | assert_equals(expected, js->value.dump());
|
| | }
|
| |
|
| | static void test_json_with_dumped_args_no_args() {
|
| |
|
| | test("{\"name\": \"python\"}", false, {}, {}, "{\"name\":\"python\"}");
|
| |
|
| | test("{\"name\": \"python\"}", false, {{}}, {}, "{\"name\":\"python\"}");
|
| |
|
| |
|
| | for (const auto & src : barely_healable_jsons) {
|
| | test(src, true, {{"arguments"}}, {}, "{}");
|
| | }
|
| |
|
| | test("{\"name\": \"python\"", true, {{"arguments"}}, {}, "{\"name\":\"python\"}");
|
| | }
|
| |
|
| | static void test_json_with_dumped_args() {
|
| |
|
| |
|
| | test("{\"content\": \"t", true, {}, {{"content"}}, "{\"content\":\"t\"}");
|
| | test("{\"content\": \"", true, {}, {{"content"}}, "{\"content\":\"\"}");
|
| | test("{\"content\": ", true, {}, {{"content"}}, "{}");
|
| |
|
| |
|
| | test("{\"name\": \"python", true, {{}}, {}, "{\"name\":\"python");
|
| | for (const auto & src : barely_healable_jsons) {
|
| | test(src, true, {{}}, {}, src);
|
| | }
|
| |
|
| |
|
| | for (auto parse_as_partial : {true, false}) {
|
| | test_with_args(
|
| | R"({"name": "python", "args": {"arg1": 1}})",
|
| | R"({"name":"python","args":"{\"arg1\":1}"})",
|
| | parse_as_partial,
|
| | false
|
| | );
|
| | }
|
| |
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {")",
|
| | R"({"foo":"bar","args":"{\""})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"ar)",
|
| | R"({"foo":"bar","args":"{\"ar"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1")",
|
| | R"({"foo":"bar","args":"{\"arg1\""})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1":)",
|
| | R"({"foo":"bar","args":"{\"arg1\":"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": )",
|
| | R"({"foo":"bar","args":"{\"arg1\":"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": 1)",
|
| | R"({"foo":"bar","args":"{\"arg1\":"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": 1 )",
|
| | R"({"foo":"bar","args":"{\"arg1\":1"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": ")",
|
| | R"({"foo":"bar","args":"{\"arg1\":\""})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "1")",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"1\""})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": [)",
|
| | R"({"foo":"bar","args":"["})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": [1)",
|
| | R"({"foo":"bar","args":"["})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": [1 )",
|
| | R"({"foo":"bar","args":"[1"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": ["1")",
|
| | R"({"foo":"bar","args":"[\"1\""})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": [1,)",
|
| | R"({"foo":"bar","args":"[1,"})"
|
| | );
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": [)",
|
| | R"({"foo":"bar","args":"{\"arg1\":["})"
|
| | );
|
| |
|
| |
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\u)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\u"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\u0)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\u0"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\u00)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\u00"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\u000)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\u000"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\u0000)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\u0000"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud8)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud8"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud80)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud80"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800\)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800\\"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800\u)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800\\u"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800\ud)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800\\ud"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800\udc)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800\\udc"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800\udc0)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800\\udc0"})"
|
| | );
|
| | test_with_args(
|
| | R"({"foo": "bar", "args": {"arg1": "\ud800\udc00)",
|
| | R"({"foo":"bar","args":"{\"arg1\":\"\\ud800\\udc00"})"
|
| | );
|
| | }
|
| |
|
| | static void test_positions() {
|
| | {
|
| | common_chat_msg_parser builder("Hello, world!", false, {});
|
| | assert_equals<size_t>(0, builder.pos());
|
| | assert_throws([&]() { builder.move_to(100); });
|
| | assert_equals<size_t>(0, builder.pos());
|
| | assert_throws([&]() { builder.move_back(1); });
|
| | assert_equals<size_t>(0, builder.pos());
|
| |
|
| | builder.move_to(8);
|
| | assert_equals<size_t>(8, builder.pos());
|
| | builder.move_back(1);
|
| | assert_equals<size_t>(7, builder.pos());
|
| | assert_equals("world!", builder.consume_rest());
|
| |
|
| | builder.move_to(0);
|
| | assert_equals<size_t>(0, builder.pos());
|
| |
|
| | assert_throws([&]() { builder.finish(); });
|
| | assert_equals<size_t>(0, builder.pos());
|
| |
|
| | builder.move_to(builder.input().size());
|
| | builder.finish();
|
| | }
|
| | {
|
| | common_chat_msg_parser builder("Hello, world!", true, {});
|
| |
|
| | builder.move_to(builder.input().size());
|
| | assert_equals<size_t>(builder.input().size(), builder.pos());
|
| | builder.finish();
|
| | }
|
| | }
|
| |
|
| | int main() {
|
| | test_positions();
|
| | test_json_with_dumped_args_no_args();
|
| | test_json_with_dumped_args();
|
| | test_reasoning();
|
| | test_regex();
|
| | test_deepseek_v3_1_tool_calls();
|
| | std::cout << "All tests passed!\n";
|
| | return 0;
|
| | }
|
| |
|