Models

The merged model catalog and how to extend it.

--list-models and /model show the merged catalog across every provider. Built-in entries cover Claude, GPT/Codex, Gemini/Gemma, Kimi/Moonshot, DeepSeek, Groq-hosted models, OpenRouter-routed models, Bedrock and Vertex model IDs, Azure deployments, Copilot models, and more.

For supported cloud discovery providers, zot also merges live IDs from their model-list endpoints, cached for six hours in $ZOT_HOME/models-cache.json. Speculative catalog entries are included too; they start working as soon as the upstream provider enables them.

Continuous browser model picker

The browser client for Continuous execution uses the host's active catalog and host-side connections, not the browser's credentials or the website's public model catalog. Updated hosts offer models across configured providers, with provider names shown in each row. Search by provider name or model ID, select a model, then click Save in session settings to apply the provider and model together while retaining conversation history.

Selecting a model closes the dropdown, not session settings. Older hosts retain single-provider selection. Listing models does not execute stored API key commands or guarantee access under an upstream account's policies. The picker does not provide login flows or live model discovery controls.

Copilot account availability

GitHub Copilot uses a separate authenticated /models query at startup to filter existing catalog entries by account availability. This is an in-memory filter, not a source of new catalog entries and not part of the six-hour disk cache. The website's model catalog remains a public list, not an account-specific availability check.

Discovery runs in the background. Before it succeeds, catalog entries remain visible; --list-models can exit before the account filter arrives. A failed refresh retains the previous snapshot, or the unfiltered catalog if there is no snapshot. Restart zot after changing accounts. Custom entries do not bypass GitHub's policies or the runtime picker filter. See Providers for Copilot setup, model retirements, and plan exceptions.

OpenRouter presets

When OpenRouter credentials are available, zot fetches the account's active presets and lists them in /model as @preset/<slug>. Presets are refreshed even while the public model cache is fresh, so account changes do not wait for the six-hour catalog refresh.

Preset capabilities

A preset can select one model, multiple fallback models, or change remotely. zot therefore treats its context window, output limit, and reasoning support as unknown instead of inventing values. Context percentages, percentage-based auto-compaction, and minimum-context compatibility checks are unavailable for preset entries. OpenRouter still applies the preset's own model and generation configuration when a request runs.

When model IDs change

Provider catalogs move quickly. zot removes built-in entries when an upstream route is stale or unavailable, but a live-discovered ID can reappear automatically if your API key can see it. If a pinned model stops resolving, run zot --list-models or open /model and pick a currently available ID.

For custom or private deployments, keep the stable deployment name in $ZOT_HOME/models.json instead of depending on a speculative public catalog entry.

Reasoning levels

Use /reasoning in the interactive TUI to set the reasoning level for subsequent model calls. The selector only shows distinct levels supported by the active model. Models without reasoning support only offer off. The current level is also shown in the model picker, and --reasoning off|minimum|low|medium|high|xhigh|max sets it from the command line.

Available levels are derived from the model's provider protocol and optional reasoningLevelMap overrides. The max tier is separate from xhigh and is available only where the active model can represent it or an explicit model override enables it. zot clamps a configured level to the nearest supported value when you switch models, so the selector and the provider request always use the same capability mapping.

For Copilot's Gemini and Kimi routes, zot does not send a reasoning-effort setting. These models can reason, but the endpoint manages that behavior. The selector's off value does not guarantee that server-side thinking is disabled.

Custom models

Add custom models with $ZOT_HOME/models.json. User entries take precedence over both baked-in and live-discovered models. This file registers models but does not select a default. Save the provider and model selection in $ZOT_HOME/config.json, as shown in Settings.

$ZOT_HOME/models.json
{
  "providers": {
    "openrouter": {
      "models": [
        {
          "id": "my-custom-model",
          "name": "My Custom Model",
          "reasoning": true,
          "reasoningLevelMap": { "minimum": "low", "max": "" },
          "contextWindow": 200000,
          "maxTokens": 8192
        }
      ]
    }
  }
}

