llama-manager runs your models using optimal configurations that degrade gracefully as context expands. This ensures that you are always serving the most capable version of your model possible by reactively adjusting model configuration as your context window grows.
Notes:
- llama-manager is in beta and may not be tested with your hardware/model. Please open an issue if you find any bugs, and include your OS, what model you were using, and any relevant logs!
- llama-manager assumes inference uses a single GPU. It will not work for pure-CPU inference, and probably will not work for split inference. If this is something you want, please open an issue.
- llama-manager uses a custom fork of llama.cpp. See (llama.cpp changes) for details on what the fork introduces.
Requires Node.js and npm.
git clone --recursive https://github.com/wadealexc/llama-manager.git
cd llama-manager
npm install
npm run buildllama-manager drives llama-server via a custom fork of llama.cpp. To build the server binary, cd into the llama.cpp submodule and build llama-server for your platform. llama.cpp has detailed build instructions here.
If you're on Linux+CUDA like me, you can use this script:
./scripts/build-server.sh --gpuSupply args/config as either/both CLI args, or YAML.
Serve a model directly from the command line using the same syntax as llama-server:
node dist/index.js \
--model "/home/models/Qwen3.8-27B-UD-Q4_K_XL.gguf" \
--mmproj "/home/models/mmproj-BF16.gguf" \
--spec-type draft-mtp \
--spec-draft-n-max 2 \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
-ngl 999See the example config file for an example. Serve multiple models and swap between them automatically using a config.yaml file:
node dist/index.js --config './my-config.yaml'Models are swapped out in response to requests: when a request comes in for a model that isn't loaded, the current model's kvcache is saved, then the model is unloaded and swapped for the requested model. When the original model is requested again, the saved kvcache is restored.
- Models are served using the best-possible configuration. If you pass the flag
cache-type-k/v: q8_0, the model will initially be served atf16precision, and will degrade to a minimum ofq8_0.- If you do not specify a precision argument, the minimum is set to
q4_0(this will not be used unless space is needed).
- If you do not specify a precision argument, the minimum is set to
- When a model is loaded for the first time, llama-manager calculates strategy breakpoints and displays them as a printed table. Use
--calc-breakpointsto do this on startup, instead. - Models unload after 10 minutes of idle by default, which also resets kvcache and strategies. Pass
--sleep-idle-seconds <n>to change it (0disables).- NOTE: Currently, idle acts as a 'reset' for strategy application, so running with a timeout is recommended.
-cis ignored: context is sized reactively as strategies are applied- CLI args take priority over YAML
- Unknown YAML fields/CLI args are passed to llama-server
See --help for the full flag list.
When serving a model, llama-manager reactively applies strategies to free up device space, expanding the context window on-demand. llama-manager recognizes the following strategies:
disable-spec: Disable speculative decodingmmproj-to-cpu: Move mmproj to CPUquantize-kv-q8: Quantize kvcache to q8_0quantize-kv-q4: Quantize kvcache to q4_0
By default, strategies are applied in the following order: [disable-spec, mmproj-to-cpu, quantize-kv-q8, quantize-kv-q4]. This order can be changed via the CLI flag --ladder (or by editing your config.yaml. See the example).
Strategies are displayed when a model is loaded for the first time:
════════════════════════════════════════════════════════════════════════════
qwen3.8-27b baseline: 167,680 tokens device: 31 GiB
════════════════════════════════════════════════════════════════════════════
i strategy ctx (tokens) gain (tokens) weights / ctx GiB
──────────────────────────────────────────────────────────────────────────
0 baseline 167,680 17.13 / 13.14
1 disable-spec 200,960 (+33,280) 17.13 / 13.16
2 mmproj-to-cpu 218,880 (+17,920) 16.02 / 14.27
3 quantize-kv-q8 262,144 (+43,264) 16.02 / 10.40
──────────────────────────────────────────────────────────────────────────
final ctx: 262,144 tokensllama-manager uses my fork of llama.cpp. This version has a few notable changes:
- New:
common_init_result::reinit_context- This method factors out some common model initialization logic from
common_init_result's constructor, and defines a methodreinit_contextto reset a model's existing context and reinit using the factored logic.
- This method factors out some common model initialization logic from
- New:
fit.cpp::common_fit_for_reload- Performs fit calculations while assuming the model's weights are already in place. (Needs work)
- New:
server_context_impl::reload_model- Acts as the reload analogue to
server_context_impl::load_model. This method performs similar steps toload_model, except that it assumes model weights have already been loaded, and instead callsreinit_contextrather thancommon_init_from_params. - Note that if
n_ctx: 0is passed in,reload_modelperforms a fit calculation to reload to the max possible ctx (similar toload_modelfit).
- Acts as the reload analogue to
- New HTTP endpoint:
POST /reload- Exposes
reload_modelas an HTTP endpoint, returning the newn_ctxafter reloading.POST /reloadaccepts input in the formReloadParams(see the type definition in types.ts).
- Exposes
- New HTTP endpoint:
GET /memory- Query the amount of space a currently-loaded model occupies on each backend device, broken down by component. Outputs
MemoryResponse(see the type definition in types.ts).
- Query the amount of space a currently-loaded model occupies on each backend device, broken down by component. Outputs
- Modified:
POST /slots/:id-slot(fix prompt reuse for swa/hybrid/recurrent models)?action=save: adds an additional 'sidecar' save file that saves prompt checkpoints?action=restore: reads the aforementioned sidecar to restore prompt checkpoints- (Here, I adapted a solution from this issue)
- Modified:
POST /slots/:id-slot(convert kvcache precision)?action=restore: when restoring a slot, automatically convert between f16 / q8_0 / q4_0 precision, rather than rejecting. (Needs work)
The llama.cpp work is admittedly a little messy in places. I'm still working on cleaning/polishing it, as I think these features are genuinely useful and would like to contribute upstream. I'm releasing it now to get feedback from the community, as getting llama.cpp maintainer eyes on PRs has proved quite challenging so far!
- Expects inference to be performed on a single GPU; does not support CPU inference. If you want support for CPU/multi-device, please open an issue.
- Hadamard rotation is disabled to simplify llama.cpp-side slot restore code.
- Strategy application is not optimized to serve multiple users - degradation strategies are applied model-wide rather than per-request. Fixing this requires more granular kvcache and model management (see Future Work below).
- I've only tested this extensively on my server. YMMV; please open an issue if you find bugs or crashes. My specs:
- Ubuntu Server
- RTX 5090
- CUDA v13.1
- Models I've tested:
- Qwen3
- Qwen3.8
- Gemma4
- Smarter kv growth and model swapping (to reduce swap time and allow inference to multiple models at once)
- Better kvcache management:
- Attach kvc to consumer via api key
- Serve model based on input tokens / cache hit rate (each request is served at precisely the config it needs, rather than having model-wide config)
- Additional degradation strategies:
- Move layers between GPU/CPU
- Mmproj "on demand" (bring mmproj to GPU temporarily/as-needed to process an image, then evict it and carry result into prompt processing)
- kvu/batching/parallelization strategies