Serve a model in three commands
The engine is one program, veymeer. It serves GGUF models over an OpenAI-compatible API on your machine. This page covers what most people need; the operator's guide that ships with the engine covers every setting.
Quick start
Check the machine, see which models are on it, and serve one. Models that Ollama already downloaded are found where they are.
veymeer doctor # what this machine offers, and anything in the way
veymeer models # the models on this machine
veymeer serve --model qwen2.5:7b # serve one at http://127.0.0.1:8080/v1
Any OpenAI client works against it. Nothing about the client changes except the address.
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8080/v1", api_key="not-needed-locally")
stream = client.chat.completions.create(
model="qwen2.5:7b",
messages=[{"role": "user", "content": "Explain what a mutex is."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen2.5:7b", "messages": [{"role": "user", "content": "Hello"}]}'
Or skip the server and talk to a model in the terminal:
veymeer chat --model qwen2.5:7b
The command line
Every command takes --help.
The output of the engine on our test machine, replayed. Yours will name your own hardware.
| Command | What it does |
|---|---|
veymeer setup | Install the engine on this machine: into place, onto the PATH, into the list of installed programs. --system for every user, --remove to take it out, --status to see what is installed. |
veymeer doctor | Check the processor, the GPUs, the models, the data folder, the port and the licence. Exits non-zero if something would stop the engine from serving. |
veymeer pull owner/repo[:QUANT] | Download a model from the Hugging Face Hub. Resumes, and checks the file against the Hub's checksum. --list shows the repository's files. |
veymeer models, rm | The models on this machine and whether the engine runs them; remove one. |
veymeer serve | Serve the API. |
veymeer chat, run "prompt" | Talk to a model in the terminal. If an engine is already serving, it answers; otherwise the model is loaded for the command. |
veymeer status | What the engine running on this machine is serving, on what, and under which tier. |
veymeer settings | The running engine's settings; settings set name=value changes them, --persist keeps the change. |
veymeer bench | Measure a model here: three kinds of work, time to read the prompt and tokens a second. |
veymeer login, logout | Sign in with a VEYMEER account through the browser to use its plan on this device; sign out. |
veymeer license | Who is signed in and the tier in force. |
veymeer service install | Run as a system service. Also start, stop, status, uninstall. |
veymeer node | Lend this machine's devices to a cluster. |
veymeer devices, paths, config | What the machine offers; where this installation keeps models, data and settings; the settings serve would run with. |
The API
OpenAI's routes and fields, plus a few of the engine's own. Streaming uses server-sent events and ends with data: [DONE]. Errors are JSON with the usual status codes.
| Route | |
|---|---|
POST /v1/chat/completions | Chat. Sampling controls, penalties, seed, stop, stream, logprobs, response_format, tools. |
POST /v1/completions | Raw text completion, with the same options and json_schema, grammar, regex. |
GET /v1/models | The model being served; every installed model when model switching is on. |
POST /tokenize | Token count of a prompt or chat, and the room left for the answer. |
POST /prime | Read a prompt or chat ahead of the question. |
GET, PATCH /v1/settings | The settings in force, and changing them while the server runs. |
GET /health | Model, device, settings, tier and the last request's speed. |
GET /livez, /readyz, /metrics | Probes and Prometheus metrics. |
Reading ahead
The first word of an answer waits for the prompt to be read. An application that knows the beginning of a prompt before the question exists, such as an open file or a long instruction, can send it ahead. The request that follows only reads what is new.
curl http://127.0.0.1:8080/prime \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "system", "content": "...a long instruction..."}]}'
# {"tokens": 1840, "read": true}
Per-request switches
Each response carries a veymeer object with the speed it was written at. A request can turn features off for itself in its own veymeer object: "remember": false, "ahead": false, "draft": false, "speculate": false. None of them changes the answer.
Structured output and tools
An answer can be held to a form, and then it always has that form.
| Request field | The answer is |
|---|---|
"response_format": {"type": "json_object"} | a JSON object |
"response_format": {"type": "json_schema", ...} | JSON that fits the schema, as OpenAI's structured outputs |
"grammar": "root ::= ..." | text the GBNF grammar accepts |
"regex": "[0-9]{4}-[0-9]{2}" | text the regular expression matches |
"tools": [...] | a reply, or tool_calls whose arguments fit each function's schema |
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5:7b",
"messages": [{"role": "user", "content": "Invoice 1042, 3 items, 59.90 EUR, due 2026-11-01"}],
"response_format": {"type": "json_schema", "json_schema": {"strict": true, "schema": {
"type": "object",
"properties": {
"number": {"type": "integer"},
"total": {"type": "number"},
"due": {"type": "string", "format": "date"}
},
"required": ["number", "total", "due"]
}}}
}'
Give max_tokens room: an answer that runs out of tokens before its form is complete ends with finish_reason: "length". A schema keyword that cannot be enforced is refused when strict is set, instead of being ignored.
Settings
Every setting can be given in a settings file, as an environment variable (VEYMEER_<SETTING>) or as a flag (--<setting>), later ones overriding earlier ones. veymeer config prints the settings a serve would run with.
| Setting | Default | Meaning |
|---|---|---|
model | qwen2.5:7b | Model name, or the path of a .gguf file |
ctx | 0 | Context length in tokens. 0 sizes it when the model loads: as long as the model was trained for, as far as the machine holds it beside the whole model |
backend | auto | auto, cuda, webgpu or cpu |
precision | high | high is exact; balanced and fast trade a little for memory and prompt-reading speed |
draft_model | auto | A small model that writes ahead: picked automatically, named, or off |
kv8 | false | Keep conversations in 8 bits on NVIDIA GPUs: about twice the context in the same memory, at a small loss of precision. Built, not yet measured |
speculation | true | Guess ahead and check several tokens a pass; false writes one at a time, with the same answers |
memory_mb | 256 | Memory for answers already given; 0 turns it off |
ahead | 3 | While idle, prepare for this many likely follow-up questions; 0 turns it off |
model_switch | false | Serve whichever installed model a request names |
max_sessions | 16 | Conversations answered at once, up to what the tier serves |
host, port | 127.0.0.1, 8080 | Where to listen |
api_keys | none | Keys clients must send; required to listen beyond this machine |
Changing settings while it runs
A running server takes changes over its API, or from the command line. A change that needs the engine rebuilt lets running answers finish, queues new ones, and is rolled back if the engine does not start with it. Web pages cannot change settings, since any site you open could send requests to your machine: only programs, pages served from this machine, and sites named in cors_origins can.
veymeer settings set ctx=8192 precision=fast --persist
curl -X PATCH http://127.0.0.1:8080/v1/settings \
-d '{"model": "llama3.1:8b", "draft_model": "off"}'
Models
A model is named the way Ollama names it (qwen2.5:7b) or by the path of a .gguf file. Named models are looked up in the models folder: --models-dir, else $OLLAMA_MODELS, else Ollama's own folder. veymeer models lists every model found and says ok or why it cannot run.
| Architectures | Llama 1 to 3.x, Mistral, Mistral Nemo, Mixtral, Qwen2 and 2.5, Qwen2-MoE, Qwen3, Qwen3-MoE, Gemma 1 to 3, Gemma 4 (E2B, E4B), Phi-2, Phi-3 and 3.5, gpt-oss, GPT-2, StarCoder 1 and 2, Falcon, GPT-NeoX (Pythia), StableLM, MPT, BLOOM, OLMo 1 and 2, OLMoE, Command-R, Cohere2, Granite, Granite-MoE, MiniCPM, InternLM2, EXAONE, Orion, Nemotron, Arcee, SmolLM3, GLM-4, ERNIE 4.5, Hunyuan, DeepSeek MoE, XVERSE: on the processor and through WebGPU, checked against each other; on NVIDIA GPUs, measured for Llama, Qwen, Mistral and Gemma 4 and built, not yet measured, for the rest. Find a model |
| Quantisations | F32, F16, BF16, Q2_K, Q3_K, Q4_0, Q4_1, Q4_K, Q5_0, Q5_1, Q5_K, Q6_K, Q8_0, IQ1_S, IQ1_M, IQ2_XXS, IQ2_XS, IQ2_S, IQ3_XXS, IQ3_S, IQ4_NL, IQ4_XS, MXFP4, TQ1_0, TQ2_0 (IQ1 to IQ3 on the processor and NVIDIA GPUs) |
| Chat formats | ChatML, Llama 3, Mistral, Gemma, Gemma 4, Phi, harmony (gpt-oss) |
| Tools | In every chat format: offered in the model's own template, read back as OpenAI's tool_calls |
A draft model has to share the large model's vocabulary: a small model of the same family, such as Qwen2.5 0.5B for Qwen2.5 7B, or Llama 3.2 1B for Llama 3.1 8B. The engine refuses a pair that does not match.
Signing in
The Individual tier needs no account. Signing in brings a paid plan to a device.
veymeer login # opens the browser; sign in there and approve this device
veymeer license # who is signed in, and the tier in force
veymeer logout # sign this device out
The engine prints a short code and opens the device page of this site. You sign in there, with your second factor if the account has one, and approve the device. The engine then holds a licence for that device and renews it by itself while it serves. A machine without a browser can use veymeer login --no-browser and approve from any other device.
The first time the engine is run at a terminal it asks whether to continue without an account or to sign in. A program that runs the engine for you, such as an editor, a service or a script, is never asked.
veymeer license install <file>. The engine checks its signature itself and needs no connection to do so.
Clusters
One model can run across machines on the same network.
# on every machine that lends its hardware
veymeer node --beacon
# on the machine that serves
veymeer serve --model llama3.1:8b --discover --cluster-key KEY
# or name the nodes
veymeer serve --model llama3.1:8b --nodes KEY@192.168.1.20,KEY@192.168.1.21
Connections are encrypted and both ends must hold the join key. Treat the key like a password. How many other machines a cluster may have depends on the tier; what we have and have not measured is on the hardware page.
What the engine sends
Serving a model, the engine talks only to the clients that call it, and to the machines of a cluster if you set one up. Beyond that it makes a network request of its own in three cases, each of which you start or can see:
| When | To | What |
|---|---|---|
veymeer pull | The Hugging Face Hub | The download request for the model you named |
veymeer login, and renewal while signed in | The account server | An identifier for the installation, the machine's name, the engine's version, the operating system |
| Once a day while serving, in a release that names an account server | The account server | The engine's version, to fetch the notice of when the launch period ends |
The identifier is a one-way hash of the operating system's installation id. It is stable for an installation and means nothing anywhere else; no hardware address is read or sent. The engine never sends your models, your prompts, your answers, or how much you use it.
Troubleshooting
| Symptom | What to check |
|---|---|
| The model is not found | veymeer models and the models folder (veymeer paths) |
| It starts on the processor although there is a GPU | veymeer devices; the log says why each faster device was passed over |
| "the weights of some layers stay in host memory" | The model is larger than the GPU memory available. Lower ctx, close other GPU programs, or accept the slower writing |
429 responses | More requests than the engine serves and queues at once |
A structured answer ends with "length" | max_tokens ran out before the form was complete: raise it |
| The first start is slower than later ones | The engine measures the GPU once and remembers the result |
Everything else is in the operator's guide
Every setting, the service, TLS, logging and monitoring are described in the guide that ships beside the engine.