Each top-level provider key is a zot provider id, for example openai, anthropic, ollama, openrouter, groq, amazon-bedrock, or google-vertex. Supported model fields are id (required), name, reasoning, reasoningLevelMap, contextWindow, maxTokens, baseUrl, priceInput, priceOutput, priceCacheRead, and priceCacheWrite.

reasoningLevelMap is optional. Protocol defaults apply when it is omitted. Use minimum, low, medium, high, xhigh, or max as keys for model-specific exceptions. Map a key to another level when the inputs are equivalent, use an identity mapping such as "max": "max" to enable a level beyond the protocol default, or map it to an empty string or off to remove that level. The effective mapping controls both /reasoning and provider requests.

Custom providers

A top-level provider key that is not a built-in id defines a custom provider. Give it a provider-level baseUrl and an api wire format (openai for OpenAI-compatible Chat Completions, the default, openai-responses for the OpenAI Responses API, or anthropic for the Anthropic Messages API). All models under that key are first-class: they show up in --list-models, /model, and /login.

$ZOT_HOME/models.json
{
  "providers": {
    "my-company": {
      "baseUrl": "https://llm.mycompany.com/v1",
      "api": "openai",
      "models": [
        {
          "id": "company-llm-v2",
          "name": "Company LLM v2",
          "contextWindow": 128000,
          "maxTokens": 32000
        },
        {
          "id": "company-llm-fast",
          "name": "Company LLM Fast",
          "baseUrl": "https://fast-llm.mycompany.com/v1"
        }
      ]
    }
  }
}

A model-level baseUrl overrides the provider-level baseUrl for that model, so a single custom provider can fan out across more than one endpoint. If api is omitted it defaults to openai; an unrecognized value falls back to openai with a warning.

Built-in models stay visible

Adding a models.json no longer hides the built-in catalog. Your custom providers and models are merged on top of the baked-in and live-discovered entries.

Credentials for custom providers

models.json never stores secrets. For a custom provider, supply its API key through /login, the --api-key flag, a provider environment variable, or a command in $ZOT_HOME/auth.json. The env var is derived from the provider id in upper snake case, so my-company reads MY_COMPANY_API_KEY and local-proxy reads LOCAL_PROXY_API_KEY.

To retrieve the key for the my-company provider above from a password manager, add a matching entry to $ZOT_HOME/auth.json:

$ZOT_HOME/auth.json
{
  "additional_api_key_creds": {
    "my-company": {
      "api_key_command": {
        "program": "op",
        "args": ["read", "op://Work/OpenAI/credential"],
        "timeout_ms": 120000
      }
    }
  }
}

The provider IDs in models.json and auth.json must match. /login also lists custom providers alongside the built-in cloud providers and stores their keys in $ZOT_HOME/auth.json. Because many self-hosted gateways do not expose a model-list endpoint, custom provider keys are accepted and saved without a verification probe; an invalid key surfaces on the first model call.

shell
# With the command-backed key above
zot --provider my-company --model company-llm-v2

# Or use an environment variable
MY_COMPANY_API_KEY=sk-... zot --provider my-company --model company-llm-v2

Live discovery for custom providers

A custom provider can list its models from an OpenAI-compatible /models endpoint instead of, or in addition to, a static models array. Set "discover": true at the provider level. Discovery is off by default, so existing configurations never gain network traffic, and it requires a provider-level baseUrl. A discover flag without one is ignored with a warning.

$ZOT_HOME/models.json
{
  "providers": {
    "m4": {
      "baseUrl": "http://127.0.0.1:48000/v1",
      "discover": true
    },
    "m5": {
      "baseUrl": "http://127.0.0.1:48001/v1",
      "discover": true,
      "models": [
        { "id": "big-model", "contextWindow": 200000, "maxTokens": 16000 }
      ]
    }
  }
}

