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
chat-auto-parser-generator.cpp510 linesDownload Raw Back to common
1#include "chat-auto-parser-helpers.h"2#include "chat-auto-parser.h"3#include "chat-peg-parser.h"4#include "chat.h"5#include "common.h"6#include "json-schema-to-grammar.h"7#include "log.h"8#include "nlohmann/json.hpp"9#include "peg-parser.h"10 11#include <stdexcept>12#include <string>13 14using json = nlohmann::ordered_json;15 16// Helper to iterate over tools/functions17static void foreach_function(const json & tools, const std::function<void(const json &)> & fn) {18    for (const auto & tool : tools) {19        if (!tool.contains("type") || tool.at("type") != "function" || !tool.contains("function")) {20            continue;21        }22        fn(tool);23    }24}25 26namespace autoparser {27 28parser_build_context::parser_build_context(common_chat_peg_builder & p, const generation_params & inputs) :29    p(p),30    inputs(inputs),31    reasoning_parser(p.eps()) {}32 33common_chat_params peg_generator::generate_parser(const common_chat_template &    tmpl,34                                                  const struct generation_params & inputs) {35    // Run differential analysis to extract template structure36    struct autoparser autoparser;37    autoparser.analyze_template(tmpl);38    return generate_parser(tmpl, inputs, autoparser);39}40 41common_chat_params peg_generator::generate_parser(const common_chat_template &    tmpl,42                                                  const struct generation_params & inputs,43                                                  const autoparser &              autoparser) {44    // Create the result structure45    common_chat_params data;46    data.prompt            = common_chat_template_direct_apply(tmpl, inputs);47    data.generation_prompt = common_chat_template_generation_prompt(tmpl, inputs);48    data.format            = COMMON_CHAT_FORMAT_PEG_NATIVE;49    data.preserved_tokens  = autoparser.preserved_tokens;50    data.additional_stops.insert(data.additional_stops.end(),51        autoparser.additional_stops.begin(), autoparser.additional_stops.end());52 53    std::string parser_generation_prompt = data.generation_prompt;54 55    if (inputs.continue_final_message != COMMON_CHAT_CONTINUATION_NONE && !inputs.continue_msg.empty()) {56        // Build up generation prompt manually57        const auto & msg = inputs.continue_msg;58 59        if (!autoparser.reasoning.start.empty()) {60            data.generation_prompt = data.generation_prompt.substr(0, data.generation_prompt.find(autoparser.reasoning.start));61            data.generation_prompt += autoparser.reasoning.start + msg.reasoning_content;62            if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT) {63                data.generation_prompt += autoparser.reasoning.end;64            }65        }66 67        if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT) {68            data.generation_prompt += msg.render_content();69        }70 71        data.prompt += data.generation_prompt;72    }73 74    auto parser = autoparser.build_parser(inputs, parser_generation_prompt);75    data.parser = parser.save();76 77    // Build grammar if tools are present78    bool has_tools =79        autoparser.tools.format.mode != tool_format::NONE && inputs.tools.is_array() && !inputs.tools.empty();80    std::string trigger_marker = !autoparser.tools.format.section_start.empty() ? autoparser.tools.format.section_start :81                                                                                  autoparser.tools.format.per_call_start;82 83    bool has_response_format = !inputs.json_schema.empty() && inputs.json_schema.is_object();84    bool include_grammar = has_response_format || (has_tools &&85            ((inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO && !trigger_marker.empty()) ||86              inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED));87 88    if (include_grammar) {89        data.grammar_lazy = !has_response_format && inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO;90        data.grammar      = build_grammar([&](const common_grammar_builder & builder) {91            foreach_function(inputs.tools, [&](const json & tool) {92                const auto & function = tool.at("function");93                auto         schema   = function.contains("parameters") ? function.at("parameters") : json::object();94                builder.resolve_refs(schema);95            });96            if (has_response_format) {97                auto schema = inputs.json_schema;98                builder.resolve_refs(schema);99            }100            parser.build_grammar(builder, data.grammar_lazy);101        });102 103        // Set grammar triggers based on tool section markers (fall back to per-call markers)104        if (data.grammar_lazy) {105            data.grammar_triggers = {106                { COMMON_GRAMMAR_TRIGGER_TYPE_WORD, trigger_marker }107            };108            if (autoparser.tools.format.openai_wrapper_trigger) {109                // model emits the OpenAI function wrapper, trigger on it110                data.grammar_triggers.push_back({ COMMON_GRAMMAR_TRIGGER_TYPE_WORD, "{\"type\": \"function\"," });111            }112        }113    }114 115    return data;116}117 118common_peg_arena autoparser::build_parser(const generation_params & inputs, const std::string & generation_prompt) const {119    if (!analysis_complete) {120        throw std::invalid_argument("Cannot call build_parser on autoparser without performing analysis first, call analyze_template(...)");121    }122    return build_chat_peg_parser([&](common_chat_peg_builder & p) {123        parser_build_context ctx(p, inputs);124        bool                 extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;125 126        ctx.extracting_reasoning = extract_reasoning && reasoning.mode != reasoning_mode::NONE;127        ctx.content              = &content;128        ctx.reasoning            = &reasoning;129 130        // Build reasoning parser131        ctx.reasoning_parser = reasoning.build_parser(ctx);132 133        auto parser = p.eps();134 135        bool has_tools           = inputs.tools.is_array() && !inputs.tools.empty();136        bool has_response_format = inputs.json_schema.is_object() && !inputs.json_schema.empty();137        bool pure_content        = reasoning.mode == reasoning_mode::NONE;138 139        if (has_response_format) {140            auto response_format = p.rule("response-format", p.content(p.schema(p.json(), "response-format-schema", inputs.json_schema)));141            parser = ctx.reasoning_parser + p.space() + p.choice({142                p.literal("```json") + p.space() + response_format + p.space() + p.literal("```"),143                p.space() + response_format  + p.space()144            }) + p.end();145            pure_content = false;146        } else if (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE && jinja_caps.supports_tool_calls) {147            parser = tools.build_parser(ctx);148            pure_content = false;149        } else {150            parser = content.build_parser(ctx);151        }152        const std::string reasoning_start = trim_whitespace(reasoning.start);153        return pure_content ? p.prefix(generation_prompt, reasoning_start) + parser : p.prefix(generation_prompt, reasoning_start) << parser;154    });155}156 157common_peg_parser analyze_reasoning::build_parser(parser_build_context & ctx) const {158    auto & p = ctx.p;159 160    if (!ctx.extracting_reasoning) {161        return p.eps();162    }163 164    if (mode == reasoning_mode::TAG_BASED || mode == reasoning_mode::TOOLS_ONLY) {165        if (!end.empty()) {166            if (!start.empty()) {167                // Standard tag-based: optional(<think>reasoning</think>)168                return p.optional(p.optspace(start) + p.reasoning(p.until(trim_whitespace(end))) + p.optspace(end));169            }170            // Delimiter-style (empty start)171            return p.optional(p.reasoning(p.until(trim_whitespace(end))) + p.optspace(end));172        }173    }174 175    return p.eps();176}177 178common_peg_parser analyze_content::build_parser(parser_build_context & ctx) const {179    auto & p = ctx.p;180 181    if (is_always_wrapped()) {182        if (ctx.extracting_reasoning) {183            return ctx.reasoning_parser + start + p.content(p.until(end)) + end + p.end();184        }185        return p.content(p.until(start)) + start + p.content(p.until(end)) + end + p.end();186    }187    return ctx.reasoning_parser + p.content(p.rest()) + p.end();188}189 190common_peg_parser analyze_content::build_optional_wrapped(parser_build_context & ctx) const {191    auto & p = ctx.p;192 193    if (is_always_wrapped()) {194        return p.optional(start + p.content(p.until(end)) + end);195    }196    return p.eps();197}198 199common_peg_parser analyze_tools::build_parser(parser_build_context & ctx) const {200    switch (format.mode) {201        case tool_format::JSON_NATIVE:202            return build_tool_parser_json_native(ctx);203        case tool_format::TAG_WITH_JSON:204            return build_tool_parser_tag_json(ctx);205        case tool_format::TAG_WITH_TAGGED:206            return build_tool_parser_tag_tagged(ctx);207        default:208            LOG_ERR("[ERROR] Template seems to support tool calls, but failed to determine tool format. Tool calling will not work properly. "209                "Check for a fixed template for your model in the models/templates directory of your llama.cpp installation or "210                "report an issue at https://github.com/ggml-org/llama.cpp/issues\n");211            return ctx.p.eps();212    }213}214 215common_peg_parser analyze_tools::build_tool_parser_json_native(parser_build_context & ctx) const {216    auto &       p           = ctx.p;217    const auto & inputs      = ctx.inputs;218 219    // Build effective field names with dot notation if function_field is set220    std::string name_field = format.name_field;221    std::string args_field = format.args_field;222 223    if (!format.function_field.empty() && format.function_field != "function" &&224        name_field.find('.') == std::string::npos) {225        name_field = format.function_field + "." + name_field;226        args_field = format.function_field + "." + args_field;227    }228 229    auto tools_parser = p.eps();230    if (format.section_start.empty() && !format.per_call_start.empty()) {231        auto single_tool_parser = p.standard_json_tools(232            format.per_call_start, format.per_call_end, inputs.tools, inputs.parallel_tool_calls,233            inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED, name_field, args_field, format.tools_array_wrapped,234            format.fun_name_is_key, format.id_field, format.gen_id_field, format.parameter_order, format.openai_wrapper_trigger);235        tools_parser = p.trigger_rule("tool-calls", p.one_or_more(single_tool_parser + p.space()));236    } else {237        tools_parser = p.standard_json_tools(238            format.section_start, format.section_end, inputs.tools, inputs.parallel_tool_calls,239            inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED, name_field, args_field, format.tools_array_wrapped,240            format.fun_name_is_key, format.id_field, format.gen_id_field, format.parameter_order, format.openai_wrapper_trigger);241    }242 243    // Handle content wrappers if present244    if (ctx.content && ctx.content->is_always_wrapped()) {245        auto wrapped_content = ctx.content->build_optional_wrapped(ctx);246        return ctx.reasoning_parser + wrapped_content + tools_parser + p.end();247    }248 249    std::string tool_start = "{";250    if (!format.section_start.empty()) {251        tool_start = format.section_start;252    } else if (!format.per_call_start.empty()) {253        tool_start = format.per_call_start;254    }255 256    return ctx.reasoning_parser + p.optional(p.content(p.until(tool_start))) + tools_parser + p.end();257}258 259common_peg_parser analyze_tools::build_func_parser(common_chat_peg_builder & p, const std::string & name,260                                                    const common_peg_parser & call_id_section, bool have_call_id,261                                                    const common_peg_parser & args,262                                                    std::optional<common_peg_parser> atomic_peek) const {263    auto              open           = p.tool_open(function.name_prefix + p.tool_name(p.literal(name)) + function.name_suffix);264    bool              matched_atomic = false;265    common_peg_parser func_parser    = p.eps();266 267    if (!function.args_separator.empty()) {268        open = open + p.space() + p.literal(function.args_separator);269    }270 271    if (!function.name_suffix.empty()) {272        func_parser    = open + call_id_section + p.space() + args;273        matched_atomic = true;274    } else if (have_call_id) {275        func_parser    = p.atomic(open + call_id_section) + p.space() + args;276        matched_atomic = true;277    } else if (atomic_peek.has_value()) {278        func_parser    = p.atomic(open + call_id_section + p.space() + *atomic_peek) + args;279        matched_atomic = true;280    } else {281        func_parser = open + call_id_section + p.space() + args;282    }283 284    if (!function.close.empty()) {285        func_parser = func_parser + p.space() + p.tool_close(p.literal(function.close));286    } else if (!format.per_call_end.empty()) {287        // When there's no func_close but there is a per_call_end marker, use peek() to ensure288        // we only emit tool_close when we can actually see the closing marker. This prevents289        // premature closing during partial parsing when we've seen e.g. "</" which could be290        // either "</tool_call>" (end) or "<arg_key>" prefix that failed to match.291        // Laguna (v4): the model may emit whitespace between the last </arg_value> and292        // </tool_call> even though the template renders them tight. Tolerate optional293        // leading space in the close lookahead so the tool call still closes.294        auto close_peek = arguments.tolerate_intertag_whitespace295                              ? p.peek(p.space() + p.literal(format.per_call_end))296                              : p.peek(p.literal(format.per_call_end));297        func_parser = func_parser + p.tool_close(close_peek);298    } else {299        func_parser = func_parser + p.tool_close(p.space());  // force this to process tool closing callbacks in mapper300    }301    if (!matched_atomic) {302        func_parser = p.atomic(func_parser);303    }304    return func_parser;305}306 307common_peg_parser analyze_tools::build_tool_parser_tag_json(parser_build_context & ctx) const {308    auto &       p           = ctx.p;309    const auto & inputs      = ctx.inputs;310 311    common_peg_parser tool_choice = p.choice();312 313    foreach_function(inputs.tools, [&](const json & tool) {314        const auto & func   = tool.at("function");315        std::string  name   = func.at("name");316        const auto & schema = func.contains("parameters") ? func.at("parameters") : json::object();317 318        // Build call_id parser based on position (if supported)319        bool have_call_id = false;320        common_peg_parser call_id_section = p.eps();321        if (call_id.pos == call_id_position::BETWEEN_FUNC_AND_ARGS && !call_id.prefix.empty() &&322            (!call_id.suffix.empty() || !arguments.start.empty())) {323            if (!call_id.suffix.empty()) {324                call_id_section = p.optional(call_id.prefix + p.tool_id(p.until(call_id.suffix))) + call_id.suffix;325            } else {326                call_id_section = p.optional(call_id.prefix + p.tool_id(p.until(arguments.start)));327            }328            have_call_id = true;329        }330        auto args_parser = p.tool_args(p.schema(p.json(), "tool-" + name + "-schema", schema));331        if (!arguments.start.empty()) {332            args_parser = p.literal(arguments.start) + args_parser;333        }334        if (!arguments.end.empty()) {335            args_parser = args_parser + p.literal(arguments.end);336        }337 338        auto atomic_peek = !arguments.start.empty() ? std::optional(p.peek(p.literal(arguments.start))) : std::nullopt;339        auto func_parser = build_func_parser(p, name, call_id_section, have_call_id, args_parser, atomic_peek);340        tool_choice |= p.rule("tool-" + name, func_parser);341    });342 343    auto require_calls = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED;344 345    common_peg_parser tool_calls = p.eps();346 347    if (!format.per_call_start.empty()) {348        auto wrapped_call = format.per_call_start + tool_choice + format.per_call_end;349        if (inputs.parallel_tool_calls) {350            tool_calls = p.trigger_rule("tool-call", wrapped_call + p.zero_or_more(p.space() + wrapped_call));351        } else {352            tool_calls = p.trigger_rule("tool-call", wrapped_call);353        }354        if (!format.section_start.empty()) {355            tool_calls = p.trigger_rule("tool-calls",356                                        p.literal(format.section_start) + p.space() + tool_calls + p.space() +357                                            (format.section_end.empty() ? p.end() : p.literal(format.section_end)));358        }359    } else {360        std::string separator = ", ";  // Default361        if (inputs.parallel_tool_calls) {362            tool_calls = p.trigger_rule("tool-call", format.section_start + tool_choice +363                                                         p.zero_or_more(separator + tool_choice) + format.section_end);364        } else {365            tool_calls = p.trigger_rule("tool-call", format.section_start + tool_choice + format.section_end);366        }367    }368 369    if (!require_calls) {370        tool_calls = p.optional(tool_calls);371    }372 373    std::string trigger_marker       = !format.section_start.empty() ? format.section_start : format.per_call_start;374    auto        content_before_tools = trigger_marker.empty() ? p.eps() : p.until(trigger_marker);375    return ctx.reasoning_parser + p.optional(p.content(content_before_tools)) + tool_calls + p.end();376}377 378common_peg_parser analyze_tools::build_tool_parser_tag_tagged(parser_build_context & ctx) const {379    auto &       p           = ctx.p;380    const auto & inputs      = ctx.inputs;381 382    auto until_suffix = p.rule("until-suffix", p.until(arguments.value_suffix));383 384    common_peg_parser tool_choice = p.choice();385 386    foreach_function(inputs.tools, [&](const json & tool) {387        const auto &          func       = tool.at("function");388        std::string           name       = func.at("name");389        auto                  params     = func.contains("parameters") ? func.at("parameters") : json::object();390        const auto &          properties = params.contains("properties") ? params.at("properties") : json::object();391 392        std::set<std::string> required;393        if (params.contains("required")) {394            params.at("required").get_to(required);395        }396 397        auto schema_info = common_schema_info();398        schema_info.resolve_refs(params);399 400        // Build parser for each argument, separating required and optional401        std::vector<common_peg_parser> required_parsers;402        std::vector<common_peg_parser> optional_parsers;403        for (const auto & [param_name, param_schema] : properties.items()) {404            bool is_required = required.find(param_name) != required.end();405 406            auto arg =407                p.tool_arg(p.tool_arg_open(arguments.name_prefix + p.tool_arg_name(p.literal(param_name)) +408                                           arguments.name_suffix) +409                           arguments.value_prefix +410                           (schema_info.resolves_to_string(param_schema) ?411                                p.ac(p.tool_arg_string_value(until_suffix) +412                                    p.tool_arg_close(p.literal(arguments.value_suffix)), arguments.value_suffix) :413                                (p.tool_arg_json_value(p.schema(414                                    p.json(), "tool-" + name + "-arg-" + param_name + "-schema", param_schema, false)) +415                                    p.tool_arg_close(p.literal(arguments.value_suffix)))));416 417            auto named_arg = p.rule("tool-" + name + "-arg-" + param_name, arg);418            if (is_required) {419                required_parsers.push_back(named_arg);420            } else {421                optional_parsers.push_back(named_arg);422            }423        }424 425        // Build required arg sequence in definition order426        common_peg_parser args_seq = p.eps();427        for (size_t i = 0; i < required_parsers.size(); i++) {428            if (i > 0) {429                args_seq = args_seq + p.space();430            }431            args_seq = args_seq + required_parsers[i];432        }433 434        // Build optional args with flexible ordering435        if (!optional_parsers.empty()) {436            common_peg_parser any_opt = p.choice();437            for (const auto & opt : optional_parsers) {438                any_opt |= opt;439            }440            args_seq = args_seq + p.repeat(p.space() + any_opt, 0, -1);441        }442 443        if (!arguments.start.empty()) {444            args_seq = p.literal(arguments.start) + args_seq;445        }446        if (!arguments.end.empty()) {447            args_seq = args_seq + p.literal(arguments.end);448        }449 450        // Build call_id parser based on position (if supported)451        common_peg_parser call_id_section = p.eps();452        bool have_call_id = false;453        if (call_id.pos == call_id_position::BETWEEN_FUNC_AND_ARGS && !call_id.prefix.empty() &&454            (!call_id.suffix.empty() || !arguments.start.empty())) {455            have_call_id = true;456            if (!call_id.suffix.empty()) {457                call_id_section = p.optional(call_id.prefix + p.tool_id(p.until(call_id.suffix)) + call_id.suffix);458            } else {459                call_id_section = p.optional(call_id.prefix + p.tool_id(p.until(arguments.start)));460            }461        }462 463        // Only peek for an arg tag when there are required args that must follow.464        // When all args are optional, the model may emit no arg tags at all (#20650).465        auto atomic_peek = (!arguments.name_prefix.empty() && !required_parsers.empty()) ?466            std::optional(p.peek(p.literal(arguments.name_prefix))) : std::nullopt;467        auto func_parser = build_func_parser(p, name, call_id_section, have_call_id, args_seq, atomic_peek);468        tool_choice |= p.rule("tool-" + name, func_parser);469    });470 471    auto require_tools = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED;472 473    common_peg_parser tool_calls = p.eps();474 475    if (!format.per_call_start.empty()) {476        auto wrapped_call = format.per_call_start + p.space() + tool_choice + p.space() + format.per_call_end;477        if (inputs.parallel_tool_calls) {478            tool_calls = p.trigger_rule("tool-call", wrapped_call + p.zero_or_more(p.space() + wrapped_call) + p.space());479        } else {480            tool_calls = p.trigger_rule("tool-call", wrapped_call + p.space());481        }482        if (!format.section_start.empty()) {483            tool_calls = p.trigger_rule("tool-calls",484                                        p.literal(format.section_start) + p.space() + tool_calls + p.space() +485                                            (format.section_end.empty() ? p.end() : p.literal(format.section_end) + p.space()));486        }487    } else {488        std::string separator = ", ";  // Default489 490        if (inputs.parallel_tool_calls) {491            tool_calls = p.trigger_rule("tool-call", format.section_start + p.space() + tool_choice +492                                                         p.zero_or_more(separator + tool_choice) + p.space() +493                                                         format.section_end);494        } else {495            tool_calls = p.trigger_rule(496                "tool-call", format.section_start + p.space() + tool_choice + p.space() + format.section_end);497        }498    }499 500    if (!require_tools) {501        tool_calls = p.optional(tool_calls);502    }503 504    std::string trigger_marker       = !format.section_start.empty() ? format.section_start : format.per_call_start;505    auto        content_before_tools = trigger_marker.empty() ? p.eps() : p.until(trigger_marker);506    return ctx.reasoning_parser + p.optional(p.content(content_before_tools)) + tool_calls + p.end();507}508 509}  // namespace autoparser510 
Brunobkr/llama.cpp_AlgMor24_github · Team Ai