@@ -31,6 +31,11 @@ static std::string string_diff(const std::string & last, const std::string & cur
31
31
return current;
32
32
}
33
33
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
+ }
34
39
throw std::runtime_error (" Invalid diff: '" + last + " ' not found at start of '" + current + " '" );
35
40
}
36
41
return current.substr (last.size ());
@@ -101,9 +106,9 @@ std::vector<common_chat_msg_diff> common_chat_msg_diff::compute_diffs(const comm
101
106
if (!args_diff.empty () || pref.id != newf.id ) {
102
107
auto & diff = diffs.emplace_back ();
103
108
diff.tool_call_index = idx;
104
- diff.tool_call_delta .name = newf.name ;
105
109
if (pref.id != newf.id ) {
106
110
diff.tool_call_delta .id = newf.id ;
111
+ diff.tool_call_delta .name = newf.name ;
107
112
}
108
113
diff.tool_call_delta .arguments = args_diff;
109
114
}
@@ -387,22 +392,19 @@ template <> json common_chat_msg_diff_to_json_oaicompat(const common_chat_msg_di
387
392
delta[" content" ] = diff.content_delta ;
388
393
}
389
394
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
+ }
390
401
json function = json::object ();
391
402
if (!diff.tool_call_delta .name .empty ()) {
392
403
function[" name" ] = diff.tool_call_delta .name ;
393
404
}
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});
406
408
}
407
409
return delta;
408
410
}
@@ -654,7 +656,6 @@ static void parse_json_tool_calls(
654
656
}
655
657
from = std::string::npos;
656
658
657
- builder.add_content (res->prelude );
658
659
auto maybe_raw_python = name == " python" && allow_raw_python;
659
660
if (builder.input ()[builder.pos ()] == ' {' || !maybe_raw_python) {
660
661
if (auto arguments = builder.try_consume_json_with_dumped_args ({{}})) {
@@ -684,7 +685,6 @@ static void parse_json_tool_calls(
684
685
};
685
686
if (block_open) {
686
687
if (auto res = builder.try_find_regex (*block_open)) {
687
- builder.add_content (res->prelude );
688
688
parse_tool_calls ();
689
689
} else {
690
690
builder.add_content (builder.consume_rest ());
@@ -697,7 +697,6 @@ static void parse_json_tool_calls(
697
697
static void parse_prefixed_json_tool_call_array (common_chat_msg_parser & builder, const common_regex & prefix, size_t rstrip_prefix = 0 ) {
698
698
static const std::vector<std::vector<std::string>> args_paths = {{" arguments" }};
699
699
if (auto res = builder.try_find_regex (prefix)) {
700
- builder.add_content (res->prelude );
701
700
builder.move_back (rstrip_prefix);
702
701
auto tool_calls = builder.consume_json_with_dumped_args (args_paths);
703
702
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
833
832
return data;
834
833
}
835
834
static 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
+ }
836
839
static const std::vector<std::vector<std::string>> content_paths = {
837
840
{" response" },
838
841
};
@@ -905,6 +908,11 @@ static common_chat_params common_chat_params_init_mistral_nemo(const common_chat
905
908
return data;
906
909
}
907
910
static 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
+
908
916
static const common_regex prefix (regex_escape (" [TOOL_CALLS]" ));
909
917
parse_prefixed_json_tool_call_array (builder, prefix);
910
918
}
@@ -999,7 +1007,6 @@ static void common_chat_parse_command_r7b(common_chat_msg_parser & builder) {
999
1007
1000
1008
if (auto res = builder.try_find_regex (start_action_regex)) {
1001
1009
// If we didn't extract thoughts, prelude includes them.
1002
- builder.add_content (res->prelude );
1003
1010
auto tool_calls = builder.consume_json_with_dumped_args ({{" parameters" }});
1004
1011
for (const auto & tool_call : tool_calls.value ) {
1005
1012
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) {
1014
1021
}
1015
1022
builder.consume_regex (end_action_regex);
1016
1023
} 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)) {
1022
1025
builder.add_content (builder.consume_rest ());
1023
1026
throw common_chat_msg_partial_exception (end_response_regex.str ());
1024
1027
}
@@ -1126,6 +1129,11 @@ static common_chat_params common_chat_params_init_llama_3_x(const common_chat_te
1126
1129
return data;
1127
1130
}
1128
1131
static 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
+
1129
1137
static const common_regex function_regex (
1130
1138
" \\ s*\\ {\\ s*(?:\" type\"\\ s*:\\ s*\" function\"\\ s*,\\ s*)?\" name\"\\ s*:\\ s*\" ([^\" ]+)\"\\ s*,\\ s*\" parameters\"\\ s*: " );
1131
1139
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
1136
1144
if (with_builtin_tools) {
1137
1145
static const common_regex builtin_call_regex (" <\\ |python_tag\\ |>" );
1138
1146
if (auto res = builder.try_find_regex (builtin_call_regex)) {
1139
- builder.add_content (res->prelude );
1140
-
1141
1147
auto fun_res = builder.consume_regex (function_name_regex);
1142
1148
auto function_name = builder.str (fun_res.groups [1 ]);
1143
1149
@@ -1253,6 +1259,10 @@ static common_chat_params common_chat_params_init_deepseek_r1(const common_chat_
1253
1259
}
1254
1260
static void common_chat_parse_deepseek_r1 (common_chat_msg_parser & builder) {
1255
1261
builder.try_parse_reasoning (" <think>" , " </think>" );
1262
+ if (!builder.syntax ().parse_tool_calls ) {
1263
+ builder.add_content (builder.consume_rest ());
1264
+ return ;
1265
+ }
1256
1266
1257
1267
static const common_regex tool_calls_begin (" (?:<|tool▁calls▁begin|>|<|tool_calls_begin|>|<|tool calls begin|>|<|tool\\\\ _calls\\\\ _begin|>|<|tool▁calls|>)" );
1258
1268
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
1314
1324
return data;
1315
1325
}
1316
1326
static 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
+ }
1317
1331
static const common_regex prefix (regex_escape (" functools[" ));
1318
1332
parse_prefixed_json_tool_call_array (builder, prefix, /* rstrip_prefix= */ 1 );
1319
1333
}
@@ -1455,15 +1469,12 @@ static common_chat_params common_chat_params_init_functionary_v3_1_llama_3_1(con
1455
1469
return data;
1456
1470
}
1457
1471
static 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 ());
1465
1474
return ;
1466
1475
}
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|>" ));
1467
1478
1468
1479
static const common_regex function_regex (R"( <function=(\w+)>)" );
1469
1480
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
1475
1486
function_regex,
1476
1487
close_regex,
1477
1488
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
+ }
1478
1495
}
1479
1496
1480
1497
static 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
1593
1610
}
1594
1611
static void common_chat_parse_hermes_2_pro (common_chat_msg_parser & builder) {
1595
1612
builder.try_parse_reasoning (" <think>" , " </think>" );
1613
+ if (!builder.syntax ().parse_tool_calls ) {
1614
+ builder.add_content (builder.consume_rest ());
1615
+ return ;
1616
+ }
1596
1617
1597
1618
static const common_regex open_regex (
1598
1619
" (?:"
@@ -1614,8 +1635,6 @@ static void common_chat_parse_hermes_2_pro(common_chat_msg_parser & builder) {
1614
1635
);
1615
1636
1616
1637
if (auto res = builder.try_find_regex (open_regex)) {
1617
- builder.add_content (res->prelude );
1618
-
1619
1638
const auto & block_start = res->groups [1 ];
1620
1639
std::string block_end = block_start.empty () ? " " : " ```" ;
1621
1640
@@ -1851,10 +1870,10 @@ static void common_chat_parse_content_only(common_chat_msg_parser & builder) {
1851
1870
builder.add_content (builder.consume_rest ());
1852
1871
}
1853
1872
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 ());
1856
1875
1857
- switch (format) {
1876
+ switch (builder. syntax (). format ) {
1858
1877
case COMMON_CHAT_FORMAT_CONTENT_ONLY:
1859
1878
common_chat_parse_content_only (builder);
1860
1879
break ;
@@ -1889,15 +1908,15 @@ static void common_chat_parse(common_chat_msg_parser & builder, common_chat_form
1889
1908
common_chat_parse_command_r7b (builder);
1890
1909
break ;
1891
1910
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 ));
1893
1912
}
1894
1913
builder.finish ();
1895
1914
}
1896
1915
1897
1916
common_chat_msg common_chat_parse (const std::string & input, bool is_partial, const common_chat_syntax & syntax) {
1898
1917
common_chat_msg_parser builder (input, is_partial, syntax);
1899
1918
try {
1900
- common_chat_parse (builder, syntax. format );
1919
+ common_chat_parse (builder);
1901
1920
} catch (const common_chat_msg_partial_exception & ex) {
1902
1921
LOG_DBG (" Partial parse: %s\n " , ex.what ());
1903
1922
if (!is_partial) {
0 commit comments