@@ -203,7 +203,10 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
203203 msg_part.text = part.at (" text" );
204204 msg.content_parts .push_back (msg_part);
205205 }
206- } else if (!content.is_null ()) {
206+ } else if (content.is_null ()) {
207+ // Handle null content by setting it to empty string
208+ msg.content = " " ;
209+ } else {
207210 throw std::runtime_error (" Invalid 'content' type: expected string or array, got " + content.dump () + " (ref: https://github.com/ggml-org/llama.cpp/issues/8367)" );
208211 }
209212 }
@@ -292,7 +295,7 @@ json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msg
292295 }
293296 }
294297 } else {
295- jmsg[" content" ] = json () ; // null
298+ jmsg[" content" ] = " " ; // empty string instead of null
296299 }
297300 if (!msg.reasoning_content .empty ()) {
298301 jmsg[" reasoning_content" ] = msg.reasoning_content ;
@@ -607,6 +610,7 @@ const char * common_chat_format_name(common_chat_format format) {
607610 case COMMON_CHAT_FORMAT_HERMES_2_PRO: return " Hermes 2 Pro" ;
608611 case COMMON_CHAT_FORMAT_COMMAND_R7B: return " Command R7B" ;
609612 case COMMON_CHAT_FORMAT_GPT_OSS: return " GPT-OSS" ;
613+ case COMMON_CHAT_FORMAT_GLM_4_5: return " GLM 4.5" ;
610614 default :
611615 throw std::runtime_error (" Unknown chat format" );
612616 }
@@ -1325,6 +1329,63 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
13251329 }
13261330}
13271331
1332+ static common_chat_params common_chat_params_init_glm_4_5 (const common_chat_template & tmpl, const struct templates_params & inputs) {
1333+ common_chat_params data;
1334+ data.prompt = apply (tmpl, inputs);
1335+ data.format = COMMON_CHAT_FORMAT_GLM_4_5;
1336+ return data;
1337+ }
1338+
1339+ static void common_chat_parse_glm_4_5 (common_chat_msg_parser & builder) {
1340+ builder.try_parse_reasoning (" <think>" , " </think>" );
1341+ if (!builder.syntax ().parse_tool_calls ) {
1342+ builder.add_content (builder.consume_rest ());
1343+ return ;
1344+ }
1345+
1346+ // GLM 4.5 uses format: <tool_call>function_name\n<arg_key>key</arg_key>\n<arg_value>value</arg_value>\n</tool_call>
1347+ static const common_regex tool_call_start (" <tool_call>([^\n <]+)" );
1348+ static const common_regex arg_key_regex (" <arg_key>([^<]+)</arg_key>" );
1349+ static const common_regex arg_value_regex (" <arg_value>([^<]*)</arg_value>" );
1350+ static const common_regex tool_call_end (" </tool_call>" );
1351+
1352+ while (auto res = builder.try_find_regex (tool_call_start)) {
1353+ // Move to the start of the tool call and consume it
1354+ builder.move_to (res->groups [0 ].begin );
1355+ builder.consume_regex (tool_call_start);
1356+
1357+ std::string function_name = builder.str (res->groups [1 ]);
1358+ json arguments = json::object ();
1359+
1360+ builder.consume_spaces ();
1361+
1362+ // Parse all arg_key/arg_value pairs
1363+ while (auto key_res = builder.try_consume_regex (arg_key_regex)) {
1364+ std::string key = builder.str (key_res->groups [1 ]);
1365+ builder.consume_spaces ();
1366+
1367+ if (auto value_res = builder.try_consume_regex (arg_value_regex)) {
1368+ std::string value = builder.str (value_res->groups [1 ]);
1369+ arguments[key] = value;
1370+ builder.consume_spaces ();
1371+ } else {
1372+ throw common_chat_msg_partial_exception (" Expected <arg_value> after <arg_key>" );
1373+ }
1374+ }
1375+
1376+ // Consume closing tag
1377+ builder.consume_regex (tool_call_end);
1378+ builder.consume_spaces ();
1379+
1380+ // Add the parsed tool call
1381+ if (!builder.add_tool_call (function_name, " " , arguments.dump ())) {
1382+ throw common_chat_msg_partial_exception (" Failed to add GLM tool call" );
1383+ }
1384+ }
1385+
1386+ builder.add_content (builder.consume_rest ());
1387+ }
1388+
13281389static common_chat_params common_chat_params_init_firefunction_v2 (const common_chat_template & tmpl, const struct templates_params & inputs) {
13291390 LOG_DBG (" %s\n " , __func__);
13301391 common_chat_params data;
@@ -1805,6 +1866,11 @@ static common_chat_params common_chat_templates_apply_jinja(
18051866 return common_chat_params_init_command_r7b (tmpl, params);
18061867 }
18071868
1869+ // GLM 4.5: detect by <arg_key> and <arg_value> tags (check before Hermes since both use <tool_call>)
1870+ if (src.find (" <arg_key>" ) != std::string::npos && src.find (" <arg_value>" ) != std::string::npos && params.json_schema .is_null ()) {
1871+ return common_chat_params_init_glm_4_5 (tmpl, params);
1872+ }
1873+
18081874 // Hermes 2/3 Pro, Qwen 2.5 Instruct (w/ tools)
18091875 if (src.find (" <tool_call>" ) != std::string::npos && params.json_schema .is_null ()) {
18101876 return common_chat_params_init_hermes_2_pro (tmpl, params);
@@ -1969,6 +2035,9 @@ static void common_chat_parse(common_chat_msg_parser & builder) {
19692035 case COMMON_CHAT_FORMAT_GPT_OSS:
19702036 common_chat_parse_gpt_oss (builder);
19712037 break ;
2038+ case COMMON_CHAT_FORMAT_GLM_4_5:
2039+ common_chat_parse_glm_4_5 (builder);
2040+ break ;
19722041 default :
19732042 throw std::runtime_error (std::string (" Unsupported format: " ) + common_chat_format_name (builder.syntax ().format ));
19742043 }
0 commit comments