@@ -1399,12 +1399,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
13991399 static const common_regex user_tool_call_regex (" (?: <\\ |constrain\\ |>([a-zA-Z]+))?<\\ |message\\ |>" );
14001400 static const common_regex builtin_tool_call_regex (" (?:browser|python)[\\ s\\ S]*<\\ |message\\ |>" );
14011401
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-
14081402 auto consume_until_next = [&](size_t from = std::string::npos) {
14091403 if (auto res = builder.try_find_regex (start_regex, from, false )) {
14101404 auto begin = res->groups [0 ].begin ;
@@ -1425,13 +1419,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
14251419 };
14261420
14271421 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-
14351422 if (auto res = builder.try_consume_regex (function_regex)) {
14361423 auto name = builder.str (res->groups [1 ]);
14371424
@@ -1443,8 +1430,28 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
14431430
14441431 if (builder.try_consume_regex (user_tool_call_regex)) {
14451432 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+ }
14481455 }
14491456 }
14501457 } else {
@@ -1470,13 +1477,25 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
14701477 if (builder.try_consume_regex (to_regex)) {
14711478 tool_call (false ); // built-in tools can be called in the analysis channel
14721479 } 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+ }
14751488
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+ }
14781497 } else {
1479- builder.add_content ( consume_until_next () );
1498+ builder.add_reasoning_content (reasoning );
14801499 }
14811500 } else {
14821501 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) {
15001519
15011520 auto message = [&]() {
15021521 if (auto res = builder.try_consume_regex (channel_regex)) {
1503- channel_start_pos = res->groups [0 ].begin ;
15041522 channel (*res);
15051523 } else if (builder.try_consume_regex (to_regex)) {
15061524 tool_call (true );
@@ -1517,7 +1535,6 @@ static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
15171535
15181536 // Read in complete messages until done or partial exception raised
15191537 while (auto res = builder.try_consume_regex (start_regex)) {
1520- message_start_pos = res->groups [0 ].begin ;
15211538 try {
15221539 message ();
15231540 } catch (const common_chat_msg_parse_exception & e) {
0 commit comments