19#include <nlohmann/json.hpp>
30constexpr const char* TOOL_RESULT_SUFFIX =
31 "Continue. Batch multiple tool calls in one response when possible.";
40 static std::atomic<uint64_t> counter{0};
41 return "tc-" + std::to_string(counter.fetch_add(1, std::memory_order_relaxed));
63 auto calls = parse_dsml_function_calls(content);
65 calls = parse_xml_function_calls(content);
90std::vector<ToolCall> Nemotron3Adapter::parse_dsml_function_calls(
91 const std::string& content)
const
93 std::vector<ToolCall> calls;
97 std::regex invoke_pattern(
98 R
"re(<|DSML|invoke name="([^"]+)">([\s\S]*?)</|DSML|invoke>)re");
100 auto begin = std::sregex_iterator(content.begin(), content.end(), invoke_pattern);
101 auto end = std::sregex_iterator();
103 for (
auto it = begin; it != end; ++it) {
106 tc.
name = (*it)[1].str();
107 tc.
arguments = extract_dsml_parameters((*it)[2].str());
108 calls.push_back(std::move(tc));
110 logger->info(
"Parsed DSML invoke: {}", calls.back().name);
126std::unordered_map<std::string, std::string> Nemotron3Adapter::extract_dsml_parameters(
127 const std::string& invoke_body)
const
129 std::unordered_map<std::string, std::string> arguments;
132 std::regex param_pattern(
133 R
"re(<|DSML|parameter name="([^"]+)"\s+(?:string|int|bool|value)="([^"]*)"\s*/>)re");
135 auto begin = std::sregex_iterator(invoke_body.begin(), invoke_body.end(), param_pattern);
136 auto end = std::sregex_iterator();
138 for (
auto it = begin; it != end; ++it) {
139 arguments[(*it)[1].str()] = (*it)[2].str();
151std::vector<ToolCall> Nemotron3Adapter::parse_xml_function_calls(
152 const std::string& content)
const
154 std::vector<ToolCall> calls;
155 std::regex func_pattern(R
"(<function=([^>]+)>([\s\S]*?)</function>)");
157 auto begin = std::sregex_iterator(content.begin(), content.end(), func_pattern);
158 auto end = std::sregex_iterator();
160 for (
auto it = begin; it != end; ++it) {
161 std::string func_name = (*it)[1].str();
162 std::string func_body = (*it)[2].str();
164 auto ns = func_name.find_first_not_of(
" \t");
165 auto ne = func_name.find_last_not_of(
" \t");
166 if (ns != std::string::npos) {
167 func_name = func_name.substr(ns, ne - ns + 1);
173 tc.arguments = extract_xml_parameters(func_body);
174 calls.push_back(std::move(tc));
176 logger->info(
"Parsed XML tool call: {}", func_name);
188std::unordered_map<std::string, std::string> Nemotron3Adapter::extract_xml_parameters(
189 const std::string& func_body)
const
191 std::string body = func_body;
192 auto nested = body.find(
"<function=");
193 if (nested != std::string::npos) {
194 logger->warn(
"Truncating function body at nested <function= tag");
195 body = body.substr(0, nested);
198 std::unordered_map<std::string, std::string> arguments;
199 std::regex param_pattern(R
"(<parameter=([^>]+)>([\s\S]*?)</parameter>)");
201 auto begin = std::sregex_iterator(body.begin(), body.end(), param_pattern);
202 auto end = std::sregex_iterator();
204 for (
auto it = begin; it != end; ++it) {
205 std::string key = (*it)[1].str();
206 std::string value = (*it)[2].str();
208 auto ks = key.find_first_not_of(
" \t\n\r");
209 auto ke = key.find_last_not_of(
" \t\n\r");
210 if (ks != std::string::npos) key = key.substr(ks, ke - ks + 1);
212 auto vs = value.find_first_not_of(
" \t\n\r");
213 auto ve = value.find_last_not_of(
" \t\n\r");
214 if (vs != std::string::npos) value = value.substr(vs, ve - vs + 1);
216 if (key.empty() || value.empty()) {
217 logger->warn(
"Skipping empty XML parameter: key='{}' value='{}'", key, value);
220 arguments[key] = value;
237 const std::string& result)
const
241 msg.
content =
"<tool_response>\n" + result +
242 "\n</tool_response>\n\n" + TOOL_RESULT_SUFFIX;
256 const std::vector<std::string>& tool_jsons)
258 nlohmann::json tool_defs = nlohmann::json::array();
259 for (
const auto& json_str : tool_jsons) {
261 auto j = nlohmann::json::parse(json_str);
262 tool_defs.push_back({
263 {
"type",
"function"},
265 {
"name", j.value(
"name",
"unknown")},
266 {
"description", j.value(
"description",
"")},
267 {
"parameters", j.value(
"inputSchema",
268 nlohmann::json::object())}
291 const std::vector<std::string>& tool_jsons)
const
293 std::ostringstream out;
295 <<
"You may call one or more functions to assist with the user query.\n"
296 <<
"Put your final answer OUTSIDE of any tool calls.\n\n"
297 <<
"Here are the available tools:\n"
301 <<
"For each function call, emit a DSML invoke block:\n"
302 <<
"<|DSML|function_calls>\n"
303 <<
"<|DSML|invoke name=\"example.tool\">\n"
304 <<
"<|DSML|parameter name=\"param_name\" string=\"value\"/>\n"
305 <<
"</|DSML|invoke>\n"
306 <<
"</|DSML|function_calls>";
328std::string Nemotron3Adapter::clean_content(
const std::string& content)
const {
329 std::string cleaned = std::regex_replace(content,
330 std::regex(R
"(<tool_call>\s*[\s\S]*?\s*</tool_call>)"), "");
331 cleaned = std::regex_replace(cleaned,
332 std::regex(R
"(<function=[^>]+>[\s\S]*?</function>)"), "");
333 cleaned = std::regex_replace(cleaned,
334 std::regex(R
"(<|DSML|function_calls>[\s\S]*?</|DSML|function_calls>)"), "");
335 cleaned = std::regex_replace(cleaned,
336 std::regex(R
"(<|DSML|invoke[\s\S]*?</|DSML|invoke>)"), "");
337 cleaned = std::regex_replace(cleaned,
338 std::regex(R
"(</?|[^>]*>)"), "");
std::vector< ToolCall > parse_tagged_tool_calls(const std::string &content) const
Parse <tool_call>JSON</tool_call> tagged blocks.
std::string strip_think_blocks(const std::string &content) const
Strip all <think>...</think> blocks from content.
std::string format_tools(const std::vector< std::string > &tool_jsons) const override
Format tools as a <tools> JSON array, then teach DSML invoke.
Message format_tool_result(const ToolCall &tool_call, const std::string &result) const override
Wrap tool result in <tool_response> tags.
ParseResult parse_tool_calls(const std::string &content) const override
Parse DSML invoke calls; fall back to qwen XML, then tagged JSON.
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Activate model on GPU (WARM → ACTIVE).
std::string generate_uuid()
Generate a UUID v4 string.
static nlohmann::json build_tool_defs(const std::vector< std::string > &tool_jsons)
Build the OpenAI-function <tools> JSON array for injection.
Nemotron 3 chat adapter (v2.1.9, gh#47).
A message in a conversation.
std::string content
Message text content (always populated)
std::string role
Message role.
Parsed tool call result: cleaned content + extracted calls.
std::string cleaned_content
Content with tool calls removed.
std::vector< ToolCall > tool_calls
Extracted tool calls.