@@ -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