@@ -1330,83 +1330,41 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1330
1330
}
1331
1331
1332
1332
static 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__);
1333
1334
common_chat_params data;
1334
1335
1335
- // Bypass minja's tool processing entirely
1336
+ // Configure template inputs
1336
1337
minja::chat_template_inputs tmpl_inputs;
1337
1338
tmpl_inputs.messages = inputs.messages ;
1338
1339
tmpl_inputs.tools = inputs.tools .empty () ? json () : inputs.tools ;
1339
1340
tmpl_inputs.add_generation_prompt = inputs.add_generation_prompt ;
1340
1341
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
1342
1343
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
1347
1345
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
1362
1347
1363
- data.prompt = result;
1348
+ // The prompt is generated here
1349
+ data.prompt = tmpl.apply (tmpl_inputs, opts);
1364
1350
data.format = COMMON_CHAT_FORMAT_GLM_4_5;
1351
+
1365
1352
data.preserved_tokens = {
1366
1353
" <|system|>" , " <|assistant|>" , " <|observation|>" ,
1367
1354
" <tool_call>" , " </tool_call>" , " <arg_key>" , " </arg_key>" ,
1368
1355
" <arg_value>" , " </arg_value>" , " <think>" , " </think>" ,
1369
1356
" <tool_response>" , " </tool_response>" ,
1370
1357
};
1371
1358
1372
- // Store tools schema for type-aware parsing
1359
+ // Store tools schema for type-aware parsing later
1373
1360
data.tools_schema = inputs.tools ;
1374
1361
1362
+ LOG_INF (" %s: GLM-4.5 native XML format enforced\n " , __func__);
1375
1363
return data;
1376
1364
}
1377
1365
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
-
1406
1366
static 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
+
1410
1368
auto get_expected_type = [&](const std::string& tool_name, const std::string& param_name) -> std::string {
1411
1369
// Access tools schema from builder syntax
1412
1370
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) {
1462
1420
builder.consume_spaces ();
1463
1421
1464
1422
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 ) {
1466
1424
std::string function_content = builder.input ().substr (builder.pos (), tool_call_end - builder.pos ());
1467
1425
std::string function_name = string_strip (function_content);
1468
1426
@@ -1479,13 +1437,8 @@ static void common_chat_parse_glm_4_5(common_chat_msg_parser & builder) {
1479
1437
json args_json = json::object ();
1480
1438
builder.move_to (arg_key_start);
1481
1439
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 ;
1489
1442
1490
1443
auto key_close = builder.try_find_literal (" </arg_key>" );
1491
1444
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) {
1514
1467
std::string expected_type = get_expected_type (function_name, key);
1515
1468
json parsed_value;
1516
1469
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" ) {
1526
1471
try {
1527
1472
parsed_value = json::parse (value);
1528
1473
} catch (...) {
1529
1474
parsed_value = value;
1530
1475
}
1531
1476
} else {
1532
- // Default to string
1477
+ // For all other types, store as string and let the unpacking logic handle it
1533
1478
parsed_value = value;
1534
1479
}
1535
1480
1536
1481
args_json[key] = parsed_value;
1537
-
1538
1482
builder.consume_spaces ();
1539
1483
}
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
+ }
1540
1500
1541
1501
if (!builder.add_tool_call (function_name, " " , args_json.dump ())) {
1542
1502
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) {
1552
1512
LOG_INF (" %s: no progress in parsing, stopping to avoid infinite loop\n " , __func__);
1553
1513
break ;
1554
1514
}
1515
+ curr_pos = builder.pos ();
1555
1516
}
1556
1517
1557
1518
if (builder.pos () < builder.input ().size ()) {
0 commit comments