Skip to content

WebUI

mlxcel ships an administrative browser UI inside the server binaries. It lists the models the server can see, downloads and removes checkpoints in the managed cache, loads and unloads models, shows runtime activity, edits settings, and provides a local chat screen. The UI is a static bundle compiled into mlxcel and mlxcel-server; it needs no Node service, CDN, or network access at runtime.

Table of Contents


Starting the Server with the WebUI

Pass --webui (alias --ui) to mlxcel serve or mlxcel-server. The flag is off by default and listed under "WebUI Options" in --help; LLAMA_ARG_UI=on enables it from the environment, and --no-webui or LLAMA_ARG_UI=0 disables it explicitly.

# Model-free start: browse and load models from the UI.
mlxcel-server --webui --models-dir ./models/mlx --metrics --props --settings

# Serve one model and expose the UI next to it.
mlxcel serve -m ./models/Qwen3-4B-4bit --webui --metrics

The UI is served at {api_prefix}/webui/ on the same listener as the API, and the startup log prints the full address as Bundled WebUI: <addr>/webui/. Existing OpenAI, llama-server, and router routes keep their paths and behavior.

Access and Security

The WebUI control plane (/ui-api/v1) is an administrative API. Every request to it needs a bearer key, whether or not the rest of the server is authenticated. The shell and its static assets are public.

Listener Without --api-key With --api-key
Loopback TCP, interactive terminal The server generates a restart-local session key (mlxcel_webui_...) and prints it once to the terminal. Paste it into the login screen. The configured key is used.
Loopback TCP, no terminal (for example a service manager) Startup is refused, because the generated key could not be shown. The configured key is used.
Non-loopback TCP Startup is refused. Requires TLS (--ssl-cert-file and --ssl-key-file). Alternatively bind to loopback behind a TLS reverse proxy.
Unix socket (--host *.sock) Refused. Refused. Use a loopback TCP listener behind a reverse proxy instead.

In WebUI mode the server also checks the Host, Origin, and Fetch Metadata headers on browser requests, including the compatibility routes that change state (POST /models/load, POST /models/unload, POST /models, DELETE /models, settings, and GET /models?reload=...). Non-browser clients that authenticate with a bearer key and send no Origin header keep working.

The browser keeps the key in memory only. It is never written to browser storage, put in a URL, or logged, and a reload asks for it again.

The llama-server options that configure its own browser UI or agent tools are refused at startup rather than ignored: --ui-config, --ui-config-file, --path, --tools, --tools-runtime, --mcp-servers-config, --mcp-servers-json, --ui-mcp-proxy, and --agent.

Model-Free and Single-Model Modes

Model-free (router) mode. When --webui is given without a model argument, the server starts as a router with no model loaded and with autoload turned off, so browsing the catalog never loads a model. The catalog lists the managed cache, the --models-dir directory, and --models-preset entries, with the router's usual precedence (cache, then models directory, then preset). Listing reads only bounded metadata such as config.json; it never opens a tokenizer or reads weights. Models are loaded and unloaded explicitly from the Models or Chat screen, one load at a time, subject to --models-max.

Single-model mode. With -m, the UI describes the one loaded model and its real inference ID. Load, unload, download, and removal are read-only there, and settings that need a different startup configuration show restart instructions instead of an Apply action.

Screens

The app shell shows the models the server currently holds, with their lifecycle state, in the toolbar. A command palette searches loaded and catalog models, and a keyboard shortcut help dialog lists the global shortcuts (Cmd+N or Ctrl+N opens Chat with a new conversation). Themes, including a glass theme, are chosen in Settings.

Models

The model library is a paginated, filterable table of the catalog with one dense line per model: name, size, quantization (or, for an unquantized checkpoint, the declared weight dtype from config.json torch_dtype or dtype, such as bf16), task badges, lifecycle state, and row actions. Clicking a column header sorts by name, size, quantization, or state; every sort keeps Ready models first, then models in transition, then the rest. Search, filters, and sort survive route changes within a tab session; a reload, logout, or a different server instance starts from the defaults, and nothing is persisted.

