@@ -1330,83 +1330,41 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
13301330}
13311331
13321332static common_chat_params common_chat_params_init_glm_4_5 (const common_chat_template & tmpl, const struct templates_params & inputs) {
1333+ LOG_INF (" %s: initializing GLM-4.5 chat params\n " , __func__);
13331334 common_chat_params data;
13341335
1335- // Bypass minja's tool processing entirely
1336+ // Configure template inputs
13361337 minja::chat_template_inputs tmpl_inputs;
13371338 tmpl_inputs.messages = inputs.messages ;
13381339 tmpl_inputs.tools = inputs.tools .empty () ? json () : inputs.tools ;
13391340 tmpl_inputs.add_generation_prompt = inputs.add_generation_prompt ;
13401341 tmpl_inputs.extra_context = inputs.extra_context ;
1341- tmpl_inputs.now = std::chrono::system_clock:: now();
1342+ tmpl_inputs.now = inputs. now ; // Use the consistent timestamp from params
13421343
1343- // Force XML mode through context
1344- tmpl_inputs.extra_context [" xml_tool_format" ] = true ;
1345- tmpl_inputs.extra_context [" native_tool_support" ] = true ;
1346-
1344+ // Configure template options to disable polyfills and enforce native XML format
13471345 minja::chat_template_options opts;
1348- opts.apply_polyfills = false ; // Hard disable all polyfills
1349- opts.use_bos_token = inputs.add_bos ;
1350- opts.use_eos_token = inputs.add_eos ;
1351-
1352- // Single apply call
1353- auto result = tmpl.apply (tmpl_inputs, opts);
1354-
1355- // Manual BOS/EOS handling (since you disabled automatic handling)
1356- if (inputs.add_bos && string_starts_with (result, tmpl.bos_token ())) {
1357- result = result.substr (tmpl.bos_token ().size ());
1358- }
1359- if (inputs.add_eos && string_ends_with (result, tmpl.eos_token ())) {
1360- result = result.substr (0 , result.size () - tmpl.eos_token ().size ());
1361- }
1346+ opts.apply_polyfills = false ; // Hard disable all polyfills
13621347
1363- data.prompt = result;
1348+ // The prompt is generated here
1349+ data.prompt = tmpl.apply (tmpl_inputs, opts);
13641350 data.format = COMMON_CHAT_FORMAT_GLM_4_5;
1351+
13651352 data.preserved_tokens = {
13661353 " <|system|>" , " <|assistant|>" , " <|observation|>" ,
13671354 " <tool_call>" , " </tool_call>" , " <arg_key>" , " </arg_key>" ,
13681355 " <arg_value>" , " </arg_value>" , " <think>" , " </think>" ,
13691356 " <tool_response>" , " </tool_response>" ,
13701357 };
13711358
1372- // Store tools schema for type-aware parsing
1359+ // Store tools schema for type-aware parsing later
13731360 data.tools_schema = inputs.tools ;
13741361
1362+ LOG_INF (" %s: GLM-4.5 native XML format enforced\n " , __func__);
13751363 return data;
13761364}
13771365
1378- static void debug_print_raw_input (const std::string& input) {
1379- LOG_INF (" === GLM-4.5 RAW INPUT ===\n " );
1380- for (size_t i = 0 ; i < input.size (); ++i) {
1381- char ch = input[i];
1382- if (ch == ' \n ' ) LOG_INF (" \\ n" );
1383- else if (ch == ' \t ' ) LOG_INF (" \\ t" );
1384- else if (ch == ' \r ' ) LOG_INF (" \\ r" );
1385- else if (std::isspace (ch)) LOG_INF (" ·" ); // visible space
1386- else LOG_INF (" %c" , ch);
1387- }
1388- LOG_INF (" \n === END RAW INPUT ===\n " );
1389- }
1390-
1391- static void debug_print_parse_position (const std::string& input, size_t pos, const char * context) {
1392- size_t start = (pos < 50 ) ? 0 : pos - 50 ;
1393- size_t end = std::min (input.size (), pos + 50 );
1394- LOG_INF (" === %s at position %zu ===\n " , context, pos);
1395- for (size_t i = start; i < end; ++i) {
1396- if (i == pos) LOG_INF (" >>>" );
1397- char ch = input[i];
1398- if (ch == ' \n ' ) LOG_INF (" \\ n" );
1399- else if (ch == ' \t ' ) LOG_INF (" \\ t" );
1400- else LOG_INF (" %c" , ch);
1401- if (i == pos) LOG_INF (" <<<" );
1402- }
1403- LOG_INF (" \n === END POSITION ===\n " );
1404- }
1405-
14061366static void common_chat_parse_glm_4_5 (common_chat_msg_parser & builder) {
1407- debug_print_raw_input (builder.input ());
1408-
1409- // Helper function to get expected type from tool schema
1367+
14101368 auto get_expected_type = [&](const std::string& tool_name, const std::string& param_name) -> std::string {
14111369 // Access tools schema from builder syntax
14121370 const auto & tools_schema = builder.syntax ().tools_schema ;
@@ -1462,7 +1420,7 @@ static void common_chat_parse_glm_4_5(common_chat_msg_parser & builder) {
14621420 builder.consume_spaces ();
14631421
14641422 size_t arg_key_start = builder.input ().find (" <arg_key>" , tool_call_start);
1465- if (arg_key_start == std::string::npos) {
1423+ if (arg_key_start == std::string::npos || arg_key_start > tool_call_end ) {
14661424 std::string function_content = builder.input ().substr (builder.pos (), tool_call_end - builder.pos ());
14671425 std::string function_name = string_strip (function_content);
14681426
@@ -1479,13 +1437,8 @@ static void common_chat_parse_glm_4_5(common_chat_msg_parser & builder) {
14791437 json args_json = json::object ();
14801438 builder.move_to (arg_key_start);
14811439
1482- while (builder.pos () < tool_call_end) {
1483- if (!builder.try_consume_literal (" <arg_key>" )) {
1484- builder.consume_spaces ();
1485- if (!builder.try_consume_literal (" <arg_key>" )) {
1486- break ;
1487- }
1488- }
1440+ while (builder.pos () < tool_call_end && builder.input ().substr (builder.pos ()).find (" <arg_key>" ) == 0 ) {
1441+ if (!builder.try_consume_literal (" <arg_key>" )) break ;
14891442
14901443 auto key_close = builder.try_find_literal (" </arg_key>" );
14911444 if (!key_close || key_close->groups [0 ].end > tool_call_end) {
@@ -1514,29 +1467,36 @@ static void common_chat_parse_glm_4_5(common_chat_msg_parser & builder) {
15141467 std::string expected_type = get_expected_type (function_name, key);
15151468 json parsed_value;
15161469
1517- if (expected_type == " integer" || expected_type == " number" ) {
1518- try {
1519- parsed_value = std::stod (value); // or std::stoi for integers
1520- } catch (...) {
1521- parsed_value = value; // Fallback to string
1522- }
1523- } else if (expected_type == " boolean" ) {
1524- parsed_value = (value == " true" );
1525- } else if (expected_type == " array" || expected_type == " object" ) {
1470+ if (expected_type == " array" || expected_type == " object" ) {
15261471 try {
15271472 parsed_value = json::parse (value);
15281473 } catch (...) {
15291474 parsed_value = value;
15301475 }
15311476 } else {
1532- // Default to string
1477+ // For all other types, store as string and let the unpacking logic handle it
15331478 parsed_value = value;
15341479 }
15351480
15361481 args_json[key] = parsed_value;
1537-
15381482 builder.consume_spaces ();
15391483 }
1484+
1485+ if (args_json.size () == 1 ) {
1486+ const auto key = args_json.begin ().key ();
1487+ auto & value = args_json.begin ().value ();
1488+
1489+ if (value.is_string ()) {
1490+ try {
1491+ json unpacked_json = json::parse (value.get <std::string>());
1492+ if (unpacked_json.is_object ()) {
1493+ args_json = unpacked_json;
1494+ }
1495+ } catch (const std::exception&) {
1496+ // Not a valid JSON string, proceed as normal
1497+ }
1498+ }
1499+ }
15401500
15411501 if (!builder.add_tool_call (function_name, " " , args_json.dump ())) {
15421502 LOG_INF (" %s: failed to add tool call with arguments\n " , __func__);
@@ -1552,6 +1512,7 @@ static void common_chat_parse_glm_4_5(common_chat_msg_parser & builder) {
15521512 LOG_INF (" %s: no progress in parsing, stopping to avoid infinite loop\n " , __func__);
15531513 break ;
15541514 }
1515+ curr_pos = builder.pos ();
15551516 }
15561517
15571518 if (builder.pos () < builder.input ().size ()) {
0 commit comments