@@ -598,31 +598,12 @@ common_chat_templates_ptr common_chat_templates_init(
598598 " {%- if false %}" );
599599 }
600600
601- // Fix "Unknown argument ensure_ascii for function tojson" by replace tojson(ensure_ascii=False) to tojson()
602601 // Fix "Unknown method: items at row NN, column MM" by replace receiver.items() to (receiver | items)
603602 // TODO: Delete this when upstream minja fix tojson problem
604603 constexpr auto replaceToJsonInTemplate = [](const std::string& input) {
605604 constexpr auto isIdentifierChar = [](char c) {
606605 return std::isalnum (c) || c == ' _' ;
607606 };
608- constexpr auto skipWhitespace = [](const std::string& s, size_t pos) {
609- while (pos < s.length () && std::isspace (s[pos])) {
610- pos++;
611- }
612- return pos;
613- };
614- constexpr auto isCompleteToJson = [isIdentifierChar](const std::string& s, size_t pos) {
615- if (s.compare (pos, 6 , " tojson" ) != 0 ) return false ;
616- size_t start = pos;
617- size_t end = pos + 6 ;
618- if (start > 0 && isIdentifierChar (s[start - 1 ])) {
619- return false ;
620- }
621- if (end < s.length () && isIdentifierChar (s[end])) {
622- return false ;
623- }
624- return true ;
625- };
626607 constexpr auto matchBrackets = [](const std::string& s, size_t startPos, size_t & endPos) {
627608 size_t pos = startPos;
628609 int bracketCount = 0 ;
@@ -660,46 +641,6 @@ common_chat_templates_ptr common_chat_templates_init(
660641 }
661642 return false ;
662643 };
663- constexpr auto isToJsonInString = [](const std::string& s, size_t toJsonPos) {
664- bool inString = false ;
665- char stringChar = 0 ;
666- for (size_t i = 0 ; i < toJsonPos; i++) {
667- char c = s[i];
668- if (!inString && (c == ' "' || c == ' \' ' )) {
669- inString = true ;
670- stringChar = c;
671- }
672- else if (inString && c == stringChar) {
673- int backslashCount = 0 ;
674- size_t checkPos = i - 1 ;
675- while (checkPos >= 0 && s[checkPos] == ' \\ ' ) {
676- backslashCount++;
677- checkPos--;
678- }
679- if (backslashCount % 2 == 0 ) {
680- inString = false ;
681- stringChar = 0 ;
682- }
683- }
684- }
685- return inString;
686- };
687- constexpr auto replaceToJsonCall = [isToJsonInString, skipWhitespace, matchBrackets](const std::string& s, size_t startPos) {
688- if (isToJsonInString (s, startPos)) {
689- return s;
690- }
691- size_t pos = startPos + 6 ;
692- pos = skipWhitespace (s, pos);
693- if (pos >= s.length () || s[pos] != ' (' ) {
694- return s;
695- }
696- size_t endPos;
697- if (!matchBrackets (s, pos, endPos)) {
698- return s;
699- }
700- std::string result = s.substr (0 , startPos) + " tojson()" + s.substr (endPos + 1 );
701- return result;
702- };
703644 constexpr auto isCompleteItemsCall = [matchBrackets](const std::string& s, size_t dotPos) {
704645 if (s.compare (dotPos, 6 , " .items" ) != 0 ) return false ;
705646 size_t itemsEnd = dotPos + 6 ;
@@ -712,8 +653,7 @@ common_chat_templates_ptr common_chat_templates_init(
712653 }
713654 return true ;
714655 };
715- constexpr auto replaceItemsCall = [isToJsonInString, isCompleteItemsCall, matchBrackets, isIdentifierChar](const std::string& s, size_t dotPos) -> std::string {
716- if (isToJsonInString (s, dotPos)) return s;
656+ constexpr auto replaceItemsCall = [isCompleteItemsCall, matchBrackets, isIdentifierChar](const std::string& s, size_t dotPos) -> std::string {
717657 if (!isCompleteItemsCall (s, dotPos)) return s;
718658 size_t itemsEnd = dotPos + 6 ;
719659 size_t openParen = itemsEnd;
@@ -726,11 +666,11 @@ common_chat_templates_ptr common_chat_templates_init(
726666 std::string var = s.substr (varStart, dotPos - varStart);
727667 return s.substr (0 , varStart) + " (" + var + " | items)" + s.substr (closeParen + 1 );
728668 };
729- constexpr auto processTemplateBlock = [isCompleteToJson, skipWhitespace, replaceToJsonCall, replaceItemsCall](const std::string& block) {
669+ constexpr auto processTemplateBlock = [replaceItemsCall](const std::string& block) {
730670 std::string result = block;
731671 size_t pos = 0 ;
732672 while (pos < result.length ()) {
733- size_t nextToJson = result. find ( " tojson " , pos) ;
673+ size_t nextToJson = std::string::npos ;
734674 size_t nextItems = result.find (" .items" , pos);
735675 size_t nextPos = std::string::npos;
736676 bool isToJson = false ;
@@ -743,18 +683,7 @@ common_chat_templates_ptr common_chat_templates_init(
743683 }
744684 if (nextPos == std::string::npos) break ;
745685 if (isToJson) {
746- if (isCompleteToJson (result, nextPos)) {
747- size_t afterToJson = skipWhitespace (result, nextPos + 6 );
748- if (afterToJson < result.length () && result[afterToJson] == ' (' ) {
749- std::string replaced = replaceToJsonCall (result, nextPos);
750- if (replaced != result) {
751- result = replaced;
752- pos = nextPos + 7 ;
753- continue ;
754- }
755- }
756- }
757- pos = nextPos + 1 ;
686+ GGML_ASSERT (false );
758687 } else {
759688 std::string replaced = replaceItemsCall (result, nextPos);
760689 if (replaced != result) {
@@ -793,19 +722,13 @@ common_chat_templates_ptr common_chat_templates_init(
793722 };
794723 default_template_src = replaceToJsonInTemplate (default_template_src);
795724
796- // Fix MiniMax-M2 template bug: message.tool_calls[-1] silently fail
797- // Upstream minja seems do not support id[-1] and cause silently fail
725+ // Fix MiniMax-M2 template bug:
726+ // 1. Type of tool_call.arguments not checked
727+ // 2. last_tool_call.name should be tool_call.function.name rather than tool_call.name
798728 // TODO: remove this once the template is fixed.
799729 if (default_template_src.find (" ]~!b[" ) != std::string::npos
800- && default_template_src.find (" ]~b]" ) != std::string::npos
801- && default_template_src.find (" [-1]" ) != std::string::npos) {
802- LOG_INF (" Detected MiniMax-M2 template with unsupported syntax \" [-1]\" , applying automatic fix...\n " );
803- string_replace_all (default_template_src,
804- " {%- set reasoning_content = content.split('</think>')[0].strip('\\ n').split('<think>')[-1].strip('\\ n') %}" ,
805- " {%- set reasoning_content = content.split('</think>') -%} {%- set reasoning_content = reasoning_content|first -%} {%- set reasoning_content = reasoning_content.strip('\\ n').split('<think>') -%} {%- set reasoning_content = reasoning_content|last -%} {%- set reasoning_content = reasoning_content.strip('\\ n') %}" );
806- string_replace_all (default_template_src,
807- " {%- set content = content.split('</think>')[-1].strip('\\ n') %}" ,
808- " {%- set content = content.split('</think>') -%} {%- set content = content|last -%} {%- set content = content.strip('\\ n') %}" );
730+ && default_template_src.find (" ]~b]" ) != std::string::npos) {
731+ LOG_INF (" Detected MiniMax-M2 template , applying automatic fix...\n " );
809732 if (default_template_src.find (" {%- set last_tool_call.name = message.tool_calls[-1].name -%}" ) != std::string::npos &&
810733 default_template_src.find (" {%- for tool_call in message.tool_calls -%}" ) != std::string::npos) {
811734 LOG_INF (" Detected MiniMax-M2 official template bug: \" last_tool_call.name = message.tool_calls[-1].name\" , applying automatic fix...\n " );
@@ -814,23 +737,13 @@ common_chat_templates_ptr common_chat_templates_init(
814737 " {%- for tool_call in message.tool_calls -%}" ,
815738 " {%- for tool_call in message.tool_calls -%} {%- set last_tool_call.name = tool_call.function.name -%}" );
816739 }
817- if (default_template_src.find (" {%- set last_tool_call.name = message.tool_calls[-1].function.name -%}" ) != std::string::npos &&
818- default_template_src.find (" {%- for tool_call in message.tool_calls -%}" ) != std::string::npos) {
819- LOG_INF (" Detected MiniMax-M2 unsloth template, applying automatic fix...\n " );
820- string_replace_all (default_template_src, " {%- set last_tool_call.name = message.tool_calls[-1].function.name -%}" , " " );
821- string_replace_all (default_template_src,
822- " {%- for tool_call in message.tool_calls -%}" ,
823- " {%- for tool_call in message.tool_calls -%} {%- set last_tool_call.name = tool_call.function.name -%}" );
740+ if (default_template_src.find (" {% set _args = tool_call.arguments %}" ) != std::string::npos) {
741+ LOG_INF (" Detected MiniMax-M2 official template bug: unchecked tool_call.arguments , applying automatic fix...\n " );
742+ string_replace_all (default_template_src, " {% set _args = tool_call.arguments %}" ,
743+ " {%- if tool_call.arguments is defined and tool_call.arguments is mapping -%} {%- set _args = tool_call.arguments -%} {%- else -%} {%- set _args = {} -%} {%- endif -%}" );
824744 }
825745 LOG_INF (" MiniMax-M2 template fixed\n " );
826746 }
827- if (default_template_src.find (" ]~!b[" ) != std::string::npos
828- && default_template_src.find (" ]~b]" ) != std::string::npos
829- && default_template_src.find (" {% set _args = tool_call.arguments %}" ) != std::string::npos) {
830- LOG_INF (" Detected MiniMax-M2 official template bug: unchecked tool_call.arguments , applying automatic fix...\n " );
831- string_replace_all (default_template_src, " {% set _args = tool_call.arguments %}" ,
832- " {%- if tool_call.arguments is defined and tool_call.arguments is mapping -%} {%- set _args = tool_call.arguments -%} {%- else -%} {%- set _args = {} -%} {%- endif -%}" );
833- }
834747
835748 std::string token_bos = bos_token_override;
836749 std::string token_eos = eos_token_override;
@@ -879,8 +792,6 @@ const char * common_chat_format_name(common_chat_format format) {
879792 case COMMON_CHAT_FORMAT_GENERIC: return " Generic" ;
880793 case COMMON_CHAT_FORMAT_MISTRAL_NEMO: return " Mistral Nemo" ;
881794 case COMMON_CHAT_FORMAT_MAGISTRAL: return " Magistral" ;
882- case COMMON_CHAT_FORMAT_MINIMAX_M2: return " MiniMax-M2" ;
883- case COMMON_CHAT_FORMAT_GLM_4_5: return " GLM 4.5" ;
884795 case COMMON_CHAT_FORMAT_LLAMA_3_X: return " Llama 3.x" ;
885796 case COMMON_CHAT_FORMAT_LLAMA_3_X_WITH_BUILTIN_TOOLS: return " Llama 3.x with builtin tools" ;
886797 case COMMON_CHAT_FORMAT_DEEPSEEK_R1: return " DeepSeek R1" ;
@@ -896,6 +807,8 @@ const char * common_chat_format_name(common_chat_format format) {
896807 case COMMON_CHAT_FORMAT_NEMOTRON_V2: return " Nemotron V2" ;
897808 case COMMON_CHAT_FORMAT_APERTUS: return " Apertus" ;
898809 case COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS: return " LFM2 with JSON tools" ;
810+ case COMMON_CHAT_FORMAT_MINIMAX_M2: return " MiniMax-M2" ;
811+ case COMMON_CHAT_FORMAT_GLM_4_5: return " GLM 4.5" ;
899812 default :
900813 throw std::runtime_error (" Unknown chat format" );
901814 }
@@ -4106,12 +4019,6 @@ static void common_chat_parse(common_chat_msg_parser & builder) {
41064019 case COMMON_CHAT_FORMAT_MAGISTRAL:
41074020 common_chat_parse_magistral (builder);
41084021 break ;
4109- case COMMON_CHAT_FORMAT_MINIMAX_M2:
4110- common_chat_parse_minimax_m2 (builder);
4111- break ;
4112- case COMMON_CHAT_FORMAT_GLM_4_5:
4113- common_chat_parse_glm_4_5 (builder);
4114- break ;
41154022 case COMMON_CHAT_FORMAT_LLAMA_3_X:
41164023 common_chat_parse_llama_3_1 (builder);
41174024 break ;
@@ -4157,6 +4064,12 @@ static void common_chat_parse(common_chat_msg_parser & builder) {
41574064 case COMMON_CHAT_FORMAT_LFM2_WITH_JSON_TOOLS:
41584065 common_chat_parse_lfm2 (builder);
41594066 break ;
4067+ case COMMON_CHAT_FORMAT_MINIMAX_M2:
4068+ common_chat_parse_minimax_m2 (builder);
4069+ break ;
4070+ case COMMON_CHAT_FORMAT_GLM_4_5:
4071+ common_chat_parse_glm_4_5 (builder);
4072+ break ;
41604073 default :
41614074 throw std::runtime_error (std::string (" Unsupported format: " ) + common_chat_format_name (builder.syntax ().format ));
41624075 }
0 commit comments