The inspector shows the selected model and keeps several facts apart instead of showing a single "works" badge: whether the checkpoint files are complete, whether the architecture is supported, whether it is runnable on the GPU backend resolved at runtime (Metal, CUDA, or ROCm), and its current lifecycle state. At 1100 px and wider it is a pane beside the list that follows the selection; below 1100 px it is a drawer that opens on an explicit Inspect. Opaque identifiers, raw server reasons, and internal counters sit in a closed Details disclosure that renders only when opened.

Each row carries its own actions:

  • Use in Chat (Ready chat models) opens the Chat screen with that model selected.
  • Load and Unload are explicit actions: a Ready model shows Unload, and every other state shows Load, disabled until the entry can load. When the server is at capacity, a load asks which loaded model to evict, and the eviction applies only to the exact model revision you confirmed. A load applies the saved next-load profile, if any.
  • Inspect opens the inspector for that row.
  • Delete (managed-cache rows only) removes the cached files. Entries from --models-dir or presets, and busy, loading, downloading, or draining models, cannot be removed.

Download accepts a public owner/name repository ID (and optionally a revision) and writes it into the managed cache, with progress and cancellation. Paths, URLs, and private repositories are not accepted.

Chat

Chat streams through the server's own /v1/chat/completions route with autoload=false. Choosing a model in the conversation header only selects it; if the model is not Ready, the header offers a Load button that asks for confirmation first. Reasoning is shown as a collapsed section, tool calls are displayed as structured cards and are never executed, and model output is rendered as a limited Markdown subset without raw HTML or remote images. Local PNG, JPEG, and non-animated WebP images can be attached when the loaded model reports vision input.

Per-turn parameter overrides (max_tokens, temperature, top_p, top_k, min_p, repetition_penalty, seed) sit in the chat settings drawer and apply to the next turn only. Conversation history stays in memory unless you enable saving; saved history lives in this browser's IndexedDB, is capped at 50 conversations, and can be exported, imported, or cleared.

Activity

Activity is a status page for the selected model. The header carries the model picker (Ready models first), Refresh, Export, and the time of the last snapshot. Below it are four summary tiles in a fixed order: Active requests (with a sparkline of the recent history), Total completed requests, Total completion tokens, and Queued requests. Then comes the slot table with per-slot context occupancy, and one line per operation (downloads, loads, removals) from the server's session history.

The page polls every two seconds while the tab is visible and stops while it is hidden. Counters come from the existing server counters, so --metrics is needed for the request counters and slots must not be disabled with --no-slots. A tile whose value the server does not report reads unknown, never 0, with a hint naming the reason; memory figures that have no cheap source are not estimated. An idle slot with a known context size reads 0 / D tokens. The "All measurements and sources" disclosure lists every measurement with its scope, observation time, and raw server reason. A diagnostics export contains only counts, states, and numeric measurements, never paths, prompts, or keys.

Settings

Settings has four tabs, one per scope:

Tab Scope What it changes
Appearance This browser Theme and display preferences.
Requests This browser session Default request parameters, held in memory only. Blank fields fall back to the server defaults.
Model The selected model The next-load profile (ctx_size 1 to 262144, n_parallel 1 to 32, and a KV cache mode) and, for a Ready model, the live values exposed by /v1/settings. Live changes apply to newly admitted requests.
Server The selected model's server Read-only startup values, the effective per-slot context reported by /props, and an optional raw token count check.

Explicit CLI flags win over a next-load profile, and the profile wins over a preset. Saving a profile does not load anything; the profile applies on the next explicit load. The WebUI never turns on --settings, --props, --metrics, or slots by itself, so start the server with the endpoints you want the tabs to use.

Endpoints the WebUI Depends On

Screen Server option Without it
Activity request counters --metrics Counters are shown as unavailable.
Activity slots slots enabled (the default) Slot data is shown as unavailable.
Settings, Model tab live values --settings Only the next-load profile is editable.
Settings, Server tab context --props The tab reports that /props is not answering.

The /ui-api/v1 routes are documented for contributors in the repository's docs/webui/ directory, including the OpenAPI schema.

Building Without the WebUI

The webui Cargo feature is on by default and embeds the compiled assets into both binaries. To build without it, drop the default features and keep the ones you need:

cargo build --release --no-default-features --features metal,accelerate,surgery

A binary built without the feature refuses --webui at startup and says that it needs the webui feature.