Compare commits

...

5 Commits

Author SHA1 Message Date
Bhavik Sharda a035a88878 server: Adding spec-decode counters to /metrics endpoint (#26389)
* * server: add spec-decode counters to /metrics endpoint

* server: fixed review comments and now aligned param names exactly with vLLM.
2026-08-05 12:36:01 +02:00
Andreas Krebbel 020760adfc convert: Add endianness conversion for Q1 and TQ2 quantizations (#26618)
* Add endianness conversion for Q1 and TQ2 quantizations

* lint

---------

Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
2026-08-05 18:06:09 +08:00
Xuan-Son Nguyen 61881b1f7f vendor : apply patches for subprocess.h (#26606) 2026-08-05 11:26:20 +02:00
Aleksander Grygier 3e3a7a416d ui: show generation statistics by default in chat settings (#26624) 2026-08-05 11:03:23 +02:00
Niklas Wenzel d52ec04a66 build : remove GGML_METAL_USE_BF16 from all build scripts (#26604) 2026-08-05 10:44:34 +02:00
14 changed files with 332 additions and 41 deletions
-5
View File
@@ -60,7 +60,6 @@ jobs:
-DCMAKE_BUILD_RPATH="@loader_path" \
-DLLAMA_FATAL_WARNINGS=ON \
-DLLAMA_BUILD_BORINGSSL=ON \
-DGGML_METAL_USE_BF16=ON \
-DGGML_METAL_EMBED_LIBRARY=OFF \
-DGGML_METAL_SHADER_DEBUG=ON \
-DGGML_RPC=ON \
@@ -127,7 +126,6 @@ jobs:
run: |
sysctl -a
cmake -B build -G Xcode \
-DGGML_METAL_USE_BF16=ON \
-DGGML_METAL_EMBED_LIBRARY=ON \
-DLLAMA_OPENSSL=OFF \
-DLLAMA_BUILD_APP=OFF \
@@ -178,7 +176,6 @@ jobs:
run: |
sysctl -a
cmake -B build -G Xcode \
-DGGML_METAL_USE_BF16=ON \
-DGGML_METAL_EMBED_LIBRARY=ON \
-DLLAMA_BUILD_COMMON=OFF \
-DLLAMA_BUILD_APP=OFF \
@@ -212,7 +209,6 @@ jobs:
run: |
sysctl -a
cmake -B build -G Xcode \
-DGGML_METAL_USE_BF16=ON \
-DGGML_METAL_EMBED_LIBRARY=ON \
-DLLAMA_BUILD_COMMON=OFF \
-DLLAMA_BUILD_APP=OFF \
@@ -257,7 +253,6 @@ jobs:
run: |
sysctl -a
cmake -B build -G Xcode \
-DGGML_METAL_USE_BF16=ON \
-DGGML_METAL_EMBED_LIBRARY=ON \
-DLLAMA_OPENSSL=OFF \
-DLLAMA_BUILD_APP=OFF \
+2 -3
View File
@@ -93,13 +93,13 @@ jobs:
- build: 'arm64'
arch: 'arm64'
os: macos-26
defines: "-DGGML_METAL_USE_BF16=ON -DGGML_METAL_EMBED_LIBRARY=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3"
defines: "-DGGML_METAL_EMBED_LIBRARY=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3"
# TODO: this build is disabled to save Github Actions resources (https://github.com/ggml-org/llama.cpp/pull/23780)
# in order to enable it again, we have to provision dedicated runners to run it
#- build: 'arm64-kleidiai'
# arch: 'arm64'
# os: macos-14
# defines: "-DGGML_METAL_USE_BF16=ON -DGGML_METAL_EMBED_LIBRARY=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3 -DGGML_CPU_KLEIDIAI=ON"
# defines: "-DGGML_METAL_EMBED_LIBRARY=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3 -DGGML_CPU_KLEIDIAI=ON"
- build: 'x64'
arch: 'x64'
os: macos-15-intel
@@ -1402,7 +1402,6 @@ jobs:
run: |
sysctl -a
cmake -B build -G Xcode \
-DGGML_METAL_USE_BF16=ON \
-DGGML_METAL_EMBED_LIBRARY=ON \
-DLLAMA_OPENSSL=OFF \
-DLLAMA_BUILD_APP=OFF \
-2
View File
@@ -17,7 +17,6 @@ LLAMA_BUILD_MTMD=ON
GGML_METAL=ON
GGML_METAL_EMBED_LIBRARY=ON
GGML_BLAS_DEFAULT=ON
GGML_METAL_USE_BF16=ON
GGML_OPENMP=OFF
COMMON_C_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g"
@@ -44,7 +43,6 @@ COMMON_CMAKE_ARGS=(
-DGGML_METAL_EMBED_LIBRARY=${GGML_METAL_EMBED_LIBRARY}
-DGGML_BLAS_DEFAULT=${GGML_BLAS_DEFAULT}
-DGGML_METAL=${GGML_METAL}
-DGGML_METAL_USE_BF16=${GGML_METAL_USE_BF16}
-DGGML_NATIVE=OFF
-DGGML_OPENMP=${GGML_OPENMP}
)
@@ -59,11 +59,29 @@ def byteswap_q6_k(tensor, block_offs):
delta.byteswap(inplace=True)
def byteswap_q1_0(tensor, block_offs):
# Each block_q1_0 consists of an f16 delta followed by 16 int8 quantizations.
# Byte-Swap f16 sized delta field
delta = tensor.data[block_offs:block_offs + 2].view(dtype=np.uint16)
delta.byteswap(inplace=True)
def byteswap_tq2_0(tensor, block_offs):
# Each block_tq2_0 consists of 64 int8 values followed by 1 f16 value.
# Byte-Swap f16 sized field
delta = tensor.data[block_offs + 64:block_offs + 66].view(dtype=np.uint16)
delta.byteswap(inplace=True)
byteswap_tensors = {
gguf.GGMLQuantizationType.Q1_0: byteswap_q1_0,
gguf.GGMLQuantizationType.Q4_0: byteswap_q4_0,
gguf.GGMLQuantizationType.Q8_0: byteswap_q8_0,
gguf.GGMLQuantizationType.Q4_K: byteswap_q4_k,
gguf.GGMLQuantizationType.Q6_K: byteswap_q6_k,
gguf.GGMLQuantizationType.TQ2_0: byteswap_tq2_0,
gguf.GGMLQuantizationType.MXFP4: byteswap_noop,
gguf.GGMLQuantizationType.NVFP4: byteswap_noop,
}
+21
View File
@@ -24,10 +24,31 @@ vendor = {
"https://raw.githubusercontent.com/sheredom/subprocess.h/8671cee1fc09f11a70ce3782a0ee13177c3aa387/subprocess.h": "vendor/sheredom/subprocess.h",
}
# TODO @ngxson : this is temporary, to be removed in the future
patches = [
# https://github.com/sheredom/subprocess.h/pull/102
"vendor/sheredom/patch-bsd.patch",
# https://github.com/sheredom/subprocess.h/pull/101
"vendor/sheredom/patch-windows-quote-backslash.patch",
# https://github.com/sheredom/subprocess.h/pull/104
# note: must be applied after patch-bsd.patch, they touch adjacent lines
"vendor/sheredom/patch-glibc-older-than-2.29.patch",
]
for url, filename in vendor.items():
print(f"downloading {url} to {filename}") # noqa: NP100
urllib.request.urlretrieve(url, filename)
for patch in patches:
print(f"applying {patch}") # noqa: NP100
try:
subprocess.check_call([
"git", "apply", "--directory", os.path.dirname(patch), patch
])
except Exception as e:
print(f"Error: {e}") # noqa: NP100
sys.exit(1)
print("Splitting httplib.h...") # noqa: NP100
try:
subprocess.check_call([
+4
View File
@@ -1076,6 +1076,10 @@ In *router mode* the query param `?model={model_id}` has to be set. This endpoin
| `llamacpp:n_tokens_max` | Counter | High watermark of the context size observed. |
| `llamacpp:n_decode_total` | Counter | Total Number of llama_decode() calls. |
| `llamacpp:n_busy_slots_per_decode` | Gauge | Average number of busy slots per llama_decode() call. |
| `llamacpp:spec_decode_num_draft_tokens_total` | Counter | Total draft tokens generated (0 when spec-decode is off). |
| `llamacpp:spec_decode_num_accepted_tokens_total` | Counter | Total draft tokens accepted by the target model (0 when spec-decode is off). |
| `llamacpp:spec_decode_num_drafts_total` | Counter | Total speculative decoding verification steps (0 when spec-decode is off). |
| `llamacpp:spec_decode_num_accepted_tokens_per_pos_total` | Counter | Accepted tokens per draft position (labeled `position="N"`; absent when spec-decode is off or before the first completed speculative request). |
### POST `/slots/{id_slot}?action=save`: Save the prompt cache of the specified slot to a file.
+44
View File
@@ -845,6 +845,11 @@ struct server_metrics {
uint64_t n_decode_total = 0;
uint64_t n_busy_slots_total = 0;
uint64_t n_draft_tokens_total = 0;
uint64_t n_draft_accepted_total = 0;
uint64_t n_draft_verif_steps_total = 0;
std::vector<uint64_t> n_accepted_per_pos_total;
void init() {
t_start = ggml_time_us();
}
@@ -863,6 +868,17 @@ struct server_metrics {
n_tokens_predicted += slot.n_decoded;
t_tokens_generation += slot.t_token_generation;
t_tokens_generation_total += slot.t_token_generation;
n_draft_tokens_total += slot.n_draft_total;
n_draft_accepted_total += slot.n_draft_accepted;
n_draft_verif_steps_total += slot.n_draft_verif_steps;
if (n_accepted_per_pos_total.size() < slot.n_accepted_per_pos.size()) {
n_accepted_per_pos_total.resize(slot.n_accepted_per_pos.size(), 0);
}
for (size_t i = 0; i < slot.n_accepted_per_pos.size(); i++) {
n_accepted_per_pos_total[i] += slot.n_accepted_per_pos[i];
}
}
void on_decoded(const std::vector<server_slot> & slots) {
@@ -2552,6 +2568,11 @@ private:
res->n_decode_total = metrics.n_decode_total;
res->n_busy_slots_total = metrics.n_busy_slots_total;
res->n_draft_tokens_total = metrics.n_draft_tokens_total;
res->n_draft_accepted_total = metrics.n_draft_accepted_total;
res->n_draft_verif_steps_total = metrics.n_draft_verif_steps_total;
res->n_accepted_per_pos_total = metrics.n_accepted_per_pos_total;
if (task.metrics_reset_bucket) {
metrics.reset_bucket();
}
@@ -4440,6 +4461,18 @@ void server_routes::init_routes() {
{"name", "n_tokens_max"},
{"help", "Largest observed n_tokens."},
{"value", res_task->n_tokens_max}
}, {
{"name", "spec_decode_num_draft_tokens_total"},
{"help", "Total draft tokens generated"},
{"value", res_task->n_draft_tokens_total}
}, {
{"name", "spec_decode_num_accepted_tokens_total"},
{"help", "Total draft tokens accepted by the target model"},
{"value", res_task->n_draft_accepted_total}
}, {
{"name", "spec_decode_num_drafts_total"},
{"help", "Total speculative decoding verification steps"},
{"value", res_task->n_draft_verif_steps_total}
}}},
{"gauge", {{
{"name", "prompt_tokens_seconds"},
@@ -4481,6 +4514,17 @@ void server_routes::init_routes() {
}
}
// labeled counter: one time series per draft position
if (!res_task->n_accepted_per_pos_total.empty()) {
prometheus << "# HELP llamacpp:spec_decode_num_accepted_tokens_per_pos_total"
" Accepted tokens per draft position\n"
<< "# TYPE llamacpp:spec_decode_num_accepted_tokens_per_pos_total counter\n";
for (size_t i = 0; i < res_task->n_accepted_per_pos_total.size(); i++) {
prometheus << "llamacpp:spec_decode_num_accepted_tokens_per_pos_total{position=\""
<< i << "\"} " << res_task->n_accepted_per_pos_total[i] << "\n";
}
}
res->headers["Process-Start-Time-Unix"] = std::to_string(res_task->t_start);
res->content_type = "text/plain; version=0.0.4";
res->status = 200;
+5
View File
@@ -1560,6 +1560,11 @@ json server_task_result_metrics::to_json() {
{ "n_decode_total", n_decode_total },
{ "n_busy_slots_total", n_busy_slots_total },
{ "n_draft_tokens_total", n_draft_tokens_total },
{ "n_draft_accepted_total", n_draft_accepted_total },
{ "n_draft_verif_steps_total", n_draft_verif_steps_total },
{ "n_accepted_per_pos_total", n_accepted_per_pos_total },
{ "slots", slots_data },
};
}
+5
View File
@@ -532,6 +532,11 @@ struct server_task_result_metrics : server_task_result {
uint64_t n_decode_total = 0;
uint64_t n_busy_slots_total = 0;
uint64_t n_draft_tokens_total = 0;
uint64_t n_draft_accepted_total = 0;
uint64_t n_draft_verif_steps_total = 0;
std::vector<uint64_t> n_accepted_per_pos_total;
// while we can also use std::vector<server_slot> this requires copying the slot object which can be quite messy
// therefore, we use json to temporarily store the slot.to_json() result
json slots_data = json::array();
@@ -198,7 +198,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
key: SETTINGS_KEYS.SHOW_MESSAGE_STATS,
label: 'Show message generation statistics',
help: 'Display generation statistics (tokens/second, token count, duration) below each assistant message.',
defaultValue: false,
defaultValue: true,
type: SettingsFieldType.CHECKBOX,
section: SETTINGS_SECTION_SLUGS.DISPLAY
},
+19
View File
@@ -0,0 +1,19 @@
Fix build on NetBSD, which provides posix_spawn_file_actions_addchdir()
but not the _np() variant.
Upstream PR: https://github.com/sheredom/subprocess.h/pull/102
Applied locally by scripts/sync_vendor.py until it is merged upstream.
diff --git a/subprocess.h b/subprocess.h
index 5e809023a4..74a4e006c7 100644
--- a/subprocess.h
+++ b/subprocess.h
@@ -1205,7 +1205,7 @@ cleanup:
// Set working directory
if (process_cwd) {
-#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000
+#if defined(__NetBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000)
posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
#else
#if defined(__APPLE__) && defined(__clang__)
+47
View File
@@ -0,0 +1,47 @@
Fix building against glibc older than 2.29, which has no
posix_spawn_file_actions_addchdir_np (the symbol is genuinely absent from
libc.so, so no feature-test macro helps). Affects manylinux2014 (glibc 2.17)
and manylinux_2_28, and was reported on RHEL 8.1. A requested process_cwd now
fails with ENOSYS there instead of failing the build.
Upstream PR: https://github.com/sheredom/subprocess.h/pull/104
Applied locally by scripts/sync_vendor.py until it is merged upstream.
(the README.md and test/ changes from the PR are omitted, we only vendor
subprocess.h; rebased on top of patch-bsd.patch, so apply it after that one)
diff --git a/subprocess.h b/subprocess.h
index 1ef424a..c363393 100644
--- a/subprocess.h
+++ b/subprocess.h
@@ -274,6 +274,21 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
#include <unistd.h>
#endif
+/* Whether subprocess_create_ex can honour process_cwd. glibc only gained
+ posix_spawn_file_actions_addchdir_np in 2.29. Define this yourself to
+ override the detection, for instance on musl older than 1.1.24. */
+#if !defined(SUBPROCESS_HAVE_CWD)
+#if defined(__GLIBC__)
+#if __GLIBC_PREREQ(2, 29)
+#define SUBPROCESS_HAVE_CWD 1
+#else
+#define SUBPROCESS_HAVE_CWD 0
+#endif
+#else
+#define SUBPROCESS_HAVE_CWD 1
+#endif
+#endif
+
#if defined(_WIN32)
#include <wchar.h>
@@ -1219,6 +1234,8 @@ cleanup:
if (process_cwd) {
#if defined(__NetBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000)
posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
+#elif !SUBPROCESS_HAVE_CWD
+ posix_error = ENOSYS;
#else
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic push
+107
View File
@@ -0,0 +1,107 @@
Fix Windows command line quoting of backslash runs: a trailing backslash, or
backslashes preceding a double quote, were not doubled, so CommandLineToArgvW
in the child parsed them as escapes and mangled the argument list.
Upstream PR: https://github.com/sheredom/subprocess.h/pull/101
Applied locally by scripts/sync_vendor.py until it is merged upstream.
(the test/ changes from the PR are omitted, we only vendor subprocess.h)
diff --git a/subprocess.h b/subprocess.h
index 5e80902..b06ad4d 100644
--- a/subprocess.h
+++ b/subprocess.h
@@ -653,6 +653,7 @@ int subprocess_create_ex(const char *const commandLine[], int options,
int wide_len;
int i, j;
int need_quoting;
+ subprocess_size_t bs_run;
unsigned long flags = 0;
unsigned long last_error = 0;
int result = subprocess_error_unknown;
@@ -906,25 +907,29 @@ int subprocess_create_ex(const char *const commandLine[], int options,
len++;
// Quote the argument if it has a space in it
- if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
- commandLine[i][0] == SUBPROCESS_NULL)
+ need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
+ commandLine[i][0] == SUBPROCESS_NULL;
+ if (need_quoting)
len += 2;
+ bs_run = 0;
for (j = 0; '\0' != commandLine[i][j]; j++) {
- switch (commandLine[i][j]) {
- default:
- break;
- case '\\':
- if (commandLine[i][j + 1] == '"') {
- len++;
- }
+ len++;
- break;
- case '"':
- len++;
- break;
+ if ('\\' == commandLine[i][j]) {
+ bs_run++;
+ } else {
+ if ('"' == commandLine[i][j]) {
+ // Duplicate the preceding run and escape the quote.
+ len += bs_run + 1;
+ }
+ bs_run = 0;
}
- len++;
+ }
+
+ if (need_quoting) {
+ // Duplicate trailing slashes before the generated closing quote.
+ len += bs_run;
}
}
@@ -949,22 +954,29 @@ int subprocess_create_ex(const char *const commandLine[], int options,
commandLineCombined[len++] = '"';
}
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- switch (commandLine[i][j]) {
- default:
- break;
- case '\\':
- if (commandLine[i][j + 1] == '"') {
- commandLineCombined[len++] = '\\';
- }
+ for (j = 0; '\0' != commandLine[i][j];) {
+ bs_run = 0;
+ while ('\\' == commandLine[i][j]) {
+ bs_run++;
+ j++;
+ }
+
+ if ('"' == commandLine[i][j]) {
+ // 2n + 1 slashes preserve n slashes and escape the quote.
+ bs_run = (bs_run * 2) + 1;
+ } else if ('\0' == commandLine[i][j] && need_quoting) {
+ // 2n slashes preserve n slashes before the closing quote.
+ bs_run *= 2;
+ }
- break;
- case '"':
+ while (bs_run > 0) {
commandLineCombined[len++] = '\\';
- break;
+ bs_run--;
}
- commandLineCombined[len++] = commandLine[i][j];
+ if ('\0' != commandLine[i][j]) {
+ commandLineCombined[len++] = commandLine[i][j++];
+ }
}
if (need_quoting) {
commandLineCombined[len++] = '"';
+59 -30
View File
@@ -274,6 +274,21 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
#include <unistd.h>
#endif
/* Whether subprocess_create_ex can honour process_cwd. glibc only gained
posix_spawn_file_actions_addchdir_np in 2.29. Define this yourself to
override the detection, for instance on musl older than 1.1.24. */
#if !defined(SUBPROCESS_HAVE_CWD)
#if defined(__GLIBC__)
#if __GLIBC_PREREQ(2, 29)
#define SUBPROCESS_HAVE_CWD 1
#else
#define SUBPROCESS_HAVE_CWD 0
#endif
#else
#define SUBPROCESS_HAVE_CWD 1
#endif
#endif
#if defined(_WIN32)
#include <wchar.h>
@@ -653,6 +668,7 @@ int subprocess_create_ex(const char *const commandLine[], int options,
int wide_len;
int i, j;
int need_quoting;
subprocess_size_t bs_run;
unsigned long flags = 0;
unsigned long last_error = 0;
int result = subprocess_error_unknown;
@@ -906,25 +922,29 @@ int subprocess_create_ex(const char *const commandLine[], int options,
len++;
// Quote the argument if it has a space in it
if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
commandLine[i][0] == SUBPROCESS_NULL)
need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
commandLine[i][0] == SUBPROCESS_NULL;
if (need_quoting)
len += 2;
bs_run = 0;
for (j = 0; '\0' != commandLine[i][j]; j++) {
switch (commandLine[i][j]) {
default:
break;
case '\\':
if (commandLine[i][j + 1] == '"') {
len++;
}
break;
case '"':
len++;
break;
}
len++;
if ('\\' == commandLine[i][j]) {
bs_run++;
} else {
if ('"' == commandLine[i][j]) {
// Duplicate the preceding run and escape the quote.
len += bs_run + 1;
}
bs_run = 0;
}
}
if (need_quoting) {
// Duplicate trailing slashes before the generated closing quote.
len += bs_run;
}
}
@@ -949,22 +969,29 @@ int subprocess_create_ex(const char *const commandLine[], int options,
commandLineCombined[len++] = '"';
}
for (j = 0; '\0' != commandLine[i][j]; j++) {
switch (commandLine[i][j]) {
default:
break;
case '\\':
if (commandLine[i][j + 1] == '"') {
commandLineCombined[len++] = '\\';
}
break;
case '"':
commandLineCombined[len++] = '\\';
break;
for (j = 0; '\0' != commandLine[i][j];) {
bs_run = 0;
while ('\\' == commandLine[i][j]) {
bs_run++;
j++;
}
commandLineCombined[len++] = commandLine[i][j];
if ('"' == commandLine[i][j]) {
// 2n + 1 slashes preserve n slashes and escape the quote.
bs_run = (bs_run * 2) + 1;
} else if ('\0' == commandLine[i][j] && need_quoting) {
// 2n slashes preserve n slashes before the closing quote.
bs_run *= 2;
}
while (bs_run > 0) {
commandLineCombined[len++] = '\\';
bs_run--;
}
if ('\0' != commandLine[i][j]) {
commandLineCombined[len++] = commandLine[i][j++];
}
}
if (need_quoting) {
commandLineCombined[len++] = '"';
@@ -1205,8 +1232,10 @@ cleanup:
// Set working directory
if (process_cwd) {
#if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000
#if defined(__NetBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000)
posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
#elif !SUBPROCESS_HAVE_CWD
posix_error = ENOSYS;
#else
#if defined(__APPLE__) && defined(__clang__)
#pragma clang diagnostic push