char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aldehir Rojascommon : implement common_schema internal representation for JSON schemas (#28736)acecd5603

char/aliceai-t5-moe
5.7 KiB130 linesraw
1#include "parsers.h"
2
3// MiniCPM5 format:
4// - Reasoning: <think>{reasoning}</think> (optional)
5// - Tool calls: <function name="foo"><param name="bar">value</param></function>
6common_chat_params common_chat_params_init_minicpm5(const common_chat_template &          tmpl,
7                                                           const autoparser::generation_params & inputs) {
8    common_chat_params data;
9
10    data.prompt            = common_chat_template_direct_apply_impl(tmpl, inputs);
11    data.generation_prompt = common_chat_template_generation_prompt_impl(tmpl, inputs);
12    data.format            = COMMON_CHAT_FORMAT_PEG_NATIVE;
13    data.supports_thinking = true;
14    data.preserved_tokens  = {
15        "<function",
16        "<param",
17        "</function>",
18        "</param>",
19        "<think>",
20        "</think>",
21    };
22
23    data.thinking_start_tag = "<think>";
24    data.thinking_end_tags  = {"</think>"};
25
26    data.message_delimiters = {
27        { COMMON_CHAT_ROLE_ASSISTANT, "<|im_start|>assistant"             },
28        { COMMON_CHAT_ROLE_TOOL,      "<|im_start|>user\n<tool_response>" },
29        { COMMON_CHAT_ROLE_USER,      "<|im_start|>user"                  },
30        { COMMON_CHAT_ROLE_SYSTEM,    "<|im_start|>system"                },
31    };
32
33    auto has_tools           = inputs.tools.is_array() && !inputs.tools.empty();
34    auto has_response_format = inputs.json_schema.is_object() && !inputs.json_schema.empty();
35    auto extract_reasoning   = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
36    auto include_grammar     = has_response_format || (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE);
37
38    if (inputs.has_continuation()) {
39        const auto & msg = inputs.continue_msg;
40
41        data.generation_prompt = "<|im_start|>assistant\n<think>\n" + msg.reasoning_content;
42        if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT) {
43            data.generation_prompt += "\n</think>\n\n" + msg.render_content();
44        }
45
46        data.prompt += data.generation_prompt;
47    }
48
49    auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {
50        auto generation_prompt = p.literal("<|im_start|>assistant\n");
51
52        auto reasoning = p.eps();
53        if (extract_reasoning) {
54            reasoning = ("<think>" << p.reasoning(p.until("</think>")) << "</think>") + p.space();
55        }
56
57        // Response format parser
58        if (has_response_format) {
59            return generation_prompt + reasoning + p.content(p.schema(p.json(), "response-format", inputs.json_schema));
60        }
61
62        if (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE) {
63            // CDATA lets a value carry characters that would otherwise close the tag (e.g.
64            // </param>); capture the inner text only, excluding the CDATA markers.
65            auto string_value = p.choice({
66                p.literal("<![CDATA[") + p.ac(p.tool_arg_string_value(p.until("]]>")) + p.literal("]]>"), "]]>") + p.tool_arg_close(p.literal("</param>")),
67                p.negate(p.literal("<![CDATA[")) + p.ac(p.tool_arg_string_value(p.until("</param>")) + p.tool_arg_close(p.literal("</param>")), "</param>")
68            });
69
70            auto tool_choice = p.choice();
71            foreach_function(inputs.tools, [&](const json & tool) {
72                const auto &      function = tool.at("function");
73                const std::string name     = function.at("name");
74
75                std::vector<common_peg_parser> arg_rules;
76                foreach_parameter(function, [&](const common_chat_schema_property & prop, const common_chat_schema_document_ptr & doc) {
77                    auto value_parser = p.eps();
78                    if (prop.schema->may_be_string()) {
79                        value_parser = string_value;
80                    } else {
81                        value_parser = p.tool_arg_json_value(
82                                p.schema(p.json(), "tool-" + name + "-arg-" + prop.name + "-schema", doc, *prop.schema)
83                            ) + p.tool_arg_close(p.literal("</param>"));
84                    }
85
86                    arg_rules.push_back(p.tool_arg(
87                        p.tool_arg_open(p.literal("<param name=\"") + p.tool_arg_name(p.literal(prop.name)) + p.literal("\">")) +
88                        value_parser
89                    ));
90                });
91
92                auto args = p.eps();
93                if (!arg_rules.empty()) {
94                    args = p.zero_or_more(p.choice(arg_rules) + p.space());
95                }
96
97                auto tool_parser = p.tool(
98                    p.tool_open(p.literal("<function name=\"") + p.tool_name(p.literal(name)) + p.literal("\">"))
99                    << p.tool_args(args)
100                    << p.tool_close(p.literal("</function>")));
101
102                tool_choice |= p.rule("tool-" + name, tool_parser);
103            });
104
105            auto max_calls  = inputs.parallel_tool_calls ? -1 : 1;
106            auto tool_calls = p.trigger_rule("tool-call", p.repeat(tool_choice + p.space(), 1, max_calls));
107
108            auto content = p.content(p.until("<function"));
109
110            return generation_prompt + reasoning + content + tool_calls + p.end();
111        }
112
113        return generation_prompt + reasoning + p.content(p.rest()) + p.end();
114    });
115
116    data.parser = parser.save();
117
118    if (include_grammar) {
119        data.grammar_lazy = !(has_response_format || (has_tools && inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED));
120        data.grammar      = build_grammar([&](const common_grammar_builder & builder) {
121            parser.build_grammar(builder, data.grammar_lazy);
122        });
123
124        data.grammar_triggers = {
125            { COMMON_GRAMMAR_TRIGGER_TYPE_WORD, "<function" },
126        };
127    }
128
129    return data;
130}