Team Ai
Datasetpublic

Brunobkr/llama.cpp_AlgMor24_github

ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes3kdownloads
test-chat.cpp7078 linesDownload Raw Back to tests
1//  Tests chat handling, including grammar genration and parsing for tool calling, for various templates.2//3//  Also acts as a CLI to generate a Markdown summary of the formats of Jinja templates,4//  e.g. given Minja (http://github.com/google/minja) checked out in parent dir:5//6//    cmake -B build && cmake --build build --parallel && ./build/bin/test-chat ../minja/build/tests/*.jinja 2>/dev/null7//8#include "../src/llama-grammar.h"9#include "../src/unicode.h"10#include "../tools/server/server-chat.h"11#include "chat-auto-parser.h"12#include "chat.h"13#include "common.h"14#include "ggml.h"15#include "log.h"16 17#include <algorithm>18#include <exception>19#include <fstream>20#include <functional>21#include <iostream>22#include <nlohmann/json.hpp>23#include <set>24#include <stdexcept>25#include <string>26 27using json = nlohmann::ordered_json;28 29static std::ostream & operator<<(std::ostream & os, const common_chat_msg_diff & diff) {30    os << "{ content_delta: " << diff.content_delta << "; ";31    os << "reasoning_content_delta: " << diff.reasoning_content_delta << "; ";32    if (diff.tool_call_index != std::string::npos) {33        os << "tool_call_index: " << diff.tool_call_index << "; ";34        os << "tool_call_delta.name: " << diff.tool_call_delta.name << "; ";35        os << "tool_call_delta.id: " << diff.tool_call_delta.id << "; ";36        os << "tool_call_delta.arguments: " << diff.tool_call_delta.arguments << "; ";37    }38    os << "}";39    return os;40}41 42// operator<< for vector<common_chat_msg_diff>:43static std::ostream & operator<<(std::ostream & os, const std::vector<common_chat_msg_diff> & diffs) {44    os << "[\n";45    for (const auto & diff : diffs) {46        os << "  " << diff << ",\n";47    }48    os << "]";49    return os;50}51 52static std::ostream & operator<<(std::ostream & os, const common_chat_msg & msg) {53    os << "{ role: " << msg.role << "; ";54    os << "content: " << msg.content << "; ";55    os << "content_parts: [\n";56    for (const auto & part : msg.content_parts) {57        os << "  { type: " << part.type << "; text: " << part.text << " },\n";58    }59    os << "]; ";60    os << "reasoning_content: " << msg.reasoning_content << "; ";61    os << "tool_calls: [\n";62    for (const auto & tool_call : msg.tool_calls) {63        os << "  { name: " << tool_call.name << "; arguments: " << tool_call.arguments << "; id: " << tool_call.id64           << " },\n";65    }66    os << "]";67    os << "}";68    return os;69}70 71template <class T> static bool equals(const T & expected, const T & actual) {72    return expected == actual;73}74 75static common_chat_msg normalize(const common_chat_msg & msg) {76    common_chat_msg normalized = msg;77    for (auto & tool_call : normalized.tool_calls) {78        try {79            tool_call.arguments = json::parse(tool_call.arguments).dump();80        } catch (const std::exception &) {81        }82    }83    return normalized;84}85 86template <> bool equals(const common_chat_msg & expected, const common_chat_msg & actual) {87    return normalize(expected) == normalize(actual);88}89 90template <class T> static void assert_equals(const T & expected, const T & actual) {91    if (!equals(expected, actual)) {92        std::ostringstream oss_expected;93        oss_expected << expected;94        std::ostringstream oss_actual;95        oss_actual << actual;96        LOG_ERR("Expected: %s\n", oss_expected.str().c_str());97        LOG_ERR("Actual: %s\n", oss_actual.str().c_str());98        common_log_flush(common_log_main());99        throw std::runtime_error("Test failed");100    }101}102 103static void assert_contains(const std::string & haystack, const std::string & needle) {104    if (haystack.find(needle) == std::string::npos) {105        LOG_ERR("Expected to contain: %s\n", needle.c_str());106        LOG_ERR("Actual: %s\n", haystack.c_str());107        common_log_flush(common_log_main());108        throw std::runtime_error("Test failed");109    }110}111 112static void assert_not_contains(const std::string & haystack, const std::string & needle) {113    if (haystack.find(needle) != std::string::npos) {114        LOG_ERR("Expected NOT to contain: %s\n", needle.c_str());115        LOG_ERR("Actual: %s\n", haystack.c_str());116        common_log_flush(common_log_main());117        throw std::runtime_error("Test failed");118    }119}120 121static void assert_ends_with(const std::string & str, const std::string & suffix) {122    if (str.size() < suffix.size() ||123        str.compare(str.size() - suffix.size(), suffix.size(), suffix) != 0) {124        LOG_ERR("Expected to end with: %s\n", suffix.c_str());125        LOG_ERR("Actual: %s\n", str.c_str());126        common_log_flush(common_log_main());127        throw std::runtime_error("Test failed");128    }129}130 131static std::string read_file(const std::string & path) {132    std::ifstream fs(path, std::ios_base::binary);133    if (!fs.is_open()) {134        fs = std::ifstream("../" + path, std::ios_base::binary);135        if (!fs.is_open()) {136            throw std::runtime_error("Failed to open file: " + path);137        }138    }139    fs.seekg(0, std::ios_base::end);140    auto size = fs.tellg();141    fs.seekg(0);142    std::string out;143    out.resize(static_cast<size_t>(size));144    fs.read(out.data(), static_cast<std::streamsize>(size));145    return out;146}147 148static common_chat_templates_ptr read_templates(const std::string & path) {149    return common_chat_templates_ptr(common_chat_templates_init(/* model= */ nullptr, read_file(path)));150}151 152static std::unique_ptr<llama_grammar> build_grammar(const std::string & grammar_str) {153    return std::unique_ptr<llama_grammar>(154        llama_grammar_init_impl(nullptr, grammar_str.c_str(), "root", false, nullptr, 0, nullptr, 0));155}156 157// Helper to format a code point as a readable string158static std::string format_codepoint(uint32_t cp) {159    if (cp >= 32 && cp < 127) {160        return std::string("'") + static_cast<char>(cp) + "'";161    } else if (cp == '\n') {162        return "'\\n'";163    } else if (cp == '\r') {164        return "'\\r'";165    } else if (cp == '\t') {166        return "'\\t'";167    } else {168        return "U+" + std::to_string(cp);169    }170}171 172// Helper to format expected element from grammar stack173static std::string format_expected_element(const llama_grammar_rules & /* rules*/, const llama_grammar_element * elem) {174    if (!elem) {175        return "<end>";176    }177 178    switch (elem->type) {179        case LLAMA_GRETYPE_END:180            return "<end of rule>";181        case LLAMA_GRETYPE_ALT:182            return "<alternative>";183        case LLAMA_GRETYPE_RULE_REF:184            {185                // Find rule name - just show rule ID for now186                return "<rule-" + std::to_string(elem->value) + ">";187            }188        case LLAMA_GRETYPE_CHAR:189            {190                std::string                   result;191                const llama_grammar_element * pos   = elem;192                bool                          first = true;193 194                do {195                    if (!first) {196                        result += " | ";197                    }198                    first = false;199 200                    if (pos[1].type == LLAMA_GRETYPE_CHAR_RNG_UPPER) {201                        // Range like [a-z]202                        result += "[" + format_codepoint(pos->value) + "-" + format_codepoint(pos[1].value) + "]";203                        pos += 2;204                    } else {205                        result += format_codepoint(pos->value);206                        pos += 1;207                    }208                } while (pos->type == LLAMA_GRETYPE_CHAR_ALT);209 210                return result;211            }212        case LLAMA_GRETYPE_CHAR_NOT:213            {214                std::string                   result = "[^";215                const llama_grammar_element * pos    = elem;216                bool                          first  = true;217 218                do {219                    if (!first) {220                        result += " ";221                    }222                    first = false;223 224                    if (pos[1].type == LLAMA_GRETYPE_CHAR_RNG_UPPER) {225                        result += format_codepoint(pos->value) + "-" + format_codepoint(pos[1].value);226                        pos += 2;227                    } else {228                        result += format_codepoint(pos->value);229                        pos += 1;230                    }231                } while (pos->type == LLAMA_GRETYPE_CHAR_ALT);232 233                return result + "]";234            }235        case LLAMA_GRETYPE_CHAR_ANY:236            return "<any char>";237        case LLAMA_GRETYPE_TOKEN:238            return "<token-" + std::to_string(elem->value) + ">";239        case LLAMA_GRETYPE_TOKEN_NOT:240            return "<not-token-" + std::to_string(elem->value) + ">";241        default:242            return "<unknown>";243    }244}245 246// Get description of what the grammar expects at current position247static std::string get_expected_description(const llama_grammar_rules & rules, const llama_grammar_stacks & stacks) {248    if (stacks.empty()) {249        return "<no valid continuations>";250    }251 252    std::string           result;253    std::set<std::string> seen;254 255    for (const auto & stack : stacks) {256        if (stack.empty()) {257            if (seen.insert("<end>").second) {258                if (!result.empty()) {259                    result += " OR ";260                }261                result += "<end>";262            }263            continue;264        }265 266        const llama_grammar_element * elem = stack.back();267        std::string                   desc = format_expected_element(rules, elem);268        if (seen.insert(desc).second) {269            if (!result.empty()) {270                result += " OR ";271            }272            result += desc;273        }274    }275 276    return result;277}278 279// Result of a detailed grammar match attempt280struct grammar_match_result {281    bool        success            = false;  // Did the string fully match the grammar?282    size_t      matched_bytes      = 0;      // Bytes successfully matched before failure283    size_t      matched_codepoints = 0;      // Codepoints successfully matched before failure284    size_t      total_bytes        = 0;      // Total bytes in input285    size_t      total_codepoints   = 0;      // Total codepoints in input286    std::string matched_prefix;              // The portion that was successfully matched287    std::string failing_char;                // The character that caused failure (if any)288    std::string expected_description;        // What the grammar expected at failure point289    bool        incomplete = false;          // True if matched all input but grammar expects more290};291 292// Detailed version of match_string that returns failure information293static grammar_match_result match_string_detailed(const std::string & input, llama_grammar * grammar) {294    grammar_match_result result;295    result.total_bytes = input.size();296 297    const auto cpts         = unicode_cpts_from_utf8(input);298    result.total_codepoints = cpts.size();299 300    auto &       stacks_cur = llama_grammar_get_stacks(grammar);301    const auto & rules      = llama_grammar_get_rules(grammar);302 303    size_t byte_pos = 0;304 305    for (size_t i = 0; i < cpts.size(); i++) {306        const auto & cpt = cpts[i];307 308        // Get expected before accepting (for error reporting)309        std::string expected_before = get_expected_description(rules, stacks_cur);310 311        llama_grammar_accept(grammar, cpt);312 313        // Calculate byte position for this codepoint314        size_t cpt_bytes = 0;315        if (cpt < 0x80) {316            cpt_bytes = 1;317        } else if (cpt < 0x800) {318            cpt_bytes = 2;319        } else if (cpt < 0x10000) {320            cpt_bytes = 3;321        } else {322            cpt_bytes = 4;323        }324 325        if (stacks_cur.empty()) {326            // Grammar failed to match at this point327            result.matched_bytes        = byte_pos;328            result.matched_codepoints   = i;329            result.matched_prefix       = input.substr(0, byte_pos);330            result.failing_char         = format_codepoint(cpt);331            result.expected_description = expected_before;332            result.incomplete           = false;333            return result;334        }335 336        byte_pos += cpt_bytes;337    }338 339    // All input matched - check if grammar is complete340    result.matched_bytes      = input.size();341    result.matched_codepoints = cpts.size();342    result.matched_prefix     = input;343 344    if (std::any_of(stacks_cur.begin(), stacks_cur.end(), [](const auto & stack) { return stack.empty(); })) {345        // An empty stack means that the grammar has been completed346        result.success    = true;347        result.incomplete = false;348    } else {349        // Grammar expects more input350        result.success              = false;351        result.incomplete           = true;352        result.expected_description = get_expected_description(rules, stacks_cur);353    }354 355    return result;356}357 358// TODO: extract to common helper (copied from test-grammar-integration.cpp)359static bool match_string(const std::string & input, llama_grammar * grammar) {360    const auto cpts = unicode_cpts_from_utf8(input);361 362    auto & stacks_cur = llama_grammar_get_stacks(grammar);363 364    for (const auto & cpt : cpts) {365        llama_grammar_accept(grammar, cpt);366 367        if (stacks_cur.empty()) {368            // no stacks means that the grammar failed to match at this point369            return false;370        }371    }372 373    if (std::any_of(stacks_cur.begin(), stacks_cur.end(), [](const auto & stack) { return stack.empty(); })) {374        // An empty stack means that the grammar has been completed375        return true;376    }377 378    return false;379}380 381static std::string renormalize_json(const std::string & json_str) {382    try {383        auto json_obj = json::parse(json_str);384        return json_obj.dump();385    } catch (const std::exception & e) {386        return "";  // ignore parial JSON contents for comparison purposes387    }388}389 390static void assert_msg_equals(const common_chat_msg & expected,391                              const common_chat_msg & actual,392                              bool                    ignore_whitespace_differences = false) {393    assert_equals(expected.role, actual.role);394    if (ignore_whitespace_differences) {395        assert_equals(string_strip(expected.content), string_strip(actual.content));396    } else {397        assert_equals(expected.content, actual.content);398    }399    assert_equals(expected.content_parts.size(), actual.content_parts.size());400    for (size_t i = 0; i < expected.content_parts.size(); i++) {401        const auto & expected_part = expected.content_parts[i];402        const auto & actual_part   = actual.content_parts[i];403        assert_equals(expected_part.type, actual_part.type);404        if (ignore_whitespace_differences) {405            assert_equals(string_strip(expected_part.text), string_strip(actual_part.text));406        } else {407            assert_equals(expected_part.text, actual_part.text);408        }409    }410    if (ignore_whitespace_differences) {411        assert_equals(string_strip(expected.reasoning_content), string_strip(actual.reasoning_content));412    } else {413        assert_equals(expected.reasoning_content, actual.reasoning_content);414    }415    assert_equals(expected.tool_calls.size(), actual.tool_calls.size());416    for (size_t i = 0; i < expected.tool_calls.size(); i++) {417        const auto & expected_tool_call = expected.tool_calls[i];418        const auto & actual_tool_call   = actual.tool_calls[i];419        assert_equals(expected_tool_call.name, actual_tool_call.name);420        assert_equals(renormalize_json(expected_tool_call.arguments), renormalize_json(actual_tool_call.arguments));421        assert_equals(expected_tool_call.id, actual_tool_call.id);422    }423}424 425static common_chat_tool special_function_tool{426    /* .name = */ "special_function",427    /* .description = */ "I'm special",428    /* .parameters = */ R"({429        "type": "object",430        "properties": {431            "arg1": {432                "type": "integer",433                "description": "The arg."434            }435        },436        "required": ["arg1"]437    })",438};439static common_chat_tool special_function_tool_with_optional_param{440    /* .name = */ "special_function_with_opt",441    /* .description = */ "I'm special but have optional stuff",442    /* .parameters = */ R"({443        "type": "object",444        "properties": {445            "arg1": {446                "type": "integer",447                "description": "The arg."448            },449            "arg2": {450                "type": "integer",451                "description": "The optional arg."452            }453        },454        "required": ["arg1"]455    })",456};457 458static common_chat_tool empty_args_tool{459    /* .name = */ "empty_args",460    /* .description = */ "A tool that takes no arguments",461    /* .parameters = */ R"({462        "type": "object",463        "properties": {}464    })",465};466 467static common_chat_tool empty_args_tool_no_properties{468    /* .name = */ "empty_args_no_props",469    /* .description = */ "A tool that takes no arguments and has no properties",470    /* .parameters = */ R"({471        "type": "object"472    })",473};474 475static common_chat_tool python_tool{476    /* .name = */ "python",477    /* .description = */ "an ipython interpreter",478    /* .parameters = */ R"({479        "type": "object",480        "properties": {481            "code": {482                "type": "string",483                "description": "Python code to execute."484            }485        },486        "required": ["code"]487    })",488};489 490static common_chat_tool html_tool{491    /* .name = */ "html",492    /* .description = */ "an html validator",493    /* .parameters = */ R"({494        "type": "object",495        "properties": {496            "markup": {497                "type": "string",498                "description": "HTML markup to validate."499            }500        },501        "required": ["markup"]502    })",503};504 505static common_chat_tool get_time_tool{506    /* .name = */ "get_time",507    /* .description = */ "Get the current time in a city",508    /* .parameters = */ R"({509        "type": "object",510        "properties": {511            "city": {512                "type": "string",513                "description": "City name"514            }515        },516        "required": ["city"]517    })",518};519 520static common_chat_tool get_weather_tool{521    /* .name = */ "get_weather",522    /* .description = */ "Get the current weather in a city",523    /* .parameters = */ R"({524        "type": "object",525        "properties": {526            "city": {527                "type": "string",528                "description": "City name"529            }530        },531        "required": ["city"]532    })",533};534 535static common_chat_tool todo_list{536    /* .name = */ "todo_list",537    /* .description = */ "Create or update the todo list",538    /* .parameters = */ R"({539        "type": "object",540        "properties": {541            "todos": {542                "type": "array",543                "description": "List of TODO list items"544            }545        },546        "required": ["todos"]547    })",548};549 550static common_chat_tool edit_tool{551    /* .name = */ "edit",552    /* .description = */ "Edit file",553    /* .parameters = */ R"({554        "type": "object",555        "properties": {556            "filename": {557                "type": "string",558                "description": "Path of file to edit"559            },560            "oldString": {561                "type": "string",562                "description": "String to replace"563            },564            "newString": {565                "type": "string",566                "description": "New (replacement) value"567            }568        },569        "required": ["filename", "oldString", "newString"]570    })",571};572 573static common_chat_tool manage_todo_list_tool{574    /* .name = */ "manage_todo_list",575    /* .description = */ "Create or update the todo list",576    /* .parameters = */ R"({577        "type": "object",578        "properties": {579            "todos": {580                "type": "array",581                "description": "List of TODO list items"582            }583        },584        "required": ["todos"]585    })",586};587 588static common_chat_tool run_in_terminal_tool{589    /* .name = */ "run_in_terminal",590    /* .description = */ "Run a shell command.",591    /* .parameters = */ R"({592        "type": "object",593        "properties": {594            "command": {595                "type": "string",596                "description": "Shell command to run"597            }598        },599        "required": ["command"]600    })",601};602 603static common_chat_tool magic_tool{604    /* .name = */ "magic",605    /* .description = */ "Magic tool that takes a hash",606    /* .parameters = */ R"({607        "type": "object",608        "properties": {609            "name": {610                "type": "string"611            },612            "ref": {613                "type": "string"614            }615        },616        "required": ["name", "ref"]617    })",618};619 620static common_chat_tool magic_int_tool{621    /* .name = */ "magic_int",622    /* .description = */ "Magic tool that takes a hash",623    /* .parameters = */ R"({624        "type": "object",625        "properties": {626            "ref": {627                "type": "integer"628            },629            "name": {630                "type": "string"631            }632        },633        "required": ["ref"]634    })",635};636 637static common_chat_tool amount_tool{638    /* .name = */ "amount",639    /* .description = */ "Amount converter",640    /* .parameters = */ R"({641        "type": "object",642        "properties": {643            "orig": {644                "type": "number"645            }646        },647        "required": ["orig"]648    })",649};650 651static common_chat_tool toggle_tool{652    /* .name = */ "toggle",653    /* .description = */ "Toggle a feature",654    /* .parameters = */ R"({655        "type": "object",656        "properties": {657            "enabled": {658                "type": "boolean",659                "description": "Whether to enable the feature"660            }661        },662        "required": ["enabled"]663    })",664};665 666static common_chat_tool nullable_tool{667    /* .name = */ "set_nullable",668    /* .description = */ "Set a nullable value",669    /* .parameters = */ R"({670        "type": "object",671        "properties": {672            "value": {673                "type": "null",674                "description": "A null value"675            }676        },677        "required": ["value"]678    })",679};680 681static common_chat_tool config_tool{682    /* .name = */ "set_config",683    /* .description = */ "Set configuration",684    /* .parameters = */ R"({685        "type": "object",686        "properties": {687            "config": {688                "type": "object",689                "description": "Configuration dict"690            }691        },692        "required": ["config"]693    })",694};695 696static common_chat_tool calendar_create_event_tool{697    /* .name = */ "Calendar.create_event",698    /* .description = */ "Create a calendar event",699    /* .parameters = */ R"({700        "type": "object",701        "properties": {702            "title": { "type": "string" },703            "participants": { "type": "array", "items": { "type": "string" } },704            "metadata": { "type": "object" }705        },706        "required": ["title", "participants", "metadata"]707    })",708};709 710static common_chat_tool imaginary_number_tool{711    /* .name = */ "imaginary_number",712    /* .description = */ "Imaginary number converter",713    /* .parameters = */ R"({714        "type": "object",715        "properties": {716            "number": {717                "type": "object",718                "properties": {719                    "real": {720                        "type": "number"721                    },722                    "imaginary": {723                        "type": "number"724                    }725                },726                "required": ["real", "imaginary"]727            }728        },729        "required": ["number"]730    })",731};732 733static common_chat_tool nested_args_tool{734    /* .name = */ "nested_args",735    /* .description = */ "Tool with nested array arguments",736    /* .parameters = */ R"({737        "type": "object",738        "properties": {739            "tags": {740                "type": "array",741                "items": { "type": "string" }742            },743            "entries": {744                "type": "array",745                "items": {746                    "type": "object",747                    "properties": {748                        "id": { "type": "integer" },749                        "label": { "type": "string" }750                    },751                    "required": ["id", "label"]752                }753            }754        },755        "required": ["tags", "entries"]756    })",757};758 759static common_chat_tool union_args_tool{760    /* .name = */ "union_args",761    /* .description = */ "Tool with union arguments",762    /* .parameters = */ R"({763        "type": "object",764        "properties": {765            "filter": {766                "anyOf": [767                    { "type": "array", "items": { "type": "string" } },768                    {769                        "type": "object",770                        "properties": {771                            "field": { "type": "string" },772                            "op": { "type": "string" }773                        },774                        "required": ["field", "op"]775                    }776                ]777            },778            "label": {779                "oneOf": [780                    { "type": "string" },781                    { "type": "object", "properties": { "text": { "type": "string" } } }782                ]783            },784            "limit": {785                "oneOf": [786                    { "type": "integer" },787                    {788                        "type": "object",789                        "properties": { "max": { "type": "integer" } },790                        "required": ["max"]791                    }792                ]793            }794        }795    })",796};797 798static common_chat_tool nullable_string_tool{799    /* .name = */ "set_nullable_str",800    /* .description = */ "Set a nullable string value",801    /* .parameters = */ R"({802        "type": "object",803        "properties": {804            "name": {805                "type": ["string", "null"],806                "description": "A nullable string"807            }808        },809        "required": ["name"]810    })",811};812 813static common_chat_tool nullable_string_null_first_tool{814    /* .name = */ "set_nullable_str_nf",815    /* .description = */ "Set a nullable string value with null first in type array",816    /* .parameters = */ R"({817        "type": "object",818        "properties": {819            "name": {820                "type": ["null", "string"],821                "description": "A nullable string with null first"822            }823        },824        "required": ["name"]825    })",826};827 828static common_chat_tool nullable_int_tool{829    /* .name = */ "set_nullable_int",830    /* .description = */ "Set a nullable integer value",831    /* .parameters = */ R"({832        "type": "object",833        "properties": {834            "count": {835                "type": ["integer", "null"],836                "description": "A nullable integer"837            }838        },839        "required": ["count"]840    })",841};842 843static common_chat_tool enum_no_type_tool{844    /* .name = */ "set_unit",845    /* .description = */ "Set a temperature unit",846    /* .parameters = */ R"({847        "type": "object",848        "properties": {849            "unit": {850                "enum": ["celsius", "fahrenheit"],851                "description": "Temperature unit"852            }853        },854        "required": ["unit"]855    })",856};857 858static common_chat_tool string_param_tool{859    /* .name = */ "string_param",860    /* .description = */ "Tool with string parameter for testing",861    /* .parameters = */ R"({862        "type": "object",863        "properties": {864            "text": {865                "type": "string",866                "description": "A text parameter"867            }868        },869        "required": []870    })",871};872 873static common_chat_tool quoted_unquoted_tool{874    /* .name = */ "quoted_unquoted",875    /* .description = */ "Tool with two string parameters, one for quoted string, one for unquoted",876    /* .parameters = */ R"({877        "type": "object",878        "properties": {879            "quoted": {880                "type": "string",881                "description": "Quoted value"882            },883            "unquoted": {884                "type": "string",885                "description": "Unquoted value"886            }887        },888        "required": ["quoted", "unquoted"]889    })",890};891 892 893static common_chat_tool tool_2req_4opt{894    /* .name = */ "tool_2req_4opt",895    /* .description = */ "Tool with 2 required and 4 optional params",896    /* .parameters = */ R"({897        "type": "object",898        "properties": {899            "req1": { "type": "string", "description": "Required string" },900            "req2": { "type": "integer", "description": "Required int" },901            "opt1": { "type": "string", "description": "Optional string 1" },902            "opt2": { "type": "integer", "description": "Optional int 1" },903            "opt3": { "type": "string", "description": "Optional string 2" },904            "opt4": { "type": "integer", "description": "Optional int 2" }905        },906        "required": ["req1", "req2"]907    })",908};909 910static common_chat_tool tool_2req_5opt{911    /* .name = */ "tool_2req_5opt",912    /* .description = */ "Tool with 2 required and 5 optional params",913    /* .parameters = */ R"({914        "type": "object",915        "properties": {916            "req1": { "type": "string", "description": "Required string" },917            "req2": { "type": "integer", "description": "Required int" },918            "opt1": { "type": "string", "description": "Optional string 1" },919            "opt2": { "type": "integer", "description": "Optional int 1" },920            "opt3": { "type": "string", "description": "Optional string 2" },921            "opt4": { "type": "integer", "description": "Optional int 2" },922            "opt5": { "type": "string", "description": "Optional string 3" }923        },924        "required": ["req1", "req2"]925    })",926};927 928static std::vector<common_chat_tool> tools{ special_function_tool, special_function_tool_with_optional_param,929                                            python_tool, html_tool, todo_list };930 931const common_chat_msg message_user{932    "user",933    "Hey there!",934    /* .content_parts = */ {},935    /* .tool_calls = */ {},936    /* .reasoning_content = */ "",937    /* .tool_name = */ "",938    /* .tool_call_id = */ "",939};940 941const common_chat_msg message_user_parts{942    "user",943    /* .content = */ "",944    /* .content_parts = */945    {946     { "text", "Hey" },947     { "text", "there" },948     },949    /* .tool_calls = */950    {                 },951    /* .reasoning_content = */952    "",953    /* .tool_name = */ "",954    /* .tool_call_id = */ "",955};956 957static common_chat_msg simple_assist_msg(const std::string & content,958                                         const std::string & reasoning_content = "",959                                         const std::string & tool_name         = "",960                                         const std::string & arguments         = "",961                                         const std::string & id                = "") {962    common_chat_msg msg;963    msg.role              = "assistant";964    msg.content           = content;965    msg.reasoning_content = reasoning_content;966    if (!tool_name.empty() || !id.empty()) {967        msg.tool_calls.push_back({ tool_name, arguments, id });968    }969    return msg;970}971 972static common_chat_msg message_with_tool_calls(const std::string & tool_name, const std::string & arguments) {973    return simple_assist_msg("", "", tool_name, arguments);974}975 976static common_chat_msg message_with_tool_calls_and_reasoning(const std::string & tool_name,977                                                             const std::string & arguments,978                                                             const std::string & reasoning) {979    return simple_assist_msg("", reasoning, tool_name, arguments);980}981 982static common_chat_msg message_with_reasoning_content_and_multiple_tool_calls(983    const std::string &                                      reasoning,984    const std::string &                                      content,985    const std::vector<std::pair<std::string, std::string>> & tool_calls) {986    common_chat_msg msg;987    msg.role              = "assistant";988    msg.content           = content;989    msg.reasoning_content = reasoning;990    for (const auto & [name, args] : tool_calls) {991        msg.tool_calls.push_back({ name, args, "" });992    }993    return msg;994}995 996static common_chat_msg message_with_content_and_tool_call(const std::string & content,997                                                          const std::string & tool_name,998                                                          const std::string & arguments) {999    return simple_assist_msg(content, "", tool_name, arguments);1000}1001 1002static common_chat_msg message_with_reasoning_and_tool_call(const std::string & reasoning,1003                                                            const std::string & tool_name,1004                                                            const std::string & arguments) {1005    return simple_assist_msg("", reasoning, tool_name, arguments);1006}1007 1008const common_chat_msg message_assist       = simple_assist_msg("Hello, world!\nWhat's up?");1009const common_chat_msg message_assist_empty = simple_assist_msg("");1010const common_chat_msg message_assist_thoughts_unparsed_deepseek =1011    simple_assist_msg("<think>I'm\nthinking</think>Hello, world!\nWhat's up?");1012const common_chat_msg message_assist_thoughts_unparsed_md =1013    simple_assist_msg("<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n```json\n{}```");1014const common_chat_msg message_assist_thoughts_unparsed_md_partial =1015    simple_assist_msg("<think>I'm\nthinking</think>Hello, world!\nWhat's up?\n```json\n{}");1016 1017const common_chat_msg message_assist_thoughts_unparsed_r7b =1018    simple_assist_msg("<|START_THINKING|>I'm\nthinking<|END_THINKING|>Hello, world!\nWhat's up?");1019const common_chat_msg message_assist_thoughts_unparsed_magistral =1020    simple_assist_msg("[THINK]raisonnement[/THINK]Réponse");1021const common_chat_msg message_assist_thoughts = simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking");1022const common_chat_msg message_assist_thoughts_unopened_unparsed =1023    simple_assist_msg("I'm\nthinking</think>Hello, world!\nWhat's up?");1024const common_chat_msg message_assist_thoughts_no_content = simple_assist_msg("", "I'm\nthinking");1025const common_chat_msg message_assist_call = simple_assist_msg("", "", "special_function", "{\"arg1\": 1}");1026const common_chat_msg message_assist_call_noopt =1027    simple_assist_msg("", "", "special_function_with_opt", "{\"arg1\": 1}");1028const common_chat_msg message_assist_call_withopt =1029    simple_assist_msg("", "", "special_function_with_opt", "{\"arg1\": 1, \"arg2\": 2}");1030const common_chat_msg message_assist_call_content =1031    simple_assist_msg("Hello, world!\nWhat's up?", "", "special_function", "{\"arg1\":1}");1032const common_chat_msg message_assist_call_empty_args  = simple_assist_msg("", "", "special_function");1033const common_chat_msg message_assist_call_cutoff_args = simple_assist_msg("", "", "special_function", "{\"arg");1034const common_chat_msg message_assist_call_thoughts =1035    simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1\":1}");1036const common_chat_msg message_assist_call_thoughts_unparsed =1037    simple_assist_msg("<think>I'm\nthinking</think>\n\n", "", "special_function", "{\"arg1\": 1}");1038const common_chat_msg message_assist_call_thoughts_content =1039    simple_assist_msg("Hello, world!\nWhat's up?", "I'm\nthinking", "special_function", "{\"arg1\": 1}");1040const common_chat_msg message_assist_call_id =1041    simple_assist_msg("", "", "special_function", "{\"arg1\":1}", /* .id = */ "123456789");1042const common_chat_msg message_assist_call_idx =1043    simple_assist_msg("", "", "special_function", "{\"arg1\":1}", /* .id = */ "0");1044const common_chat_msg message_assist_thoughts_call_idx =1045    simple_assist_msg("", "I'm\nthinking", "special_function", "{\"arg1\": 1}", /* id = */ "0");1046const common_chat_msg message_assist_thoughts_partial_call =1047    simple_assist_msg("", "I'm\nthinking", "special_function", "", /* id = */ "0");1048const common_chat_msg message_assist_call_python = simple_assist_msg("", "", "python", "{\"code\":\"print('hey')\"}");1049const common_chat_msg message_assist_call_python_lines =1050    simple_assist_msg("", "", "python", "{\"code\":\"# This is a program:\\nprint('hey')\"}");1051const common_chat_msg message_assist_call_python_lines_unclosed =1052    simple_assist_msg("", "", "python", "{\"code\":\"# This is a program:\\nprint('hey')");1053const common_chat_msg message_assist_json_content =1054    simple_assist_msg("{\n  \"response\": \"Hello, world!\\nWhat's up?\"\n}");1055const common_chat_msg message_assist_prefill_content   = simple_assist_msg("Hello, ", "I'm thinking");1056const common_chat_msg message_assist_prefill_reasoning = simple_assist_msg("", "I'm");1057 1058// Use for PEG parser implementations1059struct peg_test_case {1060    common_chat_templates_inputs params;1061    std::string                  input;1062    common_chat_msg              expect;1063    bool                         is_partial            = false;1064    bool                         expect_reconstruction = false;1065};1066 1067struct make_peg_parser {1068    common_chat_params params_;1069    common_peg_arena   arena_;1070    bool               detailed_debug_;1071 1072    make_peg_parser(common_chat_templates *              tmpls,1073                    const common_chat_templates_inputs & inputs,1074                    bool                                 detailed_debug = false) {1075        detailed_debug_ = detailed_debug;1076        params_         = common_chat_templates_apply(tmpls, inputs);1077        arena_.load(params_.parser);1078    }1079 1080    common_chat_msg parse(const std::string & msg, bool is_partial) const {1081        common_chat_parser_params parser_params(params_);1082        parser_params.debug = detailed_debug_;1083        return common_chat_peg_parse(arena_, msg, is_partial, parser_params);1084    }1085};1086 1087// Global template filter for --template flag1088static std::string g_template_filter;1089 1090// When true, run reconstruction test on every non-partial test and report results1091static bool g_force_reconstruction_test = false;1092 1093static void test_peg_parser(common_chat_templates *                      tmpls,1094                            const std::function<void(peg_test_case &)> & init,1095                            bool                                         detailed_debug) {1096    // UTF-8-safe truncation helper (same as in test_parser_with_streaming)1097    constexpr auto utf8_truncate_safe_len = [](const std::string_view s) -> size_t {1098        auto len = s.size();1099        if (len == 0) {1100            return 0;1101        }1102        auto i = len;1103        for (size_t back = 0; back < 4 && i > 0; ++back) {1104            --i;1105            unsigned char c = s[i];1106            if ((c & 0x80) == 0) {1107                return len;1108            }1109            if ((c & 0xC0) == 0xC0) {1110                size_t expected_len = 0;1111                if ((c & 0xE0) == 0xC0) {1112                    expected_len = 2;1113                } else if ((c & 0xF0) == 0xE0) {1114                    expected_len = 3;1115                } else if ((c & 0xF8) == 0xF0) {1116                    expected_len = 4;1117                } else {1118                    return i;1119                }1120                if (len - i >= expected_len) {1121                    return len;1122                }1123                return i;1124            }1125        }1126        return len - std::min(len, size_t(3));1127    };1128 1129    peg_test_case tc;1130    init(tc);1131    if (tc.params.messages.empty()) {1132        tc.params.messages = { message_user };1133    }1134    if (tc.expect.role.empty()) {1135        tc.expect.role = "assistant";1136    }1137 1138    auto parser = make_peg_parser(tmpls, tc.params, detailed_debug);1139    if (detailed_debug) {1140        LOG_DBG("Using parser: \n%s\n", parser.arena_.dump(parser.arena_.root()).c_str());1141        LOG_DBG("Generation prompt: '%s'\n", parser.params_.generation_prompt.c_str());1142    }1143 1144    common_chat_msg msg_accum;1145    common_chat_msg msg_prev;1146    msg_accum.role = msg_prev.role = "assistant";1147 1148    for (size_t i = 1; i <= tc.input.size(); ++i) {1149        auto            is_partial  = i < tc.input.size() || tc.is_partial;1150        // Use UTF-8 safe truncation to avoid corrupting multi-byte characters1151        size_t          safe_len    = utf8_truncate_safe_len(std::string_view(tc.input).substr(0, i));1152        std::string     prefix      = tc.input.substr(0, safe_len);1153        common_chat_msg msg_current = parser.parse(prefix, is_partial);1154 1155        for (const auto & diff : common_chat_msg_diff::compute_diffs(msg_prev, msg_current)) {1156            if (!diff.reasoning_content_delta.empty()) {1157                msg_accum.reasoning_content += diff.reasoning_content_delta;1158            }1159            if (!diff.content_delta.empty()) {1160                msg_accum.content += diff.content_delta;1161            }1162            if (diff.tool_call_index != std::string::npos) {1163                // During partial parsing, a new tool call may appear with empty name initially1164                // The name gets filled in as more input is parsed1165                while (msg_accum.tool_calls.size() <= diff.tool_call_index) {1166                    msg_accum.tool_calls.push_back({ "", "", "" });1167                }1168                // Always update name and id from diff (may change during incremental parsing), but only if the delta1169                // actually contains them1170                if (!diff.tool_call_delta.name.empty()) {1171                    msg_accum.tool_calls[diff.tool_call_index].name = diff.tool_call_delta.name;1172                }1173                if (!diff.tool_call_delta.id.empty()) {1174                    msg_accum.tool_calls[diff.tool_call_index].id = diff.tool_call_delta.id;1175                }1176                if (!diff.tool_call_delta.arguments.empty()) {1177                    msg_accum.tool_calls[diff.tool_call_index].arguments += diff.tool_call_delta.arguments;1178                }1179            }1180        }1181        try {1182            assert_msg_equals(msg_current, msg_accum, true);1183        } catch (std::exception & e) {1184            throw std::runtime_error((std::string("Error comparing accumulated message to current: ") + e.what()).c_str());1185        }1186 1187        msg_prev = msg_current;1188    }1189 1190    if (!tc.is_partial) {1191        assert_msg_equals(tc.expect, parser.parse(tc.input, false), true);1192    }1193    assert_msg_equals(tc.expect, msg_accum, true);1194 1195    // Test grammar if present in params1196    if (!parser.params_.grammar.empty()) {1197        auto grammar = build_grammar(parser.params_.grammar);1198        if (!grammar) {1199            throw std::runtime_error("Failed to build grammar: " + parser.params_.grammar);1200        }

Showing the first 1,200 of 7078 lines. Download the file for the rest.

Brunobkr/llama.cpp_AlgMor24_github · Team Ai