Repository: ggerganov/llama.cpp
File: tests/test-chat-parser.cpp (C++)

Code before:
    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);

    // variant: function + fenced JSON
    {
        const char* variant = "fenced";
        const std::string in = "<｜tool▁calls▁begin｜><｜tool▁call▁begin｜>function<｜tool▁sep｜>get_time\n```json\n{\"city\": \"Tokyo\"}\n```<｜tool▁call▁end｜><｜tool▁calls▁end｜>";
        auto m = common_chat_parse(in, false, syntax);
        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(""), m.reasoning_content);
    }

    // variant: function + fenced JSON + 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,
        };
        const char* variant = "fenced_thinking";
        const std::string in = "REASONING</think><｜tool▁calls▁begin｜><｜tool▁call▁begin｜>function<｜tool▁sep｜>get_time\n```json\n{\"city\": \"Tokyo\"}\n```<｜tool▁call▁end｜><｜tool▁calls▁end｜>";
        auto m = common_chat_parse(in, false, syntax);
        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);
    }
    // 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,
        };
        const char* 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);
        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);

Review comment (suggestion):
```suggestion
        const std::string variant("fenced_thinking");
```

Code after:
}

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,
    };
    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);
    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
    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);

    // 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,
        };
        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);
        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);
    }
    // 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,
        };
        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｜>";