zot requests <baseUrl>/models, or <baseUrl>/v1/models when the base URL has no trailing version segment, with a 3 second timeout. It refreshes in the background on startup, when /model opens, and when launching with a discovered model that is not yet in the catalog. The API key from the environment or auth.json is sent as a bearer token when present. A missing key is not an error because local servers commonly accept unauthenticated requests. Background refresh never executes an api_key_command; opening /model does.

Only model IDs are read from the response. Discovered models default to a 32,768 token context and 4,096 output tokens, have zero pricing, and no reasoning controls. IDs containing embed are skipped. Entries in the provider's models array override discovered metadata for the same ID, which is how to raise limits or enable reasoning for a specific model.

The discovered list stays in memory, separate from the six-hour cloud model cache. A failed refresh keeps the previous snapshot for that provider and /model shows the error while other providers stay usable. A persisted selection of a discovered model survives restarts even though the model is not in models.json. The /models wire format is assumed to be OpenAI style ([[{"data": [{"id": ...}]}]]) regardless of the provider's api setting.

Credential still required for listing

As with every custom provider, /model only lists it once a credential exists. For a keyless local server, store any placeholder value through /login or the derived environment variable. --list-models prints the catalog before the background refresh completes, so discovered models may be missing there.

Requires a zot build with custom provider discovery

Older builds ignore the discover key and only use the static models array.

LM Studio discovery and metadata

Register a server URL through /login to enable discovery for the lmstudio provider. See Providers for setup and the build requirement. Without a registered URL, zot does not query LM Studio. This local catalog is in-memory only, not part of the six-hour disk cache.

  • Native discovery excludes entries explicitly marked as embedding models. The /v1/models fallback only supplies IDs, so embedding filtering is best-effort.
  • Loaded models use their instance IDs and configured context lengths. Unloaded models use their keys and advertised maximum context lengths.
  • Quantization-specific keys are preserved. Zot does not expand the native API's separate variant list into additional choices.
  • Missing context metadata defaults to 32,768 tokens. Output defaults to at most 4,096 tokens, or a quarter of the context window for smaller models. These values are estimates, not server guarantees.
  • Discovered models have zero API pricing. Token usage still comes from inference responses, and hardware or electricity costs are not estimated.
  • Reasoning controls are not enabled automatically from LM Studio's capability metadata. This does not mean server-side reasoning is disabled.

Override metadata under lmstudio in $ZOT_HOME/models.json. User entries take precedence over discovered entries. For an unloaded model, set the context window to the limit LM Studio will actually load it with if that is smaller than its advertised maximum:

$ZOT_HOME/models.json
{
  "providers": {
    "lmstudio": {
      "models": [
        {
          "id": "my-local-model",
          "contextWindow": 32768,
          "maxTokens": 4096
        }
      ]
    }
  }
}

This file only overrides model metadata. The registered server URL and optional API key remain in $ZOT_HOME/auth.json. Discovery does not change your selected model, the coding workflow, or built-in pricing for other providers.

Local and OpenAI-compatible endpoints

For Ollama and generic single-model servers that speak OpenAI chat completions (vLLM, LocalAI, a single-model llama.cpp server, or a private gateway), use the ollama provider and set baseUrl when the endpoint is not zot's default local Ollama URL. This provides inference only and remains available for manual LM Studio configuration on older zot builds. For automatic LM Studio discovery, register the distinct lmstudio provider through /login. For llama.cpp router management, configure the distinct llama.cpp provider through /login and use /llama.

$ZOT_HOME/models.json
{
  "providers": {
    "ollama": {
      "models": [
        {
          "id": "my-local-model",
          "name": "My Local Model",
          "baseUrl": "http://localhost:1234/v1",
          "contextWindow": 32768,
          "maxTokens": 8192
        }
      ]
    }
  }
}

Then pick it with /model or run it directly:

shell
zot --provider ollama --model my-local-model

Save a remote Ollama endpoint

For one run against a remote Ollama server:

