@@ -1354,6 +1354,77 @@ static common_chat_params common_chat_params_init_lfm2(const common_chat_templat
13541354 return data;
13551355}
13561356
1357+ static common_chat_params common_chat_params_init_gigachat_v3 (
1358+ const common_chat_template & tmpl,
1359+ const autoparser::templates_params & inputs) {
1360+
1361+ common_chat_params data;
1362+
1363+ data.prompt = common_chat_template_direct_apply (tmpl, inputs);
1364+ data.format = COMMON_CHAT_FORMAT_PEG_NATIVE;
1365+ data.supports_thinking = false ;
1366+ data.preserved_tokens = {
1367+ " <|message_sep|>\n\n " ,
1368+ " <|role_sep|>\n " ,
1369+ };
1370+
1371+ auto has_tools = inputs.tools .is_array () && !inputs.tools .empty ();
1372+ auto include_grammar = has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE;
1373+ auto tool_call_start_prefix = " <|message_sep|>\n\n function call<|role_sep|>\n " ;
1374+
1375+ auto parser = build_chat_peg_parser ([&](common_chat_peg_builder & p) {
1376+ if (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE) {
1377+ // Build a choice of all available tools
1378+ auto tool_choice = p.choice ();
1379+ for (const auto & tool : inputs.tools ) {
1380+ const auto & function = tool.at (" function" );
1381+ std::string name = function.at (" name" );
1382+ const auto & schema = function.at (" parameters" );
1383+
1384+ auto tool_name = p.json_member (" name" , " \" " + p.tool_name (p.literal (name)) + " \" " );
1385+ auto tool_args = p.json_member (" arguments" , p.tool_args (p.schema (p.json (), " tool-" + name + " -schema" , schema)));
1386+
1387+ auto tool_open = p.tool_open (p.literal (" {" ) << tool_name);
1388+
1389+ tool_choice |= p.rule (" tool-" + name, tool_open << " ," << tool_args << " }" );
1390+ }
1391+
1392+ // Define the tool call structure
1393+ auto min_calls = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED ? 1 : 0 ;
1394+ auto max_calls = 1 ; // parallel toolcalls are not supported
1395+ auto tool_call = p.rule (" tool-call" , p.literal (tool_call_start_prefix) + tool_choice);
1396+ auto tool_calls = p.trigger_rule (" tool-call-root" , p.repeat (tool_call, /* min = */ min_calls, /* max = */ max_calls));
1397+
1398+ return p.content (p.until (" <|message_sep|>\n\n " )) << tool_calls;
1399+ }
1400+
1401+ // Content only parser
1402+ include_grammar = false ;
1403+ return p.content (p.rest ());
1404+
1405+ });
1406+
1407+ data.parser = parser.save ();
1408+
1409+ if (include_grammar) {
1410+ data.grammar_lazy = has_tools && inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO;
1411+
1412+ data.grammar = build_grammar ([&](const common_grammar_builder & builder) {
1413+ foreach_function (inputs.tools , [&](const json & tool) {
1414+ const auto & function = tool.at (" function" );
1415+ auto schema = function.at (" parameters" );
1416+ builder.resolve_refs (schema);
1417+ });
1418+ parser.build_grammar (builder, data.grammar_lazy );
1419+ });
1420+
1421+ data.grammar_triggers = {
1422+ {COMMON_GRAMMAR_TRIGGER_TYPE_WORD, tool_call_start_prefix}
1423+ };
1424+ }
1425+ return data;
1426+ }
1427+
13571428namespace workaround {
13581429
13591430static void map_developer_role_to_system (json & messages) {
@@ -1525,6 +1596,15 @@ static common_chat_params common_chat_templates_apply_jinja(const struct common_
15251596 return common_chat_params_init_lfm2 (tmpl, params);
15261597 }
15271598
1599+ // GigaChatV3 format detection
1600+ if (src.find (" <|role_sep|>" ) != std::string::npos &&
1601+ src.find (" <|message_sep|>" ) != std::string::npos &&
1602+ src.find (" <|function_call|>" ) == std::string::npos
1603+ ) {
1604+ LOG_DBG (" Using specialized template: GigaChatV3\n " );
1605+ return common_chat_params_init_gigachat_v3 (tmpl, params);
1606+ }
1607+
15281608 try {
15291609 LOG_DBG (" Using differential autoparser\n " );
15301610 struct autoparser ::autoparser autoparser;
0 commit comments