common/parser: use nlohmann::ordered_json to preserve parameter order (#20385)
This commit is contained in:
+15
-15
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
using json = nlohmann::ordered_json;
|
using ordered_json = nlohmann::ordered_json;
|
||||||
|
|
||||||
static std::string_view trim_trailing_space(std::string_view sv, int max = -1) {
|
static std::string_view trim_trailing_space(std::string_view sv, int max = -1) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -68,7 +68,7 @@ static int json_brace_depth(const std::string & s) {
|
|||||||
|
|
||||||
// JSON-escape a string and return the inner content (without surrounding quotes).
|
// JSON-escape a string and return the inner content (without surrounding quotes).
|
||||||
static std::string escape_json_string_inner(const std::string & s) {
|
static std::string escape_json_string_inner(const std::string & s) {
|
||||||
std::string escaped = json(s).dump();
|
std::string escaped = ordered_json(s).dump();
|
||||||
if (escaped.size() >= 2 && escaped.front() == '"' && escaped.back() == '"') {
|
if (escaped.size() >= 2 && escaped.front() == '"' && escaped.back() == '"') {
|
||||||
return escaped.substr(1, escaped.size() - 2);
|
return escaped.substr(1, escaped.size() - 2);
|
||||||
}
|
}
|
||||||
@@ -309,7 +309,7 @@ void common_chat_peg_mapper::map(const common_peg_ast_node & node) {
|
|||||||
if (arg_count > 0) {
|
if (arg_count > 0) {
|
||||||
arg_entry = ",";
|
arg_entry = ",";
|
||||||
}
|
}
|
||||||
arg_entry += json(trim(node.text)).dump() + ":";
|
arg_entry += ordered_json(trim(node.text)).dump() + ":";
|
||||||
++arg_count;
|
++arg_count;
|
||||||
|
|
||||||
auto & target = args_target();
|
auto & target = args_target();
|
||||||
@@ -343,7 +343,7 @@ void common_chat_peg_mapper::map(const common_peg_ast_node & node) {
|
|||||||
|
|
||||||
// Try to parse as JSON value (number, bool, null, object, array)
|
// Try to parse as JSON value (number, bool, null, object, array)
|
||||||
try {
|
try {
|
||||||
json parsed = json::parse(value_content);
|
ordered_json parsed = ordered_json::parse(value_content);
|
||||||
if (parsed.is_string()) {
|
if (parsed.is_string()) {
|
||||||
// Don't add closing quote yet (added by arg_close) for monotonic streaming
|
// Don't add closing quote yet (added by arg_close) for monotonic streaming
|
||||||
std::string escaped = parsed.dump();
|
std::string escaped = parsed.dump();
|
||||||
@@ -408,7 +408,7 @@ void common_chat_peg_mapper::map(const common_peg_ast_node & node) {
|
|||||||
|
|
||||||
common_peg_parser common_chat_peg_builder::standard_constructed_tools(
|
common_peg_parser common_chat_peg_builder::standard_constructed_tools(
|
||||||
const std::map<std::string, std::string> & markers,
|
const std::map<std::string, std::string> & markers,
|
||||||
const nlohmann::json & tools,
|
const ordered_json & tools,
|
||||||
bool parallel_tool_calls,
|
bool parallel_tool_calls,
|
||||||
bool force_tool_calls) {
|
bool force_tool_calls) {
|
||||||
if (!tools.is_array() || tools.empty()) {
|
if (!tools.is_array() || tools.empty()) {
|
||||||
@@ -439,7 +439,7 @@ common_peg_parser common_chat_peg_builder::standard_constructed_tools(
|
|||||||
}
|
}
|
||||||
const auto & function = tool_def.at("function");
|
const auto & function = tool_def.at("function");
|
||||||
std::string name = function.at("name");
|
std::string name = function.at("name");
|
||||||
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
|
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
|
||||||
|
|
||||||
// Build argument parsers
|
// Build argument parsers
|
||||||
auto args = eps();
|
auto args = eps();
|
||||||
@@ -479,7 +479,7 @@ common_peg_parser common_chat_peg_builder::standard_constructed_tools(
|
|||||||
// Python-style tool calls: name(arg1="value1", arg2=123)
|
// Python-style tool calls: name(arg1="value1", arg2=123)
|
||||||
// Used only by LFM2 for now, so we don't merge it into autoparser
|
// Used only by LFM2 for now, so we don't merge it into autoparser
|
||||||
common_peg_parser common_chat_peg_builder::python_style_tool_calls(
|
common_peg_parser common_chat_peg_builder::python_style_tool_calls(
|
||||||
const nlohmann::json & tools,
|
const ordered_json & tools,
|
||||||
bool parallel_tool_calls) {
|
bool parallel_tool_calls) {
|
||||||
if (!tools.is_array() || tools.empty()) {
|
if (!tools.is_array() || tools.empty()) {
|
||||||
return eps();
|
return eps();
|
||||||
@@ -493,7 +493,7 @@ common_peg_parser common_chat_peg_builder::python_style_tool_calls(
|
|||||||
}
|
}
|
||||||
const auto & function = tool_def.at("function");
|
const auto & function = tool_def.at("function");
|
||||||
std::string name = function.at("name");
|
std::string name = function.at("name");
|
||||||
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
|
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
|
||||||
|
|
||||||
auto args = eps();
|
auto args = eps();
|
||||||
if (params.contains("properties") && !params["properties"].empty()) {
|
if (params.contains("properties") && !params["properties"].empty()) {
|
||||||
@@ -555,7 +555,7 @@ static std::pair<std::string, std::string> parse_key_spec(const std::string & ke
|
|||||||
|
|
||||||
// Mode 1: function_is_key — parse {"function_name": {...}}
|
// Mode 1: function_is_key — parse {"function_name": {...}}
|
||||||
common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
|
common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
|
||||||
const nlohmann::json & tools,
|
const ordered_json & tools,
|
||||||
const std::string & args_key,
|
const std::string & args_key,
|
||||||
const std::string & effective_args_key,
|
const std::string & effective_args_key,
|
||||||
const std::string & call_id_key,
|
const std::string & call_id_key,
|
||||||
@@ -569,7 +569,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
|
|||||||
}
|
}
|
||||||
const auto & function = tool_def.at("function");
|
const auto & function = tool_def.at("function");
|
||||||
std::string name = function.at("name");
|
std::string name = function.at("name");
|
||||||
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
|
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
|
||||||
|
|
||||||
// Build inner object fields
|
// Build inner object fields
|
||||||
std::vector<common_peg_parser> inner_fields;
|
std::vector<common_peg_parser> inner_fields;
|
||||||
@@ -634,7 +634,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
|
|||||||
|
|
||||||
// Mode 2: Nested keys (dot notation like "function.name")
|
// Mode 2: Nested keys (dot notation like "function.name")
|
||||||
common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
|
common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
|
||||||
const nlohmann::json & tools,
|
const ordered_json & tools,
|
||||||
const std::string & effective_name_key,
|
const std::string & effective_name_key,
|
||||||
const std::string & effective_args_key,
|
const std::string & effective_args_key,
|
||||||
const std::string & call_id_key,
|
const std::string & call_id_key,
|
||||||
@@ -655,7 +655,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
|
|||||||
}
|
}
|
||||||
const auto & function = tool_def.at("function");
|
const auto & function = tool_def.at("function");
|
||||||
std::string name = function.at("name");
|
std::string name = function.at("name");
|
||||||
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
|
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
|
||||||
|
|
||||||
auto nested_name = literal("\"" + nested_name_field + "\"") + space() + literal(":") + space() +
|
auto nested_name = literal("\"" + nested_name_field + "\"") + space() + literal(":") + space() +
|
||||||
literal("\"") + tool_name(literal(name)) + literal("\"");
|
literal("\"") + tool_name(literal(name)) + literal("\"");
|
||||||
@@ -706,7 +706,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
|
|||||||
|
|
||||||
// Mode 3: Flat keys with optional ID fields and parameter ordering
|
// Mode 3: Flat keys with optional ID fields and parameter ordering
|
||||||
common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
|
common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
|
||||||
const nlohmann::json & tools,
|
const ordered_json & tools,
|
||||||
const std::string & effective_name_key,
|
const std::string & effective_name_key,
|
||||||
const std::string & effective_args_key,
|
const std::string & effective_args_key,
|
||||||
const std::string & call_id_key,
|
const std::string & call_id_key,
|
||||||
@@ -723,7 +723,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
|
|||||||
}
|
}
|
||||||
const auto & function = tool_def.at("function");
|
const auto & function = tool_def.at("function");
|
||||||
std::string name = function.at("name");
|
std::string name = function.at("name");
|
||||||
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
|
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
|
||||||
|
|
||||||
auto tool_name_ = name_key_parser + space() + literal(":") + space() +
|
auto tool_name_ = name_key_parser + space() + literal(":") + space() +
|
||||||
literal("\"") + tool_name(literal(name)) + literal("\"");
|
literal("\"") + tool_name(literal(name)) + literal("\"");
|
||||||
@@ -791,7 +791,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
|
|||||||
common_peg_parser common_chat_peg_builder::standard_json_tools(
|
common_peg_parser common_chat_peg_builder::standard_json_tools(
|
||||||
const std::string & section_start,
|
const std::string & section_start,
|
||||||
const std::string & section_end,
|
const std::string & section_end,
|
||||||
const nlohmann::json & tools,
|
const ordered_json & tools,
|
||||||
bool parallel_tool_calls,
|
bool parallel_tool_calls,
|
||||||
bool force_tool_calls,
|
bool force_tool_calls,
|
||||||
const std::string & name_key,
|
const std::string & name_key,
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class common_chat_peg_builder : public common_peg_parser_builder {
|
|||||||
// parameters_order: order in which JSON fields should be parsed
|
// parameters_order: order in which JSON fields should be parsed
|
||||||
common_peg_parser standard_json_tools(const std::string & section_start,
|
common_peg_parser standard_json_tools(const std::string & section_start,
|
||||||
const std::string & section_end,
|
const std::string & section_end,
|
||||||
const nlohmann::json & tools,
|
const nlohmann::ordered_json & tools,
|
||||||
bool parallel_tool_calls,
|
bool parallel_tool_calls,
|
||||||
bool force_tool_calls,
|
bool force_tool_calls,
|
||||||
const std::string & name_key = "",
|
const std::string & name_key = "",
|
||||||
@@ -108,30 +108,30 @@ class common_chat_peg_builder : public common_peg_parser_builder {
|
|||||||
// Legacy-compatible helper for building XML/tagged style tool calls
|
// Legacy-compatible helper for building XML/tagged style tool calls
|
||||||
// Used by tests and manual parsers
|
// Used by tests and manual parsers
|
||||||
common_peg_parser standard_constructed_tools(const std::map<std::string, std::string> & markers,
|
common_peg_parser standard_constructed_tools(const std::map<std::string, std::string> & markers,
|
||||||
const nlohmann::json & tools,
|
const nlohmann::ordered_json & tools,
|
||||||
bool parallel_tool_calls,
|
bool parallel_tool_calls,
|
||||||
bool force_tool_calls);
|
bool force_tool_calls);
|
||||||
|
|
||||||
// Helper for Python-style function call format: name(arg1="value1", arg2=123)
|
// Helper for Python-style function call format: name(arg1="value1", arg2=123)
|
||||||
// Used by LFM2 and similar templates
|
// Used by LFM2 and similar templates
|
||||||
common_peg_parser python_style_tool_calls(const nlohmann::json & tools,
|
common_peg_parser python_style_tool_calls(const nlohmann::ordered_json & tools,
|
||||||
bool parallel_tool_calls);
|
bool parallel_tool_calls);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Implementation helpers for standard_json_tools — one per JSON tool call layout mode
|
// Implementation helpers for standard_json_tools — one per JSON tool call layout mode
|
||||||
common_peg_parser build_json_tools_function_is_key(const nlohmann::json & tools,
|
common_peg_parser build_json_tools_function_is_key(const nlohmann::ordered_json & tools,
|
||||||
const std::string & args_key,
|
const std::string & args_key,
|
||||||
const std::string & effective_args_key,
|
const std::string & effective_args_key,
|
||||||
const std::string & call_id_key,
|
const std::string & call_id_key,
|
||||||
const std::string & gen_call_id_key);
|
const std::string & gen_call_id_key);
|
||||||
|
|
||||||
common_peg_parser build_json_tools_nested_keys(const nlohmann::json & tools,
|
common_peg_parser build_json_tools_nested_keys(const nlohmann::ordered_json & tools,
|
||||||
const std::string & effective_name_key,
|
const std::string & effective_name_key,
|
||||||
const std::string & effective_args_key,
|
const std::string & effective_args_key,
|
||||||
const std::string & call_id_key,
|
const std::string & call_id_key,
|
||||||
const std::string & gen_call_id_key);
|
const std::string & gen_call_id_key);
|
||||||
|
|
||||||
common_peg_parser build_json_tools_flat_keys(const nlohmann::json & tools,
|
common_peg_parser build_json_tools_flat_keys(const nlohmann::ordered_json & tools,
|
||||||
const std::string & effective_name_key,
|
const std::string & effective_name_key,
|
||||||
const std::string & effective_args_key,
|
const std::string & effective_args_key,
|
||||||
const std::string & call_id_key,
|
const std::string & call_id_key,
|
||||||
|
|||||||
Reference in New Issue
Block a user