ui: Restructure repo to use tools/ui folder and ui / UI / llama-ui / LLAMA_UI naming (#23064)
* webui: Move static build output from `tools/server/public` to `build/ui` directory * refactor: Move to `tools/ui` * refactor: rename CMake variables and preprocessor defines - Rename LLAMA_BUILD_WEBUI -> LLAMA_BUILD_UI (old kept as deprecated) - Rename LLAMA_USE_PREBUILT_WEBUI -> LLAMA_USE_PREBUILT_UI (old kept as deprecated) - Backward compat: old vars auto-forward to new ones with DEPRECATION warning - Rename internal vars: WEBUI_SOURCE -> UI_SOURCE, WEBUI_SOURCE_DIR -> UI_SOURCE_DIR, etc. - Rename HF bucket: LLAMA_WEBUI_HF_BUCKET -> LLAMA_UI_HF_BUCKET - Emit both LLAMA_BUILD_WEBUI and LLAMA_BUILD_UI preprocessor defines - Emit both LLAMA_WEBUI_DEFAULT_ENABLED and LLAMA_UI_DEFAULT_ENABLED * refactor: rename CLI flags (--webui -> --ui) with backward compat - Add --ui/--no-ui (old --webui/--no-webui kept as deprecated aliases) - Add --ui-config (old --webui-config kept as deprecated alias) - Add --ui-config-file (old --webui-config-file kept as deprecated alias) - Add --ui-mcp-proxy/--no-ui-mcp-proxy (old --webui-mcp-proxy kept as deprecated) - Add new env vars: LLAMA_ARG_UI, LLAMA_ARG_UI_CONFIG, LLAMA_ARG_UI_CONFIG_FILE, LLAMA_ARG_UI_MCP_PROXY - C++ struct fields: params.ui, params.ui_config_json, params.ui_mcp_proxy added alongside old fields - Backward compat: old fields synced to new ones in g_params_to_internals * refactor: update C++ server internals with backward compat - Rename json_webui_settings -> json_ui_settings (both kept in server_context_meta) - Rename params.webui usage -> params.ui (both synced, old still works) - JSON API emits both "ui"/"ui_settings" and "webui"/"webui_settings" keys - Server routes use params.ui_mcp_proxy || params.webui_mcp_proxy - Preprocessor guards use #if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI) * refactor: rename CI/CD workflows, artifacts, and build script - Rename webui-build.yml -> ui-build.yml; artifact webui-build -> ui-build - Rename webui-publish.yml -> ui-publish.yml; var HF_BUCKET_WEBUI_STATIC_OUTPUT -> HF_BUCKET_UI_STATIC_OUTPUT - Rename server-webui.yml -> server-ui.yml; job webui-build/checks -> ui-build/checks - Update server.yml: job/artifact refs webui-build -> ui-build - Update release.yml: all webui-build/publish refs -> ui-build/publish; HF_TOKEN_WEBUI_STATIC_OUTPUT -> HF_TOKEN_UI_STATIC_OUTPUT - Update server-self-hosted.yml: webui-build -> ui-build - Update build-self-hosted.yml: HF_WEBUI_VERSION -> HF_UI_VERSION - Rename webui-download.cmake -> ui-download.cmake (internal refs updated) - Update labeler.yml: server/webui -> server/ui path label * docs: update CODEOWNERS and server README docs - Update CODEOWNERS: team ggml-org/llama-webui -> ggml-org/llama-ui, path /tools/server/webui/ -> /tools/ui/ - Update server README.md: CLI tables show --ui flags with deprecated --webui aliases - Update server README-dev.md: "WebUI" -> "UI", paths updated to tools/ui/ * fix: Small fixes for UI build * fix: CMake.txt syntax * chore: Formatting * fix: `.editorconfig` for llama-ui * chore: Formatting * refactor: Use `APP_NAME` in Error route * refactor: Cleanup * refactor: Single migration service * make llama-ui a linkable target * fix: UI Build output * fix: Missing change * fix: separate llama-ui npm build output into build/tools/ui/dist subfolder + use cmake npm build instead of downloading ui-build.yml artifacts in CI * refactor: UI workflows cleanup --------- Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
This commit is contained in:
committed by
GitHub
parent
49d1701bd2
commit
59778f0196
@@ -22,6 +22,7 @@ else()
|
||||
add_subdirectory(perplexity)
|
||||
add_subdirectory(quantize)
|
||||
if (LLAMA_BUILD_SERVER)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(server)
|
||||
endif()
|
||||
|
||||
+1
-126
@@ -40,136 +40,11 @@ set(TARGET_SRCS
|
||||
server-models.h
|
||||
)
|
||||
|
||||
# Option to specify custom HF bucket for webui (defaults to llama-ui)
|
||||
# Usage: cmake -B build -DLLAMA_WEBUI_HF_BUCKET=llama-ui
|
||||
set(LLAMA_WEBUI_HF_BUCKET "llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt webui assets")
|
||||
|
||||
if (LLAMA_BUILD_WEBUI)
|
||||
set(PUBLIC_ASSETS
|
||||
index.html
|
||||
bundle.js
|
||||
bundle.css
|
||||
loading.html
|
||||
)
|
||||
|
||||
# Determine source of webui assets (priority: local > HF Bucket)
|
||||
set(WEBUI_SOURCE "")
|
||||
set(WEBUI_SOURCE_DIR "")
|
||||
|
||||
# Priority 1: Check for local webui build output
|
||||
set(LOCAL_WEBUI_DIR "${CMAKE_CURRENT_SOURCE_DIR}/public")
|
||||
|
||||
# Verify all required assets exist before declaring local source valid
|
||||
set(ALL_ASSETS_PRESENT TRUE)
|
||||
foreach(asset ${PUBLIC_ASSETS})
|
||||
if(NOT EXISTS "${LOCAL_WEBUI_DIR}/${asset}")
|
||||
set(ALL_ASSETS_PRESENT FALSE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(ALL_ASSETS_PRESENT)
|
||||
set(WEBUI_SOURCE "local")
|
||||
set(WEBUI_SOURCE_DIR "${LOCAL_WEBUI_DIR}")
|
||||
message(STATUS "WebUI: using local build from ${WEBUI_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
# Priority 2: Build-time asset provisioning (npm build → HF Bucket fallback)
|
||||
if(NOT WEBUI_SOURCE_DIR)
|
||||
# Environment variable takes precedence (e.g., from CI workflows)
|
||||
if(DEFINED ENV{HF_WEBUI_VERSION})
|
||||
set(HF_WEBUI_VERSION "$ENV{HF_WEBUI_VERSION}")
|
||||
# Validate against allowed characters to prevent CMake list separator
|
||||
# or path-traversal issues in stamp filenames and download URLs
|
||||
if(NOT HF_WEBUI_VERSION MATCHES "^[A-Za-z0-9._-]+$")
|
||||
message(FATAL_ERROR "WebUI: invalid HF_WEBUI_VERSION='${HF_WEBUI_VERSION}' - must match ^[A-Za-z0-9._-]+$")
|
||||
endif()
|
||||
message(STATUS "WebUI: using HF_WEBUI_VERSION from environment=${HF_WEBUI_VERSION}")
|
||||
elseif(DEFINED LLAMA_BUILD_NUMBER)
|
||||
set(HF_WEBUI_VERSION "b${LLAMA_BUILD_NUMBER}")
|
||||
message(STATUS "WebUI: using LLAMA_BUILD_NUMBER=${HF_WEBUI_VERSION}")
|
||||
else()
|
||||
set(HF_WEBUI_VERSION "")
|
||||
message(STATUS "WebUI: version not specified (will use HF 'latest')")
|
||||
endif()
|
||||
|
||||
# Stamp file embeds the version tag so a changed build number triggers
|
||||
# a fresh provision run on the next `cmake --build` without reconfiguring.
|
||||
if("${HF_WEBUI_VERSION}" STREQUAL "")
|
||||
set(WEBUI_VERSION_TAG "provisioned")
|
||||
else()
|
||||
set(WEBUI_VERSION_TAG "${HF_WEBUI_VERSION}")
|
||||
endif()
|
||||
set(WEBUI_STAMP "${CMAKE_CURRENT_BINARY_DIR}/.webui-${WEBUI_VERSION_TAG}.stamp")
|
||||
|
||||
# Join assets with + separator (safe across all platforms, unlike ; and |)
|
||||
string(REPLACE ";" "+" PUBLIC_ASSETS_JOINED "${PUBLIC_ASSETS}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WEBUI_STAMP}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-DSOURCE_DIR=${PROJECT_SOURCE_DIR}"
|
||||
"-DPUBLIC_DIR=${CMAKE_CURRENT_SOURCE_DIR}/public"
|
||||
"-DHF_BUCKET=${LLAMA_WEBUI_HF_BUCKET}"
|
||||
"-DHF_VERSION=${HF_WEBUI_VERSION}"
|
||||
"-DHF_ENABLED=${LLAMA_USE_PREBUILT_WEBUI}"
|
||||
"-DASSETS=${PUBLIC_ASSETS_JOINED}"
|
||||
"-DSTAMP_FILE=${WEBUI_STAMP}"
|
||||
"-DNPM_DIR=${CMAKE_CURRENT_SOURCE_DIR}/webui"
|
||||
-P ${PROJECT_SOURCE_DIR}/scripts/webui-download.cmake
|
||||
COMMENT "Building/provisioning WebUI assets (npm build -> HF Bucket fallback)"
|
||||
)
|
||||
|
||||
set(WEBUI_SOURCE "provisioned")
|
||||
set(WEBUI_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/public")
|
||||
endif()
|
||||
|
||||
# Process assets from the determined source
|
||||
if(WEBUI_SOURCE_DIR)
|
||||
foreach(asset ${PUBLIC_ASSETS})
|
||||
set(input "${WEBUI_SOURCE_DIR}/${asset}")
|
||||
set(output "${CMAKE_CURRENT_BINARY_DIR}/${asset}.hpp")
|
||||
list(APPEND TARGET_SRCS ${output})
|
||||
|
||||
if(WEBUI_SOURCE STREQUAL "local")
|
||||
# Local build: files exist at configure time
|
||||
if(NOT EXISTS "${input}")
|
||||
message(FATAL_ERROR "WebUI asset not found: ${input}")
|
||||
endif()
|
||||
set(dependency "${input}")
|
||||
else()
|
||||
# HF Bucket: files are downloaded at build time
|
||||
set(dependency "${WEBUI_STAMP}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
DEPENDS ${dependency}
|
||||
OUTPUT "${output}"
|
||||
COMMAND "${CMAKE_COMMAND}" "-DINPUT=${input}" "-DOUTPUT=${output}" -P "${PROJECT_SOURCE_DIR}/scripts/xxd.cmake"
|
||||
)
|
||||
set_source_files_properties(${output} PROPERTIES GENERATED TRUE)
|
||||
endforeach()
|
||||
|
||||
add_definitions(-DLLAMA_BUILD_WEBUI)
|
||||
add_definitions(-DLLAMA_WEBUI_DEFAULT_ENABLED=1)
|
||||
message(STATUS "WebUI: embedded with source: ${WEBUI_SOURCE}")
|
||||
else()
|
||||
# WebUI source not found - issue warning but don't fail the build
|
||||
# The server will still build but without webui embedded
|
||||
message(WARNING "WebUI: no source available. Neither local build (tools/server/public/) nor HF Bucket download succeeded.")
|
||||
message(WARNING "WebUI: building server without embedded WebUI. Set LLAMA_BUILD_WEBUI=OFF to suppress this warning.")
|
||||
add_definitions(-DLLAMA_WEBUI_DEFAULT_ENABLED=0)
|
||||
endif()
|
||||
else()
|
||||
# WebUI is disabled at build time
|
||||
add_definitions(-DLLAMA_WEBUI_DEFAULT_ENABLED=0)
|
||||
endif()
|
||||
|
||||
add_executable(${TARGET} ${TARGET_SRCS})
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
|
||||
target_include_directories(${TARGET} PRIVATE ../mtmd)
|
||||
target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
target_link_libraries(${TARGET} PRIVATE server-context PUBLIC llama-common cpp-httplib ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(${TARGET} PRIVATE server-context llama-ui PUBLIC llama-common cpp-httplib ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||
|
||||
@@ -224,7 +224,7 @@ The SvelteKit-based Web UI is introduced in this PR: https://github.com/ggml-org
|
||||
|
||||
### Architecture
|
||||
|
||||
The WebUI follows a layered architecture:
|
||||
The UI follows a layered architecture:
|
||||
|
||||
```
|
||||
Routes → Components → Hooks → Stores → Services → Storage/API
|
||||
@@ -234,7 +234,7 @@ Routes → Components → Hooks → Stores → Services → Storage/API
|
||||
- **Services** - stateless API/database communication (`ChatService`, `ModelsService`, `PropsService`, `DatabaseService`)
|
||||
- **Hooks** - reusable logic (`useModelChangeValidation`, `useProcessingState`)
|
||||
|
||||
For detailed architecture diagrams, see [`tools/server/webui/docs/`](webui/docs/):
|
||||
For detailed architecture diagrams, see [`tools/ui/docs/`](../ui/docs/):
|
||||
|
||||
- `high-level-architecture.mmd` - full architecture with all modules
|
||||
- `high-level-architecture-simplified.mmd` - simplified overview
|
||||
@@ -246,7 +246,7 @@ For detailed architecture diagrams, see [`tools/server/webui/docs/`](webui/docs/
|
||||
|
||||
```sh
|
||||
# make sure you have Node.js installed
|
||||
cd tools/server/webui
|
||||
cd tools/ui
|
||||
npm i
|
||||
|
||||
# run dev server (with hot reload)
|
||||
|
||||
@@ -189,11 +189,11 @@ For the full list of features, please refer to [server's changelog](https://gith
|
||||
| `--reuse-port` | allow multiple sockets to bind to the same port (default: disabled)<br/>(env: LLAMA_ARG_REUSE_PORT) |
|
||||
| `--path PATH` | path to serve static files from (default: )<br/>(env: LLAMA_ARG_STATIC_PATH) |
|
||||
| `--api-prefix PREFIX` | prefix path the server serves from, without the trailing slash (default: )<br/>(env: LLAMA_ARG_API_PREFIX) |
|
||||
| `--webui-config JSON` | JSON that provides default WebUI settings (overrides WebUI defaults)<br/>(env: LLAMA_ARG_WEBUI_CONFIG) |
|
||||
| `--webui-config-file PATH` | JSON file that provides default WebUI settings (overrides WebUI defaults)<br/>(env: LLAMA_ARG_WEBUI_CONFIG_FILE) |
|
||||
| `--webui-mcp-proxy, --no-webui-mcp-proxy` | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)<br/>(env: LLAMA_ARG_WEBUI_MCP_PROXY) |
|
||||
| `--ui-config JSON` / `--webui-config JSON` (deprecated) | JSON that provides default UI settings (overrides UI defaults)<br/>(env: LLAMA_ARG_UI_CONFIG / LLAMA_ARG_WEBUI_CONFIG) |
|
||||
| `--ui-config-file PATH` / `--webui-config-file PATH` (deprecated) | JSON file that provides default UI settings (overrides UI defaults)<br/>(env: LLAMA_ARG_UI_CONFIG_FILE / LLAMA_ARG_WEBUI_CONFIG_FILE) |
|
||||
| `--ui-mcp-proxy, --no-ui-mcp-proxy` / `--webui-mcp-proxy, --no-webui-mcp-proxy` (deprecated) | experimental: whether to enable MCP CORS proxy - do not enable in untrusted environments (default: disabled)<br/>(env: LLAMA_ARG_UI_MCP_PROXY / LLAMA_ARG_WEBUI_MCP_PROXY) |
|
||||
| `--tools TOOL1,TOOL2,...` | experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)<br/>specify "all" to enable all tools<br/>available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, apply_diff, get_datetime<br/>(env: LLAMA_ARG_TOOLS) |
|
||||
| `--webui, --no-webui` | whether to enable the Web UI (default: enabled)<br/>(env: LLAMA_ARG_WEBUI) |
|
||||
| `--ui, --no-ui` / `--webui, --no-webui` (deprecated) | whether to enable the Web UI (default: enabled)<br/>(env: LLAMA_ARG_UI / LLAMA_ARG_WEBUI) |
|
||||
| `--embedding, --embeddings` | restrict to only support embedding use case; use only with dedicated embedding models (default: disabled)<br/>(env: LLAMA_ARG_EMBEDDINGS) |
|
||||
| `--rerank, --reranking` | enable reranking endpoint on server (default: disabled)<br/>(env: LLAMA_ARG_RERANKING) |
|
||||
| `--api-key KEY` | API key to use for authentication, multiple keys can be provided as a comma-separated list (default: none)<br/>(env: LLAMA_API_KEY) |
|
||||
@@ -1831,10 +1831,12 @@ Apart from error types supported by OAI, we also have custom types that are spec
|
||||
|
||||
### Custom default Web UI preferences
|
||||
|
||||
You can specify default preferences for the web UI using `--webui-config <JSON config>` or `--webui-config-file <path to JSON config>`. For example, you can disable pasting long text as attachments and enable rendering Markdown in user messages with this command:
|
||||
You can specify default preferences for the web UI using `--ui-config <JSON config>` or `--ui-config-file <path to JSON config>`. For example, you can disable pasting long text as attachments and enable rendering Markdown in user messages with this command:
|
||||
|
||||
```bash
|
||||
./llama-server -m model.gguf --webui-config '{"pasteLongTextToFileLen": 0, "renderUserContentAsMarkdown": true}'
|
||||
./llama-server -m model.gguf --ui-config '{"pasteLongTextToFileLen": 0, "renderUserContentAsMarkdown": true}'
|
||||
```
|
||||
|
||||
You may find available preferences in [settings-config.ts](webui/src/lib/constants/settings-config.ts).
|
||||
> **Note:** The old flags `--webui-config` and `--webui-config-file` are deprecated but still work as aliases.
|
||||
|
||||
You may find available preferences in [settings-config.ts](../ui/src/lib/constants/settings-config.ts).
|
||||
|
||||
@@ -671,7 +671,8 @@ private:
|
||||
|
||||
server_metrics metrics;
|
||||
|
||||
json json_webui_settings = json::object();
|
||||
json json_ui_settings = json::object(); // Primary: new name
|
||||
json json_webui_settings = json::object(); // Deprecated: use json_ui_settings instead (kept for compat)
|
||||
|
||||
// Necessary similarity of prompt for slot selection
|
||||
float slot_prompt_similarity = 0.0f;
|
||||
@@ -996,13 +997,18 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// populate webui settings
|
||||
// populate UI settings (from either new ui_config_json or deprecated webui_config_json)
|
||||
{
|
||||
if (!params_base.webui_config_json.empty()) {
|
||||
const std::string & cfg = !params_base.ui_config_json.empty()
|
||||
? params_base.ui_config_json
|
||||
: params_base.webui_config_json;
|
||||
if (!cfg.empty()) {
|
||||
try {
|
||||
json_webui_settings = json::parse(params_base.webui_config_json);
|
||||
json json_settings = json::parse(cfg);
|
||||
json_ui_settings = json_settings;
|
||||
json_webui_settings = json_settings; // deprecated: keep in sync
|
||||
} catch (const std::exception & e) {
|
||||
SRV_ERR("%s: failed to parse webui config: %s\n", __func__, e.what());
|
||||
SRV_ERR("%s: failed to parse UI config: %s\n", __func__, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3292,7 +3298,8 @@ server_context_meta server_context::get_meta() const {
|
||||
/* has_mtmd */ impl->mctx != nullptr,
|
||||
/* has_inp_image */ impl->chat_params.allow_image,
|
||||
/* has_inp_audio */ impl->chat_params.allow_audio,
|
||||
/* json_webui_settings */ impl->json_webui_settings,
|
||||
/* json_ui_settings */ impl->json_ui_settings,
|
||||
/* json_webui_settings */ impl->json_webui_settings, // Deprecated
|
||||
/* slot_n_ctx */ impl->get_slot_n_ctx(),
|
||||
/* pooling_type */ llama_pooling_type(impl->ctx_tgt),
|
||||
|
||||
@@ -3814,8 +3821,12 @@ void server_routes::init_routes() {
|
||||
{ "endpoint_slots", params.endpoint_slots },
|
||||
{ "endpoint_props", params.endpoint_props },
|
||||
{ "endpoint_metrics", params.endpoint_metrics },
|
||||
{ "webui", params.webui },
|
||||
{ "webui_settings", meta->json_webui_settings },
|
||||
// New keys
|
||||
{ "ui", params.ui },
|
||||
{ "ui_settings", meta->json_ui_settings },
|
||||
// Deprecated: use ui/ui_settings instead (kept for backward compat)
|
||||
{ "webui", params.webui },
|
||||
{ "webui_settings", meta->json_webui_settings },
|
||||
{ "chat_template", tmpl_default },
|
||||
{ "chat_template_caps", meta->chat_template_caps },
|
||||
{ "bos_token", meta->bos_token_str },
|
||||
|
||||
@@ -21,7 +21,8 @@ struct server_context_meta {
|
||||
bool has_mtmd;
|
||||
bool has_inp_image;
|
||||
bool has_inp_audio;
|
||||
json json_webui_settings;
|
||||
json json_ui_settings; // Primary: new name
|
||||
json json_webui_settings; // Deprecated: use json_ui_settings instead (kept for backward compat)
|
||||
int slot_n_ctx;
|
||||
enum llama_pooling_type pooling_type;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "common.h"
|
||||
#include "server-http.h"
|
||||
#include "server-common.h"
|
||||
#include "ui.h"
|
||||
|
||||
#include <cpp-httplib/httplib.h>
|
||||
|
||||
@@ -10,14 +11,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#ifdef LLAMA_BUILD_WEBUI
|
||||
// auto generated files (see README.md for details)
|
||||
#include "index.html.hpp"
|
||||
#include "bundle.js.hpp"
|
||||
#include "bundle.css.hpp"
|
||||
#include "loading.html.hpp"
|
||||
#endif
|
||||
|
||||
//
|
||||
// HTTP implementation using cpp-httplib
|
||||
//
|
||||
@@ -238,10 +231,11 @@ bool server_http_context::init(const common_params & params) {
|
||||
};
|
||||
|
||||
auto middleware_server_state = [this](const httplib::Request & req, httplib::Response & res) {
|
||||
(void)req; // suppress unused parameter warning when LLAMA_BUILD_WEBUI is not defined
|
||||
(void)req; // suppress unused parameter warning when LLAMA_BUILD_UI / LLAMA_BUILD_WEBUI is not defined
|
||||
bool ready = is_ready.load();
|
||||
if (!ready) {
|
||||
#ifdef LLAMA_BUILD_WEBUI
|
||||
// Support both old and new preprocessor defines
|
||||
#if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI)
|
||||
auto tmp = string_split<std::string>(req.path, '.');
|
||||
if (req.path == "/" || (tmp.size() > 0 && tmp.back() == "html")) {
|
||||
res.status = 503;
|
||||
@@ -305,8 +299,10 @@ bool server_http_context::init(const common_params & params) {
|
||||
// Web UI setup
|
||||
//
|
||||
|
||||
if (!params.webui) {
|
||||
SRV_INF("%s", "the WebUI is disabled\n");
|
||||
// Use new `params.ui` field (backed by old `params.webui` for compat)
|
||||
if (!params.ui) {
|
||||
SRV_INF("%s", "The UI is disabled\n");
|
||||
SRV_INF("%s", "Use --ui/--no-ui (or deprecated --webui/--no-webui) to enable/disable\n");
|
||||
} else {
|
||||
// register static assets routes
|
||||
if (!params.public_path.empty()) {
|
||||
@@ -317,7 +313,8 @@ bool server_http_context::init(const common_params & params) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
#ifdef LLAMA_BUILD_WEBUI
|
||||
// Support both old and new preprocessor defines
|
||||
#if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI)
|
||||
// using embedded static index.html
|
||||
srv->Get(params.api_prefix + "/", [](const httplib::Request & /*req*/, httplib::Response & res) {
|
||||
// COEP and COOP headers, required by pyodide (python interpreter)
|
||||
|
||||
@@ -1152,14 +1152,17 @@ void server_models_routes::init_routes() {
|
||||
{"role", "router"},
|
||||
{"max_instances", params.models_max},
|
||||
{"models_autoload", params.models_autoload},
|
||||
// this is a dummy response to make sure webui doesn't break
|
||||
// this is a dummy response to make sure the UI doesn't break
|
||||
{"model_alias", "llama-server"},
|
||||
{"model_path", "none"},
|
||||
{"default_generation_settings", {
|
||||
{"params", json{}},
|
||||
{"n_ctx", 0},
|
||||
}},
|
||||
{"webui_settings", webui_settings},
|
||||
// New key
|
||||
{"ui_settings", ui_settings},
|
||||
// Deprecated: use ui_settings instead (kept for backward compat)
|
||||
{"webui_settings", webui_settings},
|
||||
{"build_info", std::string(llama_build_info())},
|
||||
});
|
||||
return res;
|
||||
|
||||
@@ -175,15 +175,22 @@ public:
|
||||
|
||||
struct server_models_routes {
|
||||
common_params params;
|
||||
json webui_settings = json::object();
|
||||
json ui_settings = json::object(); // Primary: new name
|
||||
json webui_settings = json::object(); // Deprecated: use ui_settings (kept for compat)
|
||||
server_models models;
|
||||
server_models_routes(const common_params & params, int argc, char ** argv)
|
||||
: params(params), models(params, argc, argv) {
|
||||
if (!this->params.webui_config_json.empty()) {
|
||||
// Support both new ui_config_json and deprecated webui_config_json
|
||||
const std::string & cfg = !this->params.ui_config_json.empty()
|
||||
? this->params.ui_config_json
|
||||
: this->params.webui_config_json;
|
||||
if (!cfg.empty()) {
|
||||
try {
|
||||
webui_settings = json::parse(this->params.webui_config_json);
|
||||
json json_settings = json::parse(cfg);
|
||||
ui_settings = json_settings;
|
||||
webui_settings = json_settings; // Deprecated: keep in sync
|
||||
} catch (const std::exception & e) {
|
||||
LOG_ERR("%s: failed to parse webui config: %s\n", __func__, e.what());
|
||||
LOG_ERR("%s: failed to parse UI config: %s\n", __func__, e.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,8 @@ int main(int argc, char ** argv) {
|
||||
ctx_http.register_gcp_compat();
|
||||
|
||||
// CORS proxy (EXPERIMENTAL, only used by the Web UI for MCP)
|
||||
if (params.webui_mcp_proxy) {
|
||||
// Supports both new ui_mcp_proxy and deprecated webui_mcp_proxy fields
|
||||
if (params.ui_mcp_proxy || params.webui_mcp_proxy) {
|
||||
SRV_WRN("%s", "-----------------\n");
|
||||
SRV_WRN("%s", "CORS proxy is enabled, do not expose server to untrusted environments\n");
|
||||
SRV_WRN("%s", "This feature is EXPERIMENTAL and may be removed or changed in future versions\n");
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
rm -rf ../public/_app;
|
||||
rm ../public/favicon.svg;
|
||||
rm -f ../public/index.html.gz; # deprecated, but may still be generated by older versions of the build process
|
||||
@@ -1,6 +0,0 @@
|
||||
export const ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY = 'LlamaCppWebui.alwaysAllowedTools';
|
||||
export const CONFIG_LOCALSTORAGE_KEY = 'LlamaCppWebui.config';
|
||||
export const DISABLED_TOOLS_LOCALSTORAGE_KEY = 'LlamaCppWebui.disabledTools';
|
||||
export const FAVORITE_MODELS_LOCALSTORAGE_KEY = 'LlamaCppWebui.favoriteModels';
|
||||
export const MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY = 'LlamaCppWebui.mcpDefaultEnabled';
|
||||
export const USER_OVERRIDES_LOCALSTORAGE_KEY = 'LlamaCppWebui.userOverrides';
|
||||
@@ -0,0 +1,157 @@
|
||||
set(TARGET llama-ui)
|
||||
|
||||
# Deprecated: use LLAMA_UI_HF_BUCKET instead
|
||||
set(LLAMA_WEBUI_HF_BUCKET "llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt webui assets (deprecated: use LLAMA_UI_HF_BUCKET)")
|
||||
set(LLAMA_UI_HF_BUCKET "llama-ui" CACHE STRING "Hugging Face bucket name for prebuilt UI assets")
|
||||
|
||||
# Backward compat: forward old var to new one
|
||||
if(DEFINED LLAMA_WEBUI_HF_BUCKET AND NOT DEFINED LLAMA_UI_HF_BUCKET)
|
||||
set(LLAMA_UI_HF_BUCKET ${LLAMA_WEBUI_HF_BUCKET})
|
||||
elseif(DEFINED LLAMA_WEBUI_HF_BUCKET AND NOT "${LLAMA_WEBUI_HF_BUCKET}" STREQUAL "${LLAMA_UI_HF_BUCKET}")
|
||||
message(DEPRECATION "LLAMA_WEBUI_HF_BUCKET is deprecated, use LLAMA_UI_HF_BUCKET instead")
|
||||
endif()
|
||||
|
||||
set(TARGET_SRCS "")
|
||||
set(UI_COMPILE_DEFS "")
|
||||
|
||||
# Support both old (LLAMA_BUILD_WEBUI) and new (LLAMA_BUILD_UI) option names
|
||||
if(LLAMA_BUILD_WEBUI OR LLAMA_BUILD_UI)
|
||||
if(LLAMA_BUILD_WEBUI AND NOT LLAMA_BUILD_UI)
|
||||
message(DEPRECATION "LLAMA_BUILD_WEBUI is deprecated, use LLAMA_BUILD_UI instead")
|
||||
endif()
|
||||
|
||||
set(PUBLIC_ASSETS
|
||||
index.html
|
||||
bundle.js
|
||||
bundle.css
|
||||
loading.html
|
||||
)
|
||||
|
||||
# Determine source of UI assets (priority: local > HF Bucket)
|
||||
set(UI_SOURCE "")
|
||||
set(UI_SOURCE_DIR "")
|
||||
|
||||
# Priority 1: Check for local build output
|
||||
set(LOCAL_UI_DIR "${PROJECT_SOURCE_DIR}/build/tools/ui/dist")
|
||||
|
||||
# Verify all required assets exist before declaring local source valid
|
||||
set(ALL_ASSETS_PRESENT TRUE)
|
||||
foreach(asset ${PUBLIC_ASSETS})
|
||||
if(NOT EXISTS "${LOCAL_UI_DIR}/${asset}")
|
||||
set(ALL_ASSETS_PRESENT FALSE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(ALL_ASSETS_PRESENT)
|
||||
set(UI_SOURCE "local")
|
||||
set(UI_SOURCE_DIR "${LOCAL_UI_DIR}")
|
||||
message(STATUS "UI: using local build from ${UI_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
# Priority 2: Build-time asset provisioning (npm build → HF Bucket fallback)
|
||||
if(NOT UI_SOURCE_DIR)
|
||||
# Environment variable takes precedence (e.g., from CI workflows)
|
||||
# Deprecated: use HF_UI_VERSION instead
|
||||
if(DEFINED ENV{HF_WEBUI_VERSION})
|
||||
set(HF_UI_VERSION "$ENV{HF_WEBUI_VERSION}")
|
||||
message(DEPRECATION "HF_WEBUI_VERSION env var is deprecated, use HF_UI_VERSION instead")
|
||||
if(NOT HF_UI_VERSION MATCHES "^[A-Za-z0-9._-]+$")
|
||||
message(FATAL_ERROR "UI: invalid HF_WEBUI_VERSION='${HF_UI_VERSION}' - must match ^[A-Za-z0-9._-]+$")
|
||||
endif()
|
||||
elseif(DEFINED ENV{HF_UI_VERSION})
|
||||
set(HF_UI_VERSION "$ENV{HF_UI_VERSION}")
|
||||
if(NOT HF_UI_VERSION MATCHES "^[A-Za-z0-9._-]+$")
|
||||
message(FATAL_ERROR "UI: invalid HF_UI_VERSION='${HF_UI_VERSION}' - must match ^[A-Za-z0-9._-]+$")
|
||||
endif()
|
||||
elseif(DEFINED LLAMA_BUILD_NUMBER)
|
||||
set(HF_UI_VERSION "b${LLAMA_BUILD_NUMBER}")
|
||||
message(STATUS "UI: derived HF_UI_VERSION=b${LLAMA_BUILD_NUMBER}")
|
||||
else()
|
||||
set(HF_UI_VERSION "")
|
||||
message(STATUS "UI: version not specified (will use HF 'latest')")
|
||||
endif()
|
||||
|
||||
if("${HF_UI_VERSION}" STREQUAL "")
|
||||
set(UI_VERSION_TAG "provisioned")
|
||||
else()
|
||||
set(UI_VERSION_TAG "${HF_UI_VERSION}")
|
||||
endif()
|
||||
set(UI_STAMP "${CMAKE_CURRENT_BINARY_DIR}/.ui-${UI_VERSION_TAG}.stamp")
|
||||
|
||||
string(REPLACE ";" "+" PUBLIC_ASSETS_JOINED "${PUBLIC_ASSETS}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${UI_STAMP}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-DSOURCE_DIR=${PROJECT_SOURCE_DIR}"
|
||||
"-DPUBLIC_DIR=${PROJECT_SOURCE_DIR}/build/tools/ui/dist"
|
||||
"-DHF_BUCKET=${LLAMA_UI_HF_BUCKET}"
|
||||
"-DHF_VERSION=${HF_UI_VERSION}"
|
||||
"-DHF_ENABLED=${LLAMA_USE_PREBUILT_UI}"
|
||||
"-DASSETS=${PUBLIC_ASSETS_JOINED}"
|
||||
"-DSTAMP_FILE=${UI_STAMP}"
|
||||
"-DNPM_DIR=${PROJECT_SOURCE_DIR}/tools/ui"
|
||||
-P ${PROJECT_SOURCE_DIR}/scripts/ui-download.cmake
|
||||
COMMENT "Building/provisioning UI assets (npm build -> HF Bucket fallback)"
|
||||
)
|
||||
|
||||
set(UI_SOURCE "provisioned")
|
||||
set(UI_SOURCE_DIR "${PROJECT_SOURCE_DIR}/build/tools/ui/dist")
|
||||
endif()
|
||||
|
||||
# Process assets from the determined source
|
||||
if(UI_SOURCE_DIR)
|
||||
foreach(asset ${PUBLIC_ASSETS})
|
||||
set(input "${UI_SOURCE_DIR}/${asset}")
|
||||
set(output "${CMAKE_CURRENT_BINARY_DIR}/${asset}.hpp")
|
||||
list(APPEND TARGET_SRCS ${output})
|
||||
|
||||
if(UI_SOURCE STREQUAL "local")
|
||||
if(NOT EXISTS "${input}")
|
||||
message(FATAL_ERROR "UI asset not found: ${input}")
|
||||
endif()
|
||||
set(dependency "${input}")
|
||||
else()
|
||||
set(dependency "${UI_STAMP}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
DEPENDS ${dependency}
|
||||
OUTPUT "${output}"
|
||||
COMMAND "${CMAKE_COMMAND}" "-DINPUT=${input}" "-DOUTPUT=${output}" -P "${PROJECT_SOURCE_DIR}/scripts/xxd.cmake"
|
||||
)
|
||||
set_source_files_properties(${output} PROPERTIES GENERATED TRUE)
|
||||
endforeach()
|
||||
|
||||
list(APPEND UI_COMPILE_DEFS
|
||||
LLAMA_BUILD_WEBUI # Deprecated: use LLAMA_BUILD_UI
|
||||
LLAMA_BUILD_UI
|
||||
LLAMA_WEBUI_DEFAULT_ENABLED=1 # Deprecated: use LLAMA_UI_DEFAULT_ENABLED
|
||||
LLAMA_UI_DEFAULT_ENABLED=1
|
||||
)
|
||||
message(STATUS "UI: embedded with source: ${UI_SOURCE}")
|
||||
else()
|
||||
message(WARNING "UI: no source available. Neither local build (build/tools/ui/dist/) nor HF Bucket download succeeded.")
|
||||
message(WARNING "UI: building server without embedded UI. Set LLAMA_BUILD_UI=OFF to suppress this warning.")
|
||||
list(APPEND UI_COMPILE_DEFS LLAMA_WEBUI_DEFAULT_ENABLED=0 LLAMA_UI_DEFAULT_ENABLED=0)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND UI_COMPILE_DEFS LLAMA_WEBUI_DEFAULT_ENABLED=0 LLAMA_UI_DEFAULT_ENABLED=0)
|
||||
endif()
|
||||
|
||||
# Build the static library
|
||||
add_library(${TARGET} STATIC ui.cpp)
|
||||
|
||||
target_include_directories(${TARGET} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_compile_definitions(${TARGET} PUBLIC ${UI_COMPILE_DEFS})
|
||||
|
||||
if(TARGET_SRCS)
|
||||
# List generated .hpp files as sources so CMake tracks them as build dependencies
|
||||
target_sources(${TARGET} PRIVATE ${TARGET_SRCS})
|
||||
set_source_files_properties(${TARGET_SRCS} PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
endif()
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A modern, feature-rich web interface for llama-server built with SvelteKit. This UI provides an intuitive chat interface with advanced file handling, conversation management, and comprehensive model interaction capabilities.
|
||||
|
||||
The WebUI supports two server operation modes:
|
||||
Llama UI supports two server operation modes:
|
||||
|
||||
- **MODEL mode** - Single model operation (standard llama-server)
|
||||
- **ROUTER mode** - Multi-model operation with dynamic model loading/unloading
|
||||
@@ -88,7 +88,7 @@ The WebUI supports two server operation modes:
|
||||
### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
cd tools/server/webui
|
||||
cd tools/ui
|
||||
npm install
|
||||
```
|
||||
|
||||
@@ -112,7 +112,7 @@ npm run dev
|
||||
|
||||
This starts:
|
||||
|
||||
- **Vite dev server** at `http://localhost:5173` - The main WebUI
|
||||
- **Vite dev server** at `http://localhost:5173` - The main UI frontend app
|
||||
- **Storybook** at `http://localhost:6006` - Component documentation
|
||||
|
||||
The Vite dev server proxies API requests to `http://localhost:8080` (default llama-server port):
|
||||
@@ -186,7 +186,7 @@ npm run build
|
||||
The build process:
|
||||
|
||||
1. **Vite Build** - Bundles all TypeScript, Svelte, and CSS
|
||||
2. **Static Adapter** - Outputs to `../public` (llama-server's static file directory)
|
||||
2. **Static Adapter** - Outputs to `../../build/tools/ui/dist` (llama-server's static file directory)
|
||||
3. **Post-Build Script** - Cleans up intermediate files
|
||||
4. **Custom Plugin** - Creates `index.html` with:
|
||||
- Inlined favicon as base64
|
||||
@@ -194,7 +194,7 @@ The build process:
|
||||
- Deterministic output (zeroed timestamps)
|
||||
|
||||
```text
|
||||
tools/server/webui/ → build → tools/server/public/
|
||||
tools/ui/ → build → build/tools/ui/dist/
|
||||
├── src/ ├── index.html (served by llama-server)
|
||||
├── static/ └── (favicon inlined)
|
||||
└── ...
|
||||
@@ -205,8 +205,8 @@ tools/server/webui/ → build → tools/server/public/
|
||||
```javascript
|
||||
// svelte.config.js
|
||||
adapter: adapter({
|
||||
pages: '../public', // Output directory
|
||||
assets: '../public', // Static assets
|
||||
pages: '../../build/tools/ui/dist', // Output directory
|
||||
assets: '../../build/tools/ui/dist', // Static assets
|
||||
fallback: 'index.html', // SPA fallback
|
||||
strict: true
|
||||
}),
|
||||
@@ -217,20 +217,19 @@ output: {
|
||||
|
||||
### Integration with llama-server
|
||||
|
||||
The WebUI is embedded directly into the llama-server binary:
|
||||
llama-ui is embedded directly into the llama-server binary:
|
||||
|
||||
1. `npm run build` outputs `index.html` to `tools/server/public/`
|
||||
1. `npm run build` outputs `index.html` to `build/tools/ui/dist/`
|
||||
2. llama-server compiles this into the binary at build time
|
||||
3. When accessing `/`, llama-server serves the gzipped HTML
|
||||
4. All assets are inlined (CSS, JS, fonts, favicon)
|
||||
3. When accessing `/`, llama-server serves the bundled HTML
|
||||
|
||||
This results in a **single portable binary** with the full WebUI included.
|
||||
This results in a **single portable binary** with the full Llama UI included.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
The WebUI follows a layered architecture with unidirectional data flow:
|
||||
Llama UI follows a layered architecture with unidirectional data flow:
|
||||
|
||||
```text
|
||||
Routes → Components → Hooks → Stores → Services → Storage/API
|
||||
@@ -659,7 +658,7 @@ npm run check # TypeScript type checking
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
tools/server/webui/
|
||||
tools/ui/
|
||||
├── src/
|
||||
│ ├── lib/
|
||||
│ │ ├── components/ # UI components (app/, ui/)
|
||||
+2
-2
@@ -58,8 +58,8 @@ sequenceDiagram
|
||||
end
|
||||
end
|
||||
|
||||
alt serverStore.props has webuiSettings
|
||||
settingsStore->>settingsStore: Apply webuiSettings from server
|
||||
alt serverStore.props has uiSettings
|
||||
settingsStore->>settingsStore: Apply uiSettings from server
|
||||
Note right of settingsStore: Server-provided UI settings<br/>(e.g. showRawOutputSwitch)
|
||||
end
|
||||
|
||||
@@ -29,7 +29,9 @@ export default ts.config(
|
||||
'no-undef': 'off',
|
||||
'svelte/no-at-html-tags': 'off',
|
||||
// This app uses hash-based routing (#/) where resolve() from $app/paths does not apply
|
||||
'svelte/no-navigation-without-resolve': 'off'
|
||||
'svelte/no-navigation-without-resolve': 'off',
|
||||
// Enforce empty line at end of file
|
||||
'eol-last': 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bash scripts/dev.sh",
|
||||
"build": "vite build && ./scripts/post-build.sh",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
@@ -2,7 +2,7 @@ import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
webServer: {
|
||||
command: 'npm run build && http-server ../public -p 8181',
|
||||
command: 'npm run build && http-server ../../build/tools/ui/dist -p 8181',
|
||||
port: 8181,
|
||||
timeout: 120000,
|
||||
reuseExistingServer: false
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
# Development script for llama-ui
|
||||
#
|
||||
# This script starts the webui development servers (Storybook and Vite).
|
||||
# This script starts the llama-ui development servers (Storybook and Vite).
|
||||
# Note: You need to start llama-server separately.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/dev.sh
|
||||
# npm run dev
|
||||
|
||||
cd ../../../
|
||||
cd ../../
|
||||
|
||||
# Check and install git hooks if missing
|
||||
check_and_install_hooks() {
|
||||
@@ -22,13 +22,13 @@ check_and_install_hooks() {
|
||||
|
||||
if [ "$hooks_missing" = true ]; then
|
||||
echo "🔧 Git hooks missing, installing them..."
|
||||
cd tools/server/webui
|
||||
cd tools/ui
|
||||
if bash scripts/install-git-hooks.sh; then
|
||||
echo "✅ Git hooks installed successfully"
|
||||
else
|
||||
echo "⚠️ Failed to install git hooks, continuing anyway..."
|
||||
fi
|
||||
cd ../../../
|
||||
cd ../../
|
||||
else
|
||||
echo "✅ Git hooks already installed"
|
||||
fi
|
||||
@@ -48,7 +48,7 @@ trap cleanup SIGINT SIGTERM
|
||||
|
||||
echo "🚀 Starting development servers..."
|
||||
echo "📝 Note: Make sure to start llama-server separately if needed"
|
||||
cd tools/server/webui
|
||||
cd tools/ui
|
||||
# Use --insecure-http-parser to handle malformed HTTP responses from llama-server
|
||||
# (some responses have both Content-Length and Transfer-Encoding headers)
|
||||
storybook dev -p 6006 --ci & NODE_OPTIONS="--insecure-http-parser" vite dev --host 0.0.0.0 &
|
||||
+14
-14
@@ -1,29 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to install pre-commit hook for webui
|
||||
# Pre-commit: formats, checks, and builds webui
|
||||
# Script to install pre-commit hook for llama-ui
|
||||
# Pre-commit: formats, checks, and builds the UI app
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
PRE_COMMIT_HOOK="$REPO_ROOT/.git/hooks/pre-commit"
|
||||
|
||||
echo "Installing pre-commit hook for webui..."
|
||||
echo "Installing pre-commit hook for llama-ui..."
|
||||
|
||||
# Create the pre-commit hook
|
||||
cat > "$PRE_COMMIT_HOOK" << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# Check if there are any changes in the webui directory
|
||||
if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
||||
# Check if there are any changes in the tools/ui directory
|
||||
if git diff --cached --name-only | grep -q "^tools/ui/"; then
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
cd "$REPO_ROOT/tools/server/webui"
|
||||
cd "$REPO_ROOT/tools/ui"
|
||||
|
||||
# Check if package.json exists
|
||||
if [ ! -f "package.json" ]; then
|
||||
echo "Error: package.json not found in tools/server/webui"
|
||||
echo "Error: package.json not found in tools/ui"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Formatting and checking webui code..."
|
||||
echo "Formatting and checking llama-ui code..."
|
||||
|
||||
# Run the format command
|
||||
npm run format
|
||||
@@ -46,17 +46,17 @@ if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Webui code formatted and checked successfully"
|
||||
echo "✅ llama-ui code formatted and checked successfully"
|
||||
|
||||
# Build the webui
|
||||
echo "Building webui..."
|
||||
# Build the llama-ui
|
||||
echo "Building llama-ui..."
|
||||
npm run build
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ npm run build failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Webui built successfully"
|
||||
echo "✅ llama-ui built successfully"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -70,8 +70,8 @@ if [ $? -eq 0 ]; then
|
||||
echo " Pre-commit: $PRE_COMMIT_HOOK"
|
||||
echo ""
|
||||
echo "The hook will automatically:"
|
||||
echo " • Format, lint and check webui code before commits"
|
||||
echo " • Build webui"
|
||||
echo " • Format, lint and check llama-ui code before commits"
|
||||
echo " • Build llama-ui"
|
||||
else
|
||||
echo "❌ Failed to make hook executable"
|
||||
exit 1
|
||||
+35
-16
@@ -1,4 +1,12 @@
|
||||
import { readFileSync, writeFileSync, existsSync, readdirSync, copyFileSync } from 'fs';
|
||||
import {
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
existsSync,
|
||||
readdirSync,
|
||||
copyFileSync,
|
||||
rmSync,
|
||||
unlinkSync
|
||||
} from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
@@ -11,28 +19,28 @@ const GUIDE_FOR_FRONTEND = `
|
||||
-->
|
||||
`.trim();
|
||||
|
||||
const OUTPUT_DIR = '../../build/tools/ui/dist';
|
||||
|
||||
export function llamaCppBuildPlugin(): Plugin {
|
||||
return {
|
||||
name: 'llamacpp:build',
|
||||
apply: 'build',
|
||||
closeBundle() {
|
||||
// Ensure the SvelteKit adapter has finished writing to ../public
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const indexPath = resolve('../public/index.html');
|
||||
const outDir = resolve(OUTPUT_DIR);
|
||||
const indexPath = resolve(outDir, 'index.html');
|
||||
if (!existsSync(indexPath)) return;
|
||||
|
||||
let content = readFileSync(indexPath, 'utf-8');
|
||||
|
||||
// Inline favicon as base64 data URL
|
||||
const faviconPath = resolve('static/favicon.svg');
|
||||
|
||||
if (existsSync(faviconPath)) {
|
||||
const faviconContent = readFileSync(faviconPath, 'utf-8');
|
||||
const faviconBase64 = Buffer.from(faviconContent).toString('base64');
|
||||
const faviconDataUrl = `data:image/svg+xml;base64,${faviconBase64}`;
|
||||
|
||||
content = content.replace(/href="[^"]*favicon\.svg"/g, `href="${faviconDataUrl}"`);
|
||||
|
||||
console.log('✓ Inlined favicon.svg as base64 data URL');
|
||||
}
|
||||
|
||||
@@ -48,17 +56,16 @@ export function llamaCppBuildPlugin(): Plugin {
|
||||
writeFileSync(indexPath, content, 'utf-8');
|
||||
console.log('✓ Updated index.html');
|
||||
|
||||
// Copy bundle.*.js -> ../public/bundle.js
|
||||
const immutableDir = resolve('../public/_app/immutable');
|
||||
const bundleDir = resolve('../public/_app/immutable/assets');
|
||||
// Copy bundle.*.js -> bundle.js at output root
|
||||
const immutableDir = resolve(outDir, '_app/immutable');
|
||||
const bundleDir = resolve(outDir, '_app/immutable/assets');
|
||||
|
||||
if (existsSync(immutableDir)) {
|
||||
const jsFiles = readdirSync(immutableDir).filter((f) => f.match(/^bundle\..+\.js$/));
|
||||
|
||||
if (jsFiles.length > 0) {
|
||||
copyFileSync(resolve(immutableDir, jsFiles[0]), resolve('../public/bundle.js'));
|
||||
copyFileSync(resolve(immutableDir, jsFiles[0]), resolve(outDir, 'bundle.js'));
|
||||
// Normalize __sveltekit_<hash> to __sveltekit__ in bundle.js
|
||||
const bundleJsPath = resolve('../public/bundle.js');
|
||||
const bundleJsPath = resolve(outDir, 'bundle.js');
|
||||
let bundleJs = readFileSync(bundleJsPath, 'utf-8');
|
||||
bundleJs = bundleJs.replace(/__sveltekit_[a-z0-9]+/g, '__sveltekit__');
|
||||
writeFileSync(bundleJsPath, bundleJs, 'utf-8');
|
||||
@@ -66,17 +73,29 @@ export function llamaCppBuildPlugin(): Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy bundle.*.css -> ../public/bundle.css
|
||||
// Copy bundle.*.css -> bundle.css at output root
|
||||
if (existsSync(bundleDir)) {
|
||||
const cssFiles = readdirSync(bundleDir).filter((f) => f.match(/^bundle\..+\.css$/));
|
||||
|
||||
if (cssFiles.length > 0) {
|
||||
copyFileSync(resolve(bundleDir, cssFiles[0]), resolve('../public/bundle.css'));
|
||||
copyFileSync(resolve(bundleDir, cssFiles[0]), resolve(outDir, 'bundle.css'));
|
||||
console.log(`✓ Copied ${cssFiles[0]} -> bundle.css`);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup: remove _app directory, favicon.svg, and legacy index.html.gz
|
||||
const appDir = resolve(outDir, '_app');
|
||||
if (existsSync(appDir)) {
|
||||
rmSync(appDir, { recursive: true, force: true });
|
||||
console.log('✓ Removed _app directory');
|
||||
}
|
||||
|
||||
const faviconOut = resolve(outDir, 'favicon.svg');
|
||||
if (existsSync(faviconOut)) {
|
||||
unlinkSync(faviconOut);
|
||||
console.log('✓ Removed favicon.svg');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update index.html:', error);
|
||||
console.error('Failed to process build output:', error);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
+6
-1
@@ -54,7 +54,12 @@
|
||||
}
|
||||
|
||||
const attachmentMenu = useAttachmentMenu(
|
||||
() => ({ hasVisionModality, hasAudioModality, hasMcpPromptsSupport, hasMcpResourcesSupport }),
|
||||
() => ({
|
||||
hasVisionModality,
|
||||
hasAudioModality,
|
||||
hasMcpPromptsSupport,
|
||||
hasMcpResourcesSupport
|
||||
}),
|
||||
() => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }),
|
||||
() => {
|
||||
dropdownOpen = false;
|
||||
+6
-1
@@ -46,7 +46,12 @@
|
||||
let sheetOpen = $state(false);
|
||||
|
||||
const attachmentMenu = useAttachmentMenu(
|
||||
() => ({ hasVisionModality, hasAudioModality, hasMcpPromptsSupport, hasMcpResourcesSupport }),
|
||||
() => ({
|
||||
hasVisionModality,
|
||||
hasAudioModality,
|
||||
hasMcpPromptsSupport,
|
||||
hasMcpResourcesSupport
|
||||
}),
|
||||
() => ({ onFileUpload, onSystemPromptClick, onMcpPromptClick, onMcpResourcesClick }),
|
||||
() => {
|
||||
sheetOpen = false;
|
||||
+2
-1
@@ -5,6 +5,7 @@
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { toolsStore } from '$lib/stores/tools.svelte';
|
||||
import { CLI_FLAGS } from '$lib/constants';
|
||||
import { mcpStore } from '$lib/stores/mcp.svelte';
|
||||
import { useToolsPanel } from '$lib/hooks/use-tools-panel.svelte';
|
||||
|
||||
@@ -33,7 +34,7 @@
|
||||
<Info class="mt-0.5 h-4 w-4 shrink-0" />
|
||||
|
||||
<span>
|
||||
Run llama-server with <code>--tools</code> flag to enable
|
||||
Run llama-server with <code>{CLI_FLAGS.TOOLS}</code> flag to enable
|
||||
|
||||
<strong>Built-in Tools</strong>.
|
||||
</span>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user