Local Multi-Model LLM Inference
1.9K

Preprint Paper Link (https://doi.org/10.32388/7MLDM5)
🤩 Star this repository - It helps others discover SnapLLM 🤩
Features | Installation | Quick Start | API Reference | Architecture | Demo Videos | Contributing | Sponsors
Developed by Mahesh Vaikri
AroorA AI Labs
SnapLLM is a local inference engine built on llama.cpp and stable-diffusion.cpp. It can keep multiple models loaded and select an already-resident model without reloading its weights. Actual load, switch, and generation latency depends on model size, hardware, memory pressure, and build configuration; benchmark your own workload before setting latency targets.
One common single-resident-model workflow:
Load Model A
Use Model A
Unload Model A
Load Model B
Use Model B
Load Model A
Load Model B
Select A or B without an avoidable reload while both remain resident
| Feature | Description |
|---|---|
| Resident Model Selection | Select between loaded models without reloading their weights |
| Reload After Eviction | Memory mapping can benefit from the operating-system page cache |
| Multi-Model Management | Load and manage multiple models simultaneously |
| GPU/CPU Hybrid | Automatic layer distribution based on available VRAM |
| OpenAI-Compatible API | Drop-in replacement for OpenAI API |
| Multi-Modal Support | LLM, Vision (VLM), and Stable Diffusion models |
| KV Cache Persistence | Persist context state and use indexed cache lookup; generation work still depends on query and context size |
| Web UI | React dashboard served by Vite at localhost:9780; the API listens on localhost:6930 |
| Desktop Application | Beautiful React-based UI for model management |
SnapLLM reports operation timing at runtime where available. Published end-to-end latency guarantees require a reproducible benchmark that records the model, quantization, prompt, build flags, hardware, cache state, and sampling configuration. No such guarantee is made by this README.
# Clone the repository
git clone https://github.com/snapllm/snapllm.git
cd snapllm
# Build with CUDA support
build_gpu.bat
build_cpu.bat
chmod +x build.sh
./build.sh gpu
# Check the build
build_gpu/bin/snapllm --help
# Start the server
build_gpu/bin/snapllm --server --port 6930
Start_Server.bat
This launcher auto-detects the newest build and starts the server on 127.0.0.1:6930.
If the desktop app is built, it will launch the UI; otherwise it opens the browser.
build_gpu\bin\snapllm.exe --server --host 127.0.0.1 --port 6930
build_gpu\bin\snapllm.exe --server --host 127.0.0.1 --port 6930 --load-model mymodel D:\Models\mymodel.gguf
chmod +x start_server.sh
./start_server.sh
The Compose service binds inside its container on 0.0.0.0, publishes only to
host loopback, and requires a runtime API key:
mkdir -p workspace models
export SNAPLLM_API_KEY='replace-with-a-random-value-at-least-32-characters'
docker compose up --build
Tagged releases publish a CPU image to Docker Hub as
<dockerhub-user>/snapllm:<version> and latest through the protected
docker-publish GitHub environment. Configure repository secrets
DOCKERHUB_USERNAME and DOCKERHUB_TOKEN, then create a release tag. The
published image listens on 0.0.0.0:6930 inside the container and includes a
health check for /health. The -cuda image uses the NVIDIA CUDA 12.6 runtime
and requires NVIDIA Container Toolkit plus --gpus all; the unqualified image
is CPU-only.
The container runs as an unprivileged user with a read-only root filesystem, dropped Linux capabilities, a read-only model mount, and a writable workspace mount. Changing the host port mapping to a public interface is a deployment decision: keep the API key enabled and put TLS plus network access controls in front of SnapLLM.
SNAPLLM_SERVER_EXE: Full path to the SnapLLM binarySNAPLLM_HOST: Host bind address (default 127.0.0.1)SNAPLLM_PORT: Server port (default 6930)SNAPLLM_WORKSPACE_ROOT: Workspace root directorySNAPLLM_API_KEY: Runtime-only API key; required when binding beyond loopbackSNAPLLM_CORS_ORIGINS: Comma-separated exact browser origins to allow# Start with a model pre-loaded
./snapllm --server --port 6930 --load-model mymodel /path/to/model.gguf
# Or start empty and load models via API
./snapllm --server --port 6930
# Load first model
curl -X POST http://localhost:6930/api/v1/models/load \
-H "Content-Type: application/json" \
-d '{
"model_id": "llama3",
"file_path": "/models/llama-3-8b-instruct.Q5_K_M.gguf"
}'
# Load second model
curl -X POST http://localhost:6930/api/v1/models/load \
-H "Content-Type: application/json" \
-d '{
"model_id": "gemma",
"file_path": "/models/gemma-2-9b-it.Q5_K_M.gguf"
}'
# Select gemma without reloading it if it is still resident
curl -X POST http://localhost:6930/api/v1/models/switch \
-H "Content-Type: application/json" \
-d '{"model_id": "gemma"}'
# OpenAI-compatible chat endpoint
curl -X POST http://localhost:6930/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gemma",
"messages": [
{"role": "user", "content": "Explain quantum computing in simple terms"}
],
"max_tokens": 256
}'
Start_Server.bat starts the API and, when the desktop binary is unavailable, starts the Vite web UI at
http://localhost:9780. The API health endpoint remains available at
http://localhost:6930/health.
To run the standalone desktop app (development mode):
cd desktop-app
npm install
npm run dev
# Open http://localhost:9780
http://localhost:6930
The default loopback listener can be used without an API key. For any
non-loopback bind, set SNAPLLM_API_KEY to 32–4096 visible ASCII characters
before starting the process. Send that value as either
Authorization: Bearer <key> or X-API-Key: <key>. The key is read from the
environment only and is not persisted or returned by the API.
All requests are checked against the configured Host. Browser requests also
use an exact Origin allowlist; add trusted origins with repeatable
--cors-origin options or SNAPLLM_CORS_ORIGINS. Wildcards and reflected
origins are not accepted. Model and workspace paths are canonicalized and must
remain inside their configured roots.
| Method | Endpoint | Description |
|---|---|---|
| GET | / | API information |
| GET | /health | Server health check |
| GET | /v1/models | List models (OpenAI format) |
| GET | /api/v1/models | List models (extended info) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/models/load | Load a model |
| POST | /api/v1/models/switch | Select an already-loaded model |
| POST | /api/v1/models/unload | Unload a model |
| DELETE | /api/v1/models/{id} | Delete a model |
| POST | /api/v1/models/scan | Scan folder for models |
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/chat/completions | Chat completion (OpenAI) |
| POST | /api/v1/generate | Text generation |
| POST | /api/v1/generate/batch | Batch generation |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/vision/generate | Analyze images |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/diffusion/generate | Generate images |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/contexts/ingest | Pre-compute KV cache |
| GET | /api/v1/contexts | List contexts |
| POST | /api/v1/contexts/{id}/query | Query with indexed cache lookup |
| DELETE | /api/v1/contexts/{id} | Delete context |
POST /api/v1/models/load
Request:
{
"model_id": "medical-llm",
"file_path": "D:/Models/medicine-llm.Q8_0.gguf",
"model_type": "auto"
}
Response:
{
"status": "success",
"message": "Model loaded: medical-llm",
"model": "medical-llm",
"model_type": "Text LLM",
"load_time_ms": 2500.5,
"active": true
}
POST /v1/chat/completions
Request:
{
"model": "medical-llm",
"messages": [
{"role": "system", "content": "You are a medical assistant."},
{"role": "user", "content": "What are the symptoms of diabetes?"}
],
"max_tokens": 512,
"temperature": 0.7,
"stream": false
}
Chat completions stream by default. Set "stream": false when a buffered
JSON response is required.
Response:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1234567890,
"model": "medical-llm",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Common symptoms of diabetes include..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 150,
"total_tokens": 175,
"tokens_per_second": 64.5
}
}
POST /api/v1/models/load
Request:
{
"model_id": "gemma-vision",
"file_path": "/models/gemma-3-4b-it.gguf",
"model_type": "vision",
"mmproj_path": "/models/mmproj-gemma-3-4b.gguf"
}
POST /api/v1/vision/generate
Request:
{
"prompt": "Describe this image in detail",
"images": ["<base64-encoded-image>"],
"max_tokens": 256
}
Response:
{
"status": "success",
"response": "The image shows a beautiful sunset over mountains...",
"model": "gemma-vision",
"generation_time_s": 2.5,
"tokens_per_second": 57.5
}
POST /api/v1/contexts/ingest
Request:
{
"content": "Your large document text here...",
"model_id": "llama3",
"name": "company-handbook",
"ttl_seconds": 86400
}
Response:
{
"status": "success",
"context_id": "ctx_abc123",
"token_count": 5000,
"storage_size_mb": 125.5,
"tier": "hot",
"ingest_time_ms": 1500.5
}
POST /api/v1/contexts/ctx_abc123/query
Request:
{
"query": "What is the vacation policy?",
"max_tokens": 100
}
Response:
{
"status": "success",
"context_id": "ctx_abc123",
"response": "According to the handbook, employees receive...",
"cache_hit": true,
"latency_ms": 15.2
}
SnapLLM uses a vPID architecture to select already-loaded models without an avoidable weight reload:
+--------------------------------------------------------------------+
| SnapLLM Server |
+--------------------------------------------------------------------+
| Model A (vPID 1) | Model B (vPID 2) | Model C (vPID 3) | ... |
+---------+----------+---------+----------+---------+----------+-----+
| | |
+--------------------+--------------------+
|
v
+--------------------------------------------------------------------+
| Unified Memory Manager |
| HOT (GPU VRAM) | WARM (CPU RAM) | COLD (SSD) |
+--------------------------------------------------------------------+
| HTTP API Server (OpenAI-compatible) |
+--------------------------------------------------------------------+
Request flow (simplified):
Client -> HTTP API -> Router -> Model Manager -> Active Model -> Response
| ^
v |
vPID Cache (L1/L2)
| Level | Name | Purpose |
|---|---|---|
| L1 | Model Cache | Model workspace metadata and engine-managed cache data |
| L2 | Context Cache | KV cache persistence with hash-indexed lookup |
| Tier | Storage | Relative access | Capacity constraint |
|---|---|---|---|
| HOT | GPU VRAM | Highest | Available VRAM |
| WARM | CPU RAM | Intermediate | Available system RAM |
| COLD | SSD | Lowest | Available storage |
SnapLLM_Workspace/
|-- index.json # Model registry
|-- medicine-llm/
| `-- Q8_0/
| `-- workspace.bin # Engine-managed workspace
|-- legal-llama/
| `-- Q4_K_M/
| `-- workspace.bin
|-- gemma-3-4b/
| `-- Q5_K_M/
| `-- workspace.bin
|-- diffusion/
| `-- sd15/
| `-- workspace.bin
`-- contexts/ # vPID L2 KV caches
|-- hot/
|-- warm/
`-- cold/
SnapLLM includes a beautiful, modern desktop application built with React and Vite.
cd desktop-app
npm install
npm run dev
# Open http://localhost:9780
./snapllm --server [OPTIONS]
Options:
--port PORT Server port (default: 6930)
--host HOST Bind address (default: 127.0.0.1)
--cors-origin ORIGIN Allow an exact browser origin (repeatable)
--workspace-root PATH Workspace directory
--ui-dir PATH Web UI directory (auto-detected)
--load-model NAME PATH Pre-load a model
./snapllm --load-model NAME PATH [OPTIONS]
Options:
--prompt TEXT Generate text from prompt
--max-tokens N Maximum tokens to generate
--temperature N Sampling temperature
--multi-model-test Run multi-model switching benchmark
--list-models List all loaded models
--stats Show cache statistics
./snapllm --load-diffusion NAME PATH [OPTIONS]
Options:
--generate-image PROMPT Generate image from text
--output PATH Output image path
--width N Image width (default: 512)
--height N Image height (default: 512)
--steps N Sampling steps (default: 20)
--cfg-scale N CFG scale (default: 7.0)
--seed N Random seed (-1 for random)
--negative PROMPT Negative prompt
./snapllm --multimodal MODEL MMPROJ [OPTIONS]
Options:
--image PATH Input image file
--vision-prompt TEXT Prompt with <__media__> marker
--max-tokens N Maximum tokens to generate
| Variable | Description | Default |
|---|---|---|
SNAPLLM_HOME | Workspace root directory | Platform default |
SNAPLLM_MODELS_PATH | Default models directory | Platform default |
SNAPLLM_CONFIG_PATH | Server config file path | Platform default |
SNAPLLM_API_KEY | Runtime-only API key (required off loopback) | Unset |
SNAPLLM_CORS_ORIGINS | Comma-separated exact browser origins | Unset |
SNAPLLM_MAX_ACTIVE_INFERENCES | Optional concurrent inference slots; bounded by HTTP workers and defaults to 1 for GPU safety | 1 |
We welcome contributions! Here's how to get started:
# Clone the repository
git clone https://github.com/snapllm/snapllm.git
cd snapllm
# Build debug version
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug
cmake --build .
# Run tests
ctest --output-on-failure
I* prefix for interfacesgit checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Support SnapLLM Development
SnapLLM is an open-source project maintained by passionate developers. Your sponsorship helps us:
SnapLLM is released under the MIT License.
Security issues should be reported privately as described in SECURITY.md.
MIT License
Copyright (c) 2024-2026 SnapLLM Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
SnapLLM is built on the shoulders of giants:
Switch models in a snap!

Follow SnapLLM in LinkedIn
SnapLLM

Creator: Mahesh Vaikri
Developed by AroorA AI Labs
Made with care for the AI community
Content type
Image
Digest
sha256:fb29f9b23…
Size
39.4 MB
Last updated
about 1 month ago
docker pull maheshvaikri/snapllm