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

Code before:

    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_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>");
    static const common_regex tool_call_end_regex("</seed:tool_call>");
    static const common_regex function_regex("<function=([^>]+)>");
    static const common_regex param_regex("<parameter=([^>]+)>");

    while (auto tool_res = builder.try_find_regex(tool_call_begin_regex)) {
        builder.consume_spaces();  // Consume whitespace after <seed:tool_call>

        // Look for function call inside tool call, ignore any content before it
        if (auto func_res = builder.try_find_regex(function_regex, std::string::npos, false)) {
            auto function_name = builder.str(func_res->groups[1]);

            // Parse Seed-OSS parameters <parameter=name>value</parameter>
            json args = json::object();
            // Parse all parameters
            while (auto param_res = builder.try_find_regex(param_regex, std::string::npos, false)) {

Review comment (suggestion):
Some of the earlier 3-series granite models have different tool calling behavior. I'm trying to verify whether any of them would return a single tool-call object (versus an array), but we may need to retain the clause that checks `is_array`.

Code after:
        // Restore position for try_parse_reasoning()
        builder.move_to(res->groups[0].begin);
    }
    builder.try_parse_reasoning("<think>", "</think>");

    // Parse response tags
    static const common_regex start_response_regex(regex_escape("<response>"));
    static const common_regex end_response_regex(regex_escape("</response>"));
    // Granite models output partial tokens such as "<" and "<response".
    // Same hack as reasoning parsing.
    if (builder.try_consume_regex(start_response_regex)) {
        builder.try_find_regex(end_response_regex);
    }

    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
        if (auto tool_call = builder.try_consume_json_with_dumped_args({{{"arguments"}}})) {
            if (!builder.add_tool_calls(tool_call->value) || tool_call->is_partial) {
                throw common_chat_msg_partial_exception("incomplete tool call");
            }
        }
    } else {
        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>");
    static const common_regex tool_call_end_regex("</seed:tool_call>");
    static const common_regex function_regex("<function=([^>]+)>");
    static const common_regex param_regex("<parameter=([^>]+)>");

    while (auto tool_res = builder.try_find_regex(tool_call_begin_regex)) {