jinja : add missing 'in' test to template engine (#19004)
The jinja template parser was missing the 'in' test from
global_builtins(), causing templates using reject("in", ...),
select("in", ...), or 'x is in(y)' to fail with
"selectattr: unknown test 'in'".
This broke tool-calling for Qwen3-Coder and any other model
whose chat template uses the 'in' test.
Added test_is_in supporting array, string, and object containment
checks, mirroring the existing 'in' operator logic in runtime.cpp.
Includes test cases for all three containment types plus
reject/select filter usage.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
41ea26144e
commit
421ffab285
|
|
@ -393,6 +393,30 @@ const func_builtins & global_builtins() {
|
|||
{"test_is_lt", test_compare_fn<value_compare_op::lt>},
|
||||
{"test_is_lessthan", test_compare_fn<value_compare_op::lt>},
|
||||
{"test_is_ne", test_compare_fn<value_compare_op::ne>},
|
||||
{"test_is_in", [](const func_args & args) -> value {
|
||||
args.ensure_count(2);
|
||||
auto needle = args.get_pos(0);
|
||||
auto haystack = args.get_pos(1);
|
||||
if (is_val<value_undefined>(haystack)) {
|
||||
return mk_val<value_bool>(false);
|
||||
}
|
||||
if (is_val<value_array>(haystack)) {
|
||||
for (const auto & item : haystack->as_array()) {
|
||||
if (*needle == *item) {
|
||||
return mk_val<value_bool>(true);
|
||||
}
|
||||
}
|
||||
return mk_val<value_bool>(false);
|
||||
}
|
||||
if (is_val<value_string>(haystack) && is_val<value_string>(needle)) {
|
||||
return mk_val<value_bool>(
|
||||
haystack->as_string().str().find(needle->as_string().str()) != std::string::npos);
|
||||
}
|
||||
if (is_val<value_object>(haystack)) {
|
||||
return mk_val<value_bool>(haystack->has_key(needle));
|
||||
}
|
||||
throw raised_exception("'in' test expects an iterable");
|
||||
}},
|
||||
{"test_is_test", [](const func_args & args) -> value {
|
||||
args.ensure_vals<value_string>();
|
||||
auto & builtins = global_builtins();
|
||||
|
|
|
|||
|
|
@ -1036,6 +1036,42 @@ static void test_tests(testing & t) {
|
|||
json::object(),
|
||||
"yes"
|
||||
);
|
||||
|
||||
test_template(t, "is in (array, true)",
|
||||
"{{ 'yes' if 2 is in([1, 2, 3]) }}",
|
||||
json::object(),
|
||||
"yes"
|
||||
);
|
||||
|
||||
test_template(t, "is in (array, false)",
|
||||
"{{ 'yes' if 5 is in([1, 2, 3]) else 'no' }}",
|
||||
json::object(),
|
||||
"no"
|
||||
);
|
||||
|
||||
test_template(t, "is in (string)",
|
||||
"{{ 'yes' if 'bc' is in('abcde') }}",
|
||||
json::object(),
|
||||
"yes"
|
||||
);
|
||||
|
||||
test_template(t, "is in (object keys)",
|
||||
"{{ 'yes' if 'a' is in(obj) }}",
|
||||
{{"obj", {{"a", 1}, {"b", 2}}}},
|
||||
"yes"
|
||||
);
|
||||
|
||||
test_template(t, "reject with in test",
|
||||
"{{ items | reject('in', skip) | join(', ') }}",
|
||||
{{"items", json::array({"a", "b", "c", "d"})}, {"skip", json::array({"b", "d"})}},
|
||||
"a, c"
|
||||
);
|
||||
|
||||
test_template(t, "select with in test",
|
||||
"{{ items | select('in', keep) | join(', ') }}",
|
||||
{{"items", json::array({"a", "b", "c", "d"})}, {"keep", json::array({"b", "c"})}},
|
||||
"b, c"
|
||||
);
|
||||
}
|
||||
|
||||
static void test_string_methods(testing & t) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue