Add a Custom Model
This guide explains every supported way to add a custom model to Lemonade Server, and documents the files and naming rules behind them.
There are three ways to get a model into Lemonade:
| Option | Use when | What happens |
|---|---|---|
| Pull a model | The model is published on Hugging Face or ModelScope | Lemonade downloads it into your model store and registers it |
| Point at a folder of GGUFs | You already have GGUF files on disk, or share a library with LM Studio or llama.cpp | Lemonade lists them where they sit and downloads nothing |
| Edit the JSON files by hand | You need full control over a definition and can restart lemond |
You describe the model yourself in user_models.json |
Pulling covers most cases and has several front ends that all end in the same registration; pick one under Pull a Model.
Pull a Model
Pull by Hugging Face or ModelScope Checkpoint
A source-less pull uses the server's configured default_model_source (shipped default: Hugging Face), so existing commands keep working unchanged:
lemonade pull org/repo
lemonade pull org/repo:Q4_K_M
Choose ModelScope explicitly when the model is hosted or mirrored there:
lemonade pull --source modelscope org/repo
lemonade pull --source modelscope org/repo:Q4_K_M
Full model URLs are accepted and select the registry automatically:
lemonade pull https://huggingface.co/unsloth/Qwen3-8B-GGUF
lemonade pull https://modelscope.cn/models/Qwen/Qwen3-8B-GGUF
For supported repository layouts, Lemonade lists GGUF quantizations and sharded variants, auto-detects mmproj-*.gguf files, infers common labels, and presents the same interactive variant menu for either registry.
Examples:
# Hugging Face (shipped default source)
lemonade pull unsloth/Qwen3-8B-GGUF:Q4_K_M
# ModelScope
lemonade pull --source modelscope Qwen/Qwen3-8B-GGUF:Q4_K_M
The selected source is persisted with the model registration. Variant discovery, downloads, display links, cache lookup, deletion, and future update checks therefore continue to use the same registry. All checkpoints belonging to one model use one registry source.
Private repositories can be authenticated with HF_TOKEN for Hugging Face or MODELSCOPE_API_TOKEN for ModelScope. MODELSCOPE_ACCESS_TOKEN is also accepted as a compatibility alias. Custom endpoints can be supplied with HF_ENDPOINT or MODELSCOPE_ENDPOINT.
Cache and revision behavior
Both registries share Lemonade's configured models_dir. Hugging Face keeps its established models--org--repo directory names. ModelScope uses the collision-free modelscope--models--org--repo namespace. Under either directory Lemonade uses the same snapshots/<id> and refs/main structure consumed by runtime resolution and cache cleanup.
Hugging Face snapshots use the immutable commit returned by the Hub. ModelScope downloads use its branch/tag revision (normally master); Lemonade derives a stable local snapshot fingerprint from the normalized remote file tree so a changed ModelScope repository is detected even when the branch name stays the same.
The desktop app's manual model form exposes the same source selector. The browse/search catalog remains Hugging Face-backed in this first version; ModelScope models can be added by repository ID through the manual form, CLI, or API.
Pull with an Explicit Recipe and Checkpoints
Give the model a name plus --checkpoint and --recipe when you need full control: multiple checkpoints, a non-default recipe, or custom labels.
lemonade pull user.NAME --source SOURCE --checkpoint TYPE CHECKPOINT --recipe RECIPE [--label LABEL ...]
The user. prefix is optional; the CLI adds it when you leave it off.
Examples:
# Register and pull a custom GGUF model with a main checkpoint
lemonade pull user.Phi-4-Mini-GGUF \
--checkpoint main unsloth/Phi-4-mini-instruct-GGUF:Q4_K_M \
--recipe llamacpp
# Register and pull a vision model with main + mmproj
lemonade pull user.Gemma-3-4b \
--checkpoint main ggml-org/gemma-3-4b-it-GGUF:Q4_K_M \
--checkpoint mmproj ggml-org/gemma-3-4b-it-GGUF:mmproj-model-f16.gguf \
--recipe llamacpp
# Register a model with multiple labels
lemonade pull user.MyCodingModel \
--checkpoint main org/model:Q4_0 \
--recipe llamacpp \
--label coding \
--label tool-calling
Supported registration flags:
| Flag | Description |
|---|---|
--source SOURCE |
Remote registry for every checkpoint in this model: huggingface or modelscope. When omitted, the server's configured default_model_source applies. |
--checkpoint TYPE CHECKPOINT |
Add a checkpoint entry. Repeat for multi-file models such as main + mmproj or main + vae. |
--recipe RECIPE |
Recipe to associate with the new user.* model. Common values: llamacpp, llamacpp-hrx, whispercpp, moonshine, kokoro, sd-cpp, flm, ryzenai-llm, vllm, thenoise, ds4, thinksound, acestep, onnxruntime, trellis, openmoss, collection.omni. |
--label LABEL |
Add a label to the new model. Repeatable. Valid labels include chat, coding, dflash, embeddings, hot, mtp, reasoning, reranking, tool-calling, vision. When no deployment label is given, the recipe's default is added — chat for llamacpp, flm, ryzenai-llm and vllm; transcription for whispercpp; image for sd-cpp; and so on. |
--components MODEL [MODEL ...] |
Components for an omni collection (see below). Use with --recipe collection.omni. |
Pull an Omni Collection
A collection is a meta-model made up of components. An omni collection is the recipe type behind Lemonade Omni Models — registered with recipe: "collection.omni".
Components must already be registered as built-in models or previously pulled user.* models. Components do not need to be downloaded already; missing component files are pulled by the same command.
lemonade pull user.MyKit \
--recipe collection.omni \
--components Qwen3-0.6B-GGUF Whisper-Tiny SD-Turbo
lemonade load user.MyKit loads every component. lemonade delete user.MyKit removes only the collection entry; component files stay on disk.
Build an Omni Model in the Desktop App
The desktop app offers a UI-driven path to register the same recipe: "collection.omni" entry — useful when you want to swap in a different planner LLM or a different image/ASR/TTS backbone without waiting for a new built-in Lemonade Omni Model to ship.
- Register or download the concrete models you want to use in Model Manager.
- In the desktop app menu bar, open File > New Omni Model > Manually (or From JSON to import an exported one).
- Pick one planner LLM and any optional models for image generation, image editing, vision analysis, speech-to-text, and text-to-speech.
- Save the Omni Model.
- Select the new
user.<name>entry in the chat model picker — it appears alongside the built-in omni models under the Lemonade category.
Custom Omni Models are registered through the same POST /v1/pull path with recipe: "collection.omni" that the built-ins and the CLI flow above use. They live under the server's user.* namespace, so a custom Omni Model named MyKit is addressable as user.MyKit. They behave like built-in omni models for routing purposes: the selected planner LLM remains the loop driver that decides when to call tools, and optional role models are only loaded/used when their corresponding tool is called.
The Omni Model editor only offers already-registered compatible models for each role:
| Omni Model role | Tool unlocked | Required model capability |
|---|---|---|
| LLM | Chat loop and tool calls | Concrete chat model, preferably tool-calling capable |
| Vision / image analysis | analyze_image |
vision label |
| Image generation | generate_image |
image label |
| Image editing | edit_image |
edit label |
| Speech-to-text | transcribe_audio |
audio or transcription label |
| Text-to-speech | text_to_speech |
tts or speech label |
If a component model is deleted later, the Omni Model entry remains registered but is hidden from the chat picker until every referenced component is available again.
The editor also exposes a System Prompt field, pre-filled with the shipped default so you can see the text you'd be replacing. Edit it to override the default for this collection only; the override stays a template — both the {tool_list} and {tool_guidance} placeholders are required in any custom prompt and the editor blocks save/export when either is missing, because the server expands them at runtime based on which components are present. A collection whose textarea matches the default — or that has been reset via Reset to default — stores no override and keeps tracking whatever the global default is at runtime.
Pull from the API
The /v1/pull endpoint accepts the same model registration fields as the CLI. Set source to huggingface or modelscope; when omitted, the server's configured default_model_source applies. The server canonicalizes and persists the resolved value for later update checks. Use this when integrating Lemonade into another app or script:
curl -X POST http://localhost:13305/v1/pull \
-H "Content-Type: application/json" \
-d '{
"model_name": "user.MyModel",
"recipe": "llamacpp",
"source": "modelscope",
"checkpoint": "org/repo:Q4_0"
}'
For multi-file models, send checkpoints:
curl -X POST http://localhost:13305/v1/pull \
-H "Content-Type: application/json" \
-d '{
"model_name": "user.Gemma-3-4b",
"recipe": "llamacpp",
"checkpoints": {
"main": "ggml-org/gemma-3-4b-it-GGUF:Q4_K_M",
"mmproj": "ggml-org/gemma-3-4b-it-GGUF:mmproj-model-f16.gguf"
},
"labels": ["vision"]
}'
For an omni collection, send components:
curl -X POST http://localhost:13305/v1/pull \
-H "Content-Type: application/json" \
-d '{
"model_name": "user.MyKit",
"recipe": "collection.omni",
"components": ["Qwen3-0.6B-GGUF", "Whisper-Tiny", "SD-Turbo"]
}'
Share a Collection Between Machines
lemonade export <collection> (and the desktop app's Export button) writes a collection file: the
collection's /v1/models/{id} object normalized into
an import-ready /v1/pull body. The file carries model_name,
recipe, components, and a models array embedding each component's definition, so it is
self-contained — the importing machine does not need any of the components registered beforehand.
Exported files never contain the user-specific runtime fields suggested, created, or downloaded —
the server regenerates those on import (suggested is set to true for registered models;
downloaded is computed from local files).
The same file works, verbatim, in three places:
lemonade import <CollectionName>.jsonon the CLI (or File > New Omni Model > From JSON in the desktop app).POST /v1/pullwith the file contents as the request body.- Uploaded to a Hugging Face or ModelScope model repo named after the collection, so that the
repo contains
<RepoName>.json. Pull it with the corresponding source, for examplelemonade pull <org>/<repo>orlemonade pull --source modelscope <org>/<repo>. Lemonade downloads the manifest, preserves its registry provenance, then registers and downloads every component. Inline component definitions inherit the collection source unless they explicitly declare their own source.
On import, component names that are already registered keep their local definition (differences from
the embedded definition are logged as warnings); unknown components are registered as user.* models
from the embedded definitions.
Example collection file:
{
"model_name": "user.MyKit",
"recipe": "collection.omni",
"checkpoints": { "main": "" },
"components": ["Qwen3-0.6B-GGUF", "Whisper-Tiny"],
"models": [
{
"model_name": "Qwen3-0.6B-GGUF",
"recipe": "llamacpp",
"checkpoints": { "main": "unsloth/Qwen3-0.6B-GGUF:Q4_0" },
"labels": ["reasoning"],
"recipe_options": {},
"size": 0.38
},
{
"model_name": "Whisper-Tiny",
"recipe": "whispercpp",
"checkpoints": {
"main": "ggerganov/whisper.cpp:ggml-tiny.bin",
"npu_cache": "amd/whisper-tiny-onnx-npu:ggml-tiny-encoder-vitisai.rai"
},
"labels": ["transcription", "realtime-transcription"],
"recipe_options": {},
"size": 0.075
}
],
"labels": [],
"recipe_options": {}
}
Model naming spec
Lemonade tracks three sources of models. Every model has a canonical ID of the form <source>.<bare-name>:
| Canonical ID | Source |
|---|---|
user.NAME |
Model registered via lemonade pull (entry in user_models.json) |
extra.NAME |
Model imported from extra_models_dir, see Imported models |
builtin.NAME |
Model compiled into Lemonade's built-in catalog (server_models.json) |
The bare name NAME is an alias that always resolves to whichever source wins precedence for that name. Precedence is registered > imported > built-in.
Where the model set comes from
Each catalog contributes entries to one build, and a later catalog cannot take a name an earlier one claimed.
| Catalog | Entries come from | Required | Config key |
|---|---|---|---|
| Built-in registry | The model list shipped with the server. | no | — |
| Registrations | Every model the user pulled or registered. | no | — |
| Extra models directory | A scan of the extra models directory; GGUF files are identified by their header, not their name. | no | extra_models_dir |
Which catalogs depend on what this machine has installed — a backend's own models, or a configured cloud provider's listing — is not listed here, because this page is the same on every machine.
What the API emits
/v1/models, /v1/models/{id}, lemonade list, and the Ollama /api/tags endpoint emit each model with an id set to either:
- the bare name if the model is the precedence-winner for its bare name, or
- the canonical-prefixed ID if another source outranks it on the same bare name.
For each bare name with collisions, the response contains one bare row plus one canonical-prefixed row per shadowed source.
What input forms are accepted
Anywhere a model name is accepted (request bodies, CLI args, URL path parameters), all four forms work:
- the bare name
NAME— resolves to the winner user.NAME— always the registered model (404 if none)extra.NAME— always the imported model (404 if none)builtin.NAME— always the built-in model (404 if none)
lemonade pull rejects model names starting with extra. or builtin. since those prefixes are reserved.
An imported model also answers to names derived from the directory layout around it, so a name keeps working after that layout changes. See Model Naming Scheme.
CLI vs. GUI display
The CLI (lemonade list) prints the API id verbatim. That means the Name column is always copy-paste-safe — every cell is a valid input to lemonade load, lemonade delete, lemonade run, etc.
The Tauri desktop app and the web app apply a display transformation on top of the API id: bare ids render as NAME, and canonical-prefixed ids render as NAME (registered) / NAME (imported) / NAME (builtin). The suffix appears only for shadowed sources.
Model Aliases (aliases.json)
You can define custom aliases for model names in aliases.json — alternative high-level names that resolve to any target model name. The target model does not define or need to be aware of being aliased:
- Standalone Alias Management: Model aliases live in a dedicated
aliases.jsonfile in the Lemonade cache directory (<cache_dir>/aliases.json). They are managed via the CLI (lemonade alias add ALIAS TARGET_MODEL) or administrative REST API (POST /internal/aliases). - Persistence:
aliases.jsonis automatically loaded on server startup and saved on every alias mutation. - Decoupled Architecture: Aliases function purely at the symbolic routing layer. They do not pollute model definition files (
user_models.jsonorserver_models.json).
Conceptual: Definition Layer vs. Symbolic Routing Layer
Lemonade separates model definition from model routing to provide stable API endpoints for client applications.
- Definition Layer (User Models and Recipes): Defines concrete models and execution parameters (checkpoints, hardware targets, context sizes, and backend engines). Use this layer to introduce new models, adjust inference parameters, or support new hardware.
- Symbolic Routing Layer (Model Aliases): Defines client addressing. Aliases function as symbolic links, decoupling client SDKs from underlying model definitions.
Use Model Aliases to abstract model identity from application code. This enables:
- Environment-independent naming: An application can request an alias like
default-llm. Developers can mapdefault-llmto a lightweight local model, while production servers map it to a larger model. The application code remains unchanged. - Active-standby failover: Administrators can redirect an alias to a fallback model if a local hardware model degrades.
- Capability preservation: Aliases inherit all capabilities of their target models. You can alias a speech-to-text model to
system-sttor an image generator tosystem-image-genwithout losing functionality or LRU pool classification.
OpenAI SDK & LiteLLM Integration Example
When using the OpenAI SDK or LiteLLM, target the alias instead of the concrete model:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:13305/v1", api_key="dummy")
# The client requests the alias "production-llm"
response = client.chat.completions.create(
model="production-llm",
messages=[{"role": "user", "content": "Hello"}]
)
If the underlying target model changes, update the alias on the server using lemonade alias add production-llm new-target or POST /internal/aliases. Client application code requires no updates.
Alias Resolution & Target Model Independence
When an alias is specified in any API request (e.g., /v1/chat/completions, /v1/embeddings, /v1/load) or CLI command, Lemonade resolves the alias to its underlying target model ID (e.g., user.MyCustomModel or Qwen3-0.6B-GGUF).
- Primary Model Precedence: Concrete model names (
user.*,extra.*,builtin.*) always take precedence over aliases. Creation of an alias that collides with an existing primary model ID is rejected. - Target Independence: Aliases function as symbolic links and can target any model ID or Hugging Face repository, even before the target model is pulled or registered locally.
Independent Listing in /v1/models
Every registered alias is exposed as an independent model entry in /v1/models, /v1/models/{id}, and lemonade list. Alias entries set id to the alias name while sharing the target model's recipe, downloaded status, and backend configuration.
Five reference cases
| Sources | /v1/models ids |
Resolution |
|---|---|---|
built-in Qwen2.5-Coder only |
Qwen2.5-Coder |
Qwen2.5-Coder, builtin.Qwen2.5-Coder → built-in |
built-in Foo + registered Foo |
Foo, builtin.Foo |
Foo/user.Foo → user; builtin.Foo → built-in |
built-in Bar + registered Bar + extra Bar |
Bar, extra.Bar, builtin.Bar |
Bar/user.Bar → user; extra.Bar → extra; builtin.Bar → built-in |
built-in Baz + extra Baz |
Baz, builtin.Baz |
Baz/extra.Baz → extra; builtin.Baz → built-in |
registered MyModel only |
MyModel |
MyModel/user.MyModel → user; builtin.MyModel → 404 |
Imported models (extra_models_dir)
extra_models_dir is a directory that lemond scans recursively for .gguf files, listing everything it finds alongside your other models. The files stay where they are and Lemonade reads them in place. It is empty by default, which disables the feature.
lemonade config set extra_models_dir="/home/you/.lmstudio/models"
lemonade pull still downloads into models_dir. Point extra_models_dir at files you already have:
| Source | Typical path |
|---|---|
| LM Studio | ~/.lmstudio/models, or C:\Users\You\.lmstudio\models |
| A Hugging Face or ModelScope cache | ~/.cache/huggingface/hub |
| Your own folder of GGUFs | anywhere readable, absolute or relative |
Note: With the Linux
.deb/.rpmpackages,lemondruns as thelemonadesystem user under a systemd unit withProtectHome=yes, which hides/homefrom the service. A folder under/homeis then rejected as "not accessible by the Lemonade server (Permission denied)" even when its permissions allow thelemonadeuser. Either keep the models outside/home(for example/srv/models), or expose just that folder to the service with a drop-in, and make sure thelemonadeuser can read the folder:sudo mkdir -p /etc/systemd/system/lemond.service.d printf '[Service]\nProtectHome=tmpfs\nBindReadOnlyPaths=/home/you/.lmstudio/models\n' | sudo tee /etc/systemd/system/lemond.service.d/models.conf sudo systemctl daemon-reload && sudo systemctl restart lemond lemonade config set extra_models_dir="/home/you/.lmstudio/models"The rejected value was not saved, so run
config setagain after the restart.ProtectHome=tmpfskeeps the rest of/homehidden.The drop-in exposes only that one folder. If you later point
extra_models_dirat a path outside it, changeBindReadOnlyPathsto match, then rundaemon-reloadand restartlemondagain.config setalone is not enough, because systemd sets up the mount when the service starts.
Model Naming Scheme
A model's name comes from the directory layout around its files. A path uses the Hugging Face / ModelScope cache rules only when its GGUFs are inside a recognized cache layout such as models--<org>--<repo>/snapshots/<commit>/... or modelscope--models--<org>--<repo>/snapshots/<commit>/.... A folder merely named like a cache repo is not sufficient. All other paths use the non-HF/MS rules below.
These notes apply to both:
- Files whose names declare the same shard series are one model, as in
model-Q4_K_M-00001-of-00003.gguf, and its name drops the shard suffix. The-,., and_separators are accepted before the shard index. - An
mmprojfile joins the model in its folder. - Names are assigned in a fixed order: files at the search root, then files in reserved directories, then folders, each group in sorted path order. A model at the search root keeps its name when a directory is reserved later.
- Every imported model is also addressable as
extra.<name>; see the model naming spec. Model aliases are a separate feature that you define yourself inaliases.json.
Non-HF/MS folders
| What is on disk | Name |
|---|---|
A .gguf file at the search root, or in a reserved directory |
the filename without .gguf |
| A folder holding one model | the folder name |
| A folder holding several quantization variants | one name per variant, each from its filename |
| A name another imported model already took, or an earlier folder owns | the name, qualified with the folder that contains it, then -2, -3 if that is also taken |
For example:
| Layout | Names |
|---|---|
Qwen3-8B-Q4_K_M.gguf |
Qwen3-8B-Q4_K_M |
Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf |
Qwen3-8B-GGUF |
Qwen3-8B-GGUF/ holding Qwen3-8B-Q4_K_M.gguf and Qwen3-8B-Q8_0.gguf |
Qwen3-8B-Q4_K_M, Qwen3-8B-Q8_0 |
Qwen3-8B-GGUF/ holding Qwen3-8B-Q4_K_M-00001-of-00002.gguf and Qwen3-8B-Q4_K_M-00002-of-00002.gguf |
Qwen3-8B-GGUF |
Qwen3-8B-GGUF/ holding Qwen3-8B-Q4_K_M.gguf and mmproj-Qwen3-8B-f16.gguf |
Qwen3-8B-GGUF |
Mixtral-GGUF/ holding a two-shard Mixtral-Q4_K_M set and a two-shard Mixtral-Q8_0 set |
Mixtral-Q4_K_M, Mixtral-Q8_0 |
Llama-Local-GGUF/ and Mistral-Local-GGUF/, each holding model-Q4_K_M.gguf and model-Q8_0.gguf |
model-Q4_K_M, model-Q8_0, Mistral-Local-GGUF-model-Q4_K_M, Mistral-Local-GGUF-model-Q8_0 |
alpha/Shared/ holding Llama-Q4_K_M.gguf and Llama-Q8_0.gguf, and beta/Shared/ holding Mistral-Q4_K_M.gguf |
Llama-Q4_K_M, Llama-Q8_0, beta-Shared |
Notes:
- Adding a second quantization variant to a folder renames the first model, as rows two and three show. The folder name keeps resolving and points at the first model alphabetically, so a request that used it still works. A model file that sorts earlier takes that pointer over, so name a specific model in scripts you intend to keep. When several folders share a name, the first in the order above owns it, in both its plain and
extra.forms.
HF/MS caches
| What is on disk | Name |
|---|---|
One model in the revision that refs/main points at |
the repo name, or <repo>-<folder> when the GGUFs sit in a subfolder |
| Several quantization variants in that revision | one name per variant, each from its filename |
| One model in a superseded revision | the active-style name prefixed with that revision's commit: <commit>-<repo> or <commit>-<repo>-<folder> |
| Several quantization variants in a superseded revision | one name per variant, each from its filename and prefixed with that revision's commit |
| A name another imported model already took | the name, qualified with its org or namespace |
For example:
| Layout | Names |
|---|---|
models--unsloth--Qwen3-8B-GGUF/snapshots/<commit>/Qwen3-8B-Q4_K_M.gguf |
Qwen3-8B-GGUF |
models--unsloth--Qwen3-235B-GGUF/snapshots/<commit>/Q4_K_M/ holding a shard set |
Qwen3-235B-GGUF-Q4_K_M |
a cache holding a live snapshots/<new>/ and a superseded snapshots/<old>/, each with Repo-Q4_K_M.gguf and Repo-Q8_0.gguf |
Repo-Q4_K_M, Repo-Q8_0, <old>-Repo-Q4_K_M, <old>-Repo-Q8_0 |
models--bartowski--Collide-7B-GGUF/ and models--unsloth--Collide-7B-GGUF/, each with an active snapshots/<commit>/Collide-7B-Q4_K_M.gguf |
Collide-7B-GGUF, unsloth-Collide-7B-GGUF |
Notes:
- A cache can hold several revisions. Names follow the revision
refs/mainpoints at, so updating a model moves its name to the new revision. - Unlike non-HF/MS folders, a cache folder's name does not keep resolving after the folder's models are renamed.
Choosing how an imported model runs
The top-level directory a model sits under selects its deployment mode. The reserved names are chat, embeddings, and reranking:
extra_models_dir/
├── chat/
├── embeddings/
└── reranking/
Files directly inside a reserved directory are listed as separate models, except where numbered shard names declare that they belong together. Nested folder models and per-variant models inherit the mode of their reserved top-level directory. Models at the root or under any other directory default to chat. The folder's old name, such asextra.embeddings, still works and points to its first file alphabetically.
Reserved names must match exactly. embeddings is reserved; Embedding, embedding, and embeddings 2 are ordinary directories. The mode comes from the top-level directory name alone, so embeddings/bge-reranker-v2.gguf is an embedding model.
A file with mmproj anywhere in its name is attached as the model's mmproj and adds the vision label. When several are present the first by filename wins, so the choice is stable across restarts. An mmproj sitting directly in a reserved directory is attached only when that directory holds one main model; if you keep several vision models in one reserved directory, give each model and its mmproj their own subdirectory.
Properties of an imported model
| Property | Value |
|---|---|
recipe |
llamacpp |
suggested |
true |
downloaded |
true |
labels |
custom, the directory-selected mode (chat by default), and vision if multimodal |
type |
Derived from labels |
size |
Sum of all .gguf file sizes in GB |
source |
extra_models_dir |
Deleting an imported model
Imported models are yours to manage, so /v1/delete on one returns an error naming the path to remove by hand.
Access and failure behavior
An extra_models_dir that already exists must be a directory the lemond process can enumerate; permission and I/O failures reject the config update and leave your current model view in place. A path that has yet to be created is accepted, so a directory watcher can pick it up later. See POST /internal/set.
During a scan, unreadable nested directories are skipped. Discovery is best-effort by design, so your registered and built-in catalogs stay intact through a filesystem failure.
Configuration files
Everything above is backed by two JSON files, both located in the Lemonade config directory. The next two sections document them field by field. You can edit them by hand; restart lemond for the changes to take effect.
| File | Purpose |
|---|---|
user_models.json |
Model registry — defines what models are available (checkpoint, recipe, etc.) |
recipe_options.json |
Per-model settings — configures how models run (context size, backend, etc.) |
If you used an installer from a Lemonade release, the config directory is typically:
| OS | Config directory |
|---|---|
| Linux systemd install | /var/lib/lemonade |
| Windows | %USERPROFILE%\.config\lemonade |
| macOS (user install) | ~/.config/lemonade |
| macOS (system install) | /Library/Application Support/lemonade/.config |
For a standalone lemond executable, the default config directory is ~/.config/lemonade. Pass an explicit config_dir positional argument if you want persistent JSON files somewhere else; cache_dir only controls downloaded/runtime data.
user_models.json Reference
This file contains a JSON object where each key is a model name and each value defines the model's properties. Create this file in your cache directory if it doesn't exist.
Template
{
"MyCustomModel": {
"source": "modelscope",
"checkpoint": "org/repo-name:filename.gguf",
"recipe": "llamacpp",
"size": 3.5
}
}
Fields
| Field | Required | Type | Description |
|---|---|---|---|
source |
No | String | Remote registry: huggingface or modelscope. When omitted, the server's configured default_model_source applies. Persisted and used for variants, downloads, cache paths, links, and update checks. A source/registry_source that conflicts with a provider URL in the checkpoint is rejected with 400. |
checkpoint |
Yes* | String | Registry checkpoint in org/repo or org/repo:variant format. Use org/repo:filename.gguf for GGUF models. |
checkpoints |
Yes* | Object | Alternative to checkpoint for models with multiple files. See Multi-file models. |
recipe |
Yes | String | Backend engine to use. One of: llamacpp, whispercpp, moonshine, sd-cpp, kokoro, ryzenai-llm, flm, collection.omni. |
components |
Yes** | Array | Components for a collection. Required when recipe: "collection.omni". See Collections. |
size |
No | Number | Model size in GB. Informational only — displayed in the UI and used for RAM filtering. |
mmproj |
No | String | Filename of the multimodal projector file for llamacpp vision models (must be in the same registry repo as the checkpoint). This is a top-level field, not inside checkpoints. |
image_defaults |
No | Object | Default image generation parameters for sd-cpp models. See Image defaults. |
* Either checkpoint or checkpoints is required, but not both.
** Required only when recipe: "collection.omni". Collections do not use checkpoint/checkpoints.
Checkpoint format
The checkpoint field uses the format org/repo:variant:
- GGUF models (exact filename):
org/repo:filename.gguf— e.g.,Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF:qwen2.5-coder-1.5b-instruct-q4_k_m.gguf - GGUF models (quantization shorthand):
org/repo:QUANT— e.g.,unsloth/Phi-4-mini-instruct-GGUF:Q4_K_M. The server will search the repo for a matching.gguffile. - ONNX models:
org/repo— e.g.,amd/Qwen2.5-0.5B-Instruct-quantized_int4-float16-cpu-onnx - Safetensor models:
org/repo:filename.safetensors— e.g.,stabilityai/sd-turbo:sd_turbo.safetensors
Multi-file models
For models that require multiple files (e.g., Whisper models with NPU cache, or Flux image models with separate VAE/text encoder), use checkpoints instead of checkpoint:
{
"My-Whisper-Model": {
"checkpoints": {
"main": "ggerganov/whisper.cpp:ggml-tiny.bin",
"npu_cache": "amd/whisper-tiny-onnx-npu:ggml-tiny-encoder-vitisai.rai"
},
"recipe": "whispercpp",
"size": 0.075
}
}
Supported checkpoint keys:
| Key | Used by | Description |
|---|---|---|
main |
All | Primary model file |
npu_cache |
whispercpp | NPU-accelerated encoder cache |
text_encoder |
sd-cpp | Text encoder for image generation models |
vae |
sd-cpp | VAE for image generation models |
Collections
A collection bundles several already-registered models so they can be loaded, pulled, or deleted as a single entry. Collections do not have their own checkpoint — they reference other models by name. An omni collection is a collection type registered with recipe: "collection.omni" — this is the recipe behind Lemonade Omni Models.
{
"MyKit": {
"recipe": "collection.omni",
"components": ["Qwen3-0.6B-GGUF", "Whisper-Tiny", "SD-Turbo"]
}
}
Components must already be registered (built-in models, or other user.* entries earlier in this file). Loading the collection (lemonade load user.MyKit) loads each component; deleting the collection removes only the collection entry, leaving components on disk.
The equivalent CLI registration is shown in Pull an Omni Collection.
Image defaults
For image models (including sd-cpp and thenoise), you can specify default generation parameters.
The optional soft_pixel_cap limits the inferred output area of /images/edits when
size is omitted; Lemonade preserves the input aspect ratio. If this field is
missing or zero, the global 4,194,304-pixel (~4 MP) fallback applies.
Explicitly requested output sizes are not subject to this soft cap:
{
"My-SD-Model": {
"checkpoint": "org/repo:model.safetensors",
"recipe": "sd-cpp",
"size": 5.2,
"image_defaults": {
"steps": 20,
"cfg_scale": 7.0,
"width": 512,
"height": 512,
"soft_pixel_cap": 262144
}
}
}
Model naming
- In
user_models.json, store model names without theuser.prefix (e.g.,MyCustomModel). - When referencing the model in API calls, CLI commands, or
recipe_options.json, use the full prefixed name (e.g.,user.MyCustomModel). - Labels like
customare added automatically, as is the recipe's default deployment label when the entry names none of its own (chatfor allamacppentry,imagefor ansd-cppone). Additional labels (reasoning,vision,embeddings,reranking) can be set via thepullCLI/API flags, or by including alabelsarray in the JSON entry.
recipe_options.json Reference
This file configures per-model runtime settings. Each key is a canonical model ID — one of user.NAME, extra.NAME, or builtin.NAME (see the Model naming spec above). Each value contains the settings for that model.
Template
{
"user.MyCustomModel": {
"ctx_size": 4096,
"llamacpp_backend": "vulkan",
"llamacpp_args": ""
},
"builtin.Qwen2.5-Coder-1.5B-Instruct": {
"ctx_size": 16384
}
}
Migration: Older Lemonade versions stored built-in entries under their bare name (e.g.
"Qwen2.5-Coder-1.5B-Instruct"with no prefix). On first load with the current version, any bare key matching a known built-in is rewritten tobuiltin.<name>in place. An INFO log line reports the number of migrated keys. Bare keys that don't match a built-in are preserved unchanged.Note: Per-model options can also be configured through the Lemonade desktop app's model settings, or via the
save_optionsparameter in the/api/v1/loadendpoint.Editing options without loading:
POST /api/v1/models/{id}/optionswrites this file directly, so options can be saved without bringing a model into memory. It merges rather than replaces, and"ctx_size": -1saves automatic context sizing even when the server-widectx_sizeis an explicit number.DELETEon the same path resets the model by removing its entry entirely.
Complete Examples
Example 1: Adding a GGUF LLM with large context
user_models.json:
{
"Qwen2.5-Coder-1.5B-Instruct": {
"source": "huggingface",
"checkpoint": "Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF:qwen2.5-coder-1.5b-instruct-q4_k_m.gguf",
"recipe": "llamacpp",
"size": 1.0
}
}
recipe_options.json:
{
"user.Qwen2.5-Coder-1.5B-Instruct": {
"ctx_size": 16384,
"llamacpp_backend": "vulkan"
}
}
(Use builtin.NAME here if you're overriding a built-in model's defaults, or extra.NAME for a GGUF imported from extra_models_dir.)
Then load the model:
lemonade run user.Qwen2.5-Coder-1.5B-Instruct
Example 2: Adding a vision model with mmproj
user_models.json:
{
"My-Vision-Model": {
"checkpoint": "ggml-org/gemma-3-4b-it-GGUF:Q4_K_M",
"mmproj": "mmproj-model-f16.gguf",
"recipe": "llamacpp",
"size": 3.61
}
}
Example 3: Adding an embedding model
user_models.json:
{
"My-Embedding-Model": {
"checkpoint": "nomic-ai/nomic-embed-text-v1-GGUF:Q4_K_S",
"recipe": "llamacpp",
"size": 0.08
}
}
The model will automatically be available as user.My-Embedding-Model. To mark it as an embedding model, use the manual registration flags on pull:
lemonade pull user.My-Embedding-Model \
--checkpoint main "nomic-ai/nomic-embed-text-v1-GGUF:Q4_K_S" \
--recipe llamacpp \
--label embeddings
lemonade pull nomic-ai/nomic-embed-text-v1-GGUF — the embeddings label is auto-applied because the repo id contains embed.
Settings Priority
When loading a model, settings are resolved in this order (highest to lowest priority):
- Values explicitly passed in the
/api/v1/loadrequest - Per-model values from
recipe_options.json - Per-architecture defaults from
architecture_defaults.json(shipped resource; keyed by GGUF architecture name read from the model file) - Global configuration values, see Server Configuration
*_args merge behavior: For options ending in _args (e.g., llamacpp_args, whispercpp_args, sdcpp_args, vllm_args), the CLI/API arguments are merged rather than replaced. The merge works at the flag level with higher priority settings taking priority.
For built-in models using recipe: "vllm", also check vllm_model_config.json. Some vLLM families need family-level arguments such as tool-call parser settings; see the vLLM model-family argument config docs before adding or updating vLLM model entries.
For full details, see the load endpoint documentation.
See Also
- CLI pull command — register and download models from the command line
/api/v1/pullendpoint — register and download models via API