diff --git a/common/chat.cpp b/common/chat.cpp index 0461c00234..ff5f2a97f8 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -973,7 +973,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_ref_from_raw(inp), inputs.mark_input); + jinja::global_from_json(ctx, common_json_from_raw(inp), inputs.mark_input); // render jinja::runtime runtime(ctx); diff --git a/common/chat.h b/common/chat.h index 6d5b220aeb..7e1dc13dc9 100644 --- a/common/chat.h +++ b/common/chat.h @@ -17,7 +17,6 @@ #include using chat_template_caps = jinja::caps; -using json = nlohmann::ordered_json; struct common_chat_templates; diff --git a/common/jinja/value.cpp b/common/jinja/value.cpp index cd92a804bc..1af09d6628 100644 --- a/common/jinja/value.cpp +++ b/common/jinja/value.cpp @@ -1355,7 +1355,7 @@ const func_builtins & value_undefined_t::get_builtins() const { ////////////////////////////////// -static value from_json(const common_json_ref & j, bool mark_input) { +static value from_json(const common_json & j, bool mark_input) { if (j.is_null()) { return mk_val(); } else if (j.is_boolean()) { @@ -1462,8 +1462,7 @@ void global_from_json(context & ctx, const T_JSON & json_obj, bool mark_input) { } } -template void global_from_json (context &, const common_json &, bool); -template void global_from_json(context &, const common_json_ref &, bool); +template void global_from_json(context &, const common_json &, bool); // recursively convert value to JSON string // TODO: avoid circular references diff --git a/common/jinja/value.h b/common/jinja/value.h index bf81cdc4a3..4926fb6801 100644 --- a/common/jinja/value.h +++ b/common/jinja/value.h @@ -86,7 +86,7 @@ struct context; // forward declaration // marking input can be useful for tracking data provenance // and preventing template injection attacks // -// Note: T_JSON can be common_json or common_json_ref +// Note: T_JSON can be common_json template void global_from_json(context & ctx, const T_JSON & json_obj, bool mark_input); diff --git a/common/json.cpp b/common/json.cpp index 9f88b71708..702d552f95 100644 --- a/common/json.cpp +++ b/common/json.cpp @@ -6,27 +6,30 @@ #include #include +#include +#include #include using nlohmann::ordered_json; -// common_json_node is never defined, it only stands for an ordered_json in this file -static ordered_json & as_json(common_json_node * node) { - return *reinterpret_cast(node); +// a common_json is the backing value, so any value of a tree can be used as a common_json +static_assert(sizeof(ordered_json) <= sizeof(common_json), "common_json storage is too small"); +static_assert(alignof(ordered_json) <= alignof(common_json), "common_json alignment is too weak"); + +static ordered_json & as_json(common_json * self) { + return *reinterpret_cast(self); } -static common_json_node * as_node(ordered_json * json) { - return reinterpret_cast(json); +static const ordered_json & as_json(const common_json * self) { + return *reinterpret_cast(self); } -void common_json_node_deleter::operator()(common_json_node * node) const { - delete reinterpret_cast(node); +static common_json & as_common(ordered_json & json) { + return *reinterpret_cast(&json); } -static common_json make_json(const ordered_json & val) { - common_json out; - as_json(out.get_node()) = val; - return out; +static const common_json & as_common(const ordered_json & json) { + return *reinterpret_cast(&json); } static ordered_json to_json(const common_json_value & val) { @@ -37,33 +40,33 @@ static ordered_json to_json(const common_json_value & val) { case common_json_value::VAL_UINT: return val.val_uint; case common_json_value::VAL_DOUBLE: return val.val_double; case common_json_value::VAL_STRING: return val.val_string; - case common_json_value::VAL_JSON: return as_json(val.val_json->get_node()); + case common_json_value::VAL_JSON: return as_json(val.val_json.get()); } return nullptr; } -template T & common_json_raw(common_json_ref & json) { - return as_json(json.get_node()); +template T & common_json_raw(common_json & json) { + return as_json(&json); } -template const T & common_json_raw(const common_json_ref & json) { - return as_json(json.get_node()); +template const T & common_json_raw(const common_json & json) { + return as_json(&json); } template common_json common_json_from_raw(const T & json) { - return make_json(json); + return common_json(as_common(json)); } -template common_json_ref common_json_ref_from_raw(T & json) { - return common_json_ref(as_node(&json)); +template common_json & common_json_ref_from_raw(T & json) { + return as_common(json); } // the bridge is usable only for the type below -template ordered_json & common_json_raw(common_json_ref &); -template const ordered_json & common_json_raw(const common_json_ref &); +template ordered_json & common_json_raw(common_json &); +template const ordered_json & common_json_raw(const common_json &); template common_json common_json_from_raw(const ordered_json &); -template common_json_ref common_json_ref_from_raw(ordered_json &); +template common_json & common_json_ref_from_raw(ordered_json &); common_json_value::common_json_value(const char * val) { if (val) { @@ -77,86 +80,29 @@ 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) { + common_json out = common_json::array(); + + for (const auto & val : vals) { + out.push_back(val); + } + + val_json = std::make_shared(std::move(out)); +} + common_json_value::common_json_value(std::initializer_list items) : type(VAL_JSON), val_json(std::make_shared(items)) {} -common_json_value::common_json_value(const common_json_ref & val) : - type(VAL_JSON), val_json(std::make_shared(make_json(as_json(val.get_node())))) {} - -bool common_json_ref::is_null() const { return as_json(node).is_null(); } -bool common_json_ref::is_object() const { return as_json(node).is_object(); } -bool common_json_ref::is_array() const { return as_json(node).is_array(); } -bool common_json_ref::is_string() const { return as_json(node).is_string(); } -bool common_json_ref::is_boolean() const { return as_json(node).is_boolean(); } -bool common_json_ref::is_number() const { return as_json(node).is_number(); } -bool common_json_ref::is_number_integer() const { return as_json(node).is_number_integer(); } -bool common_json_ref::is_number_float() const { return as_json(node).is_number_float(); } - -bool common_json_ref::empty() const { return as_json(node).empty(); } -size_t common_json_ref::size() const { return as_json(node).size(); } - -bool common_json_ref::contains(const std::string & key) const { - return as_json(node).contains(key); +common_json::common_json() { + new (storage) ordered_json(ordered_json::object()); } -bool common_json_ref::operator==(const common_json_value & val) const { - return as_json(node) == to_json(val); +common_json::common_json(const common_json & other) { + new (storage) ordered_json(as_json(&other)); } -bool common_json_ref::operator!=(const common_json_value & val) const { - return !(*this == val); -} - -common_json_ref common_json_ref::at(const std::string & key) const { - return common_json_ref(as_node(&as_json(node).at(key))); -} - -common_json_ref common_json_ref::operator[](const std::string & key) const { - return common_json_ref(as_node(&as_json(node)[key])); -} - -common_json_ref common_json_ref::operator[](size_t idx) const { - return common_json_ref(as_node(&as_json(node)[idx])); -} - -void common_json_ref::assign(const common_json_value & val) { - as_json(node) = to_json(val); -} - -void common_json_ref::set(const common_json_item & item) { - as_json(node)[item.key] = to_json(item.val); -} - -void common_json_ref::push_back(const common_json_value & val) { - as_json(node).push_back(to_json(val)); -} - -std::string common_json_ref::dump(int indent) const { - return as_json(node).dump(indent); -} - -// an array is indexed directly, an object needs a walk from the start -common_json_ref common_json_ref::iterator::operator*() const { - if (as_json(node).is_object()) { - return common_json_ref(as_node(&std::next(as_json(node).begin(), idx).value())); - } - - return common_json_ref(as_node(&as_json(node)[idx])); -} - -std::string common_json_ref::iterator::key() const { - return std::next(as_json(node).begin(), idx).key(); -} - -std::pair common_json_ref::items_view::iterator::operator*() const { - auto it = std::next(as_json(node).begin(), idx); - - return { it.key(), common_json_ref(as_node(&it.value())) }; -} - -common_json::common_json() : - common_json_ref(nullptr), pimpl(as_node(new ordered_json(ordered_json::object()))) { - node = pimpl.get(); +common_json::common_json(common_json && other) noexcept { + new (storage) ordered_json(std::move(as_json(&other))); } common_json::common_json(std::initializer_list items) : common_json() { @@ -165,43 +111,40 @@ common_json::common_json(std::initializer_list items) : common } } -common_json::common_json(const common_json & other) : - common_json_ref(nullptr), pimpl(as_node(new ordered_json(as_json(other.node)))) { - node = pimpl.get(); +common_json::common_json(const common_json_value & val) { + new (storage) ordered_json(to_json(val)); } -common_json::common_json(common_json && other) noexcept : - common_json_ref(other.node), pimpl(std::move(other.pimpl)) { - other.node = nullptr; +common_json::common_json(std::nullptr_t) { + new (storage) ordered_json(nullptr); } common_json & common_json::operator=(const common_json & other) { - as_json(node) = as_json(other.node); + as_json(this) = as_json(&other); return *this; } common_json & common_json::operator=(common_json && other) noexcept { - pimpl = std::move(other.pimpl); - node = pimpl.get(); - - other.node = nullptr; + as_json(this) = std::move(as_json(&other)); return *this; } -common_json::~common_json() = default; +common_json::~common_json() { + as_json(this).~basic_json(); +} common_json common_json::parse(const std::string & text) { try { - return make_json(ordered_json::parse(text)); + return common_json_from_raw(ordered_json::parse(text)); } catch (const std::exception & e) { throw common_json_error(e.what()); } } common_json common_json::array() { - return make_json(ordered_json::array()); + return common_json_from_raw(ordered_json::array()); } common_json common_json::array(std::initializer_list vals) { @@ -211,7 +154,7 @@ common_json common_json::array(std::initializer_list vals) { out.push_back(to_json(val)); } - return make_json(out); + return common_json_from_raw(out); } common_json common_json::object() { @@ -219,16 +162,114 @@ common_json common_json::object() { } common_json common_json::make(const common_json_value & val) { - return make_json(to_json(val)); + return common_json(val); } -template T common_json_ref::get() const { - return as_json(node).get(); +bool common_json::is_null() const { return as_json(this).is_null(); } +bool common_json::is_object() const { return as_json(this).is_object(); } +bool common_json::is_array() const { return as_json(this).is_array(); } +bool common_json::is_string() const { return as_json(this).is_string(); } +bool common_json::is_boolean() const { return as_json(this).is_boolean(); } +bool common_json::is_number() const { return as_json(this).is_number(); } +bool common_json::is_number_integer() const { return as_json(this).is_number_integer(); } +bool common_json::is_number_float() const { return as_json(this).is_number_float(); } + +bool common_json::empty() const { return as_json(this).empty(); } +size_t common_json::size() const { return as_json(this).size(); } + +bool common_json::contains(const std::string & key) const { + return as_json(this).contains(key); +} + +bool common_json::operator==(const common_json_value & val) const { + return as_json(this) == to_json(val); +} + +bool common_json::operator!=(const common_json_value & val) const { + return !(*this == val); +} + +common_json & common_json::at(const std::string & key) { return as_common(as_json(this).at(key)); } +const common_json & common_json::at(const std::string & key) const { return as_common(as_json(this).at(key)); } +common_json & common_json::at(size_t idx) { return as_common(as_json(this).at(idx)); } +const common_json & common_json::at(size_t idx) const { return as_common(as_json(this).at(idx)); } + +common_json & common_json::operator[](const std::string & key) { return as_common(as_json(this)[key]); } +const common_json & common_json::operator[](const std::string & key) const { return as_common(as_json(this).at(key)); } +common_json & common_json::operator[](size_t idx) { return as_common(as_json(this)[idx]); } +const common_json & common_json::operator[](size_t idx) const { return as_common(as_json(this).at(idx)); } + +common_json & common_json::front() { return as_common(as_json(this).front()); } +const common_json & common_json::front() const { return as_common(as_json(this).front()); } +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::erase(const std::string & key) { + as_json(this).erase(key); +} + +void common_json::erase(size_t idx) { + as_json(this).erase(idx); +} + +void common_json::assign(const common_json_value & val) { + as_json(this) = to_json(val); +} + +void common_json::set(const common_json_item & item) { + as_json(this)[item.key] = to_json(item.val); +} + +void common_json::push_back(const common_json_value & val) { + as_json(this).push_back(to_json(val)); +} + +std::string common_json::dump(int indent) const { + return as_json(this).dump(indent); +} + +std::string common_json::dump_safe(int indent) const { + return as_json(this).dump(indent, ' ', false, ordered_json::error_handler_t::replace); +} + +// an array is indexed directly, an object needs a walk from the start +common_json & common_json::iterator::operator*() const { + if (as_json(node).is_object()) { + return as_common(std::next(as_json(node).begin(), idx).value()); + } + + return as_common(as_json(node)[idx]); +} + +std::string common_json::iterator::key() const { + return std::next(as_json(node).begin(), idx).key(); +} + +common_json::iterator common_json::begin() const { + return iterator(const_cast(this), 0); +} + +common_json::iterator common_json::end() const { + return iterator(const_cast(this), size()); +} + +common_json::items_view::entry common_json::items_view::iterator::operator*() const { + auto it = std::next(as_json(node).begin(), idx); + + return { it.key(), as_common(it.value()) }; +} + +common_json::items_view common_json::items() const { + return items_view(const_cast(this), size()); +} + +template T common_json::get() const { + return as_json(this).get(); } // get() is usable only for the types below -#define COMMON_JSON_GET(...) template __VA_ARGS__ common_json_ref::get<__VA_ARGS__>() const; +#define COMMON_JSON_GET(...) template __VA_ARGS__ common_json::get<__VA_ARGS__>() const; COMMON_JSON_GET(bool) COMMON_JSON_GET(int) @@ -241,5 +282,6 @@ COMMON_JSON_GET(float) COMMON_JSON_GET(double) COMMON_JSON_GET(std::string) COMMON_JSON_GET(std::vector) +COMMON_JSON_GET(std::set) #undef COMMON_JSON_GET diff --git a/common/json.h b/common/json.h index 3b37973dd0..b435e4f362 100644 --- a/common/json.h +++ b/common/json.h @@ -2,6 +2,7 @@ // JSON object, it works without the need to include a JSON library header // the underlay library is pimpl, it should never be exposed here +// the backing value lives inside this object, so at() and the iterators give a real reference to it // note: object keys keep the order in which they are added #include @@ -12,20 +13,13 @@ #include #include #include +#include class common_json; -class common_json_ref; // common_json_value holds a list of these, and each of them holds a value, so one must come first struct common_json_item; -// one value of the backing library, only json.cpp knows what it is -struct common_json_node; - -struct common_json_node_deleter { - void operator()(common_json_node * node) const; -}; - struct common_json_error : std::runtime_error { using std::runtime_error::runtime_error; }; @@ -59,7 +53,7 @@ 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 common_json_ref & val); + common_json_value(const std::vector & vals); // nested object, e.g. {"fn", {{"name", "x"}}} common_json_value(std::initializer_list items); @@ -90,15 +84,31 @@ struct common_json_item { key(std::move(key)), val(items) {} }; -// view to a value owned by a common_json, it goes stale if the owner gets a new key -class common_json_ref { +class common_json { public: - explicit common_json_ref(common_json_node * node) : node(node) {} + common_json(); + common_json(const common_json & other); + common_json(common_json && other) noexcept; + common_json(std::initializer_list items); + common_json(const common_json_value & val); - common_json_ref(const common_json_ref &) = default; + // direct, a value would need two conversions in a row + common_json(std::nullptr_t); - // rebinding a view is almost always a write-through by mistake, use assign() to write - common_json_ref & operator=(const common_json_ref &) = delete; + common_json & operator=(const common_json & other); + common_json & operator=(common_json && other) noexcept; + + ~common_json(); + + // throws common_json_error if the text is not valid JSON + static common_json parse(const std::string & text); + + static common_json array(); + static common_json array(std::initializer_list vals); + static common_json object(); + + // holds a single value, e.g. make("abc").dump() gives "\"abc\"" + static common_json make(const common_json_value & val); bool is_null() const; bool is_object() const; @@ -118,9 +128,23 @@ class common_json_ref { bool operator!=(const common_json_value & val) const; // at() throws if the key is missing, operator[] adds a null value instead - common_json_ref at(const std::string & key) const; - common_json_ref operator[](const std::string & key) const; - common_json_ref operator[](size_t idx) const; + common_json & at(const std::string & key); + const common_json & at(const std::string & key) const; + common_json & at(size_t idx); + const common_json & at(size_t idx) const; + + common_json & operator[](const std::string & key); + const common_json & operator[](const std::string & key) const; + common_json & operator[](size_t idx); + const common_json & operator[](size_t idx) const; + + common_json & front(); + const common_json & front() const; + common_json & back(); + const common_json & back() const; + + void erase(const std::string & key); + void erase(size_t idx); // only for the types instantiated in json.cpp, the rest fails at link time template T get() const; @@ -138,21 +162,26 @@ class common_json_ref { void set(const common_json_item & item); void push_back(const common_json_value & val); - template - common_json_ref & operator=(T && val) { + // 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) { assign(common_json_value(std::forward(val))); return *this; } std::string dump(int indent = -1) const; + // same as dump(), but bad UTF-8 gets replaced instead of throwing + std::string dump_safe(int indent = -1) const; + // walks an array by index, or an object in insertion order class iterator { public: - iterator(common_json_node * node, size_t idx) : node(node), idx(idx) {} + iterator(common_json * node, size_t idx) : node(node), idx(idx) {} - common_json_ref operator*() const; - std::string key() const; + common_json & operator*() const; + common_json & value() const { return **this; } + std::string key() const; iterator & operator++() { idx++; @@ -163,23 +192,32 @@ class common_json_ref { bool operator==(const iterator & other) const { return idx == other.idx; } private: - common_json_node * node; - size_t idx; + common_json * node; + size_t idx; }; - iterator begin() const { return iterator(node, 0); } - iterator end() const { return iterator(node, size()); } + iterator begin() const; + iterator end() const; // allows: for (const auto & [key, val] : obj.items()) class items_view { public: - items_view(common_json_node * node, size_t n) : node(node), n(n) {} + // the members are public, so an entry also works with structured bindings + struct entry { + std::string k; + common_json & v; + + const std::string & key() const { return k; } + common_json & value() const { return v; } + }; + + items_view(common_json * node, size_t n) : node(node), n(n) {} class iterator { public: - iterator(common_json_node * node, size_t idx) : node(node), idx(idx) {} + iterator(common_json * node, size_t idx) : node(node), idx(idx) {} - std::pair operator*() const; + entry operator*() const; iterator & operator++() { idx++; @@ -189,62 +227,35 @@ class common_json_ref { bool operator!=(const iterator & other) const { return idx != other.idx; } private: - common_json_node * node; - size_t idx; + common_json * node; + size_t idx; }; iterator begin() const { return iterator(node, 0); } iterator end() const { return iterator(node, n); } private: - common_json_node * node; - size_t n; + common_json * node; + size_t n; }; - items_view items() const { return items_view(node, size()); } - - common_json_node * get_node() const { return node; } - - protected: - common_json_node * node; -}; - -// owns the value it points to -class common_json : public common_json_ref { - public: - common_json(); - common_json(std::initializer_list items); - common_json(const common_json & other); - common_json(common_json && other) noexcept; - - common_json & operator=(const common_json & other); - common_json & operator=(common_json && other) noexcept; - - // out-of-line, the deleter needs to know the real type - ~common_json(); - - // throws common_json_error if the text is not valid JSON - static common_json parse(const std::string & text); - - static common_json array(); - static common_json array(std::initializer_list vals); - static common_json object(); - - // holds a single value, e.g. make("abc").dump() gives "\"abc\"" - static common_json make(const common_json_value & val); + items_view items() const; private: - std::unique_ptr pimpl; + // the backing value is built here, json.cpp checks that it fits + alignas(8) unsigned char storage[32]; }; +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_ref & json); -template const T & common_json_raw(const common_json_ref & json); +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_ref common_json_ref_from_raw(T & json); +template common_json & common_json_ref_from_raw(T & json); diff --git a/tools/server/server-chat.h b/tools/server/server-chat.h index 102eae688a..2b4945980c 100644 --- a/tools/server/server-chat.h +++ b/tools/server/server-chat.h @@ -6,9 +6,8 @@ #include "server-common.h" #include "server-http.h" -#include +#include "json.h" -using json = nlohmann::ordered_json; // Convert OpenAI Responses API format to OpenAI Chat Completions API format json server_chat_convert_responses_to_chatcmpl(const json & body); diff --git a/tools/server/server-common.cpp b/tools/server/server-common.cpp index 585f65e83c..196e342f01 100644 --- a/tools/server/server-common.cpp +++ b/tools/server/server-common.cpp @@ -9,6 +9,9 @@ #include "server-common.h" +// the chat API is not migrated yet, so this file still needs the bridge +#include + #include #include #include @@ -977,9 +980,9 @@ static server_tokens tokenize_input_subprompt(const llama_vocab * vocab, mtmd_co // JSON object with prompt and multimodal key. std::vector files; for (const auto & entry : json_prompt.at(JSON_MTMD_DATA_KEY)) { - files.push_back(base64_decode(entry)); + files.push_back(base64_decode(entry.get())); } - return process_mtmd_prompt(mctx, json_prompt.at(JSON_STRING_PROMPT_KEY), files); + return process_mtmd_prompt(mctx, json_prompt.at(JSON_STRING_PROMPT_KEY).get(), files); } else { // Not multimodal, but contains a subobject. llama_tokens tmp = tokenize_mixed(vocab, json_prompt.at(JSON_STRING_PROMPT_KEY), add_special, parse_special); @@ -1258,8 +1261,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(messages); - inputs.tools = common_chat_tools_parse_oaicompat(tools); + inputs.messages = common_chat_msgs_parse_oaicompat(common_json_raw(messages)); + inputs.tools = common_chat_tools_parse_oaicompat(common_json_raw(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; @@ -1267,7 +1270,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(body.at("continue_final_message")) : + common_chat_continuation_parse(common_json_raw(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") { @@ -1300,7 +1303,8 @@ json oaicompat_chat_params_parse( } // parse the "enable_thinking" kwarg to override the default value - auto enable_thinking_kwarg = json_value(inputs.chat_template_kwargs, "enable_thinking", std::string("")); + const auto kwarg_it = inputs.chat_template_kwargs.find("enable_thinking"); + std::string enable_thinking_kwarg = kwarg_it == inputs.chat_template_kwargs.end() ? "" : kwarg_it->second; if (enable_thinking_kwarg == "true") { inputs.enable_thinking = true; } else if (enable_thinking_kwarg == "false") { @@ -1316,7 +1320,7 @@ json oaicompat_chat_params_parse( inputs.enable_thinking = false; inputs.chat_template_kwargs.erase("reasoning_effort"); } else if (!reasoning_effort.empty()) { - inputs.chat_template_kwargs["reasoning_effort"] = json(reasoning_effort).dump(); + inputs.chat_template_kwargs["reasoning_effort"] = json::make(reasoning_effort).dump(); } } @@ -1347,7 +1351,7 @@ json oaicompat_chat_params_parse( llama_params["chat_parser"] = chat_params.parser; } - llama_params["message_delimiters"] = chat_params.message_delimiters.to_json(); + llama_params["message_delimiters"] = common_json_from_raw(chat_params.message_delimiters.to_json()); // Reasoning budget: pass parameters through to sampling layer { @@ -1465,7 +1469,10 @@ json format_response_rerank( }); elements.resize(std::min(top_n, (int)elements.size())); - json results = elements; + json results = json::array(); + for (const auto & el : elements) { + results.push_back(el); + } if (is_tei_format) return results; @@ -1540,7 +1547,7 @@ std::vector get_token_probabilities(llama_context * ctx, int i } std::string safe_json_to_str(const json & data) { - return data.dump(-1, ' ', false, json::error_handler_t::replace); + return data.dump_safe(); } // TODO: reuse llama_detokenize @@ -1790,12 +1797,12 @@ server_tokens format_prompt_rerank( std::string prompt = rerank_prompt; string_replace_all(prompt, "{query}" , query); string_replace_all(prompt, "{document}", doc ); - server_tokens tokens = tokenize_input_subprompt(vocab, mctx, prompt, false, true); + server_tokens tokens = tokenize_input_subprompt(vocab, mctx, json::make(prompt), false, true); result.push_back(tokens); } else { // Get EOS token - use SEP token as fallback if EOS is not available - server_tokens query_tokens = tokenize_input_subprompt(vocab, mctx, query, false, false); - server_tokens doc_tokens = tokenize_input_subprompt(vocab, mctx, doc, false, false); + server_tokens query_tokens = tokenize_input_subprompt(vocab, mctx, json::make(query), false, false); + server_tokens doc_tokens = tokenize_input_subprompt(vocab, mctx, json::make(doc), false, false); llama_token eos_token = llama_vocab_eos(vocab); if (eos_token == LLAMA_TOKEN_NULL) { eos_token = llama_vocab_sep(vocab); diff --git a/tools/server/server-common.h b/tools/server/server-common.h index 6488be344c..48dbdfcb63 100644 --- a/tools/server/server-common.h +++ b/tools/server/server-common.h @@ -7,7 +7,7 @@ #include "mtmd.h" #define JSON_ASSERT GGML_ASSERT -#include +#include "json.h" #include #include @@ -19,7 +19,7 @@ #include #include -using json = nlohmann::ordered_json; +using json = common_json; #define SLT_DBG(slot, fmt, ...) LOG_DBG("slot %12.*s: id %2d | task %d | " fmt, 12, __func__, (slot).id, ((slot).task ? (slot).task->id : -1), __VA_ARGS__) #define SLT_TRC(slot, fmt, ...) LOG_TRC("slot %12.*s: id %2d | task %d | " fmt, 12, __func__, (slot).id, ((slot).task ? (slot).task->id : -1), __VA_ARGS__) @@ -42,9 +42,9 @@ static T json_value(const json & body, const std::string & key, const T & defaul // Fallback null to default value if (body.contains(key) && !body.at(key).is_null()) { try { - return body.at(key); - } catch (NLOHMANN_JSON_NAMESPACE::detail::type_error const & err) { - LOG_WRN("Wrong type supplied for parameter '%s'. Expected '%s', using default value: %s\n", key.c_str(), json(default_value).type_name(), err.what()); + return body.at(key).get(); + } catch (const std::exception & err) { + LOG_WRN("Wrong type supplied for parameter '%s', using default value: %s\n", key.c_str(), err.what()); return default_value; } } else { diff --git a/tools/server/server-context.h b/tools/server/server-context.h index 764df0e085..5d464b8e8c 100644 --- a/tools/server/server-context.h +++ b/tools/server/server-context.h @@ -4,7 +4,7 @@ #include "server-task.h" #include "server-queue.h" -#include +#include "json.h" #include #include diff --git a/tools/server/server-task.h b/tools/server/server-task.h index 25ff015122..9c99143f8e 100644 --- a/tools/server/server-task.h +++ b/tools/server/server-task.h @@ -11,7 +11,6 @@ // TODO: prevent including the whole server-common.h as we only use server_tokens #include "server-common.h" -using json = nlohmann::ordered_json; enum server_task_type { SERVER_TASK_TYPE_COMPLETION,