]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
ui: build-time gzip compression (#24571)
authorXuan-Son Nguyen <redacted>
Sat, 13 Jun 2026 14:57:27 +0000 (16:57 +0200)
committerGitHub <redacted>
Sat, 13 Jun 2026 14:57:27 +0000 (16:57 +0200)
* ui: keep original file name and path

* fix nocache

* ui: build-time gzip compression

scripts/ui-assets.cmake
tools/server/server-http.cpp
tools/ui/CMakeLists.txt
tools/ui/embed.cpp

index 89f22b14ac4e2127062a0f21b6db2912c74fe940..78a0f4c844f71deeffdf5e3fa5d37c152520aa4e 100644 (file)
@@ -16,6 +16,7 @@ set(HF_VERSION        "" CACHE STRING "Version to download (empty = resolve from
 set(HF_ENABLED        "" CACHE STRING "Whether to allow HF Bucket download (ON/OFF)")
 set(BUILD_UI          "" CACHE STRING "Build UI via npm (ON/OFF)")
 set(LLAMA_UI_EMBED    "" CACHE STRING "Path to llama-ui-embed helper")
+set(LLAMA_UI_GZIP     "" CACHE STRING "Apply gzip compress to assets to save bandwidth")
 
 set(DIST_DIR     "${UI_BINARY_DIR}/dist")
 set(SRC_DIST_DIR "${UI_SOURCE_DIR}/dist")
@@ -225,6 +226,33 @@ function(hf_download version out_var out_resolved)
 endfunction()
 
 function(emit_files dist_dir)
+    # If gzip is requested, compress every asset into a parallel _gzip/ tree
+    # the structure stays the same; for ex: /abc/def --> /_gzip/abc/def
+    # embed.cpp will check for _gzip and will pick it up
+    if(LLAMA_UI_GZIP AND EXISTS "${dist_dir}/index.html")
+        find_program(GZIP_EXECUTABLE gzip)
+        if(NOT GZIP_EXECUTABLE)
+            message(WARNING "UI: LLAMA_UI_GZIP requested but gzip not found, embedding uncompressed")
+        else()
+            set(gzip_dir "${dist_dir}/_gzip")
+            file(REMOVE_RECURSE "${gzip_dir}")
+            file(GLOB_RECURSE all_files RELATIVE "${dist_dir}" "${dist_dir}/*")
+            foreach(f ${all_files})
+                get_filename_component(dst_dir "${gzip_dir}/${f}" DIRECTORY)
+                file(MAKE_DIRECTORY "${dst_dir}")
+                execute_process(
+                    COMMAND "${GZIP_EXECUTABLE}" -c "${dist_dir}/${f}"
+                    OUTPUT_FILE "${gzip_dir}/${f}"
+                    RESULT_VARIABLE gz_rc
+                )
+                if(NOT gz_rc EQUAL 0)
+                    message(FATAL_ERROR "UI: gzip failed for ${f}")
+                endif()
+            endforeach()
+            message(STATUS "UI: gzip compression applied (${gzip_dir})")
+        endif()
+    endif()
+
     set(args "${UI_CPP}" "${UI_H}")
     if(EXISTS "${dist_dir}/index.html")
         list(APPEND args "${dist_dir}")
index e79f0cbab837ef2a35b018d599ab199f5601cf78..3c58fcfece8949e31f027149f2a4d666b8a0ae0f 100644 (file)
@@ -319,8 +319,26 @@ bool server_http_context::init(const common_params & params) {
             }
         } else {
 #if defined(LLAMA_UI_HAS_ASSETS)
+            static auto handle_gzip_header = [](const httplib::Request & req, httplib::Response & res) {
+                if (!llama_ui_use_gzip()) {
+                    // no gzip build, skip
+                    return true;
+                }
+                if (req.get_header_value("Accept-Encoding").find("gzip") == std::string::npos) {
+                    res.status = 415; // unsupported media type
+                    res.set_content("Error: gzip is not supported by this browser", "text/plain");
+                    return false;
+                } else {
+                    res.set_header("Content-Encoding", "gzip");
+                }
+                return true;
+            };
+
             auto serve_asset_cached = [](const std::string & name, bool isolation) {
                 return [name, isolation](const httplib::Request & req, httplib::Response & res) {
+                    if (!handle_gzip_header(req, res)) {
+                        return true; // returns error message
+                    }
                     const llama_ui_asset * a = llama_ui_find_asset(name);
                     if (!a) { res.status = 404; return false; }
                     res.set_header("ETag", a->etag);
@@ -340,7 +358,10 @@ bool server_http_context::init(const common_params & params) {
             };
 
             auto serve_asset_nocache = [](const std::string & name) {
-                return [name](const httplib::Request & /*req*/, httplib::Response & res) {
+                return [name](const httplib::Request & req, httplib::Response & res) {
+                    if (!handle_gzip_header(req, res)) {
+                        return true; // returns error message
+                    }
                     const llama_ui_asset * a = llama_ui_find_asset(name);
                     if (!a) {
                         res.status = 404;
index abea132286197a7ae7024889f11ca05d33f1b496..74bca417e3728fe0fde167e3b881f9d9ffda071f 100644 (file)
@@ -1,6 +1,7 @@
 set(TARGET llama-ui)
 
 set(LLAMA_UI_HF_BUCKET "ggml-org/llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt UI assets")
+set(LLAMA_UI_GZIP      ON                  CACHE BOOL   "Apply gzip compress to assets to save bandwidth")
 
 # Backward compat: forward old var to new one
 if(DEFINED LLAMA_BUILD_WEBUI)
@@ -83,6 +84,7 @@ add_custom_target(llama-ui-assets ALL
         "-DHF_ENABLED=${LLAMA_USE_PREBUILT_UI}"
         "-DBUILD_UI=${LLAMA_BUILD_UI}"
         "-DLLAMA_UI_EMBED=${LLAMA_UI_EMBED_EXE}"
+        "-DLLAMA_UI_GZIP=${LLAMA_UI_GZIP}"
         -P "${PROJECT_SOURCE_DIR}/scripts/ui-assets.cmake"
     COMMENT "Provisioning UI assets"
     VERBATIM
index 2ca022208f847a15b91d6398621ebb4921e736ba..d4c222f64235bcc011918e324c2ebe7dcd319682 100644 (file)
@@ -1,7 +1,7 @@
 // llama-ui-embed: generate ui.cpp / ui.h that embed UI assets as C arrays.
 //
 // Usage:
-//   llama-ui-embed <out_cpp> <out_h> [<asset_dir>]
+//   llama-ui-embed <out_cpp> <out_h> <asset_dir>
 //
 // Recursively embeds every regular file under <asset_dir>.
 // Asset names are relative paths from <asset_dir> (e.g. "_app/immutable/bundle.HASH.js").
@@ -145,12 +145,15 @@ int main(int argc, char ** argv) {
         return 1;
     }
 
-    const std::string out_cpp = argv[1];
-    const std::string out_h   = argv[2];
-    const std::string in_dir  = argv[3];
+    const std::string out_cpp   = argv[1];
+    const std::string out_h     = argv[2];
+    const std::string asset_dir = argv[3];
+
+    const bool        use_gzip = std::filesystem::exists(asset_dir + "/_gzip");
+    const std::string in_dir   = use_gzip ? (asset_dir + "/_gzip") : asset_dir;
 
     std::vector<asset_entry> assets;
-    if (argc == 4) {
+    if (!in_dir.empty()) {
         const std::filesystem::path dir = in_dir;
 
         std::error_code ec;
@@ -238,7 +241,8 @@ int main(int argc, char ** argv) {
         "    std::string           etag;\n"
         "    std::string           type;\n"
         "};\n\n"
-        "const llama_ui_asset * llama_ui_find_asset(const std::string & name);\n";
+        "const llama_ui_asset * llama_ui_find_asset(const std::string & name);\n"
+        "bool llama_ui_use_gzip();\n";
     h += fmt("const std::array<llama_ui_asset, %d> & llama_ui_get_assets();\n", n_assets);
 
     std::string cpp;
@@ -294,6 +298,7 @@ int main(int argc, char ** argv) {
             "    return empty;\n"
             "}\n";
     }
+    cpp += fmt("bool llama_ui_use_gzip() { return %s; }\n", use_gzip ? "true" : "false");
 
     bool ok = true;
     ok = write_if_different(out_h,   h)   && ok;