mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-18 08:44:49 +02:00
adapt jinja
This commit is contained in:
+2
-1
@@ -6,6 +6,7 @@
|
||||
#include "common.h"
|
||||
#include "ggml.h"
|
||||
#include "json-schema-to-grammar.h"
|
||||
#include "json.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "jinja/value.h"
|
||||
@@ -972,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, inp, inputs.mark_input);
|
||||
jinja::global_from_json(ctx, common_json_ref_from_raw(inp), inputs.mark_input);
|
||||
|
||||
// render
|
||||
jinja::runtime runtime(ctx);
|
||||
|
||||
@@ -7,7 +7,7 @@ The implementation can be found in the `common/jinja` directory.
|
||||
## Key Features
|
||||
|
||||
- Input marking: security against special token injection
|
||||
- Decoupled from `nlohmann::json`: this dependency is only used for JSON-to-internal type translation and is completely optional
|
||||
- Decoupled from the JSON library: `common_json` is only used for JSON-to-internal type translation and is completely optional
|
||||
- Minimal primitive types: int, float, bool, string, array, object, none, undefined
|
||||
- Detailed logging: allow source tracing on error
|
||||
- Clean architecture: workarounds are applied to input data before entering the runtime (see `common/chat.cpp`)
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
// note: the json dependency is only for defining input in a convenient way
|
||||
// we can remove it in the future when we figure out a better way to define inputs using jinja::value
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "json.h"
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
#define FILENAME "jinja-caps"
|
||||
|
||||
using json = nlohmann::ordered_json;
|
||||
using json = common_json;
|
||||
|
||||
namespace jinja {
|
||||
|
||||
@@ -370,7 +370,7 @@ caps caps_get(jinja::program & prog) {
|
||||
caps_try_execute(
|
||||
prog,
|
||||
[&]() {
|
||||
json args = json(R"({"arg": "value"})");
|
||||
json args = json::make(R"({"arg": "value"})");
|
||||
if (result.supports_object_arguments) {
|
||||
args = json{{"arg", "value"}};
|
||||
}
|
||||
|
||||
+12
-10
@@ -3,7 +3,7 @@
|
||||
#include "value.h"
|
||||
|
||||
// for converting from JSON to jinja values
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "json.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@@ -1355,7 +1355,7 @@ const func_builtins & value_undefined_t::get_builtins() const {
|
||||
//////////////////////////////////
|
||||
|
||||
|
||||
static value from_json(const nlohmann::ordered_json & j, bool mark_input) {
|
||||
static value from_json(const common_json_ref & j, bool mark_input) {
|
||||
if (j.is_null()) {
|
||||
return mk_val<value_none>();
|
||||
} else if (j.is_boolean()) {
|
||||
@@ -1378,8 +1378,8 @@ static value from_json(const nlohmann::ordered_json & j, bool mark_input) {
|
||||
return arr;
|
||||
} else if (j.is_object()) {
|
||||
auto obj = mk_val<value_object>();
|
||||
for (auto it = j.begin(); it != j.end(); ++it) {
|
||||
obj->insert(it.key(), from_json(it.value(), mark_input));
|
||||
for (const auto & [key, val] : j.items()) {
|
||||
obj->insert(key, from_json(val, mark_input));
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
@@ -1451,18 +1451,20 @@ bool value_compare(const value & a, const value & b, value_compare_op op) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
void global_from_json(context & ctx, const nlohmann::ordered_json & json_obj, bool mark_input) {
|
||||
// printf("global_from_json: %s\n" , json_obj.dump(2).c_str());
|
||||
template<typename T_JSON>
|
||||
void global_from_json(context & ctx, const T_JSON & json_obj, bool mark_input) {
|
||||
if (json_obj.is_null() || !json_obj.is_object()) {
|
||||
throw std::runtime_error("global_from_json: input JSON value must be an object");
|
||||
}
|
||||
for (auto it = json_obj.begin(); it != json_obj.end(); ++it) {
|
||||
JJ_DEBUG("global_from_json: setting key '%s'", it.key().c_str());
|
||||
ctx.set_val(it.key(), from_json(it.value(), mark_input));
|
||||
for (const auto & [key, val] : json_obj.items()) {
|
||||
JJ_DEBUG("global_from_json: setting key '%s'", key.c_str());
|
||||
ctx.set_val(key, from_json(val, mark_input));
|
||||
}
|
||||
}
|
||||
|
||||
template void global_from_json<common_json> (context &, const common_json &, bool);
|
||||
template void global_from_json<common_json_ref>(context &, const common_json_ref &, bool);
|
||||
|
||||
// recursively convert value to JSON string
|
||||
// TODO: avoid circular references
|
||||
static void value_to_json_internal(std::ostringstream & oss, const value & val, int curr_lvl, int indent, const std::string_view item_sep, const std::string_view key_sep) {
|
||||
|
||||
@@ -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 nlohmann::ordered_json
|
||||
// Note: T_JSON can be common_json or common_json_ref
|
||||
template<typename T_JSON>
|
||||
void global_from_json(context & ctx, const T_JSON & json_obj, bool mark_input);
|
||||
|
||||
|
||||
+19
-4
@@ -15,10 +15,6 @@ static ordered_json & as_json(common_json_node * node) {
|
||||
return *reinterpret_cast<ordered_json *>(node);
|
||||
}
|
||||
|
||||
static const ordered_json & as_json(const common_json_node * node) {
|
||||
return *reinterpret_cast<const ordered_json *>(node);
|
||||
}
|
||||
|
||||
static common_json_node * as_node(ordered_json * json) {
|
||||
return reinterpret_cast<common_json_node *>(json);
|
||||
}
|
||||
@@ -59,10 +55,15 @@ template <typename T> common_json common_json_from_raw(const T & json) {
|
||||
return make_json(json);
|
||||
}
|
||||
|
||||
template <typename T> common_json_ref common_json_ref_from_raw(T & json) {
|
||||
return common_json_ref(as_node(&json));
|
||||
}
|
||||
|
||||
// the bridge is usable only for the type below
|
||||
template ordered_json & common_json_raw<ordered_json>(common_json_ref &);
|
||||
template const ordered_json & common_json_raw<ordered_json>(const common_json_ref &);
|
||||
template common_json common_json_from_raw<ordered_json>(const ordered_json &);
|
||||
template common_json_ref common_json_ref_from_raw<ordered_json>(ordered_json &);
|
||||
|
||||
common_json_value::common_json_value(const char * val) {
|
||||
if (val) {
|
||||
@@ -76,6 +77,9 @@ 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<common_json>(val)) {}
|
||||
|
||||
common_json_value::common_json_value(std::initializer_list<common_json_item> items) :
|
||||
type(VAL_JSON), val_json(std::make_shared<common_json>(items)) {}
|
||||
|
||||
common_json_value::common_json_value(const common_json_ref & val) :
|
||||
type(VAL_JSON), val_json(std::make_shared<common_json>(make_json(as_json(val.get_node())))) {}
|
||||
|
||||
@@ -86,6 +90,7 @@ 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(); }
|
||||
@@ -199,6 +204,16 @@ common_json common_json::array() {
|
||||
return make_json(ordered_json::array());
|
||||
}
|
||||
|
||||
common_json common_json::array(std::initializer_list<common_json_value> vals) {
|
||||
ordered_json out = ordered_json::array();
|
||||
|
||||
for (const auto & val : vals) {
|
||||
out.push_back(to_json(val));
|
||||
}
|
||||
|
||||
return make_json(out);
|
||||
}
|
||||
|
||||
common_json common_json::object() {
|
||||
return common_json();
|
||||
}
|
||||
|
||||
+19
-3
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// JSON object, it works without the need to include a JSON library header
|
||||
// the underlay library is pimpl, it should never be exposed here
|
||||
// note: object keys keep the order in which they are added
|
||||
|
||||
#include <cstddef>
|
||||
@@ -15,6 +16,9 @@
|
||||
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;
|
||||
|
||||
@@ -57,6 +61,9 @@ struct common_json_value {
|
||||
common_json_value(const common_json & val);
|
||||
common_json_value(const common_json_ref & val);
|
||||
|
||||
// nested object, e.g. {"fn", {{"name", "x"}}}
|
||||
common_json_value(std::initializer_list<common_json_item> items);
|
||||
|
||||
template <typename T, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, int>::type = 0>
|
||||
common_json_value(T val) : type(std::is_signed<T>::value ? VAL_INT : VAL_UINT) {
|
||||
if (std::is_signed<T>::value) {
|
||||
@@ -77,6 +84,10 @@ struct common_json_item {
|
||||
template <typename T>
|
||||
common_json_item(std::string key, T && val) :
|
||||
key(std::move(key)), val(std::forward<T>(val)) {}
|
||||
|
||||
// a braced list cannot deduce T, so it needs its own overload
|
||||
common_json_item(std::string key, std::initializer_list<common_json_item> items) :
|
||||
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
|
||||
@@ -96,6 +107,7 @@ class common_json_ref {
|
||||
bool is_boolean() const;
|
||||
bool is_number() const;
|
||||
bool is_number_integer() const;
|
||||
bool is_number_float() const;
|
||||
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
@@ -170,9 +182,9 @@ class common_json_ref {
|
||||
std::pair<std::string, common_json_ref> operator*() const;
|
||||
|
||||
iterator & operator++() {
|
||||
idx++;
|
||||
return *this;
|
||||
}
|
||||
idx++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator & other) const { return idx != other.idx; }
|
||||
|
||||
@@ -215,6 +227,7 @@ class common_json : public common_json_ref {
|
||||
static common_json parse(const std::string & text);
|
||||
|
||||
static common_json array();
|
||||
static common_json array(std::initializer_list<common_json_value> vals);
|
||||
static common_json object();
|
||||
|
||||
// holds a single value, e.g. make("abc").dump() gives "\"abc\""
|
||||
@@ -232,3 +245,6 @@ template <typename T> T & common_json_raw(common_json_ref & json);
|
||||
template <typename T> const T & common_json_raw(const common_json_ref & json);
|
||||
|
||||
template <typename T> common_json common_json_from_raw(const T & json);
|
||||
|
||||
// view over a value of the backing library, it does not copy
|
||||
template <typename T> common_json_ref common_json_ref_from_raw(T & json);
|
||||
|
||||
Reference in New Issue
Block a user