@@ -1399,12 +1399,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1399
1399
static const common_regex user_tool_call_regex (" (?: <\\ |constrain\\ |>([a-zA-Z]+))?<\\ |message\\ |>" );
1400
1400
static const common_regex builtin_tool_call_regex (" (?:browser|python)[\\ s\\ S]*<\\ |message\\ |>" );
1401
1401
1402
- // Save the start of the message so we can roll back when we encounter a tool call and parse_tool_calls == false.
1403
- size_t message_start_pos = 0 ;
1404
-
1405
- // Similarly, save the channel start so we can roll back to defer reasoning parsing to builder.
1406
- size_t channel_start_pos = 0 ;
1407
-
1408
1402
auto consume_until_next = [&](size_t from = std::string::npos) {
1409
1403
if (auto res = builder.try_find_regex (start_regex, from, false )) {
1410
1404
auto begin = res->groups [0 ].begin ;
@@ -1425,13 +1419,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1425
1419
};
1426
1420
1427
1421
auto tool_call = [&](bool recipient_in_role) {
1428
- if (!builder.syntax ().parse_tool_calls ) {
1429
- // Move back to the start and consume up to the next message
1430
- builder.move_to (message_start_pos);
1431
- builder.add_content (consume_until_next (message_start_pos + 1 ));
1432
- return ;
1433
- }
1434
-
1435
1422
if (auto res = builder.try_consume_regex (function_regex)) {
1436
1423
auto name = builder.str (res->groups [1 ]);
1437
1424
@@ -1443,8 +1430,28 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1443
1430
1444
1431
if (builder.try_consume_regex (user_tool_call_regex)) {
1445
1432
if (auto args = builder.try_consume_json_with_dumped_args ({{}})) {
1446
- if (!builder.add_tool_call (name, " " , args->value ) || args->is_partial ) {
1447
- throw common_chat_msg_partial_exception (" incomplete tool call" );
1433
+ if (builder.syntax ().parse_tool_calls ) {
1434
+ if (!builder.add_tool_call (name, " " , args->value ) || args->is_partial ) {
1435
+ throw common_chat_msg_partial_exception (" incomplete tool call" );
1436
+ }
1437
+ } else {
1438
+ std::string args_as_string;
1439
+ if (args->value .is_object ()) {
1440
+ args_as_string = args->value .dump ();
1441
+ } else {
1442
+ args_as_string = args->value ;
1443
+ }
1444
+
1445
+ // simulate tool call in content
1446
+ builder.add_content (" <tool_call>" );
1447
+ builder.add_content (" {\" name\" : " + json (name).dump () + " , \" arguments\" : " );
1448
+ builder.add_content (args_as_string);
1449
+ if (!args->is_partial ) {
1450
+ builder.add_content (" }" );
1451
+ builder.add_content (" </tool_call>" );
1452
+ } else {
1453
+ throw common_chat_msg_partial_exception (" incomplete tool call" );
1454
+ }
1448
1455
}
1449
1456
}
1450
1457
} else {
@@ -1470,13 +1477,25 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1470
1477
if (builder.try_consume_regex (to_regex)) {
1471
1478
tool_call (false ); // built-in tools can be called in the analysis channel
1472
1479
} else if (builder.try_consume_regex (message_regex)) {
1473
- // Defer reasoning parsing to builder
1474
- builder.move_to (channel_start_pos);
1480
+ std::string reasoning;
1481
+ bool has_end = false ;
1482
+ if (auto res = builder.try_find_regex (end_regex, std::string::npos, false )) {
1483
+ reasoning = res->prelude ;
1484
+ has_end = true ;
1485
+ } else {
1486
+ reasoning = builder.consume_rest ();
1487
+ }
1475
1488
1476
- if (builder.syntax ().reasoning_format != COMMON_REASONING_FORMAT_NONE) {
1477
- builder.try_parse_reasoning (" <|channel|>analysis<|message|>" , " <|end|>" );
1489
+ if (builder.syntax ().reasoning_format == COMMON_REASONING_FORMAT_NONE || builder.syntax ().reasoning_in_content ) {
1490
+ // the templates raise an exception if <|channel|> is present
1491
+ // an assistant's content, so wrap it in think tags
1492
+ builder.add_content (" <think>" );
1493
+ builder.add_content (reasoning);
1494
+ if (has_end) {
1495
+ builder.add_content (" </think>" );
1496
+ }
1478
1497
} else {
1479
- builder.add_content ( consume_until_next () );
1498
+ builder.add_reasoning_content (reasoning );
1480
1499
}
1481
1500
} else {
1482
1501
throw common_chat_msg_parse_exception (" expected: <|message|>, got: " + consume_until_next ());
@@ -1500,7 +1519,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1500
1519
1501
1520
auto message = [&]() {
1502
1521
if (auto res = builder.try_consume_regex (channel_regex)) {
1503
- channel_start_pos = res->groups [0 ].begin ;
1504
1522
channel (*res);
1505
1523
} else if (builder.try_consume_regex (to_regex)) {
1506
1524
tool_call (true );
@@ -1517,7 +1535,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1517
1535
1518
1536
// Read in complete messages until done or partial exception raised
1519
1537
while (auto res = builder.try_consume_regex (start_regex)) {
1520
- message_start_pos = res->groups [0 ].begin ;
1521
1538
try {
1522
1539
message ();
1523
1540
} catch (const common_chat_msg_parse_exception & e) {
0 commit comments