CI/tests: refactor argument handling, add --errors-only to emit only WARN/ERR messages

This commit is contained in:
Piotr Wilkin
2026-09-17 18:47:59 +02:00
parent 2f3fd02526
commit 10532516c3
64 changed files with 2352 additions and 948 deletions
+2 -2
View File
@@ -63,9 +63,9 @@ jobs:
id: generate_models id: generate_models
run: | run: |
rm -rf build-ci-models && mkdir -p build-ci-models rm -rf build-ci-models && mkdir -p build-ci-models
./build/bin/test-llama-archs -o build-ci-models ./build/bin/test-llama-archs -o build-ci-models --errors-only
- name: Test fusion - name: Test fusion
id: test_fusion id: test_fusion
run: | run: |
./build/bin/test-fusion --models build-ci-models --device MTL0 --check tests/fusion/MTL.csv ./build/bin/test-fusion --models build-ci-models --device MTL0 --check tests/fusion/MTL.csv --errors-only
+15 -15
View File
@@ -278,17 +278,17 @@ function gg_run_test_llama_archs_tensor_split {
set -e set -e
if [ ! -z ${GG_BUILD_CUDA} ]; then if [ ! -z ${GG_BUILD_CUDA} ]; then
GGML_CUDA_DEVICES=1 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_CUDA_DEVICES=1 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
GGML_CUDA_DEVICES=2 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_CUDA_DEVICES=2 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
GGML_CUDA_DEVICES=3 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_CUDA_DEVICES=3 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
GGML_CUDA_DEVICES=4 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_CUDA_DEVICES=4 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
fi fi
if [ ! -z ${GG_BUILD_METAL} ]; then if [ ! -z ${GG_BUILD_METAL} ]; then
GGML_METAL_DEVICES=1 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_METAL_DEVICES=1 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
GGML_METAL_DEVICES=2 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_METAL_DEVICES=2 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
GGML_METAL_DEVICES=3 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_METAL_DEVICES=3 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
GGML_METAL_DEVICES=4 ./build-ci-release/bin/test-llama-archs -s 1 2>&1 GGML_METAL_DEVICES=4 ./build-ci-release/bin/test-llama-archs -s 1 --errors-only 2>&1
fi fi
set +e set +e
@@ -307,7 +307,7 @@ function gg_run_test_llama_archs_models {
rm -rf build-ci-models && mkdir -p build-ci-models rm -rf build-ci-models && mkdir -p build-ci-models
# generate the dummy models used by the model-dependent tests # generate the dummy models used by the model-dependent tests
./build-ci-release/bin/test-llama-archs -o build-ci-models 2>&1 ./build-ci-release/bin/test-llama-archs -o build-ci-models --errors-only 2>&1
fi fi
set +e set +e
@@ -455,10 +455,10 @@ function gg_run_qwen3_0_6b {
(time ./bin/llama-imatrix --model ${model_f16} -f ${wiki_test} -ngl 99 -c 1024 -b 512 --chunks 2 ) 2>&1 | tee -a $OUT/${ci}-imatrix.log (time ./bin/llama-imatrix --model ${model_f16} -f ${wiki_test} -ngl 99 -c 1024 -b 512 --chunks 2 ) 2>&1 | tee -a $OUT/${ci}-imatrix.log
(time ./bin/test-save-load-state --model ${model_q4_0} -ngl 10 -c 1024 -fa off --no-op-offload) 2>&1 | tee -a $OUT/${ci}-save-load-state.log (time ./bin/test-save-load-state --model ${model_q4_0} -ngl 10 -c 1024 -fa off --no-op-offload --errors-only) 2>&1 | tee -a $OUT/${ci}-save-load-state.log
(time ./bin/test-save-load-state --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload) 2>&1 | tee -a $OUT/${ci}-save-load-state.log (time ./bin/test-save-load-state --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload --errors-only) 2>&1 | tee -a $OUT/${ci}-save-load-state.log
(time ./bin/test-save-load-state --model ${model_q4_0} -ngl 99 -c 1024 -fa off ) 2>&1 | tee -a $OUT/${ci}-save-load-state.log (time ./bin/test-save-load-state --model ${model_q4_0} -ngl 99 -c 1024 -fa off --errors-only) 2>&1 | tee -a $OUT/${ci}-save-load-state.log
(time ./bin/test-save-load-state --model ${model_q4_0} -ngl 99 -c 1024 -fa on ) 2>&1 | tee -a $OUT/${ci}-save-load-state.log (time ./bin/test-save-load-state --model ${model_q4_0} -ngl 99 -c 1024 -fa on --errors-only) 2>&1 | tee -a $OUT/${ci}-save-load-state.log
function check_ppl { function check_ppl {
qnt="$1" qnt="$1"
@@ -676,9 +676,9 @@ function gg_run_test_backend_ops {
# TODO: reduce the test-backend-ops timeout to 1800s # TODO: reduce the test-backend-ops timeout to 1800s
if [ ! -z ${GG_BUILD_HIGH_PERF} ]; then if [ ! -z ${GG_BUILD_HIGH_PERF} ]; then
(time timeout 3600 ./bin/test-backend-ops ${args_extra} -b CPU) 2>&1 | tee -a $OUT/${ci}-test-backend-ops.log (time timeout 3600 ./bin/test-backend-ops ${args_extra} -b CPU --errors-only) 2>&1 | tee -a $OUT/${ci}-test-backend-ops.log
else else
(time timeout 3600 ./bin/test-backend-ops ${args_extra} ) 2>&1 | tee -a $OUT/${ci}-test-backend-ops.log (time timeout 3600 ./bin/test-backend-ops ${args_extra} --errors-only) 2>&1 | tee -a $OUT/${ci}-test-backend-ops.log
fi fi
set +e set +e
+8
View File
@@ -3937,6 +3937,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
common_log_set_verbosity_thold(value); common_log_set_verbosity_thold(value);
} }
).set_env("LLAMA_ARG_LOG_VERBOSITY")); ).set_env("LLAMA_ARG_LOG_VERBOSITY"));
add_opt(common_arg(
{"--errors-only"},
"Set verbosity threshold to WARN (only print warnings and errors)",
[](common_params & params) {
params.verbosity = LOG_LEVEL_WARN;
common_log_set_verbosity_thold(LOG_LEVEL_WARN);
}
));
add_opt(common_arg( add_opt(common_arg(
{"--log-prefix"}, {"--log-prefix"},
{"--no-log-prefix"}, {"--no-log-prefix"},
+3 -1
View File
@@ -1573,7 +1573,9 @@ char * common_get_model_or_exit(int argc, char * argv[]) {
char * path = getenv("LLAMACPP_TEST_MODELFILE"); char * path = getenv("LLAMACPP_TEST_MODELFILE");
if (!path || strlen(path) == 0) { if (!path || strlen(path) == 0) {
fprintf(stderr, "\033[33mWARNING: No model file provided. Skipping this test. Set LLAMACPP_TEST_MODELFILE=<gguf_model_path> to silence this warning and run this test.\n\033[0m"); const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
fprintf(stderr, "%sWARNING: No model file provided. Skipping this test. Set LLAMACPP_TEST_MODELFILE=<gguf_model_path> to silence this warning and run this test.\n%s",
use_color ? LOG_COL_YELLOW : "", use_color ? LOG_COL_DEFAULT : "");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
@@ -197,7 +197,7 @@ run_backend_ops_case() {
esac esac
echo "=== [backend-ops:$dtype] test-backend-ops -b HTP0 -o MUL_MAT ===" echo "=== [backend-ops:$dtype] test-backend-ops -b HTP0 -o MUL_MAT ==="
timeout 600 env GGML_HEXAGON_NDEV=1 GGML_HEXAGON_HOSTBUF=0 ./bin/test-backend-ops \ timeout 600 env GGML_HEXAGON_NDEV=1 GGML_HEXAGON_HOSTBUF=0 ./bin/test-backend-ops \
-b HTP0 -o MUL_MAT -p "$pattern" \ -b HTP0 -o MUL_MAT -p "$pattern" --errors-only \
> "$log" 2>&1 > "$log" 2>&1
local rc=$? local rc=$?
note_timeout_if_triggered "$rc" 600 "$log" note_timeout_if_triggered "$rc" 600 "$log"
@@ -32,7 +32,7 @@ def test_backend_ops_htp0(type_a):
pattern = f"type_a={type_a}" pattern = f"type_a={type_a}"
result = run_snapdragon( result = run_snapdragon(
["test-backend-ops", "-b", "HTP0", "-o", "MUL_MAT", "-p", pattern], ["test-backend-ops", "-b", "HTP0", "-o", "MUL_MAT", "-p", pattern, "--errors-only"],
) )
write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "") write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "")
assert result.returncode == 0, ( assert result.returncode == 0, (
+1 -1
View File
@@ -1955,7 +1955,7 @@ void llama_vocab::impl::load(llama_model_loader & ml, const LLM_KV & kv) {
// read vocab size from metadata // read vocab size from metadata
uint32_t n_tokens = 0; uint32_t n_tokens = 0;
if (ml.get_key(LLM_KV_VOCAB_SIZE, n_tokens, false)) { if (ml.get_key(LLM_KV_VOCAB_SIZE, n_tokens, false)) {
LLAMA_LOG_WARN("%s: adding %u dummy tokens\n", __func__, n_tokens); LLAMA_LOG_INFO("%s: adding %u dummy tokens\n", __func__, n_tokens);
id_to_token.resize(n_tokens); id_to_token.resize(n_tokens);
} }
+2 -2
View File
@@ -40,7 +40,7 @@ function(llama_test target)
add_test( add_test(
NAME ${TEST_NAME} NAME ${TEST_NAME}
WORKING_DIRECTORY ${LLAMA_TEST_WORKING_DIRECTORY} WORKING_DIRECTORY ${LLAMA_TEST_WORKING_DIRECTORY}
COMMAND $<TARGET_FILE:${TEST_TARGET}> COMMAND $<TARGET_FILE:${TEST_TARGET}> --errors-only
${LLAMA_TEST_ARGS}) ${LLAMA_TEST_ARGS})
set_property(TEST ${TEST_NAME} PROPERTY LABELS ${LLAMA_TEST_LABEL}) set_property(TEST ${TEST_NAME} PROPERTY LABELS ${LLAMA_TEST_LABEL})
@@ -110,7 +110,7 @@ function(llama_build_and_test source)
add_test( add_test(
NAME ${TEST_TARGET} NAME ${TEST_TARGET}
WORKING_DIRECTORY ${LLAMA_TEST_WORKING_DIRECTORY} WORKING_DIRECTORY ${LLAMA_TEST_WORKING_DIRECTORY}
COMMAND $<TARGET_FILE:${TEST_TARGET}> COMMAND $<TARGET_FILE:${TEST_TARGET}> --errors-only
${LLAMA_TEST_ARGS}) ${LLAMA_TEST_ARGS})
set_property(TEST ${TEST_TARGET} PROPERTY LABELS ${LLAMA_TEST_LABEL}) set_property(TEST ${TEST_TARGET} PROPERTY LABELS ${LLAMA_TEST_LABEL})
+23 -6
View File
@@ -4,6 +4,10 @@
#include "../ggml/src/ggml-impl.h" #include "../ggml/src/ggml-impl.h"
#include "ggml.h" #include "ggml.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <algorithm> #include <algorithm>
#include <exception> #include <exception>
#include <memory> #include <memory>
@@ -189,7 +193,7 @@ static int get_leaf_id(ggml_cgraph * graph, const char * tensor_name) {
return i; return i;
} }
} }
fprintf(stderr, "leaf not found: %s\n", tensor_name); LOG_ERR("leaf not found: %s\n", tensor_name);
return -1; return -1;
} }
@@ -199,7 +203,7 @@ static int get_node_id(ggml_cgraph * graph, const char * tensor_name) {
return i; return i;
} }
} }
fprintf(stderr, "node not found: %s", tensor_name); LOG_ERR("node not found: %s", tensor_name);
return -1; return -1;
} }
@@ -651,13 +655,23 @@ static void test_graph_optimize_alloc_dep() {
} }
static void run(const char * name, void (*f)()) { static void run(const char * name, void (*f)()) {
printf("%s ", name); LOG_INF(" running %s\n", name);
fflush(stdout); // drain the queue, so the name is on disk if f() aborts
common_log_flush(common_log_main());
f(); f();
printf("PASSED\n"); LOG_CNT("PASSED\n");
} }
int main() { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-alloc");
run("test_max_size_too_many_tensors", test_max_size_too_many_tensors); run("test_max_size_too_many_tensors", test_max_size_too_many_tensors);
run("test_max_size_tensor_too_large", test_max_size_tensor_too_large); run("test_max_size_tensor_too_large", test_max_size_tensor_too_large);
run("test_tensor_larger_than_max_size", test_tensor_larger_than_max_size); run("test_tensor_larger_than_max_size", test_tensor_larger_than_max_size);
@@ -672,5 +686,8 @@ int main() {
run("test_buffer_size_zero", test_buffer_size_zero); run("test_buffer_size_zero", test_buffer_size_zero);
run("test_reallocation", test_reallocation); run("test_reallocation", test_reallocation);
run("test_graph_optimize_alloc_dep", test_graph_optimize_alloc_dep); run("test_graph_optimize_alloc_dep", test_graph_optimize_alloc_dep);
LOG("%s: %s\n", "test-alloc", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+35 -20
View File
@@ -2,6 +2,7 @@
#include "common.h" #include "common.h"
#include "download.h" #include "download.h"
#include "llama.h" #include "llama.h"
#include "log.h"
#include "speculative.h" #include "speculative.h"
#include <cmath> #include <cmath>
@@ -101,7 +102,7 @@ static void test(void) {
assert(draft.n_outputs_max_per_seq == 1); assert(draft.n_outputs_max_per_seq == 1);
} }
printf("test-arg-parser: make sure there is no duplicated arguments in any examples\n\n"); LOG_INF("test-arg-parser: make sure there is no duplicated arguments in any examples\n\n");
for (int ex = 0; ex < LLAMA_EXAMPLE_COUNT; ex++) { for (int ex = 0; ex < LLAMA_EXAMPLE_COUNT; ex++) {
try { try {
auto ctx_arg = common_params_parser_init(params, (enum llama_example)ex); auto ctx_arg = common_params_parser_init(params, (enum llama_example)ex);
@@ -114,7 +115,7 @@ static void test(void) {
if (seen_args.find(arg) == seen_args.end()) { if (seen_args.find(arg) == seen_args.end()) {
seen_args.insert(arg); seen_args.insert(arg);
} else { } else {
fprintf(stderr, "test-arg-parser: found different handlers for the same argument: %s", arg.c_str()); LOG_ERR("test-arg-parser: found different handlers for the same argument: %s", arg.c_str());
exit(1); exit(1);
} }
} }
@@ -123,7 +124,7 @@ static void test(void) {
if (seen_env_vars.find(env) == seen_env_vars.end()) { if (seen_env_vars.find(env) == seen_env_vars.end()) {
seen_env_vars.insert(env); seen_env_vars.insert(env);
} else { } else {
fprintf(stderr, "test-arg-parser: found different handlers for the same env var: %s", env.c_str()); LOG_ERR("test-arg-parser: found different handlers for the same env var: %s", env.c_str());
exit(1); exit(1);
} }
} }
@@ -138,7 +139,7 @@ static void test(void) {
const std::string last(opt.args.back()); const std::string last(opt.args.back());
if (first.length() > last.length()) { if (first.length() > last.length()) {
fprintf(stderr, "test-arg-parser: shorter argument should come before longer one: %s, %s\n", LOG_ERR("test-arg-parser: shorter argument should come before longer one: %s, %s\n",
first.c_str(), last.c_str()); first.c_str(), last.c_str());
assert(false); assert(false);
} }
@@ -150,14 +151,15 @@ static void test(void) {
const std::string last(opt.args_neg.back()); const std::string last(opt.args_neg.back());
if (first.length() > last.length()) { if (first.length() > last.length()) {
fprintf(stderr, "test-arg-parser: shorter negated argument should come before longer one: %s, %s\n", LOG_ERR("test-arg-parser: shorter negated argument should come before longer one: %s, %s\n",
first.c_str(), last.c_str()); first.c_str(), last.c_str());
assert(false); assert(false);
} }
} }
} }
} catch (std::exception & e) { } catch (std::exception & e) {
printf("%s\n", e.what()); LOG_ERR("%s\n", e.what());
common_log_flush(common_log_main());
assert(false); assert(false);
} }
} }
@@ -172,7 +174,7 @@ static void test(void) {
std::vector<std::string> argv; std::vector<std::string> argv;
printf("test-arg-parser: test invalid usage\n\n"); LOG_INF("test-arg-parser: test invalid usage\n\n");
// missing value // missing value
argv = {"binary_name", "-m"}; argv = {"binary_name", "-m"};
@@ -229,7 +231,7 @@ static void test(void) {
argv = {"binary_name", "-lm", "hello"}; argv = {"binary_name", "-lm", "hello"};
assert(false == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON)); assert(false == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
printf("test-arg-parser: test valid usage\n\n"); LOG_INF("test-arg-parser: test valid usage\n\n");
argv = {"binary_name", "-m", "model_file.gguf"}; argv = {"binary_name", "-m", "model_file.gguf"};
assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON)); assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
@@ -305,9 +307,9 @@ static void test(void) {
// skip this part on windows, because setenv is not supported // skip this part on windows, because setenv is not supported
#ifdef _WIN32 #ifdef _WIN32
printf("test-arg-parser: skip on windows build\n"); LOG_INF("test-arg-parser: skip on windows build\n");
#else #else
printf("test-arg-parser: test environment variables (valid + invalid usages)\n\n"); LOG_INF("test-arg-parser: test environment variables (valid + invalid usages)\n\n");
setenv("LLAMA_ARG_THREADS", "blah", true); setenv("LLAMA_ARG_THREADS", "blah", true);
argv = {"binary_name"}; argv = {"binary_name"};
@@ -344,7 +346,7 @@ static void test(void) {
assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON)); assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_COMMON));
assert(params.load_mode == LLAMA_LOAD_MODE_DIRECT_IO); assert(params.load_mode == LLAMA_LOAD_MODE_DIRECT_IO);
printf("test-arg-parser: test negated environment variables\n\n"); LOG_INF("test-arg-parser: test negated environment variables\n\n");
setenv("LLAMA_ARG_LOAD_MODE", "none", true); setenv("LLAMA_ARG_LOAD_MODE", "none", true);
setenv("LLAMA_ARG_NO_PERF", "1", true); // legacy format setenv("LLAMA_ARG_NO_PERF", "1", true); // legacy format
@@ -353,7 +355,7 @@ static void test(void) {
assert(params.load_mode == LLAMA_LOAD_MODE_NONE); assert(params.load_mode == LLAMA_LOAD_MODE_NONE);
assert(params.no_perf == true); assert(params.no_perf == true);
printf("test-arg-parser: test environment variables being overwritten\n\n"); LOG_INF("test-arg-parser: test environment variables being overwritten\n\n");
setenv("LLAMA_ARG_MODEL", "blah.gguf", true); setenv("LLAMA_ARG_MODEL", "blah.gguf", true);
setenv("LLAMA_ARG_THREADS", "1010", true); setenv("LLAMA_ARG_THREADS", "1010", true);
@@ -363,12 +365,12 @@ static void test(void) {
assert(params.cpuparams.n_threads == 1010); assert(params.cpuparams.n_threads == 1010);
#endif // _WIN32 #endif // _WIN32
printf("test-arg-parser: test download functions\n\n"); LOG_INF("test-arg-parser: test download functions\n\n");
const char * GOOD_URL = "http://ggml.ai/"; const char * GOOD_URL = "http://ggml.ai/";
const char * BAD_URL = "http://ggml.ai/404"; const char * BAD_URL = "http://ggml.ai/404";
{ {
printf("test-arg-parser: test good URL\n\n"); LOG_INF("test-arg-parser: test good URL\n\n");
auto res = common_remote_get_content(GOOD_URL, {}); auto res = common_remote_get_content(GOOD_URL, {});
assert(res.first == 200); assert(res.first == 200);
assert(res.second.size() > 0); assert(res.second.size() > 0);
@@ -377,32 +379,45 @@ static void test(void) {
} }
{ {
printf("test-arg-parser: test bad URL\n\n"); LOG_INF("test-arg-parser: test bad URL\n\n");
auto res = common_remote_get_content(BAD_URL, {}); auto res = common_remote_get_content(BAD_URL, {});
assert(res.first == 404); assert(res.first == 404);
} }
{ {
printf("test-arg-parser: test max size error\n"); LOG_INF("test-arg-parser: test max size error\n");
common_remote_params params; common_remote_params params;
params.max_size = 1; params.max_size = 1;
try { try {
common_remote_get_content(GOOD_URL, params); common_remote_get_content(GOOD_URL, params);
assert(false && "it should throw an error"); assert(false && "it should throw an error");
} catch (std::exception & e) { } catch (std::exception & e) {
printf(" expected error: %s\n\n", e.what()); LOG_INF(" expected error: %s\n\n", e.what());
} }
} }
printf("test-arg-parser: all tests OK\n\n"); LOG_INF("test-arg-parser: all tests OK\n\n");
} }
int main(void) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-arg-parser");
try { try {
test(); test();
} catch (std::exception & e) { } catch (std::exception & e) {
fprintf(stderr, "test-arg-parser: exception: %s\n", e.what()); LOG_ERR("test-arg-parser: exception: %s\n", e.what());
LOG("%s: %s\n", "test-arg-parser", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
LOG("%s: %s\n", "test-arg-parser", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+33 -1
View File
@@ -1,13 +1,43 @@
// ref: https://github.com/ggml-org/llama.cpp/issues/4952#issuecomment-1892864763 // ref: https://github.com/ggml-org/llama.cpp/issues/4952#issuecomment-1892864763
#include <thread> #include <thread>
#include <vector>
#include "llama.h" #include "llama.h"
#include "common.h" #include "common.h"
#include "arg.h"
#include "log.h"
// This creates a new context inside a pthread and then tries to exit cleanly. // This creates a new context inside a pthread and then tries to exit cleanly.
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
auto * model_path = common_get_model_or_exit(argc, argv); // the model path is this test's only positional argument
char * model_argv[2] = { argv[0], nullptr };
{
common_params params;
params.model.path = "."; // this test takes no model
common_init();
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (model_argv[1] == nullptr) {
model_argv[1] = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
// falls back to LLAMACPP_TEST_MODELFILE, or warns and exits if no model is given
auto * model_path = common_get_model_or_exit(model_argv[1] == nullptr ? 1 : 2, model_argv);
// that call exits when no model is given, so the verdict below is only reached with a model
LOG("%s: running\n", "test-autorelease");
std::thread([&model_path]() { std::thread([&model_path]() {
llama_backend_init(); llama_backend_init();
@@ -18,5 +48,7 @@ int main(int argc, char ** argv) {
llama_backend_free(); llama_backend_free();
}).join(); }).join();
LOG("%s: %s\n", "test-autorelease", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+84 -49
View File
@@ -20,6 +20,10 @@
#include "ggml-backend.h" #include "ggml-backend.h"
#include "ggml-cpp.h" #include "ggml-cpp.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <array> #include <array>
@@ -833,6 +837,14 @@ struct printer {
}; };
struct console_printer : public printer { struct console_printer : public printer {
// the printer is created after the CLI has been parsed, so the threshold is final here:
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_blue = use_color ? "\033[1;34m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
void print_test_result(const test_result & result) override { void print_test_result(const test_result & result) override {
if (result.test_mode == "test") { if (result.test_mode == "test") {
print_test_console(result); print_test_console(result);
@@ -844,21 +856,21 @@ struct console_printer : public printer {
} }
void print_operation(const test_operation_info & info) override { void print_operation(const test_operation_info & info) override {
printf(" %s(%s): ", info.op_name.c_str(), info.op_params.c_str()); LOG_CNT(" %s(%s): ", info.op_name.c_str(), info.op_params.c_str());
fflush(stdout); fflush(stdout);
// Handle large tensor skip first // Handle large tensor skip first
if (info.is_large_tensor_skip) { if (info.is_large_tensor_skip) {
printf("skipping large tensors for speed \n"); LOG_CNT("skipping large tensors for speed \n");
return; return;
} }
// Handle not supported status // Handle not supported status
if (info.status == test_status_t::NOT_SUPPORTED) { if (info.status == test_status_t::NOT_SUPPORTED) {
if (!info.failure_reason.empty()) { if (!info.failure_reason.empty()) {
printf("not supported [%s]\n", info.failure_reason.c_str()); LOG_CNT("not supported [%s]\n", info.failure_reason.c_str());
} else { } else {
printf("not supported [%s]\n", info.backend_name.c_str()); LOG_CNT("not supported [%s]\n", info.backend_name.c_str());
} }
return; return;
} }
@@ -866,52 +878,52 @@ struct console_printer : public printer {
// Handle errors and additional information // Handle errors and additional information
if (info.has_error) { if (info.has_error) {
if (info.error_component == "allocation") { if (info.error_component == "allocation") {
fprintf(stderr, "failed to allocate tensors [%s] ", info.backend_name.c_str()); LOG_ERR("failed to allocate tensors [%s] ", info.backend_name.c_str());
} else if (info.error_component == "backend") { } else if (info.error_component == "backend") {
fprintf(stderr, " Failed to initialize %s backend\n", info.backend_name.c_str()); LOG_ERR(" Failed to initialize %s backend\n", info.backend_name.c_str());
} else { } else {
fprintf(stderr, "Error in %s: %s\n", info.error_component.c_str(), info.error_details.c_str()); LOG_ERR("Error in %s: %s\n", info.error_component.c_str(), info.error_details.c_str());
} }
} }
// Handle gradient info // Handle gradient info
if (info.has_gradient_info) { if (info.has_gradient_info) {
printf("[%s] nonfinite gradient at index %" PRId64 " (%s=%f) ", info.op_name.c_str(), info.gradient_index, LOG_CNT("[%s] nonfinite gradient at index %" PRId64 " (%s=%f) ", info.op_name.c_str(), info.gradient_index,
info.gradient_param_name.c_str(), info.gradient_value); info.gradient_param_name.c_str(), info.gradient_value);
} }
// Handle MAA error // Handle MAA error
if (info.has_maa_error) { if (info.has_maa_error) {
printf("[%s] MAA = %.9f > %.9f ", info.op_name.c_str(), info.maa_error, info.maa_threshold); LOG_CNT("[%s] MAA = %.9f > %.9f ", info.op_name.c_str(), info.maa_error, info.maa_threshold);
} }
// Handle compare failure // Handle compare failure
if (info.is_compare_failure) { if (info.is_compare_failure) {
printf("compare failed "); LOG_CNT("compare failed ");
} }
// Print final status // Print final status
if (info.status == test_status_t::OK) { if (info.status == test_status_t::OK) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
} }
void print_summary(const test_summary_info & info) override { void print_summary(const test_summary_info & info) override {
if (info.is_backend_summary) { if (info.is_backend_summary) {
printf("%zu/%zu backends passed\n", info.tests_passed, info.tests_total); LOG_CNT("%zu/%zu backends passed\n", info.tests_passed, info.tests_total);
} else { } else {
printf(" %zu/%zu tests passed\n", info.tests_passed, info.tests_total); LOG_CNT(" %zu/%zu tests passed\n", info.tests_passed, info.tests_total);
} }
} }
void print_backend_status(const backend_status_info & info) override { void print_backend_status(const backend_status_info & info) override {
printf(" Backend %s: ", info.backend_name.c_str()); LOG_CNT(" Backend %s: ", info.backend_name.c_str());
if (info.status == test_status_t::OK) { if (info.status == test_status_t::OK) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
} }
@@ -920,30 +932,30 @@ struct console_printer : public printer {
} }
void print_backend_init(const backend_init_info & info) override { void print_backend_init(const backend_init_info & info) override {
printf("Backend %zu/%zu: %s\n", info.device_index + 1, info.total_devices, info.device_name.c_str()); LOG_INF("Backend %zu/%zu: %s\n", info.device_index + 1, info.total_devices, info.device_name.c_str());
if (info.skipped) { if (info.skipped) {
printf(" %s\n", info.skip_reason.c_str()); LOG_INF(" %s\n", info.skip_reason.c_str());
return; return;
} }
if (!info.description.empty()) { if (!info.description.empty()) {
printf(" Device description: %s\n", info.description.c_str()); LOG_INF(" Device description: %s\n", info.description.c_str());
} }
if (info.has_memory_info) { if (info.has_memory_info) {
printf(" Device memory: %zu MB (%zu MB free)\n", info.memory_total_mb, info.memory_free_mb); LOG_INF(" Device memory: %zu MB (%zu MB free)\n", info.memory_total_mb, info.memory_free_mb);
} }
printf("\n"); LOG_CNT("\n");
} }
void print_overall_summary(const overall_summary_info & info) override { void print_overall_summary(const overall_summary_info & info) override {
printf("%zu/%zu backends passed\n", info.backends_passed, info.backends_total); printf("%zu/%zu backends passed\n", info.backends_passed, info.backends_total);
if (info.all_passed) { if (info.all_passed) {
printf("\033[1;32mOK\033[0m\n"); printf("%sOK%s\n", col_ok, col_end);
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); printf("%sFAIL%s\n", col_fail, col_end);
} }
} }
@@ -960,28 +972,30 @@ struct console_printer : public printer {
private: private:
void print_test_console(const test_result & result) { void print_test_console(const test_result & result) {
printf(" %s(%s): ", result.op_name.c_str(), result.op_params.c_str()); LOG_CNT(" %s(%s): ", result.op_name.c_str(), result.op_params.c_str());
fflush(stdout); fflush(stdout);
if (!result.supported) { if (!result.supported) {
printf("not supported [%s] ", result.backend_name.c_str()); LOG_CNT("not supported [%s] ", result.backend_name.c_str());
printf("\n"); LOG_CNT("\n");
return; return;
} }
if (result.passed) { if (result.passed) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
} }
void print_perf_console(const test_result & result) { void print_perf_console(const test_result & result) {
int len = printf(" %s(%s): ", result.op_name.c_str(), result.op_params.c_str()); const std::string label = " " + result.op_name + "(" + result.op_params + "): ";
int len = (int) label.size();
LOG_CNT("%s", label.c_str());
fflush(stdout); fflush(stdout);
if (!result.supported) { if (!result.supported) {
printf("not supported\n"); LOG_CNT("not supported\n");
return; return;
} }
@@ -991,9 +1005,9 @@ struct console_printer : public printer {
if (last - len < 5) { if (last - len < 5) {
last += align; last += align;
} }
printf("%*s", last - len, ""); LOG_CNT("%*s", last - len, "");
printf(" %8d runs - %8.2f us/run - ", result.n_runs, result.time_us); LOG_CNT(" %8d runs - %8.2f us/run - ", result.n_runs, result.time_us);
if (result.flops > 0) { if (result.flops > 0) {
auto format_flops = [](double flops) -> std::string { auto format_flops = [](double flops) -> std::string {
@@ -1010,22 +1024,22 @@ struct console_printer : public printer {
return buf; return buf;
}; };
uint64_t op_flops_per_run = result.flops * result.time_us / 1e6; uint64_t op_flops_per_run = result.flops * result.time_us / 1e6;
printf("%s/run - \033[1;34m%sS\033[0m", format_flops(op_flops_per_run).c_str(), LOG_CNT("%s/run - %s%sS%s", format_flops(op_flops_per_run).c_str(), col_blue,
format_flops(result.flops).c_str()); format_flops(result.flops).c_str(), col_end);
} else { } else {
printf("%8zu kB/run - \033[1;34m%7.2f GB/s\033[0m", result.memory_kb, result.bandwidth_gb_s); LOG_CNT("%8zu kB/run - %s%7.2f GB/s%s", result.memory_kb, col_blue, result.bandwidth_gb_s, col_end);
} }
printf("\n"); LOG_CNT("\n");
} }
void print_support_console(const test_result & result) { void print_support_console(const test_result & result) {
printf(" %s(%s): ", result.op_name.c_str(), result.op_params.c_str()); LOG_CNT(" %s(%s): ", result.op_name.c_str(), result.op_params.c_str());
fflush(stdout); fflush(stdout);
if (result.supported) { if (result.supported) {
printf("\033[1;32mSUPPORTED\033[0m\n"); LOG_CNT("%sSUPPORTED%s\n", col_ok, col_end);
} else { } else {
printf("\033[1;31mNOT SUPPORTED\033[0m\n"); LOG_CNT("%sNOT SUPPORTED%s\n", col_fail, col_end);
} }
} }
}; };
@@ -1595,7 +1609,7 @@ struct test_case {
// warmup run // warmup run
ggml_status status = ggml_backend_graph_compute(backend, gf); ggml_status status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
@@ -1650,7 +1664,7 @@ struct test_case {
int64_t start_time = ggml_time_us(); int64_t start_time = ggml_time_us();
ggml_status status = ggml_backend_graph_compute(backend, gf); ggml_status status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
int64_t end_time = ggml_time_us(); int64_t end_time = ggml_time_us();
@@ -1841,12 +1855,12 @@ struct test_case {
ggml_status status = ggml_backend_graph_compute(backend, gf); ggml_status status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
status = ggml_backend_graph_compute(backend, gb); status = ggml_backend_graph_compute(backend, gb);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
@@ -1900,7 +1914,7 @@ struct test_case {
ggml_backend_tensor_set(t, &xiu, i*sizeof(float), sizeof(float)); ggml_backend_tensor_set(t, &xiu, i*sizeof(float), sizeof(float));
status = ggml_backend_graph_compute(backend, gf); status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
ggml_backend_tensor_get(out, &fu, 0, ggml_nbytes(out)); ggml_backend_tensor_get(out, &fu, 0, ggml_nbytes(out));
@@ -1908,7 +1922,7 @@ struct test_case {
ggml_backend_tensor_set(t, &xid, i*sizeof(float), sizeof(float)); ggml_backend_tensor_set(t, &xid, i*sizeof(float), sizeof(float));
status = ggml_backend_graph_compute(backend, gf); status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
ggml_backend_tensor_get(out, &fd, 0, ggml_nbytes(out)); ggml_backend_tensor_get(out, &fd, 0, ggml_nbytes(out));
@@ -1917,7 +1931,7 @@ struct test_case {
ggml_backend_tensor_set(t, &xiuh, i*sizeof(float), sizeof(float)); ggml_backend_tensor_set(t, &xiuh, i*sizeof(float), sizeof(float));
status = ggml_backend_graph_compute(backend, gf); status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
ggml_backend_tensor_get(out, &fuh, 0, ggml_nbytes(out)); ggml_backend_tensor_get(out, &fuh, 0, ggml_nbytes(out));
@@ -1925,7 +1939,7 @@ struct test_case {
ggml_backend_tensor_set(t, &xidh, i*sizeof(float), sizeof(float)); ggml_backend_tensor_set(t, &xidh, i*sizeof(float), sizeof(float));
status = ggml_backend_graph_compute(backend, gf); status = ggml_backend_graph_compute(backend, gf);
if (status != GGML_STATUS_SUCCESS) { if (status != GGML_STATUS_SUCCESS) {
fprintf(stderr, "%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status)); LOG_ERR("%s: ggml_backend_graph_compute failed. status=%s \n", __func__, ggml_status_to_string(status));
return false; return false;
} }
ggml_backend_tensor_get(out, &fdh, 0, ggml_nbytes(out)); ggml_backend_tensor_get(out, &fdh, 0, ggml_nbytes(out));
@@ -11918,6 +11932,10 @@ static void usage(char ** argv) {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
test_mode mode = MODE_TEST; test_mode mode = MODE_TEST;
output_formats output_format = CONSOLE; output_formats output_format = CONSOLE;
const char * op_names_filter = nullptr; const char * op_names_filter = nullptr;
@@ -11926,6 +11944,9 @@ int main(int argc, char ** argv) {
const char * test_file_path = nullptr; const char * test_file_path = nullptr;
int parallel_workers = 1; int parallel_workers = 1;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "test") == 0) { if (strcmp(argv[i], "test") == 0) {
mode = MODE_TEST; mode = MODE_TEST;
@@ -11990,12 +12011,21 @@ int main(int argc, char ** argv) {
usage(argv); usage(argv);
return 1; return 1;
} }
} else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else { } else {
usage(argv); usage(argv);
return 1; return 1;
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-backend-ops");
// load and enumerate backends // load and enumerate backends
ggml_backend_load_all(); ggml_backend_load_all();
@@ -12056,12 +12086,17 @@ int main(int argc, char ** argv) {
output_printer->print_footer(); output_printer->print_footer();
} }
common_log_flush(common_log_main());
output_printer->print_overall_summary( output_printer->print_overall_summary(
overall_summary_info(n_ok, ggml_backend_dev_count(), n_ok == ggml_backend_dev_count())); overall_summary_info(n_ok, ggml_backend_dev_count(), n_ok == ggml_backend_dev_count()));
if (n_ok != ggml_backend_dev_count()) { if (n_ok != ggml_backend_dev_count()) {
LOG("%s: %s\n", "test-backend-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
LOG("%s: %s\n", "test-backend-ops", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+156 -99
View File
@@ -3,6 +3,9 @@
#include "llama-cpp.h" #include "llama-cpp.h"
#include "common.h" #include "common.h"
#include "arg.h"
#include "log.h"
#ifdef NDEBUG #ifdef NDEBUG
#undef NDEBUG #undef NDEBUG
#endif #endif
@@ -40,7 +43,7 @@ static llama_model_ptr load_model(const test_args & args) {
devs[0] = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_GPU); devs[0] = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_GPU);
if (devs[0] == nullptr) { if (devs[0] == nullptr) {
fprintf(stderr, "Error: GPU requested but not available\n"); LOG_ERR("Error: GPU requested but not available\n");
return nullptr; return nullptr;
} }
@@ -50,13 +53,13 @@ static llama_model_ptr load_model(const test_args & args) {
mparams.n_gpu_layers = 0; mparams.n_gpu_layers = 0;
} else { } else {
fprintf(stderr, "Error: invalid device '%s'\n", args.device.c_str()); LOG_ERR("Error: invalid device '%s'\n", args.device.c_str());
return nullptr; return nullptr;
} }
mparams.devices = devs; mparams.devices = devs;
fprintf(stderr, "Using device: %s\n", ggml_backend_dev_name(devs[0])); LOG_INF("Using device: %s\n", ggml_backend_dev_name(devs[0]));
} }
llama_model_ptr res; llama_model_ptr res;
@@ -64,7 +67,7 @@ static llama_model_ptr load_model(const test_args & args) {
res.reset(llama_model_load_from_file(args.model.c_str(), mparams)); res.reset(llama_model_load_from_file(args.model.c_str(), mparams));
if (!res) { if (!res) {
fprintf(stderr, "Warning: failed to load model '%s', skipping test\n", args.model.c_str()); LOG_WRN("Warning: failed to load model '%s', skipping test\n", args.model.c_str());
return nullptr; return nullptr;
} }
@@ -140,7 +143,7 @@ struct test_context {
prompt_tokens.data(), prompt_tokens.size(), prompt_tokens.data(), prompt_tokens.size(),
false, false); false, false);
if (n_tokens < 0) { if (n_tokens < 0) {
fprintf(stderr, "Warning: tokenization failed for seq_id %d\n", seq_id); LOG_WRN("Warning: tokenization failed for seq_id %d\n", seq_id);
llama_batch_free(batch); llama_batch_free(batch);
return false; return false;
} }
@@ -162,19 +165,19 @@ struct test_context {
} }
printf("Batch contents:\n"); LOG_CNT("Batch contents:\n");
printf("n_tokens: %d\n", batch.n_tokens); LOG_CNT("n_tokens: %d\n", batch.n_tokens);
for (int i = 0; i < batch.n_tokens; i++) { for (int i = 0; i < batch.n_tokens; i++) {
printf("token[%d]: tok=%-5d, pos=%d, n_seq_id=%d, seq_ids=[", i, batch.token[i], batch.pos[i], batch.n_seq_id[i]); LOG_CNT("token[%d]: tok=%-5d, pos=%d, n_seq_id=%d, seq_ids=[", i, batch.token[i], batch.pos[i], batch.n_seq_id[i]);
for (int j = 0; j < batch.n_seq_id[i]; j++) { for (int j = 0; j < batch.n_seq_id[i]; j++) {
printf("%d%s", batch.seq_id[i][j], j < batch.n_seq_id[i]-1 ? ", " : ""); LOG_CNT("%d%s", batch.seq_id[i][j], j < batch.n_seq_id[i]-1 ? ", " : "");
} }
printf("], logits=%d\n", batch.logits[i]); LOG_CNT("], logits=%d\n", batch.logits[i]);
} }
if (llama_decode(ctx.get(), batch) != 0) { if (llama_decode(ctx.get(), batch) != 0) {
fprintf(stderr, "Warning: llama_decode failed\n"); LOG_WRN("Warning: llama_decode failed\n");
llama_batch_free(batch); llama_batch_free(batch);
return false; return false;
} }
@@ -194,7 +197,7 @@ struct test_context {
int32_t idx_for_seq(llama_seq_id seq_id) { int32_t idx_for_seq(llama_seq_id seq_id) {
auto it = last_batch_info.find(seq_id); auto it = last_batch_info.find(seq_id);
if (it == last_batch_info.end()) { if (it == last_batch_info.end()) {
fprintf(stderr, "Error: no batch index found for seq_id %d\n", seq_id); LOG_ERR("Error: no batch index found for seq_id %d\n", seq_id);
return -1; return -1;
} }
return it->second; return it->second;
@@ -218,7 +221,7 @@ struct test_context {
common_batch_add(batch, token, pos, { seq_id }, true); common_batch_add(batch, token, pos, { seq_id }, true);
if (llama_decode(ctx.get(), batch) != 0) { if (llama_decode(ctx.get(), batch) != 0) {
fprintf(stderr, "Warning: llama_decode failed for token %d in seq %d\n", token, seq_id); LOG_WRN("Warning: llama_decode failed for token %d in seq %d\n", token, seq_id);
llama_batch_free(batch); llama_batch_free(batch);
return false; return false;
} }
@@ -242,7 +245,7 @@ struct test_context {
} }
if (llama_decode(ctx.get(), batch) != 0) { if (llama_decode(ctx.get(), batch) != 0) {
fprintf(stderr, "Warning: llama_decode failed for batch tokens\n"); LOG_WRN("Warning: llama_decode failed for batch tokens\n");
llama_batch_free(batch); llama_batch_free(batch);
return false; return false;
} }
@@ -352,17 +355,17 @@ static void test_backend_greedy_sampling(const test_params & params) {
int32_t batch_idx = test_ctx.idx_for_seq(seq_id); int32_t batch_idx = test_ctx.idx_for_seq(seq_id);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
printf("greedy sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("greedy sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
token = llama_get_sampled_token_ith(test_ctx.ctx.get(), -1); token = llama_get_sampled_token_ith(test_ctx.ctx.get(), -1);
printf("greedy sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("greedy sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
int32_t loop_idx = test_ctx.idx_for_seq(seq_id); int32_t loop_idx = test_ctx.idx_for_seq(seq_id);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), loop_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), loop_idx);
printf("Generation step %d: token id:%d, string: %s\n", i, token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("Generation step %d: token id:%d, string: %s\n", i, token, test_ctx.token_to_piece(token, false).c_str());
if (!test_ctx.decode_token(token, 0)) { if (!test_ctx.decode_token(token, 0)) {
GGML_ASSERT(false && "Failed to decode token"); GGML_ASSERT(false && "Failed to decode token");
} }
@@ -388,14 +391,14 @@ static void test_backend_top_k_sampling(const test_params & params) {
float * logits = llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx); float * logits = llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx);
uint32_t n_logits = llama_get_sampled_logits_count_ith(test_ctx.ctx.get(), batch_idx); uint32_t n_logits = llama_get_sampled_logits_count_ith(test_ctx.ctx.get(), batch_idx);
for (size_t i = 0; i < n_logits; ++i) { for (size_t i = 0; i < n_logits; ++i) {
printf("top_k logit[%zu] = %.6f\n", i, logits[i]); LOG_CNT("top_k logit[%zu] = %.6f\n", i, logits[i]);
} }
llama_token * candidates = llama_get_sampled_candidates_ith(test_ctx.ctx.get(), batch_idx); llama_token * candidates = llama_get_sampled_candidates_ith(test_ctx.ctx.get(), batch_idx);
uint32_t n_candidates = llama_get_sampled_candidates_count_ith(test_ctx.ctx.get(), batch_idx); uint32_t n_candidates = llama_get_sampled_candidates_count_ith(test_ctx.ctx.get(), batch_idx);
for (size_t i = 0; i < n_candidates; ++i) { for (size_t i = 0; i < n_candidates; ++i) {
printf("top_k candidate[%zu] = %d : %s\n", i, candidates[i], LOG_CNT("top_k candidate[%zu] = %d : %s\n", i, candidates[i],
test_ctx.token_to_piece(candidates[i], false).c_str()); test_ctx.token_to_piece(candidates[i], false).c_str());
} }
// Sample using CPU sampler for verification that it is possible to do hybrid // Sample using CPU sampler for verification that it is possible to do hybrid
@@ -408,7 +411,7 @@ static void test_backend_top_k_sampling(const test_params & params) {
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
printf("backend top-k hybrid sampling test PASSED\n"); LOG_CNT("backend top-k hybrid sampling test PASSED\n");
} }
static void test_backend_temp_sampling(const test_params & params) { static void test_backend_temp_sampling(const test_params & params) {
@@ -447,7 +450,7 @@ static void test_backend_temp_sampling(const test_params & params) {
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("Sequence 0 sampled token id:%d, string: '%s'\n", token, token_str.c_str()); LOG_CNT("Sequence 0 sampled token id:%d, string: '%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
@@ -463,14 +466,14 @@ static void test_backend_temp_sampling(const test_params & params) {
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("Sequence 1 sampled token id:%d, string: '%s'\n", token, token_str.c_str()); LOG_CNT("Sequence 1 sampled token id:%d, string: '%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
} }
// lambda for testing non-positive temperature values. // lambda for testing non-positive temperature values.
auto test_argmax_temp = [&](float temp) { auto test_argmax_temp = [&](float temp) {
printf("\nTesting temperature = %.1f\n", temp); LOG_CNT("\nTesting temperature = %.1f\n", temp);
int seq_id = 0; int seq_id = 0;
struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params(); struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params();
@@ -496,7 +499,7 @@ static void test_backend_temp_sampling(const test_params & params) {
test_argmax_temp(0.0f); test_argmax_temp(0.0f);
test_argmax_temp(-1.0f); test_argmax_temp(-1.0f);
printf("backend temp sampling test PASSED\n"); LOG_CNT("backend temp sampling test PASSED\n");
} }
static void test_backend_temp_ext_sampling(const test_params & params) { static void test_backend_temp_ext_sampling(const test_params & params) {
@@ -529,7 +532,7 @@ static void test_backend_temp_ext_sampling(const test_params & params) {
// lambda for testing non-positive temp/delta/exponent values. // lambda for testing non-positive temp/delta/exponent values.
auto test_argmax_temp = [&](float temp, float delta, float exponent) { auto test_argmax_temp = [&](float temp, float delta, float exponent) {
printf("\nTesting temperature = %.1f, delta = %1.f, exponent = %1.f\n", temp, delta, exponent); LOG_CNT("\nTesting temperature = %.1f, delta = %1.f, exponent = %1.f\n", temp, delta, exponent);
int seq_id = 0; int seq_id = 0;
struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params(); struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params();
@@ -561,7 +564,7 @@ static void test_backend_temp_ext_sampling(const test_params & params) {
test_argmax_temp(-1.0f, 0.3f, 2.0f); // Greedy (temp<0) test_argmax_temp(-1.0f, 0.3f, 2.0f); // Greedy (temp<0)
test_argmax_temp(0.8f, 0.0f, 2.0f); // Temperature scaling test_argmax_temp(0.8f, 0.0f, 2.0f); // Temperature scaling
printf("backend temp_ext sampling test PASSED\n"); LOG_CNT("backend temp_ext sampling test PASSED\n");
} }
static void test_backend_min_p_sampling(const test_params & params) { static void test_backend_min_p_sampling(const test_params & params) {
@@ -600,20 +603,20 @@ static void test_backend_min_p_sampling(const test_params & params) {
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("min-p cpu sampled token id:%d, string: '%s'\n", token, token_str.c_str()); LOG_CNT("min-p cpu sampled token id:%d, string: '%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
// Decode and sample 10 more tokens // Decode and sample 10 more tokens
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
int32_t loop_idx = test_ctx.idx_for_seq(seq_id); int32_t loop_idx = test_ctx.idx_for_seq(seq_id);
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), loop_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), loop_idx);
printf("min-p gen step %d: token id :%5.d, string: %s\n", i, token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("min-p gen step %d: token id :%5.d, string: %s\n", i, token, test_ctx.token_to_piece(token, false).c_str());
if (!test_ctx.decode_token(token, 0)) { if (!test_ctx.decode_token(token, 0)) {
GGML_ASSERT(false && "Failed to decode token"); GGML_ASSERT(false && "Failed to decode token");
} }
} }
printf("min-p sampling test PASSED\n"); LOG_CNT("min-p sampling test PASSED\n");
} }
static void test_backend_top_p_sampling(const test_params & params) { static void test_backend_top_p_sampling(const test_params & params) {
@@ -652,18 +655,18 @@ static void test_backend_top_p_sampling(const test_params & params) {
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("top-p cpu sampled token id:%d, string: '%s'\n", token, token_str.c_str()); LOG_CNT("top-p cpu sampled token id:%d, string: '%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
// Decode and sample 10 more tokens // Decode and sample 10 more tokens
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
int32_t loop_idx = test_ctx.idx_for_seq(seq_id); int32_t loop_idx = test_ctx.idx_for_seq(seq_id);
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), loop_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), loop_idx);
printf("top-p gen step %d: token id :%5.d, string: %s\n", i, token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("top-p gen step %d: token id :%5.d, string: %s\n", i, token, test_ctx.token_to_piece(token, false).c_str());
test_ctx.decode_token(token, 0); test_ctx.decode_token(token, 0);
} }
printf("top-p sampling test PASSED\n"); LOG_CNT("top-p sampling test PASSED\n");
} }
static void test_backend_multi_sequence_sampling(const test_params & params) { static void test_backend_multi_sequence_sampling(const test_params & params) {
@@ -697,7 +700,7 @@ static void test_backend_multi_sequence_sampling(const test_params & params) {
int32_t batch_idx = test_ctx.idx_for_seq(0); int32_t batch_idx = test_ctx.idx_for_seq(0);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("Seq 0 sampled token id=%d, string='%s'\n", token, token_str.c_str()); LOG_CNT("Seq 0 sampled token id=%d, string='%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
@@ -706,12 +709,12 @@ static void test_backend_multi_sequence_sampling(const test_params & params) {
int32_t batch_idx= test_ctx.idx_for_seq(1); int32_t batch_idx= test_ctx.idx_for_seq(1);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("Seq 1 sampled token id=%d, string='%s'\n", token, token_str.c_str()); LOG_CNT("Seq 1 sampled token id=%d, string='%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
// Generate tokens for each sequence // Generate tokens for each sequence
printf("\nMulti-sequence generation:\n"); LOG_CNT("\nMulti-sequence generation:\n");
for (int step = 0; step < 4; step++) { for (int step = 0; step < 4; step++) {
std::map<llama_seq_id, llama_token> tokens; std::map<llama_seq_id, llama_token> tokens;
@@ -719,7 +722,7 @@ static void test_backend_multi_sequence_sampling(const test_params & params) {
int32_t idx = test_ctx.idx_for_seq(seq_id); int32_t idx = test_ctx.idx_for_seq(seq_id);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf(" Seq %d, step %d: token id=%d, string='%s'\n", seq_id, step, token, token_str.c_str()); LOG_CNT(" Seq %d, step %d: token id=%d, string='%s'\n", seq_id, step, token, token_str.c_str());
tokens[seq_id] = token; tokens[seq_id] = token;
} }
@@ -729,7 +732,7 @@ static void test_backend_multi_sequence_sampling(const test_params & params) {
} }
} }
printf("backend multi-sequence sampling test PASSED\n"); LOG_CNT("backend multi-sequence sampling test PASSED\n");
} }
static void test_backend_dist_sampling(const test_params & params) { static void test_backend_dist_sampling(const test_params & params) {
@@ -749,15 +752,15 @@ static void test_backend_dist_sampling(const test_params & params) {
int32_t batch_idx = test_ctx.idx_for_seq(seq_id); int32_t batch_idx = test_ctx.idx_for_seq(seq_id);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
printf("dist sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("dist sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
//GGML_ASSERT(llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx) == nullptr); //GGML_ASSERT(llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx) == nullptr);
token = llama_get_sampled_token_ith(test_ctx.ctx.get(), -1); token = llama_get_sampled_token_ith(test_ctx.ctx.get(), -1);
printf("dist sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str()); LOG_CNT("dist sampled id:%d, string:'%s'\n", token, test_ctx.token_to_piece(token, false).c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
printf("backend dist sampling test PASSED\n"); LOG_CNT("backend dist sampling test PASSED\n");
} }
static void test_backend_dist_sampling_and_cpu(const test_params & params) { static void test_backend_dist_sampling_and_cpu(const test_params & params) {
@@ -784,10 +787,10 @@ static void test_backend_dist_sampling_and_cpu(const test_params & params) {
llama_token backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
llama_token cpu_token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token cpu_token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
printf("dist & cpu sampled id:%d, string:'%s'\n", cpu_token, test_ctx.token_to_piece(cpu_token, false).c_str()); LOG_CNT("dist & cpu sampled id:%d, string:'%s'\n", cpu_token, test_ctx.token_to_piece(cpu_token, false).c_str());
GGML_ASSERT(backend_token == cpu_token); GGML_ASSERT(backend_token == cpu_token);
printf("backend dist & cpu sampling test PASSED\n"); LOG_CNT("backend dist & cpu sampling test PASSED\n");
} }
static void test_backend_logit_bias_sampling(const test_params & params) { static void test_backend_logit_bias_sampling(const test_params & params) {
@@ -809,7 +812,7 @@ static void test_backend_logit_bias_sampling(const test_params & params) {
//logit_bias.push_back({ bias_token, +100.0f }); //logit_bias.push_back({ bias_token, +100.0f });
logit_bias.push_back({ bias_token, +10.0f }); logit_bias.push_back({ bias_token, +10.0f });
printf("biasing token piece '%s' -> token id %d\n", piece.c_str(), bias_token); LOG_CNT("biasing token piece '%s' -> token id %d\n", piece.c_str(), bias_token);
struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params(); struct llama_sampler_chain_params backend_chain_params = llama_sampler_chain_default_params();
llama_sampler_ptr backend_sampler_chain(llama_sampler_chain_init(backend_chain_params)); llama_sampler_ptr backend_sampler_chain(llama_sampler_chain_init(backend_chain_params));
@@ -830,10 +833,11 @@ static void test_backend_logit_bias_sampling(const test_params & params) {
} }
llama_token backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), test_ctx.idx_for_seq(seq_id)); llama_token backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), test_ctx.idx_for_seq(seq_id));
printf("sampled token = %d, expected = %d\n", backend_token, bias_token); LOG_CNT("sampled token = %d, expected = %d\n", backend_token, bias_token);
common_log_flush(common_log_main()); // the assert below aborts
GGML_ASSERT(backend_token == bias_token); GGML_ASSERT(backend_token == bias_token);
printf("backend logit bias sampling test PASSED\n"); LOG_CNT("backend logit bias sampling test PASSED\n");
} }
static void accept_prompt(llama_sampler * smpl, const llama_vocab * vocab, const std::string & prompt) { static void accept_prompt(llama_sampler * smpl, const llama_vocab * vocab, const std::string & prompt) {
@@ -1014,7 +1018,7 @@ static sampler_comparison_stats compare_sampler_outputs(
const float logit = actual.logits[i]; const float logit = actual.logits[i];
if (!seen.insert(token).second || std::isnan(logit)) { if (!seen.insert(token).second || std::isnan(logit)) {
if (result.n_mismatch < 5) { if (result.n_mismatch < 5) {
printf("%s token %d has invalid backend output\n", name, token); LOG_CNT("%s token %d has invalid backend output\n", name, token);
} }
++result.n_mismatch; ++result.n_mismatch;
continue; continue;
@@ -1026,7 +1030,7 @@ static sampler_comparison_stats compare_sampler_outputs(
++result.n_masked; ++result.n_masked;
} else if (!allow_extra_candidates) { } else if (!allow_extra_candidates) {
if (result.n_mismatch < 5) { if (result.n_mismatch < 5) {
printf("%s token %d was not masked\n", name, token); LOG_CNT("%s token %d was not masked\n", name, token);
} }
++result.n_mismatch; ++result.n_mismatch;
} }
@@ -1037,8 +1041,8 @@ static sampler_comparison_stats compare_sampler_outputs(
result.max_diff = std::max(result.max_diff, diff); result.max_diff = std::max(result.max_diff, diff);
if (!std::isfinite(logit) || diff > 1e-3f) { if (!std::isfinite(logit) || diff > 1e-3f) {
if (result.n_mismatch < 5) { if (result.n_mismatch < 5) {
printf("%s mismatch token %d: cpu=%.6f backend=%.6f diff=%.6f\n", LOG_CNT("%s mismatch token %d: cpu=%.6f backend=%.6f diff=%.6f\n",
name, token, it->second, logit, diff); name, token, it->second, logit, diff);
} }
++result.n_mismatch; ++result.n_mismatch;
} }
@@ -1047,14 +1051,15 @@ static sampler_comparison_stats compare_sampler_outputs(
for (const auto & item : expected) { for (const auto & item : expected) {
if (seen.find(item.first) == seen.end()) { if (seen.find(item.first) == seen.end()) {
if (result.n_mismatch < 5) { if (result.n_mismatch < 5) {
printf("%s missing backend token %d\n", name, item.first); LOG_CNT("%s missing backend token %d\n", name, item.first);
} }
++result.n_mismatch; ++result.n_mismatch;
} }
} }
printf("%s logits: max_diff=%.6f n_masked=%d n_mismatch=%d\n", LOG_CNT("%s logits: max_diff=%.6f n_masked=%d n_mismatch=%d\n",
name, result.max_diff, result.n_masked, result.n_mismatch); name, result.max_diff, result.n_masked, result.n_mismatch);
common_log_flush(common_log_main()); // the callers abort right after this on a mismatch
return result; return result;
} }
@@ -1311,10 +1316,10 @@ static void compare_masking_penalties_logits(
} }
static void test_backend_penalties_sampling(const test_params & params) { static void test_backend_penalties_sampling(const test_params & params) {
printf("Testing backend penalties (repeat + freq + presence)\n"); LOG_CNT("Testing backend penalties (repeat + freq + presence)\n");
compare_penalties_logits(params, 64, 1.1f, 0.5f, 0.25f, "Hello Hello world"); compare_penalties_logits(params, 64, 1.1f, 0.5f, 0.25f, "Hello Hello world");
printf("Testing backend penalties with penalty_last_n > 64\n"); LOG_CNT("Testing backend penalties with penalty_last_n > 64\n");
const auto * vocab = llama_model_get_vocab(params.model.get()); const auto * vocab = llama_model_get_vocab(params.model.get());
std::vector<llama_token> tokens(8); std::vector<llama_token> tokens(8);
int32_t n_tok = llama_tokenize(vocab, "a", 1, tokens.data(), (int32_t) tokens.size(), false, false); int32_t n_tok = llama_tokenize(vocab, "a", 1, tokens.data(), (int32_t) tokens.size(), false, false);
@@ -1332,52 +1337,52 @@ static void test_backend_penalties_sampling(const test_params & params) {
} }
}); });
printf("Testing backend penalties without filler entries\n"); LOG_CNT("Testing backend penalties without filler entries\n");
compare_penalties_logits(params, 64, 1.1f, 0.5f, 0.25f, "Hello", [](llama_sampler * smpl) { compare_penalties_logits(params, 64, 1.1f, 0.5f, 0.25f, "Hello", [](llama_sampler * smpl) {
for (llama_token token = 0; token < 64; ++token) { for (llama_token token = 0; token < 64; ++token) {
llama_sampler_accept(smpl, token); llama_sampler_accept(smpl, token);
} }
}); });
printf("Testing backend top-k followed by penalties\n"); LOG_CNT("Testing backend top-k followed by penalties\n");
compare_top_k_penalties_logits(params, 8, 64, 1.1f, 0.5f, 0.25f, "Hello", compare_top_k_penalties_logits(params, 8, 64, 1.1f, 0.5f, 0.25f, "Hello",
penalties_position::after_filter); penalties_position::after_filter);
printf("Testing backend penalties followed by top-k\n"); LOG_CNT("Testing backend penalties followed by top-k\n");
compare_top_k_penalties_logits(params, 8, 64, 1.1f, 0.5f, 0.25f, "Hello", compare_top_k_penalties_logits(params, 8, 64, 1.1f, 0.5f, 0.25f, "Hello",
penalties_position::before_filter); penalties_position::before_filter);
printf("Testing backend top-p followed by penalties\n"); LOG_CNT("Testing backend top-p followed by penalties\n");
compare_masking_penalties_logits(params, "top-p", []() { compare_masking_penalties_logits(params, "top-p", []() {
return llama_sampler_init_top_p(0.9f, 0); return llama_sampler_init_top_p(0.9f, 0);
}, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, true); }, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, true);
printf("Testing backend top-p followed by penalties with a large history window\n"); LOG_CNT("Testing backend top-p followed by penalties with a large history window\n");
compare_masking_penalties_logits(params, "top-p large-window", []() { compare_masking_penalties_logits(params, "top-p large-window", []() {
return llama_sampler_init_top_p(0.9f, 0); return llama_sampler_init_top_p(0.9f, 0);
}, 4096, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, true); }, 4096, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, true);
printf("Testing backend penalties followed by top-p\n"); LOG_CNT("Testing backend penalties followed by top-p\n");
compare_masking_penalties_logits(params, "top-p", []() { compare_masking_penalties_logits(params, "top-p", []() {
return llama_sampler_init_top_p(0.9f, 0); return llama_sampler_init_top_p(0.9f, 0);
}, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::before_filter, true); }, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::before_filter, true);
printf("Testing backend min-p followed by penalties\n"); LOG_CNT("Testing backend min-p followed by penalties\n");
compare_masking_penalties_logits(params, "min-p", []() { compare_masking_penalties_logits(params, "min-p", []() {
return llama_sampler_init_min_p(0.1f, 0); return llama_sampler_init_min_p(0.1f, 0);
}, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, false); }, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, false);
printf("Testing backend penalties followed by min-p\n"); LOG_CNT("Testing backend penalties followed by min-p\n");
compare_masking_penalties_logits(params, "min-p", []() { compare_masking_penalties_logits(params, "min-p", []() {
return llama_sampler_init_min_p(0.1f, 0); return llama_sampler_init_min_p(0.1f, 0);
}, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::before_filter, false); }, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::before_filter, false);
printf("Testing backend top-p followed by penalties with empty history\n"); LOG_CNT("Testing backend top-p followed by penalties with empty history\n");
compare_masking_penalties_logits(params, "top-p empty", []() { compare_masking_penalties_logits(params, "top-p empty", []() {
return llama_sampler_init_top_p(0.9f, 0); return llama_sampler_init_top_p(0.9f, 0);
}, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, true, false); }, 64, 1.1f, 0.5f, 0.25f, "Hello", penalties_position::after_filter, true, false);
printf("Testing backend top-p followed by individual penalties\n"); LOG_CNT("Testing backend top-p followed by individual penalties\n");
compare_masking_penalties_logits(params, "top-p repeat", []() { compare_masking_penalties_logits(params, "top-p repeat", []() {
return llama_sampler_init_top_p(0.9f, 0); return llama_sampler_init_top_p(0.9f, 0);
}, 64, 1.1f, 0.0f, 0.0f, "Hello", penalties_position::after_filter, true); }, 64, 1.1f, 0.0f, 0.0f, "Hello", penalties_position::after_filter, true);
@@ -1388,10 +1393,10 @@ static void test_backend_penalties_sampling(const test_params & params) {
return llama_sampler_init_top_p(0.9f, 0); return llama_sampler_init_top_p(0.9f, 0);
}, 64, 1.0f, 0.0f, 0.25f, "Hello", penalties_position::after_filter, true); }, 64, 1.0f, 0.0f, 0.25f, "Hello", penalties_position::after_filter, true);
printf("Testing backend penalty parameter values\n"); LOG_CNT("Testing backend penalty parameter values\n");
test_penalty_parameter_values(params); test_penalty_parameter_values(params);
printf("backend penalties sampling test PASSED\n"); LOG_CNT("backend penalties sampling test PASSED\n");
} }
// This test verifies that it is possible to have two different backend samplers, // This test verifies that it is possible to have two different backend samplers,
@@ -1427,7 +1432,7 @@ static void test_backend_mixed_sampling(const test_params & params) {
int32_t batch_idx = test_ctx.idx_for_seq(0); int32_t batch_idx = test_ctx.idx_for_seq(0);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("sampled token id=%d, string='%s'\n", token, token_str.c_str()); LOG_CNT("sampled token id=%d, string='%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
//GGML_ASSERT(llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx) == nullptr); //GGML_ASSERT(llama_get_sampled_logits_ith(test_ctx.ctx.get(), batch_idx) == nullptr);
//GGML_ASSERT(llama_get_sampled_logits_count_ith(test_ctx.ctx.get(), batch_idx) == 0); //GGML_ASSERT(llama_get_sampled_logits_count_ith(test_ctx.ctx.get(), batch_idx) == 0);
@@ -1443,7 +1448,7 @@ static void test_backend_mixed_sampling(const test_params & params) {
GGML_ASSERT(llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx) == LLAMA_TOKEN_NULL); GGML_ASSERT(llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx) == LLAMA_TOKEN_NULL);
} }
printf("backend mixed sampling test PASSED\n"); LOG_CNT("backend mixed sampling test PASSED\n");
} }
static void test_backend_set_sampler(const test_params & params) { static void test_backend_set_sampler(const test_params & params) {
@@ -1466,11 +1471,11 @@ static void test_backend_set_sampler(const test_params & params) {
// Sample using backend sampler configured above // Sample using backend sampler configured above
llama_token backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
const std::string backend_token_str = test_ctx.token_to_piece(backend_token, false); const std::string backend_token_str = test_ctx.token_to_piece(backend_token, false);
printf("dist sampled token = %d, string='%s'\n", backend_token, backend_token_str.c_str()); LOG_CNT("dist sampled token = %d, string='%s'\n", backend_token, backend_token_str.c_str());
// Now clear the backend sampler for this sequence. // Now clear the backend sampler for this sequence.
llama_set_sampler(test_ctx.ctx.get(), seq_id, nullptr); llama_set_sampler(test_ctx.ctx.get(), seq_id, nullptr);
printf("Cleared backend sampler for seq_id %d\n", seq_id); LOG_CNT("Cleared backend sampler for seq_id %d\n", seq_id);
// Sample using CPU sampler // Sample using CPU sampler
struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params(); struct llama_sampler_chain_params chain_params = llama_sampler_chain_default_params();
@@ -1490,7 +1495,7 @@ static void test_backend_set_sampler(const test_params & params) {
// Sample the token using the CPU sampler chain. // Sample the token using the CPU sampler chain.
llama_token token2 = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), seq_id); llama_token token2 = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), seq_id);
const std::string token2_str = test_ctx.token_to_piece(token2, false); const std::string token2_str = test_ctx.token_to_piece(token2, false);
printf("CPU sampled token after clearing backend sampler: id=%d, string='%s'\n", token2, token2_str.c_str()); LOG_CNT("CPU sampled token after clearing backend sampler: id=%d, string='%s'\n", token2, token2_str.c_str());
std::map<llama_seq_id, llama_token> tokens2 = { { seq_id, token2}, }; std::map<llama_seq_id, llama_token> tokens2 = { { seq_id, token2}, };
// Set a new backend sampler for the sequence. // Set a new backend sampler for the sequence.
@@ -1506,9 +1511,9 @@ static void test_backend_set_sampler(const test_params & params) {
llama_token new_backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), test_ctx.idx_for_seq(seq_id)); llama_token new_backend_token = llama_get_sampled_token_ith(test_ctx.ctx.get(), test_ctx.idx_for_seq(seq_id));
const std::string new_backend_token_str = test_ctx.token_to_piece(new_backend_token, false); const std::string new_backend_token_str = test_ctx.token_to_piece(new_backend_token, false);
printf("dist sampled token = %d, string='%s'\n", new_backend_token, new_backend_token_str.c_str()); LOG_CNT("dist sampled token = %d, string='%s'\n", new_backend_token, new_backend_token_str.c_str());
printf("backend set sampler test PASSED\n"); LOG_CNT("backend set sampler test PASSED\n");
} }
static void test_backend_cpu_mixed_batch(const test_params & params) { static void test_backend_cpu_mixed_batch(const test_params & params) {
@@ -1538,7 +1543,7 @@ static void test_backend_cpu_mixed_batch(const test_params & params) {
int32_t batch_idx = test_ctx.idx_for_seq(0); int32_t batch_idx = test_ctx.idx_for_seq(0);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("Seq 0 (backend) sampled token id=%d, string='%s'\n", token, token_str.c_str()); LOG_CNT("Seq 0 (backend) sampled token id=%d, string='%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
@@ -1555,7 +1560,7 @@ static void test_backend_cpu_mixed_batch(const test_params & params) {
llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx); llama_token token = llama_sampler_sample(chain.get(), test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("Seq 1 (CPU) sampled token id=%d, string='%s'\n", token, token_str.c_str()); LOG_CNT("Seq 1 (CPU) sampled token id=%d, string='%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
@@ -1592,11 +1597,11 @@ static void test_backend_cpu_mixed_batch(const test_params & params) {
int32_t batch_idx = test_ctx.idx_for_seq(0); int32_t batch_idx = test_ctx.idx_for_seq(0);
llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx); llama_token token = llama_get_sampled_token_ith(test_ctx.ctx.get(), batch_idx);
const std::string token_str = test_ctx.token_to_piece(token, false); const std::string token_str = test_ctx.token_to_piece(token, false);
printf("re-added backend sampled token id=%d, string='%s'\n", token, token_str.c_str()); LOG_CNT("re-added backend sampled token id=%d, string='%s'\n", token, token_str.c_str());
GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab); GGML_ASSERT(token >= 0 && token < test_ctx.n_vocab);
} }
printf("backend-cpu mixed batch test PASSED\n"); LOG_CNT("backend-cpu mixed batch test PASSED\n");
} }
static void test_backend_multi_output_limit(const test_params & params) { static void test_backend_multi_output_limit(const test_params & params) {
@@ -1612,14 +1617,14 @@ static void test_backend_multi_output_limit(const test_params & params) {
common_batch_add(batch, llama_vocab_bos(test_ctx.vocab), i, { seq_id }, true); common_batch_add(batch, llama_vocab_bos(test_ctx.vocab), i, { seq_id }, true);
} }
printf(">>> test_backend_multi_output_limit expected error start:\n"); LOG_CNT(">>> test_backend_multi_output_limit expected error start:\n");
const int ret = llama_decode(test_ctx.ctx.get(), batch); const int ret = llama_decode(test_ctx.ctx.get(), batch);
GGML_ASSERT(ret != 0 && "llama_decode should reject outputs above the per-sequence limit"); GGML_ASSERT(ret != 0 && "llama_decode should reject outputs above the per-sequence limit");
printf("<<< test_backend_multi_output_limit expected error end.\n"); LOG_CNT("<<< test_backend_multi_output_limit expected error end.\n");
llama_batch_free(batch); llama_batch_free(batch);
printf("backend multi-output limit test PASSED\n"); LOG_CNT("backend multi-output limit test PASSED\n");
} }
static void test_backend_multi_sequence_multi_output_dist(const test_params & params) { static void test_backend_multi_sequence_multi_output_dist(const test_params & params) {
@@ -1708,7 +1713,7 @@ static void test_backend_multi_sequence_multi_output_dist(const test_params & pa
llama_batch_free(batch); llama_batch_free(batch);
printf("backend multi-sequence multi-output dist test PASSED\n"); LOG_CNT("backend multi-sequence multi-output dist test PASSED\n");
} }
static void test_backend_multi_output_dist_transaction(const test_params & params) { static void test_backend_multi_output_dist_transaction(const test_params & params) {
@@ -1778,7 +1783,7 @@ static void test_backend_multi_output_dist_transaction(const test_params & param
verify_random(0, randoms[2]); verify_random(0, randoms[2]);
llama_batch_free(batch); llama_batch_free(batch);
printf("backend multi-output dist transaction test PASSED\n"); LOG_CNT("backend multi-output dist transaction test PASSED\n");
} }
static void test_backend_multi_output_sampling_chain(const test_params & params) { static void test_backend_multi_output_sampling_chain(const test_params & params) {
@@ -1928,7 +1933,7 @@ static void test_backend_multi_output_sampling_chain(const test_params & params)
GGML_ASSERT(llama_decode(test_ctx.ctx.get(), batch) == 0); GGML_ASSERT(llama_decode(test_ctx.ctx.get(), batch) == 0);
llama_batch_free(batch); llama_batch_free(batch);
printf("backend multi-output sampling chain test PASSED\n"); LOG_CNT("backend multi-output sampling chain test PASSED\n");
} }
static void test_backend_multi_output_cpu_suffix(const test_params & params) { static void test_backend_multi_output_cpu_suffix(const test_params & params) {
@@ -1990,7 +1995,7 @@ static void test_backend_multi_output_cpu_suffix(const test_params & params) {
llama_batch_free(batch); llama_batch_free(batch);
} }
printf("backend multi-output CPU suffix test PASSED\n"); LOG_CNT("backend multi-output CPU suffix test PASSED\n");
} }
struct backend_test_case { struct backend_test_case {
@@ -2029,7 +2034,8 @@ static test_args parse_cli(int argc, char ** argv) {
if (std::strcmp(arg, "--test") == 0) { if (std::strcmp(arg, "--test") == 0) {
if (i + 1 >= argc) { if (i + 1 >= argc) {
fprintf(stderr, "--test expects a value\n"); LOG_ERR("--test expects a value\n");
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
out.test = argv[++i]; out.test = argv[++i];
@@ -2041,7 +2047,8 @@ static test_args parse_cli(int argc, char ** argv) {
} }
if (std::strcmp(arg, "--model") == 0) { if (std::strcmp(arg, "--model") == 0) {
if (i + 1 >= argc) { if (i + 1 >= argc) {
fprintf(stderr, "--model expects a value\n"); LOG_ERR("--model expects a value\n");
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
out.model = argv[++i]; out.model = argv[++i];
@@ -2053,7 +2060,8 @@ static test_args parse_cli(int argc, char ** argv) {
} }
if (std::strcmp(arg, "--device") == 0) { if (std::strcmp(arg, "--device") == 0) {
if (i + 1 >= argc) { if (i + 1 >= argc) {
fprintf(stderr, "--device expects a value (cpu or gpu)\n"); LOG_ERR("--device expects a value (cpu or gpu)\n");
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
out.device = argv[++i]; out.device = argv[++i];
@@ -2068,12 +2076,14 @@ static test_args parse_cli(int argc, char ** argv) {
continue; continue;
} }
fprintf(stderr, "Unexpected argument: %s\n", arg); LOG_ERR("Unexpected argument: %s\n", arg);
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (out.device != "cpu" && out.device != "gpu" && out.device != "auto") { if (out.device != "cpu" && out.device != "gpu" && out.device != "auto") {
fprintf(stderr, "Invalid device '%s'. Must be 'cpu', 'gpu' or 'auto'\n", out.device.c_str()); LOG_ERR("Invalid device '%s'. Must be 'cpu', 'gpu' or 'auto'\n", out.device.c_str());
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -2091,10 +2101,12 @@ static std::vector<const backend_test_case *> collect_tests_to_run(const std::st
} }
} }
if (selected.empty()) { if (selected.empty()) {
fprintf(stderr, "Unknown test '%s'. Available tests:\n", requested.c_str()); LOG_ERR("Unknown test '%s'. Available tests:\n", requested.c_str());
for (const auto & test : BACKEND_TESTS) { for (const auto & test : BACKEND_TESTS) {
fprintf(stderr, " %s\n", test.name.c_str()); LOG_ERR(" %s\n", test.name.c_str());
} }
LOG("%s: %s\n", "test-backend-sampler", "FAILED");
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else { } else {
@@ -2108,7 +2120,7 @@ static std::vector<const backend_test_case *> collect_tests_to_run(const std::st
test.name == "mixed" || test.name == "top_p" || test.name == "mixed" || test.name == "top_p" ||
test.name == "multi_output_sampling_chain" || test.name == "multi_output_sampling_chain" ||
test.name == "multi_output_cpu") { test.name == "multi_output_cpu") {
fprintf(stderr, "Skipping test '%s' on HIP backend (no backend TOP_K support)\n", test.name.c_str()); LOG_WRN("Skipping test '%s' on HIP backend (no backend TOP_K support)\n", test.name.c_str());
continue; continue;
} }
#endif // GGML_USE_HIP #endif // GGML_USE_HIP
@@ -2117,7 +2129,7 @@ static std::vector<const backend_test_case *> collect_tests_to_run(const std::st
} }
if (selected.empty()) { if (selected.empty()) {
fprintf(stderr, "No backend sampling tests selected. Use --test=<name> to pick one.\n"); LOG_WRN("No backend sampling tests selected. Use --test=<name> to pick one.\n");
} }
return selected; return selected;
@@ -2125,32 +2137,75 @@ static std::vector<const backend_test_case *> collect_tests_to_run(const std::st
static void run_tests(const std::vector<const backend_test_case *> & tests, const test_params & args) { static void run_tests(const std::vector<const backend_test_case *> & tests, const test_params & args) {
for (const auto & test : tests) { for (const auto & test : tests) {
fprintf(stderr, "\n=== %s ===\n", test->name.c_str()); LOG_INF("\n=== %s ===\n", test->name.c_str());
try { try {
test->fn(args); test->fn(args);
} catch (const std::exception & e) { } catch (const std::exception & e) {
fprintf(stderr, "Error running test '%s': %s\n", test->name.c_str(), e.what()); LOG_ERR("Error running test '%s': %s\n", test->name.c_str(), e.what());
LOG("%s: %s\n", "test-backend-sampler", "FAILED");
common_log_flush(common_log_main());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
test_args args = parse_cli(argc, argv); test_args args;
{
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// the test's own options and the model path are handled here, everything else goes to common_params_parse
std::vector<char *> test_argv;
std::vector<char *> common_argv;
test_argv.push_back(argv[0]);
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--test") == 0 || strcmp(argv[i], "--model") == 0 || strcmp(argv[i], "--device") == 0) {
test_argv.push_back(argv[i]);
if (i + 1 < argc) {
test_argv.push_back(argv[++i]); // the option value
}
continue;
}
if (strncmp(argv[i], "--test=", 7) == 0 || strncmp(argv[i], "--model=", 8) == 0 || strncmp(argv[i], "--device=", 9) == 0) {
test_argv.push_back(argv[i]);
continue;
}
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
test_argv.push_back(argv[i]); // the model path
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
test_argv.push_back(nullptr);
args = parse_cli((int) test_argv.size() - 1, test_argv.data());
}
if (args.model.empty()) { if (args.model.empty()) {
args.model = common_get_model_or_exit(1, argv); args.model = common_get_model_or_exit(1, argv);
} }
LOG("%s: running\n", "test-backend-sampler");
{ {
std::ifstream file(args.model); std::ifstream file(args.model);
if (!file.is_open()) { if (!file.is_open()) {
fprintf(stderr, "no model '%s' found\n", args.model.c_str()); LOG_ERR("no model '%s' found\n", args.model.c_str());
LOG("%s: %s\n", "test-backend-sampler", "FAILED");
common_log_flush(common_log_main());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
fprintf(stderr, "using '%s'\n", args.model.c_str()); LOG_INF("using '%s'\n", args.model.c_str());
llama_backend_init(); llama_backend_init();
@@ -2163,5 +2218,7 @@ int main(int argc, char ** argv) {
run_tests(tests, params); run_tests(tests, params);
} }
LOG("%s: %s\n", "test-backend-sampler", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+56 -28
View File
@@ -1,11 +1,15 @@
#include "ggml.h" #include "ggml.h"
#include "ggml-cpu.h" #include "ggml-cpu.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <chrono> #include <chrono>
#include <iostream>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cassert> #include <cassert>
#include <string>
#include <vector> #include <vector>
#include <thread> #include <thread>
@@ -40,7 +44,8 @@ static void test_barrier(int n_threads, int n_rounds) {
struct ggml_threadpool_params tpp = ggml_threadpool_params_default(n_threads); struct ggml_threadpool_params tpp = ggml_threadpool_params_default(n_threads);
struct ggml_threadpool* threadpool = ggml_threadpool_new(&tpp); struct ggml_threadpool* threadpool = ggml_threadpool_new(&tpp);
if (!threadpool) { if (!threadpool) {
fprintf(stderr, "threadpool create failed : n_threads %d\n", n_threads); LOG_ERR("threadpool create failed : n_threads %d\n", n_threads);
common_log_flush(common_log_main());
exit(1); exit(1);
} }
@@ -50,11 +55,10 @@ static void test_barrier(int n_threads, int n_rounds) {
std::vector<uint8_t> work_data(cplan.work_size); std::vector<uint8_t> work_data(cplan.work_size);
cplan.work_data = work_data.data(); cplan.work_data = work_data.data();
std::cerr << "graph-compute with" LOG_INF("graph-compute with"
<< "\n n_threads: " << n_threads "\n n_threads: %d"
<< "\n n_nodes: " << n_nodes "\n n_nodes: %d"
<< "\n n_rounds: " << n_rounds "\n n_rounds: %d\n", n_threads, n_nodes, n_rounds);
<< "\n";
// ggml_graph_print(gf); // ggml_graph_print(gf);
// Warmup // Warmup
@@ -70,10 +74,10 @@ static void test_barrier(int n_threads, int n_rounds) {
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(t1-t0).count(); auto usec = std::chrono::duration_cast<std::chrono::microseconds>(t1-t0).count();
auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(t1-t0).count(); auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(t1-t0).count();
std::cerr << "graph-compute took " << usec << " usec " LOG_INF("graph-compute took %lld usec "
<< "\n " << (float) usec / n_rounds << " usec per-iter" "\n %g usec per-iter"
<< "\n " << (float) nsec / (n_rounds * n_nodes) << " nsec per-node" "\n %g nsec per-node\n",
<< "\n"; (long long) usec, (float) usec / n_rounds, (float) nsec / (n_rounds * n_nodes));
ggml_threadpool_free(threadpool); ggml_threadpool_free(threadpool);
ggml_free(ctx); ggml_free(ctx);
@@ -108,15 +112,15 @@ static void test_active(int n_threads, int n_rounds) {
struct ggml_threadpool_params tpp = ggml_threadpool_params_default(n_threads); struct ggml_threadpool_params tpp = ggml_threadpool_params_default(n_threads);
struct ggml_threadpool* threadpool = ggml_threadpool_new(&tpp); struct ggml_threadpool* threadpool = ggml_threadpool_new(&tpp);
if (!threadpool) { if (!threadpool) {
fprintf(stderr, "threadpool create failed : n_threads %d\n", n_threads); LOG_ERR("threadpool create failed : n_threads %d\n", n_threads);
common_log_flush(common_log_main());
exit(1); exit(1);
} }
std::cerr << "graph-compute with" LOG_INF("graph-compute with"
<< "\n n_threads: " << n_threads "\n n_threads: %d"
<< "\n n_nodes: " << n_nodes "\n n_nodes: %d"
<< "\n n_rounds: " << n_rounds "\n n_rounds: %d\n", n_threads, n_nodes, n_rounds);
<< "\n";
// ggml_graph_print(gf); // ggml_graph_print(gf);
// In this test we keep changing the number of threads every 4th iteration // In this test we keep changing the number of threads every 4th iteration
@@ -181,16 +185,17 @@ static void test_multi_graph(int n_threads, int n_rounds) {
struct ggml_threadpool_params tpp = ggml_threadpool_params_default(n_threads); struct ggml_threadpool_params tpp = ggml_threadpool_params_default(n_threads);
struct ggml_threadpool* threadpool = ggml_threadpool_new(&tpp); struct ggml_threadpool* threadpool = ggml_threadpool_new(&tpp);
if (!threadpool) { if (!threadpool) {
fprintf(stderr, "threadpool create failed : n_threads %d\n", n_threads); LOG_ERR("threadpool create failed : n_threads %d\n", n_threads);
common_log_flush(common_log_main());
exit(1); exit(1);
} }
std::cerr << "graph-compute with" LOG_INF("graph-compute with"
<< "\n gf0 n_nodes: " << ggml_graph_n_nodes(gf0) "\n gf0 n_nodes: %d"
<< "\n gf1 n_nodes: " << ggml_graph_n_nodes(gf1) "\n gf1 n_nodes: %d"
<< "\n n_threads: " << n_threads "\n n_threads: %d"
<< "\n n_rounds: " << n_rounds "\n n_rounds: %d\n",
<< "\n"; ggml_graph_n_nodes(gf0), ggml_graph_n_nodes(gf1), n_threads, n_rounds);
// In this test we keep changing the number of threads every 4th iteration // In this test we keep changing the number of threads every 4th iteration
// and we compute two graphs back to back to test graph frequent graph switching // and we compute two graphs back to back to test graph frequent graph switching
@@ -214,23 +219,46 @@ static void test_multi_graph(int n_threads, int n_rounds) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// this test takes n_threads and n_rounds as positional arguments
std::vector<std::string> positional;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
positional.push_back(argv[i]);
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
int n_threads = std::max(1, std::min(4, (int) std::thread::hardware_concurrency())); int n_threads = std::max(1, std::min(4, (int) std::thread::hardware_concurrency()));
int n_rounds = 100; int n_rounds = 100;
if (argc > 1) { if (positional.size() > 0) {
n_threads = std::atoi(argv[1]); n_threads = std::atoi(positional[0].c_str());
} }
if (argc > 2) { if (positional.size() > 1) {
n_rounds = std::atoi(argv[2]); n_rounds = std::atoi(positional[1].c_str());
} }
LOG("%s: running\n", "test-barrier");
test_barrier(n_threads, n_rounds); test_barrier(n_threads, n_rounds);
test_active(n_threads, n_rounds * 100); test_active(n_threads, n_rounds * 100);
test_multi_graph(n_threads, n_rounds * 10); test_multi_graph(n_threads, n_rounds * 10);
LOG("%s: %s\n", "test-barrier", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+30 -3
View File
@@ -1,6 +1,8 @@
#include "arg.h"
#include "testing.h" #include "testing.h"
#include "llama.h" #include "llama.h"
#include "log.h"
#include "../src/llama-batch.h" #include "../src/llama-batch.h"
#include "../src/llama-memory.h" #include "../src/llama-memory.h"
@@ -651,6 +653,28 @@ static void test_mrope(testing & t) {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// this test takes an optional filter as its only positional argument
std::string filter;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (filter.empty()) {
filter = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-batch-alloc");
testing t; testing t;
const char * verbose = getenv("LLAMA_TEST_VERBOSE"); const char * verbose = getenv("LLAMA_TEST_VERBOSE");
@@ -661,8 +685,8 @@ int main(int argc, char ** argv) {
llama_log_set([](ggml_log_level, const char *, void *) {}, nullptr); llama_log_set([](ggml_log_level, const char *, void *) {}, nullptr);
} }
if (argc > 1) { if (!filter.empty()) {
t.set_filter(argv[1]); t.set_filter(filter);
} }
t.test("init", test_init); t.test("init", test_init);
@@ -670,5 +694,8 @@ int main(int argc, char ** argv) {
t.test("keep_tail", test_keep_tail); t.test("keep_tail", test_keep_tail);
t.test("mrope", test_mrope); t.test("mrope", test_mrope);
return t.summary(); const int rc = t.summary();
LOG("%s: %s\n", "test-batch-alloc", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
+5 -1
View File
@@ -1,3 +1,7 @@
#include "llama.h" #include "llama.h"
int main(void) {} int main(void) {
printf("test-c: running\n");
printf("test-c: PASSED\n");
return 0;
}
+87 -49
View File
@@ -1,6 +1,8 @@
#include "chat-auto-parser.h" #include "chat-auto-parser.h"
#include "chat-auto-parser-helpers.h" #include "chat-auto-parser-helpers.h"
#include "chat.h" #include "chat.h"
#include "arg.h"
#include "common.h"
#include "log.h" #include "log.h"
#include "jinja/caps.h" #include "jinja/caps.h"
#include "jinja/runtime.h" #include "jinja/runtime.h"
@@ -84,16 +86,16 @@ static std::string read_file(const std::string & path) {
} }
static void print_usage(const char * program_name) { static void print_usage(const char * program_name) {
LOG_ERR("Debug the auto-parser's differential analysis: render a template with/without tools, reasoning, etc. and show the diffs.\n"); LOG_INF("Debug the auto-parser's differential analysis: render a template with/without tools, reasoning, etc. and show the diffs.\n");
LOG_ERR("\nUsage: %s [options]\n", program_name); LOG_INF("\nUsage: %s [options]\n", program_name);
LOG_ERR("\nOptions:\n"); LOG_INF("\nOptions:\n");
LOG_ERR(" --template <name> Analyze specific template from test suite (e.g., 'deepseek' or 'DeepSeek-V3.1')\n"); LOG_INF(" --template <name> Analyze specific template from test suite (e.g., 'deepseek' or 'DeepSeek-V3.1')\n");
LOG_ERR(" --template-file <path> Analyze custom template file\n"); LOG_INF(" --template-file <path> Analyze custom template file\n");
LOG_ERR(" --all Analyze all templates from test suite (default when no arguments are given)\n"); LOG_INF(" --all Analyze all templates from test suite (default when no arguments are given)\n");
LOG_ERR("\nExamples:\n"); LOG_INF("\nExamples:\n");
LOG_ERR(" %s --all\n", program_name); LOG_INF(" %s --all\n", program_name);
LOG_ERR(" %s --template deepseek\n", program_name); LOG_INF(" %s --template deepseek\n", program_name);
LOG_ERR(" %s --template-file my-template.jinja\n", program_name); LOG_INF(" %s --template-file my-template.jinja\n", program_name);
} }
static bool parse_options(int argc, char ** argv, analysis_options & opts) { static bool parse_options(int argc, char ** argv, analysis_options & opts) {
@@ -270,15 +272,15 @@ static json make_assistant_one_tool_with_reasoning() {
} }
static void print_diff_split(const std::string & title, const diff_split & diff) { static void print_diff_split(const std::string & title, const diff_split & diff) {
LOG_ERR("\n%s=== %s ===%s\n", ANSI_CYAN, title.c_str(), ANSI_RESET); LOG_INF("\n%s=== %s ===%s\n", ANSI_CYAN, title.c_str(), ANSI_RESET);
LOG_ERR("%sCommon Prefix:%s '%s'\n", ANSI_PREFIX, ANSI_RESET, diff.prefix.c_str()); LOG_INF("%sCommon Prefix:%s '%s'\n", ANSI_PREFIX, ANSI_RESET, diff.prefix.c_str());
LOG_ERR("%sCommon Suffix:%s '%s'\n", ANSI_SUFFIX, ANSI_RESET, diff.suffix.c_str()); LOG_INF("%sCommon Suffix:%s '%s'\n", ANSI_SUFFIX, ANSI_RESET, diff.suffix.c_str());
LOG_ERR("%sLeft (difference):%s '%s'\n", ANSI_GREEN, ANSI_RESET, diff.left.c_str()); LOG_INF("%sLeft (difference):%s '%s'\n", ANSI_GREEN, ANSI_RESET, diff.left.c_str());
LOG_ERR("%sRight (difference):%s '%s'\n", ANSI_ORANGE, ANSI_RESET, diff.right.c_str()); LOG_INF("%sRight (difference):%s '%s'\n", ANSI_ORANGE, ANSI_RESET, diff.right.c_str());
} }
static void check_reasoning_variables(const common_chat_template & tmpl) { static void check_reasoning_variables(const common_chat_template & tmpl) {
LOG_ERR("\n%s=== Checking Reasoning Variables ===%s\n", ANSI_CYAN, ANSI_RESET); LOG_INF("\n%s=== Checking Reasoning Variables ===%s\n", ANSI_CYAN, ANSI_RESET);
try { try {
// Create a list of candidate reasoning/thinking variable names to probe // Create a list of candidate reasoning/thinking variable names to probe
@@ -355,11 +357,11 @@ static void check_reasoning_variables(const common_chat_template & tmpl) {
} }
if (accessed_vars.empty()) { if (accessed_vars.empty()) {
LOG_ERR("%sNo reasoning/thinking-related variables were queried by the template%s\n", ANSI_GRAY, ANSI_RESET); LOG_INF("%sNo reasoning/thinking-related variables were queried by the template%s\n", ANSI_GRAY, ANSI_RESET);
} else { } else {
LOG_ERR("Template queries the following reasoning/thinking-related variables:\n"); LOG_INF("Template queries the following reasoning/thinking-related variables:\n");
for (const auto & var : accessed_vars) { for (const auto & var : accessed_vars) {
LOG_ERR(" %s- %s%s\n", ANSI_ORANGE, var.c_str(), ANSI_RESET); LOG_INF(" %s- %s%s\n", ANSI_ORANGE, var.c_str(), ANSI_RESET);
} }
} }
@@ -369,12 +371,12 @@ static void check_reasoning_variables(const common_chat_template & tmpl) {
} }
static void analyze_template(const std::string & template_path) { static void analyze_template(const std::string & template_path) {
LOG_ERR("\n"); LOG_INF("\n");
LOG_ERR("%s", ANSI_PURPLE); LOG_INF("%s", ANSI_PURPLE);
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
LOG_ERR(" ANALYZING TEMPLATE: %s\n", template_path.c_str()); LOG_INF(" ANALYZING TEMPLATE: %s\n", template_path.c_str());
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
LOG_ERR("%s", ANSI_RESET); LOG_INF("%s", ANSI_RESET);
std::string template_source; std::string template_source;
try { try {
@@ -389,14 +391,14 @@ static void analyze_template(const std::string & template_path) {
json tools = build_tools_definition(); json tools = build_tools_definition();
// ===== CAPABILITIES ANALYSIS ===== // ===== CAPABILITIES ANALYSIS =====
LOG_ERR("\n%s=== Template Capabilities (from jinja::caps) ===%s\n", ANSI_CYAN, ANSI_RESET); LOG_INF("\n%s=== Template Capabilities (from jinja::caps) ===%s\n", ANSI_CYAN, ANSI_RESET);
auto caps = chat_template.original_caps(); auto caps = chat_template.original_caps();
LOG_ERR("%ssupports_tools:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_tools ? "true" : "false"); LOG_INF("%ssupports_tools:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_tools ? "true" : "false");
LOG_ERR("%ssupports_tool_calls:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_tool_calls ? "true" : "false"); LOG_INF("%ssupports_tool_calls:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_tool_calls ? "true" : "false");
LOG_ERR("%ssupports_system_role:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_system_role ? "true" : "false"); LOG_INF("%ssupports_system_role:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_system_role ? "true" : "false");
LOG_ERR("%ssupports_parallel_tool_calls:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_parallel_tool_calls ? "true" : "false"); LOG_INF("%ssupports_parallel_tool_calls:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_parallel_tool_calls ? "true" : "false");
LOG_ERR("%ssupports_typed_content:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_typed_content ? "true" : "false"); LOG_INF("%ssupports_typed_content:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_typed_content ? "true" : "false");
LOG_ERR("%ssupports_string_content:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_string_content ? "true" : "false"); LOG_INF("%ssupports_string_content:%s %s\n", ANSI_BLUE, ANSI_RESET, caps.supports_string_content ? "true" : "false");
// ===== DIFFERENTIAL ANALYSIS ===== // ===== DIFFERENTIAL ANALYSIS =====
@@ -584,32 +586,68 @@ static void analyze_template(const std::string & template_path) {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
// Set log level to capture all output common_params params;
common_log_set_verbosity_thold(99); params.model.path = "."; // placeholder so common_params_parse does not require --model
common_init();
analysis_options opts; // this tool handles --all/--template/--template-file and -h itself; every other option goes to the common parser
if (!parse_options(argc, argv, opts)) { std::vector<char *> common_argv;
std::vector<char *> own_argv;
common_argv.push_back(argv[0]);
own_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
const std::string arg = argv[i];
if (arg == "--all" || arg == "-h" || arg == "--help") {
own_argv.push_back(argv[i]);
} else if (arg == "--template" || arg == "--template-file") {
own_argv.push_back(argv[i]);
if (i + 1 < argc) {
own_argv.push_back(argv[++i]);
}
} else if (arg[0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
own_argv.push_back(argv[i]); // unrecognised token: parse_options reports it
}
}
common_argv.push_back(nullptr);
own_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1; return 1;
} }
LOG_ERR("\n"); // capture all output unless the user asked for a lower verbosity threshold
LOG_ERR("%s", ANSI_PURPLE); common_log_set_verbosity_thold(params.verbosity < LOG_DEFAULT_LLAMA ? params.verbosity : 99);
LOG_ERR("================================================================================\n");
LOG_ERR(" TEMPLATE ANALYSIS TOOL\n"); analysis_options opts;
LOG_ERR("================================================================================\n"); if (!parse_options((int) own_argv.size() - 1, own_argv.data(), opts)) {
LOG_ERR("%s", ANSI_RESET); common_log_flush(common_log_main());
LOG_ERR("Analyzing %s%zu%s template(s)\n", ANSI_CYAN, opts.template_paths.size(), ANSI_RESET); return 1;
}
LOG("%s: running\n", "test-chat-analysis");
LOG_INF("\n");
LOG_INF("%s", ANSI_PURPLE);
LOG_INF("================================================================================\n");
LOG_INF(" TEMPLATE ANALYSIS TOOL\n");
LOG_INF("================================================================================\n");
LOG_INF("%s", ANSI_RESET);
LOG_INF("Analyzing %s%zu%s template(s)\n", ANSI_CYAN, opts.template_paths.size(), ANSI_RESET);
for (const auto & path : opts.template_paths) { for (const auto & path : opts.template_paths) {
LOG_INF(" running %s\n", path.c_str());
analyze_template(path); analyze_template(path);
} }
LOG_ERR("\n"); LOG_INF("\n");
LOG_ERR("%s", ANSI_GREEN); LOG_INF("%s", ANSI_GREEN);
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
LOG_ERR(" ANALYSIS COMPLETE\n"); LOG_INF(" ANALYSIS COMPLETE\n");
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
LOG_ERR("%s", ANSI_RESET); LOG_INF("%s", ANSI_RESET);
LOG("%s: %s\n", "test-chat-analysis", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+106 -57
View File
@@ -4,6 +4,8 @@
#include "chat.h" #include "chat.h"
#include "gguf.h" #include "gguf.h"
#include "jinja/runtime.h" #include "jinja/runtime.h"
#include "arg.h"
#include "common.h"
#include "log.h" #include "log.h"
#include "peg-parser.h" #include "peg-parser.h"
#include "testing.h" #include "testing.h"
@@ -16,6 +18,7 @@
#include <optional> #include <optional>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector>
using namespace autoparser; using namespace autoparser;
@@ -175,24 +178,24 @@ static std::string read_gguf_chat_template(const std::string & path) {
} }
static void print_usage(const char * program_name) { static void print_usage(const char * program_name) {
LOG_ERR("Test the chat template auto-parser; also usable as a debug tool that shows the generated PEG parser, GBNF grammar and triggers for a given template.\n"); LOG_INF("Test the chat template auto-parser; also usable as a debug tool that shows the generated PEG parser, GBNF grammar and triggers for a given template.\n");
LOG_ERR("\nUsage: %s [filter_regex] run the automated tests (default)\n", program_name); LOG_INF("\nUsage: %s [filter_regex] run the automated tests (default)\n", program_name);
LOG_ERR(" %s <template_or_gguf_path> [options] debug a single template\n", program_name); LOG_INF(" %s <template_or_gguf_path> [options] debug a single template\n", program_name);
LOG_ERR("\nDebug mode options:\n"); LOG_INF("\nDebug mode options:\n");
LOG_ERR(" --no-tools Disable tool definitions\n"); LOG_INF(" --no-tools Disable tool definitions\n");
LOG_ERR(" --force-tool-call Set tool calls to forced\n"); LOG_INF(" --force-tool-call Set tool calls to forced\n");
LOG_ERR(" --parallel-tool-calls=0|1 Set parallel_tool_calls (default: 1)\n"); LOG_INF(" --parallel-tool-calls=0|1 Set parallel_tool_calls (default: 1)\n");
LOG_ERR(" --generation-prompt=0|1 Set add_generation_prompt (default: 1)\n"); LOG_INF(" --generation-prompt=0|1 Set add_generation_prompt (default: 1)\n");
LOG_ERR(" --enable-reasoning=0|1 Enable reasoning parsing (default: 1)\n"); LOG_INF(" --enable-reasoning=0|1 Enable reasoning parsing (default: 1)\n");
LOG_ERR(" --output=MODE Output mode: analysis, template, both (default: both)\n"); LOG_INF(" --output=MODE Output mode: analysis, template, both (default: both)\n");
LOG_ERR(" --debug-jinja Enable Jinja fine-grained debug\n"); LOG_INF(" --debug-jinja Enable Jinja fine-grained debug\n");
LOG_ERR(" --input-message=TYPE Message type to render:\n"); LOG_INF(" --input-message=TYPE Message type to render:\n");
LOG_ERR(" content_only, reasoning_content, tool_call_only,\n"); LOG_INF(" content_only, reasoning_content, tool_call_only,\n");
LOG_ERR(" content_tool_call, reasoning_tool_call,\n"); LOG_INF(" content_tool_call, reasoning_tool_call,\n");
LOG_ERR(" content_fake_tool_call, all\n"); LOG_INF(" content_fake_tool_call, all\n");
LOG_ERR("\nExamples:\n"); LOG_INF("\nExamples:\n");
LOG_ERR(" %s template.jinja --input-message=all --generation-prompt=1\n", program_name); LOG_INF(" %s template.jinja --input-message=all --generation-prompt=1\n", program_name);
LOG_ERR(" %s template.jinja --output=template --input-message=tool_call_only\n", program_name); LOG_INF(" %s template.jinja --output=template --input-message=tool_call_only\n", program_name);
} }
static bool parse_bool_option(const std::string & value) { static bool parse_bool_option(const std::string & value) {
@@ -336,8 +339,8 @@ static void render_scenario(const common_chat_template & tmpl,
const json & tools, const json & tools,
bool add_generation_prompt, bool add_generation_prompt,
bool enable_thinking) { bool enable_thinking) {
LOG_ERR("\n=== Scenario: %s ===\n", scenario_name.c_str()); LOG_INF("\n=== Scenario: %s ===\n", scenario_name.c_str());
LOG_ERR("add_generation_prompt: %s, enable_thinking: %s\n", add_generation_prompt ? "true" : "false", LOG_INF("add_generation_prompt: %s, enable_thinking: %s\n", add_generation_prompt ? "true" : "false",
enable_thinking ? "true" : "false"); enable_thinking ? "true" : "false");
// When add_generation_prompt is true, add a trailing user message to trigger the prompt // When add_generation_prompt is true, add a trailing user message to trigger the prompt
@@ -349,7 +352,7 @@ static void render_scenario(const common_chat_template & tmpl,
}); });
} }
LOG_ERR("Messages:\n%s\n", final_messages.dump(2).c_str()); LOG_TRC("Messages:\n%s\n", final_messages.dump(2).c_str());
try { try {
generation_params inputs; generation_params inputs;
@@ -363,9 +366,9 @@ static void render_scenario(const common_chat_template & tmpl,
std::string output = common_chat_template_direct_apply(tmpl, inputs); std::string output = common_chat_template_direct_apply(tmpl, inputs);
LOG_ERR("\n--- Rendered Output ---\n"); LOG_INF("\n--- Rendered Output ---\n");
LOG_ERR("%s\n", output.c_str()); LOG_INF("%s\n", output.c_str());
LOG_ERR("--- End Output (length: %zu) ---\n", output.length()); LOG_INF("--- End Output (length: %zu) ---\n", output.length());
} catch (const std::exception & e) { } catch (const std::exception & e) {
LOG_ERR("Rendering failed: %s\n", e.what()); LOG_ERR("Rendering failed: %s\n", e.what());
} }
@@ -395,7 +398,7 @@ static void render_all_scenarios(const common_chat_template & tmpl,
// Also render with add_generation_prompt=true to show the prompt ending // Also render with add_generation_prompt=true to show the prompt ending
if (message_type == input_message_type::ALL) { if (message_type == input_message_type::ALL) {
LOG_ERR("\n\n=== Generation Prompt Scenarios (add_generation_prompt=true) ===\n"); LOG_INF("\n\n=== Generation Prompt Scenarios (add_generation_prompt=true) ===\n");
json prompt_messages = json::array({ user_msg }); json prompt_messages = json::array({ user_msg });
render_scenario(tmpl, "generation_prompt_only", prompt_messages, tools, true, enable_thinking); render_scenario(tmpl, "generation_prompt_only", prompt_messages, tools, true, enable_thinking);
@@ -438,8 +441,8 @@ static int debug_single_template(const debug_options & opts) {
return 1; return 1;
} }
LOG_ERR("Analyzing template: %s\n", opts.template_path.c_str()); LOG_INF("Analyzing template: %s\n", opts.template_path.c_str());
LOG_ERR("Options: with_tools=%s, generation_prompt=%s, enable_reasoning=%s\n", opts.with_tools ? "true" : "false", LOG_INF("Options: with_tools=%s, generation_prompt=%s, enable_reasoning=%s\n", opts.with_tools ? "true" : "false",
opts.generation_prompt ? "true" : "false", opts.enable_reasoning ? "true" : "false"); opts.generation_prompt ? "true" : "false", opts.enable_reasoning ? "true" : "false");
try { try {
@@ -451,17 +454,17 @@ static int debug_single_template(const debug_options & opts) {
common_chat_params parser_data; common_chat_params parser_data;
if (std::optional<common_chat_params> spec_tmpl = if (std::optional<common_chat_params> spec_tmpl =
common_chat_try_specialized_template(chat_template, template_source, params)) { common_chat_try_specialized_template(chat_template, template_source, params)) {
LOG_ERR("\n"); LOG_INF("\n");
LOG_ERR("This template uses a specialized parser, analysis results will not be available.\n"); LOG_INF("This template uses a specialized parser, analysis results will not be available.\n");
parser_data = *spec_tmpl; parser_data = *spec_tmpl;
} else { } else {
// Render template scenarios if requested // Render template scenarios if requested
if (opts.input_message != input_message_type::NONE && if (opts.input_message != input_message_type::NONE &&
(opts.mode == output_mode::TEMPLATE || opts.mode == output_mode::BOTH)) { (opts.mode == output_mode::TEMPLATE || opts.mode == output_mode::BOTH)) {
LOG_ERR("\n"); LOG_INF("\n");
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
LOG_ERR(" TEMPLATE RENDERING OUTPUT\n"); LOG_INF(" TEMPLATE RENDERING OUTPUT\n");
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
render_all_scenarios(chat_template, tools, opts.generation_prompt, opts.enable_reasoning, render_all_scenarios(chat_template, tools, opts.generation_prompt, opts.enable_reasoning,
opts.input_message); opts.input_message);
@@ -469,10 +472,10 @@ static int debug_single_template(const debug_options & opts) {
// Output analysis if requested // Output analysis if requested
if (opts.mode == output_mode::ANALYSIS || opts.mode == output_mode::BOTH) { if (opts.mode == output_mode::ANALYSIS || opts.mode == output_mode::BOTH) {
LOG_ERR("\n"); LOG_INF("\n");
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
LOG_ERR(" TEMPLATE ANALYSIS\n"); LOG_INF(" TEMPLATE ANALYSIS\n");
LOG_ERR("================================================================================\n"); LOG_INF("================================================================================\n");
struct autoparser analysis; struct autoparser analysis;
analysis.analyze_template(chat_template); analysis.analyze_template(chat_template);
@@ -483,25 +486,25 @@ static int debug_single_template(const debug_options & opts) {
} }
if (!std::empty(parser_data.parser)) { if (!std::empty(parser_data.parser)) {
LOG_ERR("\n=== Generated Parser ===\n"); LOG_INF("\n=== Generated Parser ===\n");
common_peg_arena arena; common_peg_arena arena;
arena.load(parser_data.parser); arena.load(parser_data.parser);
LOG_ERR("%s\n", arena.dump(arena.root()).c_str()); LOG_INF("%s\n", arena.dump(arena.root()).c_str());
LOG_ERR("\n=== Generated Grammar ===\n"); LOG_INF("\n=== Generated Grammar ===\n");
LOG_ERR("%s\n", parser_data.grammar.c_str()); LOG_INF("%s\n", parser_data.grammar.c_str());
LOG_ERR("\n=== Generated Lazy Grammar ===\n"); LOG_INF("\n=== Generated Lazy Grammar ===\n");
LOG_ERR("%d\n", parser_data.grammar_lazy); LOG_INF("%d\n", parser_data.grammar_lazy);
LOG_ERR("\n=== Generated Grammar Triggers ===\n"); LOG_INF("\n=== Generated Grammar Triggers ===\n");
for (const common_grammar_trigger & cgt : parser_data.grammar_triggers) { for (const common_grammar_trigger & cgt : parser_data.grammar_triggers) {
LOG_ERR("Token: %d | Type: %d | Value: %s\n", cgt.token, cgt.type, cgt.value.c_str()); LOG_INF("Token: %d | Type: %d | Value: %s\n", cgt.token, cgt.type, cgt.value.c_str());
} }
LOG_ERR("\n=== Preserved Tokens ===\n"); LOG_INF("\n=== Preserved Tokens ===\n");
for (const std::string & token : parser_data.preserved_tokens) { for (const std::string & token : parser_data.preserved_tokens) {
LOG_ERR(" '%s'\n", token.c_str()); LOG_INF(" '%s'\n", token.c_str());
} }
} }
} catch (const std::exception & e) { } catch (const std::exception & e) {
@@ -513,20 +516,59 @@ static int debug_single_template(const debug_options & opts) {
} }
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
if (argc > 1) { common_params params;
std::string arg = argv[1]; params.model.path = "."; // placeholder so common_params_parse does not require --model
common_init();
// this tool handles -h/--help, its debug options and one positional argument
// (template path or test filter) itself; every other option goes to the common parser
std::vector<char *> common_argv;
std::vector<char *> own_argv;
common_argv.push_back(argv[0]);
own_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
const std::string arg = argv[i];
if (arg == "-h" || arg == "--help" ||
arg == "--force-tool-call" || arg == "--debug-jinja" || arg == "--no-tools" ||
arg.rfind("--parallel-tool-calls=", 0) == 0 ||
arg.rfind("--generation-prompt=", 0) == 0 ||
arg.rfind("--enable-reasoning=", 0) == 0 ||
arg.rfind("--output=", 0) == 0 ||
arg.rfind("--input-message=", 0) == 0) {
own_argv.push_back(argv[i]);
} else if (arg[0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
own_argv.push_back(argv[i]); // positional: template path or test filter
}
}
common_argv.push_back(nullptr);
own_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
// capture all output unless the user asked for a lower verbosity threshold
const int own_verbosity = params.verbosity < LOG_DEFAULT_LLAMA ? params.verbosity : 99;
const int own_argc = (int) own_argv.size() - 1;
char ** own_argv_data = own_argv.data();
if (own_argc > 1) {
std::string arg = own_argv_data[1];
if (arg == "-h" || arg == "--help") { if (arg == "-h" || arg == "--help") {
common_log_set_verbosity_thold(99); common_log_set_verbosity_thold(own_verbosity);
print_usage(argv[0]); print_usage(own_argv_data[0]);
common_log_flush(common_log_main());
return 0; return 0;
} }
// debug mode: if the first argument is an existing file, analyze that template instead of running the automated tests // debug mode: if the first argument is an existing file, analyze that template instead of running the automated tests
if (std::filesystem::is_regular_file(arg)) { if (std::filesystem::is_regular_file(arg)) {
common_log_set_verbosity_thold(99); common_log_set_verbosity_thold(own_verbosity);
debug_options opts; debug_options opts;
if (!parse_debug_options(argc, argv, opts)) { if (!parse_debug_options(own_argc, own_argv_data, opts)) {
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -534,17 +576,21 @@ int main(int argc, char * argv[]) {
jinja::enable_debug(true); jinja::enable_debug(true);
} }
return debug_single_template(opts); const int rc = debug_single_template(opts);
common_log_flush(common_log_main());
return rc;
} }
} }
LOG("%s: running\n", "test-chat-auto-parser");
testing t(std::cout); testing t(std::cout);
t.verbose = true; t.verbose = true;
// usage: test-chat-auto-parser [filter_regex] // usage: test-chat-auto-parser [filter_regex]
if (argc > 1) { if (own_argc > 1) {
t.set_filter(argv[1]); t.set_filter(own_argv_data[1]);
} }
t.test("diff_split", test_calculate_diff_split); t.test("diff_split", test_calculate_diff_split);
@@ -565,7 +611,10 @@ int main(int argc, char * argv[]) {
t.test("bailing_v3", test_bailing_v3_tool_format); t.test("bailing_v3", test_bailing_v3_tool_format);
t.test("role_markers_all_templates", test_role_markers_all_templates); t.test("role_markers_all_templates", test_role_markers_all_templates);
return t.summary(); const int rc = t.summary();
LOG("%s: %s\n", "test-chat-auto-parser", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
static void test_marker_separation(testing & t) { static void test_marker_separation(testing & t) {
+31 -3
View File
@@ -1,7 +1,9 @@
#include "arg.h"
#include "chat-peg-parser.h" #include "chat-peg-parser.h"
#include "chat.h" #include "chat.h"
#include "common.h" #include "common.h"
#include "json-schema-to-grammar.h" #include "json-schema-to-grammar.h"
#include "log.h"
#include "peg-parser.h" #include "peg-parser.h"
#include "testing.h" #include "testing.h"
#include "peg-parser/simple-tokenize.h" #include "peg-parser/simple-tokenize.h"
@@ -10,6 +12,7 @@
#include <numeric> #include <numeric>
#include <regex> #include <regex>
#include <string> #include <string>
#include <vector>
#include "json.h" #include "json.h"
@@ -25,9 +28,31 @@ static void test_tagged_peg_parser(testing & t);
static void test_permute(testing & t); static void test_permute(testing & t);
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// this test takes an optional filter as its only positional argument
std::string filter;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (filter.empty()) {
filter = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-chat-peg-parser");
testing t(std::cout); testing t(std::cout);
if (argc >= 2) { if (!filter.empty()) {
t.set_filter(argv[1]); t.set_filter(filter);
} }
const char * verbose = getenv("LLAMA_TEST_VERBOSE"); const char * verbose = getenv("LLAMA_TEST_VERBOSE");
@@ -43,7 +68,10 @@ int main(int argc, char * argv[]) {
t.test("tagged peg parser", test_tagged_peg_parser); t.test("tagged peg parser", test_tagged_peg_parser);
t.test("permute", test_permute); t.test("permute", test_permute);
return t.summary(); const int rc = t.summary();
LOG("%s: %s\n", "test-chat-peg-parser", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
static json create_tools() { static json create_tools() {
+33 -8
View File
@@ -13,7 +13,9 @@
#include <cassert> #include <cassert>
#include "llama.h" #include "llama.h"
#include "arg.h"
#include "common.h" #include "common.h"
#include "log.h"
#include "chat.h" #include "chat.h"
#include "jinja/runtime.h" #include "jinja/runtime.h"
#include "jinja/parser.h" #include "jinja/parser.h"
@@ -113,6 +115,10 @@ static std::string DEFAULT_JSON_WITH_TOOLS = R"({
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
std::vector<std::string> args(argv, argv + argc); std::vector<std::string> args(argv, argv + argc);
std::string tmpl_path; std::string tmpl_path;
@@ -123,6 +129,8 @@ int main(int argc, char ** argv) {
bool use_common = true; bool use_common = true;
bool dump_prog = false; bool dump_prog = false;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (size_t i = 1; i < args.size(); i++) { for (size_t i = 1; i < args.size(); i++) {
if (args[i] == "--help" || args[i] == "-h") { if (args[i] == "--help" || args[i] == "-h") {
std::cout << HELP << "\n"; std::cout << HELP << "\n";
@@ -142,6 +150,8 @@ int main(int argc, char ** argv) {
use_common = false; use_common = false;
} else if (args[i] == "--dump-prog") { } else if (args[i] == "--dump-prog") {
dump_prog = true; dump_prog = true;
} else if (args[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (tmpl_path.empty()) { } else if (tmpl_path.empty()) {
tmpl_path = args[i]; tmpl_path = args[i];
} else { } else {
@@ -150,16 +160,27 @@ int main(int argc, char ** argv) {
return 1; return 1;
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-chat-template");
if (tmpl_path.empty()) { if (tmpl_path.empty()) {
return main_automated_tests(); const int rc = main_automated_tests();
LOG("%s: %s\n", "test-chat-template", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
json input_json; json input_json;
if (!json_path.empty()) { if (!json_path.empty()) {
std::ifstream json_file(json_path); std::ifstream json_file(json_path);
if (!json_file) { if (!json_file) {
std::cerr << "Error: Could not open JSON file: " << json_path << "\n"; LOG_ERR("Error: Could not open JSON file: %s\n", json_path.c_str());
LOG("%s: %s\n", "test-chat-template", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
std::string content = std::string( std::string content = std::string(
@@ -180,10 +201,14 @@ int main(int argc, char ** argv) {
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
run_single(contents, input_json, use_common, dump_prog, output_path); run_single(contents, input_json, use_common, dump_prog, output_path);
} else { } else {
std::cerr << "Error: PATH_TO_TEMPLATE is not a valid file or directory: " << tmpl_path << "\n"; LOG_ERR("Error: PATH_TO_TEMPLATE is not a valid file or directory: %s\n", tmpl_path.c_str());
LOG("%s: %s\n", "test-chat-template", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
LOG("%s: %s\n", "test-chat-template", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
@@ -197,7 +222,7 @@ void run_multiple(const std::string& dir_path, bool stop_on_first_fail, const js
// only process .jinja files // only process .jinja files
if (entry.path().extension() == ".jinja" && entry.is_regular_file()) { if (entry.path().extension() == ".jinja" && entry.is_regular_file()) {
test_count++; test_count++;
std::cout << "\n\n=== RUNNING TEMPLATE FILE: " << entry.path().string() << " ===\n"; LOG_INF("=== RUNNING TEMPLATE FILE: %s ===\n", entry.path().string().c_str());
std::ifstream infile(entry.path()); std::ifstream infile(entry.path());
std::string contents((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>()); std::string contents((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>());
try { try {
@@ -662,9 +687,9 @@ int main_automated_tests(void) {
assert(res > 0); assert(res > 0);
supported_tmpl.resize(res); supported_tmpl.resize(res);
res = llama_chat_builtin_templates(supported_tmpl.data(), supported_tmpl.size()); res = llama_chat_builtin_templates(supported_tmpl.data(), supported_tmpl.size());
std::cout << "Built-in chat templates:\n"; LOG_INF("Built-in chat templates:\n");
for (const auto *tmpl : supported_tmpl) { for (const auto *tmpl : supported_tmpl) {
std::cout << " " << tmpl << "\n"; LOG_CNT(" %s\n", tmpl);
} }
// test invalid chat template // test invalid chat template
@@ -673,7 +698,7 @@ int main_automated_tests(void) {
const auto add_generation_prompt = true; const auto add_generation_prompt = true;
for (const auto & test_case : test_cases) { for (const auto & test_case : test_cases) {
std::cout << "\n\n=== " << test_case.name << " ===\n\n"; LOG_INF("=== %s ===\n", test_case.name.c_str());
auto conv = conversation; auto conv = conversation;
conv.insert(conv.end(), test_case.extra_conversation.begin(), test_case.extra_conversation.end()); conv.insert(conv.end(), test_case.extra_conversation.begin(), test_case.extra_conversation.end());
formatted_chat.resize(2048); formatted_chat.resize(2048);
@@ -705,7 +730,7 @@ int main_automated_tests(void) {
if (!test_case.supported_with_jinja) { if (!test_case.supported_with_jinja) {
continue; continue;
} }
std::cout << "\n\n=== " << test_case.name << " (jinja) ===\n\n"; LOG_INF("=== %s (jinja) ===\n", test_case.name.c_str());
try { try {
auto msgs = messages; auto msgs = messages;
for (const auto & msg : test_case.extra_conversation) { for (const auto & msg : test_case.extra_conversation) {
+65 -33
View File
@@ -10,6 +10,7 @@
#include "../tools/server/server-chat.h" #include "../tools/server/server-chat.h"
#include "chat-auto-parser.h" #include "chat-auto-parser.h"
#include "chat.h" #include "chat.h"
#include "arg.h"
#include "common.h" #include "common.h"
#include "ggml.h" #include "ggml.h"
#include "log.h" #include "log.h"
@@ -1118,6 +1119,8 @@ static bool g_force_reconstruction_test = false;
static void test_peg_parser(common_chat_templates * tmpls, static void test_peg_parser(common_chat_templates * tmpls,
const std::function<void(peg_test_case &)> & init, const std::function<void(peg_test_case &)> & init,
bool detailed_debug) { bool detailed_debug) {
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
// UTF-8-safe truncation helper (same as in test_parser_with_streaming) // UTF-8-safe truncation helper (same as in test_parser_with_streaming)
constexpr auto utf8_truncate_safe_len = [](const std::string_view s) -> size_t { constexpr auto utf8_truncate_safe_len = [](const std::string_view s) -> size_t {
auto len = s.size(); auto len = s.size();
@@ -1442,10 +1445,11 @@ static void test_peg_parser(common_chat_templates * tmpls,
size_t ctx_start = diff_pos > 60 ? diff_pos - 60 : 0; size_t ctx_start = diff_pos > 60 ? diff_pos - 60 : 0;
size_t ctx_end_e = std::min(expected_text.size(), diff_pos + 40); size_t ctx_end_e = std::min(expected_text.size(), diff_pos + 40);
size_t ctx_end_r = std::min(reconstruction_params.prompt.size(), diff_pos + 40); size_t ctx_end_r = std::min(reconstruction_params.prompt.size(), diff_pos + 40);
LOG_ERR("\x1b[31m[RECONSTRUCTION FAIL]\x1b[0m " LOG_ERR("%s[RECONSTRUCTION FAIL]%s "
"first diff at byte %zu (expected len=%zu, reconstructed len=%zu)\n" "first diff at byte %zu (expected len=%zu, reconstructed len=%zu)\n"
" expected: ...%s...\n" " expected: ...%s...\n"
" reconstructed: ...%s...\n", " reconstructed: ...%s...\n",
use_color ? "\x1b[31m" : "", use_color ? "\x1b[0m" : "",
diff_pos, expected_text.size(), reconstruction_params.prompt.size(), diff_pos, expected_text.size(), reconstruction_params.prompt.size(),
expected_text.substr(ctx_start, ctx_end_e - ctx_start).c_str(), expected_text.substr(ctx_start, ctx_end_e - ctx_start).c_str(),
reconstruction_params.prompt.substr(ctx_start, ctx_end_r - ctx_start).c_str()); reconstruction_params.prompt.substr(ctx_start, ctx_end_r - ctx_start).c_str());
@@ -1461,6 +1465,18 @@ static void test_peg_parser(common_chat_templates * tmpls,
} }
} }
// Case-insensitive substring match against the --template filter
static bool template_matches_filter(const std::string & template_path) {
if (g_template_filter.empty()) {
return true;
}
std::string template_path_lower = template_path;
std::string filter_lower = g_template_filter;
std::transform(template_path_lower.begin(), template_path_lower.end(), template_path_lower.begin(), ::tolower);
std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(), ::tolower);
return template_path_lower.find(filter_lower) != std::string::npos;
}
// Fluent builder for PEG parser tests // Fluent builder for PEG parser tests
class peg_test_builder; class peg_test_builder;
@@ -1474,7 +1490,11 @@ class peg_tester {
explicit peg_tester(const std::string & template_path, const bool detailed_debug = false) : explicit peg_tester(const std::string & template_path, const bool detailed_debug = false) :
tmpls_(read_templates(template_path)), tmpls_(read_templates(template_path)),
template_path_(template_path), template_path_(template_path),
detailed_debug_(detailed_debug) {} detailed_debug_(detailed_debug) {
if (template_matches_filter(template_path_)) {
LOG_INF(" running %s\n", template_path_.c_str());
}
}
const std::string & template_path() const { return template_path_; } const std::string & template_path() const { return template_path_; }
@@ -1571,19 +1591,11 @@ class peg_test_builder {
// Execute the test // Execute the test
void run() { void run() {
// Check template filter // Check template filter
if (!g_template_filter.empty()) { if (!template_matches_filter(tester_.template_path())) {
// Case-insensitive substring match // Skip this test
std::string template_path_lower = tester_.template_path(); return;
std::string filter_lower = g_template_filter;
std::transform(template_path_lower.begin(), template_path_lower.end(), template_path_lower.begin(),
::tolower);
std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(), ::tolower);
if (template_path_lower.find(filter_lower) == std::string::npos) {
// Skip this test
return;
}
} }
LOG_INF("\n\x1b[38;5;126m[%s]\x1b[0m\n%s\n\n", tester_.template_path().c_str(), tc_.input.c_str()); LOG_TRC("\n\x1b[38;5;126m[%s]\x1b[0m\n%s\n\n", tester_.template_path().c_str(), tc_.input.c_str());
test_peg_parser(tester_.tmpls_.get(), [this](peg_test_case & t) { t = tc_; }, tester_.detailed_debug_); test_peg_parser(tester_.tmpls_.get(), [this](peg_test_case & t) { t = tc_; }, tester_.detailed_debug_);
} }
}; };
@@ -7017,7 +7029,7 @@ static void test_developer_role_to_system_workaround() {
if (params.prompt.find("<|system|>You are a helpful developer assistant.<|end|>") == std::string::npos) { if (params.prompt.find("<|system|>You are a helpful developer assistant.<|end|>") == std::string::npos) {
throw std::runtime_error("Test failed: system message not found in output"); throw std::runtime_error("Test failed: system message not found in output");
} }
LOG_ERR("Test 1 passed: developer role changed to system\n"); LOG_INF("Test 1 passed: developer role changed to system\n");
} }
} }
@@ -7321,42 +7333,61 @@ static void test_msg_diffs_compute() {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
bool detailed_debug = false; bool detailed_debug = false;
bool only_run_filtered = false; bool only_run_filtered = false;
// Check for --template and --detailed flags // Check for --template and --detailed flags
std::vector<std::string> positional;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
std::string arg = argv[i]; std::string arg = argv[i];
if (arg == "--template" && i + 1 < argc) { if (arg == "--template") {
g_template_filter = argv[++i]; if (i + 1 < argc) {
// Only run PEG parser tests with the filter g_template_filter = argv[++i];
only_run_filtered = true; // Only run PEG parser tests with the filter
} only_run_filtered = true;
if (arg == "--detailed") { }
} else if (arg == "--detailed") {
detailed_debug = true; detailed_debug = true;
common_log_set_verbosity_thold(999); } else if (arg == "--force-reconstruction-test") {
}
if (arg == "--force-reconstruction-test") {
g_force_reconstruction_test = true; g_force_reconstruction_test = true;
only_run_filtered = true; only_run_filtered = true;
} else if (arg[0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
positional.push_back(arg); // .jinja template path
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
// keep the detailed output unless the user asked for a lower verbosity threshold
if (detailed_debug) {
common_log_set_verbosity_thold(params.verbosity < LOG_DEFAULT_LLAMA ? params.verbosity : 999);
}
LOG("%s: running\n", "test-chat");
if (only_run_filtered) { if (only_run_filtered) {
test_template_output_peg_parsers(detailed_debug); test_template_output_peg_parsers(detailed_debug);
std::cout << "\n[chat] All template tests passed!" << '\n'; std::cout << "\n[chat] All template tests passed!" << '\n';
LOG("%s: %s\n", "test-chat", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
#ifndef _WIN32 #ifndef _WIN32
// Check if any argument is a .jinja file (for template format detection mode) // Check if any argument is a .jinja file (for template format detection mode)
bool has_jinja_files = false; bool has_jinja_files = false;
for (int i = 1; i < argc; i++) { for (const auto & path : positional) {
std::string arg = argv[i]; if (path.size() >= 6 && path.rfind(".jinja") == path.size() - 6) {
if (arg == "--detailed") {
continue;
}
if (arg.size() >= 6 && arg.rfind(".jinja") == arg.size() - 6) {
has_jinja_files = true; has_jinja_files = true;
break; break;
} }
@@ -7373,9 +7404,8 @@ int main(int argc, char ** argv) {
std::cout << "| Template | Format |\n"; std::cout << "| Template | Format |\n";
std::cout << "|----------|--------|\n"; std::cout << "|----------|--------|\n";
for (int i = 1; i < argc; i++) { for (const auto & path : positional) {
try { try {
std::string path = argv[i];
if (path.rfind(".jinja") != path.size() - 6) { if (path.rfind(".jinja") != path.size() - 6) {
std::cerr << "Skipping non-jinja file: " << path << '\n'; std::cerr << "Skipping non-jinja file: " << path << '\n';
continue; continue;
@@ -7386,7 +7416,7 @@ int main(int argc, char ** argv) {
const auto * format = common_chat_format_name(common_chat_templates_apply(tmpls.get(), inputs).format); const auto * format = common_chat_format_name(common_chat_templates_apply(tmpls.get(), inputs).format);
std::cout << "| " << name << " | " << format << " |\n"; std::cout << "| " << name << " | " << format << " |\n";
} catch (const std::exception & e) { } catch (const std::exception & e) {
std::cerr << "Failed to process " << argv[i] << ": " << e.what() << '\n'; std::cerr << "Failed to process " << path << ": " << e.what() << '\n';
} }
} }
} else } else
@@ -7407,5 +7437,7 @@ int main(int argc, char ** argv) {
test_template_output_peg_parsers(detailed_debug); test_template_output_peg_parsers(detailed_debug);
std::cout << "\n[chat] All tests passed!" << '\n'; std::cout << "\n[chat] All tests passed!" << '\n';
} }
LOG("%s: %s\n", "test-chat", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+21 -4
View File
@@ -13,6 +13,10 @@
#include "ggml.h" #include "ggml.h"
#include "ggml-cpu.h" #include "ggml-cpu.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
@@ -86,7 +90,16 @@ static double nmse_cropped(const float * y, const float * ref, int64_t T_out, in
return num / (den + 1e-30); return num / (den + 1e-30);
} }
int main(void) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-col2im-1d");
int fails = 0; int fails = 0;
for (const col2im_case & c : CASES) { for (const col2im_case & c : CASES) {
@@ -148,12 +161,16 @@ int main(void) {
if (!ok) { if (!ok) {
fails++; fails++;
} }
printf("col2im_1d K=%2d OC=%2d T_in=%3d s0=%d p0=%d: nmse f32=%.2e f16=%.2e bf16=%.2e %s\n", LOG_INF("col2im_1d K=%2d OC=%2d T_in=%3d s0=%d p0=%d: nmse f32=%.2e f16=%.2e bf16=%.2e %s\n",
(int) c.K, (int) c.OC, (int) c.T_in, c.s0, c.p0, e32, e16, ebf, ok ? "OK" : "FAIL"); (int) c.K, (int) c.OC, (int) c.T_in, c.s0, c.p0, e32, e16, ebf, ok ? "OK" : "FAIL");
ggml_free(ctx); ggml_free(ctx);
} }
printf(fails == 0 ? "all col2im_1d checks passed\n" : "%d col2im_1d checks FAILED\n", fails); const bool ok = fails == 0;
return fails == 0 ? 0 : 1;
printf(ok ? "all col2im_1d checks passed\n" : "%d col2im_1d checks FAILED\n", fails);
LOG("%s: %s\n", "test-col2im-1d", ok ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return ok ? 0 : 1;
} }
+22
View File
@@ -135,6 +135,8 @@ int main(int argc, char ** argv) {
return 1; return 1;
} }
LOG("%s: running\n", "test-export-graph-ops");
// Load CPU-only // Load CPU-only
ggml_backend_dev_t cpu_device = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU); ggml_backend_dev_t cpu_device = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
params.devices = { cpu_device, nullptr }; params.devices = { cpu_device, nullptr };
@@ -154,6 +156,8 @@ int main(int argc, char ** argv) {
ctx = init_result->context(); ctx = init_result->context();
if (!ctx) { if (!ctx) {
LOG_ERR("failed to initialize params\n"); LOG_ERR("failed to initialize params\n");
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
} else { } else {
@@ -166,6 +170,8 @@ int main(int argc, char ** argv) {
gguf_context_ptr gguf_ctx = gguf_fetch_gguf_ctx(hf_repo, hf_quant); gguf_context_ptr gguf_ctx = gguf_fetch_gguf_ctx(hf_repo, hf_quant);
if (!gguf_ctx) { if (!gguf_ctx) {
LOG_ERR("failed to fetch GGUF metadata from %s\n", hf_repo.c_str()); LOG_ERR("failed to fetch GGUF metadata from %s\n", hf_repo.c_str());
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -177,6 +183,8 @@ int main(int argc, char ** argv) {
if (!model) { if (!model) {
LOG_ERR("failed to create llama_model from %s\n", hf_repo.c_str()); LOG_ERR("failed to create llama_model from %s\n", hf_repo.c_str());
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -186,10 +194,14 @@ int main(int argc, char ** argv) {
if (!ctx) { if (!ctx) {
LOG_ERR("failed to create llama_context\n"); LOG_ERR("failed to create llama_context\n");
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
#else #else
LOG_ERR("test-export-graph-ops compiled without HF fetch support\n"); LOG_ERR("test-export-graph-ops compiled without HF fetch support\n");
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
#endif #endif
} }
@@ -197,11 +209,15 @@ int main(int argc, char ** argv) {
const uint32_t n_seqs = llama_n_seq_max(ctx); const uint32_t n_seqs = llama_n_seq_max(ctx);
const uint32_t n_tokens = std::min(llama_n_ctx(ctx), llama_n_ubatch(ctx)); const uint32_t n_tokens = std::min(llama_n_ctx(ctx), llama_n_ubatch(ctx));
LOG_INF(" running %s\n", params.model.get_name().c_str());
std::set<test_object> tests; std::set<test_object> tests;
auto * gf_pp = llama_graph_reserve(ctx, n_tokens, n_seqs, n_tokens); auto * gf_pp = llama_graph_reserve(ctx, n_tokens, n_seqs, n_tokens);
if (!gf_pp) { if (!gf_pp) {
LOG_ERR("failed to reserve prompt processing graph\n"); LOG_ERR("failed to reserve prompt processing graph\n");
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
extract_graph_ops(gf_pp, "pp", tests); extract_graph_ops(gf_pp, "pp", tests);
@@ -209,6 +225,8 @@ int main(int argc, char ** argv) {
auto * gf_tg = llama_graph_reserve(ctx, n_seqs, n_seqs, n_seqs); auto * gf_tg = llama_graph_reserve(ctx, n_seqs, n_seqs, n_seqs);
if (!gf_tg) { if (!gf_tg) {
LOG_ERR("failed to reserve token generation graph\n"); LOG_ERR("failed to reserve token generation graph\n");
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
extract_graph_ops(gf_tg, "tg", tests); extract_graph_ops(gf_tg, "tg", tests);
@@ -219,6 +237,8 @@ int main(int argc, char ** argv) {
if (!f.is_open()) { if (!f.is_open()) {
LOG_ERR("unable to open output file: %s\n", params.out_file.c_str()); LOG_ERR("unable to open output file: %s\n", params.out_file.c_str());
LOG("%s: %s\n", "test-export-graph-ops", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -226,5 +246,7 @@ int main(int argc, char ** argv) {
test.serialize(f); test.serialize(f);
} }
LOG("%s: %s\n", "test-export-graph-ops", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+38 -2
View File
@@ -16,6 +16,7 @@
// test-fusion --models DIR --device MTL0 --check baseline.csv # validate against it // test-fusion --models DIR --device MTL0 --check baseline.csv # validate against it
// test-fusion --model FILE --device MTL0 --check baseline.csv # validate a single model // test-fusion --model FILE --device MTL0 --check baseline.csv # validate a single model
#include "arg.h"
#include "common.h" #include "common.h"
#include "log.h" #include "log.h"
#include "llama-cpp.h" #include "llama-cpp.h"
@@ -224,17 +225,24 @@ static void usage(const char * argv0) {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes its models from --models/--model
common_init();
std::string models_dir; std::string models_dir;
std::string model_file; std::string model_file;
std::string device_name; std::string device_name;
std::string record_path; std::string record_path;
std::string check_path; std::string check_path;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
const std::string arg = argv[i]; const std::string arg = argv[i];
const auto next = [&](const char * name) -> std::string { const auto next = [&](const char * name) -> std::string {
if (i + 1 >= argc) { if (i + 1 >= argc) {
LOG_ERR("%s: %s requires an argument\n", __func__, name); LOG_ERR("%s: %s requires an argument\n", __func__, name);
common_log_flush(common_log_main());
exit(1); exit(1);
} }
return argv[++i]; return argv[++i];
@@ -248,39 +256,56 @@ int main(int argc, char ** argv) {
else if (arg == "--device"){ device_name = next("--device"); } else if (arg == "--device"){ device_name = next("--device"); }
else if (arg == "--record"){ record_path = next("--record"); } else if (arg == "--record"){ record_path = next("--record"); }
else if (arg == "--check") { check_path = next("--check"); } else if (arg == "--check") { check_path = next("--check"); }
else { else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
LOG_ERR("%s: unknown argument: %s\n", __func__, arg.c_str()); LOG_ERR("%s: unknown argument: %s\n", __func__, arg.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
if (device_name.empty()) { if (device_name.empty()) {
LOG_ERR("%s: --device NAME is required\n", __func__); LOG_ERR("%s: --device NAME is required\n", __func__);
common_log_flush(common_log_main());
return 1; return 1;
} }
if (models_dir.empty() && model_file.empty()) { if (models_dir.empty() && model_file.empty()) {
LOG_ERR("%s: --models DIR or --model FILE is required\n", __func__); LOG_ERR("%s: --models DIR or --model FILE is required\n", __func__);
common_log_flush(common_log_main());
return 1; return 1;
} }
if (!models_dir.empty() && !model_file.empty()) { if (!models_dir.empty() && !model_file.empty()) {
LOG_ERR("%s: --models DIR and --model FILE are mutually exclusive\n", __func__); LOG_ERR("%s: --models DIR and --model FILE are mutually exclusive\n", __func__);
common_log_flush(common_log_main());
return 1; return 1;
} }
if (!record_path.empty() && !check_path.empty()) { if (!record_path.empty() && !check_path.empty()) {
LOG_ERR("%s: --record and --check are mutually exclusive\n", __func__); LOG_ERR("%s: --record and --check are mutually exclusive\n", __func__);
common_log_flush(common_log_main());
return 1; return 1;
} }
LOG("%s: running\n", "test-fusion");
std::vector<std::string> models; std::vector<std::string> models;
if (!model_file.empty()) { if (!model_file.empty()) {
if (!std::filesystem::is_regular_file(model_file)) { if (!std::filesystem::is_regular_file(model_file)) {
LOG_ERR("%s: model file '%s' does not exist\n", __func__, model_file.c_str()); LOG_ERR("%s: model file '%s' does not exist\n", __func__, model_file.c_str());
LOG("%s: %s\n", "test-fusion", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
models.push_back(model_file); models.push_back(model_file);
} else { } else {
if (!std::filesystem::exists(models_dir) || !std::filesystem::is_directory(models_dir)) { if (!std::filesystem::exists(models_dir) || !std::filesystem::is_directory(models_dir)) {
LOG_ERR("%s: models directory '%s' does not exist\n", __func__, models_dir.c_str()); LOG_ERR("%s: models directory '%s' does not exist\n", __func__, models_dir.c_str());
LOG("%s: %s\n", "test-fusion", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
for (const auto & entry : std::filesystem::directory_iterator(models_dir)) { for (const auto & entry : std::filesystem::directory_iterator(models_dir)) {
@@ -292,17 +317,20 @@ int main(int argc, char ** argv) {
if (models.empty()) { if (models.empty()) {
LOG_ERR("%s: no .gguf models found in '%s'\n", __func__, models_dir.c_str()); LOG_ERR("%s: no .gguf models found in '%s'\n", __func__, models_dir.c_str());
LOG("%s: %s\n", "test-fusion", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
common_init();
ggml_backend_load_all(); ggml_backend_load_all();
ggml_backend_dev_t dev = ggml_backend_dev_by_name(device_name.c_str()); ggml_backend_dev_t dev = ggml_backend_dev_by_name(device_name.c_str());
if (!dev) { if (!dev) {
LOG_WRN("%s: device '%s' not found - skipping (baseline is device-specific)\n", LOG_WRN("%s: device '%s' not found - skipping (baseline is device-specific)\n",
__func__, device_name.c_str()); __func__, device_name.c_str());
LOG("%s: %s\n", "test-fusion", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
@@ -324,6 +352,8 @@ int main(int argc, char ** argv) {
LOG_ERR("%s: device '%s' does not export the generic fusion debugging API " LOG_ERR("%s: device '%s' does not export the generic fusion debugging API "
"(ggml_backend_fusion_*) - cannot run the fusion regression test\n", "(ggml_backend_fusion_*) - cannot run the fusion regression test\n",
__func__, device_name.c_str()); __func__, device_name.c_str());
LOG("%s: %s\n", "test-fusion", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -340,6 +370,8 @@ int main(int argc, char ** argv) {
std::ifstream in(check_path); std::ifstream in(check_path);
if (!in) { if (!in) {
LOG_ERR("%s: cannot open baseline '%s'\n", __func__, check_path.c_str()); LOG_ERR("%s: cannot open baseline '%s'\n", __func__, check_path.c_str());
LOG("%s: %s\n", "test-fusion", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
std::string line; std::string line;
@@ -368,6 +400,8 @@ int main(int argc, char ** argv) {
const size_t seed = 1; const size_t seed = 1;
for (const auto & model_path : models) { for (const auto & model_path : models) {
LOG_INF(" running %s\n", model_path.c_str());
const std::string arch = get_arch(model_path); const std::string arch = get_arch(model_path);
const bool moe = arch.find("moe") != std::string::npos; const bool moe = arch.find("moe") != std::string::npos;
@@ -560,6 +594,8 @@ int main(int argc, char ** argv) {
__func__, models_dir.c_str(), argv[0], device_name.c_str(), models_dir.c_str(), check_path.c_str()); __func__, models_dir.c_str(), argv[0], device_name.c_str(), models_dir.c_str(), check_path.c_str());
} }
LOG("%s: %s\n", "test-fusion", n_bad == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return n_bad; return n_bad;
} }
} }
+42 -6
View File
@@ -1,6 +1,9 @@
#include "../src/unicode.h" #include "../src/unicode.h"
#include "../src/llama-grammar.h" #include "../src/llama-grammar.h"
#include "arg.h"
#include "log.h"
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
@@ -37,33 +40,61 @@ static bool llama_grammar_validate(struct llama_grammar * grammar, const std::st
} }
static void print_error_message(const std::string & input_str, size_t error_pos, const std::string & error_msg) { static void print_error_message(const std::string & input_str, size_t error_pos, const std::string & error_msg) {
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_pos = use_color ? "\033[1;31m" : "";
const char * const col_rest = use_color ? "\033[0;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
fprintf(stdout, "Input string is invalid according to the grammar.\n"); fprintf(stdout, "Input string is invalid according to the grammar.\n");
fprintf(stdout, "Error: %s at position %zu\n", error_msg.c_str(), error_pos); fprintf(stdout, "Error: %s at position %zu\n", error_msg.c_str(), error_pos);
fprintf(stdout, "\n"); fprintf(stdout, "\n");
fprintf(stdout, "Input string:\n"); fprintf(stdout, "Input string:\n");
fprintf(stdout, "%s", input_str.substr(0, error_pos).c_str()); fprintf(stdout, "%s", input_str.substr(0, error_pos).c_str());
if (error_pos < input_str.size()) { if (error_pos < input_str.size()) {
fprintf(stdout, "\033[1;31m%c", input_str[error_pos]); fprintf(stdout, "%s%c", col_pos, input_str[error_pos]);
if (error_pos+1 < input_str.size()) { if (error_pos+1 < input_str.size()) {
fprintf(stdout, "\033[0;31m%s", input_str.substr(error_pos+1).c_str()); fprintf(stdout, "%s%s", col_rest, input_str.substr(error_pos+1).c_str());
} }
fprintf(stdout, "\033[0m\n"); fprintf(stdout, "%s\n", col_end);
} }
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc != 3) { common_params params;
params.model.path = "."; // this test takes no model
common_init();
std::vector<std::string> positional;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
positional.push_back(argv[i]);
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
if (positional.size() != 2) {
fprintf(stdout, "Usage: %s <grammar_filename> <input_filename>\n", argv[0]); fprintf(stdout, "Usage: %s <grammar_filename> <input_filename>\n", argv[0]);
return 1; return 1;
} }
const std::string grammar_filename = argv[1]; LOG("%s: running\n", "test-gbnf-validator");
const std::string input_filename = argv[2];
const std::string grammar_filename = positional[0];
const std::string input_filename = positional[1];
// Read the GBNF grammar file // Read the GBNF grammar file
FILE* grammar_file = fopen(grammar_filename.c_str(), "r"); FILE* grammar_file = fopen(grammar_filename.c_str(), "r");
if (!grammar_file) { if (!grammar_file) {
fprintf(stdout, "Failed to open grammar file: %s\n", grammar_filename.c_str()); fprintf(stdout, "Failed to open grammar file: %s\n", grammar_filename.c_str());
LOG("%s: %s\n", "test-gbnf-validator", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -79,6 +110,8 @@ int main(int argc, char** argv) {
llama_grammar * grammar = llama_grammar_init_impl(nullptr, grammar_str.c_str(), "root", false, nullptr, 0, nullptr, 0); llama_grammar * grammar = llama_grammar_init_impl(nullptr, grammar_str.c_str(), "root", false, nullptr, 0, nullptr, 0);
if (grammar == nullptr) { if (grammar == nullptr) {
fprintf(stdout, "Failed to initialize llama_grammar\n"); fprintf(stdout, "Failed to initialize llama_grammar\n");
LOG("%s: %s\n", "test-gbnf-validator", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
// Read the input file // Read the input file
@@ -105,5 +138,8 @@ int main(int argc, char** argv) {
// Clean up // Clean up
llama_grammar_free_impl(grammar); llama_grammar_free_impl(grammar);
LOG("%s: %s\n", "test-gbnf-validator", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+65 -40
View File
@@ -1,39 +1,56 @@
#include "gguf-model-data.h" #include "gguf-model-data.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cstdio> #include <cstdio>
#define TEST_ASSERT(cond, msg) \ #define TEST_ASSERT(cond, msg) \
do { \ do { \
if (!(cond)) { \ if (!(cond)) { \
fprintf(stderr, "FAIL: %s (line %d): %s\n", #cond, __LINE__, msg); \ LOG_ERR("FAIL: %s (line %d): %s\n", #cond, __LINE__, msg); \
LOG("%s: %s\n", "test-gguf-model-data", "FAILED"); \
common_log_flush(common_log_main()); \
return 1; \ return 1; \
} \ } \
} while (0) } while (0)
int main() { int main(int argc, char ** argv) {
fprintf(stderr, "=== test-gguf-model-data ===\n"); common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-gguf-model-data");
LOG_INF("=== test-gguf-model-data ===\n");
// Fetch Qwen3-0.6B Q8_0 metadata // Fetch Qwen3-0.6B Q8_0 metadata
LOG_INF(" running %s\n", "ggml-org/Qwen3-0.6B-GGUF (Q8_0)");
auto result = gguf_fetch_model_meta("ggml-org/Qwen3-0.6B-GGUF", "Q8_0"); auto result = gguf_fetch_model_meta("ggml-org/Qwen3-0.6B-GGUF", "Q8_0");
if (!result.has_value()) { if (!result.has_value()) {
fprintf(stderr, "SKIP: could not fetch model metadata (no network or HTTP disabled)\n"); LOG_WRN("SKIP: could not fetch model metadata (no network or HTTP disabled)\n");
LOG("%s: %s\n", "test-gguf-model-data", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
const auto & model = result.value(); const auto & model = result.value();
fprintf(stderr, "Architecture: %s\n", model.architecture.c_str()); LOG_INF("Architecture: %s\n", model.architecture.c_str());
fprintf(stderr, "n_embd: %u\n", model.n_embd); LOG_INF("n_embd: %u\n", model.n_embd);
fprintf(stderr, "n_ff: %u\n", model.n_ff); LOG_INF("n_ff: %u\n", model.n_ff);
fprintf(stderr, "n_vocab: %u\n", model.n_vocab); LOG_INF("n_vocab: %u\n", model.n_vocab);
fprintf(stderr, "n_layer: %u\n", model.n_layer); LOG_INF("n_layer: %u\n", model.n_layer);
fprintf(stderr, "n_head: %u\n", model.n_head); LOG_INF("n_head: %u\n", model.n_head);
fprintf(stderr, "n_head_kv: %u\n", model.n_head_kv); LOG_INF("n_head_kv: %u\n", model.n_head_kv);
fprintf(stderr, "n_expert: %u\n", model.n_expert); LOG_INF("n_expert: %u\n", model.n_expert);
fprintf(stderr, "n_embd_head_k: %u\n", model.n_embd_head_k); LOG_INF("n_embd_head_k: %u\n", model.n_embd_head_k);
fprintf(stderr, "n_embd_head_v: %u\n", model.n_embd_head_v); LOG_INF("n_embd_head_v: %u\n", model.n_embd_head_v);
fprintf(stderr, "tensors: %zu\n", model.tensors.size()); LOG_INF("tensors: %zu\n", model.tensors.size());
// Verify architecture // Verify architecture
TEST_ASSERT(model.architecture == "qwen3", "expected architecture 'qwen3'"); TEST_ASSERT(model.architecture == "qwen3", "expected architecture 'qwen3'");
@@ -83,24 +100,27 @@ int main() {
TEST_ASSERT(result2->tensors.size() == model.tensors.size(), "cached result should match"); TEST_ASSERT(result2->tensors.size() == model.tensors.size(), "cached result should match");
// Test a split MoE model without specifying quant (should default to Q8_0) // Test a split MoE model without specifying quant (should default to Q8_0)
LOG_INF(" running %s\n", "ggml-org/GLM-4.6V-GGUF");
auto result3 = gguf_fetch_model_meta("ggml-org/GLM-4.6V-GGUF"); auto result3 = gguf_fetch_model_meta("ggml-org/GLM-4.6V-GGUF");
if (!result3.has_value()) { if (!result3.has_value()) {
fprintf(stderr, "SKIP: could not fetch GLM-4.6V metadata (no network?)\n"); LOG_WRN("SKIP: could not fetch GLM-4.6V metadata (no network?)\n");
LOG("%s: %s\n", "test-gguf-model-data", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
const auto & model3 = result3.value(); const auto & model3 = result3.value();
fprintf(stderr, "Architecture: %s\n", model3.architecture.c_str()); LOG_INF("Architecture: %s\n", model3.architecture.c_str());
fprintf(stderr, "n_embd: %u\n", model3.n_embd); LOG_INF("n_embd: %u\n", model3.n_embd);
fprintf(stderr, "n_ff: %u\n", model3.n_ff); LOG_INF("n_ff: %u\n", model3.n_ff);
fprintf(stderr, "n_vocab: %u\n", model3.n_vocab); LOG_INF("n_vocab: %u\n", model3.n_vocab);
fprintf(stderr, "n_layer: %u\n", model3.n_layer); LOG_INF("n_layer: %u\n", model3.n_layer);
fprintf(stderr, "n_head: %u\n", model3.n_head); LOG_INF("n_head: %u\n", model3.n_head);
fprintf(stderr, "n_head_kv: %u\n", model3.n_head_kv); LOG_INF("n_head_kv: %u\n", model3.n_head_kv);
fprintf(stderr, "n_expert: %u\n", model3.n_expert); LOG_INF("n_expert: %u\n", model3.n_expert);
fprintf(stderr, "n_embd_head_k: %u\n", model3.n_embd_head_k); LOG_INF("n_embd_head_k: %u\n", model3.n_embd_head_k);
fprintf(stderr, "n_embd_head_v: %u\n", model3.n_embd_head_v); LOG_INF("n_embd_head_v: %u\n", model3.n_embd_head_v);
fprintf(stderr, "tensors: %zu\n", model3.tensors.size()); LOG_INF("tensors: %zu\n", model3.tensors.size());
// Verify architecture // Verify architecture
TEST_ASSERT(model3.architecture == "glm4moe", "expected architecture 'glm4moe'"); TEST_ASSERT(model3.architecture == "glm4moe", "expected architecture 'glm4moe'");
@@ -117,24 +137,27 @@ int main() {
TEST_ASSERT(model3.tensors.size() == 780, "expected tensor count == 780"); TEST_ASSERT(model3.tensors.size() == 780, "expected tensor count == 780");
// Test a hybrid-attention model with array-valued head counts // Test a hybrid-attention model with array-valued head counts
LOG_INF(" running %s\n", "ggml-org/Step-3.5-Flash-GGUF (Q4_K)");
auto result4 = gguf_fetch_model_meta("ggml-org/Step-3.5-Flash-GGUF", "Q4_K"); auto result4 = gguf_fetch_model_meta("ggml-org/Step-3.5-Flash-GGUF", "Q4_K");
if (!result4.has_value()) { if (!result4.has_value()) {
fprintf(stderr, "FAIL: could not fetch Step-3.5-Flash metadata\n"); LOG_ERR("FAIL: could not fetch Step-3.5-Flash metadata\n");
LOG("%s: %s\n", "test-gguf-model-data", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
const auto & model4 = result4.value(); const auto & model4 = result4.value();
fprintf(stderr, "Architecture: %s\n", model4.architecture.c_str()); LOG_INF("Architecture: %s\n", model4.architecture.c_str());
fprintf(stderr, "n_embd: %u\n", model4.n_embd); LOG_INF("n_embd: %u\n", model4.n_embd);
fprintf(stderr, "n_ff: %u\n", model4.n_ff); LOG_INF("n_ff: %u\n", model4.n_ff);
fprintf(stderr, "n_vocab: %u\n", model4.n_vocab); LOG_INF("n_vocab: %u\n", model4.n_vocab);
fprintf(stderr, "n_layer: %u\n", model4.n_layer); LOG_INF("n_layer: %u\n", model4.n_layer);
fprintf(stderr, "n_head: %u\n", model4.n_head); LOG_INF("n_head: %u\n", model4.n_head);
fprintf(stderr, "n_head_kv: %u\n", model4.n_head_kv); LOG_INF("n_head_kv: %u\n", model4.n_head_kv);
fprintf(stderr, "n_expert: %u\n", model4.n_expert); LOG_INF("n_expert: %u\n", model4.n_expert);
fprintf(stderr, "n_embd_head_k: %u\n", model4.n_embd_head_k); LOG_INF("n_embd_head_k: %u\n", model4.n_embd_head_k);
fprintf(stderr, "n_embd_head_v: %u\n", model4.n_embd_head_v); LOG_INF("n_embd_head_v: %u\n", model4.n_embd_head_v);
fprintf(stderr, "tensors: %zu\n", model4.tensors.size()); LOG_INF("tensors: %zu\n", model4.tensors.size());
TEST_ASSERT(model4.architecture == "step35", "expected architecture 'step35'"); TEST_ASSERT(model4.architecture == "step35", "expected architecture 'step35'");
@@ -149,6 +172,8 @@ int main() {
TEST_ASSERT(model4.n_vocab == 128896, "expected n_vocab == 128896"); TEST_ASSERT(model4.n_vocab == 128896, "expected n_vocab == 128896");
TEST_ASSERT(model4.tensors.size() == 754, "expected tensor count == 754"); TEST_ASSERT(model4.tensors.size() == 754, "expected tensor count == 754");
fprintf(stderr, "=== ALL TESTS PASSED ===\n"); LOG_INF("=== ALL TESTS PASSED ===\n");
LOG("%s: %s\n", "test-gguf-model-data", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+130 -72
View File
@@ -3,6 +3,10 @@
#include "../ggml/src/ggml-impl.h" #include "../ggml/src/ggml-impl.h"
#include "gguf.h" #include "gguf.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cmath> #include <cmath>
@@ -748,6 +752,14 @@ static bool handcrafted_check_tensor_data(const gguf_context * gguf_ctx, const u
} }
static std::pair<int, int> test_handcrafted_file(const unsigned int seed) { static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
LOG_INF(" running %s\n", __func__);
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
int npass = 0; int npass = 0;
int ntest = 0; int ntest = 0;
@@ -791,7 +803,7 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
}; };
for (enum handcrafted_file_type hft : hfts) { for (enum handcrafted_file_type hft : hfts) {
printf("%s: handcrafted_file_type=%s\n", __func__, handcrafted_file_type_name(hft).c_str()); LOG_CNT("%s: handcrafted_file_type=%s\n", __func__, handcrafted_file_type_name(hft).c_str());
FILE * file = get_handcrafted_file(seed, hft); FILE * file = get_handcrafted_file(seed, hft);
#ifdef _WIN32 #ifdef _WIN32
@@ -813,24 +825,24 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
struct gguf_context * gguf_ctx = gguf_init_from_file_ptr(file, gguf_params); struct gguf_context * gguf_ctx = gguf_init_from_file_ptr(file, gguf_params);
if (expect_context_not_null(hft)) { if (expect_context_not_null(hft)) {
printf("%s: - context_not_null: ", __func__); LOG_CNT("%s: - context_not_null: ", __func__);
} else { } else {
printf("%s: - context_null: ", __func__); LOG_CNT("%s: - context_null: ", __func__);
} }
if (bool(gguf_ctx) == expect_context_not_null(hft)) { if (bool(gguf_ctx) == expect_context_not_null(hft)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
if (hft >= offset_has_data && !expect_context_not_null(hft)) { if (hft >= offset_has_data && !expect_context_not_null(hft)) {
printf("%s: - no_dangling_ggml_context_pointer: ", __func__); LOG_CNT("%s: - no_dangling_ggml_context_pointer: ", __func__);
if (ctx) { if (ctx) {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} else { } else {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} }
ntest++; ntest++;
@@ -839,23 +851,23 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
const bool alignment_defined = hft == HANDCRAFTED_TENSORS_CUSTOM_ALIGN || hft == HANDCRAFTED_DATA_CUSTOM_ALIGN; const bool alignment_defined = hft == HANDCRAFTED_TENSORS_CUSTOM_ALIGN || hft == HANDCRAFTED_DATA_CUSTOM_ALIGN;
if (expect_context_not_null(hft)) { if (expect_context_not_null(hft)) {
printf("%s: - check_header: ", __func__); LOG_CNT("%s: - check_header: ", __func__);
if (handcrafted_check_header(gguf_ctx, seed, hft >= offset_has_kv, hft >= offset_has_tensors, alignment_defined)) { if (handcrafted_check_header(gguf_ctx, seed, hft >= offset_has_kv, hft >= offset_has_tensors, alignment_defined)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
} }
if (expect_context_not_null(hft) && hft >= offset_has_kv) { if (expect_context_not_null(hft) && hft >= offset_has_kv) {
printf("%s: - check_kv: ", __func__); LOG_CNT("%s: - check_kv: ", __func__);
if (handcrafted_check_kv(gguf_ctx, seed, hft >= offset_has_tensors, alignment_defined)) { if (handcrafted_check_kv(gguf_ctx, seed, hft >= offset_has_tensors, alignment_defined)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
} }
@@ -863,23 +875,23 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
// HANDCRAFTED_TENSORS_ZERO_DIM deliberately mangles the tensor shapes to 0 elements, // HANDCRAFTED_TENSORS_ZERO_DIM deliberately mangles the tensor shapes to 0 elements,
// so only assert that it loads without crashing; skip the exact-geometry comparison. // so only assert that it loads without crashing; skip the exact-geometry comparison.
if (expect_context_not_null(hft) && hft >= offset_has_tensors && hft != HANDCRAFTED_TENSORS_ZERO_DIM) { if (expect_context_not_null(hft) && hft >= offset_has_tensors && hft != HANDCRAFTED_TENSORS_ZERO_DIM) {
printf("%s: - check_tensors: ", __func__); LOG_CNT("%s: - check_tensors: ", __func__);
if (handcrafted_check_tensors(gguf_ctx, seed)) { if (handcrafted_check_tensors(gguf_ctx, seed)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
} }
if (expect_context_not_null(hft) && hft >= offset_has_data) { if (expect_context_not_null(hft) && hft >= offset_has_data) {
printf("%s: - check_tensor_data: ", __func__); LOG_CNT("%s: - check_tensor_data: ", __func__);
if (handcrafted_check_tensor_data(gguf_ctx, seed, file)) { if (handcrafted_check_tensor_data(gguf_ctx, seed, file)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
} }
@@ -889,7 +901,7 @@ static std::pair<int, int> test_handcrafted_file(const unsigned int seed) {
ggml_free(ctx); ggml_free(ctx);
gguf_free(gguf_ctx); gguf_free(gguf_ctx);
} }
printf("\n"); LOG_CNT("\n");
} }
@@ -1185,10 +1197,16 @@ static std::pair<int, int> test_roundtrip(
ggml_backend_dev_t dev, const unsigned int seed, const bool only_meta, ggml_backend_dev_t dev, const unsigned int seed, const bool only_meta,
const roundtrip_read_mode read_mode) { const roundtrip_read_mode read_mode) {
ggml_backend_t backend = ggml_backend_dev_init(dev, nullptr); ggml_backend_t backend = ggml_backend_dev_init(dev, nullptr);
printf("%s: device=%s, backend=%s, only_meta=%s, read_mode=%s\n", LOG_INF(" running %s: device=%s, backend=%s, only_meta=%s, read_mode=%s\n",
__func__, ggml_backend_dev_description(dev), ggml_backend_name(backend), __func__, ggml_backend_dev_description(dev), ggml_backend_name(backend),
only_meta ? "yes" : "no", roundtrip_read_mode_name(read_mode)); only_meta ? "yes" : "no", roundtrip_read_mode_name(read_mode));
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
int npass = 0; int npass = 0;
int ntest = 0; int ntest = 0;
@@ -1239,76 +1257,76 @@ static std::pair<int, int> test_roundtrip(
gguf_ctx_1 = gguf_init_from_file_ptr(file, gguf_params); gguf_ctx_1 = gguf_init_from_file_ptr(file, gguf_params);
} }
printf("%s: same_version: ", __func__); LOG_CNT("%s: same_version: ", __func__);
if (gguf_get_version(gguf_ctx_0) == gguf_get_version(gguf_ctx_1)) { if (gguf_get_version(gguf_ctx_0) == gguf_get_version(gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: same_n_kv: ", __func__); LOG_CNT("%s: same_n_kv: ", __func__);
if (gguf_get_n_kv(gguf_ctx_0) == gguf_get_n_kv(gguf_ctx_1)) { if (gguf_get_n_kv(gguf_ctx_0) == gguf_get_n_kv(gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: same_n_tensors: ", __func__); LOG_CNT("%s: same_n_tensors: ", __func__);
if (gguf_get_n_tensors(gguf_ctx_0) == gguf_get_n_tensors(gguf_ctx_1)) { if (gguf_get_n_tensors(gguf_ctx_0) == gguf_get_n_tensors(gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_orig_kv_in_read: ", __func__); LOG_CNT("%s: all_orig_kv_in_read: ", __func__);
if (all_kv_in_other(gguf_ctx_0, gguf_ctx_1)) { if (all_kv_in_other(gguf_ctx_0, gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_read_kv_in_orig: ", __func__); LOG_CNT("%s: all_read_kv_in_orig: ", __func__);
if (all_kv_in_other(gguf_ctx_1, gguf_ctx_0)) { if (all_kv_in_other(gguf_ctx_1, gguf_ctx_0)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_orig_tensors_in_read: ", __func__); LOG_CNT("%s: all_orig_tensors_in_read: ", __func__);
if (all_tensors_in_other(gguf_ctx_0, gguf_ctx_1)) { if (all_tensors_in_other(gguf_ctx_0, gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_read_tensors_in_orig: ", __func__); LOG_CNT("%s: all_read_tensors_in_orig: ", __func__);
if (all_tensors_in_other(gguf_ctx_1, gguf_ctx_0)) { if (all_tensors_in_other(gguf_ctx_1, gguf_ctx_0)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
if (!only_meta) { if (!only_meta) {
printf("%s: same_tensor_data: ", __func__); LOG_CNT("%s: same_tensor_data: ", __func__);
if (same_tensor_data(ctx_0, ctx_1)) { if (same_tensor_data(ctx_0, ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
} }
@@ -1321,13 +1339,20 @@ static std::pair<int, int> test_roundtrip(
ggml_backend_free(backend); ggml_backend_free(backend);
fclose(file); fclose(file);
printf("\n"); LOG_CNT("\n");
return std::make_pair(npass, ntest); return std::make_pair(npass, ntest);
} }
static std::pair<int, int> test_gguf_set_kv(ggml_backend_dev_t dev, const unsigned int seed) { static std::pair<int, int> test_gguf_set_kv(ggml_backend_dev_t dev, const unsigned int seed) {
ggml_backend_t backend = ggml_backend_dev_init(dev, nullptr); ggml_backend_t backend = ggml_backend_dev_init(dev, nullptr);
printf("%s: device=%s, backend=%s\n", __func__, ggml_backend_dev_description(dev), ggml_backend_name(backend)); LOG_INF(" running %s: device=%s, backend=%s\n",
__func__, ggml_backend_dev_description(dev), ggml_backend_name(backend));
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
int npass = 0; int npass = 0;
int ntest = 0; int ntest = 0;
@@ -1357,50 +1382,50 @@ static std::pair<int, int> test_gguf_set_kv(ggml_backend_dev_t dev, const unsign
gguf_set_kv(gguf_ctx_1, gguf_ctx_0); gguf_set_kv(gguf_ctx_1, gguf_ctx_0);
gguf_set_kv(gguf_ctx_2, gguf_ctx_0); gguf_set_kv(gguf_ctx_2, gguf_ctx_0);
printf("%s: same_n_kv: ", __func__); LOG_CNT("%s: same_n_kv: ", __func__);
if (gguf_get_n_kv(gguf_ctx_0) == gguf_get_n_kv(gguf_ctx_2)) { if (gguf_get_n_kv(gguf_ctx_0) == gguf_get_n_kv(gguf_ctx_2)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_kv_0_in_1: ", __func__); LOG_CNT("%s: all_kv_0_in_1: ", __func__);
if (all_kv_in_other(gguf_ctx_0, gguf_ctx_1)) { if (all_kv_in_other(gguf_ctx_0, gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_kv_0_in_2: ", __func__); LOG_CNT("%s: all_kv_0_in_2: ", __func__);
if (all_kv_in_other(gguf_ctx_0, gguf_ctx_2)) { if (all_kv_in_other(gguf_ctx_0, gguf_ctx_2)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
gguf_set_kv(gguf_ctx_0, gguf_ctx_1); gguf_set_kv(gguf_ctx_0, gguf_ctx_1);
printf("%s: same_n_kv_after_double_copy: ", __func__); LOG_CNT("%s: same_n_kv_after_double_copy: ", __func__);
if (gguf_get_n_kv(gguf_ctx_0) == gguf_get_n_kv(gguf_ctx_1)) { if (gguf_get_n_kv(gguf_ctx_0) == gguf_get_n_kv(gguf_ctx_1)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
printf("%s: all_kv_1_in_0_after_double_copy: ", __func__); LOG_CNT("%s: all_kv_1_in_0_after_double_copy: ", __func__);
if (all_kv_in_other(gguf_ctx_1, gguf_ctx_0)) { if (all_kv_in_other(gguf_ctx_1, gguf_ctx_0)) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
ntest++; ntest++;
@@ -1413,7 +1438,7 @@ static std::pair<int, int> test_gguf_set_kv(ggml_backend_dev_t dev, const unsign
gguf_free(gguf_ctx_2); gguf_free(gguf_ctx_2);
ggml_backend_free(backend); ggml_backend_free(backend);
printf("\n"); LOG_CNT("\n");
return std::make_pair(npass, ntest); return std::make_pair(npass, ntest);
} }
@@ -1423,17 +1448,39 @@ static void print_usage() {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
if (argc > 2) { common_params params;
params.model.path = "."; // this test takes no model
common_init();
// this test takes an optional seed as its only positional argument
std::vector<std::string> positional;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
positional.push_back(argv[i]);
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
if (positional.size() > 1) {
print_usage(); print_usage();
return 1; return 1;
} }
std::random_device rd; std::random_device rd;
const unsigned int seed = argc < 2 ? rd() : std::stoi(argv[1]); const unsigned int seed = positional.empty() ? rd() : std::stoi(positional[0]);
LOG("%s: running\n", "test-gguf");
// Initialize ggml backends early so the prints aren't interleaved with the test results: // Initialize ggml backends early so the prints aren't interleaved with the test results:
ggml_backend_dev_count(); ggml_backend_dev_count();
fprintf(stderr, "\n"); LOG_CNT("\n"); // bare separator, LOG_CNT adds no prefix
int npass = 0; int npass = 0;
int ntest = 0; int ntest = 0;
@@ -1469,11 +1516,22 @@ int main(int argc, char ** argv) {
} }
} }
const bool ok = npass == ntest;
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
common_log_flush(common_log_main());
printf("%d/%d tests passed\n", npass, ntest); printf("%d/%d tests passed\n", npass, ntest);
if (npass != ntest) { if (!ok) {
printf("\033[1;31mFAIL\033[0m\n"); printf("%sFAIL%s\n", col_fail, col_end);
return 1; } else {
printf("%sOK%s\n", col_ok, col_end);
} }
printf("\033[1;32mOK\033[0m\n"); LOG("%s: %s\n", "test-gguf", ok ? "PASSED" : "FAILED");
return 0; common_log_flush(common_log_main());
return ok ? 0 : 1;
} }
+57 -29
View File
@@ -9,6 +9,10 @@
#include "json.h" #include "json.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cassert> #include <cassert>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -24,14 +28,14 @@ static llama_grammar * build_grammar(const std::string & grammar_str) {
} }
static bool test_build_grammar_fails(const std::string & grammar_str) { static bool test_build_grammar_fails(const std::string & grammar_str) {
fprintf(stderr, "⚫ Testing failure for grammar: %s\n", grammar_str.c_str()); LOG_INF("⚫ Testing failure for grammar: %s\n", grammar_str.c_str());
bool grammar_fails = false; bool grammar_fails = false;
llama_grammar * grammar = build_grammar(grammar_str); llama_grammar * grammar = build_grammar(grammar_str);
if (grammar != nullptr) { if (grammar != nullptr) {
fprintf(stderr, " ❌ Expected build failure, but succeeded\n"); LOG_ERR(" ❌ Expected build failure, but succeeded\n");
} else { } else {
grammar_fails = true; grammar_fails = true;
fprintf(stdout, " ✅︎\n"); LOG_CNT(" ✅︎\n");
} }
return grammar_fails; return grammar_fails;
} }
@@ -114,8 +118,8 @@ static bool match_string(const std::string & input, llama_grammar * grammar) {
} }
static void test(const std::string & test_desc, const std::string & grammar_str, const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) { static void test(const std::string & test_desc, const std::string & grammar_str, const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) {
fprintf(stderr, "⚫ Testing %s\n%s\n", test_desc.c_str(), grammar_str.c_str()); LOG_INF("⚫ Testing %s\n%s\n", test_desc.c_str(), grammar_str.c_str());
fflush(stderr); common_log_flush(common_log_main());
auto * grammar = build_grammar(grammar_str); auto * grammar = build_grammar(grammar_str);
@@ -124,17 +128,18 @@ static void test(const std::string & test_desc, const std::string & grammar_str,
llama_grammar_stacks & stacks_cur = llama_grammar_get_stacks(grammar); llama_grammar_stacks & stacks_cur = llama_grammar_get_stacks(grammar);
fprintf(stderr, " 🔵 Valid strings:\n"); LOG_INF(" 🔵 Valid strings:\n");
// Passing strings // Passing strings
for (const auto & test_string : passing_strings) { for (const auto & test_string : passing_strings) {
fprintf(stderr, " \"%s\" ", test_string.c_str()); // partial line, completed by the next message: LOG_CNT adds no prefix
fflush(stderr); LOG_CNT(" \"%s\" ", test_string.c_str());
common_log_flush(common_log_main());
bool matched = match_string(test_string, grammar); bool matched = match_string(test_string, grammar);
if (!matched) { if (!matched) {
fprintf(stderr, "❌ (failed to match)\n"); LOG_ERR("❌ (failed to match)\n");
// DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed. // DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed.
// DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf // DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf
@@ -151,9 +156,9 @@ static void test(const std::string & test_desc, const std::string & grammar_str,
fclose(string_file); fclose(string_file);
} }
fprintf(stderr, "\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following command: ./llama-gbnf-validator test-grammar-integration.grammar.gbnf test-grammar-integration.string.txt\n\n"); LOG_ERR("\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following command: ./llama-gbnf-validator test-grammar-integration.grammar.gbnf test-grammar-integration.string.txt\n\n");
} else { } else {
fprintf(stdout, "✅︎\n"); LOG_CNT("✅︎\n");
} }
assert(matched); assert(matched);
@@ -162,19 +167,19 @@ static void test(const std::string & test_desc, const std::string & grammar_str,
stacks_cur = stacks_org; stacks_cur = stacks_org;
} }
fprintf(stderr, " 🟠 Invalid strings:\n"); LOG_INF(" 🟠 Invalid strings:\n");
// Failing strings // Failing strings
for (const auto & test_string : failing_strings) { for (const auto & test_string : failing_strings) {
fprintf(stderr, " \"%s\" ", test_string.c_str()); LOG_CNT(" \"%s\" ", test_string.c_str());
fflush(stderr); common_log_flush(common_log_main());
bool matched = match_string(test_string, grammar); bool matched = match_string(test_string, grammar);
if (matched) { if (matched) {
fprintf(stderr, "❌ (incorrectly matched)\n"); LOG_ERR("❌ (incorrectly matched)\n");
} else { } else {
fprintf(stdout, "✅︎\n"); LOG_CNT("✅︎\n");
} }
assert(!matched); assert(!matched);
@@ -193,6 +198,7 @@ static void test_schema(const std::string & test_desc, const std::string & schem
} }
static void test_simple_grammar() { static void test_simple_grammar() {
LOG_INF(" running %s\n", __func__);
test_schema( test_schema(
"min 0", "min 0",
R"""({ R"""({
@@ -510,6 +516,7 @@ static void test_simple_grammar() {
} }
static void test_complex_grammar() { static void test_complex_grammar() {
LOG_INF(" running %s\n", __func__);
// Test case for a more complex grammar, with both failure strings and success strings // Test case for a more complex grammar, with both failure strings and success strings
test_grammar( test_grammar(
"medium complexity grammar", "medium complexity grammar",
@@ -599,6 +606,7 @@ static void test_complex_grammar() {
} }
static void test_special_chars() { static void test_special_chars() {
LOG_INF(" running %s\n", __func__);
// A collection of tests to exercise special characters such as "." // A collection of tests to exercise special characters such as "."
test_grammar( test_grammar(
"special characters", "special characters",
@@ -626,6 +634,7 @@ static void test_special_chars() {
} }
static void test_quantifiers() { static void test_quantifiers() {
LOG_INF(" running %s\n", __func__);
// A collection of tests to exercise * + and ? quantifiers // A collection of tests to exercise * + and ? quantifiers
test_grammar( test_grammar(
@@ -809,7 +818,8 @@ static void test_quantifiers() {
} }
static void test_failure_missing_root() { static void test_failure_missing_root() {
fprintf(stderr, "⚫ Testing missing root node:\n"); LOG_INF(" running %s\n", __func__);
LOG_INF("⚫ Testing missing root node:\n");
// Test case for a grammar that is missing a root rule // Test case for a grammar that is missing a root rule
const std::string grammar_str = R"""( const std::string grammar_str = R"""(
rot ::= expr rot ::= expr
@@ -825,11 +835,12 @@ static void test_failure_missing_root() {
// Ensure we do NOT have a root node // Ensure we do NOT have a root node
assert(parsed_grammar.symbol_ids.find("root") == parsed_grammar.symbol_ids.end()); assert(parsed_grammar.symbol_ids.find("root") == parsed_grammar.symbol_ids.end());
fprintf(stderr, " ✅︎ Passed\n"); LOG_INF(" ✅︎ Passed\n");
} }
static void test_failure_missing_reference() { static void test_failure_missing_reference() {
fprintf(stderr, "⚫ Testing missing reference node:\n"); LOG_INF(" running %s\n", __func__);
LOG_INF("⚫ Testing missing reference node:\n");
// Test case for a grammar that is missing a referenced rule // Test case for a grammar that is missing a referenced rule
const std::string grammar_str = const std::string grammar_str =
@@ -838,6 +849,8 @@ static void test_failure_missing_reference() {
term ::= numero term ::= numero
number ::= [0-9]+)"""; number ::= [0-9]+)""";
// the parser below writes to stderr directly, so drain the log first
common_log_flush(common_log_main());
fprintf(stderr, " Expected error: "); fprintf(stderr, " Expected error: ");
llama_grammar_parser parsed_grammar; llama_grammar_parser parsed_grammar;
@@ -847,11 +860,12 @@ static void test_failure_missing_reference() {
assert(parsed_grammar.rules.empty()); assert(parsed_grammar.rules.empty());
fprintf(stderr, " End of expected error.\n"); fprintf(stderr, " End of expected error.\n");
fprintf(stderr, " ✅︎ Passed\n"); LOG_INF(" ✅︎ Passed\n");
} }
static void test_failure_left_recursion() { static void test_failure_left_recursion() {
fprintf(stderr, "⚫ Testing left recursion detection:\n"); LOG_INF(" running %s\n", __func__);
LOG_INF("⚫ Testing left recursion detection:\n");
// Test simple left recursion detection // Test simple left recursion detection
const std::string simple_str = R"""(root ::= "a" | root "a")"""; const std::string simple_str = R"""(root ::= "a" | root "a")""";
@@ -879,11 +893,12 @@ static void test_failure_left_recursion() {
empty ::= "blah" | )"""; empty ::= "blah" | )""";
assert(test_build_grammar_fails(hardest_str)); assert(test_build_grammar_fails(hardest_str));
fprintf(stderr, " ✅︎ Passed\n"); LOG_INF(" ✅︎ Passed\n");
} }
static void test_failure_missing_root_symbol() { static void test_failure_missing_root_symbol() {
fprintf(stderr, "⚫ Testing missing root symbol:\n"); LOG_INF(" running %s\n", __func__);
LOG_INF("⚫ Testing missing root symbol:\n");
const std::string grammar_str = R"""( const std::string grammar_str = R"""(
root ::= "foobar" root ::= "foobar"
@@ -892,11 +907,12 @@ static void test_failure_missing_root_symbol() {
llama_grammar * failure_result = build_grammar_with_root(grammar_str, "nonexistent"); llama_grammar * failure_result = build_grammar_with_root(grammar_str, "nonexistent");
assert(failure_result == nullptr); assert(failure_result == nullptr);
fprintf(stderr, " ✅︎ Passed\n"); LOG_INF(" ✅︎ Passed\n");
} }
static void test_custom_root_symbol_check() { static void test_custom_root_symbol_check() {
fprintf(stderr, "⚫ Testing custom root symbol check:\n"); LOG_INF(" running %s\n", __func__);
LOG_INF("⚫ Testing custom root symbol check:\n");
const std::string custom_root_grammar_str = R"""( const std::string custom_root_grammar_str = R"""(
foobar ::= "foobar" foobar ::= "foobar"
@@ -909,10 +925,11 @@ static void test_custom_root_symbol_check() {
assert(success_result != nullptr); assert(success_result != nullptr);
llama_grammar_free_impl(success_result); llama_grammar_free_impl(success_result);
fprintf(stderr, " ✅︎ Passed\n"); LOG_INF(" ✅︎ Passed\n");
} }
static void test_json_schema() { static void test_json_schema() {
LOG_INF(" running %s\n", __func__);
// Note that this is similar to the regular grammar tests, // Note that this is similar to the regular grammar tests,
// but we convert each json schema to a grammar before parsing. // but we convert each json schema to a grammar before parsing.
// Otherwise, this test structure is the same. // Otherwise, this test structure is the same.
@@ -1478,8 +1495,16 @@ static void test_json_schema() {
); );
} }
int main() { int main(int argc, char ** argv) {
fprintf(stdout, "Running grammar integration tests...\n"); common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-grammar-integration");
test_simple_grammar(); test_simple_grammar();
test_complex_grammar(); test_complex_grammar();
test_special_chars(); test_special_chars();
@@ -1490,6 +1515,9 @@ int main() {
test_failure_missing_root_symbol(); test_failure_missing_root_symbol();
test_custom_root_symbol_check(); test_custom_root_symbol_check();
test_json_schema(); test_json_schema();
fprintf(stdout, "All tests passed.\n"); common_log_flush(common_log_main());
// the tests abort on failure, so reaching this point means they all passed
LOG("%s: %s\n", "test-grammar-integration", "PASSED");
return 0; return 0;
} }
+48 -23
View File
@@ -4,6 +4,9 @@
#include "sampling.h" #include "sampling.h"
#include "arg.h"
#include "log.h"
#include <cassert> #include <cassert>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -49,22 +52,23 @@ static bool match_string(const std::string & input, llama_sampler * grammar) {
static void test(const std::string & test_desc, const std::string & grammar_str, static void test(const std::string & test_desc, const std::string & grammar_str,
const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) { const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) {
fprintf(stderr, "⚫ Testing %s\n%s\n", test_desc.c_str(), grammar_str.c_str()); LOG_INF("⚫ Testing %s\n%s\n", test_desc.c_str(), grammar_str.c_str());
fflush(stderr); common_log_flush(common_log_main());
auto * grammar = llama_sampler_init_llg(vocab, "lark", grammar_str.c_str()); auto * grammar = llama_sampler_init_llg(vocab, "lark", grammar_str.c_str());
fprintf(stderr, " 🔵 Valid strings:\n"); LOG_INF(" 🔵 Valid strings:\n");
// Passing strings // Passing strings
for (const auto & test_string : passing_strings) { for (const auto & test_string : passing_strings) {
fprintf(stderr, " \"%s\" ", test_string.c_str()); // partial line, completed by the next message: LOG_CNT adds no prefix
fflush(stderr); LOG_CNT(" \"%s\" ", test_string.c_str());
common_log_flush(common_log_main());
bool matched = match_string(test_string, grammar); bool matched = match_string(test_string, grammar);
if (!matched) { if (!matched) {
fprintf(stderr, "❌ (failed to match)\n"); LOG_ERR("❌ (failed to match)\n");
// DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed. // DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed.
// DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf // DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf
@@ -81,8 +85,7 @@ static void test(const std::string & test_desc, const std::string & grammar_str,
fclose(string_file); fclose(string_file);
} }
fprintf(stderr, LOG_ERR("\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following "
"\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following "
"command: ./test-gbnf-validator test-grammar-integration.grammar.gbnf " "command: ./test-gbnf-validator test-grammar-integration.grammar.gbnf "
"test-grammar-integration.string.txt\n\n"); "test-grammar-integration.string.txt\n\n");
} else { } else {
@@ -92,17 +95,18 @@ static void test(const std::string & test_desc, const std::string & grammar_str,
assert(matched); assert(matched);
} }
fprintf(stderr, " 🟠 Invalid strings:\n"); LOG_INF(" 🟠 Invalid strings:\n");
// Failing strings // Failing strings
for (const auto & test_string : failing_strings) { for (const auto & test_string : failing_strings) {
fprintf(stderr, " \"%s\" ", test_string.c_str()); // partial line, completed by the next message: LOG_CNT adds no prefix
fflush(stderr); LOG_CNT(" \"%s\" ", test_string.c_str());
common_log_flush(common_log_main());
bool matched = match_string(test_string, grammar); bool matched = match_string(test_string, grammar);
if (matched) { if (matched) {
fprintf(stderr, "❌ (incorrectly matched)\n"); LOG_ERR("❌ (incorrectly matched)\n");
} else { } else {
fprintf(stdout, "✅︎\n"); fprintf(stdout, "✅︎\n");
} }
@@ -1124,11 +1128,11 @@ start: /[A-Z ]*/)";
for (const auto token : tokens) { for (const auto token : tokens) {
one_hot(tok_arr, token); one_hot(tok_arr, token);
fprintf(stderr, "applying token: %d\n", token); LOG_DBG("applying token: %d\n", token);
llama_sampler_apply(sampler, &tok_arr); llama_sampler_apply(sampler, &tok_arr);
auto idx = tok_arr.selected; auto idx = tok_arr.selected;
fprintf(stderr, " -> %d %f\n", cur[idx].id, cur[idx].logit); LOG_DBG(" -> %d %f\n", cur[idx].id, cur[idx].logit);
assert(cur[tok_arr.selected].id == token); assert(cur[tok_arr.selected].id == token);
llama_sampler_accept(sampler, token); llama_sampler_accept(sampler, token);
} }
@@ -1144,17 +1148,35 @@ start: /[A-Z ]*/)";
assert(cur[tok_arr.selected].id == tok_eos); assert(cur[tok_arr.selected].id == tok_eos);
} }
int main(int argc, const char ** argv) { int main(int argc, char ** argv) {
fprintf(stdout, "Running llguidance integration tests...\n"); common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (argc != 2) { std::string vocab_file;
fprintf(stderr, "Usage: %s <vocab-file>\n", argv[0]); std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (vocab_file.empty()) {
vocab_file = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1; return 1;
} }
const char * vocab_file = argv[1]; fprintf(stdout, "Running llguidance integration tests...\n");
fprintf(stderr, "reading vocab from: '%s'\n", vocab_file); if (vocab_file.empty()) {
LOG_ERR("Usage: %s <vocab-file>\n", argv[0]);
common_log_flush(common_log_main());
return 1;
}
LOG_INF("reading vocab from: '%s'\n", vocab_file.c_str());
llama_model * model; llama_model * model;
llama_context * ctx; llama_context * ctx;
@@ -1167,10 +1189,11 @@ int main(int argc, const char ** argv) {
mparams.vocab_only = true; mparams.vocab_only = true;
model = llama_model_load_from_file(vocab_file, mparams); model = llama_model_load_from_file(vocab_file.c_str(), mparams);
if (model == NULL) { if (model == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, vocab_file); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, vocab_file.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -1180,8 +1203,9 @@ int main(int argc, const char ** argv) {
ctx = llama_init_from_model(model, cparams); ctx = llama_init_from_model(model, cparams);
if (ctx == NULL) { if (ctx == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, vocab_file); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, vocab_file.c_str());
llama_model_free(model); llama_model_free(model);
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
@@ -1199,6 +1223,7 @@ int main(int argc, const char ** argv) {
llama_free(ctx); llama_free(ctx);
llama_model_free(model); llama_model_free(model);
common_log_flush(common_log_main());
fprintf(stdout, "All tests passed.\n"); fprintf(stdout, "All tests passed.\n");
return 0; return 0;
} }
+47 -30
View File
@@ -7,6 +7,10 @@
// TODO: shold not include libllama sources // TODO: shold not include libllama sources
#include "../src/llama-grammar.h" #include "../src/llama-grammar.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cassert> #include <cassert>
static const char * type_str(llama_gretype type) { static const char * type_str(llama_gretype type) {
@@ -33,52 +37,53 @@ static void verify_parsing(const char *grammar_bytes, const std::vector<std::pai
} }
auto print_all = [&]() { auto print_all = [&]() {
fprintf(stderr, " verify_parsing(R\"\"\"(%s)\"\"\", {\n", grammar_bytes); // dump is emitted in partial lines: LOG_CNT adds no prefix
LOG_CNT(" verify_parsing(R\"\"\"(%s)\"\"\", {\n", grammar_bytes);
for (auto it = parsed_grammar.symbol_ids.begin(); it != parsed_grammar.symbol_ids.end(); ++it) { for (auto it = parsed_grammar.symbol_ids.begin(); it != parsed_grammar.symbol_ids.end(); ++it) {
fprintf(stderr, " {\"%s\", %u},\n", it->first.c_str(), it->second); LOG_CNT(" {\"%s\", %u},\n", it->first.c_str(), it->second);
} }
fprintf(stderr, " }, {\n"); LOG_CNT(" }, {\n");
for (size_t i_rule = 0; i_rule < parsed_grammar.rules.size(); i_rule++) { for (size_t i_rule = 0; i_rule < parsed_grammar.rules.size(); i_rule++) {
fprintf(stderr, " // %s (index %zu)\n", symbol_names[i_rule].c_str(), i_rule); LOG_CNT(" // %s (index %zu)\n", symbol_names[i_rule].c_str(), i_rule);
auto & rule = parsed_grammar.rules[i_rule]; auto & rule = parsed_grammar.rules[i_rule];
for (uint32_t i = 0; i < rule.size(); i++) { for (uint32_t i = 0; i < rule.size(); i++) {
std::string rule_str; std::string rule_str;
fprintf(stderr, " {%s, ", type_str(rule[i].type)); LOG_CNT(" {%s, ", type_str(rule[i].type));
if (rule[i].type == LLAMA_GRETYPE_CHAR || rule[i].type == LLAMA_GRETYPE_CHAR_ALT || if (rule[i].type == LLAMA_GRETYPE_CHAR || rule[i].type == LLAMA_GRETYPE_CHAR_ALT ||
rule[i].type == LLAMA_GRETYPE_CHAR_NOT || rule[i].type == LLAMA_GRETYPE_CHAR_RNG_UPPER) { rule[i].type == LLAMA_GRETYPE_CHAR_NOT || rule[i].type == LLAMA_GRETYPE_CHAR_RNG_UPPER) {
char c = rule[i].value; char c = rule[i].value;
if (c == '\n') { if (c == '\n') {
fprintf(stderr, "'\\n'"); LOG_CNT("'\\n'");
} else if (c == '\t') { } else if (c == '\t') {
fprintf(stderr, "'\\t'"); LOG_CNT("'\\t'");
} else if (c == '\r') { } else if (c == '\r') {
fprintf(stderr, "'\\r'"); LOG_CNT("'\\r'");
} else if (c == '\0') { } else if (c == '\0') {
fprintf(stderr, "'\\0'"); LOG_CNT("'\\0'");
} else { } else {
fprintf(stderr, "'%c'", c); LOG_CNT("'%c'", c);
} }
} else if (rule[i].type == LLAMA_GRETYPE_RULE_REF) { } else if (rule[i].type == LLAMA_GRETYPE_RULE_REF) {
fprintf(stderr, "/* %s */ %u", symbol_names[rule[i].value].c_str(), rule[i].value); LOG_CNT("/* %s */ %u", symbol_names[rule[i].value].c_str(), rule[i].value);
} else { } else {
fprintf(stderr, "%u", rule[i].value); LOG_CNT("%u", rule[i].value);
} }
fprintf(stderr, "},\n"); LOG_CNT("},\n");
} }
} }
fprintf(stderr, " });\n"); LOG_CNT(" });\n");
}; };
if (getenv("TEST_GRAMMAR_PARSER_PRINT_ALL")) { if (getenv("TEST_GRAMMAR_PARSER_PRINT_ALL")) {
print_all(); print_all();
fprintf(stderr, "\n"); LOG_CNT("\n");
return; return;
} }
fprintf(stderr, "Testing grammar:%s\n", grammar_bytes); LOG_INF("Testing grammar:%s\n", grammar_bytes);
if (parsed_grammar.symbol_ids.size() != expected.size()) { if (parsed_grammar.symbol_ids.size() != expected.size()) {
fprintf(stderr, "Code to update expectation (set TEST_GRAMMAR_PARSER_PRINT_ALL=1 to print all):\n"); LOG_ERR("Code to update expectation (set TEST_GRAMMAR_PARSER_PRINT_ALL=1 to print all):\n");
print_all(); print_all();
assert(parsed_grammar.symbol_ids.size() == expected.size()); assert(parsed_grammar.symbol_ids.size() == expected.size());
} }
@@ -92,11 +97,11 @@ static void verify_parsing(const char *grammar_bytes, const std::vector<std::pai
// pretty print error message before asserting // pretty print error message before asserting
if (expected_pair.first != key || expected_pair.second != value) if (expected_pair.first != key || expected_pair.second != value)
{ {
fprintf(stderr, "index: %u\n", index); LOG_ERR("index: %u\n", index);
fprintf(stderr, "expected_pair: %s, %u\n", expected_pair.first.c_str(), expected_pair.second); LOG_ERR("expected_pair: %s, %u\n", expected_pair.first.c_str(), expected_pair.second);
fprintf(stderr, "actual_pair: %s, %u\n", key.c_str(), value); LOG_ERR("actual_pair: %s, %u\n", key.c_str(), value);
fprintf(stderr, "expected_pair != actual_pair\n"); LOG_ERR("expected_pair != actual_pair\n");
fprintf(stderr, "Code to update expectation (set TEST_GRAMMAR_PARSER_PRINT_ALL=1 to print all):\n"); LOG_ERR("Code to update expectation (set TEST_GRAMMAR_PARSER_PRINT_ALL=1 to print all):\n");
print_all(); print_all();
} }
@@ -117,12 +122,12 @@ static void verify_parsing(const char *grammar_bytes, const std::vector<std::pai
// pretty print error message before asserting // pretty print error message before asserting
if (expected_element.type != element.type || expected_element.value != element.value) if (expected_element.type != element.type || expected_element.value != element.value)
{ {
fprintf(stderr, "index: %u\n", index); LOG_ERR("index: %u\n", index);
fprintf(stderr, "expected_element: %s, %u\n", type_str(expected_element.type), expected_element.value); LOG_ERR("expected_element: %s, %u\n", type_str(expected_element.type), expected_element.value);
fprintf(stderr, "actual_element: %s, %u\n", type_str(element.type), element.value); LOG_ERR("actual_element: %s, %u\n", type_str(element.type), element.value);
fprintf(stderr, "expected_element != actual_element\n"); LOG_ERR("expected_element != actual_element\n");
fprintf(stderr, "all elements:\n"); LOG_ERR("all elements:\n");
fprintf(stderr, "Code to update expectation (set TEST_GRAMMAR_PARSER_PRINT_ALL=1 to print all):\n"); LOG_ERR("Code to update expectation (set TEST_GRAMMAR_PARSER_PRINT_ALL=1 to print all):\n");
print_all(); print_all();
} }
@@ -133,14 +138,23 @@ static void verify_parsing(const char *grammar_bytes, const std::vector<std::pai
} }
static void verify_failure(const char * grammar_bytes) { static void verify_failure(const char * grammar_bytes) {
fprintf(stderr, "Testing expected failure:%s\n", grammar_bytes); LOG_INF("Testing expected failure:%s\n", grammar_bytes);
llama_grammar_parser result; llama_grammar_parser result;
result.parse(grammar_bytes); result.parse(grammar_bytes);
assert(result.rules.empty() && "should have failed"); assert(result.rules.empty() && "should have failed");
} }
int main() int main(int argc, char ** argv)
{ {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-grammar-parser");
verify_failure(R"""( verify_failure(R"""(
root ::= "a"{,}" root ::= "a"{,}"
)"""); )""");
@@ -580,5 +594,8 @@ int main()
{LLAMA_GRETYPE_END, 0}, {LLAMA_GRETYPE_END, 0},
}); });
// the test aborts on a mismatch, so reaching this point means it passed
LOG("%s: %s\n", "test-grammar-parser", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+21 -1
View File
@@ -2,7 +2,9 @@
#include <iostream> #include <iostream>
#include <random> #include <random>
#include <cstdlib> #include <cstdlib>
#include <vector>
#include "arg.h"
#include "json.h" #include "json.h"
#include "subproc.h" #include "subproc.h"
@@ -12,6 +14,7 @@
#include "jinja/utils.h" #include "jinja/utils.h"
#include "jinja/caps.h" #include "jinja/caps.h"
#include "log.h"
#include "testing.h" #include "testing.h"
using json = common_json; using json = common_json;
@@ -41,6 +44,10 @@ static void test_fuzzing(testing & t);
static bool g_python_mode = false; static bool g_python_mode = false;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
testing t(std::cout); testing t(std::cout);
t.verbose = true; t.verbose = true;
@@ -49,14 +56,24 @@ int main(int argc, char *argv[]) {
// only use this for cross-checking, not for correctness // only use this for cross-checking, not for correctness
// note: the implementation of this flag is basic, only intented to be used by maintainers // note: the implementation of this flag is basic, only intented to be used by maintainers
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
std::string arg = argv[i]; std::string arg = argv[i];
if (arg == "-py") { if (arg == "-py") {
g_python_mode = true; g_python_mode = true;
} else if (arg[0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else { } else {
t.set_filter(arg); t.set_filter(arg);
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-jinja");
t.test("whitespace control", test_whitespace_control); t.test("whitespace control", test_whitespace_control);
t.test("conditionals", test_conditionals); t.test("conditionals", test_conditionals);
@@ -80,7 +97,10 @@ int main(int argc, char *argv[]) {
t.test("fuzzing", test_fuzzing); t.test("fuzzing", test_fuzzing);
} }
return t.summary(); const int rc = t.summary();
LOG("%s: %s\n", "test-jinja", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
static void test_whitespace_control(testing & t) { static void test_whitespace_control(testing & t) {
+28 -11
View File
@@ -8,6 +8,10 @@
#include "json.h" #include "json.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cassert> #include <cassert>
#include <regex> #include <regex>
@@ -30,12 +34,12 @@ struct TestCase {
std::string expected_grammar; std::string expected_grammar;
void _print_failure_header() const { void _print_failure_header() const {
fprintf(stderr, "#\n# Test '%s' failed.\n#\n%s\n", name.c_str(), schema.c_str()); LOG_ERR("#\n# Test '%s' failed.\n#\n%s\n", name.c_str(), schema.c_str());
} }
void verify(const std::string & actual_grammar) const { void verify(const std::string & actual_grammar) const {
if (trim(actual_grammar) != trim(expected_grammar)) { if (trim(actual_grammar) != trim(expected_grammar)) {
_print_failure_header(); _print_failure_header();
fprintf(stderr, "# EXPECTED:\n%s\n# ACTUAL:\n%s\n", expected_grammar.c_str(), actual_grammar.c_str()); LOG_ERR("# EXPECTED:\n%s\n# ACTUAL:\n%s\n", expected_grammar.c_str(), actual_grammar.c_str());
assert(false); assert(false);
} }
} }
@@ -48,24 +52,24 @@ struct TestCase {
} }
} catch (const std::runtime_error & ex) { } catch (const std::runtime_error & ex) {
_print_failure_header(); _print_failure_header();
fprintf(stderr, "# GRAMMAR ERROR: %s\n", ex.what()); LOG_ERR("# GRAMMAR ERROR: %s\n", ex.what());
assert(false); assert(false);
} }
} }
void verify_status(TestCaseStatus status) const { void verify_status(TestCaseStatus status) const {
if (status != expected_status) { if (status != expected_status) {
_print_failure_header(); _print_failure_header();
fprintf(stderr, "# EXPECTED STATUS: %s\n", expected_status == SUCCESS ? "SUCCESS" : "FAILURE"); LOG_ERR("# EXPECTED STATUS: %s\n", expected_status == SUCCESS ? "SUCCESS" : "FAILURE");
fprintf(stderr, "# ACTUAL STATUS: %s\n", status == SUCCESS ? "SUCCESS" : "FAILURE"); LOG_ERR("# ACTUAL STATUS: %s\n", status == SUCCESS ? "SUCCESS" : "FAILURE");
assert(false); assert(false);
} }
} }
}; };
static void test_all(const std::string & title, std::function<void(const TestCase &)> runner) { static void test_all(const std::string & title, std::function<void(const TestCase &)> runner) {
fprintf(stderr, "#\n# %s\n#\n", title.c_str()); LOG_INF("#\n# %s\n#\n", title.c_str());
auto test = [&](const TestCase & tc) { auto test = [&](const TestCase & tc) {
fprintf(stderr, "- %s%s\n", tc.name.c_str(), tc.expected_status == FAILURE ? " (failure expected)" : ""); LOG_INF("- %s%s\n", tc.name.c_str(), tc.expected_status == FAILURE ? " (failure expected)" : "");
runner(tc); runner(tc);
}; };
@@ -1510,20 +1514,29 @@ static void test_all(const std::string & title, std::function<void(const TestCas
}); });
} }
int main() { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-json-schema-to-grammar");
test_all("JSON schema conversion", [](const TestCase & tc) { test_all("JSON schema conversion", [](const TestCase & tc) {
try { try {
tc.verify(json_schema_to_grammar(common_json::parse(tc.schema), true)); tc.verify(json_schema_to_grammar(common_json::parse(tc.schema), true));
tc.verify_status(SUCCESS); tc.verify_status(SUCCESS);
} catch (const std::invalid_argument & ex) { } catch (const std::invalid_argument & ex) {
fprintf(stderr, "Error: %s\n", ex.what()); LOG_ERR("Error: %s\n", ex.what());
tc.verify_status(FAILURE); tc.verify_status(FAILURE);
} }
}); });
// a document parsed up front gives the same grammar as the JSON, recursion included // a document parsed up front gives the same grammar as the JSON, recursion included
{ {
fprintf(stderr, "- parsed document\n"); LOG_INF("- parsed document\n");
auto schema = common_json::parse(R"""({ auto schema = common_json::parse(R"""({
"$ref": "#/$defs/node", "$ref": "#/$defs/node",
"$defs": { "$defs": {
@@ -1539,7 +1552,7 @@ int main() {
// a property node carries its $ref target, so its grammar names the ref rule // a property node carries its $ref target, so its grammar names the ref rule
{ {
fprintf(stderr, "- sub-schema $ref\n"); LOG_INF("- sub-schema $ref\n");
auto parameters = common_json::parse(R"""({ auto parameters = common_json::parse(R"""({
"type": "object", "type": "object",
"properties": {"item": {"$ref": "#/$defs/item"}}, "properties": {"item": {"$ref": "#/$defs/item"}},
@@ -1577,4 +1590,8 @@ int main() {
tc.verify_expectation_parseable(); tc.verify_expectation_parseable();
} }
}); });
// the test aborts on a failure, so reaching this point means it passed
LOG("%s: %s\n", "test-json-schema-to-grammar", "PASSED");
common_log_flush(common_log_main());
} }
+32 -3
View File
@@ -1,5 +1,8 @@
#include "arg.h"
#include "common.h"
#include "json-schema.h" #include "json-schema.h"
#include "json.h" #include "json.h"
#include "log.h"
#include "testing.h" #include "testing.h"
#include <cstdlib> #include <cstdlib>
@@ -7,6 +10,7 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector>
static common_chat_schema_document parse(const std::string & schema) { static common_chat_schema_document parse(const std::string & schema) {
return common_chat_schema_from_json(common_json::parse(schema)); return common_chat_schema_from_json(common_json::parse(schema));
@@ -484,9 +488,31 @@ static void test_errors(testing & t) {
} }
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// this test takes an optional filter as its only positional argument
std::string filter;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (filter.empty()) {
filter = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-json-schema");
testing t(std::cout); testing t(std::cout);
if (argc >= 2) { if (!filter.empty()) {
t.set_filter(argv[1]); t.set_filter(filter);
} }
const char * verbose = getenv("LLAMA_TEST_VERBOSE"); const char * verbose = getenv("LLAMA_TEST_VERBOSE");
@@ -509,5 +535,8 @@ int main(int argc, char * argv[]) {
t.test("value_types", test_value_types); t.test("value_types", test_value_types);
t.test("errors", test_errors); t.test("errors", test_errors);
return t.summary(); const int rc = t.summary();
LOG("%s: %s\n", "test-json-schema", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
+56 -28
View File
@@ -1,3 +1,4 @@
#include "arg.h"
#include "common.h" #include "common.h"
#include "log.h" #include "log.h"
#include "ggml-backend.h" #include "ggml-backend.h"
@@ -718,18 +719,24 @@ static int test_backends(const llm_arch target_arch, const size_t seed, const in
const std::string template_row_cfg = std::string("|%" + std::to_string(max_arch_name_length) + "s|%") + std::to_string(max_device_label_length) + "s|%6s|"; const std::string template_row_cfg = std::string("|%" + std::to_string(max_arch_name_length) + "s|%") + std::to_string(max_device_label_length) + "s|%6s|";
const std::string template_row_res = "%15s %10s|%20s|\n"; const std::string template_row_res = "%15s %10s|%20s|\n";
// no ANSI escapes at WARN or lower (--errors-only)
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const std::string status_skip = use_color ? "\033[1;33mSKIP\033[0m" : "SKIP";
const std::string status_ok = use_color ? "\033[1;32mOK\033[0m" : "OK";
const std::string status_fail = use_color ? "\033[1;31mFAIL\033[0m" : "FAIL";
bool all_ok = true; bool all_ok = true;
common_log_flush(common_log_main()); common_log_flush(common_log_main());
printf(template_header.c_str(), "Model arch.", "Device", "Config", "NMSE vs. CPU", "Roundtrip"); LOG_CNT(template_header.c_str(), "Model arch.", "Device", "Config", "NMSE vs. CPU", "Roundtrip");
printf("|"); LOG_CNT("|");
for (size_t i = 0; i < max_arch_name_length; i++) { for (size_t i = 0; i < max_arch_name_length; i++) {
printf("-"); LOG_CNT("-");
} }
printf("|"); LOG_CNT("|");
for (size_t i = 0; i < max_device_label_length; i++) { for (size_t i = 0; i < max_device_label_length; i++) {
printf("-"); LOG_CNT("-");
} }
printf("|------|---------------|---------|\n"); LOG_CNT("|------|---------------|---------|\n");
for (const llm_arch & arch : llm_arch_all()) { for (const llm_arch & arch : llm_arch_all()) {
if (arch == LLM_ARCH_UNKNOWN) { if (arch == LLM_ARCH_UNKNOWN) {
continue; continue;
@@ -761,14 +768,14 @@ static int test_backends(const llm_arch target_arch, const size_t seed, const in
std::vector<float> logits_cpu; std::vector<float> logits_cpu;
for (device_config & dc : dev_configs) { for (device_config & dc : dev_configs) {
// print test config first; should anything fail during model loading or inference, at least we know which test case caused it // print test config first; should anything fail during model loading or inference, at least we know which test case caused it
printf(template_row_cfg.c_str(), LOG_CNT(template_row_cfg.c_str(),
llm_arch_name(arch), dc.label.c_str(), config_name.c_str()); llm_arch_name(arch), dc.label.c_str(), config_name.c_str());
fflush(stdout); common_log_flush(common_log_main());
std::pair<llama_model_ptr, llama_context_ptr> model_and_ctx_dev; std::pair<llama_model_ptr, llama_context_ptr> model_and_ctx_dev;
std::vector<float> logits_dev; std::vector<float> logits_dev;
std::string status_nmse = "\033[1;33mSKIP\033[0m"; std::string status_nmse = status_skip;
std::string status_roundtrip = "\033[1;33mSKIP\033[0m"; std::string status_roundtrip = status_skip;
char nmse_str[12] = {0}; char nmse_str[12] = {0};
bool skip = !arch_supported(arch) || (dc.split_mode == LLAMA_SPLIT_MODE_TENSOR && dc.devs.empty()); bool skip = !arch_supported(arch) || (dc.split_mode == LLAMA_SPLIT_MODE_TENSOR && dc.devs.empty());
@@ -782,10 +789,10 @@ static int test_backends(const llm_arch target_arch, const size_t seed, const in
logits_dev = get_logits(model_and_ctx_dev.first.get(), model_and_ctx_dev.second.get(), tokens, encode); logits_dev = get_logits(model_and_ctx_dev.first.get(), model_and_ctx_dev.second.get(), tokens, encode);
const double nmse_val = nmse(logits_cpu, logits_dev); const double nmse_val = nmse(logits_cpu, logits_dev);
snprintf(nmse_str, sizeof(nmse_str), "(%.2e)", nmse_val); snprintf(nmse_str, sizeof(nmse_str), "(%.2e)", nmse_val);
status_nmse = "\033[1;32mOK\033[0m"; status_nmse = status_ok;
if (nmse_val > 1e-4) { if (nmse_val > 1e-4) {
all_ok = false; all_ok = false;
status_nmse = "\033[1;31mFAIL\033[0m"; status_nmse = status_fail;
} }
} }
@@ -803,12 +810,12 @@ static int test_backends(const llm_arch target_arch, const size_t seed, const in
auto model_and_ctx_roundtrip = get_model_and_ctx(nullptr, file, seed, dc.devs, dc.split_mode, encode); auto model_and_ctx_roundtrip = get_model_and_ctx(nullptr, file, seed, dc.devs, dc.split_mode, encode);
const std::vector<float> logits_roundtrip = get_logits( const std::vector<float> logits_roundtrip = get_logits(
model_and_ctx_roundtrip.first.get(), model_and_ctx_roundtrip.second.get(), tokens, encode); model_and_ctx_roundtrip.first.get(), model_and_ctx_roundtrip.second.get(), tokens, encode);
status_roundtrip = "\033[1;32mOK\033[0m"; status_roundtrip = status_ok;
GGML_ASSERT(logits_roundtrip.size() == logits_dev.size()); GGML_ASSERT(logits_roundtrip.size() == logits_dev.size());
for (size_t i = 0; i < logits_roundtrip.size(); i++) { for (size_t i = 0; i < logits_roundtrip.size(); i++) {
if (logits_roundtrip[i] != logits_dev[i]) { if (logits_roundtrip[i] != logits_dev[i]) {
all_ok = false; all_ok = false;
status_roundtrip = "\033[1;31mFAIL\033[0m"; status_roundtrip = status_fail;
break; break;
} }
} }
@@ -816,7 +823,7 @@ static int test_backends(const llm_arch target_arch, const size_t seed, const in
} }
// log the results for this test case // log the results for this test case
printf(template_row_res.c_str(), LOG_CNT(template_row_res.c_str(),
status_nmse.c_str(), nmse_str, status_roundtrip.c_str()); status_nmse.c_str(), nmse_str, status_roundtrip.c_str());
} }
} }
@@ -826,8 +833,8 @@ static int test_backends(const llm_arch target_arch, const size_t seed, const in
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
// init the logger at max verbosity. filter with a custom callback respecting the user-configure verbosity common_params params;
common_log_set_verbosity_thold(LOG_LEVEL_DEBUG); params.model.path = "."; // this test takes no model
common_init(); common_init();
std::random_device rd; std::random_device rd;
@@ -838,58 +845,79 @@ int main(int argc, char ** argv) {
int verbosity = LOG_LEVEL_ERROR; int verbosity = LOG_LEVEL_ERROR;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
usage(argv); usage(argv);
return 0; return 0;
} } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--arch") == 0) {
if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--arch") == 0) {
if (i + 1 < argc) { if (i + 1 < argc) {
const std::string arch_name = argv[++i]; const std::string arch_name = argv[++i];
arch = llm_arch_from_string(arch_name); arch = llm_arch_from_string(arch_name);
if (arch == LLM_ARCH_UNKNOWN) { if (arch == LLM_ARCH_UNKNOWN) {
LOG_ERR("%s: unkown LLM architecture: %s\n", __func__, arch_name.c_str()); LOG_ERR("%s: unkown LLM architecture: %s\n", __func__, arch_name.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
} else { } else {
usage(argv); usage(argv);
return 1; return 1;
} }
} } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--seed") == 0) {
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--seed") == 0) {
if (i + 1 < argc) { if (i + 1 < argc) {
seed = std::stoull(argv[++i]); seed = std::stoull(argv[++i]);
} else { } else {
usage(argv); usage(argv);
return 1; return 1;
} }
} } else if (strcmp(argv[i], "-v") == 0) {
if (strcmp(argv[i], "-v") == 0) {
if (i + 1 < argc) { if (i + 1 < argc) {
verbosity = std::stoull(argv[++i]); verbosity = std::stoull(argv[++i]);
} else { } else {
usage(argv); usage(argv);
return 1; return 1;
} }
} } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--out") == 0) {
if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--out") == 0) {
if (i + 1 < argc) { if (i + 1 < argc) {
out = argv[++i]; out = argv[++i];
} else { } else {
usage(argv); usage(argv);
return 1; return 1;
} }
} else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
// the parser sets the threshold from the parsed verbosity; keep the max verbosity so that the
// test's own -v N stays the effective filter, unless the user asked for a lower threshold (--errors-only)
common_log_set_verbosity_thold(params.verbosity < LOG_DEFAULT_LLAMA ? params.verbosity : LOG_LEVEL_DEBUG);
LOG("%s: running\n", "test-llama-archs");
common_log_flush(common_log_main()); // the seed line below goes to stdout directly, so drain the start line first
printf("%s: using seed %zu\n", __func__, seed); printf("%s: using seed %zu\n", __func__, seed);
try { try {
if (!out.empty()) { if (!out.empty()) {
return save_models(arch, seed, verbosity, out); const int rc = save_models(arch, seed, verbosity, out);
LOG("%s: %s\n", "test-llama-archs", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
return test_backends(arch, seed, verbosity); const int rc = test_backends(arch, seed, verbosity);
LOG("%s: %s\n", "test-llama-archs", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} catch (const std::exception & err) { } catch (const std::exception & err) {
fprintf(stderr, "encountered runtime error: %s\n", err.what()); LOG_ERR("encountered runtime error: %s\n", err.what());
LOG("%s: %s\n", "test-llama-archs", "FAILED");
common_log_flush(common_log_main());
return -1; return -1;
} }
} }
+21 -5
View File
@@ -6,11 +6,24 @@
#include "../src/llama-grammar.h" #include "../src/llama-grammar.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
int main() int main(int argc, char ** argv)
{ {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-llama-grammar");
llama_grammar_parser parsed_grammar; llama_grammar_parser parsed_grammar;
std::vector<std::pair<std::string, uint32_t>> expected = { std::vector<std::pair<std::string, uint32_t>> expected = {
@@ -183,10 +196,10 @@ int main()
// pretty print error message before asserting // pretty print error message before asserting
if (expected_element.type != element->type || expected_element.value != element->value) if (expected_element.type != element->type || expected_element.value != element->value)
{ {
fprintf(stderr, "index: %d\n", index); LOG_ERR("index: %d\n", index);
fprintf(stderr, "expected_element: %d, %u\n", expected_element.type, expected_element.value); LOG_ERR("expected_element: %d, %u\n", expected_element.type, expected_element.value);
fprintf(stderr, "actual_element: %d, %u\n", element->type, element->value); LOG_ERR("actual_element: %d, %u\n", element->type, element->value);
fprintf(stderr, "expected_element != actual_element\n"); LOG_ERR("expected_element != actual_element\n");
} }
assert(expected_element.type == element->type && expected_element.value == element->value); assert(expected_element.type == element->type && expected_element.value == element->value);
@@ -402,5 +415,8 @@ int main()
llama_grammar_free_impl(grammar); llama_grammar_free_impl(grammar);
// the test aborts on a mismatch, so reaching this point means it passed
LOG("%s: %s\n", "test-llama-grammar", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+17 -5
View File
@@ -1,9 +1,20 @@
#include "arg.h"
#include "common.h"
#include "log.h" #include "log.h"
#include <cstdlib> #include <cstdlib>
#include <thread> #include <thread>
int main() { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-log");
const int n_thread = 8; const int n_thread = 8;
std::thread threads[n_thread]; std::thread threads[n_thread];
@@ -12,13 +23,12 @@ int main() {
const int n_msg = 1000; const int n_msg = 1000;
for (int j = 0; j < n_msg; j++) { for (int j = 0; j < n_msg; j++) {
const int log_type = std::rand() % 4; const int log_type = std::rand() % 3;
switch (log_type) { switch (log_type) {
case 0: LOG_INF("Thread %d: %d\n", i, j); break; case 0: LOG_INF("Thread %d: %d\n", i, j); break;
case 1: LOG_WRN("Thread %d: %d\n", i, j); break; case 1: LOG_TRC("Thread %d: %d\n", i, j); break;
case 2: LOG_ERR("Thread %d: %d\n", i, j); break; case 2: LOG_DBG("Thread %d: %d\n", i, j); break;
case 3: LOG_DBG("Thread %d: %d\n", i, j); break;
default: default:
break; break;
} }
@@ -35,6 +45,8 @@ int main() {
threads[i].join(); threads[i].join();
} }
LOG("%s: %s\n", "test-log", "PASSED");
common_log_flush(common_log_main()); common_log_flush(common_log_main());
// We explicitly free the logger singleton to avoid hanging on Windows // We explicitly free the logger singleton to avoid hanging on Windows
// related to timing issues of thread startup and DLL teardown // related to timing issues of thread startup and DLL teardown
+39 -4
View File
@@ -1,17 +1,50 @@
#include "llama.h" #include "llama.h"
#include "common.h" #include "common.h"
#include "arg.h"
#include "log.h"
#include <cstdlib> #include <cstdlib>
#include <vector>
int main(int argc, char ** argv) {
// the model path is this test's only positional argument
char * model_argv[2] = { argv[0], nullptr };
{
common_params params;
params.model.path = "."; // this test takes no model
common_init();
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (model_argv[1] == nullptr) {
model_argv[1] = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
// falls back to LLAMACPP_TEST_MODELFILE, or warns and exits if no model is given
auto * model_path = common_get_model_or_exit(model_argv[1] == nullptr ? 1 : 2, model_argv);
// that call exits when no model is given, so the verdict below is only reached with a model
LOG("%s: running\n", "test-model-load-cancel");
int main(int argc, char *argv[] ) {
auto * model_path = common_get_model_or_exit(argc, argv);
auto * file = fopen(model_path, "r"); auto * file = fopen(model_path, "r");
if (file == nullptr) { if (file == nullptr) {
fprintf(stderr, "no model at '%s' found\n", model_path); LOG_ERR("no model at '%s' found\n", model_path);
LOG("%s: %s\n", "test-model-load-cancel", "FAILED");
common_log_flush(common_log_main());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
fprintf(stderr, "using '%s'\n", model_path); LOG_INF("using '%s'\n", model_path);
fclose(file); fclose(file);
llama_backend_init(); llama_backend_init();
@@ -23,5 +56,7 @@ int main(int argc, char *argv[] ) {
}; };
auto * model = llama_model_load_from_file(model_path, params); auto * model = llama_model_load_from_file(model_path, params);
llama_backend_free(); llama_backend_free();
LOG("%s: %s\n", "test-model-load-cancel", model == nullptr ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return model == nullptr ? EXIT_SUCCESS : EXIT_FAILURE; return model == nullptr ? EXIT_SUCCESS : EXIT_FAILURE;
} }
+15 -1
View File
@@ -468,7 +468,16 @@ static void test_task_assembly() {
g_repos.clear(); g_repos.clear();
} }
int main(void) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-model-resolution");
// unbuffered, so a crash cannot swallow the reports already printed // unbuffered, so a crash cannot swallow the reports already printed
setvbuf(stdout, nullptr, _IONBF, 0); setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0); setvbuf(stderr, nullptr, _IONBF, 0);
@@ -502,5 +511,10 @@ int main(void) {
std::filesystem::remove_all(cache_dir); std::filesystem::remove_all(cache_dir);
printf("test-model-resolution: all tests OK\n"); printf("test-model-resolution: all tests OK\n");
// the log worker was paused above, restart it for the verdict
common_log_resume(common_log_main());
LOG("%s: %s\n", "test-model-resolution", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+6
View File
@@ -6,7 +6,10 @@
#include "mtmd.h" #include "mtmd.h"
#include "mtmd-helper.h" #include "mtmd-helper.h"
// this test is C, it cannot use the common LOG_* macros
int main(void) { int main(void) {
printf("%s: running\n", "test-mtmd-c-api");
printf("\n\nTesting libmtmd C API...\n"); printf("\n\nTesting libmtmd C API...\n");
printf("--------\n\n"); printf("--------\n\n");
@@ -17,6 +20,7 @@ int main(void) {
if (!chunks) { if (!chunks) {
fprintf(stderr, "Failed to create input chunks\n"); fprintf(stderr, "Failed to create input chunks\n");
printf("%s: %s\n", "test-mtmd-c-api", "FAILED");
return 1; return 1;
} }
@@ -168,5 +172,7 @@ int main(void) {
printf("\n\nDONE: test libmtmd C API...\n"); printf("\n\nDONE: test libmtmd C API...\n");
printf("%s: %s\n", "test-mtmd-c-api", "PASSED");
return 0; return 0;
} }
+23 -2
View File
@@ -1,3 +1,4 @@
#include "arg.h"
#include "testing.h" #include "testing.h"
#include "mtmd-image.h" #include "mtmd-image.h"
@@ -142,17 +143,37 @@ MAKE_TEST(test_temporal_merge_grouping) {
// //
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
testing t(std::cout); testing t(std::cout);
t.verbose = true; t.verbose = true;
// usage: test-mtmd-impl [filter_regex] // usage: test-mtmd-impl [filter_regex]
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
t.set_filter(argv[i]); if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
t.set_filter(argv[i]);
}
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
// log.h cannot be included here (clip-impl.h redefines LOG_*), so print the start line directly
printf("%s: running\n", "test-mtmd-impl");
for (const auto & e : test_registry::all()) { for (const auto & e : test_registry::all()) {
t.test(e.name, e.fn); t.test(e.name, e.fn);
} }
return t.summary(); const int rc = t.summary();
// log.h cannot be included here (clip-impl.h redefines LOG_*), so print the result line directly
printf("%s: %s\n", "test-mtmd-impl", rc == 0 ? "PASSED" : "FAILED");
return rc;
} }
+68 -24
View File
@@ -5,6 +5,10 @@
#include "ggml-backend.h" #include "ggml-backend.h"
#include "ggml-opt.h" #include "ggml-opt.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cmath> #include <cmath>
#include <cinttypes> #include <cinttypes>
#include <cstring> #include <cstring>
@@ -171,25 +175,47 @@ static void helper_free_ctx_data(struct helper_ctx_data ctx_data) {
ggml_opt_dataset_free(ctx_data.dataset_unsupervised); ggml_opt_dataset_free(ctx_data.dataset_unsupervised);
} }
// verdict of a single sub-test; INFO, so hidden with --errors-only
static void print_ok_detail(bool subtest_ok) {
const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
if (subtest_ok) {
LOG_CNT("%sOK%s\n", col_ok, col_end);
} else {
LOG_CNT("%sFAIL%s\n", col_fail, col_end);
}
}
// verdict of the whole test; always visible
static void print_ok(bool subtest_ok) { static void print_ok(bool subtest_ok) {
printf(subtest_ok ? "\033[1;32mOK\033[0m\n" : "\033[1;31mFAIL\033[0m\n"); const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
if (subtest_ok) {
printf("%sOK%s\n", col_ok, col_end);
} else {
printf("%sFAIL%s\n", col_fail, col_end);
}
} }
static void helper_after_test( static void helper_after_test(
enum ggml_opt_optimizer_type optim, enum ggml_opt_optimizer_type optim,
const char * func, const bool high_level, const std::string options, const char * func, const bool high_level, const std::string options,
const std::string subtest, const bool subtest_ok, int & ntest, int & npass) { const std::string subtest, const bool subtest_ok, int & ntest, int & npass) {
printf(" %s(high_level=%s%s, subtest=%s, optimizer=%s): ", LOG_CNT(" %s(high_level=%s%s, subtest=%s, optimizer=%s): ",
func, high_level ? "yes" : "no", options.c_str(), subtest.c_str(), ggml_opt_optimizer_name(optim)); func, high_level ? "yes" : "no", options.c_str(), subtest.c_str(), ggml_opt_optimizer_name(optim));
print_ok(subtest_ok); print_ok_detail(subtest_ok);
if (subtest_ok) if (subtest_ok)
npass++; npass++;
ntest++; ntest++;
} }
static void print_ok(const char * func, bool subtest_ok, int & npass, int & ntest, const char * args = "") { static void print_ok(const char * func, bool subtest_ok, int & npass, int & ntest, const char * args = "") {
printf(" %s(%s): ", func, args); LOG_CNT(" %s(%s): ", func, args);
print_ok(subtest_ok); print_ok_detail(subtest_ok);
if (subtest_ok) if (subtest_ok)
npass++; npass++;
++ntest; ++ntest;
@@ -263,13 +289,13 @@ static std::pair<int, int> test_dataset(
} }
} }
printf(" %s(shuffle=%s, ndata_shard=%" PRId64 ", ndata_batch=%" PRId64 "): ", LOG_CNT(" %s(shuffle=%s, ndata_shard=%" PRId64 ", ndata_batch=%" PRId64 "): ",
__func__, shuffle ? "yes" : "no", ndata_shard, ndata_batch); __func__, shuffle ? "yes" : "no", ndata_shard, ndata_batch);
if (subtest_ok) { if (subtest_ok) {
printf("\033[1;32mOK\033[0m\n"); print_ok_detail(true);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); print_ok_detail(false);
} }
ntest++; ntest++;
} }
@@ -310,12 +336,12 @@ static std::pair<int, int> test_grad(
subtest_ok = false; subtest_ok = false;
} }
} }
printf(" %s(): ", __func__); LOG_CNT(" %s(): ", __func__);
if (subtest_ok) { if (subtest_ok) {
printf("\033[1;32mOK\033[0m\n"); print_ok_detail(true);
npass++; npass++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); print_ok_detail(false);
} }
ntest++; ntest++;
} }
@@ -896,7 +922,16 @@ static std::pair<int, int> test_backend(
} }
int main(void) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-opt");
ggml_log_set(nullptr, nullptr); ggml_log_set(nullptr, nullptr);
ggml_backend_load_all(); ggml_backend_load_all();
const size_t dev_count = ggml_backend_dev_count(); const size_t dev_count = ggml_backend_dev_count();
@@ -931,12 +966,12 @@ int main(void) {
backends_modded.data(), nullptr, backends_modded.size(), GGML_DEFAULT_GRAPH_SIZE, false, true); backends_modded.data(), nullptr, backends_modded.size(), GGML_DEFAULT_GRAPH_SIZE, false, true);
char const* devname = ggml_backend_dev_name(devs[i]); char const* devname = ggml_backend_dev_name(devs[i]);
printf("Backend %zu/%zu: %s\n", i + 1, dev_count, devname); LOG_INF("Backend %zu/%zu: %s\n", i + 1, dev_count, devname);
printf(" Device description: %s\n", ggml_backend_dev_description(devs[i])); LOG_INF(" Device description: %s\n", ggml_backend_dev_description(devs[i]));
size_t free, total; // NOLINT size_t free, total; // NOLINT
ggml_backend_dev_memory(devs[i], &free, &total); ggml_backend_dev_memory(devs[i], &free, &total);
printf(" Device memory: %zu MB (%zu MB free)\n", total / 1024 / 1024, free / 1024 / 1024); LOG_INF(" Device memory: %zu MB (%zu MB free)\n", total / 1024 / 1024, free / 1024 / 1024);
printf("\n"); LOG_CNT("\n");
bool skip; bool skip;
{ {
@@ -973,21 +1008,27 @@ int main(void) {
std::pair<int, int> result; std::pair<int, int> result;
if (!skip) { if (!skip) {
result = test_backend(backend_sched, backends[i], optim); result = test_backend(backend_sched, backends[i], optim);
printf(" %d/%d tests passed\n", result.first, result.second); LOG_CNT(" %d/%d tests passed\n", result.first, result.second);
} }
printf(" Backend %s %s: ", ggml_backend_name(backends[i]), ggml_opt_optimizer_name(optim)); const bool use_color = common_log_get_verbosity_thold() > LOG_LEVEL_WARN;
const char * const col_ok = use_color ? "\033[1;32m" : "";
const char * const col_fail = use_color ? "\033[1;31m" : "";
const char * const col_skip = use_color ? "\033[0;33m" : "";
const char * const col_end = use_color ? "\033[0m" : "";
LOG_CNT(" Backend %s %s: ", ggml_backend_name(backends[i]), ggml_opt_optimizer_name(optim));
if (skip) { if (skip) {
printf("\033[0;33mSKIPPED\033[0m\n"); LOG_CNT("%sSKIPPED%s\n", col_skip, col_end);
n_ok++; n_ok++;
} else if (result.first == result.second) { } else if (result.first == result.second) {
printf("\033[1;32mOK\033[0m\n"); LOG_CNT("%sOK%s\n", col_ok, col_end);
n_ok++; n_ok++;
} else { } else {
printf("\033[1;31mFAIL\033[0m\n"); LOG_CNT("%sFAIL%s\n", col_fail, col_end);
} }
++n_total; ++n_total;
printf("\n"); LOG_CNT("\n");
ggml_backend_sched_free(backend_sched); ggml_backend_sched_free(backend_sched);
} }
} }
@@ -996,8 +1037,11 @@ int main(void) {
ggml_backend_free(backend); ggml_backend_free(backend);
} }
common_log_flush(common_log_main());
printf("%zu/%zu backend*optimizer passed\n", n_ok, n_total); printf("%zu/%zu backend*optimizer passed\n", n_ok, n_total);
bool ok = n_ok == n_total; bool ok = n_ok == n_total;
print_ok(ok); print_ok(ok);
LOG("%s: %s\n", "test-opt", ok ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return ok ? 0 : 1; return ok ? 0 : 1;
} }
+33 -3
View File
@@ -1,13 +1,40 @@
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <vector>
#include "peg-parser/tests.h" #include "peg-parser/tests.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// this test takes an optional filter as its only positional argument
std::string filter;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (filter.empty()) {
filter = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-peg-parser");
testing t(std::cout); testing t(std::cout);
if (argc >= 2) { if (!filter.empty()) {
t.set_filter(argv[1]); t.set_filter(filter);
} }
const char * verbose = getenv("LLAMA_TEST_VERBOSE"); const char * verbose = getenv("LLAMA_TEST_VERBOSE");
@@ -22,5 +49,8 @@ int main(int argc, char *argv[]) {
t.test("serialization", test_json_serialization); t.test("serialization", test_json_serialization);
t.test("python-dict", test_python_dict_parser); t.test("python-dict", test_python_dict_parser);
return t.summary(); const int rc = t.summary();
LOG("%s: %s\n", "test-peg-parser", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
+56 -29
View File
@@ -2,6 +2,8 @@
#include "ggml-cpp.h" #include "ggml-cpp.h"
#include "gguf-model-data.h" #include "gguf-model-data.h"
#include "llama.h" #include "llama.h"
#include "arg.h"
#include "log.h"
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
@@ -121,7 +123,7 @@ static bool parse_snapshot_file(const std::string & path, std::vector<snapshot_s
if (line[0] == '[') { if (line[0] == '[') {
auto close = line.find(']'); auto close = line.find(']');
if (close == std::string::npos) { if (close == std::string::npos) {
fprintf(stderr, "parse error: missing ] in '%s'\n", line.c_str()); LOG_ERR("parse error: missing ] in '%s'\n", line.c_str());
return false; return false;
} }
std::string ftype_str = line.substr(1, close - 1); std::string ftype_str = line.substr(1, close - 1);
@@ -134,13 +136,13 @@ static bool parse_snapshot_file(const std::string & path, std::vector<snapshot_s
llama_ftype ftype = llama_ftype_from_name(ftype_str.c_str()); llama_ftype ftype = llama_ftype_from_name(ftype_str.c_str());
if ((int) ftype < 0) { if ((int) ftype < 0) {
fprintf(stderr, "parse error: unknown ftype '%s'\n", ftype_str.c_str()); LOG_ERR("parse error: unknown ftype '%s'\n", ftype_str.c_str());
return false; return false;
} }
ggml_type dtype = ggml_type_from_name(default_str); ggml_type dtype = ggml_type_from_name(default_str);
if (dtype == GGML_TYPE_COUNT) { if (dtype == GGML_TYPE_COUNT) {
fprintf(stderr, "parse error: unknown default type '%s'\n", default_str.c_str()); LOG_ERR("parse error: unknown default type '%s'\n", default_str.c_str());
return false; return false;
} }
@@ -150,13 +152,13 @@ static bool parse_snapshot_file(const std::string & path, std::vector<snapshot_s
} }
if (!cur) { if (!cur) {
fprintf(stderr, "parse error: tensor line before any section: '%s'\n", line.c_str()); LOG_ERR("parse error: tensor line before any section: '%s'\n", line.c_str());
return false; return false;
} }
auto sp = line.rfind(' '); auto sp = line.rfind(' ');
if (sp == std::string::npos) { if (sp == std::string::npos) {
fprintf(stderr, "parse error: no space in tensor line: '%s'\n", line.c_str()); LOG_ERR("parse error: no space in tensor line: '%s'\n", line.c_str());
return false; return false;
} }
@@ -165,7 +167,7 @@ static bool parse_snapshot_file(const std::string & path, std::vector<snapshot_s
ggml_type gt = ggml_type_from_name(ttype); ggml_type gt = ggml_type_from_name(ttype);
if (gt == GGML_TYPE_COUNT) { if (gt == GGML_TYPE_COUNT) {
fprintf(stderr, "parse error: unknown type '%s' for tensor '%s'\n", ttype.c_str(), tname.c_str()); LOG_ERR("parse error: unknown type '%s' for tensor '%s'\n", ttype.c_str(), tname.c_str());
return false; return false;
} }
@@ -326,15 +328,18 @@ static std::string generate_snapshot(const std::string & name,
} }
static int run_generate(const std::string & snapshot_dir) { static int run_generate(const std::string & snapshot_dir) {
fprintf(stderr, "This will overwrite all snapshot files in:\n %s\n", snapshot_dir.c_str()); // the confirmation prompt must stay visible under --errors-only
fprintf(stderr, "Continue? [y/N] "); LOG_WRN("This will overwrite all snapshot files in:\n %s\n", snapshot_dir.c_str());
LOG_WRN("Continue? [y/N] ");
common_log_flush(common_log_main()); // the log is async, drain it before blocking on stdin
int ch = fgetc(stdin); int ch = fgetc(stdin);
if (ch != 'y' && ch != 'Y') { if (ch != 'y' && ch != 'Y') {
fprintf(stderr, "Aborted.\n"); LOG_WRN("Aborted.\n");
common_log_flush(common_log_main());
return 1; return 1;
} }
fprintf(stderr, "\n"); LOG_WRN("\n");
int n_written = 0; int n_written = 0;
@@ -342,10 +347,11 @@ static int run_generate(const std::string & snapshot_dir) {
const auto & spec = model_specs[m]; const auto & spec = model_specs[m];
std::string name = model_name_from_repo(spec.repo); std::string name = model_name_from_repo(spec.repo);
fprintf(stderr, "Fetching model metadata for %s from %s...\n", name.c_str(), spec.repo); LOG_INF("Fetching model metadata for %s from %s...\n", name.c_str(), spec.repo);
auto result = gguf_fetch_model_meta(spec.repo, spec.quant); auto result = gguf_fetch_model_meta(spec.repo, spec.quant);
if (!result.has_value()) { if (!result.has_value()) {
fprintf(stderr, "ERROR: could not fetch model metadata for %s\n", name.c_str()); LOG_ERR("ERROR: could not fetch model metadata for %s\n", name.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -360,19 +366,20 @@ static int run_generate(const std::string & snapshot_dir) {
std::ofstream f(path); std::ofstream f(path);
if (!f.good()) { if (!f.good()) {
fprintf(stderr, "ERROR: could not write %s\n", path.c_str()); LOG_ERR("ERROR: could not write %s\n", path.c_str());
llama_quant_free(qs); llama_quant_free(qs);
llama_model_free(model); llama_model_free(model);
common_log_flush(common_log_main());
return 1; return 1;
} }
f << content; f << content;
n_written++; n_written++;
fprintf(stderr, " wrote %s\n", path.c_str()); LOG_INF(" wrote %s\n", path.c_str());
llama_quant_free(qs); llama_quant_free(qs);
llama_model_free(model); llama_model_free(model);
} }
fprintf(stderr, "%d files written\n", n_written); LOG_INF("%d files written\n", n_written);
return 0; return 0;
} }
@@ -384,8 +391,8 @@ static bool run_test_section(quantize_state_impl * qs, mock_tensors & mt, const
// verify default_type matches what llama_ftype_get_default_type returns // verify default_type matches what llama_ftype_get_default_type returns
ggml_type computed_default = llama_ftype_get_default_type(section.ftype); ggml_type computed_default = llama_ftype_get_default_type(section.ftype);
if (computed_default != section.default_type) { if (computed_default != section.default_type) {
printf(" FAIL [%s] default type mismatch: file says %s, code says %s\n", llama_ftype_to_name(section.ftype), LOG_INF(" FAIL [%s] default type mismatch: file says %s, code says %s\n", llama_ftype_to_name(section.ftype),
ggml_type_name(section.default_type), ggml_type_name(computed_default)); ggml_type_name(section.default_type), ggml_type_name(computed_default));
return false; return false;
} }
@@ -409,15 +416,15 @@ static bool run_test_section(quantize_state_impl * qs, mock_tensors & mt, const
} }
if (got != expected) { if (got != expected) {
printf(" FAIL %-50s %-10s expected %s, got %s\n", name, llama_ftype_to_name(section.ftype), LOG_INF(" FAIL %-50s %-10s expected %s, got %s\n", name, llama_ftype_to_name(section.ftype),
ggml_type_name(expected), ggml_type_name(got)); ggml_type_name(expected), ggml_type_name(got));
all_pass = false; all_pass = false;
} }
} }
if (n_override_found != (int) section.overrides.size()) { if (n_override_found != (int) section.overrides.size()) {
printf(" FAIL [%s] override count mismatch: listed %d, matched %d\n", llama_ftype_to_name(section.ftype), LOG_INF(" FAIL [%s] override count mismatch: listed %d, matched %d\n", llama_ftype_to_name(section.ftype),
(int) section.overrides.size(), n_override_found); (int) section.overrides.size(), n_override_found);
all_pass = false; all_pass = false;
} }
@@ -432,11 +439,11 @@ static int run_remote_tests(const std::string & snapshot_dir, const char * argv0
for (int m = 0; m < n_model_specs; m++) { for (int m = 0; m < n_model_specs; m++) {
const auto & spec = model_specs[m]; const auto & spec = model_specs[m];
std::string name = model_name_from_repo(spec.repo); std::string name = model_name_from_repo(spec.repo);
printf("=== %s ===\n", name.c_str()); LOG_INF("=== %s ===\n", name.c_str());
auto result = gguf_fetch_model_meta(spec.repo, spec.quant, "", false); auto result = gguf_fetch_model_meta(spec.repo, spec.quant, "", false);
if (!result.has_value()) { if (!result.has_value()) {
printf(" SKIP (could not fetch model metadata)\n\n"); LOG_INF(" SKIP (could not fetch model metadata)\n\n");
total_skip++; total_skip++;
continue; continue;
} }
@@ -450,7 +457,7 @@ static int run_remote_tests(const std::string & snapshot_dir, const char * argv0
std::string snapshot_path = snapshot_dir + "/" + snapshot_file_from_name(name) + ".schema"; std::string snapshot_path = snapshot_dir + "/" + snapshot_file_from_name(name) + ".schema";
std::vector<snapshot_section> sections; std::vector<snapshot_section> sections;
if (!parse_snapshot_file(snapshot_path, sections)) { if (!parse_snapshot_file(snapshot_path, sections)) {
printf(" SKIP (could not read snapshot file: %s)\n\n", snapshot_path.c_str()); LOG_INF(" SKIP (could not read snapshot file: %s)\n\n", snapshot_path.c_str());
llama_quant_free(qs); llama_quant_free(qs);
llama_model_free(model); llama_model_free(model);
total_skip++; total_skip++;
@@ -469,9 +476,9 @@ static int run_remote_tests(const std::string & snapshot_dir, const char * argv0
} }
} }
printf(" %s %s: %d/%d ftype sections passed (%d tensors)\n", model_fail == 0 ? "PASS" : "FAIL", name.c_str(), LOG_INF(" %s %s: %d/%d ftype sections passed (%d tensors)\n", model_fail == 0 ? "PASS" : "FAIL", name.c_str(),
model_pass, model_pass + model_fail, (int) mt.tensors.size()); model_pass, model_pass + model_fail, (int) mt.tensors.size());
printf("\n"); LOG_CNT("\n");
if (model_fail == 0) { if (model_fail == 0) {
total_pass++; total_pass++;
@@ -498,23 +505,43 @@ static int run_remote_tests(const std::string & snapshot_dir, const char * argv0
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
std::string snapshot_dir = SNAPSHOT_DIR; std::string snapshot_dir = SNAPSHOT_DIR;
bool generate = false; bool generate = false;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--generate") == 0) { if (strcmp(argv[i], "--generate") == 0) {
generate = true; generate = true;
} else if (strcmp(argv[i], "--snapshot-dir") == 0 && i + 1 < argc) { } else if (strcmp(argv[i], "--snapshot-dir") == 0 && i + 1 < argc) {
snapshot_dir = argv[++i]; snapshot_dir = argv[++i];
} else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-quant-type-selection");
if (generate) { if (generate) {
return run_generate(snapshot_dir); const int rc = run_generate(snapshot_dir);
LOG("%s: %s\n", "test-quant-type-selection", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
// suppress llama log warnings during test (e.g. tensor type fallback messages) // suppress llama log warnings during test (e.g. tensor type fallback messages)
llama_log_set([](enum ggml_log_level, const char *, void *) {}, nullptr); llama_log_set([](enum ggml_log_level, const char *, void *) {}, nullptr);
return run_remote_tests(snapshot_dir, argv[0]); const int rc = run_remote_tests(snapshot_dir, argv[0]);
LOG("%s: %s\n", "test-quant-type-selection", rc == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return rc;
} }
+31 -10
View File
@@ -2,6 +2,8 @@
#include "ggml.h" #include "ggml.h"
#include "ggml-cpu.h" #include "ggml-cpu.h"
#include "arg.h"
#include "log.h"
#undef NDEBUG #undef NDEBUG
#include <assert.h> #include <assert.h>
@@ -148,8 +150,8 @@ static int test_vec_dot_f32(bool verbose) {
const bool failed = !(error < MAX_QUANTIZATION_REFERENCE_ERROR); const bool failed = !(error < MAX_QUANTIZATION_REFERENCE_ERROR);
num_failed += failed; num_failed += failed;
if (failed || verbose) { if (failed || verbose) {
printf(" f32 vec_dot n=%4d: %s (ref=%f got=%f err=%f)\n", LOG_INF(" f32 vec_dot n=%4d: %s (ref=%f got=%f err=%f)\n",
n, RESULT_STR[failed], ref, result, error); n, RESULT_STR[failed], ref, result, error);
} }
} }
return num_failed; return num_failed;
@@ -182,7 +184,7 @@ static int test_vec_dot_q(bool verbose) {
const ggml_type ei = (ggml_type)i; const ggml_type ei = (ggml_type)i;
printf("Testing %s\n", ggml_type_name((ggml_type) i)); LOG_INF("Testing %s\n", ggml_type_name((ggml_type) i));
ggml_quantize_init(ei); ggml_quantize_init(ei);
if (qfns_cpu->from_float && qfns->to_float) { if (qfns_cpu->from_float && qfns->to_float) {
@@ -201,14 +203,14 @@ static int test_vec_dot_q(bool verbose) {
bool failed = !(total_error < max_quantization_error); bool failed = !(total_error < max_quantization_error);
num_failed += failed; num_failed += failed;
if (failed || verbose) { if (failed || verbose) {
printf("%5s absolute quantization error: %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], total_error); LOG_INF("%5s absolute quantization error: %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], total_error);
} }
const float reference_error = reference_quantization_error(qfns, qfns_cpu, test_size, test_data.data()); const float reference_error = reference_quantization_error(qfns, qfns_cpu, test_size, test_data.data());
failed = !(reference_error < MAX_QUANTIZATION_REFERENCE_ERROR); failed = !(reference_error < MAX_QUANTIZATION_REFERENCE_ERROR);
num_failed += failed; num_failed += failed;
if (failed || verbose) { if (failed || verbose) {
printf("%5s reference implementation error: %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], reference_error); LOG_INF("%5s reference implementation error: %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], reference_error);
} }
const float vec_dot_error = dot_product_error(qfns_cpu, type, test_size, test_data.data(), test_data2.data(), nullptr, nullptr, 1); const float vec_dot_error = dot_product_error(qfns_cpu, type, test_size, test_data.data(), test_data2.data(), nullptr, nullptr, 1);
@@ -225,7 +227,7 @@ static int test_vec_dot_q(bool verbose) {
failed = !(vec_dot_error < max_allowed_error); failed = !(vec_dot_error < max_allowed_error);
num_failed += failed; num_failed += failed;
if (failed || verbose) { if (failed || verbose) {
printf("%5s dot product error: %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], vec_dot_error); LOG_INF("%5s dot product error: %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], vec_dot_error);
} }
// Test nrc=2 path for types that support it // Test nrc=2 path for types that support it
@@ -234,7 +236,7 @@ static int test_vec_dot_q(bool verbose) {
failed = !(vec_dot_error_nrc2 < max_allowed_error); failed = !(vec_dot_error_nrc2 < max_allowed_error);
num_failed += failed; num_failed += failed;
if (failed || verbose) { if (failed || verbose) {
printf("%5s dot product error (nrc=2): %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], vec_dot_error_nrc2); LOG_INF("%5s dot product error (nrc=2): %s (%f)\n", ggml_type_name(type), RESULT_STR[failed], vec_dot_error_nrc2);
} }
} }
} }
@@ -244,19 +246,34 @@ static int test_vec_dot_q(bool verbose) {
} }
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
bool verbose = false; bool verbose = false;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
std::string arg; std::string arg;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
arg = argv[i]; arg = argv[i];
if (arg == "-v") { if (arg == "-v") {
verbose = true; verbose = true; // this test's own -v, not the common --verbose
} else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else { } else {
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); LOG_ERR("error: unknown argument: %s\n", arg.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-quantize-fns");
ggml_cpu_init(); ggml_cpu_init();
@@ -265,9 +282,13 @@ int main(int argc, char * argv[]) {
num_failed += test_vec_dot_f32(verbose); num_failed += test_vec_dot_f32(verbose);
num_failed += test_vec_dot_q(verbose); num_failed += test_vec_dot_q(verbose);
const bool ok = num_failed == 0;
if (num_failed || verbose) { if (num_failed || verbose) {
printf("%d tests failed\n", num_failed); printf("%d tests failed\n", num_failed);
} }
return num_failed > 0; LOG("%s: %s\n", "test-quantize-fns", ok ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return ok ? 0 : 1;
} }
+50 -25
View File
@@ -2,6 +2,8 @@
#include "ggml.h" #include "ggml.h"
#include "ggml-cpu.h" #include "ggml-cpu.h"
#include "arg.h"
#include "log.h"
#undef NDEBUG #undef NDEBUG
#include <algorithm> #include <algorithm>
@@ -101,10 +103,10 @@ static void benchmark_function(size_t size, size_t q_size, int64_t iterations, c
min_time_us = std::min(min_time_us, end_time - start_time); min_time_us = std::min(min_time_us, end_time - start_time);
} }
printf(" min cycles/%d vals : %9.2f\n", QK, QK * min_time_cycles / (float) size); LOG_CNT(" min cycles/%d vals : %9.2f\n", QK, QK * min_time_cycles / (float) size);
printf(" avg cycles/%d vals : %9.2f\n", QK, QK * total_time_cycles / (float) (size * iterations)); LOG_CNT(" avg cycles/%d vals : %9.2f\n", QK, QK * total_time_cycles / (float) (size * iterations));
printf(" float32 throughput : %9.2f GB/s\n", gigabytes_per_second(4 * size * iterations, total_time_us)); LOG_CNT(" float32 throughput : %9.2f GB/s\n", gigabytes_per_second(4 * size * iterations, total_time_us));
printf(" quantized throughput : %9.2f GB/s\n", gigabytes_per_second(q_size * iterations, total_time_us)); LOG_CNT(" quantized throughput : %9.2f GB/s\n", gigabytes_per_second(q_size * iterations, total_time_us));
} }
static void usage(char * argv[]) { static void usage(char * argv[]) {
@@ -138,12 +140,16 @@ static void usage(char * argv[]) {
} }
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
common_init();
quantize_perf_params params {}; quantize_perf_params params {};
// read command line // read command line
bool invalid_param = false; bool invalid_param = false;
std::string arg; std::string arg;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
arg = argv[i]; arg = argv[i];
@@ -154,7 +160,7 @@ int main(int argc, char * argv[]) {
} }
size_t size = std::stoi(argv[i]); size_t size = std::stoi(argv[i]);
if (size % 32 != 0) { if (size % 32 != 0) {
fprintf(stderr, "error: size %zu not divisible by 32\n", size); LOG_ERR("error: size %zu not divisible by 32\n", size);
invalid_param = true; invalid_param = true;
break; break;
} }
@@ -203,7 +209,7 @@ int main(int argc, char * argv[]) {
} }
int alignment = std::stoi(argv[i]); int alignment = std::stoi(argv[i]);
if (alignment < 0 || alignment > MAX_ALIGNMENT) { if (alignment < 0 || alignment > MAX_ALIGNMENT) {
fprintf(stderr, "error: alignment-offset must be less than %d\n", MAX_ALIGNMENT); LOG_ERR("error: alignment-offset must be less than %d\n", MAX_ALIGNMENT);
invalid_param = true; invalid_param = true;
break; break;
} }
@@ -215,7 +221,7 @@ int main(int argc, char * argv[]) {
} }
int number = std::stoi(argv[i]); int number = std::stoi(argv[i]);
if (number < 0 || number > MAX_ITERATIONS) { if (number < 0 || number > MAX_ITERATIONS) {
fprintf(stderr, "error: iterations must be less than %d\n", MAX_ITERATIONS); LOG_ERR("error: iterations must be less than %d\n", MAX_ITERATIONS);
invalid_param = true; invalid_param = true;
break; break;
} }
@@ -223,15 +229,31 @@ int main(int argc, char * argv[]) {
} else if ((arg == "-h") || (arg == "--help")) { } else if ((arg == "-h") || (arg == "--help")) {
usage(argv); usage(argv);
return 1; return 1;
} else if (arg[0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else { } else {
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); LOG_ERR("error: unknown argument: %s\n", arg.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
if (invalid_param) { if (invalid_param) {
fprintf(stderr, "error: invalid parameter for argument: %s\n", arg.c_str()); LOG_ERR("error: invalid parameter for argument: %s\n", arg.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
common_argv.push_back(nullptr);
// the test's own "params" is declared above, so scope the parsed one
{
common_params params;
params.model.path = "."; // this test takes no model
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
LOG("%s: running\n", "test-quantize-perf");
if (params.test_sizes.empty()) { if (params.test_sizes.empty()) {
params.test_sizes.push_back(L1_SIZE); params.test_sizes.push_back(L1_SIZE);
@@ -271,14 +293,14 @@ int main(int argc, char * argv[]) {
} }
if (qfns_cpu->from_float && qfns->to_float) { if (qfns_cpu->from_float && qfns->to_float) {
printf("%s\n", ggml_type_name(type)); LOG_INF(" running %s\n", ggml_type_name(type));
ggml_quantize_init(type); ggml_quantize_init(type);
if (params.op_quantize_row_q_reference) { if (params.op_quantize_row_q_reference) {
printf(" quantize_row_q_reference\n"); LOG_CNT(" quantize_row_q_reference\n");
for (size_t size : params.test_sizes) { for (size_t size : params.test_sizes) {
printf(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024)); LOG_CNT(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024));
auto quantize_fn = [&](void) -> float { auto quantize_fn = [&](void) -> float {
qfns->from_float_ref(test_data1, test_q1, size); qfns->from_float_ref(test_data1, test_q1, size);
return test_q1[0]; return test_q1[0];
@@ -286,13 +308,13 @@ int main(int argc, char * argv[]) {
size_t quantized_size = ggml_row_size(type, size); size_t quantized_size = ggml_row_size(type, size);
benchmark_function(size, quantized_size, iterations, quantize_fn); benchmark_function(size, quantized_size, iterations, quantize_fn);
} }
printf("\n"); LOG_CNT("\n");
} }
if (params.op_quantize_row_q) { if (params.op_quantize_row_q) {
printf(" quantize_row_q\n"); LOG_CNT(" quantize_row_q\n");
for (size_t size : params.test_sizes) { for (size_t size : params.test_sizes) {
printf(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024)); LOG_CNT(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024));
auto quantize_fn = [&](void) -> float { auto quantize_fn = [&](void) -> float {
qfns_cpu->from_float(test_data1, test_q1, size); qfns_cpu->from_float(test_data1, test_q1, size);
return test_q1[0]; return test_q1[0];
@@ -300,14 +322,14 @@ int main(int argc, char * argv[]) {
size_t quantized_size = ggml_row_size(type, size); size_t quantized_size = ggml_row_size(type, size);
benchmark_function(size, quantized_size, iterations, quantize_fn); benchmark_function(size, quantized_size, iterations, quantize_fn);
} }
printf("\n"); LOG_CNT("\n");
} }
if (params.op_dequantize_row_q) { if (params.op_dequantize_row_q) {
printf(" dequantize_row_q\n"); LOG_CNT(" dequantize_row_q\n");
qfns_cpu->from_float(test_data1, test_q1, largest); qfns_cpu->from_float(test_data1, test_q1, largest);
for (size_t size : params.test_sizes) { for (size_t size : params.test_sizes) {
printf(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024)); LOG_CNT(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024));
auto quantize_fn = [&](void) -> float { auto quantize_fn = [&](void) -> float {
qfns->to_float(test_q1, test_out, size); qfns->to_float(test_q1, test_out, size);
return test_out[0]; return test_out[0];
@@ -315,13 +337,13 @@ int main(int argc, char * argv[]) {
size_t quantized_size = ggml_row_size(type, size); size_t quantized_size = ggml_row_size(type, size);
benchmark_function(size, quantized_size, iterations, quantize_fn); benchmark_function(size, quantized_size, iterations, quantize_fn);
} }
printf("\n"); LOG_CNT("\n");
} }
if (params.op_quantize_row_q_dot) { if (params.op_quantize_row_q_dot) {
printf(" quantize_row_q_dot\n"); LOG_CNT(" quantize_row_q_dot\n");
for (size_t size : params.test_sizes) { for (size_t size : params.test_sizes) {
printf(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024)); LOG_CNT(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024));
auto quantize_fn = [&](void) -> float { auto quantize_fn = [&](void) -> float {
const auto * vdot = ggml_get_type_traits_cpu(qfns_cpu->vec_dot_type); const auto * vdot = ggml_get_type_traits_cpu(qfns_cpu->vec_dot_type);
vdot->from_float(test_data1, test_q1, size); vdot->from_float(test_data1, test_q1, size);
@@ -330,15 +352,15 @@ int main(int argc, char * argv[]) {
size_t quantized_size = ggml_row_size(type, size); size_t quantized_size = ggml_row_size(type, size);
benchmark_function(size, quantized_size, iterations, quantize_fn); benchmark_function(size, quantized_size, iterations, quantize_fn);
} }
printf("\n"); LOG_CNT("\n");
} }
if (params.op_vec_dot_q) { if (params.op_vec_dot_q) {
printf(" vec_dot_q\n"); LOG_CNT(" vec_dot_q\n");
qfns_cpu->from_float(test_data1, test_q1, largest); qfns_cpu->from_float(test_data1, test_q1, largest);
qfns_cpu->from_float(test_data2, test_q2, largest); qfns_cpu->from_float(test_data2, test_q2, largest);
for (size_t size : params.test_sizes) { for (size_t size : params.test_sizes) {
printf(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024)); LOG_CNT(" %zu values (%.2f MB)\n", size, 4*size/(float)(1024*1024));
auto quantize_fn = [&](void) -> float { auto quantize_fn = [&](void) -> float {
float result; float result;
qfns_cpu->vec_dot(size, &result, 0, test_q1, 0, test_q2, 0, 1); qfns_cpu->vec_dot(size, &result, 0, test_q1, 0, test_q2, 0, 1);
@@ -347,10 +369,13 @@ int main(int argc, char * argv[]) {
size_t quantized_size = ggml_row_size(type, size); size_t quantized_size = ggml_row_size(type, size);
benchmark_function(size, quantized_size, iterations, quantize_fn); benchmark_function(size, quantized_size, iterations, quantize_fn);
} }
printf("\n"); LOG_CNT("\n");
} }
} }
} }
// this benchmark has no failure path, so reaching this point means it all passed
LOG("%s: %s\n", "test-quantize-perf", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+73 -40
View File
@@ -1,7 +1,9 @@
#include "llama.h" #include "llama.h"
#include "arg.h"
#include "build-info.h" #include "build-info.h"
#include "common.h" #include "common.h"
#include "log.h"
#include "../src/llama-model.h" #include "../src/llama-model.h"
@@ -46,29 +48,31 @@ struct error_stats {
uint64_t error_histogram[HISTOGRAM_BUCKETS]; uint64_t error_histogram[HISTOGRAM_BUCKETS];
}; };
static void quantize_stats_print_usage(int /*argc*/, char ** argv) { // print the usage text at the given level: INFO for -h, ERROR when it follows a bad argument
static void quantize_stats_print_usage(int /*argc*/, char ** argv, ggml_log_level level) {
quantize_stats_params params; quantize_stats_params params;
fprintf(stderr, "usage: %s [options]\n", argv[0]); const int verbosity = level == GGML_LOG_LEVEL_ERROR ? LOG_LEVEL_ERROR : LOG_LEVEL_INFO;
fprintf(stderr, "\n"); LOG_TMPL(level, verbosity, "usage: %s [options]\n", argv[0]);
fprintf(stderr, "options:\n"); LOG_TMPL(level, verbosity, "\n");
fprintf(stderr, " -h, --help show this help message and exit\n"); LOG_TMPL(level, verbosity, "options:\n");
fprintf(stderr, " -m FNAME, --model FNAME\n"); LOG_TMPL(level, verbosity, " -h, --help show this help message and exit\n");
fprintf(stderr, " model path (default: %s)\n", params.model.c_str()); LOG_TMPL(level, verbosity, " -m FNAME, --model FNAME\n");
fprintf(stderr, " -r, --reference\n"); LOG_TMPL(level, verbosity, " model path (default: %s)\n", params.model.c_str());
fprintf(stderr, " use reference implementation (default: false)\n"); LOG_TMPL(level, verbosity, " -r, --reference\n");
fprintf(stderr, " -v, --verbose\n"); LOG_TMPL(level, verbosity, " use reference implementation (default: false)\n");
fprintf(stderr, " verbose output (default: false)\n"); LOG_TMPL(level, verbosity, " -v, --verbose\n");
fprintf(stderr, " -p, --per-layer-stats\n"); LOG_TMPL(level, verbosity, " verbose output (default: false)\n");
fprintf(stderr, " print stats per layer (default: false)\n"); LOG_TMPL(level, verbosity, " -p, --per-layer-stats\n");
fprintf(stderr, " --histogram\n"); LOG_TMPL(level, verbosity, " print stats per layer (default: false)\n");
fprintf(stderr, " print error histogram (default: false)\n"); LOG_TMPL(level, verbosity, " --histogram\n");
fprintf(stderr, " -l LAYER, --include-layer LAYER\n"); LOG_TMPL(level, verbosity, " print error histogram (default: false)\n");
fprintf(stderr, " only test layers matching pattern\n"); LOG_TMPL(level, verbosity, " -l LAYER, --include-layer LAYER\n");
fprintf(stderr, " -L LAYER, --exclude-layer LAYER\n"); LOG_TMPL(level, verbosity, " only test layers matching pattern\n");
fprintf(stderr, " exclude layers matching pattern\n"); LOG_TMPL(level, verbosity, " -L LAYER, --exclude-layer LAYER\n");
fprintf(stderr, " -t TYPE, --type TYPE\n"); LOG_TMPL(level, verbosity, " exclude layers matching pattern\n");
fprintf(stderr, " only test given type (q4_0, q4_1)\n"); LOG_TMPL(level, verbosity, " -t TYPE, --type TYPE\n");
fprintf(stderr, "\n"); LOG_TMPL(level, verbosity, " only test given type (q4_0, q4_1)\n");
LOG_TMPL(level, verbosity, "\n");
} }
// Check if a layer is included/excluded by command line // Check if a layer is included/excluded by command line
@@ -121,14 +125,14 @@ static void print_error_stats(const std::string & name, const error_stats & stat
double rmse = sqrt(stats.total_error / (double) stats.num_samples); double rmse = sqrt(stats.total_error / (double) stats.num_samples);
double median = find_quantile(stats, .5); double median = find_quantile(stats, .5);
double pct95 = find_quantile(stats, .95); double pct95 = find_quantile(stats, .95);
printf("%-50s: rmse %.8f, maxerr %.8f, 95pct<%.4f, median<%.4f\n", name.c_str(), rmse, stats.max_error, pct95, median); LOG_INF("%-50s: rmse %.8f, maxerr %.8f, 95pct<%.4f, median<%.4f\n", name.c_str(), rmse, stats.max_error, pct95, median);
if (print_histogram) { if (print_histogram) {
printf("Error distribution:\n"); LOG_INF("Error distribution:\n");
for (size_t i = 0; i < HISTOGRAM_BUCKETS; i++) { for (size_t i = 0; i < HISTOGRAM_BUCKETS; i++) {
double lower = i * HISTOGRAM_RANGE / HISTOGRAM_BUCKETS; double lower = i * HISTOGRAM_RANGE / HISTOGRAM_BUCKETS;
double upper = (i+1) * HISTOGRAM_RANGE / HISTOGRAM_BUCKETS; double upper = (i+1) * HISTOGRAM_RANGE / HISTOGRAM_BUCKETS;
if (i == HISTOGRAM_BUCKETS -1) upper = INFINITY; if (i == HISTOGRAM_BUCKETS -1) upper = INFINITY;
printf("[%3.4f, %3.4f): %11" PRIu64 "\n", lower, upper, stats.error_histogram[i]); LOG_INF("[%3.4f, %3.4f): %11" PRIu64 "\n", lower, upper, stats.error_histogram[i]);
} }
} }
} }
@@ -226,6 +230,8 @@ static void test_roundtrip_on_layer(
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
common_init();
ggml_time_init(); ggml_time_init();
quantize_stats_params params; quantize_stats_params params;
@@ -235,11 +241,14 @@ int main(int argc, char ** argv) {
int max_thread = 0; int max_thread = 0;
bool invalid_param = false; bool invalid_param = false;
std::string arg; std::string arg;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
arg = argv[i]; arg = argv[i];
if (arg == "-h" || arg == "--help") { if (arg == "-h" || arg == "--help") {
quantize_stats_print_usage(argc, argv); quantize_stats_print_usage(argc, argv, GGML_LOG_LEVEL_INFO);
common_log_flush(common_log_main());
exit(0); exit(0);
} else if (arg == "-r" || arg == "--reference") { } else if (arg == "-r" || arg == "--reference") {
params.reference = true; params.reference = true;
@@ -280,7 +289,7 @@ int main(int argc, char ** argv) {
if (j < GGML_TYPE_COUNT) { if (j < GGML_TYPE_COUNT) {
params.include_types.push_back((ggml_type) j); params.include_types.push_back((ggml_type) j);
} else { } else {
fprintf(stderr, "error: %s not in list of types\n", argv[i]); LOG_ERR("error: %s not in list of types\n", argv[i]);
invalid_param = true; invalid_param = true;
} }
} else if (arg == "-n" || arg == "--num-threads") { } else if (arg == "-n" || arg == "--num-threads") {
@@ -289,22 +298,38 @@ int main(int argc, char ** argv) {
break; break;
} }
max_thread = atoi(argv[i]); max_thread = atoi(argv[i]);
} else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else { } else {
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str()); LOG_ERR("error: unknown argument: %s\n", arg.c_str());
quantize_stats_print_usage(argc, argv); quantize_stats_print_usage(argc, argv, GGML_LOG_LEVEL_ERROR);
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
if (invalid_param) { if (invalid_param) {
fprintf(stderr, "error: invalid parameter for argument: %s\n", arg.c_str()); LOG_ERR("error: invalid parameter for argument: %s\n", arg.c_str());
quantize_stats_print_usage(argc, argv); quantize_stats_print_usage(argc, argv, GGML_LOG_LEVEL_ERROR);
common_log_flush(common_log_main());
return 1; return 1;
} }
common_argv.push_back(nullptr);
// the test's own "params" is declared above, so scope the parsed one
{
common_params params;
params.model.path = "."; // this test takes no model
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
LOG("%s: running\n", "test-quantize-stats");
llama_print_build_info(llama_version()); llama_print_build_info(llama_version());
// load the model // load the model
fprintf(stderr, "Loading model\n"); LOG_INF("Loading model\n");
const int64_t t_main_start_us = ggml_time_us(); const int64_t t_main_start_us = ggml_time_us();
llama_model * model; llama_model * model;
@@ -317,7 +342,9 @@ int main(int argc, char ** argv) {
model = llama_model_load_from_file(params.model.c_str(), mparams); model = llama_model_load_from_file(params.model.c_str(), mparams);
if (model == NULL) { if (model == NULL) {
fprintf(stderr, "%s: error: failed to load model '%s'\n", __func__, params.model.c_str()); LOG_ERR("%s: error: failed to load model '%s'\n", __func__, params.model.c_str());
LOG("%s: %s\n", "test-quantize-stats", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -327,8 +354,10 @@ int main(int argc, char ** argv) {
ctx = llama_init_from_model(model, cparams); ctx = llama_init_from_model(model, cparams);
if (ctx == NULL) { if (ctx == NULL) {
fprintf(stderr, "%s: error: failed to create context with model '%s'\n", __func__, params.model.c_str()); LOG_ERR("%s: error: failed to create context with model '%s'\n", __func__, params.model.c_str());
llama_model_free(model); llama_model_free(model);
LOG("%s: %s\n", "test-quantize-stats", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
@@ -344,15 +373,17 @@ int main(int argc, char ** argv) {
continue; continue;
} }
if (params.verbose) { if (params.verbose) {
printf("%s: type %s, size %" PRId64 "\n", kv_tensor.first.c_str(), ggml_type_name(kv_tensor.second->type), ggml_nelements(kv_tensor.second)); LOG_INF("%s: type %s, size %" PRId64 "\n", kv_tensor.first.c_str(), ggml_type_name(kv_tensor.second->type), ggml_nelements(kv_tensor.second));
} }
if (kv_tensor.second->type == GGML_TYPE_F16) { if (kv_tensor.second->type == GGML_TYPE_F16) {
is_f16 = true; is_f16 = true;
} else if (kv_tensor.second->type != GGML_TYPE_F32) { } else if (kv_tensor.second->type != GGML_TYPE_F32) {
fprintf(stderr, "%s: error: Quantization should be tested with a float model, " LOG_ERR("%s: error: Quantization should be tested with a float model, "
"this model contains already quantized layers (%s is type %d)\n", __func__, kv_tensor.first.c_str(), kv_tensor.second->type); "this model contains already quantized layers (%s is type %d)\n", __func__, kv_tensor.first.c_str(), kv_tensor.second->type);
llama_free(ctx); llama_free(ctx);
llama_model_free(model); llama_model_free(model);
LOG("%s: %s\n", "test-quantize-stats", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
included_layers++; included_layers++;
@@ -360,9 +391,9 @@ int main(int argc, char ** argv) {
} }
if (is_f16) { if (is_f16) {
printf("note: source model is f16\n"); LOG_INF("note: source model is f16\n");
} }
printf("testing %d layers with max size %" PRId64 "\n", included_layers, max_nelements); LOG_INF("testing %d layers with max size %" PRId64 "\n", included_layers, max_nelements);
// allocate scratch space // allocate scratch space
std::vector<float> input_scratch; std::vector<float> input_scratch;
std::vector<char> quantized_scratch; std::vector<char> quantized_scratch;
@@ -378,7 +409,7 @@ int main(int argc, char ** argv) {
const auto * qfns_cpu = ggml_get_type_traits_cpu(type); const auto * qfns_cpu = ggml_get_type_traits_cpu(type);
if (qfns_cpu->from_float && qfns->to_float) { if (qfns_cpu->from_float && qfns->to_float) {
if (params.verbose) { if (params.verbose) {
printf("testing %s ...\n", ggml_type_name(type)); LOG_INF("testing %s ...\n", ggml_type_name(type));
} }
ggml_quantize_init(type); ggml_quantize_init(type);
@@ -390,7 +421,7 @@ int main(int argc, char ** argv) {
continue; continue;
} }
if (params.verbose) { if (params.verbose) {
printf(" %s ...\n", kv_tensor.first.c_str()); LOG_INF(" %s ...\n", kv_tensor.first.c_str());
} }
std::string layer_name { ggml_type_name(type) }; std::string layer_name { ggml_type_name(type) };
layer_name += "::" + kv_tensor.first; layer_name += "::" + kv_tensor.first;
@@ -423,5 +454,7 @@ int main(int argc, char ** argv) {
printf("%s: total time = %8.2f ms\n", __func__, (t_main_end_us - t_main_start_us)/1000.0); printf("%s: total time = %8.2f ms\n", __func__, (t_main_end_us - t_main_start_us)/1000.0);
} }
LOG("%s: %s\n", "test-quantize-stats", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+28 -13
View File
@@ -4,6 +4,10 @@
#include "llama.h" #include "llama.h"
#include "ggml.h" #include "ggml.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#ifdef NDEBUG #ifdef NDEBUG
#undef NDEBUG #undef NDEBUG
#endif #endif
@@ -85,7 +89,7 @@ static void test_reasoning_budget(
llama_sampler_accept(sampler, sequence[i]); llama_sampler_accept(sampler, sequence[i]);
fprintf(stderr, " i=%zu: token=%d, finite_count=%zu, finite_token=%d\n", i, (int)sequence[i], finite_count, (int)finite_token); LOG_TRC(" i=%zu: token=%d, finite_count=%zu, finite_token=%d\n", i, (int)sequence[i], finite_count, (int)finite_token);
if (finite_count == 1) { if (finite_count == 1) {
if (actual_force_start == SIZE_MAX) { if (actual_force_start == SIZE_MAX) {
@@ -103,28 +107,28 @@ static void test_reasoning_budget(
// Verify forcing occurred at expected positions // Verify forcing occurred at expected positions
if (expected_force_start == SIZE_MAX) { if (expected_force_start == SIZE_MAX) {
if (actual_force_start != SIZE_MAX) { if (actual_force_start != SIZE_MAX) {
fprintf(stderr, "Test '%s' FAILED: Expected no forcing, but forcing occurred at %zu\n", test_name, actual_force_start); LOG_ERR("Test '%s' FAILED: Expected no forcing, but forcing occurred at %zu\n", test_name, actual_force_start);
GGML_ASSERT(false && "Expected no forcing, but forcing occurred"); GGML_ASSERT(false && "Expected no forcing, but forcing occurred");
} }
} else { } else {
if (actual_force_start == SIZE_MAX) { if (actual_force_start == SIZE_MAX) {
fprintf(stderr, "Test '%s' FAILED: Expected forcing but none occurred\n", test_name); LOG_ERR("Test '%s' FAILED: Expected forcing but none occurred\n", test_name);
GGML_ASSERT(false && "Expected forcing but none occurred"); GGML_ASSERT(false && "Expected forcing but none occurred");
} }
if (actual_force_start != expected_force_start) { if (actual_force_start != expected_force_start) {
fprintf(stderr, "Test '%s' FAILED: Forcing started at %zu, expected %zu\n", test_name, actual_force_start, expected_force_start); LOG_ERR("Test '%s' FAILED: Forcing started at %zu, expected %zu\n", test_name, actual_force_start, expected_force_start);
GGML_ASSERT(false && "Forcing started at wrong position"); GGML_ASSERT(false && "Forcing started at wrong position");
} }
} }
if (expected_force_end != SIZE_MAX) { if (expected_force_end != SIZE_MAX) {
if (actual_force_end < expected_force_end) { if (actual_force_end < expected_force_end) {
fprintf(stderr, "Test '%s' FAILED: Forcing ended at %zu, expected >= %zu\n", test_name, actual_force_end, expected_force_end); LOG_ERR("Test '%s' FAILED: Forcing ended at %zu, expected >= %zu\n", test_name, actual_force_end, expected_force_end);
GGML_ASSERT(false && "Forcing ended too early"); GGML_ASSERT(false && "Forcing ended too early");
} }
} }
fprintf(stderr, " Test '%s' passed (force_start=%zu, force_end=%zu)\n", test_name, actual_force_start, actual_force_end); LOG_INF(" Test '%s' passed (force_start=%zu, force_end=%zu)\n", test_name, actual_force_start, actual_force_end);
(void)sequence; (void)sequence;
} }
@@ -255,7 +259,7 @@ static void test_reasoning_budget_force_manual() {
// a null sampler is safely ignored // a null sampler is safely ignored
GGML_ASSERT(!common_reasoning_budget_force(nullptr)); GGML_ASSERT(!common_reasoning_budget_force(nullptr));
fprintf(stderr, " Test 'manual force transition' passed\n"); LOG_INF(" Test 'manual force transition' passed\n");
} }
static void test_reasoning_budget_end_match() { static void test_reasoning_budget_end_match() {
@@ -330,7 +334,7 @@ static void test_reasoning_budget_end_match() {
// a null sampler is safely ignored // a null sampler is safely ignored
GGML_ASSERT(common_reasoning_budget_get_end_match(nullptr) == nullptr); GGML_ASSERT(common_reasoning_budget_get_end_match(nullptr) == nullptr);
fprintf(stderr, " Test 'matched end sequence' passed\n"); LOG_INF(" Test 'matched end sequence' passed\n");
} }
// UTF-8 boundary detection unit test // UTF-8 boundary detection unit test
@@ -358,9 +362,18 @@ static void test_utf8_boundary_detection() {
GGML_ASSERT(common_utf8_is_complete(std::string("hello\xC3\xA9", 7))); // ASCII + complete 2-byte GGML_ASSERT(common_utf8_is_complete(std::string("hello\xC3\xA9", 7))); // ASCII + complete 2-byte
} }
int main(void) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-reasoning-budget");
// Reasoning budget sampler tests // Reasoning budget sampler tests
printf("Testing reasoning budget sampler... "); LOG_INF("Testing reasoning budget sampler...\n");
// Test 1: Basic budget with start/end tokens - no forcing (natural end before budget exhausted) // Test 1: Basic budget with start/end tokens - no forcing (natural end before budget exhausted)
{ {
@@ -495,11 +508,13 @@ int main(void) {
test_reasoning_budget_force_manual(); test_reasoning_budget_force_manual();
test_reasoning_budget_end_match(); test_reasoning_budget_end_match();
printf("OK (12 tests passed)\n"); LOG_CNT("OK (12 tests passed)\n");
printf("Testing UTF-8 boundary detection... "); LOG_INF("Testing UTF-8 boundary detection...\n");
test_utf8_boundary_detection(); test_utf8_boundary_detection();
printf("OK\n"); LOG_CNT("OK\n");
LOG("%s: %s\n", "test-reasoning-budget", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+41 -30
View File
@@ -1,5 +1,6 @@
#include "arg.h" #include "arg.h"
#include "common.h" #include "common.h"
#include "log.h"
#include "ggml-backend.h" #include "ggml-backend.h"
#include "llama.h" #include "llama.h"
@@ -67,7 +68,7 @@ static llama_context * init_ctx(llama_model * model, llama_context_params cparam
llama_get_memory(ctx)->state_write(collector); llama_get_memory(ctx)->state_write(collector);
llama_memory_clear(llama_get_memory(ctx), true); llama_memory_clear(llama_get_memory(ctx), true);
if (collector.buffers.empty()) { if (collector.buffers.empty()) {
fprintf(stderr, "%s : no cache buffers found\n", __func__); LOG_ERR("%s : no cache buffers found\n", __func__);
llama_free(ctx); llama_free(ctx);
return nullptr; return nullptr;
} }
@@ -117,7 +118,7 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
llama_context * ctx_roll = make_ctx_multi(); llama_context * ctx_roll = make_ctx_multi();
llama_context * ctx_ref = make_ctx_multi(); llama_context * ctx_ref = make_ctx_multi();
if (ctx_roll == nullptr || ctx_ref == nullptr) { if (ctx_roll == nullptr || ctx_ref == nullptr) {
fprintf(stderr, "%s : failed to init multi-seq contexts\n", __func__); LOG_ERR("%s : failed to init multi-seq contexts\n", __func__);
return false; return false;
} }
@@ -127,7 +128,7 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
}; };
if (llama_n_rs_seq(ctx_roll) < n_rollback) { if (llama_n_rs_seq(ctx_roll) < n_rollback) {
fprintf(stderr, "%s : skipping because n_rs_seq is too small\n", __func__); LOG_WRN("%s : skipping because n_rs_seq is too small\n", __func__);
cleanup(); cleanup();
return true; return true;
} }
@@ -161,7 +162,7 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
ok = ok && !llama_memory_seq_rm(llama_get_memory(ctx_roll), (llama_seq_id) s, p0 - 1, -1); ok = ok && !llama_memory_seq_rm(llama_get_memory(ctx_roll), (llama_seq_id) s, p0 - 1, -1);
} }
if (!ok) { if (!ok) {
fprintf(stderr, "%s : multi-seq prefill/rollback failed\n", __func__); LOG_ERR("%s : multi-seq prefill/rollback failed\n", __func__);
cleanup(); cleanup();
return false; return false;
} }
@@ -177,7 +178,7 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
ok = ok && llama_decode(ctx_ref, batch) == 0; ok = ok && llama_decode(ctx_ref, batch) == 0;
llama_batch_free(batch); llama_batch_free(batch);
if (!ok) { if (!ok) {
fprintf(stderr, "%s : multi-seq replay decode failed\n", __func__); LOG_ERR("%s : multi-seq replay decode failed\n", __func__);
cleanup(); cleanup();
return false; return false;
} }
@@ -193,7 +194,7 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
const float * l_roll = llama_get_logits_ith(ctx_roll, i); const float * l_roll = llama_get_logits_ith(ctx_roll, i);
const float * l_ref = llama_get_logits_ith(ctx_ref, i); const float * l_ref = llama_get_logits_ith(ctx_ref, i);
if (l_roll == nullptr || l_ref == nullptr) { if (l_roll == nullptr || l_ref == nullptr) {
fprintf(stderr, "%s : missing multi-seq logits at index %u\n", __func__, i); LOG_ERR("%s : missing multi-seq logits at index %u\n", __func__, i);
cleanup(); cleanup();
return false; return false;
} }
@@ -208,13 +209,13 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
} }
if (diff_max > eps) { if (diff_max > eps) {
fprintf(stderr, "%s : multi-seq split replay logits mismatch (max diff %g, first at seq %u pos %d)\n", LOG_ERR("%s : multi-seq split replay logits mismatch (max diff %g, first at seq %u pos %d)\n",
__func__, (double) diff_max, seq_first, pos_first); __func__, (double) diff_max, seq_first, pos_first);
cleanup(); cleanup();
return false; return false;
} }
fprintf(stderr, "%s : multi-seq split replay matched (max diff %g)\n", __func__, (double) diff_max); LOG_INF("%s : multi-seq split replay matched (max diff %g)\n", __func__, (double) diff_max);
// seq-1-only decodes must be independent of seq 0's content: diverge seq 0 // seq-1-only decodes must be independent of seq 0's content: diverge seq 0
// in ctx_ref only, then compare identical seq-1-only continuations bitwise // in ctx_ref only, then compare identical seq-1-only continuations bitwise
@@ -251,13 +252,13 @@ static bool test_multi_seq_split_replay(const common_params & params, llama_mode
} }
if (!ok || diff_tail > eps) { if (!ok || diff_tail > eps) {
fprintf(stderr, "%s : seq-1-only decode leaked seq 0 state (ok=%d, max diff %g)\n", LOG_ERR("%s : seq-1-only decode leaked seq 0 state (ok=%d, max diff %g)\n",
__func__, ok ? 1 : 0, (double) diff_tail); __func__, ok ? 1 : 0, (double) diff_tail);
cleanup(); cleanup();
return false; return false;
} }
fprintf(stderr, "%s : seq-1-only decode independent of seq 0 (max diff %g)\n", __func__, (double) diff_tail); LOG_INF("%s : seq-1-only decode independent of seq 0 (max diff %g)\n", __func__, (double) diff_tail);
cleanup(); cleanup();
return true; return true;
} }
@@ -269,12 +270,12 @@ static int test_rollback(const common_params & params, llama_model * model, uint
llama_context * ctx_src = make_ctx(params, model, fill); llama_context * ctx_src = make_ctx(params, model, fill);
llama_context * ctx_dst = make_ctx(params, model, fill); llama_context * ctx_dst = make_ctx(params, model, fill);
if (ctx_src == nullptr || ctx_dst == nullptr) { if (ctx_src == nullptr || ctx_dst == nullptr) {
fprintf(stderr, "%s : failed to init contexts\n", __func__); LOG_ERR("%s : failed to init contexts\n", __func__);
return 1; return 1;
} }
if (llama_n_rs_seq(ctx_src) == 0) { if (llama_n_rs_seq(ctx_src) == 0) {
fprintf(stderr, "%s : skipping because n_rs_seq is disabled\n", __func__); LOG_WRN("%s : skipping because n_rs_seq is disabled\n", __func__);
llama_free(ctx_src); llama_free(ctx_src);
llama_free(ctx_dst); llama_free(ctx_dst);
return 0; return 0;
@@ -289,13 +290,13 @@ static int test_rollback(const common_params & params, llama_model * model, uint
const uint32_t n_rs_seq = llama_n_rs_seq(ctx_src); const uint32_t n_rs_seq = llama_n_rs_seq(ctx_src);
constexpr uint32_t n_rollback = 3; constexpr uint32_t n_rollback = 3;
if (n_rs_seq < n_rollback) { if (n_rs_seq < n_rollback) {
fprintf(stderr, "%s : skipping because n_rs_seq is too small\n", __func__); LOG_WRN("%s : skipping because n_rs_seq is too small\n", __func__);
llama_free(ctx_src); llama_free(ctx_src);
llama_free(ctx_dst); llama_free(ctx_dst);
return 0; return 0;
} }
if (tokens.empty()) { if (tokens.empty()) {
fprintf(stderr, "%s : not enough prompt tokens\n", __func__); LOG_ERR("%s : not enough prompt tokens\n", __func__);
return 1; return 1;
} }
tokens.resize(n_rs_seq + 1, tokens.back()); tokens.resize(n_rs_seq + 1, tokens.back());
@@ -307,11 +308,11 @@ static int test_rollback(const common_params & params, llama_model * model, uint
// Replaying them crosses DSV4's ratio-4 compressor boundary. // Replaying them crosses DSV4's ratio-4 compressor boundary.
// Rollback leaves the recurrent memory in a snapshot state (rs_idx != 0). // Rollback leaves the recurrent memory in a snapshot state (rs_idx != 0).
if (!decode_tokens(ctx_src, tokens, n_tokens)) { if (!decode_tokens(ctx_src, tokens, n_tokens)) {
fprintf(stderr, "%s : failed to decode prompt\n", __func__); LOG_ERR("%s : failed to decode prompt\n", __func__);
return 1; return 1;
} }
if (!llama_memory_seq_rm(llama_get_memory(ctx_src), 0, rollback_pos, -1)) { if (!llama_memory_seq_rm(llama_get_memory(ctx_src), 0, rollback_pos, -1)) {
fprintf(stderr, "%s : rollback failed\n", __func__); LOG_ERR("%s : rollback failed\n", __func__);
return 1; return 1;
} }
@@ -327,21 +328,21 @@ static int test_rollback(const common_params & params, llama_model * model, uint
const llama_pos pos = rollback_pos + i; const llama_pos pos = rollback_pos + i;
if (!decode_one(ctx_src, tokens[pos], pos) || if (!decode_one(ctx_src, tokens[pos], pos) ||
!decode_one(ctx_dst, tokens[pos], pos)) { !decode_one(ctx_dst, tokens[pos], pos)) {
fprintf(stderr, "%s : %s replay failed at position %d\n", __func__, mode, pos); LOG_ERR("%s : %s replay failed at position %d\n", __func__, mode, pos);
return false; return false;
} }
const float * logits_src = llama_get_logits_ith(ctx_src, 0); const float * logits_src = llama_get_logits_ith(ctx_src, 0);
const float * logits_dst = llama_get_logits_ith(ctx_dst, 0); const float * logits_dst = llama_get_logits_ith(ctx_dst, 0);
if (logits_src == nullptr || logits_dst == nullptr) { if (logits_src == nullptr || logits_dst == nullptr) {
fprintf(stderr, "%s : missing %s logits at position %d\n", __func__, mode, pos); LOG_ERR("%s : missing %s logits at position %d\n", __func__, mode, pos);
return false; return false;
} }
logits_src_replay[i].assign(logits_src, logits_src + n_vocab); logits_src_replay[i].assign(logits_src, logits_src + n_vocab);
for (int token = 0; token < n_vocab; ++token) { for (int token = 0; token < n_vocab; ++token) {
if (logit_diff(logits_src[token], logits_dst[token]) > eps) { if (logit_diff(logits_src[token], logits_dst[token]) > eps) {
fprintf(stderr, "%s : %s logits mismatch at position %d, token %d (%g != %g)\n", LOG_ERR("%s : %s logits mismatch at position %d, token %d (%g != %g)\n",
__func__, mode, pos, token, (double) logits_src[token], (double) logits_dst[token]); __func__, mode, pos, token, (double) logits_src[token], (double) logits_dst[token]);
return false; return false;
} }
@@ -355,7 +356,7 @@ static int test_rollback(const common_params & params, llama_model * model, uint
if (!llama_memory_seq_rm(llama_get_memory(ctx_src), 0, rollback_pos, -1) || if (!llama_memory_seq_rm(llama_get_memory(ctx_src), 0, rollback_pos, -1) ||
!llama_memory_seq_rm(llama_get_memory(ctx_dst), 0, rollback_pos, -1)) { !llama_memory_seq_rm(llama_get_memory(ctx_dst), 0, rollback_pos, -1)) {
fprintf(stderr, "%s : partial rollback failed\n", __func__); LOG_ERR("%s : partial rollback failed\n", __func__);
return 1; return 1;
} }
@@ -373,7 +374,7 @@ static int test_rollback(const common_params & params, llama_model * model, uint
// non-zero at load time. The restore must wipe that state and still match. // non-zero at load time. The restore must wipe that state and still match.
llama_context * ctx_dirty = make_ctx(params, model, fill); llama_context * ctx_dirty = make_ctx(params, model, fill);
if (ctx_dirty == nullptr) { if (ctx_dirty == nullptr) {
fprintf(stderr, "%s : failed to init dirty ctx\n", __func__); LOG_ERR("%s : failed to init dirty ctx\n", __func__);
return 1; return 1;
} }
@@ -385,11 +386,11 @@ static int test_rollback(const common_params & params, llama_model * model, uint
} }
} }
if (!decode_tokens(ctx_dirty, noise, n_tokens)) { if (!decode_tokens(ctx_dirty, noise, n_tokens)) {
fprintf(stderr, "%s : dirty prompt decode failed\n", __func__); LOG_ERR("%s : dirty prompt decode failed\n", __func__);
return 1; return 1;
} }
if (!llama_memory_seq_rm(llama_get_memory(ctx_dirty), 0, rollback_pos, -1)) { if (!llama_memory_seq_rm(llama_get_memory(ctx_dirty), 0, rollback_pos, -1)) {
fprintf(stderr, "%s : dirty rollback failed\n", __func__); LOG_ERR("%s : dirty rollback failed\n", __func__);
return 1; return 1;
} }
@@ -398,26 +399,26 @@ static int test_rollback(const common_params & params, llama_model * model, uint
for (uint32_t i = 0; i < n_rollback; ++i) { for (uint32_t i = 0; i < n_rollback; ++i) {
const llama_pos pos = rollback_pos + i; const llama_pos pos = rollback_pos + i;
if (!decode_one(ctx_dirty, tokens[pos], pos)) { if (!decode_one(ctx_dirty, tokens[pos], pos)) {
fprintf(stderr, "%s : dirty replay failed at position %d\n", __func__, pos); LOG_ERR("%s : dirty replay failed at position %d\n", __func__, pos);
return 1; return 1;
} }
const float * logits_dirty = llama_get_logits_ith(ctx_dirty, 0); const float * logits_dirty = llama_get_logits_ith(ctx_dirty, 0);
if (logits_dirty == nullptr) { if (logits_dirty == nullptr) {
fprintf(stderr, "%s : missing dirty logits at position %d\n", __func__, pos); LOG_ERR("%s : missing dirty logits at position %d\n", __func__, pos);
return 1; return 1;
} }
for (int token = 0; token < n_vocab; ++token) { for (int token = 0; token < n_vocab; ++token) {
if (logit_diff(logits_src_replay[i][token], logits_dirty[token]) > eps) { if (logit_diff(logits_src_replay[i][token], logits_dirty[token]) > eps) {
fprintf(stderr, "%s : dirty-ctx logits mismatch at position %d, token %d (%g != %g)\n", LOG_ERR("%s : dirty-ctx logits mismatch at position %d, token %d (%g != %g)\n",
__func__, pos, token, (double) logits_src_replay[i][token], (double) logits_dirty[token]); __func__, pos, token, (double) logits_src_replay[i][token], (double) logits_dirty[token]);
return 1; return 1;
} }
} }
} }
fprintf(stderr, "%s : recurrent rollback checkpoint restored successfully\n", __func__); LOG_INF("%s : recurrent rollback checkpoint restored successfully\n", __func__);
llama_free(ctx_src); llama_free(ctx_src);
llama_free(ctx_dst); llama_free(ctx_dst);
llama_free(ctx_dirty); llama_free(ctx_dirty);
@@ -442,26 +443,36 @@ int main(int argc, char ** argv) {
return 1; return 1;
} }
LOG("%s: running\n", "test-recurrent-state-rollback");
ggml_backend_load_all(); ggml_backend_load_all();
common_init_result_ptr llama_init = common_init_from_params(params); common_init_result_ptr llama_init = common_init_from_params(params);
llama_model * model = llama_init->model(); llama_model * model = llama_init->model();
if (model == nullptr) { if (model == nullptr) {
fprintf(stderr, "%s : failed to init model\n", __func__); LOG_ERR("%s : failed to init model\n", __func__);
LOG("%s: %s\n", "test-recurrent-state-rollback", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
if (!llama_model_is_recurrent(model) && !llama_model_is_hybrid(model)) { if (!llama_model_is_recurrent(model) && !llama_model_is_hybrid(model)) {
fprintf(stderr, "%s : skipping for non-recurrent model\n", __func__); LOG_WRN("%s : skipping for non-recurrent model\n", __func__);
LOG("%s: %s\n", "test-recurrent-state-rollback", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
for (uint8_t fill : { 0, 0x3e }) { for (uint8_t fill : { 0, 0x3e }) {
fprintf(stderr, "%s : testing with cache fill 0x%02x\n", __func__, fill); LOG_INF("%s : testing with cache fill 0x%02x\n", __func__, fill);
if (test_rollback(params, model, fill) != 0) { if (test_rollback(params, model, fill) != 0) {
LOG("%s: %s\n", "test-recurrent-state-rollback", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
LOG("%s: %s\n", "test-recurrent-state-rollback", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+27 -7
View File
@@ -1,6 +1,10 @@
#include "ggml.h" #include "ggml.h"
#include "ggml-cpu.h" #include "ggml-cpu.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cmath> #include <cmath>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@@ -124,7 +128,19 @@ static void ggml_graph_compute_helper(std::vector<uint8_t> & buf, ggml_cgraph *
ggml_graph_compute(graph, &plan); ggml_graph_compute(graph, &plan);
} }
int main(int /*argc*/, const char ** /*argv*/) { int main(int argc, char ** argv) {
// the body below declares its own "params", so scope the parsed one
{
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
LOG("%s: running\n", "test-rope");
struct ggml_init_params params = { struct ggml_init_params params = {
/* .mem_size = */ 128*1024*1024, /* .mem_size = */ 128*1024*1024,
/* .mem_buffer = */ NULL, /* .mem_buffer = */ NULL,
@@ -245,12 +261,14 @@ int main(int /*argc*/, const char ** /*argv*/) {
// printf("%f %f\n", r1_data[i], r2_data[i]); // printf("%f %f\n", r1_data[i], r2_data[i]);
//} //}
printf("mode: %d\n", mode); LOG_INF("mode: %d\n", mode);
printf("sum0: %f\n", sum0); LOG_INF("sum0: %f\n", sum0);
printf("sum1: %f\n", sum1); LOG_INF("sum1: %f\n", sum1);
printf("diff: %f\n", diff); LOG_INF("diff: %f\n", diff);
printf("rel err: %f\n", diff / sum0); LOG_INF("rel err: %f\n", diff / sum0);
printf("rel err: %f\n", diff / sum1); LOG_INF("rel err: %f\n", diff / sum1);
// drain the queue, the checks below abort
common_log_flush(common_log_main());
GGML_ASSERT(diff / sum0 < 0.0001f); GGML_ASSERT(diff / sum0 < 0.0001f);
GGML_ASSERT(diff / sum1 < 0.0001f); GGML_ASSERT(diff / sum1 < 0.0001f);
@@ -259,5 +277,7 @@ int main(int /*argc*/, const char ** /*argv*/) {
ggml_free(ctx0); ggml_free(ctx0);
LOG("%s: %s\n", "test-rope", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+37 -3
View File
@@ -4,12 +4,43 @@
#include "ggml-rpc.h" #include "ggml-rpc.h"
#include "ggml.h" #include "ggml.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <vector>
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
GGML_ASSERT(argc == 3); std::vector<const char *> endpoints;
{
common_params params;
params.model.path = "."; // this test takes no model
common_init();
// the test takes two positional endpoints, everything else goes to common_params_parse
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
endpoints.push_back(argv[i]);
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
LOG("%s: running\n", "test-rpc-multi-server");
GGML_ASSERT(endpoints.size() == 2);
ggml_backend_load_all(); ggml_backend_load_all();
const char * endpoint_a = argv[1]; const char * endpoint_a = endpoints[0];
const char * endpoint_b = argv[2]; const char * endpoint_b = endpoints[1];
ggml_backend_t backend_a = ggml_backend_rpc_init(endpoint_a, 0); ggml_backend_t backend_a = ggml_backend_rpc_init(endpoint_a, 0);
ggml_backend_t backend_b = ggml_backend_rpc_init(endpoint_b, 0); ggml_backend_t backend_b = ggml_backend_rpc_init(endpoint_b, 0);
@@ -43,5 +74,8 @@ int main(int argc, char ** argv) {
ggml_free(ctx); ggml_free(ctx);
ggml_backend_free(backend_b); ggml_backend_free(backend_b);
ggml_backend_free(backend_a); ggml_backend_free(backend_a);
LOG("%s: %s\n", "test-rpc-multi-server", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+33 -1
View File
@@ -5,11 +5,14 @@
#include "llama.h" #include "llama.h"
#include "common.h" #include "common.h"
#include "arg.h"
#include "log.h"
#include <cstdint> #include <cstdint>
#include <mach/mach.h> #include <mach/mach.h>
#include <mach/mach_host.h> #include <mach/mach_host.h>
#include <unistd.h> #include <unistd.h>
#include <vector>
static uint64_t wired_memory() { static uint64_t wired_memory() {
vm_statistics64_data_t vmstat; vm_statistics64_data_t vmstat;
@@ -21,7 +24,34 @@ static uint64_t wired_memory() {
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
auto * model_path = common_get_model_or_exit(argc, argv); // the model path is this test's only positional argument
char * model_argv[2] = { argv[0], nullptr };
{
common_params params;
params.model.path = "."; // this test takes no model
common_init();
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (model_argv[1] == nullptr) {
model_argv[1] = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
}
// falls back to LLAMACPP_TEST_MODELFILE, or warns and exits if no model is given
auto * model_path = common_get_model_or_exit(model_argv[1] == nullptr ? 1 : 2, model_argv);
// that call exits when no model is given, so the verdict below is only reached with a model
LOG("%s: running\n", "test-rset-release");
llama_backend_init(); llama_backend_init();
@@ -49,5 +79,7 @@ int main(int argc, char ** argv) {
llama_backend_free(); llama_backend_free();
LOG("%s: %s\n", "test-rset-release", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+50 -8
View File
@@ -1,6 +1,10 @@
#include "ggml.h" #include "ggml.h"
#include "llama.h" #include "llama.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#ifdef NDEBUG #ifdef NDEBUG
#undef NDEBUG #undef NDEBUG
#endif #endif
@@ -15,11 +19,11 @@ extern struct llama_sampler * llama_sampler_init_dry_testing(float dry_multiplie
static void dump(const llama_token_data_array * cur_p) { static void dump(const llama_token_data_array * cur_p) {
for (size_t i = 0; i < cur_p->size; i++) { for (size_t i = 0; i < cur_p->size; i++) {
printf("%d: %f (%f)\n", cur_p->data[i].id, cur_p->data[i].p, cur_p->data[i].logit); LOG_CNT("%d: %f (%f)\n", cur_p->data[i].id, cur_p->data[i].p, cur_p->data[i].logit);
} }
} }
#define DUMP(__cur_p) do { printf("%s:%d (%s)\n", __FILE__, __LINE__, __func__); dump((__cur_p)); printf("-\n"); } while(0) #define DUMP(__cur_p) do { LOG_CNT("%s:%d (%s)\n", __FILE__, __LINE__, __func__); dump((__cur_p)); LOG_CNT("-\n"); } while(0)
struct sampler_tester { struct sampler_tester {
sampler_tester(size_t n_vocab) { sampler_tester(size_t n_vocab) {
@@ -76,6 +80,8 @@ static llama_token sample_dist(llama_sampler * sampler, const std::vector<float>
} }
static void test_dist_singleton_rng() { static void test_dist_singleton_rng() {
LOG_INF(" running %s\n", __func__);
llama_sampler * singleton = llama_sampler_init_dist(4242); llama_sampler * singleton = llama_sampler_init_dist(4242);
llama_sampler * control = llama_sampler_init_dist(4242); llama_sampler * control = llama_sampler_init_dist(4242);
@@ -92,6 +98,8 @@ static void test_dist_singleton_rng() {
} }
static void test_temp(const std::vector<float> & probs, const std::vector<float> & probs_expected, float temp) { static void test_temp(const std::vector<float> & probs, const std::vector<float> & probs_expected, float temp) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -103,6 +111,8 @@ static void test_temp(const std::vector<float> & probs, const std::vector<float>
} }
static void test_temp_ext(const std::vector<float> & probs, const std::vector<float> & probs_expected, float temp, float delta, float exponent) { static void test_temp_ext(const std::vector<float> & probs, const std::vector<float> & probs_expected, float temp, float delta, float exponent) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -114,6 +124,8 @@ static void test_temp_ext(const std::vector<float> & probs, const std::vector<fl
} }
static void test_top_k(const std::vector<float> & probs, const std::vector<float> & probs_expected, int k) { static void test_top_k(const std::vector<float> & probs, const std::vector<float> & probs_expected, int k) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -125,6 +137,8 @@ static void test_top_k(const std::vector<float> & probs, const std::vector<float
} }
static void test_top_p(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p) { static void test_top_p(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -136,6 +150,8 @@ static void test_top_p(const std::vector<float> & probs, const std::vector<float
} }
static void test_min_p(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p) { static void test_min_p(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -147,6 +163,8 @@ static void test_min_p(const std::vector<float> & probs, const std::vector<float
} }
static void test_xtc(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p, float t) { static void test_xtc(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p, float t) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -157,6 +175,8 @@ static void test_xtc(const std::vector<float> & probs, const std::vector<float>
} }
static void test_typical(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p) { static void test_typical(const std::vector<float> & probs, const std::vector<float> & probs_expected, float p) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -170,6 +190,8 @@ static void test_penalties(
const std::vector<float> & probs, const std::vector<llama_token> & last_tokens, const std::vector<float> & probs, const std::vector<llama_token> & last_tokens,
const std::vector<float> & probs_expected, float repeat_penalty, float alpha_frequency, float alpha_presence const std::vector<float> & probs_expected, float repeat_penalty, float alpha_frequency, float alpha_presence
) { ) {
LOG_INF(" running %s\n", __func__);
GGML_ASSERT(probs.size() == probs_expected.size()); GGML_ASSERT(probs.size() == probs_expected.size());
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
@@ -194,6 +216,8 @@ static void test_dry(
int dry_allowed_length, int dry_penalty_last_n, int dry_allowed_length, int dry_penalty_last_n,
const std::vector<std::vector<llama_token>> & seq_breakers const std::vector<std::vector<llama_token>> & seq_breakers
) { ) {
LOG_INF(" running %s\n", __func__);
GGML_ASSERT(probs.size() == expected_probs.size()); GGML_ASSERT(probs.size() == expected_probs.size());
sampler_tester tester(probs, expected_probs); sampler_tester tester(probs, expected_probs);
@@ -212,6 +236,8 @@ static void test_dry(
} }
static void test_top_n_sigma(const std::vector<float> & probs, const std::vector<float> & probs_expected, int n) { static void test_top_n_sigma(const std::vector<float> & probs, const std::vector<float> & probs_expected, int n) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(probs, probs_expected); sampler_tester tester(probs, probs_expected);
DUMP(&tester.cur_p); DUMP(&tester.cur_p);
@@ -224,6 +250,8 @@ static void test_top_n_sigma(const std::vector<float> & probs, const std::vector
static void test_sampler_queue(const size_t n_vocab, const std::string & samplers_sequence, const int top_k, const float top_p, const float min_p static void test_sampler_queue(const size_t n_vocab, const std::string & samplers_sequence, const int top_k, const float top_p, const float min_p
) { ) {
LOG_INF(" running %s\n", __func__);
sampler_tester tester(n_vocab); sampler_tester tester(n_vocab);
llama_token min_token_id = 0; llama_token min_token_id = 0;
@@ -293,7 +321,7 @@ static void test_sampler_queue(const size_t n_vocab, const std::string & sampler
} }
} }
printf("Sampler queue %3s OK with n_vocab=%05zu top_k=%5d top_p=%f min_p=%f\n", LOG_CNT("Sampler queue %3s OK with n_vocab=%05zu top_k=%5d top_p=%f min_p=%f\n",
samplers_sequence.c_str(), n_vocab, top_k, top_p, min_p); samplers_sequence.c_str(), n_vocab, top_k, top_p, min_p);
} }
@@ -312,12 +340,14 @@ static void bench(llama_sampler * cnstr, const char * cnstr_name, const std::vec
} }
const int64_t t_end = ggml_time_us(); const int64_t t_end = ggml_time_us();
llama_sampler_free(cnstr); llama_sampler_free(cnstr);
printf("%-43s: %8.3f us/iter\n", cnstr_name, (t_end - t_start) / (float)n_iter); LOG_CNT("%-43s: %8.3f us/iter\n", cnstr_name, (t_end - t_start) / (float)n_iter);
} }
#define BENCH(__cnstr, __data, __n_iter) bench((__cnstr), #__cnstr, (__data), (__n_iter)) #define BENCH(__cnstr, __data, __n_iter) bench((__cnstr), #__cnstr, (__data), (__n_iter))
static void test_perf() { static void test_perf() {
LOG_INF(" running %s\n", __func__);
const int n_vocab = 1 << 17; const int n_vocab = 1 << 17;
std::vector<llama_token_data> data; std::vector<llama_token_data> data;
@@ -335,7 +365,16 @@ static void test_perf() {
BENCH(llama_sampler_init_xtc (1.0f, 0.1f, 1, 1), data, 32); BENCH(llama_sampler_init_xtc (1.0f, 0.1f, 1, 1), data, 32);
} }
int main(void) { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-sampling");
ggml_time_init(); ggml_time_init();
test_dist_singleton_rng(); test_dist_singleton_rng();
@@ -366,12 +405,12 @@ int main(void) {
test_min_p({0.1f, 0.2f, 0.3f, 0.4f}, {0.4f/0.4f}, 1.00f); test_min_p({0.1f, 0.2f, 0.3f, 0.4f}, {0.4f/0.4f}, 1.00f);
test_min_p({0.1f, 0.2f, 0.3f, 0.4f}, {0.4f/0.4f}, 1.05f); test_min_p({0.1f, 0.2f, 0.3f, 0.4f}, {0.4f/0.4f}, 1.05f);
printf("XTC should:\n"); LOG_CNT("XTC should:\n");
test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.1f}, 0.99f, 0.09f); test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.1f}, 0.99f, 0.09f);
test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.2f, 0.1f}, 0.99f, 0.19f); test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.2f, 0.1f}, 0.99f, 0.19f);
test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.3f, 0.2f, 0.1f}, 0.99f, 0.29f); test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.3f, 0.2f, 0.1f}, 0.99f, 0.29f);
printf("XTC should not:\n"); LOG_CNT("XTC should not:\n");
test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.4f, 0.3f, 0.2f, 0.1f}, 0.99f, 0.39f); test_xtc({0.4f, 0.3f, 0.2f, 0.1f}, {0.4f, 0.3f, 0.2f, 0.1f}, 0.99f, 0.39f);
test_typical({0.97f, 0.01f, 0.01f, 0.01f}, {0.97f}, 0.5f); test_typical({0.97f, 0.01f, 0.01f, 0.01f}, {0.97f}, 0.5f);
@@ -424,9 +463,12 @@ int main(void) {
test_sampler_queue(10000, "mkp", 100, 0.8f, 0.1f); test_sampler_queue(10000, "mkp", 100, 0.8f, 0.1f);
test_sampler_queue(10000, "mpk", 100, 0.8f, 0.1f); test_sampler_queue(10000, "mpk", 100, 0.8f, 0.1f);
printf("OK\n"); LOG_CNT("OK\n");
test_perf(); test_perf();
// the tests abort on failure, so reaching this point means they all passed
LOG("%s: %s\n", "test-sampling", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+34 -20
View File
@@ -35,7 +35,8 @@ static llama_tokens generate_tokens(llama_context * ctx, llama_sampler * smpl, i
for (int i = 0; i < n_predict; i++) { for (int i = 0; i < n_predict; i++) {
auto next_token = llama_sampler_sample(smpl, ctx, -1); auto next_token = llama_sampler_sample(smpl, ctx, -1);
LOG("%d ", next_token); // one row per generated token: pure noise, TRACE only
LOG_CNTV(LOG_LEVEL_TRACE, "%d ", next_token);
result.push_back(next_token); result.push_back(next_token);
common_batch_clear(batch.get()); common_batch_clear(batch.get());
@@ -48,6 +49,8 @@ static llama_tokens generate_tokens(llama_context * ctx, llama_sampler * smpl, i
n_past++; n_past++;
} }
LOG_CNTV(LOG_LEVEL_TRACE, "\n");
return result; return result;
} }
@@ -71,15 +74,13 @@ static llama_tokens test_baseline(struct llama_model * model, const struct commo
return {}; return {};
} }
LOG("\n=== Test 1: baseline ===\n"); LOG_CNT("=== Test 1: baseline ===\n");
auto result = generate_tokens(ctx.get(), smpl.get(), n_past, params.n_predict, 0); auto result = generate_tokens(ctx.get(), smpl.get(), n_past, params.n_predict, 0);
if (result.empty()) { if (result.empty()) {
return {}; return {};
} }
LOG("\n");
return result; return result;
} }
@@ -103,7 +104,7 @@ static bool test_seq_rm_isolated(
return false; return false;
} }
LOG("\n=== Test 2: sequence removal isolation ===\n"); LOG_CNT("=== Test 2: sequence removal isolation ===\n");
const size_t n_tokens = tokens.size() < 128 ? tokens.size() : 128; const size_t n_tokens = tokens.size() < 128 ? tokens.size() : 128;
for (llama_seq_id seq_id = 0; seq_id < 2; ++seq_id) { for (llama_seq_id seq_id = 0; seq_id < 2; ++seq_id) {
@@ -156,7 +157,8 @@ static bool test_seq_rm_isolated(
return false; return false;
} }
LOG("PASS\n"); // per-sub-test verdict: TRACE, the per-model line already reports the result
LOG_TRC("PASS\n");
return true; return true;
} }
@@ -175,7 +177,7 @@ static bool test_state_load(struct llama_model * model, const struct common_para
auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)}; auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)};
llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed)); llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed));
LOG("\n=== Test 3: state load ===\n"); LOG_CNT("=== Test 3: state load ===\n");
// Load state from file // Load state from file
llama_tokens unused_sts(tokens.size()); llama_tokens unused_sts(tokens.size());
@@ -206,7 +208,7 @@ static bool test_state_load(struct llama_model * model, const struct common_para
return false; return false;
} }
LOG("\nPASS\n"); LOG_TRC("PASS\n");
return true; return true;
} }
@@ -226,7 +228,7 @@ static bool test_seq_cp_host(struct llama_model * model, const struct common_par
auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)}; auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)};
llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed)); llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed));
LOG("\n=== Test 4: seq copy (host) ===\n"); LOG_CNT("=== Test 4: seq copy (host) ===\n");
// Load state from file // Load state from file
llama_tokens unused_sts(tokens.size()); llama_tokens unused_sts(tokens.size());
@@ -278,7 +280,7 @@ static bool test_seq_cp_host(struct llama_model * model, const struct common_par
return false; return false;
} }
LOG("\nPASS\n"); LOG_TRC("PASS\n");
return true; return true;
} }
@@ -298,7 +300,7 @@ static bool test_seq_cp_device(struct llama_model * model, const struct common_p
auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)}; auto smpl = llama_sampler_ptr{llama_sampler_chain_init(sparams)};
llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed)); llama_sampler_chain_add(smpl.get(), llama_sampler_init_dist(params.sampling.seed));
LOG("\n=== Test 5: seq copy (device) ===\n"); LOG_CNT("=== Test 5: seq copy (device) ===\n");
// Load state from file // Load state from file
llama_tokens unused_sts(tokens.size()); llama_tokens unused_sts(tokens.size());
@@ -350,7 +352,7 @@ static bool test_seq_cp_device(struct llama_model * model, const struct common_p
return false; return false;
} }
LOG("\nPASS\n"); LOG_TRC("PASS\n");
return true; return true;
} }
@@ -367,7 +369,7 @@ static bool test_seq_cp_scatter(struct llama_model * model, const struct common_
params_ctx.kv_unified = true; params_ctx.kv_unified = true;
auto ctx = llama_context_ptr{llama_init_from_model(model, params_ctx)}; auto ctx = llama_context_ptr{llama_init_from_model(model, params_ctx)};
LOG("\n=== Test %d: seq copy (%s, scatter) ===\n", test_num, on_device ? "device" : "host"); LOG_CNT("=== Test %d: seq copy (%s, scatter) ===\n", test_num, on_device ? "device" : "host");
const uint32_t flags = on_device ? LLAMA_STATE_SEQ_FLAGS_ON_DEVICE : LLAMA_STATE_SEQ_FLAGS_NONE; const uint32_t flags = on_device ? LLAMA_STATE_SEQ_FLAGS_ON_DEVICE : LLAMA_STATE_SEQ_FLAGS_NONE;
@@ -444,7 +446,7 @@ static bool test_seq_cp_scatter(struct llama_model * model, const struct common_
return false; return false;
} }
LOG("\nPASS\n"); LOG_TRC("PASS\n");
return true; return true;
} }
@@ -455,7 +457,7 @@ static bool test_state_roundtrip(struct llama_model * model, const struct common
auto params_ctx = common_context_params_to_llama(params); auto params_ctx = common_context_params_to_llama(params);
auto ctx = llama_context_ptr{llama_init_from_model(model, params_ctx)}; auto ctx = llama_context_ptr{llama_init_from_model(model, params_ctx)};
LOG("\n=== Test 8: state blob round-trip ===\n"); LOG_CNT("=== Test 8: state blob round-trip ===\n");
if (llama_decode(ctx.get(), llama_batch_get_one(const_cast<llama_token *>(tokens.data()), (int32_t) tokens.size()))) { if (llama_decode(ctx.get(), llama_batch_get_one(const_cast<llama_token *>(tokens.data()), (int32_t) tokens.size()))) {
LOG_ERR("\n%s: failed to decode prompt\n", __func__); LOG_ERR("\n%s: failed to decode prompt\n", __func__);
@@ -503,7 +505,7 @@ static bool test_state_roundtrip(struct llama_model * model, const struct common
return false; return false;
} }
LOG("\nPASS\n"); LOG_TRC("PASS\n");
return true; return true;
} }
@@ -590,7 +592,7 @@ static bool run_save_load_tests_for_model(const std::string & model_path, const
return false; return false;
} }
LOG("\nAll tests passed.\n"); LOG_CNT("All tests passed.\n");
return true; return true;
} }
@@ -615,6 +617,7 @@ int main(int argc, char ** argv) {
if (strcmp(argv[i], "--models") == 0) { if (strcmp(argv[i], "--models") == 0) {
if (i + 1 >= argc) { if (i + 1 >= argc) {
LOG_ERR("%s: --models requires a directory argument\n", __func__); LOG_ERR("%s: --models requires a directory argument\n", __func__);
common_log_flush(common_log_main());
return 1; return 1;
} }
models_dir = argv[i + 1]; models_dir = argv[i + 1];
@@ -645,12 +648,16 @@ int main(int argc, char ** argv) {
params.n_predict = 16; params.n_predict = 16;
} }
LOG("%s: running\n", "test-save-load-state");
ggml_backend_load_all(); ggml_backend_load_all();
if (!models_dir.empty()) { if (!models_dir.empty()) {
// run the suite over every dummy model in the directory // run the suite over every dummy model in the directory
if (!std::filesystem::exists(models_dir) || !std::filesystem::is_directory(models_dir)) { if (!std::filesystem::exists(models_dir) || !std::filesystem::is_directory(models_dir)) {
LOG_ERR("%s: models directory '%s' does not exist\n", __func__, models_dir.c_str()); LOG_ERR("%s: models directory '%s' does not exist\n", __func__, models_dir.c_str());
LOG("%s: %s\n", "test-save-load-state", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -664,6 +671,8 @@ int main(int argc, char ** argv) {
if (models.empty()) { if (models.empty()) {
LOG_ERR("%s: no .gguf models found in '%s'\n", __func__, models_dir.c_str()); LOG_ERR("%s: no .gguf models found in '%s'\n", __func__, models_dir.c_str());
LOG("%s: %s\n", "test-save-load-state", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -672,7 +681,7 @@ int main(int argc, char ** argv) {
size_t n_pass = 0; size_t n_pass = 0;
size_t n_fail = 0; size_t n_fail = 0;
for (const auto & model_path : models) { for (const auto & model_path : models) {
LOG("\n================================================================\n"); LOG_CNT("================================================================\n");
LOG_INF("%s: model %s\n", __func__, model_path.c_str()); LOG_INF("%s: model %s\n", __func__, model_path.c_str());
if (run_save_load_tests_for_model(model_path, params)) { if (run_save_load_tests_for_model(model_path, params)) {
@@ -682,12 +691,17 @@ int main(int argc, char ** argv) {
} }
} }
LOG("\n================================================================\n"); LOG_CNT("================================================================\n");
LOG_INF("%s: summary: %zu passed, %zu failed (of %zu)\n", __func__, n_pass, n_fail, models.size()); LOG_INF("%s: summary: %zu passed, %zu failed (of %zu)\n", __func__, n_pass, n_fail, models.size());
LOG("%s: %s\n", "test-save-load-state", n_fail == 0 ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return n_fail == 0 ? 0 : 1; return n_fail == 0 ? 0 : 1;
} }
// single-model mode // single-model mode
return run_save_load_tests_for_model(params.model.path, params) ? 0 : 1; const bool success = run_save_load_tests_for_model(params.model.path, params);
LOG("%s: %s\n", "test-save-load-state", success ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return success ? 0 : 1;
} }
+27 -12
View File
@@ -8,6 +8,7 @@
#include "arg.h" #include "arg.h"
#include "common.h" #include "common.h"
#include "log.h"
#include "llama.h" #include "llama.h"
#include <vector> #include <vector>
@@ -28,6 +29,8 @@ int main(int argc, char ** argv) {
return 1; return 1;
} }
LOG("%s: running\n", "test-state-restore-fragmented");
// init // init
ggml_backend_load_all(); ggml_backend_load_all();
@@ -38,7 +41,9 @@ int main(int argc, char ** argv) {
llama_context * ctx = llama_init->context(); llama_context * ctx = llama_init->context();
if (model == nullptr || ctx == nullptr) { if (model == nullptr || ctx == nullptr) {
fprintf(stderr, "%s : failed to init\n", __func__); LOG_ERR("%s : failed to init\n", __func__);
LOG("%s: %s\n", "test-state-restore-fragmented", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -58,26 +63,30 @@ int main(int argc, char ** argv) {
batch.logits[batch.n_tokens - 1] = true; batch.logits[batch.n_tokens - 1] = true;
if (llama_decode(ctx, batch)) { if (llama_decode(ctx, batch)) {
fprintf(stderr, "%s : failed to decode seq 0\n", __func__); LOG_ERR("%s : failed to decode seq 0\n", __func__);
LOG("%s: %s\n", "test-state-restore-fragmented", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
fprintf(stderr, "%s : processed prompt on seq 0, 1, 2 (%zu tokens each)\n", __func__, tokens.size()); LOG_INF("%s : processed prompt on seq 0, 1, 2 (%zu tokens each)\n", __func__, tokens.size());
// Save state of seq 1 // Save state of seq 1
std::vector<uint8_t> seq_state(llama_state_seq_get_size(ctx, 1)); std::vector<uint8_t> seq_state(llama_state_seq_get_size(ctx, 1));
const size_t ncopy = llama_state_seq_get_data(ctx, seq_state.data(), seq_state.size(), 1); const size_t ncopy = llama_state_seq_get_data(ctx, seq_state.data(), seq_state.size(), 1);
if (ncopy != seq_state.size()) { if (ncopy != seq_state.size()) {
fprintf(stderr, "%s : failed to save seq 1 state\n", __func__); LOG_ERR("%s : failed to save seq 1 state\n", __func__);
LOG("%s: %s\n", "test-state-restore-fragmented", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
fprintf(stderr, "%s : saved seq 1 state, %zu bytes\n", __func__, ncopy); LOG_INF("%s : saved seq 1 state, %zu bytes\n", __func__, ncopy);
// clear seq 1 to create a "hole" in the KV cache (fragmentation) // clear seq 1 to create a "hole" in the KV cache (fragmentation)
// 0.20.20.20.2.... // 0.20.20.20.2....
llama_memory_t mem = llama_get_memory(ctx); llama_memory_t mem = llama_get_memory(ctx);
llama_memory_seq_rm(mem, 1, -1, -1); llama_memory_seq_rm(mem, 1, -1, -1);
fprintf(stderr, "%s : cleared seq 1 to create fragmentation\n", __func__); LOG_INF("%s : cleared seq 1 to create fragmentation\n", __func__);
// Now the cache has holes where seq 1 was // Now the cache has holes where seq 1 was
// This creates fragmentation - there's no contiguous block large enough // This creates fragmentation - there's no contiguous block large enough
@@ -88,13 +97,15 @@ int main(int argc, char ** argv) {
// Before the fix, this would fail with "failed to find available cells in kv cache" // Before the fix, this would fail with "failed to find available cells in kv cache"
const size_t nset = llama_state_seq_set_data(ctx, seq_state.data(), seq_state.size(), 1); const size_t nset = llama_state_seq_set_data(ctx, seq_state.data(), seq_state.size(), 1);
if (nset != seq_state.size()) { if (nset != seq_state.size()) {
fprintf(stderr, "%s : FAILED to restore seq state into fragmented cache (got %zu, expected %zu)\n", LOG_ERR("%s : FAILED to restore seq state into fragmented cache (got %zu, expected %zu)\n",
__func__, nset, seq_state.size()); __func__, nset, seq_state.size());
fprintf(stderr, "%s : This is the bug - state restore fails with fragmented KV cache\n", __func__); LOG_ERR("%s : This is the bug - state restore fails with fragmented KV cache\n", __func__);
LOG("%s: %s\n", "test-state-restore-fragmented", "FAILED");
llama_batch_free(batch); llama_batch_free(batch);
common_log_flush(common_log_main());
return 1; return 1;
} }
fprintf(stderr, "%s : restored state into seq 1, %zu bytes\n", __func__, nset); LOG_INF("%s : restored state into seq 1, %zu bytes\n", __func__, nset);
// Verify we can decode with the restored state // Verify we can decode with the restored state
// Generate one token to verify the restored state is usable // Generate one token to verify the restored state is usable
@@ -109,17 +120,21 @@ int main(int argc, char ** argv) {
common_batch_add(batch, next_token, (int)tokens.size(), {1}, true); common_batch_add(batch, next_token, (int)tokens.size(), {1}, true);
if (llama_decode(ctx, batch)) { if (llama_decode(ctx, batch)) {
fprintf(stderr, "%s : failed to decode with restored state\n", __func__); LOG_ERR("%s : failed to decode with restored state\n", __func__);
LOG("%s: %s\n", "test-state-restore-fragmented", "FAILED");
llama_sampler_free(smpl); llama_sampler_free(smpl);
llama_batch_free(batch); llama_batch_free(batch);
common_log_flush(common_log_main());
return 1; return 1;
} }
fprintf(stderr, "%s : successfully decoded with restored state, generated: '%s'\n", __func__, next_token_str.c_str()); LOG_INF("%s : successfully decoded with restored state, generated: '%s'\n", __func__, next_token_str.c_str());
fprintf(stderr, "%s : SUCCESS - state restore works with fragmented KV cache\n", __func__); LOG_INF("%s : SUCCESS - state restore works with fragmented KV cache\n", __func__);
llama_sampler_free(smpl); llama_sampler_free(smpl);
llama_batch_free(batch); llama_batch_free(batch);
LOG("%s: %s\n", "test-state-restore-fragmented", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+14 -4
View File
@@ -22,6 +22,8 @@ int main(int argc, char ** argv) {
return 1; return 1;
} }
LOG("%s: running\n", "test-thread-safety");
llama_backend_init(); llama_backend_init();
llama_numa_init(params.numa); llama_numa_init(params.numa);
@@ -56,6 +58,8 @@ int main(int argc, char ** argv) {
std::atomic<bool> failed = false; std::atomic<bool> failed = false;
for (int m = 0; m < num_models; ++m) { for (int m = 0; m < num_models; ++m) {
LOG_INF(" running model %d/%d\n", m + 1, num_models);
auto mparams = common_model_params_to_llama(params); auto mparams = common_model_params_to_llama(params);
if (m < gpu_dev_count) { if (m < gpu_dev_count) {
@@ -71,6 +75,8 @@ int main(int argc, char ** argv) {
llama_model * model = llama_model_load_from_file(params.model.path.c_str(), mparams); llama_model * model = llama_model_load_from_file(params.model.path.c_str(), mparams);
if (model == NULL) { if (model == NULL) {
LOG_ERR("%s: failed to load model '%s'\n", __func__, params.model.path.c_str()); LOG_ERR("%s: failed to load model '%s'\n", __func__, params.model.path.c_str());
LOG("%s: %s\n", "test-thread-safety", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -156,11 +162,15 @@ int main(int argc, char ** argv) {
thread.join(); thread.join();
} }
if (failed) { const bool ok = !failed;
if (!ok) {
LOG_ERR("One or more threads failed.\n"); LOG_ERR("One or more threads failed.\n");
return 1; } else {
LOG_INF("All threads finished without errors.\n");
} }
LOG_INF("All threads finished without errors.\n"); LOG("%s: %s\n", "test-thread-safety", ok ? "PASSED" : "FAILED");
return 0; common_log_flush(common_log_main());
return ok ? 0 : 1;
} }
+75 -33
View File
@@ -2,6 +2,9 @@
#include "common.h" #include "common.h"
#include "console.h" #include "console.h"
#include "arg.h"
#include "log.h"
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <map> #include <map>
@@ -63,7 +66,7 @@ static llama_tests read_tests(const std::string & fname_inp, const std::string &
std::ifstream ifs_inp(fname_inp); std::ifstream ifs_inp(fname_inp);
if (!ifs_inp) { if (!ifs_inp) {
fprintf(stderr, "%s : error: could not open file '%s'\n", __func__, fname_inp.c_str()); LOG_ERR("%s : error: could not open file '%s'\n", __func__, fname_inp.c_str());
return tests; return tests;
} }
@@ -71,7 +74,7 @@ static llama_tests read_tests(const std::string & fname_inp, const std::string &
std::ifstream ifs_out(fname_out); std::ifstream ifs_out(fname_out);
if (!ifs_out) { if (!ifs_out) {
fprintf(stderr, "%s : error: could not open file '%s'\n", __func__, fname_out.c_str()); LOG_ERR("%s : error: could not open file '%s'\n", __func__, fname_out.c_str());
return tests; return tests;
} }
@@ -96,7 +99,7 @@ static llama_tests read_tests(const std::string & fname_inp, const std::string &
} }
if (sinp.size() != sout.size()) { if (sinp.size() != sout.size()) {
fprintf(stderr, "%s : error: input and output files have different number of tests\n", __func__); LOG_ERR("%s : error: input and output files have different number of tests\n", __func__);
return tests; return tests;
} }
@@ -124,22 +127,44 @@ static llama_tests read_tests(const std::string & fname_inp, const std::string &
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc < 2) { common_params params;
fprintf(stderr, "Usage: %s vocab-file [text-file]\n", argv[0]); params.model.path = "."; // this test takes no model
common_init();
std::vector<std::string> positional;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
positional.push_back(argv[i]);
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1; return 1;
} }
const std::string fname = argv[1]; if (positional.empty()) {
LOG_ERR("Usage: %s vocab-file [text-file]\n", argv[0]);
common_log_flush(common_log_main());
return 1;
}
LOG("%s: running\n", "test-tokenizer-0");
const std::string fname = positional[0];
const std::string fname_inp = fname + ".inp"; const std::string fname_inp = fname + ".inp";
const std::string fname_out = fname + ".out"; const std::string fname_out = fname + ".out";
std::string fname_text; std::string fname_text;
if (argc > 2) { if (positional.size() > 1) {
fname_text = argv[2]; fname_text = positional[1];
} }
fprintf(stderr, "%s : reading vocab from: '%s'\n", __func__, fname.c_str()); LOG_INF("%s : reading vocab from: '%s'\n", __func__, fname.c_str());
llama_model * model; llama_model * model;
llama_context * ctx; llama_context * ctx;
@@ -155,7 +180,9 @@ int main(int argc, char **argv) {
model = llama_model_load_from_file(fname.c_str(), mparams); model = llama_model_load_from_file(fname.c_str(), mparams);
if (model == NULL) { if (model == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, fname.c_str()); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, fname.c_str());
LOG("%s: %s\n", "test-tokenizer-0", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -164,8 +191,10 @@ int main(int argc, char **argv) {
ctx = llama_init_from_model(model, cparams); ctx = llama_init_from_model(model, cparams);
if (ctx == NULL) { if (ctx == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, fname.c_str()); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, fname.c_str());
llama_model_free(model); llama_model_free(model);
LOG("%s: %s\n", "test-tokenizer-0", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
@@ -186,7 +215,9 @@ int main(int argc, char **argv) {
const auto res = read_tests(fname_inp, fname_out); const auto res = read_tests(fname_inp, fname_out);
if (res.empty()) { if (res.empty()) {
fprintf(stderr, "%s : error: no tests found\n", __func__); LOG_ERR("%s : error: no tests found\n", __func__);
LOG("%s: %s\n", "test-tokenizer-0", "FAILED");
common_log_flush(common_log_main());
exit(1); exit(1);
} }
@@ -210,14 +241,15 @@ int main(int argc, char **argv) {
continue; continue;
} }
printf("\n"); // per-data-item detail: INFO, so hidden under --errors-only
printf("src: '%s'\n", test_kv.first.c_str()); LOG_CNT("\n");
printf("res: '%s'\n", common_detokenize(ctx, res).c_str()); LOG_CNT("src: '%s'\n", test_kv.first.c_str());
printf("tok: "); LOG_CNT("res: '%s'\n", common_detokenize(ctx, res).c_str());
LOG_CNT("tok: ");
for (const auto & tok : res) { for (const auto & tok : res) {
printf("%d ", tok); LOG_CNT("%d ", tok);
} }
printf("\n"); LOG_CNT("\n");
bool correct = res.size() == test_kv.second.size(); bool correct = res.size() == test_kv.second.size();
for (int i = 0; i < (int) res.size() && correct; ++i) { for (int i = 0; i < (int) res.size() && correct; ++i) {
@@ -227,20 +259,21 @@ int main(int argc, char **argv) {
} }
if (!correct) { if (!correct) {
fprintf(stderr, "%s : failed test: '%s'\n", __func__, test_kv.first.c_str()); LOG_ERR("%s : failed test: '%s'\n", __func__, test_kv.first.c_str());
fprintf(stderr, "%s : detokenized to: '%s' instead of '%s'\n", __func__, LOG_ERR("%s : detokenized to: '%s' instead of '%s'\n", __func__,
common_detokenize(ctx, res).c_str(), common_detokenize(ctx, res).c_str(),
common_detokenize(ctx, test_kv.second).c_str()); common_detokenize(ctx, test_kv.second).c_str());
fprintf(stderr, "%s : expected tokens: ", __func__); // partial line: LOG_CNTV adds no prefix, error level so --errors-only keeps it
LOG_ERR("%s : expected tokens: ", __func__);
for (const auto & t : test_kv.second) { for (const auto & t : test_kv.second) {
fprintf(stderr, "%6d '%s', ", t, common_token_to_piece(ctx, t).c_str()); LOG_CNTV(LOG_LEVEL_ERROR, "%6d '%s', ", t, common_token_to_piece(ctx, t).c_str());
} }
fprintf(stderr, "\n"); LOG_CNTV(LOG_LEVEL_ERROR, "\n");
fprintf(stderr, "%s : got tokens: ", __func__); LOG_ERR("%s : got tokens: ", __func__);
for (const auto & t : res) { for (const auto & t : res) {
fprintf(stderr, "%6d '%s', ", t, common_token_to_piece(ctx, t).c_str()); LOG_CNTV(LOG_LEVEL_ERROR, "%6d '%s', ", t, common_token_to_piece(ctx, t).c_str());
} }
fprintf(stderr, "\n"); LOG_CNTV(LOG_LEVEL_ERROR, "\n");
success = false; success = false;
} }
@@ -254,19 +287,21 @@ int main(int argc, char **argv) {
// single threaded tokenization // single threaded tokenization
if (!fname_text.empty()) { if (!fname_text.empty()) {
fprintf(stderr, "%s : tokenizing: '%s'\n", __func__, fname_text.c_str()); LOG_INF("%s : tokenizing: '%s'\n", __func__, fname_text.c_str());
std::string text; std::string text;
{ {
std::ifstream ifs(fname_text); std::ifstream ifs(fname_text);
if (!ifs) { if (!ifs) {
fprintf(stderr, "%s : error: could not open file '%s'\n", __func__, fname_text.c_str()); LOG_ERR("%s : error: could not open file '%s'\n", __func__, fname_text.c_str());
LOG("%s: %s\n", "test-tokenizer-0", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
text = std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>()); text = std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
} }
fprintf(stderr, "%s : text size: %zu\n", __func__, text.size()); LOG_INF("%s : text size: %zu\n", __func__, text.size());
std::vector<llama_token> res; std::vector<llama_token> res;
@@ -277,17 +312,19 @@ int main(int argc, char **argv) {
const auto t_end = ggml_time_us(); const auto t_end = ggml_time_us();
fprintf(stderr, "%s : tokenized in %.3f ms (cpp)\n", __func__, (t_end - t_start) / 1000.0); LOG_INF("%s : tokenized in %.3f ms (cpp)\n", __func__, (t_end - t_start) / 1000.0);
} }
fprintf(stderr, "%s : tokens: %zu\n", __func__, res.size()); LOG_INF("%s : tokens: %zu\n", __func__, res.size());
{ {
const std::string fname_out = fname_text + ".tokcpp"; const std::string fname_out = fname_text + ".tokcpp";
std::ofstream ofs(fname_out); std::ofstream ofs(fname_out);
if (!ofs) { if (!ofs) {
fprintf(stderr, "%s : error: could not open file '%s'\n", __func__, fname_out.c_str()); LOG_ERR("%s : error: could not open file '%s'\n", __func__, fname_out.c_str());
LOG("%s: %s\n", "test-tokenizer-0", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -297,7 +334,7 @@ int main(int argc, char **argv) {
} }
} }
fprintf(stderr, "%s : tokens written to '%s'\n", __func__, (fname_text + ".tokcpp").c_str()); LOG_INF("%s : tokens written to '%s'\n", __func__, (fname_text + ".tokcpp").c_str());
} }
llama_free(ctx); llama_free(ctx);
@@ -305,8 +342,13 @@ int main(int argc, char **argv) {
llama_backend_free(); llama_backend_free();
common_log_flush(common_log_main());
printf("\n"); printf("\n");
printf("Tests %s\n", success ? "passed" : "failed"); printf("Tests %s\n", success ? "passed" : "failed");
LOG("%s: %s\n", "test-tokenizer-0", success ? "PASSED" : "FAILED");
common_log_flush(common_log_main());
return success ? 0 : 3; return success ? 0 : 3;
} }
+57 -21
View File
@@ -2,6 +2,9 @@
#include "common.h" #include "common.h"
#include "console.h" #include "console.h"
#include "arg.h"
#include "log.h"
#include "../src/unicode.h" #include "../src/unicode.h"
#include <cassert> #include <cassert>
@@ -15,25 +18,40 @@
#include <atomic> #include <atomic>
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc < 2 || argc > 3) { common_params params;
fprintf(stderr, "Usage: %s <vocab-file> [--ignore-merges]\n", argv[0]); params.model.path = "."; // this test takes no model
common_init();
std::vector<std::string> positional;
bool ignore_merges = false;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (std::strcmp(argv[i], "--ignore-merges") == 0) {
ignore_merges = true;
} else if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else {
positional.push_back(argv[i]);
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1; return 1;
} }
const std::string fname = argv[1]; if (positional.size() != 1) {
bool ignore_merges = false; LOG_ERR("Usage: %s <vocab-file> [--ignore-merges]\n", argv[0]);
if (argc == 3) { common_log_flush(common_log_main());
if (std::strcmp(argv[2], "--ignore-merges") != 0) { return 1;
fprintf(stderr, "Usage: %s <vocab-file> [--ignore-merges]\n", argv[0]);
return 1;
}
ignore_merges = true;
} }
fprintf(stderr, "%s : reading vocab from: '%s'\n", __func__, fname.c_str()); const std::string fname = positional[0];
LOG_INF("%s : reading vocab from: '%s'\n", __func__, fname.c_str());
if (ignore_merges) { if (ignore_merges) {
fprintf(stderr, "%s : ignoring merges for tokens inside vocab\n", __func__); LOG_INF("%s : ignoring merges for tokens inside vocab\n", __func__);
} }
llama_model * model; llama_model * model;
@@ -50,7 +68,8 @@ int main(int argc, char **argv) {
model = llama_model_load_from_file(fname.c_str(), mparams); model = llama_model_load_from_file(fname.c_str(), mparams);
if (model == NULL) { if (model == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, fname.c_str()); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, fname.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -59,8 +78,9 @@ int main(int argc, char **argv) {
ctx = llama_init_from_model(model, cparams); ctx = llama_init_from_model(model, cparams);
if (ctx == NULL) { if (ctx == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, fname.c_str()); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, fname.c_str());
llama_model_free(model); llama_model_free(model);
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
@@ -69,9 +89,13 @@ int main(int argc, char **argv) {
//GGML_ASSERT(llama_vocab_type(vocab) == LLAMA_VOCAB_TYPE_BPE); //GGML_ASSERT(llama_vocab_type(vocab) == LLAMA_VOCAB_TYPE_BPE);
if (llama_vocab_type(vocab) != LLAMA_VOCAB_TYPE_BPE) { if (llama_vocab_type(vocab) != LLAMA_VOCAB_TYPE_BPE) {
// a vocab of another type is a skip (99), so it does not get a running line
common_log_flush(common_log_main());
return 99; return 99;
} }
LOG("%s: running\n", "test-tokenizer-1-bpe");
#ifdef _WIN32 #ifdef _WIN32
// We need this for unicode console support // We need this for unicode console support
console::init(false, false); console::init(false, false);
@@ -80,27 +104,33 @@ int main(int argc, char **argv) {
const int n_vocab = llama_vocab_n_tokens(vocab); const int n_vocab = llama_vocab_n_tokens(vocab);
LOG_INF(" running vocab detokenize round-trip (%d tokens)\n", n_vocab);
for (int i = 0; i < n_vocab; ++i) { for (int i = 0; i < n_vocab; ++i) {
std::string str = common_detokenize(ctx, std::vector<int>(1, i)); std::string str = common_detokenize(ctx, std::vector<int>(1, i));
try { try {
auto cps = unicode_cpts_from_utf8(str); auto cps = unicode_cpts_from_utf8(str);
std::vector<llama_token> tokens = common_tokenize(ctx, str, false, true); std::vector<llama_token> tokens = common_tokenize(ctx, str, false, true);
if (ignore_merges && tokens.size() > 1) { if (ignore_merges && tokens.size() > 1) {
fprintf(stderr, LOG_ERR("%s : error: token %d detokenizes to '%s'(%zu) but "
"%s : error: token %d detokenizes to '%s'(%zu) but "
"tokenization of this to multiple tokens: [", "tokenization of this to multiple tokens: [",
__func__, i, str.c_str(), str.length()); __func__, i, str.c_str(), str.length());
fprintf(stderr, "%d", tokens[0]); // partial line: LOG_CNTV adds no prefix, error level so --errors-only keeps it
LOG_CNTV(LOG_LEVEL_ERROR, "%d", tokens[0]);
for (size_t i = 1; i < tokens.size(); i++) { for (size_t i = 1; i < tokens.size(); i++) {
fprintf(stderr, ", %d", tokens[i]); LOG_CNTV(LOG_LEVEL_ERROR, ", %d", tokens[i]);
} }
fprintf(stderr, "]\n"); LOG_CNTV(LOG_LEVEL_ERROR, "]\n");
LOG("%s: %s\n", "test-tokenizer-1-bpe", "FAILED");
common_log_flush(common_log_main());
return 2; return 2;
} }
std::string check = common_detokenize(ctx, tokens); std::string check = common_detokenize(ctx, tokens);
if (check != str) { if (check != str) {
fprintf(stderr, "%s : error: token %d detokenizes to '%s'(%zu) but tokenization of this detokenizes to '%s'(%zu)\n", LOG_ERR("%s : error: token %d detokenizes to '%s'(%zu) but tokenization of this detokenizes to '%s'(%zu)\n",
__func__, i, str.c_str(), str.length(), check.c_str(), check.length()); __func__, i, str.c_str(), str.length(), check.c_str(), check.length());
LOG("%s: %s\n", "test-tokenizer-1-bpe", "FAILED");
common_log_flush(common_log_main());
return 2; return 2;
} }
} }
@@ -111,6 +141,8 @@ int main(int argc, char **argv) {
// unicode // unicode
{ {
LOG_INF(" running unicode codepoint round-trip\n");
const int nthread = std::thread::hardware_concurrency(); const int nthread = std::thread::hardware_concurrency();
std::vector<std::thread> threads(nthread); std::vector<std::thread> threads(nthread);
@@ -129,7 +161,7 @@ int main(int argc, char **argv) {
std::vector<llama_token> tokens = common_tokenize(ctx, str, false); std::vector<llama_token> tokens = common_tokenize(ctx, str, false);
std::string check = common_detokenize(ctx, tokens); std::string check = common_detokenize(ctx, tokens);
if (cp != 9601 && str != check) { if (cp != 9601 && str != check) {
fprintf(stderr, "error: codepoint 0x%x detokenizes to '%s'(%zu) instead of '%s'(%zu)\n", LOG_ERR("error: codepoint 0x%x detokenizes to '%s'(%zu) instead of '%s'(%zu)\n",
cp, check.c_str(), check.length(), str.c_str(), str.length()); cp, check.c_str(), check.length(), str.c_str(), str.length());
errcode = 3; errcode = 3;
} }
@@ -142,6 +174,8 @@ int main(int argc, char **argv) {
} }
if (errcode) { if (errcode) {
LOG("%s: %s\n", "test-tokenizer-1-bpe", "FAILED");
common_log_flush(common_log_main());
return errcode; return errcode;
} }
} }
@@ -151,5 +185,7 @@ int main(int argc, char **argv) {
llama_backend_free(); llama_backend_free();
LOG("%s: %s\n", "test-tokenizer-1-bpe", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+45 -8
View File
@@ -2,6 +2,9 @@
#include "common.h" #include "common.h"
#include "console.h" #include "console.h"
#include "arg.h"
#include "log.h"
#include "../src/unicode.h" #include "../src/unicode.h"
#include <cassert> #include <cassert>
@@ -15,14 +18,32 @@
#include <atomic> #include <atomic>
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
if (argc < 2) { common_params params;
fprintf(stderr, "Usage: %s <vocab-file>\n", argv[0]); params.model.path = "."; // this test takes no model
common_init();
std::string fname;
std::vector<char *> common_argv;
common_argv.push_back(argv[0]);
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
common_argv.push_back(argv[i]); // an option: let common_params_parse handle it
} else if (fname.empty()) {
fname = argv[i];
}
}
common_argv.push_back(nullptr);
if (!common_params_parse((int) common_argv.size() - 1, common_argv.data(), params, LLAMA_EXAMPLE_COMMON)) {
return 1; return 1;
} }
const std::string fname = argv[1]; if (fname.empty()) {
LOG_ERR("Usage: %s <vocab-file>\n", argv[0]);
common_log_flush(common_log_main());
return 1;
}
fprintf(stderr, "%s : reading vocab from: '%s'\n", __func__, fname.c_str()); LOG_INF("%s : reading vocab from: '%s'\n", __func__, fname.c_str());
llama_model * model; llama_model * model;
llama_context * ctx; llama_context * ctx;
@@ -38,7 +59,8 @@ int main(int argc, char ** argv) {
model = llama_model_load_from_file(fname.c_str(), mparams); model = llama_model_load_from_file(fname.c_str(), mparams);
if (model == NULL) { if (model == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, fname.c_str()); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, fname.c_str());
common_log_flush(common_log_main());
return 1; return 1;
} }
@@ -47,8 +69,9 @@ int main(int argc, char ** argv) {
ctx = llama_init_from_model(model, cparams); ctx = llama_init_from_model(model, cparams);
if (ctx == NULL) { if (ctx == NULL) {
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, fname.c_str()); LOG_ERR("%s: error: failed to load vocab '%s'\n", __func__, fname.c_str());
llama_model_free(model); llama_model_free(model);
common_log_flush(common_log_main());
return 1; return 1;
} }
} }
@@ -57,9 +80,13 @@ int main(int argc, char ** argv) {
//GGML_ASSERT(llama_vocab_type(model) == LLAMA_VOCAB_TYPE_SPM); //GGML_ASSERT(llama_vocab_type(model) == LLAMA_VOCAB_TYPE_SPM);
if (llama_vocab_type(vocab) != LLAMA_VOCAB_TYPE_SPM) { if (llama_vocab_type(vocab) != LLAMA_VOCAB_TYPE_SPM) {
// a vocab of another type is a skip (99), so it does not get a running line
common_log_flush(common_log_main());
return 99; return 99;
} }
LOG("%s: running\n", "test-tokenizer-1-spm");
#ifdef _WIN32 #ifdef _WIN32
// We need this for unicode console support // We need this for unicode console support
console::init(false, false); console::init(false, false);
@@ -68,19 +95,25 @@ int main(int argc, char ** argv) {
const int n_vocab = llama_vocab_n_tokens(vocab); const int n_vocab = llama_vocab_n_tokens(vocab);
LOG_INF(" running vocab detokenize round-trip (%d tokens)\n", n_vocab);
for (int i = 0; i < n_vocab; ++i) { for (int i = 0; i < n_vocab; ++i) {
std::string str = common_detokenize(ctx, std::vector<int>(1, i), true); std::string str = common_detokenize(ctx, std::vector<int>(1, i), true);
std::vector<llama_token> tokens = common_tokenize(ctx, str, false, true); std::vector<llama_token> tokens = common_tokenize(ctx, str, false, true);
std::string check = common_detokenize(ctx, tokens); std::string check = common_detokenize(ctx, tokens);
if (check != str) { if (check != str) {
fprintf(stderr, "%s : error: token %d detokenizes to '%s'(%zu) but tokenization of this detokenizes to '%s'(%zu)\n", LOG_ERR("%s : error: token %d detokenizes to '%s'(%zu) but tokenization of this detokenizes to '%s'(%zu)\n",
__func__, i, str.c_str(), str.length(), check.c_str(), check.length()); __func__, i, str.c_str(), str.length(), check.c_str(), check.length());
LOG("%s: %s\n", "test-tokenizer-1-spm", "FAILED");
common_log_flush(common_log_main());
return 2; return 2;
} }
} }
// unicode // unicode
{ {
LOG_INF(" running unicode codepoint round-trip\n");
const int nthread = std::thread::hardware_concurrency(); const int nthread = std::thread::hardware_concurrency();
std::vector<std::thread> threads(nthread); std::vector<std::thread> threads(nthread);
@@ -99,7 +132,7 @@ int main(int argc, char ** argv) {
std::vector<llama_token> tokens = common_tokenize(ctx, str, false, true); std::vector<llama_token> tokens = common_tokenize(ctx, str, false, true);
std::string check = common_detokenize(ctx, tokens); std::string check = common_detokenize(ctx, tokens);
if (cp != 9601 && str != check) { if (cp != 9601 && str != check) {
fprintf(stderr, "error: codepoint 0x%x detokenizes to '%s'(%zu) instead of '%s'(%zu)\n", LOG_ERR("error: codepoint 0x%x detokenizes to '%s'(%zu) instead of '%s'(%zu)\n",
cp, check.c_str(), check.length(), str.c_str(), str.length()); cp, check.c_str(), check.length(), str.c_str(), str.length());
errcode = 3; errcode = 3;
} }
@@ -112,6 +145,8 @@ int main(int argc, char ** argv) {
} }
if(errcode) { if(errcode) {
LOG("%s: %s\n", "test-tokenizer-1-spm", "FAILED");
common_log_flush(common_log_main());
return errcode; return errcode;
} }
} }
@@ -121,5 +156,7 @@ int main(int argc, char ** argv) {
llama_backend_free(); llama_backend_free();
LOG("%s: %s\n", "test-tokenizer-1-spm", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+21 -4
View File
@@ -1,10 +1,23 @@
#include "../src/unicode.h" #include "../src/unicode.h"
#include "arg.h"
#include "common.h"
#include "log.h"
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <vector> #include <vector>
int main() { int main(int argc, char ** argv) {
common_params params;
params.model.path = "."; // this test takes no model
common_init();
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
LOG("%s: running\n", "test-unicode");
const std::vector<std::string> regex_exprs = { const std::vector<std::string> regex_exprs = {
"[~][A-Za-z]+| ?[\\p{S}]+|\\s+", "[~][A-Za-z]+| ?[\\p{S}]+|\\s+",
}; };
@@ -12,13 +25,17 @@ int main() {
const auto actual = unicode_regex_split(" ~foo", regex_exprs, false); const auto actual = unicode_regex_split(" ~foo", regex_exprs, false);
if (actual != expected) { if (actual != expected) {
fprintf(stderr, "unexpected split:"); LOG_ERR("unexpected split:");
for (const auto & piece : actual) { for (const auto & piece : actual) {
fprintf(stderr, " [%s]", piece.c_str()); LOG_ERR(" [%s]", piece.c_str());
} }
fprintf(stderr, "\n"); LOG_ERR("\n");
LOG("%s: %s\n", "test-unicode", "FAILED");
common_log_flush(common_log_main());
return 1; return 1;
} }
LOG("%s: %s\n", "test-unicode", "PASSED");
common_log_flush(common_log_main());
return 0; return 0;
} }
+22 -2
View File
@@ -2,6 +2,10 @@
#include "common.h" #include "common.h"
// "log.h" cannot be included here: clip-impl.h (pulled in by test-mtmd-impl.cpp) defines the
// LOG_* macros unconditionally, which conflicts with log.h and is an error with -Werror
int common_log_get_verbosity_thold(void);
#include <chrono> #include <chrono>
#include <exception> #include <exception>
#include <iostream> #include <iostream>
@@ -28,9 +32,16 @@ struct testing {
std::string skip_reason; std::string skip_reason;
static constexpr std::size_t status_column = 80; static constexpr std::size_t status_column = 80;
// LOG_LEVEL_WARN, see common/log.h
static constexpr int log_level_warn = 2;
explicit testing(std::ostream &os = std::cout) : out(os) {} explicit testing(std::ostream &os = std::cout) : out(os) {}
// hide the per-test detail when only warnings and errors are requested
bool show_details() const {
return common_log_get_verbosity_thold() > log_level_warn;
}
std::string indent() const { std::string indent() const {
if (stack.empty()) { if (stack.empty()) {
return ""; return "";
@@ -89,6 +100,10 @@ struct testing {
} }
void print_result(const std::string &label, int new_failures, int new_assertions, const std::string &extra = "", bool was_skipped = false) const { void print_result(const std::string &label, int new_failures, int new_assertions, const std::string &extra = "", bool was_skipped = false) const {
if (new_failures == 0 && !show_details()) {
return; // hide [PASS]/[SKIP], always report failures
}
std::string line = indent() + label; std::string line = indent() + label;
std::string details; std::string details;
@@ -131,7 +146,9 @@ struct testing {
} }
++tests; ++tests;
out << indent() << name << "\n"; if (show_details()) {
out << indent() << name << "\n";
}
int before_failures = failures; int before_failures = failures;
int before_assertions = assertions; int before_assertions = assertions;
@@ -174,7 +191,9 @@ struct testing {
} }
++tests; ++tests;
out << indent() << "[bench] " << name << "\n"; if (show_details()) {
out << indent() << "[bench] " << name << "\n";
}
int before_failures = failures; int before_failures = failures;
int before_assertions = assertions; int before_assertions = assertions;
@@ -263,6 +282,7 @@ struct testing {
out << "failures : " << failures << "\n"; out << "failures : " << failures << "\n";
out << "exceptions : " << exceptions << "\n"; out << "exceptions : " << exceptions << "\n";
out << "skipped : " << skipped << "\n"; out << "skipped : " << skipped << "\n";
out << (failures == 0 ? "PASSED" : "FAILED") << "\n";
return failures == 0 ? 0 : 1; return failures == 0 ? 0 : 1;
} }
}; };
+1 -1
View File
@@ -5,7 +5,7 @@ target_compile_features(${TARGET} PRIVATE cxx_std_17)
if (LLAMA_BUILD_TESTS AND UNIX AND NOT GGML_BACKEND_DL) if (LLAMA_BUILD_TESTS AND UNIX AND NOT GGML_BACKEND_DL)
add_executable(test-rpc-multi-server ${PROJECT_SOURCE_DIR}/tests/test-rpc-multi-server.cpp) add_executable(test-rpc-multi-server ${PROJECT_SOURCE_DIR}/tests/test-rpc-multi-server.cpp)
target_link_libraries(test-rpc-multi-server PRIVATE ggml ggml-rpc) target_link_libraries(test-rpc-multi-server PRIVATE ggml ggml-rpc llama-common)
target_include_directories(test-rpc-multi-server PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src) target_include_directories(test-rpc-multi-server PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src)
add_test( add_test(
NAME test-rpc-multi-server NAME test-rpc-multi-server
+16 -1
View File
@@ -2,6 +2,21 @@ import os
import pytest import pytest
from filelock import FileLock from filelock import FileLock
from utils import * from utils import *
import utils
def pytest_addoption(parser):
parser.addoption(
"--errors-only",
action="store_true",
default=False,
help="Run llama-server with WARN log verbosity, hiding INFO/TRACE/DEBUG logs",
)
@pytest.fixture(scope="session", autouse=True)
def configure_errors_only(request):
utils.errors_only = request.config.getoption("--errors-only")
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
@@ -26,7 +41,7 @@ def stop_server_after_each_test():
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def load_server_presets(configure_worker_port, tmp_path_factory): def load_server_presets(configure_worker_port, configure_errors_only, tmp_path_factory):
# this will be run once per test session, before any tests # this will be run once per test session, before any tests
# serialize model downloads across parallel workers. # serialize model downloads across parallel workers.
+3 -3
View File
@@ -8,12 +8,12 @@ set -eu
WORKERS="${PYTEST_WORKERS:-auto}" WORKERS="${PYTEST_WORKERS:-auto}"
if [ $# -lt 1 ] if [ $# -lt 1 ] || [ "$1" = "--errors-only" ]
then then
if [[ "${SLOW_TESTS:-0}" == 1 ]]; then if [[ "${SLOW_TESTS:-0}" == 1 ]]; then
pytest --durations=30 -v -x -n "${WORKERS}" --dist=worksteal pytest --durations=30 -v -x -n "${WORKERS}" --dist=worksteal "$@"
else else
pytest --durations=30 -v -x -n "${WORKERS}" --dist=worksteal -m "not slow" pytest --durations=30 -v -x -n "${WORKERS}" --dist=worksteal -m "not slow" "$@"
fi fi
else else
pytest --durations=30 -n "${WORKERS}" --dist=worksteal "$@" pytest --durations=30 -n "${WORKERS}" --dist=worksteal "$@"
+5
View File
@@ -34,6 +34,9 @@ DEFAULT_HTTP_TIMEOUT = 60
# per-request timeout, a hung server fails the test instead of stalling the CI for hours # per-request timeout, a hung server fails the test instead of stalling the CI for hours
DEFAULT_REQUEST_TIMEOUT = 600 DEFAULT_REQUEST_TIMEOUT = 600
# set by conftest from --errors-only, makes servers run with WARN log verbosity
errors_only: bool = False
class ServerResponse: class ServerResponse:
headers: dict headers: dict
@@ -295,6 +298,8 @@ class ServerProcess:
if self.gcp_compat: if self.gcp_compat:
env["AIP_MODE"] = "PREDICTION" env["AIP_MODE"] = "PREDICTION"
env["AIP_HTTP_PORT"] = str(self.server_port) env["AIP_HTTP_PORT"] = str(self.server_port)
if errors_only and not self.debug:
server_args.extend(["--log-verbosity", "2"])
args = [str(arg) for arg in [server_path, *server_args]] args = [str(arg) for arg in [server_path, *server_args]]
print(f"tests: starting server with: {' '.join(args)}") print(f"tests: starting server with: {' '.join(args)}")