Compare commits

...

4 Commits

Author SHA1 Message Date
Xuan Son Nguyen d20a821181 server: (router) do not evict busy models 2026-08-05 11:36:14 +02: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
11 changed files with 288 additions and 51 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}
)
+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([
+23 -8
View File
@@ -721,7 +721,9 @@ void server_models::unload_lru() {
for (const auto & m : mapping) {
if (m.second.meta.is_running()) {
count_active++;
if (m.second.meta.last_used < lru_last_used) {
// do not evict busy one
bool is_model_idle = m.second.req_count == 0 && m.second.meta.is_ready_or_sleep();
if (is_model_idle && m.second.meta.last_used < lru_last_used) {
lru_model_name = m.first;
lru_last_used = m.second.meta.last_used;
}
@@ -739,6 +741,7 @@ void server_models::unload_lru() {
});
}
}
// TODO @ngxson : if no idle model is found, queue the load request
}
void server_models::load(const std::string & name) {
@@ -1180,9 +1183,12 @@ server_http_res_ptr server_models::proxy_request(const server_http_req & req, co
if (!meta->is_running()) {
throw std::invalid_argument("model name=" + name + " is not running");
}
if (update_last_used) {
{
std::unique_lock<std::mutex> lk(mutex);
mapping[name].meta.last_used = ggml_time_ms();
if (update_last_used) {
mapping[name].meta.last_used = ggml_time_ms();
}
mapping[name].req_count++;
}
SRV_INF("proxying request to model %s on port %d\n", name.c_str(), meta->port);
std::string proxy_path = req.path;
@@ -1198,13 +1204,22 @@ server_http_res_ptr server_models::proxy_request(const server_http_req & req, co
req.headers,
req.body,
req.files,
// a detached request belongs to a replay session that outlives the client socket:
// it reaches the child even when the downstream died during the load wait, the
// session buffer is the recipient and DELETE remains the stop
detached ? std::function<bool()>([]() { return false; }) : req.should_stop,
// a detached request belongs to a replay session
detached
? std::function<bool()>([]() { return false; })
: req.should_stop,
base_params.timeout_read,
base_params.timeout_write
);
proxy->cleanup = [this, name]() {
std::unique_lock<std::mutex> lk(mutex);
auto it = mapping.find(name);
if (it != mapping.end() && it->second.req_count > 0) {
it->second.req_count--;
}
};
return proxy;
}
@@ -2064,7 +2079,7 @@ server_http_proxy::server_http_proxy(
cli->set_write_timeout(timeout_read, 0); // reversed for cli (client) vs srv (server)
cli->set_read_timeout(timeout_write, 0);
this->status = 500; // to be overwritten upon response
this->cleanup = [pipe]() {
this->cleanup_pipes = [pipe]() {
pipe->close_read();
pipe->close_write();
};
+9 -2
View File
@@ -84,7 +84,6 @@ struct server_model_meta {
int exit_code = 0; // exit code of the model instance process (only valid if status == FAILED)
int stop_timeout = 0; // seconds to wait before force-killing the model instance during shutdown
mtmd_caps multimodal; // multimodal capabilities
// bool need_download = false; // whether the model needs to be downloaded before loading // TODO @ngxson: implement this
bool is_ready() const {
return status == SERVER_MODEL_STATUS_LOADED;
@@ -94,6 +93,10 @@ struct server_model_meta {
return status == SERVER_MODEL_STATUS_LOADED || status == SERVER_MODEL_STATUS_LOADING || status == SERVER_MODEL_STATUS_SLEEPING;
}
bool is_ready_or_sleep() const {
return status == SERVER_MODEL_STATUS_LOADED || status == SERVER_MODEL_STATUS_SLEEPING;
}
bool is_failed() const {
return status == SERVER_MODEL_STATUS_UNLOADED && exit_code != 0;
}
@@ -113,6 +116,7 @@ private:
std::shared_ptr<server_subproc> subproc; // shared between main thread and monitoring thread
std::thread th;
server_model_meta meta;
int req_count = 0; // number of active proxy requests
};
std::mutex mutex;
@@ -343,7 +347,6 @@ struct server_models_routes {
*/
struct server_http_proxy : server_http_res {
std::function<void()> cleanup = nullptr;
public:
server_http_proxy(const std::string & method,
const std::string & scheme,
const std::string & host,
@@ -357,11 +360,15 @@ public:
int32_t timeout_write
);
~server_http_proxy() {
if (cleanup_pipes) {
cleanup_pipes();
}
if (cleanup) {
cleanup();
}
}
private:
std::function<void()> cleanup_pipes = nullptr;
std::thread thread;
struct msg_t {
std::map<std::string, std::string> headers;
@@ -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