ADR-0020: Engine presets: pick a known engine, or point at any custom URL
Status: Accepted · 2026-08-30
Context
ADR-0017 made the client engine-agnostic: it speaks the OpenAI /v1 dialect to whatever URL it is given. But Settings still exposed only a raw endpoint field and a single oMLX-specific button ("Manage Models in oMLX…"). A user running Ollama or LM Studio had to know the right port, know GOAT was OpenAI-compatible at all, and got no help managing models. The transport was general; the UX was not.
Decision
Settings → Engine offers a preset picker plus a Custom option:
- A preset is pure data (
EnginePresetin GoatInference): a name, the root URL its server listens on (the client still appends/v1/...), a one-line blurb, and how models are managed. It is not a second transport. Every preset speaks the one dialect. - Shipped presets: oMLX (recommended), vMLX, Ollama (
:11434), LM Studio (:1234), llama.cpp server (:8080), and Custom… (type any URL). - Choosing a preset fills the URL and re-probes; the URL stays editable, so a preset is a convenience, never a cage. The chosen preset id is remembered (
engine.preset). - Model management is engine-aware, not removed: open the engine's app when the preset names one and it's installed (oMLX/vMLX/LM Studio); show a copyable command for CLI engines (
ollama pull <model>,llama-server …); show nothing for bare/custom endpoints. The composer capsule and the empty-state banner use the same generalized affordance ("Wake<engine>"). - Discovery also probes Ollama's
:11434alongside the MLX ports.
oMLX remains the recommended engine everywhere (picker default, discovery seed, docs).
Consequences
Adding an engine is a row in EnginePreset.all: no new types, transports, or views. docs/ENGINES.md carries the preset table and stays the compatibility contract. Presets are conventional defaults; an unusual port is one edit away in the same field. Remote/hosted engines remain parked (ADR-0017). Presets are local-first only.
Alternatives considered
Raw URL only (rejected: leaves non-oMLX users to guess ports and compatibility), presets only with no free-form field (rejected: blocks unusual ports and self-hosted setups), removing model management entirely (rejected: the oMLX "manage models" jump is genuinely useful; generalizing it serves more engines than dropping it), a per-engine adapter class (rejected: still one dialect; a data preset is enough, mirroring ADR-0017's reasoning).

