14namespace entropic::inference::adapters {
23inline std::string trim_ws(
const std::string& s) {
24 auto first = s.find_first_not_of(
" \t\n\r");
25 if (first == std::string::npos) {
return {}; }
26 auto last = s.find_last_not_of(
" \t\n\r");
27 return s.substr(first, last - first + 1);
35const std::regex& open_tag_pattern() {
36 static const std::regex pat(R
"(<parameter=([^>]+)>)");
49inline std::string truncate_at_nested_function(
50 const std::string& body,
51 const std::shared_ptr<spdlog::logger>& logger) {
52 auto nested = body.find(
"<function=");
53 if (nested == std::string::npos) {
return body; }
55 logger->warn(
"Truncating function body at nested <function= tag");
57 return body.substr(0, nested);
71inline std::pair<size_t, size_t> find_close_tag(
72 const std::string& body,
size_t value_offset,
const std::string& key) {
73 const std::string close_paren =
"</parameter>";
74 const std::string close_named =
"</" + key +
">";
75 const auto p_pos = body.find(close_paren, value_offset);
76 const auto n_pos = body.find(close_named, value_offset);
77 if (p_pos == std::string::npos && n_pos == std::string::npos) {
78 return {std::string::npos, 0};
80 if (p_pos < n_pos) {
return {p_pos, close_paren.size()}; }
81 return {n_pos, close_named.size()};
90 std::unordered_map<std::string, std::string>& args,
91 const std::string& key,
92 const std::string& raw_value,
93 const std::shared_ptr<spdlog::logger>& logger) {
94 auto value = trim_ws(raw_value);
97 logger->warn(
"Skipping empty XML parameter value: key='{}'", key);
101 args[key] = std::move(value);
117 const std::string& func_body,
118 const std::shared_ptr<spdlog::logger>& logger) {
120 const std::string body = truncate_at_nested_function(func_body, logger);
121 std::unordered_map<std::string, std::string> arguments;
123 auto search_start = body.cbegin();
124 std::smatch open_match;
125 while (std::regex_search(search_start, body.cend(),
126 open_match, open_tag_pattern())) {
127 const std::string key = trim_ws(open_match[1].str());
128 const auto value_begin = open_match[0].second;
129 const auto value_offset =
130 static_cast<size_t>(std::distance(body.cbegin(), value_begin));
133 if (logger) { logger->warn(
"Skipping <parameter=> with empty key"); }
134 search_start = value_begin;
138 const auto [end_pos, close_len] = find_close_tag(body, value_offset, key);
139 if (end_pos == std::string::npos) {
142 "Skipping <parameter={}> with no closing tag", key);
144 search_start = value_begin;
148 emit_arg(arguments, key,
149 body.substr(value_offset, end_pos - value_offset), logger);
151 search_start = body.cbegin()
152 +
static_cast<std::ptrdiff_t
>(end_pos + close_len);
std::unordered_map< std::string, std::string > parse_xml_parameters(const std::string &func_body, const std::shared_ptr< spdlog::logger > &logger)
Extract <parameter=NAME>value</parameter> pairs from a function body.
Shared <parameter=NAME>value</parameter> extractor for XML-format chat adapters (Qwen 3....