# Models & role defaults

> The Models page (Console → Building blocks → Models) is the one place you add models to your account: open-weight models from the embedded catalog, Hugging Face, Ollama or ONNX, network endpoints and cloud providers. Configs and role slots only select from these models. Every entry is described along three decoupled axes: the registry it comes from, the weight format it is in, and the runtime it executes on. As of September 25, 2026, OpenEng AI isn't live yet.

The **Models** page (Console → Building blocks → Models) is the one place you add models to your
account: open-weight models first (the embedded catalog, the Hugging Face Hub, Ollama, an ONNX
export), then network endpoints and cloud provider accounts. Configs and role slots never create
models; they only **select** from the ones you add here. Every entry is described along **three
decoupled axes**: the registry it comes from, the weight format it is in, and the runtime it
executes on. Which model serves which job is decided by the **role models** (see
[below](#role-models)).

> [!WARNING] Status on September 25, 2026: OpenEng AI isn't live yet
> The Console and Web Console interfaces load, but the OpenEng AI API routes and the Broker aren't
> deployed. Nothing can be saved or published, and no AI engine can register or connect. Pro,
> Enterprise and pay-as-you-go launch in Q4 2026 and can't be purchased yet. This page describes
> OpenEng AI as it works once those services are live ([status](ai.md)).

*Figure: Building blocks → Models — the My models and Embedded catalog tabs. The glowing arrow marks where you are in the console.*

## What it is, and when to use it

OpenEng is local-first: chat and coding run on open-weight models that the
engine downloads and runs on your machine. The catalog is how you choose and go beyond them — pull a
bigger model from a registry, point a role at a hosted API for heavyweight work, or keep routing
prompts through another agentic CLI you already set up — and publish the routing once for every
machine on your account.

## Three axes: WHERE, WHAT, HOW

A model entry answers three independent questions:

| Axis | Question | The choices |
|---|---|---|
| **Registry** | WHERE do the weights come from? | Hugging Face · Ollama library · ModelScope · direct URL |
| **Weight format** | WHAT are the weights? | GGUF · Safetensors · GPTQ · AWQ · EXL2 / EXL3 · MLX · ONNX · TensorRT engine |
| **Runtime** | HOW do they execute? | llama.cpp · ONNX Runtime · vLLM · SGLang · ExLlamaV2 / V3 · MLX · TensorRT-LLM · Transformers · Ollama |

Remote models use the same model: their format is **Remote** and their
runtime is one of **Cloud serverless** (a hosted HTTPS model API), **External
CLI** (an installed vendor CLI), or **Ollama** pointing at a remote host.
The axes are decoupled on purpose — the registry says nothing about how a
model runs, and the runtime says nothing about where it was downloaded from.

### The format ↔ runtime compatibility matrix

**Format determines which runtimes can run a model** — never where it came
from. The engine enforces this matrix, and the editor disables incompatible
combinations rather than letting you author one:

| Format | Compatible runtimes |
|---|---|
| GGUF | llama.cpp · Ollama · vLLM *(limited)* |
| Safetensors | vLLM · SGLang · TensorRT-LLM · Transformers · MLX |
| GPTQ | vLLM · TensorRT-LLM · Transformers · ExLlamaV2 |
| AWQ | vLLM · TensorRT-LLM · Transformers |
| EXL2 / EXL3 | ExLlamaV2 · ExLlamaV3 |
| MLX | MLX |
| ONNX | ONNX Runtime |
| TensorRT engine | TensorRT-LLM |
| Remote | Cloud serverless · Ollama · External CLI · Platform funded |

The live, machine-readable copy of this matrix — including the *(limited)*
annotations and each runtime's toolchain notes, 13 runtimes in all — is published at
[models.openeng.app/runtimes.json](https://models.openeng.app/runtimes.json),
alongside the on-device catalog
([catalog.json](https://models.openeng.app/catalog.json), 28 models) and the
provider directory
([providers.json](https://models.openeng.app/providers.json), 12 providers).

> [!NOTE] Which runtimes you can use today
> The engine's built-in models (Gemma 3 4B and Qwen2.5 GGUF weights) run in-process on llama.cpp, on
> the CPU in the published build, inside the engine's sandboxed model daemon. The 28 models in the
> public catalog are Ollama-library tags: the engine pulls and runs them through a local Ollama daemon
> that you install and run, outside the engine's sandbox. Other runtimes (vLLM, SGLang, ExLlama,
> TensorRT-LLM, Transformers, MLX) are used only when their own toolchain is installed: vLLM, SGLang,
> ExLlama, TensorRT-LLM and Transformers need a CUDA-capable GPU, and MLX needs macOS on Apple
> silicon, where there is no AI engine build yet.

## One gateway, one sandbox boundary

Two rules apply to every model entry, on every plan:

- **One mandatory Model Gateway.** Every model call — and every embedding —
  goes through a single gateway choke point in the engine. There is no side
  path; embeddings are metered like any other call.
- **Sandbox-confined execution.** The engine's built-in local models run only inside its
  sandboxed model process (`openeng daemon`), never inside the serving
  process on your bare host. Catalog models are the exception: they run in your own Ollama daemon,
  outside the engine's sandbox. Where the sandbox can't be set up, the engine
  refuses to run local models rather than silently degrading
  ([Local models need a sandbox](engine.md#local-model-sandbox)).
  Calls to hosted and cloud models, and to an Ollama server, always pass the
  gateway, and when the sandbox works they go out through it too. On a host
  without a working sandbox they currently go directly from the engine, which
  prints a one-time warning; set `OPENENG_SANDBOX_MANDATORY=1` to refuse them
  instead. An external-CLI model runs inside the sandbox when it's available,
  and otherwise as a plain process in the working directory with your OpenEng
  key removed from its environment.

## The Models page: My models and the Embedded catalog

The page has two tabs:

- **My models** — a card for every model you've added. Each card shows the model's kind (Embedded,
  HTTP API, Ollama, Hugging Face, ONNX, External CLI, or Included credit), capability chips, a
  *vision* chip, its context window, and a *disabled* or *duplicate name* chip when one applies.
  Clicking a card opens the full-page editor. Copies the config builder made to pin a model to a
  config are kept apart, in a collapsed **Pinned to configs** group under the cards.
- **Embedded catalog** — the [models.openeng.app](https://models.openeng.app) catalog: 28
  open-weight models as of September 25, 2026, for chat, coding, goal execution and reasoning, and
  multimodal (vision) work. All 28 are Ollama-library tags: the engine pulls and runs them through a
  local Ollama daemon (`http://127.0.0.1:11434`) that you install and run, outside the engine's
  sandbox. They're separate from the engine's built-in suite
  ([`openeng pull`](engine.md#pull)), which runs in the sandboxed model daemon. Each card shows the model's benefits, approximate size and the roles it
  can serve; models you've already added say *In My models*. Filter by text or by role group, then
  pick a card to **Add to my models**: a dialog asks for a unique name (the catalog name is
  suggested). This is the only way a catalog model enters your account; the engine pulls it through
  your Ollama daemon when it's first needed, so Ollama has to be installed and running there. The catalog site lists each model's minimum machine requirements and
  license.

*New model* (or ⌘K → *New model*) starts a fresh entry. **Delete** in the editor sits behind a
confirmation that spells out the consequence: any role still referencing the model falls back to
the embedded default, with a visible notice from the engine.

Role bindings don't live on this page. Account-wide defaults are on **Govern → Defaults**
([Defaults](govern.md#defaults)), and each config picks its own in its **Models** step (see
[Role models](#role-models)).

## The model editor, field by field

*Figure: The model editor at full width — here an existing external-CLI entry: the preset row, the command and arguments with the parameter reference inline beside them, and the explicit Save in the header.*

**Saving** — the editor never writes to the server on its own. The header's
**Save** button is disabled with the reason while the document is invalid
(e.g. a missing name or base URL, or a name another of your models already uses) or unchanged;
saving shows a spinner and a toast either way. Until you Save, your edits live only in the browser's
local draft mirror — reopen the editor and it offers **"Restore unsaved
draft?"**. A failed save keeps the draft so nothing is lost. Background help (how the engine
resolves models, and how they're isolated) sits behind the ⓘ in the header.

**Identity**

- **Name** — shown in lists, pickers, and chips. Names must be **unique** across your models; the
  editor refuses to save a duplicate. Role slots authored in the
  Console bind by the model's stable **id**, so renaming here is safe and
  never breaks routing. Exact-name references are tolerated only in
  hand-authored configs. A line under the name shows which account role defaults currently use
  this model.
- **Description** — for humans, shown in lists and pickers.

**Kind** — *New model* starts with kind cards that pick the broad shape of
the entry, open-weight first:

| Kind | What you add |
| --- | --- |
| **Embedded** | A curated open-weight model from the catalog (opens the Embedded catalog tab); an Ollama-library tag that needs Ollama installed and running |
| **Hugging Face** | Any public GGUF on the Hugging Face Hub: browse the Hub in the flow, or enter the repo and file |
| **Ollama** | A public Ollama library model (curated tags or any tag), served over your Ollama endpoint |
| **ONNX** | A public ONNX LLM export with fp32 inputs and outputs (fp16 and q4f16 exports are rejected); TensorFlow models work through an ONNX export |
| **Network model** | Any OpenAI-protocol endpoint you can reach: a base URL plus a `${ENV}` credential placeholder |
| **Provider** | A cloud provider account over the OpenAI protocol: Claude, ChatGPT, Gemini, DeepSeek, OpenRouter, Groq, Mistral or Together |

The card only seeds sensible defaults — the axes below stay
independently editable. External-CLI entries you already have keep their full form, but *New
model* no longer offers External CLI as a kind.

**Execution** (the three axes — each select is exactly what the engine
consumes)

- **Registry** — WHERE the weights come from: Hugging Face, ModelScope, the
  Ollama library, or a direct URL. The registry choice drives the **browse**
  surface: search *Hugging Face · ModelScope · Ollama library* without
  leaving the editor, and picking a repo or tag fills the fields for you. If browsing fails
  (offline, rate-limited), the manual fields always remain. No API key is sent while browsing.
- **Weight format** — WHAT the weights are: GGUF, Safetensors, GPTQ, AWQ,
  EXL2/EXL3, MLX, ONNX, or a TensorRT engine.
- **Runtime** — HOW they execute. Only runtimes compatible with the chosen
  format are enabled; incompatible options are disabled with the reason, and
  constrained pairs are annotated **"(limited)"**. Each runtime shows a
  short toolchain hint (what must be present on the machine for it to run).
- **Provider** — WHO serves the model, for remote and serverless entries only.
- **Quantization** — a structured field with candidate chips scanned from
  the registry (e.g. the quant variants a repo actually publishes). Pick a
  chip or type your own; the quant is no longer buried in a filename.
- **Version** — the revision or tag to pull, with **"Pin to revision"** to
  freeze a model to an exact snapshot instead of a moving tag.

**Remote models** (Cloud serverless / External CLI runtimes)

- *Cloud serverless* — **Base URL** (the endpoint root the engine sends
  requests to) + **Model reference** (the provider-side model identifier
  sent with each request, e.g. `claude-opus-4-8`). A **Provider** entry fills the base URL, a
  suggested `${ENV}` credential name and a curated model list from its preset; you can still type
  any model id.
- *External CLI* — **Command** (the binary) + **Arguments** (one per line,
  passed in order). The `{prompt}` marker shows where the prompt is delivered
  as an argument; **without it the engine pipes the prompt on stdin**. Prompts
  route through that CLI while OpenEng keeps threads and context.
- *Ollama* — **Endpoint** (your Ollama server; `http://localhost:11434` locally, or a remote host)
  + **Model name** (a tag, e.g. `llama3.1:8b`) — the engine calls that host's Ollama API with
  that tag, and pulls a public library tag when the model is provisioned.

### External-CLI presets and the parameter reference

For an external-CLI entry, the editor offers one-click **presets** that fill the real default
command + arguments for the popular agentic CLIs (edit freely after):

| Preset | Command | Default arguments |
|---|---|---|
| **Claude Code** | `claude` | `-p --output-format text --dangerously-skip-permissions {prompt}` |
| **Codex** | `codex` | `exec --full-auto --skip-git-repo-check {prompt}` |
| **Gemini** | `gemini` | `--yolo -o text -p {prompt}` |
| **Cursor** | `cursor-agent` | `-p --output-format text {prompt}` |
| **Custom** | — | clears the fields for free-form entry |

Each preset runs the external CLI **headless and autonomous** with an
explicit text output format (Codex additionally allows running outside a git
repository). Model and effort flags are deliberately *not* baked into the
presets — they live in the **parameter reference** under the Arguments field: a per-preset
table of the commonly documented flags (model selection, turn caps, output
format, permission/sandbox modes, reasoning effort) with one-line
descriptions. **Click a row to append that flag** to the Arguments — it slots
in *before* a trailing `{prompt}` marker. The panel is honestly labeled
**reference**: these flags belong to the external CLI and can change with its
releases — verify with `<command> --help`.

**Credential** — exists **only on hosted-API entries** (network models and providers; external CLIs
and on-device models carry no platform credential). Always `${ENV_VAR}` placeholders,
never raw values — the editor refuses anything that doesn't normalize to a
placeholder, resolved on each machine from its environment or from the value stored for the
config on the Web Console's **Placeholder values** screen; the environment wins. Credentials are **one
or many rows** (*Add variable*): some providers need several variables, and
every row travels as a placeholder. The first row is the key the engine
consumes; extra rows publish as numbered siblings older readers safely
ignore. The **https law** is pre-flagged here too: a credential sent over
plain `http://` to a non-loopback host means the engine **skips the entry at
runtime** — the editor blocks the save and tells you why.

> **Note:** The placeholder grammar, everywhere on the platform: `${NAME}`
> where `NAME` starts with a letter or `_` and continues with letters,
> digits, or `_`. Write `$${…}` for a literal `${`. The engine resolves every
> placeholder **when it resolves a config**, from the machine's own environment or from the value
> stored for the config (the Web Console's **Placeholder values**); the environment wins — a missing
> variable disables the owning entry with a one-line notice; a blank secret
> is never sent.

**Capabilities & limits**

- **Capability chips** — mark which of the eleven roles the model may serve (`complexityAnalyzer`,
  `planner`, `planOrchestrator`, `agent`, `subAgent`, `delegator`, `subDelegator`, `reviewer`,
  `summarizer`, `docResearcher`, `codeResearcher`). Role pickers list **only** models with that
  capability, so a model without the chip never appears in that role's dropdown. Per-model daily
  token caps live in [Token limits](govern.md#token-limits).
- **Modalities** — mark input beyond text. **Vision** marks a model that can read screenshots;
  only vision models are offered for a config's Device Use slot.
- **Advanced limits** (collapsed) — **Context window** rides the published entry and shows in every
  role picker's option label; **Max output tokens** is advisory.
- A model stored as disabled shows a *disabled* chip on its card; the engine skips disabled entries
  at runtime.

## Role models — the eleven roles the engine consumes

OpenEng routes every call through one of **eleven model roles**, grouped in five bands:

| Role | Band | What runs on it |
|---|---|---|
| `complexityAnalyzer` | Routing | Routes each turn by complexity (a direct answer vs. an agentic run) |
| `planner` | Planning | Agentic runs — plan synthesis |
| `planOrchestrator` | Planning | Agentic runs — orchestrates the plan across steps |
| `agent` | Execution | The main agent loop (chat · coding · execution) |
| `subAgent` | Execution | A spawned sub-agent working a delegated slice of work |
| `delegator` | Execution | Fans work out to sub-agents / workers |
| `subDelegator` | Execution | A nested delegator inside a sub-agent |
| `reviewer` | Quality | Reviews every layer's response against its request |
| `summarizer` | Quality | Summarizes long context and run results |
| `docResearcher` | Research | Web / document research |
| `codeResearcher` | Research | Searches and reasons over the codebase |

You bind models to roles in two places, and both **select** from My models:

- **Govern → Defaults** holds your account-wide role defaults: drag a model chip onto a role slot,
  or use each slot's dropdown (the keyboard path). This is a settings page, not a config.
- **A config's Models step** picks one model per role group for that config; its picks are what the
  engine uses, and a group you leave unset uses the embedded default. Govern → Defaults only sets what
  the Console's model and agent editors show as inherited, and doesn't reach the engine. The step shows only the roles the config's
  [execution flow](execution-flow.md) uses, plus a **Device Use** slot (vision models only) when the
  flow enables device use. Models too big for the config's machine profile are listed but disabled,
  with the reason.

Every role dropdown lists your **role-capable models only**, smallest first, with their approximate
size and context window; the stored value is always the model's stable **id**, so renames never
break a slot. The planner's dropdown also offers the included **Cloud Planner**, which runs on the
planning credit that comes with Pro ([Plans & billing](account.md)). A stored id that no longer
exists shows as *(unknown model)*.

Bindings publish as `models.roles.<role>` (for example `models.roles.planner`) — the exact keys the
engine consumes. Binding is by **stable model id** (renames never break routing);
exact names remain tolerated for hand-authored configs. The slot stores a
bare reference — the model entry itself carries the full execution spec
(registry, weight format, quantization, runtime, version), so changing how
a model runs never means re-touching every role that uses it. Older configs published under the
earlier role names still load: `chat`, `coding` and `executor` read as `agent`, `research` as
`docResearcher`, `orchestrator` as `planOrchestrator`, and `complexity` as `complexityAnalyzer`.

When the engine reads a published slot, it's in one of three states:

| Slot value | Meaning |
|---|---|
| **absent** | The config doesn't author this slot — the role's built-in default applies (for the complexity analyzer, that's the engine's internal complexity algorithm). |
| **`embedded`** (or empty) | An older config pinned the role to its embedded model. Today's pickers always store a specific model instead. |
| *a model id* | The config assigns that model to the role. |

> **Note:** A role pointing at an unknown or deleted model falls back to the
> role's embedded default — never an error, and never silent: the engine prints
> a one-line notice in its startup preflight.

The daemon-served helpers (the category classifier, the embedder and the internal summarizer
daemon) and the guardrail are fixed and not authored here; the `summarizer` role above is a
separate, authorable slot. Those helpers are small local models that run only on your machine: the
engine treats them as embedded-only and never routes them to a hosted model or an external CLI. The
`summarizer` role runs on whichever model you bind to it, hosted ones included.

## How the engine resolves all this

The ⓘ help in the model editor quotes the real mechanics, and they're worth knowing:

- The engine fetches `GET /v1/config?config=<id>&version=<v>` from
  `api.openeng.app`, authenticated with your OpenEng key in the `X-OpenEng-Key` header.
- Which config: in the Web Console, the config and version you pick for the session. Otherwise the
  engine's default config: the one it is told to use (`openeng serve --default=<config>`, or the
  `OPENENG_DEFAULT_CONFIG` environment variable; add `@<version>` to pin a version), otherwise your
  account's ([Which config the engine serves](engine.md#config-selection)). With nothing published at
  all, the API answers `no_published_config` and the engine refuses to serve turns until you publish.
- The result is cached at `~/.openeng/config-cache.json`. A copy younger than 30 seconds is reused
  without a network call; an older one is revalidated with the API (a cheap conditional request).
  The engine resolves its default config when it starts, so restart it on a machine to load a
  version you just published as the default; in the Web Console, pick the new version instead.

Each machine resolves which model it uses for each role and prints the result when it starts.

## Plan & tier notes

Authoring models and role defaults in the Console is open on every plan. Running any of them needs
the `openeng` engine, which starts only on an active **Pro** plan; Pro and Enterprise launch in
Q4 2026 and can't be purchased yet ([Plans & billing](account.md)). Your current entitlement is
always visible to you under **Account → Plan & billing**.

## Troubleshooting

**The Runtime select won't let me pick the runtime I want.** That's the
compatibility matrix: the weight format determines which runtimes can run a
model. Change the format (or pick a different quantized artifact from the
registry) and the runtime unlocks; *(limited)* pairs work but with caveats
noted inline. The live matrix is at
[models.openeng.app/runtimes.json](https://models.openeng.app/runtimes.json).

**My model doesn't appear in a role's dropdown.** Role dropdowns list only models whose
capabilities include that role: open the model and turn on the role's chip. If it's listed but
grayed out, it doesn't fit the config's machine profile.

**A role ignores my model and uses the embedded one.** Check the role's binding in the config's
Models step (the Defaults page doesn't reach the engine). An older config may still carry the `embedded` value, which pins the
embedded model on purpose, and an unknown or deleted assignment falls back to the embedded default
— with a one-line notice in the startup preflight naming it. Re-pick the
model for the role, then republish.

**My credentialed model never answers on a machine.** If its URL is plain
`http://` to a non-loopback host, the engine skips the entry by design — use
`https://`. If the URL is fine, check that the `${ENV_VAR}` is exported on
that machine: a missing variable disables the entry rather than sending a
blank secret.

**I renamed a model — does routing break?** No: Console-authored role slots
bind by the model's stable id, so renames are safe. Only hand-authored
configs that reference a model by exact name can dangle; re-point them and republish.

**"This name is already used by another of your models."** Display names must be unique across My
models, because they're how models are picked everywhere. Choose another name.

**The editor refuses my API key.** That's the point — paste the
*environment-variable name* (e.g. `OPENAI_KEY`), not the key. It's stored as
`${OPENAI_KEY}` and resolved on each machine from its environment or from the value stored for the
config on the Web Console's **Placeholder values** screen; the environment wins.
