Skip to content

Commit b5fe455

Browse files
authored
common/parser: use nlohmann::ordered_json to preserve parameter order (ggml-org#20385)
1 parent acb7c79 commit b5fe455

2 files changed

Lines changed: 41 additions & 41 deletions

File tree

common/chat-peg-parser.cpp

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#include <nlohmann/json.hpp>
88

9-
using json = nlohmann::ordered_json;
9+
using ordered_json = nlohmann::ordered_json;
1010

1111
static std::string_view trim_trailing_space(std::string_view sv, int max = -1) {
1212
int count = 0;
@@ -68,7 +68,7 @@ static int json_brace_depth(const std::string & s) {
6868

6969
// JSON-escape a string and return the inner content (without surrounding quotes).
7070
static std::string escape_json_string_inner(const std::string & s) {
71-
std::string escaped = json(s).dump();
71+
std::string escaped = ordered_json(s).dump();
7272
if (escaped.size() >= 2 && escaped.front() == '"' && escaped.back() == '"') {
7373
return escaped.substr(1, escaped.size() - 2);
7474
}
@@ -309,7 +309,7 @@ void common_chat_peg_mapper::map(const common_peg_ast_node & node) {
309309
if (arg_count > 0) {
310310
arg_entry = ",";
311311
}
312-
arg_entry += json(trim(node.text)).dump() + ":";
312+
arg_entry += ordered_json(trim(node.text)).dump() + ":";
313313
++arg_count;
314314

315315
auto & target = args_target();
@@ -343,7 +343,7 @@ void common_chat_peg_mapper::map(const common_peg_ast_node & node) {
343343

344344
// Try to parse as JSON value (number, bool, null, object, array)
345345
try {
346-
json parsed = json::parse(value_content);
346+
ordered_json parsed = ordered_json::parse(value_content);
347347
if (parsed.is_string()) {
348348
// Don't add closing quote yet (added by arg_close) for monotonic streaming
349349
std::string escaped = parsed.dump();
@@ -408,7 +408,7 @@ void common_chat_peg_mapper::map(const common_peg_ast_node & node) {
408408

409409
common_peg_parser common_chat_peg_builder::standard_constructed_tools(
410410
const std::map<std::string, std::string> & markers,
411-
const nlohmann::json & tools,
411+
const ordered_json & tools,
412412
bool parallel_tool_calls,
413413
bool force_tool_calls) {
414414
if (!tools.is_array() || tools.empty()) {
@@ -439,7 +439,7 @@ common_peg_parser common_chat_peg_builder::standard_constructed_tools(
439439
}
440440
const auto & function = tool_def.at("function");
441441
std::string name = function.at("name");
442-
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
442+
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
443443

444444
// Build argument parsers
445445
auto args = eps();
@@ -479,8 +479,8 @@ common_peg_parser common_chat_peg_builder::standard_constructed_tools(
479479
// Python-style tool calls: name(arg1="value1", arg2=123)
480480
// Used only by LFM2 for now, so we don't merge it into autoparser
481481
common_peg_parser common_chat_peg_builder::python_style_tool_calls(
482-
const nlohmann::json & tools,
483-
bool parallel_tool_calls) {
482+
const ordered_json & tools,
483+
bool parallel_tool_calls) {
484484
if (!tools.is_array() || tools.empty()) {
485485
return eps();
486486
}
@@ -493,7 +493,7 @@ common_peg_parser common_chat_peg_builder::python_style_tool_calls(
493493
}
494494
const auto & function = tool_def.at("function");
495495
std::string name = function.at("name");
496-
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
496+
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
497497

498498
auto args = eps();
499499
if (params.contains("properties") && !params["properties"].empty()) {
@@ -555,11 +555,11 @@ static std::pair<std::string, std::string> parse_key_spec(const std::string & ke
555555

556556
// Mode 1: function_is_key — parse {"function_name": {...}}
557557
common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
558-
const nlohmann::json & tools,
559-
const std::string & args_key,
560-
const std::string & effective_args_key,
561-
const std::string & call_id_key,
562-
const std::string & gen_call_id_key) {
558+
const ordered_json & tools,
559+
const std::string & args_key,
560+
const std::string & effective_args_key,
561+
const std::string & call_id_key,
562+
const std::string & gen_call_id_key) {
563563

564564
auto tool_choices = choice();
565565

@@ -569,7 +569,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
569569
}
570570
const auto & function = tool_def.at("function");
571571
std::string name = function.at("name");
572-
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
572+
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
573573

574574
// Build inner object fields
575575
std::vector<common_peg_parser> inner_fields;
@@ -634,11 +634,11 @@ common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key(
634634

635635
// Mode 2: Nested keys (dot notation like "function.name")
636636
common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
637-
const nlohmann::json & tools,
638-
const std::string & effective_name_key,
639-
const std::string & effective_args_key,
640-
const std::string & call_id_key,
641-
const std::string & gen_call_id_key) {
637+
const ordered_json & tools,
638+
const std::string & effective_name_key,
639+
const std::string & effective_args_key,
640+
const std::string & call_id_key,
641+
const std::string & gen_call_id_key) {
642642

643643
auto tool_choices = choice();
644644

@@ -655,7 +655,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
655655
}
656656
const auto & function = tool_def.at("function");
657657
std::string name = function.at("name");
658-
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
658+
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
659659

660660
auto nested_name = literal("\"" + nested_name_field + "\"") + space() + literal(":") + space() +
661661
literal("\"") + tool_name(literal(name)) + literal("\"");
@@ -706,7 +706,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys(
706706

707707
// Mode 3: Flat keys with optional ID fields and parameter ordering
708708
common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
709-
const nlohmann::json & tools,
709+
const ordered_json & tools,
710710
const std::string & effective_name_key,
711711
const std::string & effective_args_key,
712712
const std::string & call_id_key,
@@ -723,7 +723,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
723723
}
724724
const auto & function = tool_def.at("function");
725725
std::string name = function.at("name");
726-
nlohmann::json params = function.contains("parameters") ? function.at("parameters") : nlohmann::json::object();
726+
ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object();
727727

728728
auto tool_name_ = name_key_parser + space() + literal(":") + space() +
729729
literal("\"") + tool_name(literal(name)) + literal("\"");
@@ -791,7 +791,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys(
791791
common_peg_parser common_chat_peg_builder::standard_json_tools(
792792
const std::string & section_start,
793793
const std::string & section_end,
794-
const nlohmann::json & tools,
794+
const ordered_json & tools,
795795
bool parallel_tool_calls,
796796
bool force_tool_calls,
797797
const std::string & name_key,

common/chat-peg-parser.h

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class common_chat_peg_builder : public common_peg_parser_builder {
9494
// parameters_order: order in which JSON fields should be parsed
9595
common_peg_parser standard_json_tools(const std::string & section_start,
9696
const std::string & section_end,
97-
const nlohmann::json & tools,
97+
const nlohmann::ordered_json & tools,
9898
bool parallel_tool_calls,
9999
bool force_tool_calls,
100100
const std::string & name_key = "",
@@ -108,30 +108,30 @@ class common_chat_peg_builder : public common_peg_parser_builder {
108108
// Legacy-compatible helper for building XML/tagged style tool calls
109109
// Used by tests and manual parsers
110110
common_peg_parser standard_constructed_tools(const std::map<std::string, std::string> & markers,
111-
const nlohmann::json & tools,
111+
const nlohmann::ordered_json & tools,
112112
bool parallel_tool_calls,
113113
bool force_tool_calls);
114114

115115
// Helper for Python-style function call format: name(arg1="value1", arg2=123)
116116
// Used by LFM2 and similar templates
117-
common_peg_parser python_style_tool_calls(const nlohmann::json & tools,
118-
bool parallel_tool_calls);
117+
common_peg_parser python_style_tool_calls(const nlohmann::ordered_json & tools,
118+
bool parallel_tool_calls);
119119

120120
private:
121121
// Implementation helpers for standard_json_tools — one per JSON tool call layout mode
122-
common_peg_parser build_json_tools_function_is_key(const nlohmann::json & tools,
123-
const std::string & args_key,
124-
const std::string & effective_args_key,
125-
const std::string & call_id_key,
126-
const std::string & gen_call_id_key);
127-
128-
common_peg_parser build_json_tools_nested_keys(const nlohmann::json & tools,
129-
const std::string & effective_name_key,
130-
const std::string & effective_args_key,
131-
const std::string & call_id_key,
132-
const std::string & gen_call_id_key);
133-
134-
common_peg_parser build_json_tools_flat_keys(const nlohmann::json & tools,
122+
common_peg_parser build_json_tools_function_is_key(const nlohmann::ordered_json & tools,
123+
const std::string & args_key,
124+
const std::string & effective_args_key,
125+
const std::string & call_id_key,
126+
const std::string & gen_call_id_key);
127+
128+
common_peg_parser build_json_tools_nested_keys(const nlohmann::ordered_json & tools,
129+
const std::string & effective_name_key,
130+
const std::string & effective_args_key,
131+
const std::string & call_id_key,
132+
const std::string & gen_call_id_key);
133+
134+
common_peg_parser build_json_tools_flat_keys(const nlohmann::ordered_json & tools,
135135
const std::string & effective_name_key,
136136
const std::string & effective_args_key,
137137
const std::string & call_id_key,

0 commit comments

Comments
 (0)