First Run
The first time LlamaForge starts with a fresh config.json (no ui_mode key on disk), config.py's migrate() classifies the install: if server_bin is already set (an existing llama.cpp build was found), the config is marked ui_mode: "advanced" and onboarded: true; otherwise it is marked ui_mode: "lite" and onboarded: false, which is what causes the onboarding wizard to appear. This check is idempotent — a config that already carries ui_mode is left untouched on every later startup.
Lite vs Advanced mode
LlamaForge has two UI densities, toggled at any time from the mode switch in the dashboard header (applyMode() in web/js/ui.js toggles a mode-lite class on <body> and persists the choice via PUT /api/config with ui_mode):
- Lite — a reduced set of controls, aimed at getting a model loaded quickly.
- Advanced — the full set of ~220 llama.cpp knobs and every tab exposed.
Finishing the wizard sets ui_mode to "lite"; skipping it sets ui_mode to "advanced". You can switch between them afterward at any time.
The onboarding wizard
wizMaybeStart() shows the wizard automatically whenever the server reports onboarding.onboarded as false. It walks five steps, defined in WIZ.steps in web/js/wizard.js:
- Engine — "Do you already have a llama.cpp build?" Choose Yes, I have one built or No — clone & build it for me, and a build flavor (official llama.cpp; a mainline fork; ik_llama is listed but disabled, marked "coming soon"). The clone path hands off to the same flow as the Build tab.
- Hardware — a read-only summary of detected GPUs and their VRAM (or "No GPU detected — CPU mode" if none).
- Model — pick an already-registered model from a dropdown. If none are registered, the step instead links to the Discover tab and closes the wizard so you can download one.
- Tune — choose a goal (Balanced, Max speed, Max context, or Coding) and click Auto-tune to call
/api/autotune/recommendfor that model and intent; the resulting knobs and their rationale are shown in a table. An optional Refine by benchmarking (~1 min) button calls/api/autotune/refineto try a few high-impact variants (e.g. alternateubatch-size/batch-size) and keep whichever measured the highest tokens/second. - Ready — confirms the chosen settings will be applied to the selected model and it will be loaded.
Finishing the wizard saves the recommended knobs with /api/save, loads the model with /api/load, and marks the config onboarded: true, ui_mode: "lite" regardless of whether the load itself succeeded (a failed load surfaces a toast telling you to load it manually from the Models tab). Skip instead marks the config onboarded: true, ui_mode: "advanced" and closes the wizard without touching any model.
What auto-tune decides
backend/autotune.py's recommend(meta, hw, intent, size_bytes) is a pure function that turns a GGUF's header metadata and the detected hardware into a small set of knobs — everything else is left at llama.cpp's own defaults. Four intents are supported: balanced, speed, context, coding.
- GPU offload (
n-gpu-layers) — with no GPU detected, set to0andflash-attnset tooff. With a GPU, the weights' size is compared against a VRAM budget (total_vram * headroom, headroom0.90balanced,0.92speed,0.78context,0.90coding); if the weights fit, offload is99(all layers, llama.cpp caps to the real count), otherwise it is scaled to the fraction of layers that fit the budget.flash-attnis set toonwhenever a GPU is present. - Threads — set to the CPU's hardware thread (or core) count, when known.
- Context window (
ctx-size) — the model's trained context length, capped per intent:65536for balanced and coding,16384for speed, and150000for context (the max-context ceiling). - Intent-specific shaping —
contextsetscache-type-k/cache-type-vtoq8_0(roughly halves KV-cache memory per token);speedsets them tof16and raisesbatch-size/ubatch-sizeto2048/512;codinglowerstempto0.2andtop-pto0.9. With more than one GPU,tensor-splitis set to split proportionally by each GPU's VRAM.
Every knob recommend() sets comes with a plain-language reason, which is what populates the rationale column in the wizard's Tune step.
Auto-tune only ever writes the handful of knobs above. Anything else you set by hand on the Models tab afterward is preserved — per-model settings always win over the global [*] defaults.