@@ -1423,12 +1423,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1423
1423
static const common_regex user_tool_call_regex (" (?: <\\ |constrain\\ |>([a-zA-Z]+))?<\\ |message\\ |>" );
1424
1424
static const common_regex builtin_tool_call_regex (" (?:browser|python)[\\ s\\ S]*<\\ |message\\ |>" );
1425
1425
1426
- // Save the start of the message so we can roll back when we encounter a tool call and parse_tool_calls == false.
1427
- size_t message_start_pos = 0 ;
1428
-
1429
- // Similarly, save the channel start so we can roll back to defer reasoning parsing to builder.
1430
- size_t channel_start_pos = 0 ;
1431
-
1432
1426
auto consume_until_next = [&](size_t from = std::string::npos) {
1433
1427
if (auto res = builder.try_find_regex (start_regex, from, false )) {
1434
1428
auto begin = res->groups [0 ].begin ;
@@ -1449,13 +1443,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1449
1443
};
1450
1444
1451
1445
auto tool_call = [&](bool recipient_in_role) {
1452
- if (!builder.syntax ().parse_tool_calls ) {
1453
- // Move back to the start and consume up to the next message
1454
- builder.move_to (message_start_pos);
1455
- builder.add_content (consume_until_next (message_start_pos + 1 ));
1456
- return ;
1457
- }
1458
-
1459
1446
if (auto res = builder.try_consume_regex (function_regex)) {
1460
1447
auto name = builder.str (res->groups [1 ]);
1461
1448
@@ -1467,8 +1454,28 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1467
1454
1468
1455
if (builder.try_consume_regex (user_tool_call_regex)) {
1469
1456
if (auto args = builder.try_consume_json_with_dumped_args ({{}})) {
1470
- if (!builder.add_tool_call (name, " " , args->value ) || args->is_partial ) {
1471
- throw common_chat_msg_partial_exception (" incomplete tool call" );
1457
+ if (builder.syntax ().parse_tool_calls ) {
1458
+ if (!builder.add_tool_call (name, " " , args->value ) || args->is_partial ) {
1459
+ throw common_chat_msg_partial_exception (" incomplete tool call" );
1460
+ }
1461
+ } else {
1462
+ std::string args_as_string;
1463
+ if (args->value .is_object ()) {
1464
+ args_as_string = args->value .dump ();
1465
+ } else {
1466
+ args_as_string = args->value ;
1467
+ }
1468
+
1469
+ // simulate tool call in content
1470
+ builder.add_content (" <tool_call>" );
1471
+ builder.add_content (" {\" name\" : " + json (name).dump () + " , \" arguments\" : " );
1472
+ builder.add_content (args_as_string);
1473
+ if (!args->is_partial ) {
1474
+ builder.add_content (" }" );
1475
+ builder.add_content (" </tool_call>" );
1476
+ } else {
1477
+ throw common_chat_msg_partial_exception (" incomplete tool call" );
1478
+ }
1472
1479
}
1473
1480
}
1474
1481
} else {
@@ -1494,13 +1501,25 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1494
1501
if (builder.try_consume_regex (to_regex)) {
1495
1502
tool_call (false ); // built-in tools can be called in the analysis channel
1496
1503
} else if (builder.try_consume_regex (message_regex)) {
1497
- // Defer reasoning parsing to builder
1498
- builder.move_to (channel_start_pos);
1504
+ std::string reasoning;
1505
+ bool has_end = false ;
1506
+ if (auto res = builder.try_find_regex (end_regex, std::string::npos, false )) {
1507
+ reasoning = res->prelude ;
1508
+ has_end = true ;
1509
+ } else {
1510
+ reasoning = builder.consume_rest ();
1511
+ }
1499
1512
1500
- if (builder.syntax ().reasoning_format != COMMON_REASONING_FORMAT_NONE) {
1501
- builder.try_parse_reasoning (" <|channel|>analysis<|message|>" , " <|end|>" );
1513
+ if (builder.syntax ().reasoning_format == COMMON_REASONING_FORMAT_NONE || builder.syntax ().reasoning_in_content ) {
1514
+ // the templates raise an exception if <|channel|> is present
1515
+ // an assistant's content, so wrap it in think tags
1516
+ builder.add_content (" <think>" );
1517
+ builder.add_content (reasoning);
1518
+ if (has_end) {
1519
+ builder.add_content (" </think>" );
1520
+ }
1502
1521
} else {
1503
- builder.add_content ( consume_until_next () );
1522
+ builder.add_reasoning_content (reasoning );
1504
1523
}
1505
1524
} else {
1506
1525
throw common_chat_msg_parse_exception (" expected: <|message|>, got: " + consume_until_next ());
@@ -1524,7 +1543,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1524
1543
1525
1544
auto message = [&]() {
1526
1545
if (auto res = builder.try_consume_regex (channel_regex)) {
1527
- channel_start_pos = res->groups [0 ].begin ;
1528
1546
channel (*res);
1529
1547
} else if (builder.try_consume_regex (to_regex)) {
1530
1548
tool_call (true );
@@ -1541,7 +1559,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1541
1559
1542
1560
// Read in complete messages until done or partial exception raised
1543
1561
while (auto res = builder.try_consume_regex (start_regex)) {
1544
- message_start_pos = res->groups [0 ].begin ;
1545
1562
try {
1546
1563
message ();
1547
1564
} catch (const common_chat_msg_parse_exception & e) {
0 commit comments