@fugood/llama.node 1.4.6 → 1.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/binding.ts +8 -0
- package/package.json +15 -15
- package/scripts/llama.cpp.patch +25 -26
- package/src/LlamaContext.cpp +2 -2
- package/src/llama.cpp/common/CMakeLists.txt +2 -0
- package/src/llama.cpp/common/arg.cpp +364 -193
- package/src/llama.cpp/common/arg.h +43 -2
- package/src/llama.cpp/common/chat-parser-xml-toolcall.cpp +36 -18
- package/src/llama.cpp/common/chat-parser-xml-toolcall.h +1 -1
- package/src/llama.cpp/common/chat-parser.cpp +3 -2
- package/src/llama.cpp/common/chat-peg-parser.cpp +16 -2
- package/src/llama.cpp/common/chat.cpp +272 -0
- package/src/llama.cpp/common/common.cpp +130 -67
- package/src/llama.cpp/common/common.h +40 -16
- package/src/llama.cpp/common/console.cpp +680 -47
- package/src/llama.cpp/common/console.h +30 -8
- package/src/llama.cpp/common/download.cpp +69 -25
- package/src/llama.cpp/common/json-schema-to-grammar.cpp +132 -3
- package/src/llama.cpp/common/json-schema-to-grammar.h +20 -0
- package/src/llama.cpp/common/log.cpp +5 -0
- package/src/llama.cpp/common/log.h +1 -0
- package/src/llama.cpp/common/peg-parser.cpp +1 -1
- package/src/llama.cpp/common/preset.cpp +206 -0
- package/src/llama.cpp/common/preset.h +32 -0
- package/src/llama.cpp/common/sampling.cpp +91 -92
- package/src/llama.cpp/common/sampling.h +11 -6
- package/src/llama.cpp/common/speculative.cpp +1 -1
- package/src/llama.cpp/ggml/CMakeLists.txt +5 -0
- package/src/llama.cpp/ggml/include/ggml-alloc.h +9 -0
- package/src/llama.cpp/ggml/include/ggml-backend.h +1 -0
- package/src/llama.cpp/ggml/include/ggml-cpu.h +1 -0
- package/src/llama.cpp/ggml/include/ggml.h +7 -8
- package/src/llama.cpp/ggml/src/CMakeLists.txt +3 -0
- package/src/llama.cpp/ggml/src/ggml-cpu/CMakeLists.txt +3 -0
- package/src/llama.cpp/ggml/src/ggml-cpu/arch/arm/repack.cpp +2 -0
- package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.c +69 -39
- package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.cpp +4 -0
- package/src/llama.cpp/ggml/src/ggml-cpu/repack.cpp +2 -1
- package/src/llama.cpp/include/llama.h +18 -1
- package/src/llama.cpp/src/CMakeLists.txt +2 -1
- package/src/llama.cpp/src/llama-arch.cpp +1890 -2248
- package/src/llama.cpp/src/llama-arch.h +9 -2
- package/src/llama.cpp/src/llama-batch.cpp +12 -2
- package/src/llama.cpp/src/llama-batch.h +4 -2
- package/src/llama.cpp/src/llama-context.cpp +99 -29
- package/src/llama.cpp/src/llama-context.h +9 -3
- package/src/llama.cpp/src/llama-grammar.cpp +233 -33
- package/src/llama.cpp/src/llama-grammar.h +20 -1
- package/src/llama.cpp/src/llama-graph.cpp +85 -17
- package/src/llama.cpp/src/llama-graph.h +17 -4
- package/src/llama.cpp/src/llama-hparams.cpp +6 -0
- package/src/llama.cpp/src/llama-hparams.h +5 -1
- package/src/llama.cpp/src/llama-impl.cpp +4 -0
- package/src/llama.cpp/src/llama-kv-cache.cpp +90 -42
- package/src/llama.cpp/src/llama-kv-cache.h +19 -2
- package/src/llama.cpp/src/llama-memory-hybrid.cpp +1 -1
- package/src/llama.cpp/src/llama-model-loader.cpp +2 -0
- package/src/llama.cpp/src/llama-model-loader.h +2 -0
- package/src/llama.cpp/src/llama-model.cpp +123 -52
- package/src/llama.cpp/src/llama-model.h +1 -0
- package/src/llama.cpp/src/llama-quant.cpp +1 -1
- package/src/llama.cpp/src/llama-vocab.cpp +2 -1
- package/src/llama.cpp/src/llama.cpp +675 -1
- package/src/llama.cpp/src/models/deepseek2.cpp +9 -5
- package/src/llama.cpp/src/models/{gemma3-iswa.cpp → gemma3.cpp} +30 -5
- package/src/llama.cpp/src/models/glm4-moe.cpp +28 -11
- package/src/llama.cpp/src/models/glm4.cpp +27 -4
- package/src/llama.cpp/src/models/models.h +8 -7
- package/src/llama.cpp/src/models/nemotron-h.cpp +35 -6
- package/src/llama.cpp/src/models/qwen2.cpp +12 -3
- package/src/llama.cpp/src/models/qwen3next.cpp +81 -266
|
@@ -2,18 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
#pragma once
|
|
4
4
|
|
|
5
|
+
#include "common.h"
|
|
6
|
+
|
|
5
7
|
#include <string>
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
enum display_type {
|
|
10
|
+
DISPLAY_TYPE_RESET = 0,
|
|
11
|
+
DISPLAY_TYPE_INFO,
|
|
12
|
+
DISPLAY_TYPE_PROMPT,
|
|
13
|
+
DISPLAY_TYPE_REASONING,
|
|
14
|
+
DISPLAY_TYPE_USER_INPUT,
|
|
15
|
+
DISPLAY_TYPE_ERROR
|
|
16
|
+
};
|
|
14
17
|
|
|
18
|
+
namespace console {
|
|
15
19
|
void init(bool use_simple_io, bool use_advanced_display);
|
|
16
20
|
void cleanup();
|
|
17
|
-
void set_display(
|
|
21
|
+
void set_display(display_type display);
|
|
18
22
|
bool readline(std::string & line, bool multiline_input);
|
|
23
|
+
|
|
24
|
+
namespace spinner {
|
|
25
|
+
void start();
|
|
26
|
+
void stop();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// note: the logging API below output directly to stdout
|
|
30
|
+
// it can negatively impact performance if used on inference thread
|
|
31
|
+
// only use in in a dedicated CLI thread
|
|
32
|
+
// for logging in inference thread, use log.h instead
|
|
33
|
+
|
|
34
|
+
LLAMA_COMMON_ATTRIBUTE_FORMAT(1, 2)
|
|
35
|
+
void log(const char * fmt, ...);
|
|
36
|
+
|
|
37
|
+
LLAMA_COMMON_ATTRIBUTE_FORMAT(1, 2)
|
|
38
|
+
void error(const char * fmt, ...);
|
|
39
|
+
|
|
40
|
+
void flush();
|
|
19
41
|
}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
#include <filesystem>
|
|
13
13
|
#include <fstream>
|
|
14
14
|
#include <future>
|
|
15
|
+
#include <map>
|
|
16
|
+
#include <mutex>
|
|
15
17
|
#include <regex>
|
|
16
18
|
#include <string>
|
|
17
19
|
#include <thread>
|
|
@@ -472,36 +474,79 @@ std::pair<long, std::vector<char>> common_remote_get_content(const std::string &
|
|
|
472
474
|
|
|
473
475
|
#elif defined(LLAMA_USE_HTTPLIB)
|
|
474
476
|
|
|
475
|
-
|
|
477
|
+
class ProgressBar {
|
|
478
|
+
static inline std::mutex mutex;
|
|
479
|
+
static inline std::map<const ProgressBar *, int> lines;
|
|
480
|
+
static inline int max_line = 0;
|
|
481
|
+
|
|
482
|
+
static void cleanup(const ProgressBar * line) {
|
|
483
|
+
lines.erase(line);
|
|
484
|
+
if (lines.empty()) {
|
|
485
|
+
max_line = 0;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
static bool is_output_a_tty() {
|
|
476
490
|
#if defined(_WIN32)
|
|
477
|
-
|
|
491
|
+
return _isatty(_fileno(stdout));
|
|
478
492
|
#else
|
|
479
|
-
|
|
493
|
+
return isatty(1);
|
|
480
494
|
#endif
|
|
481
|
-
}
|
|
495
|
+
}
|
|
482
496
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
497
|
+
public:
|
|
498
|
+
ProgressBar() = default;
|
|
499
|
+
|
|
500
|
+
~ProgressBar() {
|
|
501
|
+
std::lock_guard<std::mutex> lock(mutex);
|
|
502
|
+
cleanup(this);
|
|
486
503
|
}
|
|
487
504
|
|
|
488
|
-
|
|
489
|
-
|
|
505
|
+
void update(size_t current, size_t total) {
|
|
506
|
+
if (!is_output_a_tty()) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (!total) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
std::lock_guard<std::mutex> lock(mutex);
|
|
515
|
+
|
|
516
|
+
if (lines.find(this) == lines.end()) {
|
|
517
|
+
lines[this] = max_line++;
|
|
518
|
+
std::cout << "\n";
|
|
519
|
+
}
|
|
520
|
+
int lines_up = max_line - lines[this];
|
|
521
|
+
|
|
522
|
+
size_t width = 50;
|
|
523
|
+
size_t pct = (100 * current) / total;
|
|
524
|
+
size_t pos = (width * current) / total;
|
|
525
|
+
|
|
526
|
+
std::cout << "\033[s";
|
|
527
|
+
|
|
528
|
+
if (lines_up > 0) {
|
|
529
|
+
std::cout << "\033[" << lines_up << "A";
|
|
530
|
+
}
|
|
531
|
+
std::cout << "\033[2K\r["
|
|
532
|
+
<< std::string(pos, '=')
|
|
533
|
+
<< (pos < width ? ">" : "")
|
|
534
|
+
<< std::string(width - pos, ' ')
|
|
535
|
+
<< "] " << std::setw(3) << pct << "% ("
|
|
536
|
+
<< current / (1024 * 1024) << " MB / "
|
|
537
|
+
<< total / (1024 * 1024) << " MB) "
|
|
538
|
+
<< "\033[u";
|
|
539
|
+
|
|
540
|
+
std::cout.flush();
|
|
541
|
+
|
|
542
|
+
if (current == total) {
|
|
543
|
+
cleanup(this);
|
|
544
|
+
}
|
|
490
545
|
}
|
|
491
546
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
std::cout << "["
|
|
497
|
-
<< std::string(pos, '=')
|
|
498
|
-
<< (pos < width ? ">" : "")
|
|
499
|
-
<< std::string(width - pos, ' ')
|
|
500
|
-
<< "] " << std::setw(3) << pct << "% ("
|
|
501
|
-
<< current / (1024 * 1024) << " MB / "
|
|
502
|
-
<< total / (1024 * 1024) << " MB)\r";
|
|
503
|
-
std::cout.flush();
|
|
504
|
-
}
|
|
547
|
+
ProgressBar(const ProgressBar &) = delete;
|
|
548
|
+
ProgressBar & operator=(const ProgressBar &) = delete;
|
|
549
|
+
};
|
|
505
550
|
|
|
506
551
|
static bool common_pull_file(httplib::Client & cli,
|
|
507
552
|
const std::string & resolve_path,
|
|
@@ -523,6 +568,7 @@ static bool common_pull_file(httplib::Client & cli,
|
|
|
523
568
|
const char * func = __func__; // avoid __func__ inside a lambda
|
|
524
569
|
size_t downloaded = existing_size;
|
|
525
570
|
size_t progress_step = 0;
|
|
571
|
+
ProgressBar bar;
|
|
526
572
|
|
|
527
573
|
auto res = cli.Get(resolve_path, headers,
|
|
528
574
|
[&](const httplib::Response &response) {
|
|
@@ -554,7 +600,7 @@ static bool common_pull_file(httplib::Client & cli,
|
|
|
554
600
|
progress_step += len;
|
|
555
601
|
|
|
556
602
|
if (progress_step >= total_size / 1000 || downloaded == total_size) {
|
|
557
|
-
|
|
603
|
+
bar.update(downloaded, total_size);
|
|
558
604
|
progress_step = 0;
|
|
559
605
|
}
|
|
560
606
|
return true;
|
|
@@ -562,8 +608,6 @@ static bool common_pull_file(httplib::Client & cli,
|
|
|
562
608
|
nullptr
|
|
563
609
|
);
|
|
564
610
|
|
|
565
|
-
std::cout << "\n";
|
|
566
|
-
|
|
567
611
|
if (!res) {
|
|
568
612
|
LOG_ERR("%s: error during download. Status: %d\n", __func__, res ? res->status : -1);
|
|
569
613
|
return false;
|
|
@@ -305,8 +305,9 @@ static std::string format_literal(const std::string & literal) {
|
|
|
305
305
|
|
|
306
306
|
std::string gbnf_format_literal(const std::string & literal) { return format_literal(literal); }
|
|
307
307
|
|
|
308
|
-
class
|
|
308
|
+
class common_schema_converter {
|
|
309
309
|
private:
|
|
310
|
+
friend class common_schema_info;
|
|
310
311
|
friend std::string build_grammar(const std::function<void(const common_grammar_builder &)> & cb, const common_grammar_options & options);
|
|
311
312
|
std::function<json(const std::string &)> _fetch_json;
|
|
312
313
|
bool _dotall;
|
|
@@ -729,7 +730,7 @@ private:
|
|
|
729
730
|
}
|
|
730
731
|
|
|
731
732
|
public:
|
|
732
|
-
|
|
733
|
+
common_schema_converter(
|
|
733
734
|
const std::function<json(const std::string &)> & fetch_json,
|
|
734
735
|
bool dotall)
|
|
735
736
|
: _fetch_json(fetch_json), _dotall(dotall)
|
|
@@ -990,6 +991,134 @@ public:
|
|
|
990
991
|
}
|
|
991
992
|
};
|
|
992
993
|
|
|
994
|
+
// common_schema_info implementation (pimpl)
|
|
995
|
+
|
|
996
|
+
common_schema_info::common_schema_info()
|
|
997
|
+
: impl_(std::make_unique<common_schema_converter>(
|
|
998
|
+
[](const std::string &) { return json(); },
|
|
999
|
+
false)) {}
|
|
1000
|
+
|
|
1001
|
+
common_schema_info::~common_schema_info() = default;
|
|
1002
|
+
|
|
1003
|
+
common_schema_info::common_schema_info(common_schema_info &&) noexcept = default;
|
|
1004
|
+
common_schema_info & common_schema_info::operator=(common_schema_info &&) noexcept = default;
|
|
1005
|
+
|
|
1006
|
+
void common_schema_info::resolve_refs(nlohmann::ordered_json & schema) {
|
|
1007
|
+
impl_->resolve_refs(schema, "");
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Determines if a JSON schema can resolve to a string type through any path.
|
|
1011
|
+
// Some models emit raw string values rather than JSON-encoded strings for string parameters.
|
|
1012
|
+
// If any branch of the schema (via oneOf, anyOf, $ref, etc.) permits a string, this returns
|
|
1013
|
+
// true, allowing callers to handle the value as a raw string for simplicity.
|
|
1014
|
+
bool common_schema_info::resolves_to_string(const nlohmann::ordered_json & schema) {
|
|
1015
|
+
std::unordered_set<std::string> visited_refs;
|
|
1016
|
+
|
|
1017
|
+
std::function<bool(const json &)> check = [&](const json & s) -> bool {
|
|
1018
|
+
if (!s.is_object()) {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// Handle $ref
|
|
1023
|
+
if (s.contains("$ref")) {
|
|
1024
|
+
const std::string & ref = s["$ref"];
|
|
1025
|
+
if (visited_refs.find(ref) != visited_refs.end()) {
|
|
1026
|
+
// Circular reference, assume not a string to be safe
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
visited_refs.insert(ref);
|
|
1030
|
+
auto it = impl_->_refs.find(ref);
|
|
1031
|
+
if (it != impl_->_refs.end()) {
|
|
1032
|
+
return check(it->second);
|
|
1033
|
+
}
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// Check type field
|
|
1038
|
+
if (s.contains("type")) {
|
|
1039
|
+
const json & schema_type = s["type"];
|
|
1040
|
+
if (schema_type.is_string()) {
|
|
1041
|
+
if (schema_type == "string") {
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
} else if (schema_type.is_array()) {
|
|
1045
|
+
// Type can be an array like ["string", "null"]
|
|
1046
|
+
for (const auto & t : schema_type) {
|
|
1047
|
+
if (t == "string") {
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Check oneOf/anyOf - if any alternative can be a string
|
|
1055
|
+
if (s.contains("oneOf")) {
|
|
1056
|
+
for (const auto & alt : s["oneOf"]) {
|
|
1057
|
+
if (check(alt)) {
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (s.contains("anyOf")) {
|
|
1063
|
+
for (const auto & alt : s["anyOf"]) {
|
|
1064
|
+
if (check(alt)) {
|
|
1065
|
+
return true;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// Check allOf - all components must be compatible with string type
|
|
1071
|
+
if (s.contains("allOf")) {
|
|
1072
|
+
bool all_string = true;
|
|
1073
|
+
for (const auto & component : s["allOf"]) {
|
|
1074
|
+
if (!check(component)) {
|
|
1075
|
+
all_string = false;
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (all_string) {
|
|
1080
|
+
return true;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Check const - if the constant value is a string
|
|
1085
|
+
if (s.contains("const")) {
|
|
1086
|
+
if (s["const"].is_string()) {
|
|
1087
|
+
return true;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Check enum - if any enum value is a string
|
|
1092
|
+
if (s.contains("enum")) {
|
|
1093
|
+
for (const auto & val : s["enum"]) {
|
|
1094
|
+
if (val.is_string()) {
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// String-specific keywords imply string type
|
|
1101
|
+
if (s.contains("pattern") || s.contains("minLength") || s.contains("maxLength")) {
|
|
1102
|
+
return true;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Check format - many formats imply string
|
|
1106
|
+
if (s.contains("format")) {
|
|
1107
|
+
const std::string & fmt = s["format"];
|
|
1108
|
+
if (fmt == "date" || fmt == "time" || fmt == "date-time" ||
|
|
1109
|
+
fmt == "uri" || fmt == "email" || fmt == "hostname" ||
|
|
1110
|
+
fmt == "ipv4" || fmt == "ipv6" || fmt == "uuid" ||
|
|
1111
|
+
fmt.find("uuid") == 0) {
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
return false;
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
return check(schema);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
993
1122
|
std::string json_schema_to_grammar(const json & schema, bool force_gbnf) {
|
|
994
1123
|
#ifdef LLAMA_USE_LLGUIDANCE
|
|
995
1124
|
if (!force_gbnf) {
|
|
@@ -1006,7 +1135,7 @@ std::string json_schema_to_grammar(const json & schema, bool force_gbnf) {
|
|
|
1006
1135
|
}
|
|
1007
1136
|
|
|
1008
1137
|
std::string build_grammar(const std::function<void(const common_grammar_builder &)> & cb, const common_grammar_options & options) {
|
|
1009
|
-
|
|
1138
|
+
common_schema_converter converter([&](const std::string &) { return json(); }, options.dotall);
|
|
1010
1139
|
common_grammar_builder builder {
|
|
1011
1140
|
/* .add_rule = */ [&](const std::string & name, const std::string & rule) {
|
|
1012
1141
|
return converter._add_rule(name, rule);
|
|
@@ -3,11 +3,31 @@
|
|
|
3
3
|
#include <nlohmann/json_fwd.hpp>
|
|
4
4
|
|
|
5
5
|
#include <functional>
|
|
6
|
+
#include <memory>
|
|
6
7
|
#include <string>
|
|
7
8
|
|
|
8
9
|
std::string json_schema_to_grammar(const nlohmann::ordered_json & schema,
|
|
9
10
|
bool force_gbnf = false);
|
|
10
11
|
|
|
12
|
+
class common_schema_converter;
|
|
13
|
+
|
|
14
|
+
// Probes a JSON schema to extract information about its structure and type constraints.
|
|
15
|
+
class common_schema_info {
|
|
16
|
+
std::unique_ptr<common_schema_converter> impl_;
|
|
17
|
+
|
|
18
|
+
public:
|
|
19
|
+
common_schema_info();
|
|
20
|
+
~common_schema_info();
|
|
21
|
+
|
|
22
|
+
common_schema_info(const common_schema_info &) = delete;
|
|
23
|
+
common_schema_info & operator=(const common_schema_info &) = delete;
|
|
24
|
+
common_schema_info(common_schema_info &&) noexcept;
|
|
25
|
+
common_schema_info & operator=(common_schema_info &&) noexcept;
|
|
26
|
+
|
|
27
|
+
void resolve_refs(nlohmann::ordered_json & schema);
|
|
28
|
+
bool resolves_to_string(const nlohmann::ordered_json & schema);
|
|
29
|
+
};
|
|
30
|
+
|
|
11
31
|
struct common_grammar_builder {
|
|
12
32
|
std::function<std::string(const std::string &, const std::string &)> add_rule;
|
|
13
33
|
std::function<std::string(const std::string &, const nlohmann::ordered_json &)> add_schema;
|
|
@@ -420,6 +420,11 @@ void common_log_set_timestamps(struct common_log * log, bool timestamps) {
|
|
|
420
420
|
log->set_timestamps(timestamps);
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
void common_log_flush(struct common_log * log) {
|
|
424
|
+
log->pause();
|
|
425
|
+
log->resume();
|
|
426
|
+
}
|
|
427
|
+
|
|
423
428
|
static int common_get_verbosity(enum ggml_log_level level) {
|
|
424
429
|
switch (level) {
|
|
425
430
|
case GGML_LOG_LEVEL_DEBUG: return LOG_LEVEL_DEBUG;
|
|
@@ -84,6 +84,7 @@ void common_log_set_file (struct common_log * log, const char * file); // n
|
|
|
84
84
|
void common_log_set_colors (struct common_log * log, log_colors colors); // not thread-safe
|
|
85
85
|
void common_log_set_prefix (struct common_log * log, bool prefix); // whether to output prefix to each log
|
|
86
86
|
void common_log_set_timestamps(struct common_log * log, bool timestamps); // whether to output timestamps in the prefix
|
|
87
|
+
void common_log_flush (struct common_log * log); // flush all pending log messages
|
|
87
88
|
|
|
88
89
|
// helper macros for logging
|
|
89
90
|
// use these to avoid computing log arguments if the verbosity of the log is higher than the threshold
|
|
@@ -425,7 +425,7 @@ struct parser_executor {
|
|
|
425
425
|
|
|
426
426
|
if (result.need_more_input()) {
|
|
427
427
|
// Propagate - need to know what child would match before negating
|
|
428
|
-
return
|
|
428
|
+
return common_peg_parse_result(COMMON_PEG_PARSE_RESULT_NEED_MORE_INPUT, start_pos);
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
// Child failed, so negation succeeds
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#include "arg.h"
|
|
2
|
+
#include "preset.h"
|
|
3
|
+
#include "peg-parser.h"
|
|
4
|
+
#include "log.h"
|
|
5
|
+
|
|
6
|
+
#include <fstream>
|
|
7
|
+
#include <sstream>
|
|
8
|
+
#include <filesystem>
|
|
9
|
+
|
|
10
|
+
static std::string rm_leading_dashes(const std::string & str) {
|
|
11
|
+
size_t pos = 0;
|
|
12
|
+
while (pos < str.size() && str[pos] == '-') {
|
|
13
|
+
++pos;
|
|
14
|
+
}
|
|
15
|
+
return str.substr(pos);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
std::vector<std::string> common_preset::to_args() const {
|
|
19
|
+
std::vector<std::string> args;
|
|
20
|
+
|
|
21
|
+
for (const auto & [opt, value] : options) {
|
|
22
|
+
args.push_back(opt.args.back()); // use the last arg as the main arg
|
|
23
|
+
if (opt.value_hint == nullptr && opt.value_hint_2 == nullptr) {
|
|
24
|
+
// flag option, no value
|
|
25
|
+
if (common_arg_utils::is_falsey(value)) {
|
|
26
|
+
// use negative arg if available
|
|
27
|
+
if (!opt.args_neg.empty()) {
|
|
28
|
+
args.back() = opt.args_neg.back();
|
|
29
|
+
} else {
|
|
30
|
+
// otherwise, skip the flag
|
|
31
|
+
// TODO: maybe throw an error instead?
|
|
32
|
+
args.pop_back();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (opt.value_hint != nullptr) {
|
|
37
|
+
// single value
|
|
38
|
+
args.push_back(value);
|
|
39
|
+
}
|
|
40
|
+
if (opt.value_hint != nullptr && opt.value_hint_2 != nullptr) {
|
|
41
|
+
throw std::runtime_error(string_format(
|
|
42
|
+
"common_preset::to_args(): option '%s' has two values, which is not supported yet",
|
|
43
|
+
opt.args.back()
|
|
44
|
+
));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return args;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
std::string common_preset::to_ini() const {
|
|
52
|
+
std::ostringstream ss;
|
|
53
|
+
|
|
54
|
+
ss << "[" << name << "]\n";
|
|
55
|
+
for (const auto & [opt, value] : options) {
|
|
56
|
+
auto espaced_value = value;
|
|
57
|
+
string_replace_all(espaced_value, "\n", "\\\n");
|
|
58
|
+
ss << rm_leading_dashes(opt.args.back()) << " = ";
|
|
59
|
+
ss << espaced_value << "\n";
|
|
60
|
+
}
|
|
61
|
+
ss << "\n";
|
|
62
|
+
|
|
63
|
+
return ss.str();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static std::map<std::string, std::map<std::string, std::string>> parse_ini_from_file(const std::string & path) {
|
|
67
|
+
std::map<std::string, std::map<std::string, std::string>> parsed;
|
|
68
|
+
|
|
69
|
+
if (!std::filesystem::exists(path)) {
|
|
70
|
+
throw std::runtime_error("preset file does not exist: " + path);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
std::ifstream file(path);
|
|
74
|
+
if (!file.good()) {
|
|
75
|
+
throw std::runtime_error("failed to open server preset file: " + path);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
79
|
+
|
|
80
|
+
static const auto parser = build_peg_parser([](auto & p) {
|
|
81
|
+
// newline ::= "\r\n" / "\n" / "\r"
|
|
82
|
+
auto newline = p.rule("newline", p.literal("\r\n") | p.literal("\n") | p.literal("\r"));
|
|
83
|
+
|
|
84
|
+
// ws ::= [ \t]*
|
|
85
|
+
auto ws = p.rule("ws", p.chars("[ \t]", 0, -1));
|
|
86
|
+
|
|
87
|
+
// comment ::= [;#] (!newline .)*
|
|
88
|
+
auto comment = p.rule("comment", p.chars("[;#]", 1, 1) + p.zero_or_more(p.negate(newline) + p.any()));
|
|
89
|
+
|
|
90
|
+
// eol ::= ws comment? (newline / EOF)
|
|
91
|
+
auto eol = p.rule("eol", ws + p.optional(comment) + (newline | p.end()));
|
|
92
|
+
|
|
93
|
+
// ident ::= [a-zA-Z_] [a-zA-Z0-9_.-]*
|
|
94
|
+
auto ident = p.rule("ident", p.chars("[a-zA-Z_]", 1, 1) + p.chars("[a-zA-Z0-9_.-]", 0, -1));
|
|
95
|
+
|
|
96
|
+
// value ::= (!eol-start .)*
|
|
97
|
+
auto eol_start = p.rule("eol-start", ws + (p.chars("[;#]", 1, 1) | newline | p.end()));
|
|
98
|
+
auto value = p.rule("value", p.zero_or_more(p.negate(eol_start) + p.any()));
|
|
99
|
+
|
|
100
|
+
// header-line ::= "[" ws ident ws "]" eol
|
|
101
|
+
auto header_line = p.rule("header-line", "[" + ws + p.tag("section-name", p.chars("[^]]")) + ws + "]" + eol);
|
|
102
|
+
|
|
103
|
+
// kv-line ::= ident ws "=" ws value eol
|
|
104
|
+
auto kv_line = p.rule("kv-line", p.tag("key", ident) + ws + "=" + ws + p.tag("value", value) + eol);
|
|
105
|
+
|
|
106
|
+
// comment-line ::= ws comment (newline / EOF)
|
|
107
|
+
auto comment_line = p.rule("comment-line", ws + comment + (newline | p.end()));
|
|
108
|
+
|
|
109
|
+
// blank-line ::= ws (newline / EOF)
|
|
110
|
+
auto blank_line = p.rule("blank-line", ws + (newline | p.end()));
|
|
111
|
+
|
|
112
|
+
// line ::= header-line / kv-line / comment-line / blank-line
|
|
113
|
+
auto line = p.rule("line", header_line | kv_line | comment_line | blank_line);
|
|
114
|
+
|
|
115
|
+
// ini ::= line* EOF
|
|
116
|
+
auto ini = p.rule("ini", p.zero_or_more(line) + p.end());
|
|
117
|
+
|
|
118
|
+
return ini;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
common_peg_parse_context ctx(contents);
|
|
122
|
+
const auto result = parser.parse(ctx);
|
|
123
|
+
if (!result.success()) {
|
|
124
|
+
throw std::runtime_error("failed to parse server config file: " + path);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
std::string current_section = COMMON_PRESET_DEFAULT_NAME;
|
|
128
|
+
std::string current_key;
|
|
129
|
+
|
|
130
|
+
ctx.ast.visit(result, [&](const auto & node) {
|
|
131
|
+
if (node.tag == "section-name") {
|
|
132
|
+
const std::string section = std::string(node.text);
|
|
133
|
+
current_section = section;
|
|
134
|
+
parsed[current_section] = {};
|
|
135
|
+
} else if (node.tag == "key") {
|
|
136
|
+
const std::string key = std::string(node.text);
|
|
137
|
+
current_key = key;
|
|
138
|
+
} else if (node.tag == "value" && !current_key.empty() && !current_section.empty()) {
|
|
139
|
+
parsed[current_section][current_key] = std::string(node.text);
|
|
140
|
+
current_key.clear();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static std::map<std::string, common_arg> get_map_key_opt(common_params_context & ctx_params) {
|
|
148
|
+
std::map<std::string, common_arg> mapping;
|
|
149
|
+
for (const auto & opt : ctx_params.options) {
|
|
150
|
+
for (const auto & env : opt.get_env()) {
|
|
151
|
+
mapping[env] = opt;
|
|
152
|
+
}
|
|
153
|
+
for (const auto & arg : opt.get_args()) {
|
|
154
|
+
mapping[rm_leading_dashes(arg)] = opt;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return mapping;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static bool is_bool_arg(const common_arg & arg) {
|
|
161
|
+
return !arg.args_neg.empty();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static std::string parse_bool_arg(const common_arg & arg, const std::string & key, const std::string & value) {
|
|
165
|
+
// if this is a negated arg, we need to reverse the value
|
|
166
|
+
for (const auto & neg_arg : arg.args_neg) {
|
|
167
|
+
if (rm_leading_dashes(neg_arg) == key) {
|
|
168
|
+
return common_arg_utils::is_truthy(value) ? "false" : "true";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// otherwise, not negated
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
common_presets common_presets_load(const std::string & path, common_params_context & ctx_params) {
|
|
176
|
+
common_presets out;
|
|
177
|
+
auto key_to_opt = get_map_key_opt(ctx_params);
|
|
178
|
+
auto ini_data = parse_ini_from_file(path);
|
|
179
|
+
|
|
180
|
+
for (auto section : ini_data) {
|
|
181
|
+
common_preset preset;
|
|
182
|
+
if (section.first.empty()) {
|
|
183
|
+
preset.name = COMMON_PRESET_DEFAULT_NAME;
|
|
184
|
+
} else {
|
|
185
|
+
preset.name = section.first;
|
|
186
|
+
}
|
|
187
|
+
LOG_DBG("loading preset: %s\n", preset.name.c_str());
|
|
188
|
+
for (const auto & [key, value] : section.second) {
|
|
189
|
+
LOG_DBG("option: %s = %s\n", key.c_str(), value.c_str());
|
|
190
|
+
if (key_to_opt.find(key) != key_to_opt.end()) {
|
|
191
|
+
auto & opt = key_to_opt[key];
|
|
192
|
+
if (is_bool_arg(opt)) {
|
|
193
|
+
preset.options[opt] = parse_bool_arg(opt, key, value);
|
|
194
|
+
} else {
|
|
195
|
+
preset.options[opt] = value;
|
|
196
|
+
}
|
|
197
|
+
LOG_DBG("accepted option: %s = %s\n", key.c_str(), preset.options[opt].c_str());
|
|
198
|
+
} else {
|
|
199
|
+
// TODO: maybe warn about unknown key?
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
out[preset.name] = preset;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "common.h"
|
|
4
|
+
#include "arg.h"
|
|
5
|
+
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <vector>
|
|
8
|
+
#include <map>
|
|
9
|
+
|
|
10
|
+
//
|
|
11
|
+
// INI preset parser and writer
|
|
12
|
+
//
|
|
13
|
+
|
|
14
|
+
constexpr const char * COMMON_PRESET_DEFAULT_NAME = "default";
|
|
15
|
+
|
|
16
|
+
struct common_preset {
|
|
17
|
+
std::string name;
|
|
18
|
+
// TODO: support repeated args in the future
|
|
19
|
+
std::map<common_arg, std::string> options;
|
|
20
|
+
|
|
21
|
+
// convert preset to CLI argument list
|
|
22
|
+
std::vector<std::string> to_args() const;
|
|
23
|
+
|
|
24
|
+
// convert preset to INI format string
|
|
25
|
+
std::string to_ini() const;
|
|
26
|
+
|
|
27
|
+
// TODO: maybe implement to_env() if needed
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// interface for multiple presets in one file
|
|
31
|
+
using common_presets = std::map<std::string, common_preset>;
|
|
32
|
+
common_presets common_presets_load(const std::string & path, common_params_context & ctx_params);
|