Entropic 2.9.5
Local-first agentic inference engine
Loading...
Searching...
No Matches
xml_parameter_parser.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
9
10#include <regex>
11#include <string>
12#include <utility>
13
14namespace entropic::inference::adapters {
15
16namespace {
17
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);
28}
29
35const std::regex& open_tag_pattern() {
36 static const std::regex pat(R"(<parameter=([^>]+)>)");
37 return pat;
38}
39
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; }
54 if (logger) {
55 logger->warn("Truncating function body at nested <function= tag");
56 }
57 return body.substr(0, nested);
58}
59
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};
79 }
80 if (p_pos < n_pos) { return {p_pos, close_paren.size()}; }
81 return {n_pos, close_named.size()};
82}
83
89inline void emit_arg(
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);
95 if (value.empty()) {
96 if (logger) {
97 logger->warn("Skipping empty XML parameter value: key='{}'", key);
98 }
99 return;
100 }
101 args[key] = std::move(value);
102}
103
104} // namespace
105
116std::unordered_map<std::string, std::string> parse_xml_parameters(
117 const std::string& func_body,
118 const std::shared_ptr<spdlog::logger>& logger) {
119
120 const std::string body = truncate_at_nested_function(func_body, logger);
121 std::unordered_map<std::string, std::string> arguments;
122
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));
131
132 if (key.empty()) {
133 if (logger) { logger->warn("Skipping <parameter=> with empty key"); }
134 search_start = value_begin;
135 continue;
136 }
137
138 const auto [end_pos, close_len] = find_close_tag(body, value_offset, key);
139 if (end_pos == std::string::npos) {
140 if (logger) {
141 logger->warn(
142 "Skipping <parameter={}> with no closing tag", key);
143 }
144 search_start = value_begin;
145 continue;
146 }
147
148 emit_arg(arguments, key,
149 body.substr(value_offset, end_pos - value_offset), logger);
150
151 search_start = body.cbegin()
152 + static_cast<std::ptrdiff_t>(end_pos + close_len);
153 }
154
155 return arguments;
156}
157
158} // namespace entropic::inference::adapters
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....