more tests

This commit is contained in:
Xuan Son Nguyen 2025-12-29 10:53:32 +01:00
parent 2a31c9a30c
commit 1cf25734a9
5 changed files with 94 additions and 13 deletions

View File

@ -385,14 +385,30 @@ const func_builtins & value_string_t::get_builtins() const {
return input;
}
}},
{"slice", [](const func_args & args) -> value {
auto & input = args.args[0];
if (!is_val<value_string>(input)) {
throw raised_exception("slice() first argument must be a string");
}
if (args.args.size() < 1 || args.args.size() > 4) {
throw raised_exception("slice() takes between 1 and 4 arguments");
}
int64_t start = is_val<value_int>(args.args[1]) ? args.args[1]->as_int() : 0;
int64_t stop = is_val<value_int>(args.args[2]) ? args.args[2]->as_int() : -1;
int64_t step = is_val<value_int>(args.args[3]) ? args.args[3]->as_int() : 1;
if (step == 0) {
throw raised_exception("slice step cannot be zero");
}
auto sliced = slice(input->as_string().str(), start, stop, step);
auto res = mk_val<value_string>(sliced);
res->val_str.mark_input_based_on(input->as_string());
return res;
}},
{"indent", [](const func_args &) -> value {
throw std::runtime_error("indent builtin not implemented");
throw std::runtime_error("String indent builtin not implemented");
}},
{"join", [](const func_args &) -> value {
throw std::runtime_error("join builtin not implemented");
}},
{"slice", [](const func_args &) -> value {
throw std::runtime_error("slice builtin not implemented");
throw std::runtime_error("String join builtin not implemented");
}},
};
return builtins;
@ -635,15 +651,21 @@ const func_builtins & value_object_t::get_builtins() const {
const func_builtins & value_null_t::get_builtins() const {
static const func_builtins builtins = {
{"list", [](const func_args &) -> value {
{"list", [](const func_args & args) -> value {
// fix for meetkai-functionary-medium-v3.1.jinja
// TODO: hide under a flag?
return mk_val<value_array>();
if (args.ctx.wrk_around.none_has_builtins) {
return mk_val<value_array>();
} else {
throw raised_exception("'list' builtin not supported for none type");
}
}},
{"selectattr", [](const func_args &) -> value {
{"selectattr", [](const func_args & args) -> value {
// fix for meetkai-functionary-medium-v3.1.jinja
// TODO: hide under a flag?
return mk_val<value_array>();
if (args.ctx.wrk_around.none_has_builtins) {
return mk_val<value_array>();
} else {
throw raised_exception("'selectattr' builtin not supported for none type");
}
}},
};
return builtins;

View File

@ -109,6 +109,15 @@ value binary_expression::execute_impl(context & ctx) {
// Special case: `anything in undefined` is `false` and `anything not in undefined` is `true`
return mk_val<value_bool>(op.value == "not in");
}
if (ctx.wrk_around.string_plus_undefined_is_string && (op.value == "+" || op.value == "~")) {
JJ_DEBUG("%s", "Workaround: treating undefined as empty string for string concatenation");
auto left_str = left_val->is_undefined() ? string() : left_val->as_string();
auto right_str = right_val->is_undefined() ? string() : right_val->as_string();
auto output = left_str.append(right_str);
auto res = mk_val<value_string>();
res->val_str = std::move(output);
return res;
}
throw std::runtime_error("Cannot perform operation " + op.value + " on undefined values");
} else if (is_val<value_null>(left_val) || is_val<value_null>(right_val)) {
throw std::runtime_error("Cannot perform operation on null values");
@ -628,9 +637,12 @@ value member_expression::execute_impl(context & ctx) {
} else if (is_val<value_array>(object) || is_val<value_string>(object)) {
if (is_val<value_int>(property)) {
int64_t index = property->as_int();
JJ_DEBUG("Accessing %s index %lld", is_val<value_array>(object) ? "array" : "string", index);
JJ_DEBUG("Accessing %s index %lld", object->type().c_str(), index);
if (is_val<value_array>(object)) {
auto & arr = object->as_array();
if (index < 0) {
index += static_cast<int64_t>(arr.size());
}
if (index >= 0 && index < static_cast<int64_t>(arr.size())) {
val = arr[index];
}

View File

@ -2,6 +2,7 @@
#include "jinja-lexer.h"
#include "jinja-value.h"
#include "jinja-workaround.h"
#include <string>
#include <vector>
@ -52,6 +53,8 @@ struct context {
std::time_t current_time; // for functions that need current time
workarounds wrk_around; // workarounds for non-standard jinja behavior
context() {
var["true"] = mk_val<value_bool>(true);
var["false"] = mk_val<value_bool>(false);

View File

@ -0,0 +1,20 @@
#pragma once
#include "jinja-value.h"
#include <string>
#include <vector>
namespace jinja {
// containing workarounds for Jinja templates that rely on non-standard behavior
struct workarounds {
// meetkai-functionary-medium-v3.1.jinja call filter on None type
bool none_has_builtins = true;
// Olmo calls operation + between string and undefined
bool string_plus_undefined_is_string = true;
};
} // namespace jinja

View File

@ -28,11 +28,32 @@ int main(void) {
std::vector<std::string> failed_tests;
auto is_ignored_file = [](const std::string & filename) -> bool {
std::vector<std::string> ignored_files = {
"Apriel-",
"Olmo-3-7B-Instruct-Heretic-GGUF",
};
for (const auto & ignored : ignored_files) {
if (filename.find(ignored) != std::string::npos) {
return true;
}
}
return false;
};
// list all files in models/templates/ and run each
size_t test_count = 0;
std::string dir_path = "models/templates/";
size_t skip_count = 0;
//std::string dir_path = "models/templates/";
std::string dir_path = "../test-jinja/templates/";
for (const auto & entry : std::filesystem::directory_iterator(dir_path)) {
if (entry.is_regular_file()) {
if (is_ignored_file(entry.path().filename().string())) {
std::cout << "=== SKIPPING TEMPLATE FILE: " << entry.path().string() << " ===\n";
skip_count++;
continue;
}
test_count++;
std::cout << "\n\n=== RUNNING TEMPLATE FILE: " << entry.path().string() << " ===\n";
std::ifstream infile(entry.path());
@ -43,6 +64,7 @@ int main(void) {
std::cout << "Exception: " << e.what() << "\n";
std::cout << "=== ERROR WITH TEMPLATE FILE: " << entry.path().string() << " ===\n";
failed_tests.push_back(entry.path().string());
exit(1);
}
}
}
@ -50,6 +72,7 @@ int main(void) {
std::cout << "\n\n=== TEST SUMMARY ===\n";
std::cout << "Total tests run: " << test_count << "\n";
std::cout << "Total failed tests: " << failed_tests.size() << "\n";
std::cout << "Total skipped tests: " << skip_count << "\n";
for (const auto & test : failed_tests) {
std::cout << "FAILED TEST: " << test << "\n";
}
@ -92,6 +115,7 @@ void run(std::string contents) {
],
"bos_token": "<s>",
"eos_token": "</s>",
"tools": [],
"functions": "",
"datetime": ""
})";