shell
zot --provider ollama --model qwen3.5:4b --base-url http://ollama-server:11434/v1

To persist that endpoint, add this model to $ZOT_HOME/models.json:

$ZOT_HOME/models.json
{
  "providers": {
    "ollama": {
      "models": [
        {
          "id": "qwen3.5:4b",
          "baseUrl": "http://ollama-server:11434/v1",
          "contextWindow": 32768,
          "maxTokens": 8192
        }
      ]
    }
  }
}

Select it by default with these fields in $ZOT_HOME/config.json:

$ZOT_HOME/config.json
{
  "provider": "ollama",
  "model": "qwen3.5:4b"
}

The model ID, including its tag, must match a model installed on that server. --base-url overrides the model's endpoint for one run. Set contextWindow and maxTokens to limits the model and server configuration actually support. These fields inform zot's budgets, they do not configure Ollama's context size or install models.

Only connect to trusted servers. Do not expose an unauthenticated Ollama endpoint to the public internet. Use HTTPS for remote endpoints that require a token.

Same Ollama model on two hosts

Catalog identity is the pair (provider, model ID). Repeating the same ID under ollama does not create separately selectable hosts, even with different display names. Use a distinct custom provider ID for each endpoint and keep the actual Ollama model ID unchanged:

$ZOT_HOME/models.json
{
  "providers": {
    "ollama-home": {
      "baseUrl": "http://localhost:11434/v1",
      "api": "openai",
      "discover": true,
      "models": [
        { "id": "qwen3.5:4b", "contextWindow": 32768, "maxTokens": 8192 }
      ]
    },
    "ollama-server": {
      "baseUrl": "http://ollama-server:11434/v1",
      "api": "openai",
      "discover": true,
      "models": [
        { "id": "qwen3.5:4b", "contextWindow": 32768, "maxTokens": 8192 }
      ]
    }
  }
}

Select either host:

shell
zot --provider ollama-home --model qwen3.5:4b
zot --provider ollama-server --model qwen3.5:4b

To use the second host by default, set provider to ollama-server and model to qwen3.5:4b in config.json. Discovery-enabled custom providers can run against unauthenticated servers without a key. For picker visibility, see the credential note under Live discovery for custom providers above.

discover lists models already installed on the server. It does not download models into Ollama. Static model entries are optional with discovery enabled, but let you override the discovered metadata defaults. The built-in ollama provider has no dedicated /login endpoint-setup flow or automatic discovery. Use explicit model IDs, static entries, or discovery-enabled custom providers instead.

API keys for custom local endpoints

models.json does not store credentials. It only describes models and their optional baseUrl. Named custom providers can read keys from --api-key, provider environment variables, or $ZOT_HOME/auth.json written by /login. The built-in ollama provider uses --api-key or a placeholder credential, not a saved Ollama key from auth.json.

If your local or private OpenAI-compatible endpoint requires a bearer token, pass it at launch:

shell
zot --provider ollama \
  --model my-local-model \
  --base-url http://localhost:1234/v1 \
  --api-key "$LOCAL_API_KEY"

Naming a custom provider

Use the ollama provider only for OpenAI-compatible local servers without their own credentials. For an authenticated gateway, prefer a named custom provider (see above) so its key works through /login and the matching environment variable instead of requiring --api-key on every launch.

If a private endpoint uses a self-signed TLS certificate, add --insecure to skip certificate verification. It applies to the explicit --base-url endpoint, to a baseUrl defined for a user model in models.json, and to models discovered from a custom provider's baseUrl, so built-in providers, /login auth, and model discovery requests keep normal TLS verification.

shell
zot --provider ollama \
  --model my-local-model \
  --base-url https://my-llm.internal/v1 \
  --api-key "$LOCAL_API_KEY" \
  --insecure

For unauthenticated Ollama on the default URL, no key is needed:

shell
ollama pull qwen3.5:4b
zot --provider ollama --model qwen3.5:4b