diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 36f1e0cd50..3f7d4b760d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -81,6 +81,7 @@ add_library(${TARGET} imatrix-loader.cpp imatrix-loader.h json-schema-to-grammar.cpp + json-shim.h json.cpp json.h llguidance.cpp diff --git a/common/chat-auto-parser-generator.cpp b/common/chat-auto-parser-generator.cpp index af84ff323d..81a4ef0aee 100644 --- a/common/chat-auto-parser-generator.cpp +++ b/common/chat-auto-parser-generator.cpp @@ -5,13 +5,12 @@ #include "common.h" #include "json-schema-to-grammar.h" #include "log.h" -#include "nlohmann/json.hpp" #include "peg-parser.h" #include #include -using json = nlohmann::ordered_json; +using json = common_json; // Helper to iterate over tools/functions static void foreach_function(const json & tools, const std::function & fn) { @@ -312,7 +311,7 @@ common_peg_parser analyze_tools::build_tool_parser_tag_json(parser_build_context foreach_function(inputs.tools, [&](const json & tool) { const auto & func = tool.at("function"); - std::string name = func.at("name"); + std::string name = func.at("name").get(); const auto & schema = func.contains("parameters") ? func.at("parameters") : json::object(); // Build call_id parser based on position (if supported) @@ -385,13 +384,13 @@ common_peg_parser analyze_tools::build_tool_parser_tag_tagged(parser_build_conte foreach_function(inputs.tools, [&](const json & tool) { const auto & func = tool.at("function"); - std::string name = func.at("name"); + std::string name = func.at("name").get(); auto params = func.contains("parameters") ? func.at("parameters") : json::object(); const auto & properties = params.contains("properties") ? params.at("properties") : json::object(); std::set required; if (params.contains("required")) { - params.at("required").get_to(required); + required = params.at("required").get>(); } auto schema_info = common_schema_info(); diff --git a/common/chat-auto-parser.h b/common/chat-auto-parser.h index 074216b11e..8ae15c91e1 100644 --- a/common/chat-auto-parser.h +++ b/common/chat-auto-parser.h @@ -4,7 +4,7 @@ #include "common.h" #include "jinja/caps.h" #include "peg-parser.h" -#include "nlohmann/json.hpp" +#include "json.h" #include #include @@ -12,7 +12,7 @@ #include #include -using json = nlohmann::ordered_json; +using json = common_json; class common_chat_peg_builder; diff --git a/common/chat-diff-analyzer.cpp b/common/chat-diff-analyzer.cpp index d6d2af2d50..79ce8153bf 100644 --- a/common/chat-diff-analyzer.cpp +++ b/common/chat-diff-analyzer.cpp @@ -4,9 +4,9 @@ #include "chat.h" #include "common.h" #include "log.h" -#include "nlohmann/json.hpp" #include "peg-parser.h" +#include #include #include #include @@ -17,7 +17,7 @@ #define ANSI_ORANGE "\033[1m\x1b[38;5;214m" #define ANSI_RED "\033[1m\x1b[38;5;196m" -using json = nlohmann::ordered_json; +using json = common_json; namespace autoparser { @@ -929,10 +929,10 @@ void analyze_tools::analyze_tool_call_format_json_native(const std::string & cle int json_end = clean_haystack.find_last_of('}'); std::string cut = clean_haystack.substr(json_start, json_end - json_start + 1); json call_struct = json::parse(cut); - auto register_field = [&](const std::string & prefix, const nlohmann::detail::iteration_proxy_value & subel) { - if (subel.value().is_string() && std::string(subel.value()).find("call0000") != std::string::npos) { + auto register_field = [&](const std::string & prefix, const common_json_entry & subel) { + if (subel.value().is_string() && subel.value().get().find("call0000") != std::string::npos) { format.id_field = !prefix.empty() ? prefix + "." + subel.key() : subel.key(); - } else if (subel.value().is_string() && std::string(subel.value()) == fun_name_needle) { + } else if (subel.value().is_string() && subel.value().get() == fun_name_needle) { format.name_field = !prefix.empty() ? prefix + "." + subel.key() : subel.key(); } else if (subel.value().dump().find(arg_name_needle) != std::string::npos) { // handle both string and JSON obj variants diff --git a/common/chat-peg-parser.cpp b/common/chat-peg-parser.cpp index 06737b165c..6f41daa403 100644 --- a/common/chat-peg-parser.cpp +++ b/common/chat-peg-parser.cpp @@ -4,12 +4,11 @@ #include "ggml.h" #include "peg-parser.h" -#include #include #include -using ordered_json = nlohmann::ordered_json; +using ordered_json = common_json; static std::string_view trim_trailing_space(std::string_view sv, int max = -1) { int count = 0; @@ -489,7 +488,7 @@ common_peg_parser common_chat_peg_builder::standard_constructed_tools( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); // Build argument parsers @@ -566,7 +565,7 @@ common_peg_parser common_chat_peg_builder::python_style_tool_calls( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); auto args = eps(); @@ -641,7 +640,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_function_is_key( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); // Build inner object fields @@ -727,7 +726,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_nested_keys( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); auto nested_name = literal("\"" + nested_name_field + "\"") + space() + literal(":") + space() + @@ -796,7 +795,7 @@ common_peg_parser common_chat_peg_builder::build_json_tools_flat_keys( continue; } const auto & function = tool_def.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); ordered_json params = function.contains("parameters") ? function.at("parameters") : ordered_json::object(); auto tool_name_ = name_key_parser + space() + literal(":") + space() + diff --git a/common/chat-peg-parser.h b/common/chat-peg-parser.h index 5d764dbaa0..114fa049fa 100644 --- a/common/chat-peg-parser.h +++ b/common/chat-peg-parser.h @@ -128,7 +128,7 @@ class common_chat_peg_builder : public common_peg_parser_builder { // parameters_order: order in which JSON fields should be parsed common_peg_parser standard_json_tools(const std::string & section_start, const std::string & section_end, - const nlohmann::ordered_json & tools, + const common_json & tools, bool parallel_tool_calls, bool force_tool_calls, const std::string & name_key = "", @@ -143,13 +143,13 @@ class common_chat_peg_builder : public common_peg_parser_builder { // Legacy-compatible helper for building XML/tagged style tool calls // Used by tests and manual parsers common_peg_parser standard_constructed_tools(const std::map & markers, - const nlohmann::ordered_json & tools, + const common_json & tools, bool parallel_tool_calls, bool force_tool_calls); // Helper for Python-style function call format: name(arg1="value1", arg2=123) // Used by LFM2 and similar templates - common_peg_parser python_style_tool_calls(const nlohmann::ordered_json & tools, + common_peg_parser python_style_tool_calls(const common_json & tools, bool parallel_tool_calls, bool allow_json_literals); @@ -158,19 +158,19 @@ class common_chat_peg_builder : public common_peg_parser_builder { common_peg_parser python_or_json_value(); // Implementation helpers for standard_json_tools — one per JSON tool call layout mode - common_peg_parser build_json_tools_function_is_key(const nlohmann::ordered_json & tools, + common_peg_parser build_json_tools_function_is_key(const common_json & tools, const std::string & args_key, const std::string & effective_args_key, const std::string & call_id_key, const std::string & gen_call_id_key); - common_peg_parser build_json_tools_nested_keys(const nlohmann::ordered_json & tools, + common_peg_parser build_json_tools_nested_keys(const common_json & tools, const std::string & effective_name_key, const std::string & effective_args_key, const std::string & call_id_key, const std::string & gen_call_id_key); - common_peg_parser build_json_tools_flat_keys(const nlohmann::ordered_json & tools, + common_peg_parser build_json_tools_flat_keys(const common_json & tools, const std::string & effective_name_key, const std::string & effective_args_key, const std::string & call_id_key, diff --git a/common/chat.cpp b/common/chat.cpp index ff5f2a97f8..4409a2b2e7 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -14,7 +14,6 @@ #include "jinja/caps.h" #include "peg-parser.h" -#include "nlohmann/json.hpp" #include #include @@ -31,7 +30,7 @@ #include #include -using json = nlohmann::ordered_json; +using json = common_json; static std::string format_time(const std::chrono::system_clock::time_point & now, const std::string & format) { auto time = std::chrono::system_clock::to_time_t(now); @@ -49,7 +48,7 @@ static json safe_args_parse(const std::string & to_parse) { } try { return json::parse(stripped); - } catch (json::exception & e) { + } catch (const common_json_error & e) { return stripped; } } @@ -387,14 +386,14 @@ std::vector common_chat_msgs_parse_oaicompat(const json & messa if (!message.contains("role")) { throw std::invalid_argument("Missing 'role' in message: " + message.dump()); } - msg.role = message.at("role"); + msg.role = message.at("role").get(); auto has_content = message.contains("content"); auto has_tool_calls = message.contains("tool_calls"); if (has_content) { const auto & content = message.at("content"); if (content.is_string()) { - msg.content = content; + msg.content = content.get(); } else if (content.is_array()) { for (const auto & part : content) { if (!part.contains("type")) { @@ -405,8 +404,8 @@ std::vector common_chat_msgs_parse_oaicompat(const json & messa throw std::invalid_argument("Unsupported content part type: " + type.dump()); } common_chat_msg_content_part msg_part; - msg_part.type = type; - msg_part.text = part.at("text"); + msg_part.type = type.get(); + msg_part.text = part.at("text").get(); msg.content_parts.push_back(msg_part); } } else if (!content.is_null()) { @@ -432,15 +431,15 @@ std::vector common_chat_msgs_parse_oaicompat(const json & messa if (!fc.contains("name")) { throw std::invalid_argument("Missing tool call name: " + tool_call.dump()); } - tc.name = fc.at("name"); + tc.name = fc.at("name").get(); const auto & args = fc.at("arguments"); if (args.is_string()) { - tc.arguments = args; + tc.arguments = args.get(); } else { tc.arguments = args.dump(); } if (tool_call.contains("id")) { - tc.id = tool_call.at("id"); + tc.id = tool_call.at("id").get(); } msg.tool_calls.push_back(tc); } @@ -451,13 +450,13 @@ std::vector common_chat_msgs_parse_oaicompat(const json & messa "https://github.com/ggml-org/llama.cpp/issues/12279)"); } if (message.contains("reasoning_content")) { - msg.reasoning_content = message.at("reasoning_content"); + msg.reasoning_content = message.at("reasoning_content").get(); } if (message.contains("name")) { - msg.tool_name = message.at("name"); + msg.tool_name = message.at("name").get(); } if (message.contains("tool_call_id")) { - msg.tool_call_id = message.at("tool_call_id"); + msg.tool_call_id = message.at("tool_call_id").get(); } msgs.push_back(msg); @@ -489,17 +488,17 @@ struct messages_inp_normalizer { json normalized = json::array(); for (const auto & msg : messages) { json copy = msg; - auto it = copy.find("content"); - if (it != copy.end()) { - if (only_typed && it->is_string()) { - *it = json::array({ + if (copy.contains("content")) { + json & it = copy.at("content"); + if (only_typed && it.is_string()) { + it = json::array({ json{ {"type", "text"}, - {"text", it->get()}, + {"text", it.get()}, } }); - } else if (only_string && it->is_array()) { - *it = concat_content_parts(*it); + } else if (only_string && it.is_array()) { + it = concat_content_parts(it); } } normalized.push_back(std::move(copy)); @@ -596,7 +595,7 @@ std::vector common_chat_tools_parse_oaicompat(const json & too const auto & function = tool.at("function"); result.push_back({ - /* .name = */ function.at("name"), + /* .name = */ function.at("name").get(), /* .description = */ function.value("description", ""), /* .parameters = */ function.value("parameters", json::object()).dump(), }); @@ -609,7 +608,7 @@ std::vector common_chat_tools_parse_oaicompat(const json & too return result; } -common_chat_continuation common_chat_continuation_parse(const nlohmann::ordered_json & value) { +common_chat_continuation common_chat_continuation_parse(const common_json & value) { if (value.is_boolean() && value.get()) { return COMMON_CHAT_CONTINUATION_AUTO; } @@ -921,7 +920,7 @@ static void foreach_parameter(const json & const auto & props = params.at("properties"); std::set required; if (params.contains("required") && params.at("required").is_array()) { - params.at("required").get_to(required); + required = params.at("required").get>(); } for (const auto & [name, prop] : props.items()) { bool is_required = (required.find(name) != required.end()); @@ -938,7 +937,7 @@ static std::string common_chat_template_direct_apply_impl( jinja::context ctx(tmpl.source()); // messages_override is already built for this template, do not touch its content parts - nlohmann::ordered_json inp = nlohmann::ordered_json{ + common_json inp = common_json{ {"messages", messages_override.has_value() ? *messages_override : messages_inp_normalizer(tmpl.original_caps()).normalize(inputs.messages)}, @@ -973,7 +972,7 @@ static std::string common_chat_template_direct_apply_impl( jinja::caps_apply_reasoning_effort(ctx, reasoning_effort); } - jinja::global_from_json(ctx, common_json_from_raw(inp), inputs.mark_input); + jinja::global_from_json(ctx, inp, inputs.mark_input); // render jinja::runtime runtime(ctx); @@ -1059,7 +1058,7 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_ }); } else if (msg.at("content").is_array()) { auto blocks = msg.at("content"); - content.insert(content.end(), blocks.begin(), blocks.end()); + content.insert(blocks); } } @@ -1114,7 +1113,7 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_ auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); const auto & schema = function.at("parameters"); tool_choice |= @@ -1222,7 +1221,7 @@ static common_chat_params common_chat_params_init_qwen3_coder(const common_chat_ // starting . The model may hallucinate a tool name, but it is preferable over // constraining on foreach_function(inputs.tools, [&](const json & tool) { - const std::string name = tool.at("function").at("name"); + const std::string name = tool.at("function").at("name").get(); tool_call_starts.push_back(""); }); @@ -1250,7 +1249,7 @@ static common_chat_params common_chat_params_init_qwen3_coder(const common_chat_ auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); auto parameters = function.contains("parameters") ? function.at("parameters") : json::object(); auto schema_info = common_schema_info(); @@ -1441,7 +1440,7 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); const auto & params = function.at("parameters"); auto func_name = p.literal(" to=functions.") + p.tool_name(p.literal(name)); @@ -1607,7 +1606,7 @@ static common_chat_params common_chat_params_init_gemma4(const common_chat_templ foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); // TODO @aldehir : need to extend json-schema-to-grammar to produce more than JSON rules // const auto & params = function.at("parameters"); @@ -1706,7 +1705,7 @@ static common_chat_params common_chat_params_init_functionary_v3_2(const common_ auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); const auto & schema = function.at("parameters"); // Tool format: >>>function_name\n{json_args} @@ -1843,7 +1842,7 @@ static common_chat_params common_chat_params_init_kimi_k2(const common_chat_temp auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); const auto & schema = function.at("parameters"); // Match: functions.: @@ -2037,7 +2036,7 @@ static common_chat_params common_chat_params_init_gigachat_v3( auto tool_choice = p.choice(); for (const auto & tool : inputs.tools) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); const auto & schema = function.at("parameters"); auto tool_name = p.json_member("name", "\"" + p.tool_name(p.literal(name)) + "\""); @@ -2233,13 +2232,13 @@ static common_chat_params common_chat_params_init_deepseek_v3_2(const common_cha if (has_tool_calls) { foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); const auto & props = params.contains("properties") ? params.at("properties") : json::object(); std::set required; if (params.contains("required")) { - params.at("required").get_to(required); + required = params.at("required").get>(); } auto schema_info = common_schema_info(); @@ -2468,7 +2467,7 @@ static common_chat_params common_chat_params_init_kimi_k3(const common_chat_temp auto tool_choices = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); const json schema = function.contains("parameters") ? function.at("parameters") : json::object(); // arguments come one tag per key, with the JSON type in a type="..." @@ -2789,7 +2788,7 @@ static common_chat_params common_chat_params_init_minimax_m3(const common_chat_t auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - std::string name = function.at("name"); + std::string name = function.at("name").get(); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); auto schema_info = common_schema_info(); @@ -2861,7 +2860,7 @@ static common_chat_params common_chat_params_init_minimax_m3(const common_chat_t std::set required; if (schema.contains("required")) { - schema.at("required").get_to(required); + required = schema.at("required").get>(); } std::vector required_elements; @@ -2973,10 +2972,10 @@ static void system_message_not_supported(json & messages) { auto & second_msg = messages[1]; second_msg["content"] = first_msg.at("content").get() + "\n" + second_msg.at("content").get(); - messages.erase(messages.begin()); + messages.erase(0); } else { LOG_WRN("Removing system prompt due to template not supporting system role\n"); - messages.erase(messages.begin()); + messages.erase(0); } } } @@ -3243,7 +3242,7 @@ static common_chat_params common_chat_params_init_minicpm5(const common_chat_tem auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - const std::string name = function.at("name"); + const std::string name = function.at("name").get(); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); auto args = p.eps(); @@ -3389,7 +3388,7 @@ static common_chat_params common_chat_params_init_muse_glimmer(const common_chat auto tool_choice = p.choice(); foreach_function(inputs.tools, [&](const json & tool) { const auto & function = tool.at("function"); - const std::string name = function.at("name"); + const std::string name = function.at("name").get(); auto params = function.contains("parameters") ? function.at("parameters") : json::object(); auto args = p.eps(); diff --git a/common/chat.h b/common/chat.h index 7e1dc13dc9..cb39e3458f 100644 --- a/common/chat.h +++ b/common/chat.h @@ -8,7 +8,7 @@ #include "jinja/runtime.h" #include "jinja/caps.h" -#include "nlohmann/json_fwd.hpp" +#include "json.h" #include #include @@ -86,7 +86,7 @@ struct common_chat_msg { std::string tool_name; std::string tool_call_id; - nlohmann::ordered_json to_json_oaicompat(bool concat_typed_text = false) const; + common_json to_json_oaicompat(bool concat_typed_text = false) const; std::string render_content(const std::string & delimiter = "\n\n") const; @@ -210,7 +210,7 @@ struct common_chat_msg_delimiters { // split tokens into message spans. skips maps a start index to a length of a region to jump over without matching common_chat_msg_spans split(const llama_tokens & tokens, const std::map & skips = {}) const; - nlohmann::ordered_json to_json() const; + common_json to_json() const; }; struct common_chat_tool { @@ -349,16 +349,16 @@ common_chat_tool_choice common_chat_tool_choice_parse_oaicompat(const std::strin bool common_chat_templates_support_enable_thinking(const common_chat_templates * chat_templates); // Parses a JSON array of messages in OpenAI's chat completion API format. -std::vector common_chat_msgs_parse_oaicompat(const nlohmann::ordered_json & messages); +std::vector common_chat_msgs_parse_oaicompat(const common_json & messages); -std::vector common_chat_tools_parse_oaicompat(const nlohmann::ordered_json & tools); +std::vector common_chat_tools_parse_oaicompat(const common_json & tools); -common_chat_continuation common_chat_continuation_parse(const nlohmann::ordered_json & value); +common_chat_continuation common_chat_continuation_parse(const common_json & value); // DEPRECATED: only used in tests -nlohmann::ordered_json common_chat_msgs_to_json_oaicompat(const std::vector & msgs, bool concat_typed_text = false); +common_json common_chat_msgs_to_json_oaicompat(const std::vector & msgs, bool concat_typed_text = false); -nlohmann::ordered_json common_chat_tools_to_json_oaicompat(const std::vector & tools); +common_json common_chat_tools_to_json_oaicompat(const std::vector & tools); // get template caps, useful for reporting to server /props endpoint std::map common_chat_templates_get_caps(const common_chat_templates * chat_templates); @@ -385,4 +385,4 @@ struct common_chat_prompt_preset { common_chat_prompt_preset common_chat_get_asr_prompt(const common_chat_templates * chat_templates); -common_chat_msg_delimiters common_chat_msg_delimiters_parse(const nlohmann::ordered_json & delimiters); +common_chat_msg_delimiters common_chat_msg_delimiters_parse(const common_json & delimiters); diff --git a/common/json-schema-to-grammar.cpp b/common/json-schema-to-grammar.cpp index e7b12aed8a..0912cdd5d8 100644 --- a/common/json-schema-to-grammar.cpp +++ b/common/json-schema-to-grammar.cpp @@ -1,4 +1,6 @@ #include "json-schema-to-grammar.h" +// the grammar builder walks the schema with the library API +#include "json-shim.h" #include "common.h" #include @@ -12,7 +14,7 @@ #include #include -using json = nlohmann::ordered_json; +using json = common_json; static std::string build_repetition(const std::string & item_rule, int min_items, int max_items, const std::string & separator_rule = "") { auto has_max = max_items != std::numeric_limits::max(); @@ -843,7 +845,7 @@ public: } } else if (n.is_object()) { if (n.contains("$ref")) { - std::string ref = n["$ref"]; + std::string ref = n["$ref"].get(); if (_refs.find(ref) == _refs.end()) { json target; if (ref.find("https://") == 0) { @@ -914,7 +916,7 @@ public: std::string rule_name = is_reserved_name(name) ? name + "-" : name.empty() ? "root" : name; if (schema.contains("$ref")) { - return _add_rule(rule_name, _resolve_ref(schema["$ref"])); + return _add_rule(rule_name, _resolve_ref(schema["$ref"].get())); } if (schema.contains("oneOf") || schema.contains("anyOf")) { std::vector alt_schemas = schema.contains("oneOf") ? schema["oneOf"].get>() : schema["anyOf"].get>(); @@ -968,7 +970,7 @@ public: const std::string& hybrid_name = name; std::function add_component = [&](const json & comp_schema, bool is_required) { if (comp_schema.contains("$ref")) { - add_component(_refs[comp_schema["$ref"]], is_required); + add_component(_refs[comp_schema["$ref"].get()], is_required); } else if (comp_schema.contains("properties")) { for (const auto & prop : comp_schema["properties"].items()) { properties.emplace_back(prop.key(), prop.value()); @@ -1031,7 +1033,7 @@ public: return _add_rule(rule_name, "\"[\" space " + build_repetition(item_rule_name, min_items, max_items, "\",\" space") + " space \"]\""); } if ((schema_type.is_null() || schema_type == "string") && schema.contains("pattern")) { - return _visit_pattern(schema["pattern"], rule_name); + return _visit_pattern(schema["pattern"].get(), rule_name); } if ((schema_type.is_null() || schema_type == "string") && std::regex_match(schema_format, std::regex("^uuid[1-5]?$"))) { return _add_primitive(rule_name == "root" ? "root" : schema_format, PRIMITIVE_RULES.at("uuid")); @@ -1111,7 +1113,7 @@ common_schema_info::~common_schema_info() = default; common_schema_info::common_schema_info(common_schema_info &&) noexcept = default; common_schema_info & common_schema_info::operator=(common_schema_info &&) noexcept = default; -void common_schema_info::resolve_refs(nlohmann::ordered_json & schema) { +void common_schema_info::resolve_refs(common_json & schema) { impl_->resolve_refs(schema, ""); } @@ -1119,7 +1121,7 @@ void common_schema_info::resolve_refs(nlohmann::ordered_json & schema) { // Some models emit raw string values rather than JSON-encoded strings for string parameters. // If any branch of the schema (via oneOf, anyOf, $ref, etc.) permits a string, this returns // true, allowing callers to handle the value as a raw string for simplicity. -bool common_schema_info::resolves_to_string(const nlohmann::ordered_json & schema) { +bool common_schema_info::resolves_to_string(const common_json & schema) { std::unordered_set visited_refs; std::function check = [&](const json & s) -> bool { @@ -1129,7 +1131,7 @@ bool common_schema_info::resolves_to_string(const nlohmann::ordered_json & schem // Handle $ref if (s.contains("$ref")) { - const std::string & ref = s["$ref"]; + const std::string ref = s["$ref"].get(); if (visited_refs.find(ref) != visited_refs.end()) { // Circular reference, assume not a string to be safe return false; @@ -1212,7 +1214,7 @@ bool common_schema_info::resolves_to_string(const nlohmann::ordered_json & schem // Check format - many formats imply string if (s.contains("format")) { - const std::string & fmt = s["format"]; + const std::string fmt = s["format"].get(); if (fmt == "date" || fmt == "time" || fmt == "date-time" || fmt == "uri" || fmt == "email" || fmt == "hostname" || fmt == "ipv4" || fmt == "ipv6" || fmt == "uuid" || @@ -1236,7 +1238,7 @@ std::string json_schema_to_grammar(const common_json & schema, bool force_gbnf) (void)force_gbnf; #endif // LLAMA_USE_LLGUIDANCE return build_grammar([&](const common_grammar_builder & callbacks) { - auto copy = common_json_raw(schema); + auto copy = common_json_raw(schema); callbacks.resolve_refs(copy); callbacks.add_schema("", copy); }); @@ -1248,10 +1250,10 @@ std::string build_grammar(const std::function #include #include @@ -26,14 +25,14 @@ class common_schema_info { common_schema_info(common_schema_info &&) noexcept; common_schema_info & operator=(common_schema_info &&) noexcept; - void resolve_refs(nlohmann::ordered_json & schema); - bool resolves_to_string(const nlohmann::ordered_json & schema); + void resolve_refs(common_json & schema); + bool resolves_to_string(const common_json & schema); }; struct common_grammar_builder { std::function add_rule; - std::function add_schema; - std::function resolve_refs; + std::function add_schema; + std::function resolve_refs; }; struct common_grammar_options { diff --git a/common/json-shim.h b/common/json-shim.h new file mode 100644 index 0000000000..9c8cda0fe7 --- /dev/null +++ b/common/json-shim.h @@ -0,0 +1,16 @@ +#pragma once + +// converts between common_json and the backing JSON library +// +// include this only in a cpp file that touches an internal component of the library, +// never in a header. every use here is a place to fix if the library changes. + +#include "json.h" + +template T & common_json_raw(common_json & json); +template const T & common_json_raw(const common_json & json); + +template common_json common_json_from_raw(const T & json); + +// view over a value of the backing library, it does not copy +template common_json & common_json_ref_from_raw(T & json); diff --git a/common/json.cpp b/common/json.cpp index 702d552f95..dd0db5face 100644 --- a/common/json.cpp +++ b/common/json.cpp @@ -1,4 +1,6 @@ #include "json.h" +// defines the shim +#include "json-shim.h" #include "ggml.h" @@ -80,7 +82,8 @@ common_json_value::common_json_value(const char * val) { common_json_value::common_json_value(const common_json & val) : type(VAL_JSON), val_json(std::make_shared(val)) {} -common_json_value::common_json_value(const std::vector & vals) : type(VAL_JSON) { +template +common_json_value::common_json_value(const std::vector & vals) : type(VAL_JSON) { common_json out = common_json::array(); for (const auto & val : vals) { @@ -90,6 +93,23 @@ common_json_value::common_json_value(const std::vector & vals) : ty val_json = std::make_shared(std::move(out)); } +// a vector value is usable only for the types below +// note: std::vector is not here, its proxy reference does not convert +#define COMMON_JSON_VEC(...) template common_json_value::common_json_value(const std::vector<__VA_ARGS__> &); + +COMMON_JSON_VEC(int) +COMMON_JSON_VEC(unsigned int) +COMMON_JSON_VEC(long) +COMMON_JSON_VEC(unsigned long) +COMMON_JSON_VEC(long long) +COMMON_JSON_VEC(unsigned long long) +COMMON_JSON_VEC(float) +COMMON_JSON_VEC(double) +COMMON_JSON_VEC(std::string) +COMMON_JSON_VEC(common_json) + +#undef COMMON_JSON_VEC + common_json_value::common_json_value(std::initializer_list items) : type(VAL_JSON), val_json(std::make_shared(items)) {} @@ -143,6 +163,14 @@ common_json common_json::parse(const std::string & text) { } } +common_json common_json::parse_no_throw(const std::string & text) { + return common_json_from_raw(ordered_json::parse(text, nullptr, false)); +} + +bool common_json::is_discarded() const { + return as_json(this).is_discarded(); +} + common_json common_json::array() { return common_json_from_raw(ordered_json::array()); } @@ -161,6 +189,10 @@ common_json common_json::object() { return common_json(); } +common_json common_json::object(std::initializer_list items) { + return common_json(items); +} + common_json common_json::make(const common_json_value & val) { return common_json(val); } @@ -204,6 +236,10 @@ const common_json & common_json::front() const { return as_common(as_json(this). common_json & common_json::back() { return as_common(as_json(this).back()); } const common_json & common_json::back() const { return as_common(as_json(this).back()); } +void common_json::clear() { + as_json(this).clear(); +} + void common_json::erase(const std::string & key) { as_json(this).erase(key); } @@ -224,6 +260,22 @@ void common_json::push_back(const common_json_value & val) { as_json(this).push_back(to_json(val)); } +void common_json::push_back(std::initializer_list items) { + common_json val(items); + + as_json(this).push_back(as_json(&val)); +} + +size_t common_json::count(const std::string & key) const { + return as_json(this).count(key); +} + +void common_json::insert(const common_json & vals) { + ordered_json & self = as_json(this); + + self.insert(self.end(), as_json(&vals).begin(), as_json(&vals).end()); +} + std::string common_json::dump(int indent) const { return as_json(this).dump(indent); } @@ -283,5 +335,6 @@ COMMON_JSON_GET(double) COMMON_JSON_GET(std::string) COMMON_JSON_GET(std::vector) COMMON_JSON_GET(std::set) +COMMON_JSON_GET(std::vector) #undef COMMON_JSON_GET diff --git a/common/json.h b/common/json.h index b435e4f362..1b2f8adbbc 100644 --- a/common/json.h +++ b/common/json.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,8 @@ struct common_json_value { common_json_value(std::string val) : type(VAL_STRING), val_string(std::move(val)) {} common_json_value(const char * val); common_json_value(const common_json & val); - common_json_value(const std::vector & vals); + // only for the types instantiated in json.cpp, the rest fails at link time + template common_json_value(const std::vector & vals); // nested object, e.g. {"fn", {{"name", "x"}}} common_json_value(std::initializer_list items); @@ -95,6 +97,11 @@ class common_json { // direct, a value would need two conversions in a row common_json(std::nullptr_t); + // one step, so that "abc" or a vector can go straight into a common_json + template ::type, common_json>::value && + !std::is_same::type, common_json_value>::value, int>::type = 0> + common_json(T && val) : common_json(common_json_value(std::forward(val))) {} + common_json & operator=(const common_json & other); common_json & operator=(common_json && other) noexcept; @@ -103,9 +110,15 @@ class common_json { // throws common_json_error if the text is not valid JSON static common_json parse(const std::string & text); + // gives a discarded value instead of throwing, check it with is_discarded() + static common_json parse_no_throw(const std::string & text); + + bool is_discarded() const; + static common_json array(); static common_json array(std::initializer_list vals); static common_json object(); + static common_json object(std::initializer_list items); // holds a single value, e.g. make("abc").dump() gives "\"abc\"" static common_json make(const common_json_value & val); @@ -143,6 +156,8 @@ class common_json { common_json & back(); const common_json & back() const; + void clear(); + void erase(const std::string & key); void erase(size_t idx); @@ -162,6 +177,15 @@ class common_json { void set(const common_json_item & item); void push_back(const common_json_value & val); + // appends one object, e.g. push_back({{"a", 1}}) + void push_back(std::initializer_list items); + + // 1 if the key is there, 0 if not + size_t count(const std::string & key) const; + + // appends every value of another array + void insert(const common_json & vals); + // a common_json goes through the copy assignment above, everything else becomes a value template ::type, common_json>::value, int>::type = 0> common_json & operator=(T && val) { @@ -177,6 +201,12 @@ class common_json { // walks an array by index, or an object in insertion order class iterator { public: + using iterator_category = std::forward_iterator_tag; + using value_type = common_json; + using difference_type = std::ptrdiff_t; + using pointer = common_json *; + using reference = common_json &; + iterator(common_json * node, size_t idx) : node(node), idx(idx) {} common_json & operator*() const; @@ -247,15 +277,3 @@ class common_json { }; using common_json_entry = common_json::items_view::entry; - -// bridge for code that still uses internal component from nlohmann::json -// usage: common_json_raw(j) -// TODO: maybe completely remove this in the future - -template T & common_json_raw(common_json & json); -template const T & common_json_raw(const common_json & json); - -template common_json common_json_from_raw(const T & json); - -// view over a value of the backing library, it does not copy -template common_json & common_json_ref_from_raw(T & json); diff --git a/common/peg-parser.cpp b/common/peg-parser.cpp index 4a4be7cf78..96cc267b9d 100644 --- a/common/peg-parser.cpp +++ b/common/peg-parser.cpp @@ -1,4 +1,6 @@ #include "peg-parser.h" +// the interface takes common_json, the parser internals stay on the library +#include "json-shim.h" #include "common.h" #include "json-schema-to-grammar.h" @@ -1120,8 +1122,8 @@ common_peg_parser common_peg_parser_builder::chars(const std::string & classes, return wrap(arena_.add_parser(common_peg_chars_parser{classes, ranges, negated, min, max})); } -common_peg_parser common_peg_parser_builder::schema(const common_peg_parser & p, const std::string & name, const nlohmann::ordered_json & schema, bool raw) { - return wrap(arena_.add_parser(common_peg_schema_parser{p.id(), name, std::make_shared(schema), raw})); +common_peg_parser common_peg_parser_builder::schema(const common_peg_parser & p, const std::string & name, const common_json & schema, bool raw) { + return wrap(arena_.add_parser(common_peg_schema_parser{p.id(), name, std::make_shared(schema), raw})); } common_peg_parser common_peg_parser_builder::rule(const std::string & name, const common_peg_parser & p, bool trigger) { @@ -1805,8 +1807,8 @@ void common_peg_arena::build_grammar(const common_grammar_builder & builder, boo } } -static nlohmann::json serialize_parser_variant(const common_peg_parser_variant & variant) { - using json = nlohmann::json; +static nlohmann::ordered_json serialize_parser_variant(const common_peg_parser_variant & variant) { + using json = nlohmann::ordered_json; return std::visit([](const auto & p) -> json { using T = std::decay_t; @@ -1860,7 +1862,7 @@ static nlohmann::json serialize_parser_variant(const common_peg_parser_variant & {"type", "schema"}, {"child", p.child}, {"name", p.name}, - {"schema", p.schema ? *p.schema : nullptr}, + {"schema", p.schema ? common_json_raw(*p.schema) : nlohmann::ordered_json(nullptr)}, {"raw", p.raw} }; } else if constexpr (std::is_same_v) { @@ -1888,19 +1890,19 @@ static nlohmann::json serialize_parser_variant(const common_peg_parser_variant & }, variant); } -nlohmann::json common_peg_arena::to_json() const { - auto parsers = nlohmann::json::array(); +common_json common_peg_arena::to_json() const { + auto parsers = nlohmann::ordered_json::array(); for (const auto & parser : parsers_) { parsers.push_back(serialize_parser_variant(parser)); } - return nlohmann::json{ + return common_json_from_raw(nlohmann::ordered_json{ {"parsers", parsers}, {"rules", rules_}, {"root", root_} - }; + }); } -static common_peg_parser_variant deserialize_parser_variant(const nlohmann::json & j) { +static common_peg_parser_variant deserialize_parser_variant(const nlohmann::ordered_json & j) { if (!j.contains("type") || !j["type"].is_string()) { throw std::runtime_error("Parser variant JSON missing or invalid 'type' field"); } @@ -2007,7 +2009,7 @@ static common_peg_parser_variant deserialize_parser_variant(const nlohmann::json parser.child = j["child"].get(); parser.name = j["name"]; if (!j["schema"].is_null()) { - parser.schema = std::make_shared(j["schema"]); + parser.schema = std::make_shared(common_json_from_raw(j["schema"])); } parser.raw = j["raw"].get(); return parser; @@ -2069,7 +2071,8 @@ static common_peg_parser_variant deserialize_parser_variant(const nlohmann::json throw std::runtime_error("Unknown parser type: " + type); } -common_peg_arena common_peg_arena::from_json(const nlohmann::json & j) { +common_peg_arena common_peg_arena::from_json(const common_json & j_in) { + const nlohmann::ordered_json & j = common_json_raw(j_in); if (!j.contains("parsers") || !j["parsers"].is_array()) { throw std::runtime_error("JSON missing or invalid 'parsers' array"); } @@ -2109,7 +2112,7 @@ std::string common_peg_arena::save() const { } void common_peg_arena::load(const std::string & data) { - *this = from_json(nlohmann::json::parse(data)); + *this = from_json(common_json::parse(data)); } common_peg_arena build_peg_parser(const std::function & fn) { diff --git a/common/peg-parser.h b/common/peg-parser.h index c198499dd9..ab095cc7d6 100644 --- a/common/peg-parser.h +++ b/common/peg-parser.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "json.h" #include #include @@ -245,7 +245,7 @@ struct common_peg_until_parser { struct common_peg_schema_parser { common_peg_parser_id child; std::string name; - std::shared_ptr schema; + std::shared_ptr schema; // Indicates if the GBNF should accept a raw string that matches the schema. bool raw; @@ -332,8 +332,8 @@ class common_peg_arena { std::string dump(common_peg_parser_id id) const; - nlohmann::json to_json() const; - static common_peg_arena from_json(const nlohmann::json & j); + common_json to_json() const; + static common_peg_arena from_json(const common_json & j); std::string save() const; void load(const std::string & data); @@ -490,7 +490,7 @@ class common_peg_parser_builder { // Wraps a parser with JSON schema metadata for grammar generation. // Used internally to convert JSON schemas to GBNF grammar rules. - common_peg_parser schema(const common_peg_parser & p, const std::string & name, const nlohmann::ordered_json & schema, bool raw = false); + common_peg_parser schema(const common_peg_parser & p, const std::string & name, const common_json & schema, bool raw = false); // Creates a named rule, stores it in the grammar, and returns a ref. // If trigger=true, marks this rule as an entry point for lazy grammar generation. diff --git a/tools/cli/cli-context.cpp b/tools/cli/cli-context.cpp index 3d801b73d4..f6a9c74ac6 100644 --- a/tools/cli/cli-context.cpp +++ b/tools/cli/cli-context.cpp @@ -7,7 +7,7 @@ #include "console.h" #define JSON_ASSERT GGML_ASSERT -#include +#include "json.h" #include #include @@ -16,7 +16,7 @@ #include #include -using json = nlohmann::ordered_json; +using json = common_json; struct cli_context_impl { json messages = json::array(); @@ -73,7 +73,7 @@ static std::string format_error_message(const json & err) { // err is the raw response body of a failed request; it may or may not be JSON static std::string format_error_message(const std::string & err) { - json parsed = json::parse(err, nullptr, false); + json parsed = json::parse_no_throw(err); if (!parsed.is_discarded()) { return format_error_message(parsed); } @@ -157,7 +157,7 @@ bool cli_context::init() { if (!list_and_ask_models()) { return false; } - } catch (const json::parse_error & e) { + } catch (const common_json_error & e) { ui::show_error(e.what()); ui::show_message("This might be caused by an incorrect server-base endpoint URL"); return false; @@ -364,7 +364,7 @@ bool cli_context::generate_completion(generated_content & content_out, cli_timin ui::assistant_turn a; std::string err = client.post_sse("/v1/chat/completions", body.dump(), should_stop, [&](const std::string & payload) { - json chunk = json::parse(payload, nullptr, false); + json chunk = json::parse_no_throw(payload); if (chunk.is_discarded()) { return; } diff --git a/tools/parser/debug-template-parser.cpp b/tools/parser/debug-template-parser.cpp index 8a916f79c7..abe4270223 100644 --- a/tools/parser/debug-template-parser.cpp +++ b/tools/parser/debug-template-parser.cpp @@ -5,7 +5,7 @@ #include "gguf.h" #include "jinja/runtime.h" #include "log.h" -#include "nlohmann/json.hpp" +#include "json.h" #include "peg-parser.h" #include @@ -15,7 +15,7 @@ #include #include -using json = nlohmann::ordered_json; +using json = common_json; enum class output_mode { ANALYSIS, // Only output analysis results (default) diff --git a/tools/parser/template-analysis.cpp b/tools/parser/template-analysis.cpp index bf898a2290..11225bd8c0 100644 --- a/tools/parser/template-analysis.cpp +++ b/tools/parser/template-analysis.cpp @@ -11,9 +11,9 @@ #include #include -#include "nlohmann/json.hpp" +#include "json.h" -using json = nlohmann::ordered_json; +using json = common_json; // ANSI color codes - using 256-color palette for brighter colors (all bold) #define ANSI_RESET "\033[0m" diff --git a/tools/server/server-chat.cpp b/tools/server/server-chat.cpp index 0322e54cce..a6fe3c6ba6 100644 --- a/tools/server/server-chat.cpp +++ b/tools/server/server-chat.cpp @@ -153,7 +153,7 @@ json server_chat_convert_responses_to_chatcmpl(const json & response_body) { prev_msg["content"] = json::array(); } auto & prev_content = prev_msg["content"]; - prev_content.insert(prev_content.end(), chatcmpl_content.begin(), chatcmpl_content.end()); + prev_content.insert(chatcmpl_content); } else { item.erase("status"); item.erase("type"); diff --git a/tools/server/server-common.cpp b/tools/server/server-common.cpp index 196e342f01..8111a3ba10 100644 --- a/tools/server/server-common.cpp +++ b/tools/server/server-common.cpp @@ -9,8 +9,6 @@ #include "server-common.h" -// the chat API is not migrated yet, so this file still needs the bridge -#include #include #include @@ -1261,8 +1259,8 @@ json oaicompat_chat_params_parse( auto caps = common_chat_templates_get_caps(opt.tmpls.get()); common_chat_templates_inputs inputs; - inputs.messages = common_chat_msgs_parse_oaicompat(common_json_raw(messages)); - inputs.tools = common_chat_tools_parse_oaicompat(common_json_raw(tools)); + inputs.messages = common_chat_msgs_parse_oaicompat(messages); + inputs.tools = common_chat_tools_parse_oaicompat(tools); inputs.tool_choice = common_chat_tool_choice_parse_oaicompat(tool_choice); inputs.json_schema = json_schema.is_null() ? "" : json_schema.dump(); inputs.grammar = grammar; @@ -1270,7 +1268,7 @@ json oaicompat_chat_params_parse( inputs.parallel_tool_calls = json_value(body, "parallel_tool_calls", caps["supports_parallel_tool_calls"]); inputs.add_generation_prompt = json_value(body, "add_generation_prompt", true); inputs.continue_final_message = body.contains("continue_final_message") ? - common_chat_continuation_parse(common_json_raw(body.at("continue_final_message"))) : + common_chat_continuation_parse(body.at("continue_final_message")) : COMMON_CHAT_CONTINUATION_NONE; if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_NONE && opt.prefill_assistant && !inputs.messages.empty() && inputs.messages.back().role == "assistant") { @@ -1351,7 +1349,7 @@ json oaicompat_chat_params_parse( llama_params["chat_parser"] = chat_params.parser; } - llama_params["message_delimiters"] = common_json_from_raw(chat_params.message_delimiters.to_json()); + llama_params["message_delimiters"] = chat_params.message_delimiters.to_json(); // Reasoning budget: pass parameters through to sampling layer { diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp index 1293c86402..1df9e4a1af 100644 --- a/tools/server/server-context.cpp +++ b/tools/server/server-context.cpp @@ -35,7 +35,6 @@ #include #endif -using json = nlohmann::ordered_json; constexpr int HTTP_POLLING_SECONDS = 1; @@ -4220,7 +4219,8 @@ std::unique_ptr server_routes::handle_completions_impl( // tasks.reserve(inputs.size()); // TODO: this is inaccurate due to child tasks // message delimiters for checkpointing - auto delimiters = common_chat_msg_delimiters_parse(json_value(data, "message_delimiters", json::array())); + json delims = json_value(data, "message_delimiters", json::array()); + auto delimiters = common_chat_msg_delimiters_parse(delims); delimiters.tokenize(ctx_server.vocab); for (size_t i = 0; i < inputs.size(); i++) { @@ -4483,8 +4483,8 @@ static json get_res_model_info(const server_context_meta & meta) { static json get_res_models(const server_context_meta & meta) { // note: do NOT use ctx_server here, otherwise it's not possible to use this during sleep - return { - {"models", { + return json{ + {"models", json::array({ { {"name", meta.model_name}, {"model", meta.model_name}, @@ -4493,23 +4493,23 @@ static json get_res_models(const server_context_meta & meta) { {"digest", ""}, // dummy value, llama.cpp does not support managing model file's hash {"type", "model"}, {"description", ""}, - {"tags", {""}}, - {"capabilities", meta.has_mtmd ? json({"completion","multimodal"}) : json({"completion"})}, + {"tags", json::array({""})}, + {"capabilities", meta.has_mtmd ? json::array({"completion","multimodal"}) : json::array({"completion"})}, {"parameters", ""}, {"details", { {"parent_model", ""}, {"format", "gguf"}, {"family", ""}, - {"families", {""}}, + {"families", json::array({""})}, {"parameter_size", ""}, {"quantization_level", ""} }} } - }}, + })}, {"object", "list"}, - {"data", { + {"data", json::array({ get_res_model_info(meta), - }} + })} }; } @@ -5045,7 +5045,7 @@ void server_routes::init_routes() { std::string content; if (body.count("tokens") != 0) { - const llama_tokens tokens = body.at("tokens"); + const llama_tokens tokens = body.at("tokens").get(); content = tokens_to_str(ctx_server.vocab, tokens); } @@ -5103,7 +5103,7 @@ void server_routes::init_routes() { std::vector tasks; tasks.reserve(documents.size()); for (size_t i = 0; i < documents.size(); i++) { - auto tmp = format_prompt_rerank(ctx_server.model_tgt, ctx_server.vocab, ctx_server.mctx, query, documents[i]); + auto tmp = format_prompt_rerank(ctx_server.model_tgt, ctx_server.vocab, ctx_server.mctx, query.get(), documents[i]); server_task task = server_task(SERVER_TASK_TYPE_RERANK); task.id = rd.get_new_id(); task.tokens = std::move(tmp); @@ -5207,7 +5207,7 @@ void server_routes::init_routes() { std::unique_ptr server_routes::handle_slots_save(const server_http_req & req, int id_slot) { auto res = create_response(); const json request_data = json::parse(req.body); - std::string filename = request_data.at("filename"); + std::string filename = request_data.at("filename").get(); if (!fs_validate_filename(filename)) { res->error(format_error_response("Invalid filename", ERROR_TYPE_INVALID_REQUEST)); return res; @@ -5243,7 +5243,7 @@ std::unique_ptr server_routes::handle_slots_save(const ser std::unique_ptr server_routes::handle_slots_restore(const server_http_req & req, int id_slot) { auto res = create_response(); const json request_data = json::parse(req.body); - std::string filename = request_data.at("filename"); + std::string filename = request_data.at("filename").get(); if (!fs_validate_filename(filename)) { res->error(format_error_response("Invalid filename", ERROR_TYPE_INVALID_REQUEST)); return res; @@ -5332,7 +5332,7 @@ std::unique_ptr server_routes::handle_embeddings_impl(cons bool use_base64 = false; if (body.count("encoding_format") != 0) { - const std::string & format = body.at("encoding_format"); + const std::string format = body.at("encoding_format").get(); if (format == "base64") { use_base64 = true; } else if (format != "float") { @@ -5352,7 +5352,7 @@ std::unique_ptr server_routes::handle_embeddings_impl(cons int embd_normalize = params.embd_normalize; if (body.count("embd_normalize") != 0) { - embd_normalize = body.at("embd_normalize"); + embd_normalize = body.at("embd_normalize").get(); if (meta->pooling_type == LLAMA_POOLING_TYPE_NONE) { SRV_DBG("embd_normalize is not supported by pooling type %d, ignoring it\n", meta->pooling_type); } diff --git a/tools/server/server-models.cpp b/tools/server/server-models.cpp index d605451941..db0fac9952 100644 --- a/tools/server/server-models.cpp +++ b/tools/server/server-models.cpp @@ -2462,7 +2462,7 @@ server_http_proxy::server_http_proxy( bool has_files = !files.empty(); if (has_files) { - json form_fields = json::parse(body, nullptr, false); + json form_fields = json::parse_no_throw(body); if (!form_fields.is_discarded()) { auto boundary = generate_multipart_boundary(); effective_body = build_multipart_body(form_fields, files, boundary); diff --git a/tools/server/server-schema.cpp b/tools/server/server-schema.cpp index 33c4ecd46e..7b368ef788 100644 --- a/tools/server/server-schema.cpp +++ b/tools/server/server-schema.cpp @@ -258,7 +258,7 @@ std::vector> make_llama_cmpl_schema(const common_params & try { auto schema = json_value(data, "json_schema", json::object()); SRV_DBG("JSON schema: %s\n", schema.dump(2).c_str()); - std::string grammar_str = json_schema_to_grammar(common_json_from_raw(schema)); + std::string grammar_str = json_schema_to_grammar(schema); SRV_DBG("Converted grammar: %s\n", grammar_str.c_str()); params.sampling.grammar = {COMMON_GRAMMAR_TYPE_OUTPUT_FORMAT, std::move(grammar_str)}; } catch (const std::exception & e) { @@ -487,7 +487,7 @@ std::vector> make_llama_cmpl_schema(const common_params & const auto & stop = data.at("stop"); if (stop.is_array()) { for (const auto & word : stop) { - if (!word.empty()) ctx.params.antiprompt.push_back(word); + if (!word.empty()) ctx.params.antiprompt.push_back(word.get()); } } else if (stop.is_string()) { ctx.params.antiprompt.push_back(stop.get()); @@ -503,7 +503,7 @@ std::vector> make_llama_cmpl_schema(const common_params & ->set_handler([&](field_eval_context & ctx, const json & data) { const auto & samplers = data.at("samplers"); if (samplers.is_array()) { - ctx.params.sampling.samplers = common_sampler_types_from_names(samplers); + ctx.params.sampling.samplers = common_sampler_types_from_names(samplers.get>()); } else if (samplers.is_string()) { ctx.params.sampling.samplers = common_sampler_types_from_chars(samplers.get()); } @@ -580,8 +580,7 @@ static void handle_with_catch(const char * name, std::function func) { // treat a null value as absent so clients can send null to request the server default static bool has_value(const json & data, const char * n) { - auto it = data.find(n); - return it != data.end() && !it->is_null(); + return data.contains(n) && !data.at(n).is_null(); } template diff --git a/tools/server/server-task.cpp b/tools/server/server-task.cpp index 258cdcf8fb..2bca3dd744 100644 --- a/tools/server/server-task.cpp +++ b/tools/server/server-task.cpp @@ -1,5 +1,6 @@ #include "server-task.h" + #include "build-info.h" #include "server-chat.h" #include "chat.h" @@ -12,7 +13,6 @@ #include -using json = nlohmann::ordered_json; // // task_params @@ -304,7 +304,7 @@ json completion_token_output::probs_vector_to_json(const std::vector::lowest() : std::log(x); } @@ -407,7 +407,7 @@ json server_task_result_cmpl_final::to_json_oaicompat() { res["__verbose"] = to_json_non_oaicompat(); } if (stats.is_set()) { - res.push_back({"timings", stats.to_json()}); + res["timings"] = stats.to_json(); } return res; @@ -455,7 +455,7 @@ json server_task_result_cmpl_final::to_json_oaicompat_chat() { res["__verbose"] = to_json_non_oaicompat(); } if (stats.is_set()) { - res.push_back({"timings", stats.to_json()}); + res["timings"] = stats.to_json(); } return res; @@ -516,7 +516,7 @@ json server_task_result_cmpl_final::to_json_oaicompat_chat_stream() { } if (stats.is_set()) { - deltas.back().push_back({"timings", stats.to_json()}); + deltas.back()["timings"] = stats.to_json(); } // extra fields for debugging purposes @@ -709,7 +709,7 @@ json server_task_result_cmpl_final::to_json_oaicompat_resp_stream() { }); if (stats.is_set()) { - server_sent_events.back().at("data").push_back({"timings", stats.to_json()}); + server_sent_events.back().at("data")["timings"] = stats.to_json(); } return server_sent_events; @@ -1061,10 +1061,10 @@ json server_task_result_cmpl_partial::to_json_non_oaicompat() { }; // populate the timings object when needed (usually for the last response or with timings_per_token enabled) if (stats.is_set()) { - res.push_back({"timings", stats.to_json()}); + res["timings"] = stats.to_json(); } if (is_progress) { - res.push_back({"prompt_progress", progress.to_json()}); + res["prompt_progress"] = progress.to_json(); } if (!prob_output.probs.empty()) { res["completion_probabilities"] = completion_token_output::probs_vector_to_json({prob_output}, post_sampling_probs); @@ -1101,10 +1101,10 @@ json server_task_result_cmpl_partial::to_json_oaicompat() { res["__verbose"] = to_json_non_oaicompat(); } if (stats.is_set()) { - res.push_back({"timings", stats.to_json()}); + res["timings"] = stats.to_json(); } if (is_progress) { - res.push_back({"prompt_progress", progress.to_json()}); + res["prompt_progress"] = progress.to_json(); } return res; @@ -1155,10 +1155,10 @@ json server_task_result_cmpl_partial::to_json_oaicompat_chat() { } if (stats.is_set()) { - last_json.push_back({"timings", stats.to_json()}); + last_json["timings"] = stats.to_json(); } if (is_progress) { - last_json.push_back({"prompt_progress", progress.to_json()}); + last_json["prompt_progress"] = progress.to_json(); } } @@ -1305,10 +1305,10 @@ json server_task_result_cmpl_partial::to_json_oaicompat_resp() { if (!events.empty()) { json & data = events.back().at("data"); if (stats.is_set()) { - data.push_back({"timings", stats.to_json()}); + data["timings"] = stats.to_json(); } if (is_progress) { - data.push_back({"prompt_progress", progress.to_json()}); + data["prompt_progress"] = progress.to_json(); } } diff --git a/tools/server/server-tools.cpp b/tools/server/server-tools.cpp index b5c5c078ae..12e9dbb8cf 100644 --- a/tools/server/server-tools.cpp +++ b/tools/server/server-tools.cpp @@ -2156,7 +2156,7 @@ void server_tools::setup(const std::vector & enabled_tools, res->status = 200; res->data = safe_json_to_str(result); } - } catch (const json::exception & e) { + } catch (const common_json_error & e) { res->status = 400; res->data = safe_json_to_str(format_error_response(e.what(), ERROR_TYPE_INVALID_REQUEST)); } catch (const std::invalid_argument & e) {