* Takes a prefix regex that must have 1 group to capture the function name, a closing suffix, and expects json parameters in between. * Aggregates the prefix, suffix and in-between text into the content. */
| 623 | * Aggregates the prefix, suffix and in-between text into the content. |
| 624 | */ |
| 625 | static void parse_json_tool_calls( |
| 626 | common_chat_msg_parser & builder, |
| 627 | const std::optional<common_regex> & block_open, |
| 628 | const std::optional<common_regex> & function_regex_start_only, |
| 629 | const std::optional<common_regex> & function_regex, |
| 630 | const common_regex & close_regex, |
| 631 | const std::optional<common_regex> & block_close, |
| 632 | bool allow_raw_python = false, |
| 633 | const std::function<std::string(const common_chat_msg_parser::find_regex_result & fres)> & get_function_name = nullptr) { |
| 634 | |
| 635 | auto parse_tool_calls = [&]() { |
| 636 | size_t from = std::string::npos; |
| 637 | auto first = true; |
| 638 | while (true) { |
| 639 | auto res = function_regex_start_only && first |
| 640 | ? builder.try_consume_regex(*function_regex_start_only) |
| 641 | : function_regex |
| 642 | ? builder.try_find_regex(*function_regex, from) |
| 643 | : std::nullopt; |
| 644 | if (res) { |
| 645 | std::string name; |
| 646 | if (get_function_name) { |
| 647 | name = get_function_name(*res); |
| 648 | } else { |
| 649 | GGML_ASSERT(res->groups.size() == 2); |
| 650 | name = builder.str(res->groups[1]); |
| 651 | } |
| 652 | first = false; |
| 653 | if (name.empty()) { |
| 654 | // get_function_name signalled us that we should skip this match and treat it as content. |
| 655 | from = res->groups[0].begin + 1; |
| 656 | continue; |
| 657 | } |
| 658 | from = std::string::npos; |
| 659 | |
| 660 | auto maybe_raw_python = name == "python" && allow_raw_python; |
| 661 | if (builder.input()[builder.pos()] == '{' || !maybe_raw_python) { |
| 662 | if (auto arguments = builder.try_consume_json_with_dumped_args({{}})) { |
| 663 | if (!builder.add_tool_call(name, "", arguments->value) || arguments->is_partial) { |
| 664 | throw common_chat_msg_partial_exception("incomplete tool call"); |
| 665 | } |
| 666 | builder.consume_regex(close_regex); |
| 667 | } |
| 668 | continue; |
| 669 | } |
| 670 | if (maybe_raw_python) { |
| 671 | auto arguments = wrap_code_as_arguments(builder, builder.consume_rest()); |
| 672 | if (!builder.add_tool_call(name, "", arguments)) { |
| 673 | throw common_chat_msg_partial_exception("incomplete tool call"); |
| 674 | } |
| 675 | return; |
| 676 | } |
| 677 | throw common_chat_msg_partial_exception("incomplete tool call"); |
| 678 | } |
| 679 | break; |
| 680 | } |
| 681 | if (block_close) { |
| 682 | builder.consume_regex(*block_close); |
no test coverage detected