Commit 33c923db1 for llama.cpp
commit 33c923db1b3fec0d20818e32facb4914534c415c
Author: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Date: Sun Sep 27 13:41:50 2026 +0200
jinja : add support for dict builtin (#29477)
* add support for dict builtin
* add tests
diff --git a/common/jinja/value.cpp b/common/jinja/value.cpp
index ce4c66f87..10130a6b0 100644
--- a/common/jinja/value.cpp
+++ b/common/jinja/value.cpp
@@ -348,6 +348,43 @@ static value default_value(const func_args & args) {
return no_value ? args.get_pos(1) : args.get_pos(0);
}
+static value toobject(const func_args & args) {
+ auto out = mk_val<value_object>();
+ value iter = args.get_pos(0, mk_val<value_undefined>());
+ bool iter_first = false;
+ if (is_val<value_array>(iter)) {
+ iter_first = true;
+ for (const auto & it : iter->as_array()) {
+ if (is_val<value_array>(it) && it->as_array().size() == 2) {
+ auto tuple = it->as_array();
+ auto key = tuple[0];
+ auto val = tuple[1];
+ JJ_DEBUG("namespace/dict: adding key '%s'", key->as_string().str().c_str());
+ out->insert(key, val);
+ } else {
+ throw raised_exception("namespace/dict() iterable argument must consist of tuples, not " + it->type());
+ }
+ }
+ } else if (is_val<value_object>(iter)) {
+ iter_first = true;
+ for (const auto & pair : iter->as_ordered_object()) {
+ JJ_DEBUG("namespace/dict: adding key '%s'", pair.first->as_string().str().c_str());
+ out->insert(pair.first, pair.second);
+ }
+ }
+ for (const auto & arg : args.get_args()) {
+ if (is_val<value_kwarg>(arg)) {
+ auto kwarg = cast_val<value_kwarg>(arg);
+ JJ_DEBUG("namespace/dict: adding key '%s'", kwarg->key.c_str());
+ out->insert(kwarg->key, kwarg->val);
+ } else if (!iter_first) {
+ throw raised_exception("namespace/dict() arguments must be kwargs, dict and/or iterable of tuples, not " + arg->type());
+ }
+ iter_first = false;
+ }
+ return out;
+}
+
const func_builtins & global_builtins() {
static const func_builtins builtins = {
{"raise_exception", [](const func_args & args) -> value {
@@ -355,18 +392,8 @@ const func_builtins & global_builtins() {
std::string msg = args.get_pos(0)->as_string().str();
throw raised_exception("Jinja Exception: " + msg);
}},
- {"namespace", [](const func_args & args) -> value {
- auto out = mk_val<value_object>();
- for (const auto & arg : args.get_args()) {
- if (!is_val<value_kwarg>(arg)) {
- throw raised_exception("namespace() arguments must be kwargs");
- }
- auto kwarg = cast_val<value_kwarg>(arg);
- JJ_DEBUG("namespace: adding key '%s'", kwarg->key.c_str());
- out->insert(kwarg->key, kwarg->val);
- }
- return out;
- }},
+ {"dict", toobject},
+ {"namespace", toobject},
{"strftime_now", [](const func_args & args) -> value {
args.ensure_vals<value_string>();
std::string format = args.get_pos(0)->as_string().str();
diff --git a/tests/test-jinja.cpp b/tests/test-jinja.cpp
index 50a79af3f..891b785c4 100644
--- a/tests/test-jinja.cpp
+++ b/tests/test-jinja.cpp
@@ -1976,6 +1976,30 @@ static void test_object_methods(testing & t) {
json::object(),
""
);
+
+ test_template(t, "dict from dict",
+ "{% set o = dict({'a': 3, 'b': 1, 'c': 2}) %}{{ o|tojson }}",
+ json::object(),
+ "{\"a\": 3, \"b\": 1, \"c\": 2}"
+ );
+
+ test_template(t, "dict from kwargs",
+ "{% set o = dict(a=3, b=1, c=2) %}{{ o|tojson }}",
+ json::object(),
+ "{\"a\": 3, \"b\": 1, \"c\": 2}"
+ );
+
+ test_template(t, "dict from tuples",
+ "{% set o = dict((obj | items | list)) %}{{ o|tojson }}",
+ {{"obj", {{"a", 3}, {"b", 1}, {"c", 2}}}},
+ "{\"a\": 3, \"b\": 1, \"c\": 2}"
+ );
+
+ test_template(t, "dict from tuples and kwargs",
+ "{% set o = dict((obj | items | list), c=2) %}{{ o|tojson }}",
+ {{"obj", {{"a", 3}, {"b", 1}}}},
+ "{\"a\": 3, \"b\": 1, \"c\": 2}"
+ );
}
static void test_hasher(testing & t) {