mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-10-25 22:17:37 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d854d98bae | ||
|
|
48c9f08b7b | ||
|
|
6050b467c0 | ||
|
|
2280f0fd16 | ||
|
|
1a0b56cec7 | ||
|
|
63412d8737 | ||
|
|
5c965fd8ca |
@@ -460,10 +460,10 @@ function gg_run_qwen3_0_6b {
|
||||
# https://github.com/ggml-org/llama.cpp/actions/runs/39639985732
|
||||
if [ -z ${GG_BUILD_WEBGPU} ]; then
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa off --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa off ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa on ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
fi
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 10 -c 1024 -fa on --no-op-offload) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
(time ./bin/test-llama-context --model ${model_q4_0} -ngl 99 -c 1024 -fa on ) 2>&1 | tee -a $OUT/${ci}-test-llama-context.log
|
||||
|
||||
check_ppl "f16" "$(cat $OUT/${ci}-tg-f16.log | grep "^\[1\]")"
|
||||
if [ -z ${GG_BUILD_NO_BF16} ]; then
|
||||
|
||||
+45
-39
@@ -742,50 +742,56 @@ static inline bool glob_class_match(const char c, const char * pattern, const ch
|
||||
}
|
||||
|
||||
// simple glob: * matches non-/ chars, ** matches anything including /, [] matches character class
|
||||
static inline bool glob_match(const char * pattern, const char * str) {
|
||||
if (*pattern == '\0') {
|
||||
return *str == '\0';
|
||||
}
|
||||
if (pattern[0] == '*' && pattern[1] == '*') {
|
||||
const char * p = pattern + 2;
|
||||
if (glob_match(p, str)) return true;
|
||||
if (*str != '\0') return glob_match(pattern, str + 1);
|
||||
// only wildcards recurse, too many wildcards: no match
|
||||
static bool glob_match(const char * pattern, const char * str, int depth = 0) {
|
||||
if (depth >= COMMON_RECURSIVE_LIMIT) {
|
||||
return false;
|
||||
}
|
||||
if (*pattern == '*') {
|
||||
const char * p = pattern + 1;
|
||||
for (; *str != '\0' && *str != '/'; str++) {
|
||||
if (glob_match(p, str)) return true;
|
||||
}
|
||||
return glob_match(p, str);
|
||||
}
|
||||
if (*pattern == '?' && *str != '\0' && *str != '/') {
|
||||
return glob_match(pattern + 1, str + 1);
|
||||
}
|
||||
if (*pattern == '[') {
|
||||
const char * class_end = pattern + 1;
|
||||
// If first character after '[' is ']' or '-', treat it as literal
|
||||
if (*class_end == ']' || *class_end == '-') {
|
||||
class_end++;
|
||||
}
|
||||
while (*class_end != '\0' && *class_end != ']') {
|
||||
class_end++;
|
||||
}
|
||||
if (*class_end == ']') {
|
||||
if (*str == '\0') return false;
|
||||
bool matched = glob_class_match(*str, pattern + 1, class_end);
|
||||
return matched && glob_match(class_end + 1, str + 1);
|
||||
} else {
|
||||
if (*str == '[') {
|
||||
return glob_match(pattern + 1, str + 1);
|
||||
while (*pattern != '\0') {
|
||||
if (pattern[0] == '*' && pattern[1] == '*') {
|
||||
const char * p = pattern + 2;
|
||||
for (;; str++) {
|
||||
if (glob_match(p, str, depth + 1)) return true;
|
||||
if (*str == '\0') return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (*pattern == '*') {
|
||||
const char * p = pattern + 1;
|
||||
for (; *str != '\0' && *str != '/'; str++) {
|
||||
if (glob_match(p, str, depth + 1)) return true;
|
||||
}
|
||||
return glob_match(p, str, depth + 1);
|
||||
}
|
||||
if (*pattern == '?' && *str != '\0' && *str != '/') {
|
||||
pattern++;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*pattern == '[') {
|
||||
const char * class_end = pattern + 1;
|
||||
// If first character after '[' is ']' or '-', treat it as literal
|
||||
if (*class_end == ']' || *class_end == '-') {
|
||||
class_end++;
|
||||
}
|
||||
while (*class_end != '\0' && *class_end != ']') {
|
||||
class_end++;
|
||||
}
|
||||
if (*class_end == ']') {
|
||||
if (*str == '\0' || !glob_class_match(*str, pattern + 1, class_end)) return false;
|
||||
pattern = class_end + 1;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*str != '[') return false;
|
||||
pattern++;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*pattern != *str) return false;
|
||||
pattern++;
|
||||
str++;
|
||||
}
|
||||
if (*pattern == *str) {
|
||||
return glob_match(pattern + 1, str + 1);
|
||||
}
|
||||
return false;
|
||||
return *str == '\0';
|
||||
}
|
||||
|
||||
bool glob_match(const std::string & pattern, const std::string & str) {
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#define DIRECTORY_SEPARATOR '/'
|
||||
#endif // _WIN32
|
||||
|
||||
#define COMMON_RECURSIVE_LIMIT 128
|
||||
|
||||
#define COM_DBG(fmt, ...) LOG_DBG("cmn %12.*s: " fmt, 12, __func__, __VA_ARGS__)
|
||||
#define COM_TRC(fmt, ...) LOG_TRC("cmn %12.*s: " fmt, 12, __func__, __VA_ARGS__)
|
||||
#define COM_INF(fmt, ...) LOG_INF("cmn %12.*s: " fmt, 12, __func__, __VA_ARGS__)
|
||||
|
||||
+21
-3
@@ -21,6 +21,17 @@ static bool is_type(const statement_ptr & ptr) {
|
||||
class parser {
|
||||
const std::vector<token> & tokens;
|
||||
size_t current = 0;
|
||||
int depth = 0;
|
||||
|
||||
struct depth_guard {
|
||||
parser & p;
|
||||
depth_guard(parser & p) : p(p) {
|
||||
if (++p.depth > JINJA_RECURSIVE_LIMIT) {
|
||||
throw parser_exception("Parser Error: template is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep", p.source, p.peek().pos);
|
||||
}
|
||||
}
|
||||
~depth_guard() { p.depth--; }
|
||||
};
|
||||
|
||||
std::string source; // for error reporting
|
||||
|
||||
@@ -94,6 +105,7 @@ private:
|
||||
}
|
||||
|
||||
statement_ptr parse_any() {
|
||||
depth_guard guard(*this);
|
||||
size_t start_pos = current;
|
||||
switch (peek().t) {
|
||||
case token::comment:
|
||||
@@ -254,6 +266,7 @@ private:
|
||||
size_t pos0 = current;
|
||||
++current; // consume {%
|
||||
++current; // consume 'elif'
|
||||
depth_guard guard(*this);
|
||||
alternate.push_back(parse_if_statement(pos0)); // nested If
|
||||
} else if (is_statement({"else"})) {
|
||||
++current; // consume {%
|
||||
@@ -330,6 +343,7 @@ private:
|
||||
}
|
||||
|
||||
statement_ptr parse_if_expression() {
|
||||
depth_guard guard(*this);
|
||||
auto a = parse_logical_or_expression();
|
||||
if (is_identifier("if")) {
|
||||
// Ternary expression
|
||||
@@ -373,6 +387,7 @@ private:
|
||||
statement_ptr parse_logical_negation_expression() {
|
||||
// Try parse unary operators
|
||||
if (is_identifier("not")) {
|
||||
depth_guard guard(*this);
|
||||
size_t start_pos = current;
|
||||
auto op = next();
|
||||
return mk_stmt<unary_expression>(start_pos, op, parse_logical_negation_expression());
|
||||
@@ -458,6 +473,7 @@ private:
|
||||
|
||||
statement_ptr parse_unary_expression() {
|
||||
if (is(token::unary_operator)) {
|
||||
depth_guard guard(*this);
|
||||
size_t start_pos = current;
|
||||
auto op = next();
|
||||
return mk_stmt<unary_expression>(start_pos, op, parse_unary_expression());
|
||||
@@ -477,9 +493,11 @@ private:
|
||||
size_t start_pos = current;
|
||||
auto expr = mk_stmt<call_expression>(start_pos, std::move(callee), parse_args());
|
||||
auto member = parse_member_expression(std::move(expr)); // foo.x().y
|
||||
return is(token::open_paren)
|
||||
? parse_call_expression(std::move(member)) // foo.x()()
|
||||
: std::move(member);
|
||||
if (!is(token::open_paren)) {
|
||||
return member;
|
||||
}
|
||||
depth_guard guard(*this);
|
||||
return parse_call_expression(std::move(member)); // foo.x()()
|
||||
}
|
||||
|
||||
statements parse_args() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "value.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@@ -52,32 +53,39 @@ static void ensure_key_type_allowed(const value & val) {
|
||||
|
||||
// execute with error handling
|
||||
value statement::execute(context & ctx) const {
|
||||
std::exception_ptr pending;
|
||||
std::string message;
|
||||
try {
|
||||
return execute_impl(ctx);
|
||||
} catch (const continue_statement::signal & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const break_statement::signal & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const rethrown_exception & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const not_implemented_exception & /* ex */) {
|
||||
throw;
|
||||
pending = std::current_exception();
|
||||
} catch (const std::exception & e) {
|
||||
const std::string & source = *ctx.src;
|
||||
if (source.empty()) {
|
||||
std::ostringstream oss;
|
||||
oss << "\nError executing " << type() << " at position " << pos << ": " << e.what();
|
||||
throw rethrown_exception(oss.str());
|
||||
message = oss.str();
|
||||
} else {
|
||||
std::ostringstream oss;
|
||||
oss << "\n------------\n";
|
||||
oss << "While executing " << type() << " at " << get_line_col(source, pos) << " in source:\n";
|
||||
oss << peak_source(source, pos) << "\n";
|
||||
oss << "Error: " << e.what();
|
||||
// throw as another exception to avoid repeated formatting
|
||||
throw rethrown_exception(oss.str());
|
||||
message = oss.str();
|
||||
}
|
||||
}
|
||||
// throw outside the catch: MSVC keeps the unwound frames on the stack while a catch block runs
|
||||
if (pending) {
|
||||
std::rethrow_exception(pending);
|
||||
}
|
||||
// throw as another exception to avoid repeated formatting
|
||||
throw rethrown_exception(message);
|
||||
}
|
||||
|
||||
value identifier::execute_impl(context & ctx) const {
|
||||
@@ -747,7 +755,11 @@ value macro_statement::execute_impl(context & ctx) const {
|
||||
std::string name = cast_stmt<identifier>(this->name)->val;
|
||||
|
||||
const func_handler func = [this, name](const func_args & args) -> value {
|
||||
if (args.ctx.macro_depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("macro calls are nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
context macro_ctx(args.ctx); // new scope for macro execution
|
||||
macro_ctx.macro_depth = args.ctx.macro_depth + 1;
|
||||
|
||||
bind_parameters(name, this->args, args, macro_ctx);
|
||||
|
||||
@@ -778,7 +790,11 @@ value call_statement::execute_impl(context & ctx) const {
|
||||
context caller_ctx(ctx); // new scope for caller execution
|
||||
|
||||
const func_handler func = [this, caller_ctx = std::move(caller_ctx)](const func_args & args) -> value {
|
||||
if (args.ctx.macro_depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("macro calls are nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
context block_ctx(caller_ctx); // new scope for block execution
|
||||
block_ctx.macro_depth = args.ctx.macro_depth + 1;
|
||||
|
||||
bind_parameters("caller", this->caller_args, args, block_ctx);
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ struct context {
|
||||
|
||||
bool is_get_stats = false; // whether to collect stats
|
||||
|
||||
int macro_depth = 0;
|
||||
|
||||
visitor_fn visitor;
|
||||
|
||||
// src is optional, used for error reporting
|
||||
@@ -78,6 +80,7 @@ struct context {
|
||||
src(parent.src),
|
||||
current_time(parent.current_time),
|
||||
is_get_stats(parent.is_get_stats),
|
||||
macro_depth(parent.macro_depth),
|
||||
// inherit variables (for example, when entering a new scope)
|
||||
env(mk_val<value_object>(*parent.env)) {}
|
||||
|
||||
@@ -710,7 +713,7 @@ struct rethrown_exception : public std::exception {
|
||||
|
||||
//////////////////////
|
||||
|
||||
static void gather_string_parts_recursive(const value & val, value_string & parts) {
|
||||
static void gather_string_parts_recursive(const value & val, value_string & parts, int depth = 0) {
|
||||
// TODO: probably allow print value_none as "None" string? currently this breaks some templates
|
||||
if (is_val<value_string>(val)) {
|
||||
const auto & str_val = cast_val<value_string>(val)->val_str;
|
||||
@@ -718,10 +721,12 @@ static void gather_string_parts_recursive(const value & val, value_string & part
|
||||
} else if (is_val<value_int>(val) || is_val<value_float>(val) || is_val<value_bool>(val)) {
|
||||
std::string str_val = val->as_string().str();
|
||||
parts->val_str.append(str_val);
|
||||
} else if (is_val<value_array>(val) && depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("value is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
} else if (is_val<value_array>(val)) {
|
||||
auto items = cast_val<value_array>(val)->as_array();
|
||||
for (const auto & item : items) {
|
||||
gather_string_parts_recursive(item, parts);
|
||||
gather_string_parts_recursive(item, parts, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#define JINJA_RECURSIVE_LIMIT 128
|
||||
|
||||
namespace jinja {
|
||||
|
||||
static void string_replace_all(std::string & s, const std::string & search, const std::string & replace) {
|
||||
|
||||
+11
-8
@@ -1441,8 +1441,6 @@ const func_builtins & value_undefined_t::get_builtins() const {
|
||||
|
||||
|
||||
// the runtime walks values with recursion, a deeper input overflows the stack
|
||||
static constexpr int FROM_JSON_MAX_DEPTH = 128;
|
||||
|
||||
static value from_json(const common_json & j, bool mark_input, int depth = 0) {
|
||||
if (j.is_null()) {
|
||||
return mk_val<value_none>();
|
||||
@@ -1458,8 +1456,8 @@ static value from_json(const common_json & j, bool mark_input, int depth = 0) {
|
||||
str->mark_input();
|
||||
}
|
||||
return str;
|
||||
} else if ((j.is_array() || j.is_object()) && depth >= FROM_JSON_MAX_DEPTH) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(FROM_JSON_MAX_DEPTH) + " levels deep");
|
||||
} else if ((j.is_array() || j.is_object()) && depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
} else if (j.is_array()) {
|
||||
auto arr = mk_val<value_array>();
|
||||
for (const auto & item : j) {
|
||||
@@ -1664,6 +1662,8 @@ static void value_to_json_internal(std::ostringstream & oss, const value & val,
|
||||
}
|
||||
}
|
||||
oss << "\"";
|
||||
} else if ((is_val<value_array>(val) || is_val<value_object>(val)) && curr_lvl >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("value is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
} else if (is_val<value_array>(val)) {
|
||||
const auto & arr = val->as_array();
|
||||
oss << "[";
|
||||
@@ -1729,17 +1729,20 @@ std::string value_to_string_repr(const value & val) {
|
||||
}
|
||||
|
||||
// stats utility
|
||||
void value_t::stats_t::mark_used(value & val, bool deep) {
|
||||
void value_t::stats_t::mark_used(value & val, bool deep, int depth) {
|
||||
val->stats.used = true;
|
||||
if (deep && (is_val<value_array>(val) || is_val<value_object>(val)) && depth >= JINJA_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("value is nested more than " + std::to_string(JINJA_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (deep) {
|
||||
if (is_val<value_array>(val)) {
|
||||
for (auto & item : val->val_arr) {
|
||||
mark_used(item, deep);
|
||||
mark_used(item, deep, depth + 1);
|
||||
}
|
||||
} else if (is_val<value_object>(val)) {
|
||||
for (auto & pair : val->val_obj) {
|
||||
mark_used(pair.first, deep);
|
||||
mark_used(pair.second, deep);
|
||||
mark_used(pair.first, deep, depth + 1);
|
||||
mark_used(pair.second, deep, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ struct value_t {
|
||||
// ops can be builtin calls or operators: "array_access", "object_access"
|
||||
std::set<std::string> ops;
|
||||
// utility to recursively mark value and its children as used
|
||||
static void mark_used(value & val, bool deep = false);
|
||||
static void mark_used(value & val, bool deep = false, int depth = 0);
|
||||
} stats;
|
||||
|
||||
value_t() = default;
|
||||
|
||||
@@ -272,15 +272,12 @@ static bool is_reserved_name(const std::string & name) {
|
||||
return RESERVED_NAMES.find(name) != RESERVED_NAMES.end();
|
||||
}
|
||||
|
||||
static std::regex INVALID_RULE_CHARS_RE("[^a-zA-Z0-9-]+");
|
||||
static std::regex GRAMMAR_LITERAL_ESCAPE_RE("[\r\n\"\\\\]");
|
||||
static std::regex GRAMMAR_RANGE_LITERAL_ESCAPE_RE("[\r\n\"\\]\\-\\\\]");
|
||||
static std::unordered_map<char, std::string> GRAMMAR_LITERAL_ESCAPES = {
|
||||
{'\r', "\\r"}, {'\n', "\\n"}, {'"', "\\\""}, {'-', "\\-"}, {']', "\\]"}, {'\\', "\\\\"}
|
||||
};
|
||||
|
||||
static const int MAX_PATTERN_DEPTH = 100;
|
||||
|
||||
static std::unordered_set<char> NON_LITERAL_SET = {'|', '.', '(', ')', '[', ']', '{', '}', '*', '+', '?', '^', '$'};
|
||||
static std::unordered_set<char> ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS = {'^', '$', '.', '[', ']', '(', ')', '|', '{', '}', '*', '+', '?'};
|
||||
|
||||
@@ -312,6 +309,22 @@ static std::string format_literal(const std::string & literal) {
|
||||
|
||||
std::string gbnf_format_literal(const std::string & literal) { return format_literal(literal); }
|
||||
|
||||
// same as std::regex_replace with [^a-zA-Z0-9-]+ -> "-", no std::regex: libstdc++ recurses per char
|
||||
std::string gbnf_format_rule_name(const std::string & name) {
|
||||
std::string out;
|
||||
bool in_invalid = false;
|
||||
for (char c : name) {
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-') {
|
||||
out += c;
|
||||
in_invalid = false;
|
||||
} else if (!in_invalid) {
|
||||
out += '-';
|
||||
in_invalid = true;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static size_t gbnf_escape_length(const std::string & pattern, size_t pos) {
|
||||
if (pos + 1 >= pattern.length() || pattern[pos] != '\\') {
|
||||
return 0;
|
||||
@@ -347,6 +360,17 @@ private:
|
||||
std::unordered_set<std::string> _refs_being_resolved;
|
||||
std::vector<std::string> _errors;
|
||||
std::vector<std::string> _warnings;
|
||||
int _depth = 0;
|
||||
|
||||
struct depth_guard {
|
||||
int & depth;
|
||||
depth_guard(int & depth) : depth(depth) {
|
||||
if (++depth > COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
}
|
||||
~depth_guard() { depth--; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static const T & as(const common_chat_schema & node) {
|
||||
@@ -354,7 +378,7 @@ private:
|
||||
}
|
||||
|
||||
std::string _add_rule(const std::string & name, const std::string & rule) {
|
||||
std::string esc_name = regex_replace(name, INVALID_RULE_CHARS_RE, "-");
|
||||
std::string esc_name = gbnf_format_rule_name(name);
|
||||
if (_rules.find(esc_name) == _rules.end() || _rules[esc_name] == rule) {
|
||||
_rules[esc_name] = rule;
|
||||
return esc_name;
|
||||
@@ -482,7 +506,7 @@ private:
|
||||
}
|
||||
}
|
||||
paren_depth++;
|
||||
if (paren_depth > MAX_PATTERN_DEPTH) {
|
||||
if (paren_depth > COMMON_RECURSIVE_LIMIT) {
|
||||
throw unsupported_pattern("pattern nesting too deep");
|
||||
}
|
||||
seq.emplace_back("(" + to_rule(transform()) + ")", false);
|
||||
@@ -644,7 +668,11 @@ private:
|
||||
std::string char_rule = _add_primitive("char", PRIMITIVE_RULES.at("char"));
|
||||
std::ostringstream out;
|
||||
out << "[\"] ( ";
|
||||
std::function<void(size_t)> visit = [&](size_t idx) {
|
||||
// one level per char of the longest name
|
||||
std::function<void(size_t, int)> visit = [&](size_t idx, int depth) {
|
||||
if (depth >= COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema property name is longer than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " chars");
|
||||
}
|
||||
const auto & node = trie.nodes[idx];
|
||||
std::string rejects;
|
||||
auto first = true;
|
||||
@@ -659,7 +687,7 @@ private:
|
||||
out << "[" << c << "]";
|
||||
if (!trie.nodes[child].children.empty()) {
|
||||
out << " (";
|
||||
visit(child);
|
||||
visit(child, depth + 1);
|
||||
out << ")";
|
||||
} else {
|
||||
out << " " << char_rule << "+";
|
||||
@@ -669,7 +697,7 @@ private:
|
||||
out << " | [^\"" << rejects << "] " << char_rule << "*";
|
||||
}
|
||||
};
|
||||
visit(0);
|
||||
visit(0, 0);
|
||||
|
||||
out << " )";
|
||||
if (trie.nodes[0].pattern < 0) {
|
||||
@@ -682,8 +710,7 @@ private:
|
||||
std::string _resolve_ref(const common_chat_schema_ref & schema) {
|
||||
auto it = schema.ref.find('#');
|
||||
std::string ref_fragment = it != std::string::npos ? schema.ref.substr(it + 1) : schema.ref;
|
||||
static const std::regex nonalphanumeric_regex(R"([^a-zA-Z0-9-]+)");
|
||||
std::string ref_name = "ref" + std::regex_replace(ref_fragment, nonalphanumeric_regex, "-");
|
||||
std::string ref_name = "ref" + gbnf_format_rule_name(ref_fragment);
|
||||
if (_rules.find(ref_name) == _rules.end() && _refs_being_resolved.find(schema.ref) == _refs_being_resolved.end()) {
|
||||
if (!schema.target) {
|
||||
_errors.push_back("Unresolved $ref " + schema.ref);
|
||||
@@ -833,10 +860,13 @@ public:
|
||||
std::unordered_set<std::string> required;
|
||||
std::vector<std::pair<std::string, const common_chat_schema *>> properties;
|
||||
std::map<std::string, size_t> enum_values;
|
||||
std::function<void(const common_chat_schema &, bool)> add_component = [&](const common_chat_schema & comp, bool is_required) {
|
||||
std::function<void(const common_chat_schema &, bool, int)> add_component = [&](const common_chat_schema & comp, bool is_required, int depth) {
|
||||
if (depth >= COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
if (comp.kind() == common_chat_schema::KIND_REF) {
|
||||
if (const auto * target = as<common_chat_schema_ref>(comp).target) {
|
||||
add_component(*target, is_required);
|
||||
add_component(*target, is_required, depth + 1);
|
||||
}
|
||||
} else if (comp.kind() == common_chat_schema::KIND_OBJECT) {
|
||||
for (const auto & prop : as<common_chat_schema_object>(comp).properties) {
|
||||
@@ -854,10 +884,10 @@ public:
|
||||
for (const auto & child : schema.children) {
|
||||
if (child->kind() == common_chat_schema::KIND_ANY_OF) {
|
||||
for (const auto & alt : as<common_chat_schema_any_of>(*child).children) {
|
||||
add_component(*alt, false);
|
||||
add_component(*alt, false, 0);
|
||||
}
|
||||
} else {
|
||||
add_component(*child, true);
|
||||
add_component(*child, true, 0);
|
||||
}
|
||||
}
|
||||
if (!enum_values.empty()) {
|
||||
@@ -875,6 +905,8 @@ public:
|
||||
}
|
||||
|
||||
std::string visit(const common_chat_schema & schema, const std::string & name) {
|
||||
// the $ref targets are visited too, a $ref chain is not bounded by the builder
|
||||
depth_guard guard(_depth);
|
||||
std::string rule_name = is_reserved_name(name) ? name + "-" : name.empty() ? "root" : name;
|
||||
std::string sub_name = name + (name.empty() ? "" : "-");
|
||||
|
||||
|
||||
@@ -19,5 +19,6 @@ struct common_grammar_options {
|
||||
};
|
||||
|
||||
std::string gbnf_format_literal(const std::string & literal);
|
||||
std::string gbnf_format_rule_name(const std::string & name);
|
||||
|
||||
std::string build_grammar(const std::function<void(const common_grammar_builder &)> & cb, const common_grammar_options & options = {});
|
||||
|
||||
+30
-20
@@ -9,6 +9,18 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
struct depth_guard {
|
||||
int & depth;
|
||||
depth_guard(int & depth) : depth(depth) {
|
||||
if (++depth > COMMON_RECURSIVE_LIMIT) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(COMMON_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
}
|
||||
~depth_guard() { depth--; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class common_chat_schema_builder {
|
||||
const common_json & root_;
|
||||
common_chat_schema_document & doc_;
|
||||
@@ -224,18 +236,11 @@ class common_chat_schema_builder {
|
||||
return node;
|
||||
}
|
||||
|
||||
// the builder and the code that walks the built schema recurse once per level, a deeper schema overflows the stack
|
||||
static constexpr int max_depth = 128;
|
||||
int depth_ = 0;
|
||||
int depth_ = 0;
|
||||
|
||||
common_chat_schema_ptr build_node(const common_json & schema, const std::string & path) {
|
||||
if (depth_ >= max_depth) {
|
||||
throw std::invalid_argument("JSON schema is nested more than " + std::to_string(max_depth) + " levels deep");
|
||||
}
|
||||
depth_++;
|
||||
auto node = build_node_impl(schema, path);
|
||||
depth_--;
|
||||
return node;
|
||||
depth_guard guard(depth_);
|
||||
return build_node_impl(schema, path);
|
||||
}
|
||||
|
||||
common_chat_schema_ptr build_node_impl(const common_json & schema, const std::string & path) {
|
||||
@@ -379,7 +384,9 @@ static common_chat_schema::value_type json_type(const common_json & value) {
|
||||
return common_chat_schema::TYPE_OBJECT;
|
||||
}
|
||||
|
||||
static common_chat_schema::type_set value_types_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited) {
|
||||
// depth counts the $ref, anyOf and allOf hops, a $ref chain is not bounded by the builder
|
||||
static common_chat_schema::type_set value_types_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited, int & depth) {
|
||||
depth_guard guard(depth);
|
||||
switch (s.kind()) {
|
||||
case common_chat_schema::KIND_ANY:
|
||||
return common_chat_schema::type_set::all();
|
||||
@@ -413,21 +420,21 @@ static common_chat_schema::type_set value_types_impl(const common_chat_schema &
|
||||
// a cycle contributes no type, to be safe
|
||||
return {};
|
||||
}
|
||||
auto types = value_types_impl(*target, visited);
|
||||
auto types = value_types_impl(*target, visited, depth);
|
||||
visited.erase(target);
|
||||
return types;
|
||||
}
|
||||
case common_chat_schema::KIND_ANY_OF: {
|
||||
common_chat_schema::type_set types;
|
||||
for (const auto & child : static_cast<const common_chat_schema_any_of &>(s).children) {
|
||||
types |= value_types_impl(*child, visited);
|
||||
types |= value_types_impl(*child, visited, depth);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
case common_chat_schema::KIND_ALL_OF: {
|
||||
auto types = common_chat_schema::type_set::all();
|
||||
for (const auto & child : static_cast<const common_chat_schema_all_of &>(s).children) {
|
||||
types &= value_types_impl(*child, visited);
|
||||
types &= value_types_impl(*child, visited, depth);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
@@ -437,10 +444,12 @@ static common_chat_schema::type_set value_types_impl(const common_chat_schema &
|
||||
|
||||
common_chat_schema::type_set common_chat_schema::value_types() const {
|
||||
std::unordered_set<const common_chat_schema *> visited;
|
||||
return value_types_impl(*this, visited);
|
||||
int depth = 0;
|
||||
return value_types_impl(*this, visited, depth);
|
||||
}
|
||||
|
||||
static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited) {
|
||||
static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<const common_chat_schema *> & visited, int & depth) {
|
||||
depth_guard guard(depth);
|
||||
switch (s.kind()) {
|
||||
case common_chat_schema::KIND_STRING:
|
||||
return true;
|
||||
@@ -459,13 +468,13 @@ static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<
|
||||
if (!target || !visited.insert(target).second) {
|
||||
return false;
|
||||
}
|
||||
bool result = may_be_string_impl(*target, visited);
|
||||
bool result = may_be_string_impl(*target, visited, depth);
|
||||
visited.erase(target);
|
||||
return result;
|
||||
}
|
||||
case common_chat_schema::KIND_ANY_OF:
|
||||
for (const auto & child : static_cast<const common_chat_schema_any_of &>(s).children) {
|
||||
if (may_be_string_impl(*child, visited)) {
|
||||
if (may_be_string_impl(*child, visited, depth)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -477,7 +486,7 @@ static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<
|
||||
if (child->kind() == common_chat_schema::KIND_ANY) {
|
||||
continue;
|
||||
}
|
||||
if (!may_be_string_impl(*child, visited)) {
|
||||
if (!may_be_string_impl(*child, visited, depth)) {
|
||||
return false;
|
||||
}
|
||||
any_string = true;
|
||||
@@ -491,7 +500,8 @@ static bool may_be_string_impl(const common_chat_schema & s, std::unordered_set<
|
||||
|
||||
bool common_chat_schema::may_be_string() const {
|
||||
std::unordered_set<const common_chat_schema *> visited;
|
||||
return may_be_string_impl(*this, visited);
|
||||
int depth = 0;
|
||||
return may_be_string_impl(*this, visited, depth);
|
||||
}
|
||||
|
||||
const char * common_chat_schema::kind_name(node_kind kind) {
|
||||
|
||||
+27
-15
@@ -10,10 +10,12 @@
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
// The parser can run with a lower limit than COMMON_RECURSIVE_LIMIT
|
||||
static constexpr int RULE_RECURSIVE_LIMIT = 64;
|
||||
|
||||
// Trick to catch missing branches
|
||||
template <typename T>
|
||||
inline constexpr bool is_always_false_v = false;
|
||||
@@ -186,13 +188,17 @@ std::string common_peg_ast_node::sanitized_text() const {
|
||||
}
|
||||
|
||||
void common_peg_ast_arena::visit(common_peg_ast_id id, const common_peg_ast_visitor & visitor) const {
|
||||
if (id == COMMON_PEG_INVALID_AST_ID) {
|
||||
return;
|
||||
}
|
||||
const auto & node = get(id);
|
||||
visitor(node);
|
||||
for (const auto & child : node.children) {
|
||||
visit(child, visitor);
|
||||
// no recursion: the AST is several nodes deep per level of the input
|
||||
std::vector<common_peg_ast_id> stack = { id };
|
||||
while (!stack.empty()) {
|
||||
const auto cur = stack.back();
|
||||
stack.pop_back();
|
||||
if (cur == COMMON_PEG_INVALID_AST_ID) {
|
||||
continue;
|
||||
}
|
||||
const auto & node = get(cur);
|
||||
visitor(node);
|
||||
stack.insert(stack.end(), node.children.rbegin(), node.children.rend());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -736,8 +742,15 @@ struct parser_executor {
|
||||
}
|
||||
|
||||
common_peg_parse_result operator()(const common_peg_rule_parser & p) {
|
||||
// only rules recurse, a recursive rule is entered once per input level
|
||||
if (ctx.rule_depth[p.child] >= RULE_RECURSIVE_LIMIT) {
|
||||
throw common_peg_recursion_error("input is nested more than " + std::to_string(RULE_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
|
||||
// Parse the child
|
||||
ctx.rule_depth[p.child]++;
|
||||
auto result = arena.parse(p.child, ctx, start_pos);
|
||||
ctx.rule_depth[p.child]--;
|
||||
|
||||
if (!result.fail()) {
|
||||
std::string_view text;
|
||||
@@ -819,6 +832,10 @@ common_peg_parse_result common_peg_arena::parse(common_peg_parse_context & ctx,
|
||||
if (root_ == COMMON_PEG_INVALID_PARSER_ID) {
|
||||
throw std::runtime_error("No root parser set");
|
||||
}
|
||||
|
||||
// Initialize rule depth for all parsers
|
||||
ctx.rule_depth.assign(parsers_.size(), 0);
|
||||
|
||||
return parse(root_, ctx, start);
|
||||
}
|
||||
|
||||
@@ -1069,11 +1086,6 @@ common_peg_parser operator|(const std::string & str, const common_peg_parser & p
|
||||
return operator|(str.c_str(), p);
|
||||
}
|
||||
|
||||
static std::string rule_name(const std::string & name) {
|
||||
static const std::regex invalid_rule_chars_re("[^a-zA-Z0-9-]+");
|
||||
return std::regex_replace(name, invalid_rule_chars_re, "-");
|
||||
}
|
||||
|
||||
common_peg_parser_builder::common_peg_parser_builder() {}
|
||||
|
||||
common_peg_parser common_peg_parser_builder::sequence(const std::vector<common_peg_parser_id> & parsers) {
|
||||
@@ -1155,14 +1167,14 @@ common_peg_parser common_peg_parser_builder::schema(const common_peg_parser & p,
|
||||
}
|
||||
|
||||
common_peg_parser common_peg_parser_builder::rule(const std::string & name, const common_peg_parser & p, bool trigger) {
|
||||
auto clean_name = rule_name(name);
|
||||
auto clean_name = gbnf_format_rule_name(name);
|
||||
auto rule_id = arena_.add_parser(common_peg_rule_parser{clean_name, p.id(), trigger});
|
||||
arena_.add_rule(clean_name, rule_id);
|
||||
return ref(clean_name);
|
||||
}
|
||||
|
||||
common_peg_parser common_peg_parser_builder::rule(const std::string & name, const std::function<common_peg_parser()> & builder_fn, bool trigger) {
|
||||
auto clean_name = rule_name(name);
|
||||
auto clean_name = gbnf_format_rule_name(name);
|
||||
if (arena_.has_rule(clean_name)) {
|
||||
return ref(clean_name);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -182,6 +183,10 @@ inline common_peg_parse_flags operator~(common_peg_parse_flags a) {
|
||||
return static_cast<common_peg_parse_flags>(~int(a));
|
||||
}
|
||||
|
||||
struct common_peg_recursion_error : std::invalid_argument {
|
||||
using std::invalid_argument::invalid_argument;
|
||||
};
|
||||
|
||||
struct common_peg_parse_context {
|
||||
std::string input; // [h, e, l, l, o, _, w, o, r, l, d]
|
||||
std::vector<llama_token> tokens; // [id, -1, -1, -1, -1, id, -1, -1, -1, -1, -1]
|
||||
@@ -189,6 +194,7 @@ struct common_peg_parse_context {
|
||||
common_peg_ast_arena ast;
|
||||
|
||||
int parse_depth;
|
||||
std::vector<int> rule_depth; // index: rule child id
|
||||
|
||||
common_peg_parse_context(common_peg_parse_flags flags = COMMON_PEG_PARSE_FLAG_NONE)
|
||||
: flags(flags), parse_depth(0) {}
|
||||
|
||||
+54
-15
@@ -41,6 +41,8 @@ class Qwen4ExpTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
self._ple_shards: dict[int, str] = {}
|
||||
self._ple_row_dim: int | None = None
|
||||
self._mtp_fc: dict[str, Tensor] = {}
|
||||
# halves of the merged weights, keyed by (bid, merged tensor), see _merge_shared_input
|
||||
self._merge_parts: dict[tuple[int, gguf.MODEL_TENSOR], dict[int, Tensor]] = {}
|
||||
|
||||
@classmethod
|
||||
def filter_tensors(cls, item):
|
||||
@@ -131,44 +133,79 @@ class Qwen4ExpTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
# int64 hash constants must stay exact; 1-D tensors force F32, so use KV
|
||||
if name.endswith("ple_embedding.layer_multipliers"):
|
||||
self._ple_multipliers = [int(x) for x in data_torch.tolist()]
|
||||
return []
|
||||
return
|
||||
if name.endswith("ple_embedding.ngram_heads_offsets"):
|
||||
self._ple_head_offsets = [int(x) for x in data_torch.tolist()]
|
||||
return []
|
||||
return
|
||||
if name.endswith("ple_embedding.ngram_heads_vocab_sizes"):
|
||||
self._ple_head_vocab_sizes = [int(x) for x in data_torch.tolist()]
|
||||
return []
|
||||
return
|
||||
|
||||
if ".ngram_embedding.shard_" in name:
|
||||
return self._place_ple_shard(data_torch, name)
|
||||
yield from self._place_ple_shard(data_torch, name)
|
||||
return
|
||||
|
||||
# eh_proj([e ; h_s]) = fc_embedding(e) + fc_hidden(h_s) for every hc stream s
|
||||
if name.endswith((".nextn_fc_embedding.weight", ".nextn_fc_hidden.weight")):
|
||||
self._mtp_fc[name.rsplit(".", 2)[1]] = data_torch
|
||||
if len(self._mtp_fc) < 2:
|
||||
return []
|
||||
return
|
||||
eh = torch.cat([self._mtp_fc.pop("nextn_fc_embedding"), self._mtp_fc.pop("nextn_fc_hidden")], dim=1)
|
||||
return [(self.format_tensor_name(gguf.MODEL_TENSOR.NEXTN_EH_PROJ, bid, ".weight"), eh)]
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.NEXTN_EH_PROJ, bid, ".weight"), eh)
|
||||
return
|
||||
|
||||
# one projection feeds indexer q and k; split it, as minimax-m3 does
|
||||
if ".indexer.index_qk_proj.weight" in name:
|
||||
n_q = self.hparams["indexer_n_heads"] * self.hparams["indexer_head_dim"]
|
||||
q = data_torch[:n_q]
|
||||
k = data_torch[n_q:]
|
||||
return [
|
||||
(self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_Q_PROJ, bid, ".weight"), q),
|
||||
(self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_K_PROJ, bid, ".weight"), k),
|
||||
]
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_Q_PROJ, bid, ".weight"), data_torch[:n_q])
|
||||
yield (self.format_tensor_name(gguf.MODEL_TENSOR.INDEXER_K_PROJ, bid, ".weight"), data_torch[n_q:])
|
||||
return
|
||||
|
||||
# Gemma zero-centred gammas the inherited norm.weight rule misses
|
||||
if name.endswith((".ple.norm_key.weight", ".ple.norm_query.weight", ".ple.norm_conv.weight",
|
||||
".indexer.q_layernorm.weight", ".indexer.k_layernorm.weight")):
|
||||
return [(self.map_tensor_name(name), data_torch + 1)]
|
||||
yield (self.map_tensor_name(name), data_torch + 1)
|
||||
return
|
||||
|
||||
if name.endswith(".ple.conv1d.weight"):
|
||||
return [(self.map_tensor_name(name), data_torch.squeeze())]
|
||||
yield (self.map_tensor_name(name), data_torch.squeeze())
|
||||
return
|
||||
|
||||
return super().modify_tensors(data_torch, name, bid)
|
||||
for new_name, data in super().modify_tensors(data_torch, name, bid):
|
||||
yield from self._merge_shared_input(new_name, data, bid)
|
||||
|
||||
# matmuls that read the same input, written as one weight [a | b] so the runtime does one matmul:
|
||||
# (merged tensor, (a, b), linear-attention layers only)
|
||||
_SHARED_INPUT_MERGES = (
|
||||
(gguf.MODEL_TENSOR.HC_ATTN_DOWN_INJECT, (gguf.MODEL_TENSOR.HC_ATTN_DOWN, gguf.MODEL_TENSOR.HC_ATTN_INJECT), False),
|
||||
(gguf.MODEL_TENSOR.HC_FFN_DOWN_INJECT, (gguf.MODEL_TENSOR.HC_FFN_DOWN, gguf.MODEL_TENSOR.HC_FFN_INJECT), False),
|
||||
(gguf.MODEL_TENSOR.ATTN_QKV_GATE, (gguf.MODEL_TENSOR.ATTN_QKV, gguf.MODEL_TENSOR.ATTN_GATE), True),
|
||||
(gguf.MODEL_TENSOR.SSM_BETA_ALPHA_CAT, (gguf.MODEL_TENSOR.SSM_BETA, gguf.MODEL_TENSOR.SSM_ALPHA), True),
|
||||
)
|
||||
|
||||
# caches one half of a merged weight until the other half arrives, then yields [a | b]
|
||||
def _merge_shared_input(self, new_name: str, data: Tensor, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
||||
# a pre-quantized checkpoint carries per-tensor scales, which one merged weight cannot hold
|
||||
if bid is None or self.hparams.get("quantization_config"):
|
||||
yield (new_name, data)
|
||||
return
|
||||
|
||||
layer_types = self.hparams.get("layer_types") or []
|
||||
is_linear = bid < len(layer_types) and layer_types[bid] == "linear_attention"
|
||||
|
||||
match = next(((merged, parts.index(part)) for merged, parts, linear_only in self._SHARED_INPUT_MERGES
|
||||
for part in parts
|
||||
if (is_linear or not linear_only) and self.match_model_tensor_name(new_name, part, bid)), None)
|
||||
if match is None:
|
||||
yield (new_name, data)
|
||||
return
|
||||
|
||||
merged, idx = match
|
||||
halves = self._merge_parts.setdefault((bid, merged), {})
|
||||
halves[idx] = data
|
||||
if len(halves) == 2:
|
||||
del self._merge_parts[(bid, merged)]
|
||||
yield (self.format_tensor_name(merged, bid), torch.cat([halves[0], halves[1]], dim=0))
|
||||
|
||||
# the shards concatenate into a tensor of well over 100 GB
|
||||
# use LazyChunkedTensor here, a single shard resident at a time
|
||||
@@ -214,6 +251,8 @@ class Qwen4ExpTextModel(_Qwen35MRopeMixin, _LinearAttentionVReorderBase):
|
||||
super().prepare_tensors()
|
||||
if self._mtp_fc:
|
||||
raise ValueError(f"MTP projection missing its other half: {sorted(self._mtp_fc)}")
|
||||
if self._merge_parts:
|
||||
raise ValueError(f"merged weights missing a half: {sorted((bid, t.name) for bid, t in self._merge_parts)}")
|
||||
n_parts = self.hparams.get("split_ngram_parts", 0)
|
||||
if self._ple_shards and len(self._ple_shards) != n_parts:
|
||||
raise ValueError(
|
||||
|
||||
@@ -214,52 +214,36 @@ static cudaError_t ggml_cuda_device_malloc(void ** ptr, size_t size, int device)
|
||||
}
|
||||
|
||||
#if defined(GGML_USE_HIP)
|
||||
static int ggml_cuda_parse_id(char devName[]) {
|
||||
static int ggml_cuda_parse_id(const char * devName) {
|
||||
// A list of possible Target IDs can be found under the rocclr/clr repo in device.cpp
|
||||
// these values are not stable so this is susceptible to breakage
|
||||
// https://github.com/ROCm/clr/blob/amd-staging/rocclr/device/device.cpp
|
||||
int archMajor = 0x0;
|
||||
int archMinor = 0x0;
|
||||
int archNum = GGML_CUDA_CC_OFFSET_AMD;
|
||||
int archLen = strlen(devName);
|
||||
char archName[archLen + 1];
|
||||
// Extract the base architecture model ("gfx908:sramecc+:xnack-" -> "908")
|
||||
std::string archName = devName;
|
||||
archName.erase(0, 3);
|
||||
archName = archName.substr(0, archName.find(':'));
|
||||
|
||||
// strip leading 'gfx' while copying into our buffer
|
||||
if (archLen > 3) {
|
||||
strcpy(archName, &devName[3]);
|
||||
archLen -= 3;
|
||||
}
|
||||
const auto hex = [](const std::string & s) { return (int) strtoul(s.c_str(), nullptr, 16); };
|
||||
|
||||
// trim trailing :xnack- or :sramecc- statuses
|
||||
archLen = strcspn(archName, ":");
|
||||
archName[archLen] = '\0';
|
||||
int cc = 0;
|
||||
|
||||
// tease out the version information
|
||||
if (archLen > 8) {
|
||||
if (archName.size() > 8) {
|
||||
// versions labeled generic use '-' as delimiter
|
||||
// strip the trailing "-generic" then iterate through what remains
|
||||
if ((strstr(archName, "-generic"))) {
|
||||
archName[archLen - 8] = '\0';
|
||||
char * pch;
|
||||
if ((pch = strtok(archName, "-"))) {
|
||||
archMajor = (int)strtoul(pch, 0, 16);
|
||||
if ((pch = strtok(NULL, "-"))) {
|
||||
archMinor = 0x10 * (int)strtoul(pch, 0, 16);
|
||||
}
|
||||
if (archName.compare(archName.size() - 8, 8, "-generic") == 0) {
|
||||
archName.resize(archName.size() - 8);
|
||||
const size_t sep = archName.find('-');
|
||||
cc = 0x100 * hex(archName.substr(0, sep));
|
||||
if (sep != std::string::npos) {
|
||||
cc += 0x10 * hex(archName.substr(sep + 1));
|
||||
}
|
||||
}
|
||||
} else if (archLen >= 3) {
|
||||
// last two digits should be the minor * 0x10 + stepping
|
||||
archMinor = (int)strtoul(&archName[archLen - 2], 0, 16);
|
||||
archName[archLen - 2] = '\0';
|
||||
|
||||
// only the major version remains
|
||||
archMajor = (int)strtoul(archName, 0, 16);
|
||||
} else if (archName.size() >= 3) {
|
||||
// ordinary IDs are the version in hex
|
||||
cc = hex(archName);
|
||||
}
|
||||
archNum += archMajor * 0x100;
|
||||
archNum += archMinor;
|
||||
|
||||
return archNum;
|
||||
return GGML_CUDA_CC_OFFSET_AMD + cc;
|
||||
}
|
||||
#endif // defined(GGML_USE_HIP)
|
||||
|
||||
|
||||
@@ -920,6 +920,10 @@ class MODEL_TENSOR(IntEnum):
|
||||
HC_FFN_DOWN = auto() # qwen4exp
|
||||
HC_FFN_UP = auto() # qwen4exp
|
||||
HC_FFN_INJECT = auto() # qwen4exp
|
||||
HC_ATTN_DOWN_INJECT = auto() # qwen4exp, [hc_attn_down | hc_attn_inject]
|
||||
HC_FFN_DOWN_INJECT = auto() # qwen4exp, [hc_ffn_down | hc_ffn_inject]
|
||||
ATTN_QKV_GATE = auto() # qwen4exp, [attn_qkv | attn_gate]
|
||||
SSM_BETA_ALPHA_CAT = auto() # qwen4exp, [ssm_beta | ssm_alpha], unlike the grouped qwen3next ssm_ba
|
||||
PLE_KEY = auto() # qwen4exp
|
||||
PLE_VALUE = auto() # qwen4exp
|
||||
PLE_NORM_KEY = auto() # qwen4exp
|
||||
@@ -1746,6 +1750,10 @@ TENSOR_NAMES: dict[MODEL_TENSOR, str] = {
|
||||
MODEL_TENSOR.HC_FFN_DOWN: "blk.{bid}.hc_ffn_down", # qwen4exp
|
||||
MODEL_TENSOR.HC_FFN_UP: "blk.{bid}.hc_ffn_up", # qwen4exp
|
||||
MODEL_TENSOR.HC_FFN_INJECT: "blk.{bid}.hc_ffn_inject", # qwen4exp
|
||||
MODEL_TENSOR.HC_ATTN_DOWN_INJECT: "blk.{bid}.hc_attn_down_inject", # qwen4exp
|
||||
MODEL_TENSOR.HC_FFN_DOWN_INJECT: "blk.{bid}.hc_ffn_down_inject", # qwen4exp
|
||||
MODEL_TENSOR.ATTN_QKV_GATE: "blk.{bid}.attn_qkv_gate", # qwen4exp
|
||||
MODEL_TENSOR.SSM_BETA_ALPHA_CAT: "blk.{bid}.ssm_beta_alpha", # qwen4exp
|
||||
MODEL_TENSOR.PLE_KEY: "blk.{bid}.ple_key", # qwen4exp
|
||||
MODEL_TENSOR.PLE_VALUE: "blk.{bid}.ple_value", # qwen4exp
|
||||
MODEL_TENSOR.PLE_NORM_KEY: "blk.{bid}.ple_norm_key", # qwen4exp
|
||||
@@ -3309,6 +3317,10 @@ MODEL_TENSORS: dict[MODEL_ARCH, list[MODEL_TENSOR]] = {
|
||||
MODEL_TENSOR.HC_FFN_DOWN,
|
||||
MODEL_TENSOR.HC_FFN_UP,
|
||||
MODEL_TENSOR.HC_FFN_INJECT,
|
||||
MODEL_TENSOR.HC_ATTN_DOWN_INJECT,
|
||||
MODEL_TENSOR.HC_FFN_DOWN_INJECT,
|
||||
MODEL_TENSOR.ATTN_QKV_GATE,
|
||||
MODEL_TENSOR.SSM_BETA_ALPHA_CAT,
|
||||
# full attention layers: ATTN_Q holds [q|gate] interleaved per head
|
||||
MODEL_TENSOR.ATTN_Q,
|
||||
MODEL_TENSOR.ATTN_Q_NORM,
|
||||
|
||||
@@ -1564,6 +1564,10 @@ extern "C" {
|
||||
float penalty_freq, // must be finite, 0.0 = disabled
|
||||
float penalty_present); // must be finite, 0.0 = disabled
|
||||
|
||||
/// maximum number of distinct DRY sequence breakers, duplicates not counted
|
||||
/// processing each breaker scans the entire vocabulary, so the init cost is O(num_breakers x n_vocab)
|
||||
#define LLAMA_DRY_MAX_SEQ_BREAKERS 64
|
||||
|
||||
/// @details DRY sampler, designed by p-e-w, as described in: https://github.com/oobabooga/text-generation-webui/pull/5677, porting Koboldcpp implementation authored by pi6am: https://github.com/LostRuins/koboldcpp/pull/982
|
||||
LLAMA_API struct llama_sampler * llama_sampler_init_dry(
|
||||
const struct llama_vocab * vocab,
|
||||
|
||||
@@ -58,6 +58,7 @@ Mandatory on every review; any finding here is **blocking**. Rule of thumb: GGUF
|
||||
- **Reused/reserved buffers:** recheck bounds after a buffer is shrunk or reused; watch `reserve()` then index-by-assumed-size, and header fields read before their length is checked.
|
||||
- **Server JSON ints:** clamp client-supplied integers (token/discard counts, offsets) to non-negative and an upper bound before they reach index/pointer arithmetic.
|
||||
- **RPC-deserialized fields:** treat every field (type/buffer/data/ne/nb/op_params) as hostile - validate before use. Null/zero buffers skipping validation, attacker data pointers, out-of-range type indices, and negative strides sign-extending past a corner-only assert all give arbitrary read/write.
|
||||
- **Recursion on untrusted input:** a function that recurses once per level (or per char) of request JSON, model output, a grammar, a template or a regex pattern overflows the stack - the HTTP threads have 512 KB on macOS, so a few hundred levels are enough. Every such function needs its own depth bound, checked in the function itself, not a pre-walk at a caller (the next caller skips it): libcommon uses `COMMON_RECURSIVE_LIMIT` (`common/common.h`), jinja `JINJA_RECURSIVE_LIMIT` (`common/jinja/utils.h`), the grammar `GRAMMAR_RECURSIVE_LIMIT` (`src/llama-grammar.h`). Count the levels of the input, not the frames, and throw `std::invalid_argument` for request input so the server answers 400. Look for recursion the code does not spell out: mutual recursion, recursive `std::function` lambdas, visitor/virtual dispatch, `std::regex` (libstdc++ recurses per char of the input), and per-char helpers that recurse where a loop would do.
|
||||
- **Lifetime/UAF:** flag stored raw pointers to caller/temporary storage, cached pointers to buffers a later free releases, async ops whose source may drop before completion, and structures not invalidated on free/realloc. Null-check conditionally-built or "not required" tensors before dereferencing.
|
||||
|
||||
## Approach and design (when a new component/infra is introduced)
|
||||
|
||||
@@ -635,6 +635,10 @@ static const std::map<llm_tensor, const char *> LLM_TENSOR_NAMES = {
|
||||
{ LLM_TENSOR_HC_FFN_DOWN, "blk.%d.hc_ffn_down" },
|
||||
{ LLM_TENSOR_HC_FFN_UP, "blk.%d.hc_ffn_up" },
|
||||
{ LLM_TENSOR_HC_FFN_INJECT, "blk.%d.hc_ffn_inject" },
|
||||
{ LLM_TENSOR_HC_ATTN_DOWN_INJECT, "blk.%d.hc_attn_down_inject" },
|
||||
{ LLM_TENSOR_HC_FFN_DOWN_INJECT, "blk.%d.hc_ffn_down_inject" },
|
||||
{ LLM_TENSOR_ATTN_QKV_GATE, "blk.%d.attn_qkv_gate" },
|
||||
{ LLM_TENSOR_SSM_BETA_ALPHA_CAT, "blk.%d.ssm_beta_alpha" },
|
||||
{ LLM_TENSOR_PLE_KEY, "blk.%d.ple_key" },
|
||||
{ LLM_TENSOR_PLE_VALUE, "blk.%d.ple_value" },
|
||||
{ LLM_TENSOR_PLE_NORM_KEY, "blk.%d.ple_norm_key" },
|
||||
@@ -907,6 +911,10 @@ static const std::map<llm_tensor, llm_tensor_info> LLM_TENSOR_INFOS = {
|
||||
{LLM_TENSOR_HC_FFN_DOWN, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_FFN_UP, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_FFN_INJECT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_ATTN_DOWN_INJECT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_HC_FFN_DOWN_INJECT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_ATTN_QKV_GATE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_SSM_BETA_ALPHA_CAT, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_PLE_KEY, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_PLE_VALUE, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL_MAT}},
|
||||
{LLM_TENSOR_PLE_NORM_KEY, {LLM_TENSOR_LAYER_REPEATING, GGML_OP_MUL}},
|
||||
|
||||
@@ -679,6 +679,10 @@ enum llm_tensor {
|
||||
LLM_TENSOR_HC_FFN_DOWN, // qwen4exp
|
||||
LLM_TENSOR_HC_FFN_UP, // qwen4exp
|
||||
LLM_TENSOR_HC_FFN_INJECT, // qwen4exp
|
||||
LLM_TENSOR_HC_ATTN_DOWN_INJECT, // qwen4exp
|
||||
LLM_TENSOR_HC_FFN_DOWN_INJECT, // qwen4exp
|
||||
LLM_TENSOR_ATTN_QKV_GATE, // qwen4exp
|
||||
LLM_TENSOR_SSM_BETA_ALPHA_CAT, // qwen4exp
|
||||
LLM_TENSOR_PLE_KEY, // qwen4exp
|
||||
LLM_TENSOR_PLE_VALUE, // qwen4exp
|
||||
LLM_TENSOR_PLE_NORM_KEY, // qwen4exp
|
||||
|
||||
+130
-64
@@ -8,10 +8,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
|
||||
#define MAX_REPETITION_THRESHOLD 2000
|
||||
#define MAX_GRAMMAR_RECURSION_DEPTH 256
|
||||
#define GRAMMAR_RECURSIVE_LIMIT 256
|
||||
//
|
||||
// helpers
|
||||
//
|
||||
@@ -598,8 +599,8 @@ const char * llama_grammar_parser::parse_sequence(
|
||||
n_prev_rules = 1;
|
||||
rule.push_back({LLAMA_GRETYPE_RULE_REF, ref_rule_id});
|
||||
} else if (*pos == '(') { // grouping
|
||||
if (depth >= MAX_GRAMMAR_RECURSION_DEPTH) {
|
||||
throw std::runtime_error("grammar recursion depth limit exceeded (max " + std::to_string(MAX_GRAMMAR_RECURSION_DEPTH) + "), please reduce nesting depth");
|
||||
if (depth >= GRAMMAR_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("grammar recursion depth limit exceeded (max " + std::to_string(GRAMMAR_RECURSIVE_LIMIT) + "), please reduce nesting depth");
|
||||
}
|
||||
// parse nested alternates into synthesized rule
|
||||
pos = parse_space(pos + 1, true);
|
||||
@@ -947,34 +948,19 @@ static void llama_grammar_advance_stack(
|
||||
}
|
||||
}
|
||||
|
||||
static llama_grammar_candidates llama_grammar_reject_candidates(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stacks & stacks,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
GGML_ASSERT(!stacks.empty()); // REVIEW
|
||||
|
||||
if (candidates.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto rejects = llama_grammar_reject_candidates_for_stack(rules, stacks.front(), candidates);
|
||||
|
||||
for (size_t i = 1, size = stacks.size(); i < size; ++i) {
|
||||
rejects = llama_grammar_reject_candidates_for_stack(rules, stacks[i], rejects);
|
||||
}
|
||||
|
||||
return rejects;
|
||||
}
|
||||
|
||||
static bool llama_grammar_detect_left_recursion(
|
||||
const llama_grammar_rules & rules,
|
||||
size_t rule_index,
|
||||
std::vector<bool> * rules_visited,
|
||||
std::vector<bool> * rules_in_progress,
|
||||
std::vector<bool> * rules_may_be_empty) {
|
||||
std::vector<bool> * rules_may_be_empty,
|
||||
uint32_t depth = 0) {
|
||||
if ((*rules_in_progress)[rule_index]) {
|
||||
return true;
|
||||
}
|
||||
if (depth > GRAMMAR_RECURSIVE_LIMIT) {
|
||||
throw std::runtime_error("grammar rules are chained more than " + std::to_string(GRAMMAR_RECURSIVE_LIMIT) + " levels deep");
|
||||
}
|
||||
|
||||
(*rules_in_progress)[rule_index] = true;
|
||||
|
||||
@@ -1000,7 +986,7 @@ static bool llama_grammar_detect_left_recursion(
|
||||
bool recurse_into_nonterminal = true;
|
||||
for (size_t i = 0; i < rule.size(); i++) {
|
||||
if (rule[i].type == LLAMA_GRETYPE_RULE_REF && recurse_into_nonterminal) {
|
||||
if (llama_grammar_detect_left_recursion(rules, (size_t)rule[i].value, rules_visited, rules_in_progress, rules_may_be_empty)) {
|
||||
if (llama_grammar_detect_left_recursion(rules, (size_t)rule[i].value, rules_visited, rules_in_progress, rules_may_be_empty, depth + 1)) {
|
||||
return true;
|
||||
}
|
||||
if (!((*rules_may_be_empty)[(size_t)rule[i].value])) {
|
||||
@@ -1064,56 +1050,63 @@ void llama_grammar_accept(struct llama_grammar * grammar, uint32_t chr) {
|
||||
grammar->stacks = std::move(stacks_new);
|
||||
}
|
||||
|
||||
llama_grammar_candidates llama_grammar_reject_candidates_for_stack(
|
||||
// checks the code point at depth of each candidate (an index into the input) against the stack
|
||||
// returns true when some match: they go to next, and the stacks that follow to next_stacks
|
||||
static bool llama_grammar_reject_step(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stack & stack,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
|
||||
llama_grammar_candidates rejects;
|
||||
rejects.reserve(candidates.size());
|
||||
const llama_grammar_candidates & candidates,
|
||||
size_t depth,
|
||||
const std::vector<uint32_t> & cur,
|
||||
std::vector<uint32_t> & rejects,
|
||||
std::vector<uint32_t> & next,
|
||||
llama_grammar_stacks & next_stacks) {
|
||||
rejects.reserve(cur.size());
|
||||
|
||||
if (stack.empty()) {
|
||||
for (const auto & tok : candidates) {
|
||||
if (*tok.code_points != 0 || tok.partial_utf8.n_remain != 0) {
|
||||
rejects.push_back(tok);
|
||||
for (const uint32_t i : cur) {
|
||||
const auto & tok = candidates[i];
|
||||
if (tok.code_points[depth] != 0 || tok.partial_utf8.n_remain != 0) {
|
||||
rejects.push_back(i);
|
||||
}
|
||||
}
|
||||
return rejects;
|
||||
return false;
|
||||
}
|
||||
|
||||
const llama_grammar_element * stack_pos = stack.back();
|
||||
|
||||
// if the top of the stack is a token rule, then we only need to check the token id
|
||||
if (stack_pos->type == LLAMA_GRETYPE_TOKEN || stack_pos->type == LLAMA_GRETYPE_TOKEN_NOT) {
|
||||
for (const auto & tok : candidates) {
|
||||
if (*tok.code_points == 0) {
|
||||
for (const uint32_t i : cur) {
|
||||
const auto & tok = candidates[i];
|
||||
if (tok.code_points[depth] == 0) {
|
||||
// reached the end of a token consumed by char rules, reject iff it ended
|
||||
// in a partial response
|
||||
if (tok.partial_utf8.n_remain != 0) {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
} else if (!llama_grammar_match_token(stack_pos, tok.id)) {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
}
|
||||
return rejects;
|
||||
return false;
|
||||
}
|
||||
|
||||
llama_grammar_candidates next_candidates;
|
||||
next_candidates.reserve(candidates.size());
|
||||
next.reserve(cur.size());
|
||||
|
||||
for (const auto & tok : candidates) {
|
||||
if (*tok.code_points == 0) {
|
||||
for (const uint32_t i : cur) {
|
||||
const auto & tok = candidates[i];
|
||||
if (tok.code_points[depth] == 0) {
|
||||
// reached end of full codepoints in token, reject iff it ended in a partial sequence
|
||||
// that cannot satisfy this position in grammar
|
||||
if (tok.partial_utf8.n_remain != 0 &&
|
||||
!llama_grammar_match_partial_char(stack_pos, tok.partial_utf8)) {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
} else if (llama_grammar_match_char(stack_pos, *tok.code_points).first) {
|
||||
next_candidates.push_back({ tok.index, tok.code_points + 1, tok.partial_utf8, tok.id });
|
||||
} else if (llama_grammar_match_char(stack_pos, tok.code_points[depth]).first) {
|
||||
next.push_back(i);
|
||||
} else {
|
||||
rejects.push_back(tok);
|
||||
rejects.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,17 +1117,80 @@ llama_grammar_candidates llama_grammar_reject_candidates_for_stack(
|
||||
if (!llama_grammar_is_end_of_sequence(stack_pos_after)) {
|
||||
stack_after.push_back(stack_pos_after);
|
||||
}
|
||||
llama_grammar_stacks next_stacks;
|
||||
llama_grammar_advance_stack(rules, stack_after, next_stacks);
|
||||
|
||||
auto next_rejects = llama_grammar_reject_candidates(rules, next_stacks, next_candidates);
|
||||
for (const auto & tok : next_rejects) {
|
||||
rejects.push_back({ tok.index, tok.code_points - 1, tok.partial_utf8, tok.id });
|
||||
return true;
|
||||
}
|
||||
|
||||
// the candidates that no stack accepts, each stack narrows the rejects of the previous one
|
||||
// no recursion: a level per code point, a token from the GGUF can be long
|
||||
static llama_grammar_candidates llama_grammar_reject_candidates(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stacks & stacks,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
GGML_ASSERT(!stacks.empty()); // REVIEW
|
||||
|
||||
// a level checks the candidates that reach its code point against its stacks in turn
|
||||
struct level {
|
||||
llama_grammar_stacks stacks;
|
||||
size_t i; // the stack in progress
|
||||
std::vector<uint32_t> cur; // the candidates rejected by stacks[0, i)
|
||||
std::vector<uint32_t> rejects; // the rejects of stacks[i] at this code point
|
||||
};
|
||||
|
||||
std::vector<uint32_t> all(candidates.size());
|
||||
std::iota(all.begin(), all.end(), 0u);
|
||||
|
||||
std::vector<level> levels;
|
||||
levels.push_back({ stacks, 0, std::move(all), {} });
|
||||
|
||||
while (true) {
|
||||
level & lv = levels.back();
|
||||
|
||||
if (lv.i == lv.stacks.size() || lv.cur.empty()) {
|
||||
if (levels.size() == 1) {
|
||||
break;
|
||||
}
|
||||
// the rejects at the next code point belong to stacks[i] of the parent level
|
||||
level & parent = levels[levels.size() - 2];
|
||||
parent.rejects.insert(parent.rejects.end(), lv.cur.begin(), lv.cur.end());
|
||||
levels.pop_back();
|
||||
parent.cur = std::move(parent.rejects);
|
||||
parent.rejects = {};
|
||||
parent.i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> rejects;
|
||||
std::vector<uint32_t> next;
|
||||
llama_grammar_stacks next_stacks;
|
||||
|
||||
if (!llama_grammar_reject_step(rules, lv.stacks[lv.i], candidates, levels.size() - 1, lv.cur, rejects, next, next_stacks)) {
|
||||
lv.cur = std::move(rejects);
|
||||
lv.i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
GGML_ASSERT(!next_stacks.empty());
|
||||
lv.rejects = std::move(rejects);
|
||||
levels.push_back({ std::move(next_stacks), 0, std::move(next), {} });
|
||||
}
|
||||
|
||||
llama_grammar_candidates rejects;
|
||||
rejects.reserve(levels[0].cur.size());
|
||||
for (const uint32_t i : levels[0].cur) {
|
||||
rejects.push_back(candidates[i]);
|
||||
}
|
||||
return rejects;
|
||||
}
|
||||
|
||||
llama_grammar_candidates llama_grammar_reject_candidates_for_stack(
|
||||
const llama_grammar_rules & rules,
|
||||
const llama_grammar_stack & stack,
|
||||
const llama_grammar_candidates & candidates) {
|
||||
return llama_grammar_reject_candidates(rules, llama_grammar_stacks{ stack }, candidates);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
struct llama_grammar * llama_grammar_init_impl(
|
||||
@@ -1169,14 +1225,19 @@ struct llama_grammar * llama_grammar_init_impl(
|
||||
std::vector<bool> rules_visited(n_rules);
|
||||
std::vector<bool> rules_in_progress(n_rules);
|
||||
std::vector<bool> rules_may_be_empty(n_rules);
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu", i);
|
||||
return nullptr;
|
||||
try {
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu", i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception & err) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar: %s\n", err.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// loop over alternates of start rule to build initial stacks
|
||||
@@ -1262,14 +1323,19 @@ struct llama_grammar * llama_grammar_init_impl(
|
||||
std::vector<bool> rules_visited(n_rules);
|
||||
std::vector<bool> rules_in_progress(n_rules);
|
||||
std::vector<bool> rules_may_be_empty(n_rules);
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu\n", i);
|
||||
return nullptr;
|
||||
try {
|
||||
for (size_t i = 0; i < n_rules; i++) {
|
||||
if (rules_visited[i]) {
|
||||
continue;
|
||||
}
|
||||
if (llama_grammar_detect_left_recursion(vec_rules, i, &rules_visited, &rules_in_progress, &rules_may_be_empty)) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar, left recursion detected for nonterminal at index %zu\n", i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception & err) {
|
||||
LLAMA_LOG_ERROR("unsupported grammar: %s\n", err.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// loop over alternates of start rule to build initial stacks
|
||||
|
||||
@@ -623,6 +623,10 @@ struct llama_layer {
|
||||
struct ggml_tensor * hc_ffn_down = nullptr;
|
||||
struct ggml_tensor * hc_ffn_up = nullptr;
|
||||
struct ggml_tensor * hc_ffn_inject = nullptr;
|
||||
struct ggml_tensor * hc_attn_down_inject = nullptr; // [hc_attn_down | hc_attn_inject] in one weight
|
||||
struct ggml_tensor * hc_ffn_down_inject = nullptr; // [hc_ffn_down | hc_ffn_inject] in one weight
|
||||
struct ggml_tensor * wqkv_z = nullptr; // [wqkv | wqkv_gate] in one weight
|
||||
struct ggml_tensor * ssm_beta_alpha_cat = nullptr; // [ssm_beta | ssm_alpha] in one weight, unlike the grouped ssm_beta_alpha
|
||||
|
||||
struct ggml_tensor * ple_key = nullptr;
|
||||
struct ggml_tensor * ple_value = nullptr;
|
||||
|
||||
+14
-12
@@ -125,7 +125,8 @@ static tensor_category tensor_get_category(const std::string & tensor_name) {
|
||||
if (tensor_name_match_token_embd(tensor_name.c_str())) {
|
||||
return tensor_category::TOKEN_EMBD;
|
||||
}
|
||||
if (tensor_name.find("attn_qkv.weight") != std::string::npos) {
|
||||
// attn_qkv_gate is [attn_qkv | attn_gate] in one weight, the qkv rows decide its type
|
||||
if (tensor_name.find("attn_qkv.weight") != std::string::npos || tensor_name.find("attn_qkv_gate.weight") != std::string::npos) {
|
||||
return tensor_category::ATTENTION_QKV;
|
||||
}
|
||||
if (tensor_name.find("attn_kv_b.weight") != std::string::npos) {
|
||||
@@ -161,6 +162,12 @@ static tensor_category tensor_get_category(const std::string & tensor_name) {
|
||||
return tensor_category::OTHER;
|
||||
}
|
||||
|
||||
// check if tensor is a MoE expert tensor (used by GGML_OP_MUL_MAT_ID)
|
||||
static bool tensor_is_expert(const std::string & tensor_name) {
|
||||
return tensor_name.find("_exps.") != std::string::npos ||
|
||||
tensor_name.find("_chexps.") != std::string::npos;
|
||||
}
|
||||
|
||||
// check if category is for attention-v-like tensors (more sensitive to quantization)
|
||||
static bool category_is_attn_v(tensor_category cat) {
|
||||
return cat == tensor_category::ATTENTION_V ||
|
||||
@@ -296,7 +303,7 @@ static bool tensor_allows_quantization(const llama_model_quantize_params * param
|
||||
// trivial checks first -- no string ops needed
|
||||
if (params->only_copy) return false;
|
||||
|
||||
// quantize only 2D and 3D tensors (experts)
|
||||
// quantize only 2D+ tensors (matrices, experts, conv kernels)
|
||||
if (ggml_n_dims(tensor) < 2) return false;
|
||||
|
||||
const std::string name = ggml_get_name(tensor);
|
||||
@@ -541,12 +548,7 @@ static ggml_type llama_tensor_get_type_impl(quantize_state_impl & qs, ggml_type
|
||||
} else if (ftype == LLAMA_FTYPE_MOSTLY_MXFP4_MOE) {
|
||||
// MoE tensors -> MXFP4
|
||||
// other tensors -> Q8_0
|
||||
// MLA projection tensors are also 3D, so match expert tensor roles explicitly.
|
||||
const bool is_bailingmoe3_expert = arch == LLM_ARCH_BAILINGMOE3 &&
|
||||
(category == tensor_category::FFN_UP ||
|
||||
category == tensor_category::FFN_GATE ||
|
||||
category == tensor_category::FFN_DOWN);
|
||||
if (tensor->ne[2] > 1 && (arch != LLM_ARCH_BAILINGMOE3 || is_bailingmoe3_expert)) {
|
||||
if (tensor_is_expert(name)) {
|
||||
new_type = GGML_TYPE_MXFP4;
|
||||
} else {
|
||||
new_type = GGML_TYPE_Q8_0;
|
||||
@@ -1307,11 +1309,11 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
if (it == imatrix_data->end()) {
|
||||
LLAMA_LOG_INFO("\n====== %s: did not find weights for %s\n", __func__, tensor->name);
|
||||
} else {
|
||||
if (it->second.size() == (size_t)tensor->ne[0]*tensor->ne[2]) {
|
||||
if (it->second.size() == (size_t)tensor->ne[0]*tensor->ne[2]*tensor->ne[3]) {
|
||||
imatrix = it->second.data();
|
||||
} else {
|
||||
LLAMA_LOG_INFO("\n====== %s: imatrix size %d is different from tensor size %d for %s\n", __func__,
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]), tensor->name);
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]*tensor->ne[3]), tensor->name);
|
||||
|
||||
// this can happen when quantizing an old mixtral model with split tensors with a new incompatible imatrix
|
||||
// this is a significant error and it may be good idea to abort the process if this happens,
|
||||
@@ -1319,7 +1321,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
// tok_embd should be ignored in this case, since it always causes this warning
|
||||
if (!tensor_name_match_token_embd(tensor->name)) {
|
||||
throw std::runtime_error(format("imatrix size %d is different from tensor size %d for %s",
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]), tensor->name));
|
||||
int(it->second.size()), int(tensor->ne[0]*tensor->ne[2]*tensor->ne[3]), tensor->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1341,7 +1343,7 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
|
||||
const int64_t n_per_row = tensor->ne[0];
|
||||
const int64_t nrows_per_expert = tensor->ne[1];
|
||||
const int64_t nrows_total = tensor->ne[1] * tensor->ne[2];
|
||||
const int64_t nrows_total = ggml_nrows(tensor);
|
||||
|
||||
const size_t row_size_src = ggml_row_size(tensor->type, n_per_row);
|
||||
const size_t row_size_dst = ggml_row_size(new_type, n_per_row);
|
||||
|
||||
+36
-12
@@ -18,6 +18,7 @@
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <stdexcept>
|
||||
|
||||
// the ring buffer works similarly to std::deque, but with a fixed capacity
|
||||
@@ -3333,9 +3334,13 @@ struct llama_sampler_dry {
|
||||
};
|
||||
|
||||
// Ported from Koboldcpp, original PR: https://github.com/LostRuins/koboldcpp/pull/982 (Original author: pi6am)
|
||||
static void get_overlapping_token_sequences(const llama_vocab & vocab, const std::string& str, std::unordered_multimap<llama_token, std::vector<llama_token>>& token_sequences, int max_tail_len = -1) {
|
||||
for (llama_token token_id = 0; token_id < (llama_token) vocab.n_tokens(); token_id++) {
|
||||
std::string word = vocab.detokenize({token_id}, true);
|
||||
static void get_overlapping_token_sequences(const llama_vocab & vocab, const std::vector<std::string> & vocab_words, const std::string& str, std::unordered_multimap<llama_token, std::vector<llama_token>>& token_sequences, int max_tail_len = -1) {
|
||||
// the tail tokenization only depends on how many characters of str were matched, so compute each one at most once
|
||||
std::vector<std::vector<llama_token>> tail_cache(str.size() + 1);
|
||||
std::vector<bool> tail_cached(str.size() + 1, false);
|
||||
|
||||
for (llama_token token_id = 0; token_id < (llama_token) vocab_words.size(); token_id++) {
|
||||
const std::string & word = vocab_words[token_id];
|
||||
if (word.find(str) != std::string::npos) {
|
||||
token_sequences.emplace(token_id, std::vector<llama_token>());
|
||||
} else {
|
||||
@@ -3352,10 +3357,14 @@ static void get_overlapping_token_sequences(const llama_vocab & vocab, const std
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
std::vector<llama_token> tokenization = vocab.tokenize(str.substr(i), false, false);
|
||||
if (max_tail_len >= 0 && tokenization.size() > (size_t)max_tail_len) {
|
||||
tokenization.resize(max_tail_len);
|
||||
if (!tail_cached[i]) {
|
||||
tail_cache[i] = vocab.tokenize(str.substr(i), false, false);
|
||||
if (max_tail_len >= 0 && tail_cache[i].size() > (size_t)max_tail_len) {
|
||||
tail_cache[i].resize(max_tail_len);
|
||||
}
|
||||
tail_cached[i] = true;
|
||||
}
|
||||
const std::vector<llama_token> & tokenization = tail_cache[i];
|
||||
|
||||
// Ensure we don't already have a duplicate matching tokenization
|
||||
auto its = token_sequences.equal_range(token_id);
|
||||
@@ -3656,6 +3665,16 @@ struct llama_sampler * llama_sampler_init_dry(const struct llama_vocab * vocab,
|
||||
|
||||
if (dry_enabled && seq_breakers != nullptr && num_breakers > 0) {
|
||||
// Process sequence breakers
|
||||
// each distinct breaker costs a full vocabulary scan: drop duplicates and stop at the cap
|
||||
std::unordered_set<std::string> seen_breakers;
|
||||
|
||||
// detokenize the vocabulary once instead of rescanning it for every breaker
|
||||
std::vector<std::string> vocab_words;
|
||||
vocab_words.reserve(vocab->n_tokens());
|
||||
for (llama_token token_id = 0; token_id < (llama_token) vocab->n_tokens(); ++token_id) {
|
||||
vocab_words.push_back(vocab->detokenize({token_id}, true));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_breakers; ++i) {
|
||||
if (seq_breakers[i] == nullptr || std::strlen(seq_breakers[i]) == 0) {
|
||||
LLAMA_LOG_WARN("skipping null or empty DRY sequence breaker at index %zu\n", i);
|
||||
@@ -3663,17 +3682,22 @@ struct llama_sampler * llama_sampler_init_dry(const struct llama_vocab * vocab,
|
||||
}
|
||||
|
||||
std::string sequence_break(seq_breakers[i]);
|
||||
if (sequence_break.empty()) {
|
||||
LLAMA_LOG_WARN("skipping empty DRY sequence breaker\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sequence_break.size() > MAX_CHAR_LEN) {
|
||||
LLAMA_LOG_WARN("truncating DRY sequence breaker to %d characters\n", MAX_CHAR_LEN);
|
||||
sequence_break.resize(MAX_CHAR_LEN);
|
||||
}
|
||||
|
||||
get_overlapping_token_sequences(*vocab, sequence_break, processed_breakers, MAX_SEQ_LEN);
|
||||
if (!seen_breakers.insert(sequence_break).second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seen_breakers.size() > LLAMA_DRY_MAX_SEQ_BREAKERS) {
|
||||
LLAMA_LOG_WARN("ignoring DRY sequence breaker at index %zu: more than %d distinct breakers\n",
|
||||
i, LLAMA_DRY_MAX_SEQ_BREAKERS);
|
||||
break;
|
||||
}
|
||||
|
||||
get_overlapping_token_sequences(*vocab, vocab_words, sequence_break, processed_breakers, MAX_SEQ_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+40
-39
@@ -29,31 +29,25 @@
|
||||
struct naive_trie {
|
||||
naive_trie() : has_value(false), value(0) {
|
||||
}
|
||||
// no recursion: a token from the GGUF can be long
|
||||
void insert(const char * key, size_t len, int32_t value = 0) {
|
||||
if (len == 0) {
|
||||
this->has_value = true;
|
||||
this->value = value;
|
||||
return;
|
||||
}
|
||||
char c = key[0];
|
||||
auto res = children.find(c);
|
||||
if (res != children.end()) {
|
||||
res->second.insert(key + 1, len - 1, value);
|
||||
} else {
|
||||
auto res = children.insert(std::make_pair(c, naive_trie()));
|
||||
res.first->second.insert(key + 1, len - 1, value);
|
||||
naive_trie * node = this;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
node = &node->children[key[i]];
|
||||
}
|
||||
node->has_value = true;
|
||||
node->value = value;
|
||||
}
|
||||
std::pair<const char *, size_t> get_longest_prefix(const char * key, size_t len, size_t offset = 0) const {
|
||||
if (len == 0 || offset == len) {
|
||||
return std::make_pair(key, offset);
|
||||
const naive_trie * node = this;
|
||||
while (offset < len) {
|
||||
auto res = node->children.find(key[offset]);
|
||||
if (res == node->children.end()) {
|
||||
break;
|
||||
}
|
||||
node = &res->second;
|
||||
offset++;
|
||||
}
|
||||
char c = key[offset];
|
||||
auto res = children.find(c);
|
||||
if (res != children.end()) {
|
||||
return res->second.get_longest_prefix(key, len, offset + 1);
|
||||
}
|
||||
|
||||
return std::make_pair(key, offset);
|
||||
}
|
||||
const struct naive_trie * traverse(const char c) const {
|
||||
@@ -174,30 +168,37 @@ struct llm_tokenizer_spm_session {
|
||||
}
|
||||
|
||||
private:
|
||||
// no recursion: the merges of a long token can be deep
|
||||
void resegment(llm_symbol & symbol, std::vector<llama_token> & output) {
|
||||
auto text = std::string(symbol.text, symbol.n);
|
||||
auto token = vocab.text_to_token(text);
|
||||
std::vector<const llm_symbol *> todo = { &symbol };
|
||||
while (!todo.empty()) {
|
||||
const llm_symbol & cur = *todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
// Do we need to support is_unused?
|
||||
if (token != LLAMA_TOKEN_NULL) {
|
||||
output.push_back(token);
|
||||
return;
|
||||
}
|
||||
auto text = std::string(cur.text, cur.n);
|
||||
auto token = vocab.text_to_token(text);
|
||||
|
||||
const auto p = rev_merge.find(text);
|
||||
|
||||
if (p == rev_merge.end()) {
|
||||
// output any symbols that did not form tokens as bytes.
|
||||
output.reserve(output.size() + symbol.n);
|
||||
for (int j = 0; j < (int)symbol.n; ++j) {
|
||||
llama_token id = vocab.byte_to_token(symbol.text[j]);
|
||||
output.push_back(id);
|
||||
// Do we need to support is_unused?
|
||||
if (token != LLAMA_TOKEN_NULL) {
|
||||
output.push_back(token);
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
resegment(symbols[p->second.first], output);
|
||||
resegment(symbols[p->second.second], output);
|
||||
const auto p = rev_merge.find(text);
|
||||
|
||||
if (p == rev_merge.end()) {
|
||||
// output any symbols that did not form tokens as bytes.
|
||||
output.reserve(output.size() + cur.n);
|
||||
for (int j = 0; j < (int)cur.n; ++j) {
|
||||
llama_token id = vocab.byte_to_token(cur.text[j]);
|
||||
output.push_back(id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
todo.push_back(&symbols[p->second.second]);
|
||||
todo.push_back(&symbols[p->second.first]);
|
||||
}
|
||||
}
|
||||
|
||||
void try_add_bigram(int left, int right) {
|
||||
|
||||
@@ -2741,6 +2741,7 @@ struct llama_model_qwen4exp : public llama_model_base {
|
||||
ggml_tensor * w_down,
|
||||
ggml_tensor * w_up,
|
||||
ggml_tensor * w_inject,
|
||||
ggml_tensor * w_down_inject, // [w_down | w_inject] in one weight, can be null
|
||||
ggml_tensor ** inject,
|
||||
int il);
|
||||
|
||||
|
||||
+87
-24
@@ -231,15 +231,32 @@ void llama_model_qwen4exp::load_arch_tensors(llama_model_loader & ml) {
|
||||
const int64_t value_dim = head_v_dim * n_v_heads;
|
||||
const int64_t conv_dim = key_dim * 2 + value_dim;
|
||||
|
||||
// matmuls that read the same input: try the merged [a | b] weight the converter writes, else the separate ones
|
||||
auto create_merged = [&](llm_tensor t_merged, llm_tensor t_a, llm_tensor t_b, int64_t n_in, int64_t n_a, int64_t n_b,
|
||||
ggml_tensor *& merged, ggml_tensor *& a, ggml_tensor *& b) {
|
||||
if (flags & TENSOR_SKIP) {
|
||||
const int skip = TENSOR_NOT_REQUIRED | TENSOR_SKIP;
|
||||
create_tensor(tn(t_merged, "weight", il), { n_in, n_a + n_b }, skip | TENSOR_SKIP_IF_VIRTUAL);
|
||||
create_tensor(tn(t_a, "weight", il), { n_in, n_a }, skip);
|
||||
create_tensor(tn(t_b, "weight", il), { n_in, n_b }, skip);
|
||||
return;
|
||||
}
|
||||
merged = create_tensor(tn(t_merged, "weight", il), { n_in, n_a + n_b }, flags | TENSOR_NOT_REQUIRED | TENSOR_SKIP_IF_VIRTUAL);
|
||||
if (!merged) {
|
||||
a = create_tensor(tn(t_a, "weight", il), { n_in, n_a }, flags);
|
||||
b = create_tensor(tn(t_b, "weight", il), { n_in, n_b }, flags);
|
||||
}
|
||||
};
|
||||
|
||||
// two HC modules per layer: before the token mixer, before the MoE
|
||||
layer.hc_attn_norm = create_tensor(tn(LLM_TENSOR_HC_ATTN_NORM, "weight", il), { n_embd, hc }, flags | TENSOR_ALLOW_RESHAPE);
|
||||
layer.hc_attn_down = create_tensor(tn(LLM_TENSOR_HC_ATTN_DOWN, "weight", il), { hc_dim, hc_lr }, flags);
|
||||
layer.hc_attn_up = create_tensor(tn(LLM_TENSOR_HC_ATTN_UP, "weight", il), { hc_lr, hc_dim }, flags);
|
||||
layer.hc_attn_inject = create_tensor(tn(LLM_TENSOR_HC_ATTN_INJECT, "weight", il), { hc_dim, hc }, flags);
|
||||
create_merged(LLM_TENSOR_HC_ATTN_DOWN_INJECT, LLM_TENSOR_HC_ATTN_DOWN, LLM_TENSOR_HC_ATTN_INJECT, hc_dim, hc_lr, hc,
|
||||
layer.hc_attn_down_inject, layer.hc_attn_down, layer.hc_attn_inject);
|
||||
layer.hc_ffn_norm = create_tensor(tn(LLM_TENSOR_HC_FFN_NORM, "weight", il), { n_embd, hc }, flags | TENSOR_ALLOW_RESHAPE);
|
||||
layer.hc_ffn_down = create_tensor(tn(LLM_TENSOR_HC_FFN_DOWN, "weight", il), { hc_dim, hc_lr }, flags);
|
||||
layer.hc_ffn_up = create_tensor(tn(LLM_TENSOR_HC_FFN_UP, "weight", il), { hc_lr, hc_dim }, flags);
|
||||
layer.hc_ffn_inject = create_tensor(tn(LLM_TENSOR_HC_FFN_INJECT, "weight", il), { hc_dim, hc }, flags);
|
||||
create_merged(LLM_TENSOR_HC_FFN_DOWN_INJECT, LLM_TENSOR_HC_FFN_DOWN, LLM_TENSOR_HC_FFN_INJECT, hc_dim, hc_lr, hc,
|
||||
layer.hc_ffn_down_inject, layer.hc_ffn_down, layer.hc_ffn_inject);
|
||||
|
||||
if (!hparams.is_recr(il)) {
|
||||
// full attention: wq holds [q|gate] interleaved per head
|
||||
@@ -255,13 +272,13 @@ void llama_model_qwen4exp::load_arch_tensors(llama_model_loader & ml) {
|
||||
layer.index_q_norm = create_tensor(tn(LLM_TENSOR_INDEXER_Q_NORM, "weight", il), { idx_dim }, flags);
|
||||
layer.index_k_norm = create_tensor(tn(LLM_TENSOR_INDEXER_K_NORM, "weight", il), { idx_dim }, flags);
|
||||
} else {
|
||||
layer.wqkv = create_tensor(tn(LLM_TENSOR_ATTN_QKV, "weight", il), { n_embd, key_dim * 2 + value_dim }, flags);
|
||||
layer.wqkv_gate = create_tensor(tn(LLM_TENSOR_ATTN_GATE, "weight", il), { n_embd, value_dim }, flags);
|
||||
create_merged(LLM_TENSOR_ATTN_QKV_GATE, LLM_TENSOR_ATTN_QKV, LLM_TENSOR_ATTN_GATE, n_embd, key_dim * 2 + value_dim, value_dim,
|
||||
layer.wqkv_z, layer.wqkv, layer.wqkv_gate);
|
||||
layer.ssm_conv1d = create_tensor(tn(LLM_TENSOR_SSM_CONV1D, "weight", il), { hparams.ssm_d_conv, conv_dim }, flags);
|
||||
layer.ssm_dt = create_tensor(tn(LLM_TENSOR_SSM_DT, "bias", il), { hparams.ssm_dt_rank }, flags);
|
||||
layer.ssm_a = create_tensor(tn(LLM_TENSOR_SSM_A_NOSCAN, il), { hparams.ssm_dt_rank }, flags);
|
||||
layer.ssm_beta = create_tensor(tn(LLM_TENSOR_SSM_BETA, "weight", il), { n_embd, n_v_heads }, flags);
|
||||
layer.ssm_alpha = create_tensor(tn(LLM_TENSOR_SSM_ALPHA, "weight", il), { n_embd, n_v_heads }, flags);
|
||||
create_merged(LLM_TENSOR_SSM_BETA_ALPHA_CAT, LLM_TENSOR_SSM_BETA, LLM_TENSOR_SSM_ALPHA, n_embd, n_v_heads, n_v_heads,
|
||||
layer.ssm_beta_alpha_cat, layer.ssm_beta, layer.ssm_alpha);
|
||||
layer.ssm_norm = create_tensor(tn(LLM_TENSOR_SSM_NORM, "weight", il), { head_v_dim }, flags);
|
||||
layer.ssm_out = create_tensor(tn(LLM_TENSOR_SSM_OUT, "weight", il), { value_dim, n_embd }, flags);
|
||||
}
|
||||
@@ -333,6 +350,7 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix(
|
||||
ggml_tensor * w_down,
|
||||
ggml_tensor * w_up,
|
||||
ggml_tensor * w_inject,
|
||||
ggml_tensor * w_down_inject,
|
||||
ggml_tensor ** inject,
|
||||
int il) {
|
||||
const int64_t hc = hparams.dsv4_hc_mult;
|
||||
@@ -345,8 +363,20 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix(
|
||||
xn = ggml_reshape_2d(ctx0, xn, hc_dim, nt);
|
||||
cb(xn, "hc_norm", il);
|
||||
|
||||
ggml_tensor * lo = build_lora_mm(w_down, xn);
|
||||
lo = ggml_silu(ctx0, ggml_scale(ctx0, lo, 1.0f / (float) hc));
|
||||
ggml_tensor * lo = nullptr;
|
||||
if (inject && w_down_inject) {
|
||||
// one matmul for both: the hc_down rows, then the hc_inject rows
|
||||
const int64_t n_lo = w_down_inject->ne[1] - hc;
|
||||
ggml_tensor * lo_inject = build_lora_mm(w_down_inject, xn);
|
||||
// scale the combined rows before the split; both lo and inject use 1/hc
|
||||
lo_inject = ggml_scale(ctx0, lo_inject, 1.0f / (float) hc);
|
||||
lo = ggml_view_2d(ctx0, lo_inject, n_lo, nt, lo_inject->nb[1], 0);
|
||||
*inject = ggml_view_2d(ctx0, lo_inject, hc, nt, lo_inject->nb[1], ggml_row_size(lo_inject->type, n_lo));
|
||||
cb(*inject, "hc_inject", il);
|
||||
} else {
|
||||
lo = ggml_scale(ctx0, build_lora_mm(w_down, xn), 1.0f / (float) hc);
|
||||
}
|
||||
lo = ggml_silu(ctx0, lo);
|
||||
ggml_tensor * gate = build_lora_mm(w_up, lo);
|
||||
cb(gate, "hc_gate", il);
|
||||
|
||||
@@ -375,8 +405,8 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_mix(
|
||||
}
|
||||
cb(mixed, "hc_mixed", il);
|
||||
|
||||
if (inject) {
|
||||
*inject = build_lora_mm(w_inject, xn);
|
||||
if (inject && !w_down_inject) {
|
||||
*inject = ggml_scale(ctx0, build_lora_mm(w_inject, xn), 1.0f / (float) hc);
|
||||
cb(*inject, "hc_inject", il);
|
||||
}
|
||||
|
||||
@@ -392,7 +422,7 @@ ggml_tensor * llama_model_qwen4exp::graph::build_hc_combine(
|
||||
const int64_t nt = residual->ne[2];
|
||||
|
||||
// 2*sigmoid centres the scatter weights on 1, so a zero injection is a plain residual add
|
||||
ggml_tensor * w = ggml_sigmoid(ctx0, ggml_scale(ctx0, inject, 1.0f / (float) hc));
|
||||
ggml_tensor * w = ggml_sigmoid(ctx0, inject);
|
||||
w = ggml_scale(ctx0, w, 2.0f);
|
||||
|
||||
ggml_tensor * cur = nullptr;
|
||||
@@ -480,6 +510,7 @@ llama_model_qwen4exp::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
model.layers[il].hc_attn_down,
|
||||
model.layers[il].hc_attn_up,
|
||||
model.layers[il].hc_attn_inject,
|
||||
model.layers[il].hc_attn_down_inject,
|
||||
&inject, il);
|
||||
|
||||
ggml_build_forward_expand(gf, cur);
|
||||
@@ -507,6 +538,7 @@ llama_model_qwen4exp::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
model.layers[il].hc_ffn_down,
|
||||
model.layers[il].hc_ffn_up,
|
||||
model.layers[il].hc_ffn_inject,
|
||||
model.layers[il].hc_ffn_down_inject,
|
||||
&inject, il);
|
||||
|
||||
cur = build_layer_ffn(cur, il);
|
||||
@@ -532,7 +564,7 @@ llama_model_qwen4exp::graph::graph(const llama_model & model, const llm_graph_pa
|
||||
// the final mixer is the output norm: there is no separate one
|
||||
ggml_tensor * cur = build_hc_mix(res_hc,
|
||||
model.hc_head_norm, model.hc_head_down, model.hc_head_up,
|
||||
nullptr, nullptr, -1);
|
||||
nullptr, nullptr, nullptr, -1);
|
||||
|
||||
cb(cur, "result_norm", -1);
|
||||
res->t_embd = cur;
|
||||
@@ -610,11 +642,11 @@ llama_model_qwen4exp::graph_mtp::graph_mtp(const llama_model & model, const llm_
|
||||
cb(res_hc, "mtp_eh_proj", il);
|
||||
|
||||
ggml_tensor * inject = nullptr;
|
||||
ggml_tensor * cur = build_hc_mix(res_hc, layer.hc_attn_norm, layer.hc_attn_down, layer.hc_attn_up, layer.hc_attn_inject, &inject, il);
|
||||
ggml_tensor * cur = build_hc_mix(res_hc, layer.hc_attn_norm, layer.hc_attn_down, layer.hc_attn_up, layer.hc_attn_inject, layer.hc_attn_down_inject, &inject, il);
|
||||
cur = build_layer_attn(inp_hyb->get_attn(), mctx_hyb, inp_kpool, cur, inp_pos, sections, il);
|
||||
res_hc = build_hc_combine(res_hc, cur, inject, il);
|
||||
|
||||
cur = build_hc_mix(res_hc, layer.hc_ffn_norm, layer.hc_ffn_down, layer.hc_ffn_up, layer.hc_ffn_inject, &inject, il);
|
||||
cur = build_hc_mix(res_hc, layer.hc_ffn_norm, layer.hc_ffn_down, layer.hc_ffn_up, layer.hc_ffn_inject, layer.hc_ffn_down_inject, &inject, il);
|
||||
cur = build_layer_ffn(cur, il);
|
||||
res_hc = build_hc_combine(res_hc, cur, inject, il);
|
||||
|
||||
@@ -627,7 +659,7 @@ llama_model_qwen4exp::graph_mtp::graph_mtp(const llama_model & model, const llm_
|
||||
|
||||
cur = build_hc_mix(ggml_reshape_3d(ctx0, flat_out, n_embd, hc, flat_out->ne[1]),
|
||||
layer.nextn.hc_head_norm, layer.nextn.hc_head_down, layer.nextn.hc_head_up,
|
||||
nullptr, nullptr, il);
|
||||
nullptr, nullptr, nullptr, il);
|
||||
cb(cur, "result_norm", -1);
|
||||
res->t_embd = cur;
|
||||
|
||||
@@ -642,6 +674,23 @@ std::pair<ggml_tensor *, ggml_tensor *> llama_model_qwen4exp::graph::build_qkvz(
|
||||
const int64_t n_seqs = ubatch.n_seqs;
|
||||
const int64_t n_seq_tokens = ubatch.n_seq_tokens;
|
||||
|
||||
if (model.layers[il].wqkv_z) {
|
||||
// qkv and z read the same input: one matmul, then the qkv rows and the z rows
|
||||
ggml_tensor * qkvz = build_lora_mm(model.layers[il].wqkv_z, input);
|
||||
cb(qkvz, "linear_attn_qkvz", il);
|
||||
|
||||
const int64_t n_z = hparams.ssm_d_state * hparams.ssm_dt_rank;
|
||||
const int64_t n_qkv = qkvz->ne[0] - n_z;
|
||||
|
||||
ggml_tensor * qkv_mixed = ggml_view_3d(ctx0, qkvz, n_qkv, n_seq_tokens, n_seqs, qkvz->nb[1], qkvz->nb[1] * n_seq_tokens, 0);
|
||||
cb(qkv_mixed, "linear_attn_qkv_mixed", il);
|
||||
|
||||
ggml_tensor * z = ggml_view_2d(ctx0, qkvz, n_z, n_seq_tokens * n_seqs, qkvz->nb[1], ggml_row_size(qkvz->type, n_qkv));
|
||||
cb(z, "z", il);
|
||||
|
||||
return { qkv_mixed, z };
|
||||
}
|
||||
|
||||
ggml_tensor * qkv_mixed = build_lora_mm(model.layers[il].wqkv, input, model.layers[il].wqkv_s);
|
||||
qkv_mixed = ggml_reshape_3d(ctx0, qkv_mixed, qkv_mixed->ne[0], n_seq_tokens, n_seqs);
|
||||
cb(qkv_mixed, "linear_attn_qkv_mixed", il);
|
||||
@@ -1072,17 +1121,30 @@ ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn_linear(
|
||||
ggml_tensor * qkv_mixed = qkvz.first;
|
||||
ggml_tensor * z = qkvz.second;
|
||||
|
||||
ggml_tensor * beta = build_lora_mm(model.layers[il].ssm_beta, cur, model.layers[il].ssm_beta_s);
|
||||
beta = ggml_reshape_4d(ctx0, beta, 1, num_v_heads, n_seq_tokens, n_seqs);
|
||||
ggml_tensor * beta = nullptr;
|
||||
ggml_tensor * alpha = nullptr;
|
||||
if (model.layers[il].ssm_beta_alpha_cat) {
|
||||
// beta and alpha read the same input: one matmul, then the beta rows and the alpha rows
|
||||
ggml_tensor * ba = build_lora_mm(model.layers[il].ssm_beta_alpha_cat, cur);
|
||||
cb(ba, "beta_alpha", il);
|
||||
|
||||
beta = ggml_view_4d(ctx0, ba, 1, num_v_heads, n_seq_tokens, n_seqs,
|
||||
ggml_element_size(ba), ba->nb[1], ba->nb[1] * n_seq_tokens, 0);
|
||||
alpha = ggml_view_3d(ctx0, ba, num_v_heads, n_seq_tokens, n_seqs,
|
||||
ba->nb[1], ba->nb[1] * n_seq_tokens, ggml_row_size(ba->type, num_v_heads));
|
||||
} else {
|
||||
beta = build_lora_mm(model.layers[il].ssm_beta, cur, model.layers[il].ssm_beta_s);
|
||||
beta = ggml_reshape_4d(ctx0, beta, 1, num_v_heads, n_seq_tokens, n_seqs);
|
||||
|
||||
alpha = build_lora_mm(model.layers[il].ssm_alpha, cur, model.layers[il].ssm_alpha_s);
|
||||
alpha = ggml_reshape_3d(ctx0, alpha, num_v_heads, n_seq_tokens, n_seqs);
|
||||
}
|
||||
cb(beta, "beta", il);
|
||||
cb(alpha, "alpha", il);
|
||||
|
||||
beta = ggml_sigmoid(ctx0, beta);
|
||||
cb(beta, "beta_sigmoid", il);
|
||||
|
||||
ggml_tensor * alpha = build_lora_mm(model.layers[il].ssm_alpha, cur, model.layers[il].ssm_alpha_s);
|
||||
alpha = ggml_reshape_3d(ctx0, alpha, num_v_heads, n_seq_tokens, n_seqs);
|
||||
cb(alpha, "alpha", il);
|
||||
|
||||
ggml_tensor * alpha_biased = ggml_add(ctx0, alpha, model.layers[il].ssm_dt);
|
||||
ggml_tensor * alpha_softplus = ggml_softplus(ctx0, alpha_biased);
|
||||
cb(alpha_softplus, "a_softplus", il);
|
||||
@@ -1160,7 +1222,8 @@ ggml_tensor * llama_model_qwen4exp::graph::build_layer_attn_linear(
|
||||
|
||||
ggml_tensor * output = build_recurrent_attn(inp, ssm_states_all, q_conv, k_conv, v_conv, gate, beta, state, il);
|
||||
|
||||
ggml_tensor * z_2d = ggml_reshape_4d(ctx0, z, head_v_dim, num_v_heads, n_seq_tokens, n_seqs);
|
||||
ggml_tensor * z_2d = ggml_view_4d(ctx0, z, head_v_dim, num_v_heads, n_seq_tokens, n_seqs,
|
||||
ggml_row_size(z->type, head_v_dim), z->nb[1], z->nb[1] * n_seq_tokens, 0);
|
||||
|
||||
// gated normalization, as self.norm(core_attn_out, z) in the reference
|
||||
ggml_tensor * attn_out_norm = build_norm_gated(output, model.layers[il].ssm_norm, z_2d, il);
|
||||
|
||||
@@ -130,4 +130,23 @@ void test_json_parser(testing &t) {
|
||||
t.assert_true("fail", result.fail());
|
||||
});
|
||||
});
|
||||
|
||||
// json-value is entered once per level
|
||||
t.test("deep JSON", [](testing &t) {
|
||||
auto json = build_peg_parser([](common_peg_parser_builder & p) { return p.json(); });
|
||||
|
||||
const auto parse = [&](size_t depth) {
|
||||
std::string input = std::string(depth, '[') + std::string(depth, ']');
|
||||
common_peg_parse_context ctx(input);
|
||||
try {
|
||||
return json.parse(ctx).success() ? std::string() : std::string("fail");
|
||||
} catch (const common_peg_recursion_error & e) {
|
||||
return std::string(e.what());
|
||||
}
|
||||
};
|
||||
|
||||
t.assert_equal("64 levels", std::string(), parse(64));
|
||||
t.assert_true("65 levels", parse(65).find("nested more than 64 levels deep") != std::string::npos);
|
||||
t.assert_true("100000 levels", parse(100000).find("nested more than 64 levels deep") != std::string::npos);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2927,430 +2927,552 @@ blk.60.attn_k_b.weight q4_0
|
||||
[MXFP4_MOE] mxfp4
|
||||
output.weight q8_0
|
||||
token_embd.weight q8_0
|
||||
blk.0.attn_k_b.weight q8_0
|
||||
blk.0.attn_kv_a_mqa.weight q8_0
|
||||
blk.0.attn_output.weight q8_0
|
||||
blk.0.attn_q_a.weight q8_0
|
||||
blk.0.attn_q_b.weight q8_0
|
||||
blk.0.attn_v_b.weight q8_0
|
||||
blk.0.ffn_down.weight q8_0
|
||||
blk.0.ffn_gate.weight q8_0
|
||||
blk.0.ffn_up.weight q8_0
|
||||
blk.1.attn_k_b.weight q8_0
|
||||
blk.1.attn_kv_a_mqa.weight q8_0
|
||||
blk.1.attn_output.weight q8_0
|
||||
blk.1.attn_q_a.weight q8_0
|
||||
blk.1.attn_q_b.weight q8_0
|
||||
blk.1.attn_v_b.weight q8_0
|
||||
blk.1.ffn_down.weight q8_0
|
||||
blk.1.ffn_gate.weight q8_0
|
||||
blk.1.ffn_up.weight q8_0
|
||||
blk.2.attn_k_b.weight q8_0
|
||||
blk.2.attn_kv_a_mqa.weight q8_0
|
||||
blk.2.attn_output.weight q8_0
|
||||
blk.2.attn_q_a.weight q8_0
|
||||
blk.2.attn_q_b.weight q8_0
|
||||
blk.2.attn_v_b.weight q8_0
|
||||
blk.2.ffn_down.weight q8_0
|
||||
blk.2.ffn_gate.weight q8_0
|
||||
blk.2.ffn_up.weight q8_0
|
||||
blk.3.attn_k_b.weight q8_0
|
||||
blk.3.attn_kv_a_mqa.weight q8_0
|
||||
blk.3.attn_output.weight q8_0
|
||||
blk.3.attn_q_a.weight q8_0
|
||||
blk.3.attn_q_b.weight q8_0
|
||||
blk.3.attn_v_b.weight q8_0
|
||||
blk.3.ffn_down_shexp.weight q8_0
|
||||
blk.3.ffn_gate_shexp.weight q8_0
|
||||
blk.3.ffn_up_shexp.weight q8_0
|
||||
blk.4.attn_k_b.weight q8_0
|
||||
blk.4.attn_kv_a_mqa.weight q8_0
|
||||
blk.4.attn_output.weight q8_0
|
||||
blk.4.attn_q_a.weight q8_0
|
||||
blk.4.attn_q_b.weight q8_0
|
||||
blk.4.attn_v_b.weight q8_0
|
||||
blk.4.ffn_down_shexp.weight q8_0
|
||||
blk.4.ffn_gate_shexp.weight q8_0
|
||||
blk.4.ffn_up_shexp.weight q8_0
|
||||
blk.5.attn_k_b.weight q8_0
|
||||
blk.5.attn_kv_a_mqa.weight q8_0
|
||||
blk.5.attn_output.weight q8_0
|
||||
blk.5.attn_q_a.weight q8_0
|
||||
blk.5.attn_q_b.weight q8_0
|
||||
blk.5.attn_v_b.weight q8_0
|
||||
blk.5.ffn_down_shexp.weight q8_0
|
||||
blk.5.ffn_gate_shexp.weight q8_0
|
||||
blk.5.ffn_up_shexp.weight q8_0
|
||||
blk.6.attn_k_b.weight q8_0
|
||||
blk.6.attn_kv_a_mqa.weight q8_0
|
||||
blk.6.attn_output.weight q8_0
|
||||
blk.6.attn_q_a.weight q8_0
|
||||
blk.6.attn_q_b.weight q8_0
|
||||
blk.6.attn_v_b.weight q8_0
|
||||
blk.6.ffn_down_shexp.weight q8_0
|
||||
blk.6.ffn_gate_shexp.weight q8_0
|
||||
blk.6.ffn_up_shexp.weight q8_0
|
||||
blk.7.attn_k_b.weight q8_0
|
||||
blk.7.attn_kv_a_mqa.weight q8_0
|
||||
blk.7.attn_output.weight q8_0
|
||||
blk.7.attn_q_a.weight q8_0
|
||||
blk.7.attn_q_b.weight q8_0
|
||||
blk.7.attn_v_b.weight q8_0
|
||||
blk.7.ffn_down_shexp.weight q8_0
|
||||
blk.7.ffn_gate_shexp.weight q8_0
|
||||
blk.7.ffn_up_shexp.weight q8_0
|
||||
blk.8.attn_k_b.weight q8_0
|
||||
blk.8.attn_kv_a_mqa.weight q8_0
|
||||
blk.8.attn_output.weight q8_0
|
||||
blk.8.attn_q_a.weight q8_0
|
||||
blk.8.attn_q_b.weight q8_0
|
||||
blk.8.attn_v_b.weight q8_0
|
||||
blk.8.ffn_down_shexp.weight q8_0
|
||||
blk.8.ffn_gate_shexp.weight q8_0
|
||||
blk.8.ffn_up_shexp.weight q8_0
|
||||
blk.9.attn_k_b.weight q8_0
|
||||
blk.9.attn_kv_a_mqa.weight q8_0
|
||||
blk.9.attn_output.weight q8_0
|
||||
blk.9.attn_q_a.weight q8_0
|
||||
blk.9.attn_q_b.weight q8_0
|
||||
blk.9.attn_v_b.weight q8_0
|
||||
blk.9.ffn_down_shexp.weight q8_0
|
||||
blk.9.ffn_gate_shexp.weight q8_0
|
||||
blk.9.ffn_up_shexp.weight q8_0
|
||||
blk.10.attn_k_b.weight q8_0
|
||||
blk.10.attn_kv_a_mqa.weight q8_0
|
||||
blk.10.attn_output.weight q8_0
|
||||
blk.10.attn_q_a.weight q8_0
|
||||
blk.10.attn_q_b.weight q8_0
|
||||
blk.10.attn_v_b.weight q8_0
|
||||
blk.10.ffn_down_shexp.weight q8_0
|
||||
blk.10.ffn_gate_shexp.weight q8_0
|
||||
blk.10.ffn_up_shexp.weight q8_0
|
||||
blk.11.attn_k_b.weight q8_0
|
||||
blk.11.attn_kv_a_mqa.weight q8_0
|
||||
blk.11.attn_output.weight q8_0
|
||||
blk.11.attn_q_a.weight q8_0
|
||||
blk.11.attn_q_b.weight q8_0
|
||||
blk.11.attn_v_b.weight q8_0
|
||||
blk.11.ffn_down_shexp.weight q8_0
|
||||
blk.11.ffn_gate_shexp.weight q8_0
|
||||
blk.11.ffn_up_shexp.weight q8_0
|
||||
blk.12.attn_k_b.weight q8_0
|
||||
blk.12.attn_kv_a_mqa.weight q8_0
|
||||
blk.12.attn_output.weight q8_0
|
||||
blk.12.attn_q_a.weight q8_0
|
||||
blk.12.attn_q_b.weight q8_0
|
||||
blk.12.attn_v_b.weight q8_0
|
||||
blk.12.ffn_down_shexp.weight q8_0
|
||||
blk.12.ffn_gate_shexp.weight q8_0
|
||||
blk.12.ffn_up_shexp.weight q8_0
|
||||
blk.13.attn_k_b.weight q8_0
|
||||
blk.13.attn_kv_a_mqa.weight q8_0
|
||||
blk.13.attn_output.weight q8_0
|
||||
blk.13.attn_q_a.weight q8_0
|
||||
blk.13.attn_q_b.weight q8_0
|
||||
blk.13.attn_v_b.weight q8_0
|
||||
blk.13.ffn_down_shexp.weight q8_0
|
||||
blk.13.ffn_gate_shexp.weight q8_0
|
||||
blk.13.ffn_up_shexp.weight q8_0
|
||||
blk.14.attn_k_b.weight q8_0
|
||||
blk.14.attn_kv_a_mqa.weight q8_0
|
||||
blk.14.attn_output.weight q8_0
|
||||
blk.14.attn_q_a.weight q8_0
|
||||
blk.14.attn_q_b.weight q8_0
|
||||
blk.14.attn_v_b.weight q8_0
|
||||
blk.14.ffn_down_shexp.weight q8_0
|
||||
blk.14.ffn_gate_shexp.weight q8_0
|
||||
blk.14.ffn_up_shexp.weight q8_0
|
||||
blk.15.attn_k_b.weight q8_0
|
||||
blk.15.attn_kv_a_mqa.weight q8_0
|
||||
blk.15.attn_output.weight q8_0
|
||||
blk.15.attn_q_a.weight q8_0
|
||||
blk.15.attn_q_b.weight q8_0
|
||||
blk.15.attn_v_b.weight q8_0
|
||||
blk.15.ffn_down_shexp.weight q8_0
|
||||
blk.15.ffn_gate_shexp.weight q8_0
|
||||
blk.15.ffn_up_shexp.weight q8_0
|
||||
blk.16.attn_k_b.weight q8_0
|
||||
blk.16.attn_kv_a_mqa.weight q8_0
|
||||
blk.16.attn_output.weight q8_0
|
||||
blk.16.attn_q_a.weight q8_0
|
||||
blk.16.attn_q_b.weight q8_0
|
||||
blk.16.attn_v_b.weight q8_0
|
||||
blk.16.ffn_down_shexp.weight q8_0
|
||||
blk.16.ffn_gate_shexp.weight q8_0
|
||||
blk.16.ffn_up_shexp.weight q8_0
|
||||
blk.17.attn_k_b.weight q8_0
|
||||
blk.17.attn_kv_a_mqa.weight q8_0
|
||||
blk.17.attn_output.weight q8_0
|
||||
blk.17.attn_q_a.weight q8_0
|
||||
blk.17.attn_q_b.weight q8_0
|
||||
blk.17.attn_v_b.weight q8_0
|
||||
blk.17.ffn_down_shexp.weight q8_0
|
||||
blk.17.ffn_gate_shexp.weight q8_0
|
||||
blk.17.ffn_up_shexp.weight q8_0
|
||||
blk.18.attn_k_b.weight q8_0
|
||||
blk.18.attn_kv_a_mqa.weight q8_0
|
||||
blk.18.attn_output.weight q8_0
|
||||
blk.18.attn_q_a.weight q8_0
|
||||
blk.18.attn_q_b.weight q8_0
|
||||
blk.18.attn_v_b.weight q8_0
|
||||
blk.18.ffn_down_shexp.weight q8_0
|
||||
blk.18.ffn_gate_shexp.weight q8_0
|
||||
blk.18.ffn_up_shexp.weight q8_0
|
||||
blk.19.attn_k_b.weight q8_0
|
||||
blk.19.attn_kv_a_mqa.weight q8_0
|
||||
blk.19.attn_output.weight q8_0
|
||||
blk.19.attn_q_a.weight q8_0
|
||||
blk.19.attn_q_b.weight q8_0
|
||||
blk.19.attn_v_b.weight q8_0
|
||||
blk.19.ffn_down_shexp.weight q8_0
|
||||
blk.19.ffn_gate_shexp.weight q8_0
|
||||
blk.19.ffn_up_shexp.weight q8_0
|
||||
blk.20.attn_k_b.weight q8_0
|
||||
blk.20.attn_kv_a_mqa.weight q8_0
|
||||
blk.20.attn_output.weight q8_0
|
||||
blk.20.attn_q_a.weight q8_0
|
||||
blk.20.attn_q_b.weight q8_0
|
||||
blk.20.attn_v_b.weight q8_0
|
||||
blk.20.ffn_down_shexp.weight q8_0
|
||||
blk.20.ffn_gate_shexp.weight q8_0
|
||||
blk.20.ffn_up_shexp.weight q8_0
|
||||
blk.21.attn_k_b.weight q8_0
|
||||
blk.21.attn_kv_a_mqa.weight q8_0
|
||||
blk.21.attn_output.weight q8_0
|
||||
blk.21.attn_q_a.weight q8_0
|
||||
blk.21.attn_q_b.weight q8_0
|
||||
blk.21.attn_v_b.weight q8_0
|
||||
blk.21.ffn_down_shexp.weight q8_0
|
||||
blk.21.ffn_gate_shexp.weight q8_0
|
||||
blk.21.ffn_up_shexp.weight q8_0
|
||||
blk.22.attn_k_b.weight q8_0
|
||||
blk.22.attn_kv_a_mqa.weight q8_0
|
||||
blk.22.attn_output.weight q8_0
|
||||
blk.22.attn_q_a.weight q8_0
|
||||
blk.22.attn_q_b.weight q8_0
|
||||
blk.22.attn_v_b.weight q8_0
|
||||
blk.22.ffn_down_shexp.weight q8_0
|
||||
blk.22.ffn_gate_shexp.weight q8_0
|
||||
blk.22.ffn_up_shexp.weight q8_0
|
||||
blk.23.attn_k_b.weight q8_0
|
||||
blk.23.attn_kv_a_mqa.weight q8_0
|
||||
blk.23.attn_output.weight q8_0
|
||||
blk.23.attn_q_a.weight q8_0
|
||||
blk.23.attn_q_b.weight q8_0
|
||||
blk.23.attn_v_b.weight q8_0
|
||||
blk.23.ffn_down_shexp.weight q8_0
|
||||
blk.23.ffn_gate_shexp.weight q8_0
|
||||
blk.23.ffn_up_shexp.weight q8_0
|
||||
blk.24.attn_k_b.weight q8_0
|
||||
blk.24.attn_kv_a_mqa.weight q8_0
|
||||
blk.24.attn_output.weight q8_0
|
||||
blk.24.attn_q_a.weight q8_0
|
||||
blk.24.attn_q_b.weight q8_0
|
||||
blk.24.attn_v_b.weight q8_0
|
||||
blk.24.ffn_down_shexp.weight q8_0
|
||||
blk.24.ffn_gate_shexp.weight q8_0
|
||||
blk.24.ffn_up_shexp.weight q8_0
|
||||
blk.25.attn_k_b.weight q8_0
|
||||
blk.25.attn_kv_a_mqa.weight q8_0
|
||||
blk.25.attn_output.weight q8_0
|
||||
blk.25.attn_q_a.weight q8_0
|
||||
blk.25.attn_q_b.weight q8_0
|
||||
blk.25.attn_v_b.weight q8_0
|
||||
blk.25.ffn_down_shexp.weight q8_0
|
||||
blk.25.ffn_gate_shexp.weight q8_0
|
||||
blk.25.ffn_up_shexp.weight q8_0
|
||||
blk.26.attn_k_b.weight q8_0
|
||||
blk.26.attn_kv_a_mqa.weight q8_0
|
||||
blk.26.attn_output.weight q8_0
|
||||
blk.26.attn_q_a.weight q8_0
|
||||
blk.26.attn_q_b.weight q8_0
|
||||
blk.26.attn_v_b.weight q8_0
|
||||
blk.26.ffn_down_shexp.weight q8_0
|
||||
blk.26.ffn_gate_shexp.weight q8_0
|
||||
blk.26.ffn_up_shexp.weight q8_0
|
||||
blk.27.attn_k_b.weight q8_0
|
||||
blk.27.attn_kv_a_mqa.weight q8_0
|
||||
blk.27.attn_output.weight q8_0
|
||||
blk.27.attn_q_a.weight q8_0
|
||||
blk.27.attn_q_b.weight q8_0
|
||||
blk.27.attn_v_b.weight q8_0
|
||||
blk.27.ffn_down_shexp.weight q8_0
|
||||
blk.27.ffn_gate_shexp.weight q8_0
|
||||
blk.27.ffn_up_shexp.weight q8_0
|
||||
blk.28.attn_k_b.weight q8_0
|
||||
blk.28.attn_kv_a_mqa.weight q8_0
|
||||
blk.28.attn_output.weight q8_0
|
||||
blk.28.attn_q_a.weight q8_0
|
||||
blk.28.attn_q_b.weight q8_0
|
||||
blk.28.attn_v_b.weight q8_0
|
||||
blk.28.ffn_down_shexp.weight q8_0
|
||||
blk.28.ffn_gate_shexp.weight q8_0
|
||||
blk.28.ffn_up_shexp.weight q8_0
|
||||
blk.29.attn_k_b.weight q8_0
|
||||
blk.29.attn_kv_a_mqa.weight q8_0
|
||||
blk.29.attn_output.weight q8_0
|
||||
blk.29.attn_q_a.weight q8_0
|
||||
blk.29.attn_q_b.weight q8_0
|
||||
blk.29.attn_v_b.weight q8_0
|
||||
blk.29.ffn_down_shexp.weight q8_0
|
||||
blk.29.ffn_gate_shexp.weight q8_0
|
||||
blk.29.ffn_up_shexp.weight q8_0
|
||||
blk.30.attn_k_b.weight q8_0
|
||||
blk.30.attn_kv_a_mqa.weight q8_0
|
||||
blk.30.attn_output.weight q8_0
|
||||
blk.30.attn_q_a.weight q8_0
|
||||
blk.30.attn_q_b.weight q8_0
|
||||
blk.30.attn_v_b.weight q8_0
|
||||
blk.30.ffn_down_shexp.weight q8_0
|
||||
blk.30.ffn_gate_shexp.weight q8_0
|
||||
blk.30.ffn_up_shexp.weight q8_0
|
||||
blk.31.attn_k_b.weight q8_0
|
||||
blk.31.attn_kv_a_mqa.weight q8_0
|
||||
blk.31.attn_output.weight q8_0
|
||||
blk.31.attn_q_a.weight q8_0
|
||||
blk.31.attn_q_b.weight q8_0
|
||||
blk.31.attn_v_b.weight q8_0
|
||||
blk.31.ffn_down_shexp.weight q8_0
|
||||
blk.31.ffn_gate_shexp.weight q8_0
|
||||
blk.31.ffn_up_shexp.weight q8_0
|
||||
blk.32.attn_k_b.weight q8_0
|
||||
blk.32.attn_kv_a_mqa.weight q8_0
|
||||
blk.32.attn_output.weight q8_0
|
||||
blk.32.attn_q_a.weight q8_0
|
||||
blk.32.attn_q_b.weight q8_0
|
||||
blk.32.attn_v_b.weight q8_0
|
||||
blk.32.ffn_down_shexp.weight q8_0
|
||||
blk.32.ffn_gate_shexp.weight q8_0
|
||||
blk.32.ffn_up_shexp.weight q8_0
|
||||
blk.33.attn_k_b.weight q8_0
|
||||
blk.33.attn_kv_a_mqa.weight q8_0
|
||||
blk.33.attn_output.weight q8_0
|
||||
blk.33.attn_q_a.weight q8_0
|
||||
blk.33.attn_q_b.weight q8_0
|
||||
blk.33.attn_v_b.weight q8_0
|
||||
blk.33.ffn_down_shexp.weight q8_0
|
||||
blk.33.ffn_gate_shexp.weight q8_0
|
||||
blk.33.ffn_up_shexp.weight q8_0
|
||||
blk.34.attn_k_b.weight q8_0
|
||||
blk.34.attn_kv_a_mqa.weight q8_0
|
||||
blk.34.attn_output.weight q8_0
|
||||
blk.34.attn_q_a.weight q8_0
|
||||
blk.34.attn_q_b.weight q8_0
|
||||
blk.34.attn_v_b.weight q8_0
|
||||
blk.34.ffn_down_shexp.weight q8_0
|
||||
blk.34.ffn_gate_shexp.weight q8_0
|
||||
blk.34.ffn_up_shexp.weight q8_0
|
||||
blk.35.attn_k_b.weight q8_0
|
||||
blk.35.attn_kv_a_mqa.weight q8_0
|
||||
blk.35.attn_output.weight q8_0
|
||||
blk.35.attn_q_a.weight q8_0
|
||||
blk.35.attn_q_b.weight q8_0
|
||||
blk.35.attn_v_b.weight q8_0
|
||||
blk.35.ffn_down_shexp.weight q8_0
|
||||
blk.35.ffn_gate_shexp.weight q8_0
|
||||
blk.35.ffn_up_shexp.weight q8_0
|
||||
blk.36.attn_k_b.weight q8_0
|
||||
blk.36.attn_kv_a_mqa.weight q8_0
|
||||
blk.36.attn_output.weight q8_0
|
||||
blk.36.attn_q_a.weight q8_0
|
||||
blk.36.attn_q_b.weight q8_0
|
||||
blk.36.attn_v_b.weight q8_0
|
||||
blk.36.ffn_down_shexp.weight q8_0
|
||||
blk.36.ffn_gate_shexp.weight q8_0
|
||||
blk.36.ffn_up_shexp.weight q8_0
|
||||
blk.37.attn_k_b.weight q8_0
|
||||
blk.37.attn_kv_a_mqa.weight q8_0
|
||||
blk.37.attn_output.weight q8_0
|
||||
blk.37.attn_q_a.weight q8_0
|
||||
blk.37.attn_q_b.weight q8_0
|
||||
blk.37.attn_v_b.weight q8_0
|
||||
blk.37.ffn_down_shexp.weight q8_0
|
||||
blk.37.ffn_gate_shexp.weight q8_0
|
||||
blk.37.ffn_up_shexp.weight q8_0
|
||||
blk.38.attn_k_b.weight q8_0
|
||||
blk.38.attn_kv_a_mqa.weight q8_0
|
||||
blk.38.attn_output.weight q8_0
|
||||
blk.38.attn_q_a.weight q8_0
|
||||
blk.38.attn_q_b.weight q8_0
|
||||
blk.38.attn_v_b.weight q8_0
|
||||
blk.38.ffn_down_shexp.weight q8_0
|
||||
blk.38.ffn_gate_shexp.weight q8_0
|
||||
blk.38.ffn_up_shexp.weight q8_0
|
||||
blk.39.attn_k_b.weight q8_0
|
||||
blk.39.attn_kv_a_mqa.weight q8_0
|
||||
blk.39.attn_output.weight q8_0
|
||||
blk.39.attn_q_a.weight q8_0
|
||||
blk.39.attn_q_b.weight q8_0
|
||||
blk.39.attn_v_b.weight q8_0
|
||||
blk.39.ffn_down_shexp.weight q8_0
|
||||
blk.39.ffn_gate_shexp.weight q8_0
|
||||
blk.39.ffn_up_shexp.weight q8_0
|
||||
blk.40.attn_k_b.weight q8_0
|
||||
blk.40.attn_kv_a_mqa.weight q8_0
|
||||
blk.40.attn_output.weight q8_0
|
||||
blk.40.attn_q_a.weight q8_0
|
||||
blk.40.attn_q_b.weight q8_0
|
||||
blk.40.attn_v_b.weight q8_0
|
||||
blk.40.ffn_down_shexp.weight q8_0
|
||||
blk.40.ffn_gate_shexp.weight q8_0
|
||||
blk.40.ffn_up_shexp.weight q8_0
|
||||
blk.41.attn_k_b.weight q8_0
|
||||
blk.41.attn_kv_a_mqa.weight q8_0
|
||||
blk.41.attn_output.weight q8_0
|
||||
blk.41.attn_q_a.weight q8_0
|
||||
blk.41.attn_q_b.weight q8_0
|
||||
blk.41.attn_v_b.weight q8_0
|
||||
blk.41.ffn_down_shexp.weight q8_0
|
||||
blk.41.ffn_gate_shexp.weight q8_0
|
||||
blk.41.ffn_up_shexp.weight q8_0
|
||||
blk.42.attn_k_b.weight q8_0
|
||||
blk.42.attn_kv_a_mqa.weight q8_0
|
||||
blk.42.attn_output.weight q8_0
|
||||
blk.42.attn_q_a.weight q8_0
|
||||
blk.42.attn_q_b.weight q8_0
|
||||
blk.42.attn_v_b.weight q8_0
|
||||
blk.42.ffn_down_shexp.weight q8_0
|
||||
blk.42.ffn_gate_shexp.weight q8_0
|
||||
blk.42.ffn_up_shexp.weight q8_0
|
||||
blk.43.attn_k_b.weight q8_0
|
||||
blk.43.attn_kv_a_mqa.weight q8_0
|
||||
blk.43.attn_output.weight q8_0
|
||||
blk.43.attn_q_a.weight q8_0
|
||||
blk.43.attn_q_b.weight q8_0
|
||||
blk.43.attn_v_b.weight q8_0
|
||||
blk.43.ffn_down_shexp.weight q8_0
|
||||
blk.43.ffn_gate_shexp.weight q8_0
|
||||
blk.43.ffn_up_shexp.weight q8_0
|
||||
blk.44.attn_k_b.weight q8_0
|
||||
blk.44.attn_kv_a_mqa.weight q8_0
|
||||
blk.44.attn_output.weight q8_0
|
||||
blk.44.attn_q_a.weight q8_0
|
||||
blk.44.attn_q_b.weight q8_0
|
||||
blk.44.attn_v_b.weight q8_0
|
||||
blk.44.ffn_down_shexp.weight q8_0
|
||||
blk.44.ffn_gate_shexp.weight q8_0
|
||||
blk.44.ffn_up_shexp.weight q8_0
|
||||
blk.45.attn_k_b.weight q8_0
|
||||
blk.45.attn_kv_a_mqa.weight q8_0
|
||||
blk.45.attn_output.weight q8_0
|
||||
blk.45.attn_q_a.weight q8_0
|
||||
blk.45.attn_q_b.weight q8_0
|
||||
blk.45.attn_v_b.weight q8_0
|
||||
blk.45.ffn_down_shexp.weight q8_0
|
||||
blk.45.ffn_gate_shexp.weight q8_0
|
||||
blk.45.ffn_up_shexp.weight q8_0
|
||||
blk.46.attn_k_b.weight q8_0
|
||||
blk.46.attn_kv_a_mqa.weight q8_0
|
||||
blk.46.attn_output.weight q8_0
|
||||
blk.46.attn_q_a.weight q8_0
|
||||
blk.46.attn_q_b.weight q8_0
|
||||
blk.46.attn_v_b.weight q8_0
|
||||
blk.46.ffn_down_shexp.weight q8_0
|
||||
blk.46.ffn_gate_shexp.weight q8_0
|
||||
blk.46.ffn_up_shexp.weight q8_0
|
||||
blk.47.attn_k_b.weight q8_0
|
||||
blk.47.attn_kv_a_mqa.weight q8_0
|
||||
blk.47.attn_output.weight q8_0
|
||||
blk.47.attn_q_a.weight q8_0
|
||||
blk.47.attn_q_b.weight q8_0
|
||||
blk.47.attn_v_b.weight q8_0
|
||||
blk.47.ffn_down_shexp.weight q8_0
|
||||
blk.47.ffn_gate_shexp.weight q8_0
|
||||
blk.47.ffn_up_shexp.weight q8_0
|
||||
blk.48.attn_k_b.weight q8_0
|
||||
blk.48.attn_kv_a_mqa.weight q8_0
|
||||
blk.48.attn_output.weight q8_0
|
||||
blk.48.attn_q_a.weight q8_0
|
||||
blk.48.attn_q_b.weight q8_0
|
||||
blk.48.attn_v_b.weight q8_0
|
||||
blk.48.ffn_down_shexp.weight q8_0
|
||||
blk.48.ffn_gate_shexp.weight q8_0
|
||||
blk.48.ffn_up_shexp.weight q8_0
|
||||
blk.49.attn_k_b.weight q8_0
|
||||
blk.49.attn_kv_a_mqa.weight q8_0
|
||||
blk.49.attn_output.weight q8_0
|
||||
blk.49.attn_q_a.weight q8_0
|
||||
blk.49.attn_q_b.weight q8_0
|
||||
blk.49.attn_v_b.weight q8_0
|
||||
blk.49.ffn_down_shexp.weight q8_0
|
||||
blk.49.ffn_gate_shexp.weight q8_0
|
||||
blk.49.ffn_up_shexp.weight q8_0
|
||||
blk.50.attn_k_b.weight q8_0
|
||||
blk.50.attn_kv_a_mqa.weight q8_0
|
||||
blk.50.attn_output.weight q8_0
|
||||
blk.50.attn_q_a.weight q8_0
|
||||
blk.50.attn_q_b.weight q8_0
|
||||
blk.50.attn_v_b.weight q8_0
|
||||
blk.50.ffn_down_shexp.weight q8_0
|
||||
blk.50.ffn_gate_shexp.weight q8_0
|
||||
blk.50.ffn_up_shexp.weight q8_0
|
||||
blk.51.attn_k_b.weight q8_0
|
||||
blk.51.attn_kv_a_mqa.weight q8_0
|
||||
blk.51.attn_output.weight q8_0
|
||||
blk.51.attn_q_a.weight q8_0
|
||||
blk.51.attn_q_b.weight q8_0
|
||||
blk.51.attn_v_b.weight q8_0
|
||||
blk.51.ffn_down_shexp.weight q8_0
|
||||
blk.51.ffn_gate_shexp.weight q8_0
|
||||
blk.51.ffn_up_shexp.weight q8_0
|
||||
blk.52.attn_k_b.weight q8_0
|
||||
blk.52.attn_kv_a_mqa.weight q8_0
|
||||
blk.52.attn_output.weight q8_0
|
||||
blk.52.attn_q_a.weight q8_0
|
||||
blk.52.attn_q_b.weight q8_0
|
||||
blk.52.attn_v_b.weight q8_0
|
||||
blk.52.ffn_down_shexp.weight q8_0
|
||||
blk.52.ffn_gate_shexp.weight q8_0
|
||||
blk.52.ffn_up_shexp.weight q8_0
|
||||
blk.53.attn_k_b.weight q8_0
|
||||
blk.53.attn_kv_a_mqa.weight q8_0
|
||||
blk.53.attn_output.weight q8_0
|
||||
blk.53.attn_q_a.weight q8_0
|
||||
blk.53.attn_q_b.weight q8_0
|
||||
blk.53.attn_v_b.weight q8_0
|
||||
blk.53.ffn_down_shexp.weight q8_0
|
||||
blk.53.ffn_gate_shexp.weight q8_0
|
||||
blk.53.ffn_up_shexp.weight q8_0
|
||||
blk.54.attn_k_b.weight q8_0
|
||||
blk.54.attn_kv_a_mqa.weight q8_0
|
||||
blk.54.attn_output.weight q8_0
|
||||
blk.54.attn_q_a.weight q8_0
|
||||
blk.54.attn_q_b.weight q8_0
|
||||
blk.54.attn_v_b.weight q8_0
|
||||
blk.54.ffn_down_shexp.weight q8_0
|
||||
blk.54.ffn_gate_shexp.weight q8_0
|
||||
blk.54.ffn_up_shexp.weight q8_0
|
||||
blk.55.attn_k_b.weight q8_0
|
||||
blk.55.attn_kv_a_mqa.weight q8_0
|
||||
blk.55.attn_output.weight q8_0
|
||||
blk.55.attn_q_a.weight q8_0
|
||||
blk.55.attn_q_b.weight q8_0
|
||||
blk.55.attn_v_b.weight q8_0
|
||||
blk.55.ffn_down_shexp.weight q8_0
|
||||
blk.55.ffn_gate_shexp.weight q8_0
|
||||
blk.55.ffn_up_shexp.weight q8_0
|
||||
blk.56.attn_k_b.weight q8_0
|
||||
blk.56.attn_kv_a_mqa.weight q8_0
|
||||
blk.56.attn_output.weight q8_0
|
||||
blk.56.attn_q_a.weight q8_0
|
||||
blk.56.attn_q_b.weight q8_0
|
||||
blk.56.attn_v_b.weight q8_0
|
||||
blk.56.ffn_down_shexp.weight q8_0
|
||||
blk.56.ffn_gate_shexp.weight q8_0
|
||||
blk.56.ffn_up_shexp.weight q8_0
|
||||
blk.57.attn_k_b.weight q8_0
|
||||
blk.57.attn_kv_a_mqa.weight q8_0
|
||||
blk.57.attn_output.weight q8_0
|
||||
blk.57.attn_q_a.weight q8_0
|
||||
blk.57.attn_q_b.weight q8_0
|
||||
blk.57.attn_v_b.weight q8_0
|
||||
blk.57.ffn_down_shexp.weight q8_0
|
||||
blk.57.ffn_gate_shexp.weight q8_0
|
||||
blk.57.ffn_up_shexp.weight q8_0
|
||||
blk.58.attn_k_b.weight q8_0
|
||||
blk.58.attn_kv_a_mqa.weight q8_0
|
||||
blk.58.attn_output.weight q8_0
|
||||
blk.58.attn_q_a.weight q8_0
|
||||
blk.58.attn_q_b.weight q8_0
|
||||
blk.58.attn_v_b.weight q8_0
|
||||
blk.58.ffn_down_shexp.weight q8_0
|
||||
blk.58.ffn_gate_shexp.weight q8_0
|
||||
blk.58.ffn_up_shexp.weight q8_0
|
||||
blk.59.attn_k_b.weight q8_0
|
||||
blk.59.attn_kv_a_mqa.weight q8_0
|
||||
blk.59.attn_output.weight q8_0
|
||||
blk.59.attn_q_a.weight q8_0
|
||||
blk.59.attn_q_b.weight q8_0
|
||||
blk.59.attn_v_b.weight q8_0
|
||||
blk.59.ffn_down_shexp.weight q8_0
|
||||
blk.59.ffn_gate_shexp.weight q8_0
|
||||
blk.59.ffn_up_shexp.weight q8_0
|
||||
blk.60.attn_k_b.weight q8_0
|
||||
blk.60.attn_kv_a_mqa.weight q8_0
|
||||
blk.60.attn_output.weight q8_0
|
||||
blk.60.attn_q_a.weight q8_0
|
||||
blk.60.attn_q_b.weight q8_0
|
||||
blk.60.attn_v_b.weight q8_0
|
||||
blk.60.ffn_down_shexp.weight q8_0
|
||||
blk.60.ffn_gate_shexp.weight q8_0
|
||||
blk.60.ffn_up_shexp.weight q8_0
|
||||
|
||||
@@ -879,6 +879,18 @@ static void test_failure_left_recursion() {
|
||||
empty ::= "blah" | )""";
|
||||
assert(test_build_grammar_fails(hardest_str));
|
||||
|
||||
const auto chain = [](int n) {
|
||||
std::string s = "root ::= r0\n";
|
||||
for (int i = 0; i < n; i++) {
|
||||
s += "r" + std::to_string(i) + " ::= r" + std::to_string(i + 1) + "\n";
|
||||
}
|
||||
return s + "r" + std::to_string(n) + " ::= \"a\"\n";
|
||||
};
|
||||
llama_grammar * grammar = build_grammar(chain(100));
|
||||
assert(grammar != nullptr);
|
||||
llama_grammar_free_impl(grammar);
|
||||
assert(test_build_grammar_fails(chain(1000)));
|
||||
|
||||
fprintf(stderr, " ✅︎ Passed\n");
|
||||
}
|
||||
|
||||
@@ -1476,6 +1488,25 @@ static void test_json_schema() {
|
||||
// R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green", "home"]})""",
|
||||
}
|
||||
);
|
||||
|
||||
// the deepest schema of json_schema_to_grammar must give a valid grammar
|
||||
std::string deep_schema;
|
||||
for (int i = 0; i < 127; i++) {
|
||||
deep_schema += R"({"type": "array", "items": )";
|
||||
}
|
||||
deep_schema += R"({"type": "string"})" + std::string(127, '}');
|
||||
test_schema(
|
||||
"127 nested arrays",
|
||||
deep_schema,
|
||||
// Passing strings
|
||||
{
|
||||
std::string(127, '[') + R"("a")" + std::string(127, ']'),
|
||||
},
|
||||
// Failing strings
|
||||
{
|
||||
std::string(126, '[') + R"("a")" + std::string(126, ']'),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
using json = common_json;
|
||||
|
||||
static void test_template(testing & t, const std::string & name, const std::string & tmpl, const json & vars, const std::string & expect);
|
||||
static void test_template_error(testing & t, const std::string & name, const std::string & tmpl, const std::string & expect);
|
||||
|
||||
static void test_whitespace_control(testing & t);
|
||||
static void test_conditionals(testing & t);
|
||||
@@ -606,6 +607,17 @@ static void test_expressions(testing & t) {
|
||||
{{"cond", true}},
|
||||
"yes"
|
||||
);
|
||||
|
||||
test_template(t, "nested parentheses",
|
||||
"{{ " + std::string(30, '(') + "1" + std::string(30, ')') + " }}",
|
||||
json::object(),
|
||||
"1"
|
||||
);
|
||||
|
||||
test_template_error(t, "too deeply nested parentheses",
|
||||
"{{ " + std::string(1000, '(') + "1" + std::string(1000, ')') + " }}",
|
||||
"template is nested more than 128 levels deep"
|
||||
);
|
||||
}
|
||||
|
||||
static void test_set_statement(testing & t) {
|
||||
@@ -1029,6 +1041,11 @@ static void test_filters(testing & t) {
|
||||
{{"x", nullptr}},
|
||||
"None"
|
||||
);
|
||||
|
||||
test_template_error(t, "tojson of a list that contains itself",
|
||||
"{% set arr = [] %}{% set _ = arr.append(arr) %}{{ arr|tojson }}",
|
||||
"value is nested more than 128 levels deep"
|
||||
);
|
||||
}
|
||||
|
||||
static void test_literals(testing & t) {
|
||||
@@ -1291,6 +1308,17 @@ static void test_macros(testing & t) {
|
||||
json::object(),
|
||||
"Dict {\n root1: 1\n root2: {\n nest1: 1\n nest2: {\n nest3: 2\n }\n }\n}"
|
||||
);
|
||||
|
||||
test_template(t, "macro recursion",
|
||||
"{% macro f(n) %}{% if n < 100 %}{{ f(n + 1) }}{% else %}{{ n }}{% endif %}{% endmacro %}{{ f(0) }}",
|
||||
json::object(),
|
||||
"100"
|
||||
);
|
||||
|
||||
test_template_error(t, "endless macro recursion",
|
||||
"{% macro f(n) %}{{ f(n + 1) }}{% endmacro %}{{ f(0) }}",
|
||||
"macro calls are nested more than 128 levels deep"
|
||||
);
|
||||
}
|
||||
|
||||
static void test_namespace(testing & t) {
|
||||
@@ -2554,6 +2582,22 @@ static void test_string_parts(testing & t) {
|
||||
|
||||
}
|
||||
|
||||
static void test_template_error(testing & t, const std::string & name, const std::string & tmpl, const std::string & expect) {
|
||||
t.test(name, [&tmpl, &expect](testing & t) {
|
||||
std::string error;
|
||||
try {
|
||||
jinja::lexer lexer;
|
||||
jinja::program ast = jinja::parse_from_tokens(lexer.tokenize(tmpl));
|
||||
jinja::context ctx(tmpl);
|
||||
jinja::runtime runtime(ctx);
|
||||
runtime.gather_string_parts(runtime.execute(ast));
|
||||
} catch (const std::exception & e) {
|
||||
error = e.what();
|
||||
}
|
||||
t.assert_true("expected error: " + expect + ", got: " + error, error.find(expect) != std::string::npos);
|
||||
});
|
||||
}
|
||||
|
||||
static void test_template_cpp(testing & t, const std::string & name, const std::string & tmpl, const json & vars, const std::string & expect) {
|
||||
t.test(name, [&tmpl, &vars, &expect](testing & t) {
|
||||
jinja::lexer lexer;
|
||||
|
||||
@@ -1513,6 +1513,23 @@ static void test_all(const std::string & title, std::function<void(const TestCas
|
||||
""
|
||||
});
|
||||
|
||||
test({
|
||||
FAILURE,
|
||||
"allOf with a $ref cycle",
|
||||
R"""({
|
||||
"allOf": [{"$ref": "#/$defs/a"}],
|
||||
"$defs": {"a": {"$ref": "#/$defs/a"}}
|
||||
})""",
|
||||
""
|
||||
});
|
||||
|
||||
test({
|
||||
FAILURE,
|
||||
"long property name with additionalProperties",
|
||||
R"""({"type": "object", "properties": {")""" + std::string(1000, 'a') + R"""(": {"type": "string"}}, "additionalProperties": true})""",
|
||||
""
|
||||
});
|
||||
|
||||
// only the property with the bad pattern degrades
|
||||
test({
|
||||
SUCCESS,
|
||||
|
||||
@@ -220,7 +220,7 @@ int main(int argc, char ** argv) {
|
||||
bool list_cases = false;
|
||||
bool dump_logs_on_fail = true;
|
||||
|
||||
int parallel_workers = 1; // 0 = auto, same flag as in test-backend-ops
|
||||
int parallel_workers = 0; // 0 = auto, same flag as in test-backend-ops
|
||||
|
||||
auto parse_parallel_workers = [&](const char * flag, const char * value) {
|
||||
try {
|
||||
@@ -361,12 +361,9 @@ int main(int argc, char ** argv) {
|
||||
|
||||
llama_backend_init();
|
||||
|
||||
// the threads a context runs on, resolved by the common parser - in table mode every worker
|
||||
// builds a context of its own, so the run uses n_pool times this many
|
||||
const int32_t n_threads = params.cpuparams.n_threads;
|
||||
|
||||
// run the full suite for a single model
|
||||
if (models_dir.empty()) {
|
||||
const int32_t n_threads = params.cpuparams.n_threads;
|
||||
LOG("threads: %d\n", n_threads);
|
||||
|
||||
const model_result res = run_model(params.model.path, params, cases, logs_dir,
|
||||
@@ -404,14 +401,14 @@ int main(int argc, char ** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// the dummy models are tiny, but the same flag is meant to run sweeps of real checkpoints,
|
||||
// where every worker holds the weights it loaded - so do not scale with the cores
|
||||
const unsigned n_hw = std::thread::hardware_concurrency();
|
||||
const size_t n_auto = n_hw > 0 ? std::min<size_t>(n_hw, 4) : 1;
|
||||
const unsigned n_hw_2 = std::thread::hardware_concurrency() / 2;
|
||||
const size_t n_auto = std::max<size_t>(1, std::min<size_t>(n_hw_2, 4));
|
||||
const size_t n_pool = parallel_workers > 0
|
||||
? std::min<size_t>((size_t) parallel_workers, models.size())
|
||||
: n_auto;
|
||||
|
||||
const int32_t n_threads = std::max<int32_t>(1, n_hw_2 / n_pool);
|
||||
|
||||
// a column is as wide as its case name, but never narrower than the widest verdict
|
||||
const size_t verdict_width = std::max({ test_status_width(test_status::PASS),
|
||||
test_status_width(test_status::FAIL),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../tools/server/server-common.h"
|
||||
#include "../tools/server/server-decision.h"
|
||||
#include "../tools/server/server-schema.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <regex>
|
||||
@@ -490,6 +491,68 @@ static void test_decision_depth(testing & t) {
|
||||
});
|
||||
}
|
||||
|
||||
// DRY sequence breakers
|
||||
//
|
||||
|
||||
static void test_dry_sequence_breakers(testing & t) {
|
||||
common_params params_base;
|
||||
|
||||
const auto eval = [&](const json & data) {
|
||||
return server_schema::eval_llama_cmpl_schema(nullptr, params_base, {}, data);
|
||||
};
|
||||
|
||||
{
|
||||
json data;
|
||||
data["dry_sequence_breakers"] = json::array({"ab", "ab", "cd", "ab"});
|
||||
const auto params = eval(data);
|
||||
const std::vector<std::string> expected = {"ab", "cd"};
|
||||
t.assert_true("duplicates removed, order kept", params.sampling.dry_sequence_breakers == expected);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> many;
|
||||
for (int i = 0; i <= LLAMA_DRY_MAX_SEQ_BREAKERS; ++i) {
|
||||
many.push_back("b" + std::to_string(i));
|
||||
}
|
||||
json data;
|
||||
data["dry_sequence_breakers"] = many;
|
||||
|
||||
bool thrown = false;
|
||||
try {
|
||||
eval(data);
|
||||
} catch (const std::exception &) {
|
||||
thrown = true;
|
||||
}
|
||||
t.assert_true("more than the cap is rejected", thrown);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// glob_match
|
||||
//
|
||||
|
||||
static void test_glob_match(testing & t) {
|
||||
t.test("patterns", [](testing & t) {
|
||||
t.assert_true("literal", glob_match("src/main.cpp", "src/main.cpp"));
|
||||
t.assert_true("star", glob_match("src/*.cpp", "src/main.cpp"));
|
||||
t.assert_true("star stops at /", !glob_match("*.cpp", "src/main.cpp"));
|
||||
t.assert_true("double star", glob_match("**/*.cpp", "a/b/c/main.cpp"));
|
||||
t.assert_true("double star at end", glob_match("src/**", "src/a/b"));
|
||||
t.assert_true("question mark", glob_match("ma?n.cpp", "main.cpp"));
|
||||
t.assert_true("question mark not /", !glob_match("a?b", "a/b"));
|
||||
t.assert_true("class", glob_match("[a-c]x", "bx"));
|
||||
t.assert_true("negated class", !glob_match("[!a-c]x", "bx"));
|
||||
t.assert_true("unclosed class", glob_match("[ab", "[ab"));
|
||||
t.assert_true("mismatch", !glob_match("main.cpp", "main.c"));
|
||||
});
|
||||
t.test("long input", [](testing & t) {
|
||||
const std::string path(100000, 'a');
|
||||
t.assert_true("long literal", glob_match(path, path));
|
||||
t.assert_true("long path", glob_match("**", path));
|
||||
t.assert_true("too many wildcards", !glob_match(std::string(1000, '*'), "a"));
|
||||
});
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
testing t;
|
||||
|
||||
@@ -511,6 +574,8 @@ int main(int argc, char ** argv) {
|
||||
t.test("decision_escape", test_decision_escape);
|
||||
t.test("rerank_template", test_rerank_template);
|
||||
t.test("decision_depth", test_decision_depth);
|
||||
t.test("glob_match", test_glob_match);
|
||||
t.test("dry_sequence_breakers", test_dry_sequence_breakers);
|
||||
|
||||
return t.summary();
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ These words will not be included in the completion, so make sure to add them to
|
||||
|
||||
`dry_penalty_last_n`: How many tokens to scan for repetitions. Default: `64`, where `0` is disabled.
|
||||
|
||||
`dry_sequence_breakers`: Specify an array of sequence breakers for DRY sampling. Only a JSON array of strings is accepted. Default: `['\n', ':', '"', '*']`
|
||||
`dry_sequence_breakers`: Specify an array of sequence breakers for DRY sampling. Only a JSON array of strings is accepted. At most 64 distinct breakers, duplicates are ignored. Default: `['\n', ':', '"', '*']`
|
||||
|
||||
`xtc_probability`: Set the chance for token removal via XTC sampler. Default: `0.0`, which is disabled.
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "json-schema-to-grammar.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace server_schema {
|
||||
|
||||
//
|
||||
@@ -246,6 +248,23 @@ std::vector<std::unique_ptr<field>> make_llama_cmpl_schema(const common_params &
|
||||
if (ctx.params.sampling.dry_sequence_breakers.empty()) {
|
||||
throw std::runtime_error("Error: dry_sequence_breakers must be a non-empty array of strings");
|
||||
}
|
||||
|
||||
// each distinct breaker costs a full vocabulary scan at sampler init, so dedupe before counting
|
||||
std::unordered_set<std::string> seen;
|
||||
std::vector<std::string> unique_breakers;
|
||||
|
||||
for (const auto & breaker : ctx.params.sampling.dry_sequence_breakers) {
|
||||
if (seen.insert(breaker).second) {
|
||||
unique_breakers.push_back(breaker);
|
||||
}
|
||||
}
|
||||
|
||||
if (unique_breakers.size() > LLAMA_DRY_MAX_SEQ_BREAKERS) {
|
||||
throw std::runtime_error(string_format(
|
||||
"Error: dry_sequence_breakers must contain at most %d distinct entries", LLAMA_DRY_MAX_SEQ_BREAKERS));
|
||||
}
|
||||
|
||||
ctx.params.sampling.dry_sequence_breakers = std::move(unique_breakers);
|
||||
}));
|
||||
|
||||
// handle both "json_schema" and "grammar"
|
||||
|
||||
Reference in New Issue
Block a user