Repository: ggerganov/llama.cpp
File: common/chat.cpp (C++)

Code before:
        // Extract the content between the tags (capture group 1)
        auto content = builder.str(res->groups[1]);
        builder.add_content(content);
        builder.move_to(res->groups[0].end);
    }

    if (!builder.syntax().parse_tool_calls) {
        builder.add_content(builder.consume_rest());
        return;
    }

    // Look for tool calls
    static const common_regex tool_call_regex(regex_escape("
"));
    if (auto res = builder.try_find_regex(tool_call_regex)) {
        builder.move_to(res->groups[0].end);

        // Expect JSON array of tool calls
        auto tool_calls_data = builder.consume_json();
        if (tool_calls_data.json.is_array()) {
            if (!builder.add_tool_calls(tool_calls_data.json)) {
                builder.add_content("
" + tool_calls_data.json.dump());
            }
        } else {
            builder.add_content("
" + tool_calls_data.json.dump());
        }
    } else {
        builder.add_content(builder.consume_rest());
    }
}

static void common_chat_parse_nemotron_v2(common_chat_msg_parser & builder) {
    // Parse thinking tags
    builder.try_parse_reasoning("<think>", "</think>");
    if (!builder.syntax().parse_tool_calls) {
        builder.add_content(builder.consume_rest());
        return;
    }

    // Look for tool calls
    static const common_regex tool_call_regex(regex_escape("<TOOLCALL>"));
    if (auto res = builder.try_find_regex(tool_call_regex)) {
        builder.move_to(res->groups[0].end);

        // Expect JSON array of tool calls
        auto tool_calls_data = builder.consume_json();
        if (tool_calls_data.json.is_array()) {
            if (!builder.try_consume_literal("</TOOLCALL>")) {
                throw common_chat_msg_partial_exception("Incomplete tool call");
            }
            builder.add_tool_calls(tool_calls_data.json);
        } else {

Review comment (suggestion):
I'm pretty sure `try_find_regex()` places you at the end of the match if it passes. This shouldn't be necessary.

Code after:
        auto tool_calls_data = builder.consume_json();
        if (tool_calls_data.json.is_array()) {
            if (!builder.add_tool_calls(tool_calls_data.json)) {
                builder.add_content("
" + tool_calls_data.json.dump());
            }
        } else {
            builder.add_content("
" + tool_calls_data.json.dump());
        }
    } else {
        builder.add_content(builder.consume_rest());
    }
}

static void common_chat_parse_nemotron_v2(common_chat_msg_parser & builder) {
    // Parse thinking tags
    builder.try_parse_reasoning("<think>", "</think>");
    if (!builder.syntax().parse_tool_calls) {
        builder.add_content(builder.consume_rest());
        return;
    }

    // Look for tool calls
    static const common_regex tool_call_regex(regex_escape("<TOOLCALL>"));
    if (auto res = builder.try_find_regex(tool_call_regex)) {
        builder.move_to(res->groups[0].end);

        // Expect JSON array of tool calls
        auto tool_calls_data = builder.consume_json();
        if (tool_calls_data.json.is_array()) {
            if (!builder.try_consume_literal("</TOOLCALL>")) {
                throw common_chat_msg_partial_exception("Incomplete tool call");
            }
            builder.add_tool_calls(tool_calls_data.json);
        } else {
            throw common_chat_msg_partial_exception("Incomplete tool call");
        }
    }
    builder.add_content(builder.consume_rest());
}

static void common_chat_parse_seed_oss(common_chat_msg_parser & builder) {
    // Parse thinking tags first - this handles the main reasoning content
    builder.try_parse_reasoning("<seed:think>", "</seed:think>");

    if (!builder.syntax().parse_tool_calls) {
        builder.add_content(builder.consume_rest());
        return;
    }

    // Parse tool calls - Seed-OSS uses <seed:tool_call> format
    static const common_regex tool_call_begin_regex("<seed:tool_call>");