Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions common/chat-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,32 @@ static void common_chat_parse_deepseek_v3_1(common_chat_msg_parser & builder) {
}
}

static void common_chat_parse_gigachat_v3(common_chat_msg_parser & builder) {
if (!builder.syntax().parse_tool_calls) {
builder.add_content(builder.consume_rest());
return;
}

// Regex to capture function name from JSON after "function call<|role_sep|\n"
static const common_regex function_regex(
R"(<\|message_sep\|>\n\nfunction call<\|role_sep\|>\n\s*\{\s*\"name\"\s*:\s*\"([^\"]+)\"\s*,\s*\"arguments\"\s*:)"
);

// Closing token of a tool call
static const common_regex close_regex(
R"(\}\s*)"
);

parse_json_tool_calls(
builder,
/* block_open = */ std::nullopt,
/* function_regex_start_only = */ std::nullopt,
/* function_regex = */ function_regex,
close_regex,
/* block_close = */ std::nullopt
);
}

static void common_chat_parse_minimax_m2(common_chat_msg_parser & builder) {
static const xml_tool_call_format form {
/* form.scope_start = */ "<minimax:tool_call>",
Expand Down Expand Up @@ -1479,6 +1505,9 @@ static void common_chat_parse(common_chat_msg_parser & builder) {
case COMMON_CHAT_FORMAT_XIAOMI_MIMO:
common_chat_parse_xiaomi_mimo(builder);
break;
case COMMON_CHAT_FORMAT_GIGACHAT_V3:
common_chat_parse_gigachat_v3(builder);
break;
default:
throw std::runtime_error(std::string("Unsupported format: ") + common_chat_format_name(builder.syntax().format));
}
Expand Down
66 changes: 66 additions & 0 deletions common/chat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ const char * common_chat_format_name(common_chat_format format) {
case COMMON_CHAT_FORMAT_PEG_SIMPLE: return "peg-simple";
case COMMON_CHAT_FORMAT_PEG_NATIVE: return "peg-native";
case COMMON_CHAT_FORMAT_PEG_CONSTRUCTED: return "peg-constructed";
case COMMON_CHAT_FORMAT_GIGACHAT_V3: return "GigaChat V3";
default:
throw std::runtime_error("Unknown chat format");
}
Expand Down Expand Up @@ -1616,6 +1617,66 @@ static common_chat_params common_chat_params_init_deepseek_v3_1(const common_cha
return data;
}

static common_chat_params common_chat_params_init_gigachat_v3(
const common_chat_template & tmpl,
const struct templates_params & inputs) {

common_chat_params data;

auto prompt = apply(tmpl, inputs);
data.prompt = prompt;
data.format = COMMON_CHAT_FORMAT_GIGACHAT_V3;

data.preserved_tokens = {
"<|message_sep|>\n\n",
"<|role_sep|>\n",
};

if (inputs.tools.is_array() && !inputs.tools.empty()) {
data.grammar_lazy = inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_REQUIRED && inputs.json_schema.is_null();

data.grammar = build_grammar([&](const common_grammar_builder & builder) {
std::vector<std::string> rules;


foreach_function(inputs.tools, [&](const json & tool) {
const auto & function = tool.at("function");
std::string name = function.at("name");
auto parameters = function.at("parameters");
builder.resolve_refs(parameters);

// JSON schema for this tool
json schema = {
{"type", "object"},
{"properties", {
{"name", {{"type", "string"}, {"const", name}}},
{"arguments", parameters}
}},
{"required", json::array({"name", "arguments"})}
};

// Add a rule for this tool
rules.push_back(
R"("<|message_sep|>\n\nfunction call<|role_sep|>\n")" +
builder.add_schema(name + "-call", schema)
);
});

builder.add_rule("root",
"(" + string_join(rules, " | ") + ")" +
(inputs.parallel_tool_calls ? "*" : "")
);

data.grammar_triggers.push_back({
COMMON_GRAMMAR_TRIGGER_TYPE_WORD,
"<|message_sep|>\n\nfunction call<|role_sep|>\n"
});
});
}

return data;
}

static common_chat_params common_chat_params_init_minimax_m2(const common_chat_template & tmpl, const struct templates_params & params) {
common_chat_params data;
data.grammar_lazy = params.tools.is_array() && !params.tools.empty() && params.tool_choice != COMMON_CHAT_TOOL_CHOICE_REQUIRED;
Expand Down Expand Up @@ -2601,6 +2662,11 @@ static common_chat_params common_chat_templates_apply_jinja(
return common_chat_params_init_apriel_1_5(tmpl, params);
}

// GigaChatV3 format detection
if (src.find("<|role_sep|>") != std::string::npos && src.find("<|message_sep|>") != std::string::npos) {
return common_chat_params_init_gigachat_v3(tmpl, params);
}

// Use generic handler when mixing tools + JSON schema.
// TODO: support that mix in handlers below.
if ((params.tools.is_array() && params.json_schema.is_object())) {
Expand Down
1 change: 1 addition & 0 deletions common/chat.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ enum common_chat_format {
COMMON_CHAT_FORMAT_QWEN3_CODER_XML,
COMMON_CHAT_FORMAT_APRIEL_1_5,
COMMON_CHAT_FORMAT_XIAOMI_MIMO,
COMMON_CHAT_FORMAT_GIGACHAT_V3,

// These are intended to be parsed by the PEG parser
COMMON_CHAT_FORMAT_PEG_SIMPLE,
Expand Down
Loading