@@ -31,6 +31,11 @@ static std::string string_diff(const std::string & last, const std::string & cur
3131 return current;
3232 }
3333 if (!string_starts_with (current, last)) {
34+ if (string_starts_with (last, current)) {
35+ // This happens if the last generation ended on a partial stop word (not erased),
36+ // and the current ended on a stop word (erased).
37+ return " " ;
38+ }
3439 throw std::runtime_error (" Invalid diff: '" + last + " ' not found at start of '" + current + " '" );
3540 }
3641 return current.substr (last.size ());
@@ -101,9 +106,9 @@ std::vector<common_chat_msg_diff> common_chat_msg_diff::compute_diffs(const comm
101106 if (!args_diff.empty () || pref.id != newf.id ) {
102107 auto & diff = diffs.emplace_back ();
103108 diff.tool_call_index = idx;
104- diff.tool_call_delta .name = newf.name ;
105109 if (pref.id != newf.id ) {
106110 diff.tool_call_delta .id = newf.id ;
111+ diff.tool_call_delta .name = newf.name ;
107112 }
108113 diff.tool_call_delta .arguments = args_diff;
109114 }
@@ -387,22 +392,19 @@ template <> json common_chat_msg_diff_to_json_oaicompat(const common_chat_msg_di
387392 delta[" content" ] = diff.content_delta ;
388393 }
389394 if (diff.tool_call_index != std::string::npos) {
395+ json tool_call;
396+ tool_call[" index" ] = diff.tool_call_index ;
397+ if (!diff.tool_call_delta .id .empty ()) {
398+ tool_call[" id" ] = diff.tool_call_delta .id ;
399+ tool_call[" type" ] = " function" ;
400+ }
390401 json function = json::object ();
391402 if (!diff.tool_call_delta .name .empty ()) {
392403 function[" name" ] = diff.tool_call_delta .name ;
393404 }
394- if (!diff.tool_call_delta .id .empty ()) {
395- function[" id" ] = diff.tool_call_delta .id ;
396- }
397- if (!diff.tool_call_delta .arguments .empty ()) {
398- function[" arguments" ] = diff.tool_call_delta .arguments ;
399- }
400- delta[" tool_calls" ] = json::array ({
401- json {
402- {" index" , diff.tool_call_index },
403- {" function" , function}
404- }
405- });
405+ function[" arguments" ] = diff.tool_call_delta .arguments ;
406+ tool_call[" function" ] = function;
407+ delta[" tool_calls" ] = json::array ({tool_call});
406408 }
407409 return delta;
408410}
@@ -654,7 +656,6 @@ static void parse_json_tool_calls(
654656 }
655657 from = std::string::npos;
656658
657- builder.add_content (res->prelude );
658659 auto maybe_raw_python = name == " python" && allow_raw_python;
659660 if (builder.input ()[builder.pos ()] == ' {' || !maybe_raw_python) {
660661 if (auto arguments = builder.try_consume_json_with_dumped_args ({{}})) {
@@ -684,7 +685,6 @@ static void parse_json_tool_calls(
684685 };
685686 if (block_open) {
686687 if (auto res = builder.try_find_regex (*block_open)) {
687- builder.add_content (res->prelude );
688688 parse_tool_calls ();
689689 } else {
690690 builder.add_content (builder.consume_rest ());
@@ -697,7 +697,6 @@ static void parse_json_tool_calls(
697697static void parse_prefixed_json_tool_call_array (common_chat_msg_parser & builder, const common_regex & prefix, size_t rstrip_prefix = 0 ) {
698698 static const std::vector<std::vector<std::string>> args_paths = {{" arguments" }};
699699 if (auto res = builder.try_find_regex (prefix)) {
700- builder.add_content (res->prelude );
701700 builder.move_back (rstrip_prefix);
702701 auto tool_calls = builder.consume_json_with_dumped_args (args_paths);
703702 if (!builder.add_tool_calls (tool_calls.value ) || tool_calls.is_partial ) {
@@ -833,6 +832,10 @@ static common_chat_params common_chat_params_init_generic(const common_chat_temp
833832 return data;
834833}
835834static void common_chat_parse_generic (common_chat_msg_parser & builder) {
835+ if (!builder.syntax ().parse_tool_calls ) {
836+ builder.add_content (builder.consume_rest ());
837+ return ;
838+ }
836839 static const std::vector<std::vector<std::string>> content_paths = {
837840 {" response" },
838841 };
@@ -905,6 +908,11 @@ static common_chat_params common_chat_params_init_mistral_nemo(const common_chat
905908 return data;
906909}
907910static void common_chat_parse_mistral_nemo (common_chat_msg_parser & builder) {
911+ if (!builder.syntax ().parse_tool_calls ) {
912+ builder.add_content (builder.consume_rest ());
913+ return ;
914+ }
915+
908916 static const common_regex prefix (regex_escape (" [TOOL_CALLS]" ));
909917 parse_prefixed_json_tool_call_array (builder, prefix);
910918}
@@ -999,7 +1007,6 @@ static void common_chat_parse_command_r7b(common_chat_msg_parser & builder) {
9991007
10001008 if (auto res = builder.try_find_regex (start_action_regex)) {
10011009 // If we didn't extract thoughts, prelude includes them.
1002- builder.add_content (res->prelude );
10031010 auto tool_calls = builder.consume_json_with_dumped_args ({{" parameters" }});
10041011 for (const auto & tool_call : tool_calls.value ) {
10051012 std::string name = tool_call.contains (" tool_name" ) ? tool_call.at (" tool_name" ) : " " ;
@@ -1014,11 +1021,7 @@ static void common_chat_parse_command_r7b(common_chat_msg_parser & builder) {
10141021 }
10151022 builder.consume_regex (end_action_regex);
10161023 } else if (auto res = builder.try_find_regex (start_response_regex)) {
1017- // If we didn't extract thoughts, prelude includes them.
1018- builder.add_content (res->prelude );
1019- if (auto res = builder.try_find_regex (end_response_regex)) {
1020- builder.add_content (res->prelude );
1021- } else {
1024+ if (!builder.try_find_regex (end_response_regex)) {
10221025 builder.add_content (builder.consume_rest ());
10231026 throw common_chat_msg_partial_exception (end_response_regex.str ());
10241027 }
@@ -1126,6 +1129,11 @@ static common_chat_params common_chat_params_init_llama_3_x(const common_chat_te
11261129 return data;
11271130}
11281131static void common_chat_parse_llama_3_1 (common_chat_msg_parser & builder, bool with_builtin_tools = false ) {
1132+ if (!builder.syntax ().parse_tool_calls ) {
1133+ builder.add_content (builder.consume_rest ());
1134+ return ;
1135+ }
1136+
11291137 static const common_regex function_regex (
11301138 " \\ s*\\ {\\ s*(?:\" type\"\\ s*:\\ s*\" function\"\\ s*,\\ s*)?\" name\"\\ s*:\\ s*\" ([^\" ]+)\"\\ s*,\\ s*\" parameters\"\\ s*: " );
11311139 static const common_regex close_regex (" \\ }\\ s*" );
@@ -1136,8 +1144,6 @@ static void common_chat_parse_llama_3_1(common_chat_msg_parser & builder, bool w
11361144 if (with_builtin_tools) {
11371145 static const common_regex builtin_call_regex (" <\\ |python_tag\\ |>" );
11381146 if (auto res = builder.try_find_regex (builtin_call_regex)) {
1139- builder.add_content (res->prelude );
1140-
11411147 auto fun_res = builder.consume_regex (function_name_regex);
11421148 auto function_name = builder.str (fun_res.groups [1 ]);
11431149
@@ -1253,6 +1259,10 @@ static common_chat_params common_chat_params_init_deepseek_r1(const common_chat_
12531259}
12541260static void common_chat_parse_deepseek_r1 (common_chat_msg_parser & builder) {
12551261 builder.try_parse_reasoning (" <think>" , " </think>" );
1262+ if (!builder.syntax ().parse_tool_calls ) {
1263+ builder.add_content (builder.consume_rest ());
1264+ return ;
1265+ }
12561266
12571267 static const common_regex tool_calls_begin (" (?:<|tool▁calls▁begin|>|<|tool_calls_begin|>|<|tool calls begin|>|<|tool\\\\ _calls\\\\ _begin|>|<|tool▁calls|>)" );
12581268 static const common_regex tool_calls_end (" <|tool▁calls▁end|>" );
@@ -1314,6 +1324,10 @@ static common_chat_params common_chat_params_init_firefunction_v2(const common_c
13141324 return data;
13151325}
13161326static void common_chat_parse_firefunction_v2 (common_chat_msg_parser & builder) {
1327+ if (!builder.syntax ().parse_tool_calls ) {
1328+ builder.add_content (builder.consume_rest ());
1329+ return ;
1330+ }
13171331 static const common_regex prefix (regex_escape (" functools[" ));
13181332 parse_prefixed_json_tool_call_array (builder, prefix, /* rstrip_prefix= */ 1 );
13191333}
@@ -1455,15 +1469,12 @@ static common_chat_params common_chat_params_init_functionary_v3_1_llama_3_1(con
14551469 return data;
14561470}
14571471static void common_chat_parse_functionary_v3_1_llama_3_1 (common_chat_msg_parser & builder) {
1458- // This version of Functionary still supports the llama 3.1 tool call format for the python tool.
1459- static const common_regex python_tag_regex (regex_escape (" <|python_tag|>" ));
1460-
1461- if (auto res = builder.try_find_regex (python_tag_regex)) {
1462- builder.add_content (res->prelude );
1463- auto arguments = wrap_code_as_arguments (builder, builder.consume_rest ());
1464- builder.add_tool_call (" python" , " " , arguments);
1472+ if (!builder.syntax ().parse_tool_calls ) {
1473+ builder.add_content (builder.consume_rest ());
14651474 return ;
14661475 }
1476+ // This version of Functionary still supports the llama 3.1 tool call format for the python tool.
1477+ static const common_regex python_tag_regex (regex_escape (" <|python_tag|>" ));
14671478
14681479 static const common_regex function_regex (R"( <function=(\w+)>)" );
14691480 static const common_regex close_regex (R"( </function>)" );
@@ -1475,6 +1486,12 @@ static void common_chat_parse_functionary_v3_1_llama_3_1(common_chat_msg_parser
14751486 function_regex,
14761487 close_regex,
14771488 std::nullopt );
1489+
1490+ if (auto res = builder.try_find_regex (python_tag_regex)) {
1491+ auto arguments = wrap_code_as_arguments (builder, builder.consume_rest ());
1492+ builder.add_tool_call (" python" , " " , arguments);
1493+ return ;
1494+ }
14781495}
14791496
14801497static common_chat_params common_chat_params_init_hermes_2_pro (const common_chat_template & tmpl, const struct templates_params & inputs) {
@@ -1593,6 +1610,10 @@ static common_chat_params common_chat_params_init_hermes_2_pro(const common_chat
15931610}
15941611static void common_chat_parse_hermes_2_pro (common_chat_msg_parser & builder) {
15951612 builder.try_parse_reasoning (" <think>" , " </think>" );
1613+ if (!builder.syntax ().parse_tool_calls ) {
1614+ builder.add_content (builder.consume_rest ());
1615+ return ;
1616+ }
15961617
15971618 static const common_regex open_regex (
15981619 " (?:"
@@ -1614,8 +1635,6 @@ static void common_chat_parse_hermes_2_pro(common_chat_msg_parser & builder) {
16141635 );
16151636
16161637 if (auto res = builder.try_find_regex (open_regex)) {
1617- builder.add_content (res->prelude );
1618-
16191638 const auto & block_start = res->groups [1 ];
16201639 std::string block_end = block_start.empty () ? " " : " ```" ;
16211640
@@ -1851,10 +1870,10 @@ static void common_chat_parse_content_only(common_chat_msg_parser & builder) {
18511870 builder.add_content (builder.consume_rest ());
18521871}
18531872
1854- static void common_chat_parse (common_chat_msg_parser & builder, common_chat_format format ) {
1855- LOG_DBG (" Parsing input with format %s: %s\n " , common_chat_format_name (format), builder.input ().c_str ());
1873+ static void common_chat_parse (common_chat_msg_parser & builder) {
1874+ LOG_DBG (" Parsing input with format %s: %s\n " , common_chat_format_name (builder. syntax (). format ), builder.input ().c_str ());
18561875
1857- switch (format) {
1876+ switch (builder. syntax (). format ) {
18581877 case COMMON_CHAT_FORMAT_CONTENT_ONLY:
18591878 common_chat_parse_content_only (builder);
18601879 break ;
@@ -1889,15 +1908,15 @@ static void common_chat_parse(common_chat_msg_parser & builder, common_chat_form
18891908 common_chat_parse_command_r7b (builder);
18901909 break ;
18911910 default :
1892- throw std::runtime_error (std::string (" Unsupported format: " ) + common_chat_format_name (format));
1911+ throw std::runtime_error (std::string (" Unsupported format: " ) + common_chat_format_name (builder. syntax (). format ));
18931912 }
18941913 builder.finish ();
18951914}
18961915
18971916common_chat_msg common_chat_parse (const std::string & input, bool is_partial, const common_chat_syntax & syntax) {
18981917 common_chat_msg_parser builder (input, is_partial, syntax);
18991918 try {
1900- common_chat_parse (builder, syntax. format );
1919+ common_chat_parse (builder);
19011920 } catch (const common_chat_msg_partial_exception & ex) {
19021921 LOG_DBG (" Partial parse: %s\n " , ex.what ());
19031922 if (!is_partial) {
0 commit comments