mirror of
https://github.com/ggerganov/llama.cpp.git
synced 2026-02-05 13:53:23 +02:00
* jinja vm * lexer * add vm types * demo * clean up * parser ok * binary_expression::execute * shadow naming * bin ops works! * fix map object * add string builtins * add more builtins * wip * use mk_val * eval with is_user_input * render gemma tmpl ok * track input string even after transformations * support binded functions * keyword arguments and slicing array * use shared_ptr for values * add mk_stmt * allow print source on exception * fix negate test * testing more templates * mostly works * add filter_statement * allow func to access ctx * add jinja-value.cpp * impl global_from_json * a lot of fixes * more tests * more fix, more tests * more fixes * rm workarounds * demo: type inferrence * add placeholder for tojson * improve function args handling * rm type inference * no more std::regex * trailing spaces * make testing more flexible * make output a bit cleaner * (wip) redirect minja calls * test: add --output * fix crash on macro kwargs * add minimal caps system * add some workarounds * rm caps_apply_workarounds * get rid of preprocessing * more fixes * fix test-chat-template * move test-chat-jinja into test-chat-template * rm test-chat-jinja from cmake * test-chat-template: use common * fix build * fix build (2) * rename vm --> interpreter * improve error reporting * correct lstrip behavior * add tojson * more fixes * disable tests for COMMON_CHAT_FORMAT_GENERIC * make sure tojson output correct order * add object.length * fully functional selectattr / rejectattr * improve error reporting * more builtins added, more fixes * create jinja rendering tests * fix testing.h path * adjust whitespace rules * more fixes * temporary disable test for ibm-granite * r/lstrip behavior matched with hf.js * minimax, glm4.5 ok * add append and pop * kimi-k2 ok * test-chat passed * fix lstrip_block * add more jinja tests * cast to unsigned char * allow dict key to be numeric * nemotron: rm windows newline * tests ok * fix test * rename interpreter --> runtime * fix build * add more checks * bring back generic format support * fix Apertus * [json.exception.out_of_range.403] key 'content' not found * rm generic test * refactor input marking * add docs * fix windows build * clarify error message * improved tests * split/rsplit with maxsplit * non-inverse maxsplit forgot to change after simplifying * implement separators for tojson and fix indent * i like to move it move it * rename null -- > none * token::eof * some nits + comments * add exception classes for lexer and parser * null -> none * rename global -> env * rm minja * update docs * docs: add input marking caveats * imlement missing jinja-tests functions * oops * support trim filter with args, remove bogus to_json reference * numerous argument fixes * updated tests * implement optional strip chars parameter * use new chars parameter * float filter also has default * always leave at least one decimal in float string * jinja : static analysis + header cleanup + minor fixes * add fuzz test * add string.cpp * fix chat_template_kwargs * nits * fix build * revert * unrevert sorry :) * add fuzz func_args, refactor to be safer * fix array.map() * loosen ensure_vals max count condition, add not impl for map(int) * hopefully fix windows * check if empty first * normalize newlines --------- Co-authored-by: Alde Rojas <hello@alde.dev> Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@scala.com> Co-authored-by: Georgi Gerganov <ggerganov@gmail.com>
244 lines
6.6 KiB
C++
244 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include "common.h"
|
|
|
|
#include <chrono>
|
|
#include <exception>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <regex>
|
|
#include <vector>
|
|
|
|
struct testing {
|
|
std::ostream &out;
|
|
std::vector<std::string> stack;
|
|
std::regex filter;
|
|
bool filter_tests = false;
|
|
bool throw_exception = false;
|
|
bool verbose = false;
|
|
int tests = 0;
|
|
int assertions = 0;
|
|
int failures = 0;
|
|
int unnamed = 0;
|
|
int exceptions = 0;
|
|
|
|
static constexpr std::size_t status_column = 80;
|
|
|
|
explicit testing(std::ostream &os = std::cout) : out(os) {}
|
|
|
|
std::string indent() const {
|
|
if (stack.empty()) {
|
|
return "";
|
|
}
|
|
return std::string((stack.size() - 1) * 2, ' ');
|
|
}
|
|
|
|
std::string full_name() const {
|
|
return string_join(stack, ".");
|
|
}
|
|
|
|
void log(const std::string & msg) {
|
|
if (verbose) {
|
|
out << indent() << " " << msg << "\n";
|
|
}
|
|
}
|
|
|
|
void set_filter(const std::string & re) {
|
|
filter = std::regex(re);
|
|
filter_tests = true;
|
|
}
|
|
|
|
bool should_run() const {
|
|
if (filter_tests) {
|
|
if (!std::regex_match(full_name(), filter)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename F>
|
|
void run_with_exceptions(F &&f, const char *ctx) {
|
|
try {
|
|
f();
|
|
} catch (const std::exception &e) {
|
|
++failures;
|
|
++exceptions;
|
|
out << indent() << "UNHANDLED EXCEPTION (" << ctx << "): " << e.what() << "\n";
|
|
if (throw_exception) {
|
|
throw;
|
|
}
|
|
} catch (...) {
|
|
++failures;
|
|
++exceptions;
|
|
out << indent() << "UNHANDLED EXCEPTION (" << ctx << "): unknown\n";
|
|
if (throw_exception) {
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
void print_result(const std::string &label, int new_failures, int new_assertions, const std::string &extra = "") const {
|
|
std::string line = indent() + label;
|
|
|
|
std::string details;
|
|
if (new_assertions > 0) {
|
|
if (new_failures == 0) {
|
|
details = std::to_string(new_assertions) + " assertion(s)";
|
|
} else {
|
|
details = std::to_string(new_failures) + " of " +
|
|
std::to_string(new_assertions) + " assertion(s) failed";
|
|
}
|
|
}
|
|
if (!extra.empty()) {
|
|
if (!details.empty()) {
|
|
details += ", ";
|
|
}
|
|
details += extra;
|
|
}
|
|
|
|
if (!details.empty()) {
|
|
line += " (" + details + ")";
|
|
}
|
|
|
|
std::string status = (new_failures == 0) ? "[PASS]" : "[FAIL]";
|
|
|
|
if (line.size() + 1 < status_column) {
|
|
line.append(status_column - line.size(), ' ');
|
|
} else {
|
|
line.push_back(' ');
|
|
}
|
|
|
|
out << line << status << "\n";
|
|
}
|
|
|
|
template <typename F>
|
|
void test(const std::string &name, F f) {
|
|
stack.push_back(name);
|
|
if (!should_run()) {
|
|
stack.pop_back();
|
|
return;
|
|
}
|
|
|
|
++tests;
|
|
out << indent() << name << "\n";
|
|
|
|
int before_failures = failures;
|
|
int before_assertions = assertions;
|
|
|
|
run_with_exceptions([&] { f(*this); }, "test");
|
|
|
|
int new_failures = failures - before_failures;
|
|
int new_assertions = assertions - before_assertions;
|
|
|
|
print_result(name, new_failures, new_assertions);
|
|
|
|
stack.pop_back();
|
|
}
|
|
|
|
template <typename F>
|
|
void test(F f) {
|
|
test("test #" + std::to_string(++unnamed), f);
|
|
}
|
|
|
|
template <typename F>
|
|
void bench(const std::string &name, F f, int iterations = 100) {
|
|
stack.push_back(name);
|
|
if (!should_run()) {
|
|
stack.pop_back();
|
|
return;
|
|
}
|
|
|
|
++tests;
|
|
out << indent() << "[bench] " << name << "\n";
|
|
|
|
int before_failures = failures;
|
|
int before_assertions = assertions;
|
|
|
|
using clock = std::chrono::high_resolution_clock;
|
|
|
|
std::chrono::microseconds duration(0);
|
|
|
|
run_with_exceptions([&] {
|
|
for (auto i = 0; i < iterations; i++) {
|
|
auto start = clock::now();
|
|
f();
|
|
duration += std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start);
|
|
}
|
|
}, "bench");
|
|
|
|
auto avg_elapsed = duration.count() / iterations;
|
|
auto avg_elapsed_s = std::chrono::duration_cast<std::chrono::duration<double>>(duration).count() / iterations;
|
|
auto rate = (avg_elapsed_s > 0.0) ? (1.0 / avg_elapsed_s) : 0.0;
|
|
|
|
int new_failures = failures - before_failures;
|
|
int new_assertions = assertions - before_assertions;
|
|
|
|
std::string extra =
|
|
"n=" + std::to_string(iterations) +
|
|
" avg=" + std::to_string(avg_elapsed) + "us" +
|
|
" rate=" + std::to_string(int(rate)) + "/s";
|
|
|
|
print_result("[bench] " + name, new_failures, new_assertions, extra);
|
|
|
|
stack.pop_back();
|
|
}
|
|
|
|
template <typename F>
|
|
void bench(F f, int iterations = 100) {
|
|
bench("bench #" + std::to_string(++unnamed), f, iterations);
|
|
}
|
|
|
|
// Assertions
|
|
bool assert_true(bool cond) {
|
|
return assert_true("", cond);
|
|
}
|
|
|
|
bool assert_true(const std::string &msg, bool cond) {
|
|
++assertions;
|
|
if (!cond) {
|
|
++failures;
|
|
out << indent() << "ASSERTION FAILED";
|
|
if (!msg.empty()) {
|
|
out << " : " << msg;
|
|
}
|
|
out << "\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename A, typename B>
|
|
bool assert_equal(const A &expected, const B &actual) {
|
|
return assert_equal("", expected, actual);
|
|
}
|
|
|
|
template <typename A, typename B>
|
|
bool assert_equal(const std::string &msg, const A &expected, const B &actual) {
|
|
++assertions;
|
|
if (!(actual == expected)) {
|
|
++failures;
|
|
out << indent() << "ASSERT EQUAL FAILED";
|
|
if (!msg.empty()) {
|
|
out << " : " << msg;
|
|
}
|
|
out << "\n";
|
|
|
|
out << indent() << " expected: " << expected << "\n";
|
|
out << indent() << " actual : " << actual << "\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Print summary and return an exit code
|
|
int summary() const {
|
|
out << "\n";
|
|
out << "tests : " << tests << "\n";
|
|
out << "assertions : " << assertions << "\n";
|
|
out << "failures : " << failures << "\n";
|
|
out << "exceptions : " << exceptions << "\n";
|
|
return failures == 0 ? 0 : 1;
|
|
}
|
|
};
|