Custom Models
Add custom providers and models (Ollama, vLLM, LM Studio, proxies) via the singlemodels.json in the active Atomic agent directory, normally ~/.atomic/agent/models.json, or the directory selected by ATOMIC_CODING_AGENT_DIR/PI_CODING_AGENT_DIR. Atomic reads only that file: it does not read project-scoped .atomic/models.json, fall back to ~/.pi/agent/models.json, or merge .pi and .atomic model configuration files. The legacy .pi read fallback remains available for configuration surfaces that explicitly use layered config paths, such as auth.json; it does not apply to models.json.
A complete defaultProvider/defaultModel pair in settings.json is resolved after built-in, configured, and extension providers register. If the provider remains unsupported, interactive mode reports a generic saved-configuration warning and leaves model selection open instead of routing the session to a different provider. Print and JSON modes write that diagnostic to stderr and exit nonzero before prompting, keeping JSON stdout JSONL-clean. RPC rejects prompt with the same correlated diagnostic until an explicit successful set_model selects an available model or an explicit model cycle returns a different available model. A null or unchanged cycle result does not clear the condition. If the provider is supported but the model is unknown or lacks authentication, normal automatic selection of an available authenticated model continues. Valid custom- and extension-provider defaults resolve once their provider registration is available. See Settings.
Table of Contents
- Minimal Example
- Full Example
- Supported APIs
- Provider Configuration
- Model Configuration
- Request-wide Cost Tiers
- Overriding Built-in Providers
- Per-model Overrides
- Anthropic Messages Compatibility
- OpenAI Compatibility
Minimal Example
For local models (Ollama, LM Studio, vLLM), onlyid is required per model:
apiKey is required but Ollama ignores it, so any value works.
Some OpenAI-compatible servers do not understand the developer role used for reasoning-capable models. For those providers, set compat.supportsDeveloperRole to false so Atomic sends the system prompt as a system message instead. If the server also does not support reasoning_effort, set compat.supportsReasoningEffort to false too.
You can set compat at the provider level to apply to all models, or at the model level to override a specific model. This commonly applies to Ollama, vLLM, SGLang, and similar OpenAI-compatible servers.
Full Example
Override defaults when you need specific values:models.json each time you open /model. Provider definitions, per-model overrides, dynamic catalogs, and isolated-engine model state are rebuilt from that fresh configuration, so edits take effect without restarting. Invalid edits report an error.
Google AI Studio Example
Usegoogle-generative-ai with a baseUrl to add models from Google AI Studio, including custom Gemma 4 entries:
baseUrl is required when adding custom models to the google-generative-ai API type.
Supported APIs
Set
api at provider level (default for all models) or model level (override per model).
These four values are the generic custom-provider APIs supported by models.json. Atomic’s installed native provider runtime also implements provider-owned APIs including mistral-conversations, azure-openai-responses, openai-codex-responses, bedrock-converse-stream, google-vertex, and pi-messages; those native APIs are not implied to be stable generic custom-provider contracts.
Provider Configuration
For a custom Radius gateway, set
"oauth": "radius" and its baseUrl. Atomic uses Radius OAuth credentials and the gateway’s dynamic pi-messages catalog.
Value Resolution
TheapiKey and headers fields support three formats:
- Shell command:
"!command"executes and uses stdout - Environment variable: Prefix the variable name with
$(or use${VAR}) to resolve it from the environment - Literal value: Used directly when the value does not use shell-command or explicit environment-variable syntax. Use
$MY_API_KEY/${MY_API_KEY}for new environment-variable references; legacy uppercase env-var-like values may be migrated as described below.
models.json provider config, such as MY_API_KEY, are migrated to $MY_API_KEY on startup only when that environment variable is present during migration; otherwise the value is preserved as a literal. New configs should use explicit $ENV_VAR/${ENV_VAR} syntax for environment variables.
For models.json, shell commands are resolved at request time. Atomic intentionally does not apply built-in TTL, stale reuse, or recovery logic for arbitrary commands. Different commands need different caching and failure strategies, and Atomic cannot infer the right one.
If your command is slow, expensive, rate-limited, or should keep using a previous value on transient failures, wrap it in your own script or command that implements the caching or TTL behavior you want.
/model availability checks use configured auth presence and do not execute shell commands.
Custom Headers
Model Configuration
Current behavior:
/model,--list-models, and the interactive footer display entries by modelid.- The configured
nameis used for model matching and secondary model detail text. It does not replace the footer/status-bar model id.
provider/literal[free]:high, that complete model wins and :high remains part of its ID; it does not become a thinking-level suffix and [free] is not treated as a character class. Only when the complete ID is absent does Atomic parse a valid thinking suffix, try the stripped exact ID, then apply glob/fuzzy matching. This preserves literal provider IDs without changing ordinary *, ?, bracket-glob, ambiguity, ordering, or deduplication behavior.
Request-wide Cost Tiers
Custom models can declare request-wide long-context pricing undercost.tiers. The base cost and every tier must provide all four rates: input, output, cacheRead, and cacheWrite, in cost per million tokens. Each tier also requires inputTokensAbove.
input + cacheRead + cacheWrite, selects only tiers whose threshold is strictly exceeded, and uses the matching tier with the highest inputTokensAbove. Exactly 272,000 aggregate input tokens in the example still use the base rates; 272,001 use every rate from the tier, including the tier’s output rate.
For modelOverrides, cost is partial: any supplied scalar rate replaces that scalar while omitted scalar rates remain inherited. A scalar-only cost override also preserves inherited tiers. Supplying tiers replaces the whole inherited tier array; use "tiers": [] to clear it explicitly. Every supplied replacement tier must still be complete.
Thinking Level Map
UsethinkingLevelMap on a model to describe model-specific thinking controls. Keys are Atomic thinking levels: off, minimal, low, medium, high, xhigh, max. A level is selectable only when the active model supports it; xhigh and max are not universal provider capabilities.
Values are tristate:
Example for a model that only supports off, high, and max reasoning:
compat.reasoningEffortMap should move that mapping to model-level thinkingLevelMap. Use null for levels that should not appear in the UI.
Context Window
contextWindow is the model’s context size in tokens and drives local budgeting,
compaction thresholds, footer/stats, session replay, and RPC/SDK state.
contextWindow from the bundled pi-ai catalog. To
change one, use modelOverrides:
models:
Overriding Built-in Providers
Route a built-in provider through a proxy without redefining models:models array:
- Built-in models are kept.
- Custom models are upserted by
idwithin the provider. - If a custom model
idmatches a built-in modelid, the custom model replaces that built-in model. - If a custom model
idis new, it is added alongside built-in models.
Per-model Overrides
UsemodelOverrides to customize specific models without replacing the provider’s full model list. Overrides apply to matching built-in models and to models later registered by an extension through pi.registerProvider().
modelOverrides supports these fields per model: name, reasoning, thinkingLevelMap, input, cost (partial scalar rates plus optional full tier-array replacement), contextWindow, maxTokens, headers, compat.
Atomic reads one models.json from the active agent directory. It does not layer model overrides from .pi and .atomic files.
Within a single file, custom model definitions replace matching built-in entries after built-in overrides are applied. modelOverrides composes only with built-in and extension-registered models; it does not modify a same-ID custom model definition.
Behavior notes:
- Atomic retains the parsed override map even when an extension registers the matching provider/model after
models.jsonis loaded. - Model overrides come from the active agent directory’s single
models.json; no cross-file layering or merging is performed. - For matching built-in and extension-registered models, the model definition is the base and
modelOverrideswins configured fields. Extension-registered model headers are shallow-merged with override headers, with override headers winning duplicate names. A same-ID custom model replaces the built-in override result, including its complete header record. - A scalar-only
costoverride preserves inherited tiers. Supplyingcost.tiersreplaces the complete tier array, including[]to clear it; omitted scalar cost fields remain inherited. - Provider-level request headers remain a separate provider layer and are combined at request time.
- Unknown model IDs are ignored unless a matching model is subsequently registered by an extension.
- If
modelsis also defined for a provider inmodels.json, those custom models are merged after built-in overrides. A custom model with the sameidreplaces the overridden built-in model entry.
Anthropic Messages Compatibility
For providers or proxies usingapi: "anthropic-messages", use compat.supportsEagerToolInputStreaming to control Anthropic fine-grained tool streaming compatibility.
By default, Atomic sends per-tool eager_input_streaming: true. If a proxy or Anthropic-compatible backend rejects that field, set supportsEagerToolInputStreaming to false. Atomic will omit tools[].eager_input_streaming and send the legacy fine-grained-tool-streaming-2025-05-14 beta header for tool-enabled requests instead.
OpenAI Compatibility
For providers with partial OpenAI compatibility, use thecompat field.
- Provider-level
compatapplies defaults to all models under that provider. - Model-level
compatoverrides provider-level values for that model.
Constrained tool sampling
Tools may request{ type: "json_schema", strict: "prefer" | "require" } or { type: "grammar", variants: { openai_lark?: string, openai_regex?: string } }. prefer may fall back to ordinary tool calling; require must fail if the active provider/model cannot enforce the schema. Grammar tools use OpenAI custom-tool syntax and fall back to normal function handling when grammar capability is absent. Do not infer support from a provider name: Atomic carries the model’s explicit capability metadata through built-in catalogs, dynamic catalogs, overrides, SDK/RPC model objects, and isolated execution.
Strict JSON-schema support currently includes OpenAI, Anthropic, capable Bedrock Converse models, Mistral, and Gemini 3 through Google/Vertex. Earlier Gemini models cannot enforce required parameters: prefer falls back and require fails. OpenAI grammar tools are limited to capable GPT-5+ models on endpoints known to preserve custom tools; gateways such as OpenRouter may normalize and break them.
Catalog freshness and precedence
Authenticated remote catalogs are cached inmodels-store.json. Atomic revalidates pi.dev catalogs with the stored ETag through If-None-Match; an empty 304 Not Modified is success and retains the cached body while updating its check time. A newer bundled catalog wins over an older persisted overlay even when package file mtimes are misleading. Final visibility is built-ins, persisted/remote data subject to freshness, configured .pi then .atomic layers, and live provider catalogs/overrides. Provider failures retain the last usable provider-specific snapshot.
Claude Opus 5 is present in the generated Anthropic and Amazon Bedrock catalogs. Its metadata enables adaptive thinking, including xhigh where advertised. Bedrock uses its generated inference-profile ID, prompt-caching and strict-tool metadata, and preserves provider/AWS validation errors. Custom entries must reproduce those capabilities honestly rather than copying a display name alone.
openrouter uses reasoning: { effort }. together uses reasoning: { enabled } and also reasoning_effort when supportsReasoningEffort is enabled. qwen uses top-level enable_thinking. Use qwen-chat-template for local Qwen-compatible servers that require chat_template_kwargs.enable_thinking and preserve_thinking. Use chat-template for vLLM/Hugging Face chat templates that need configurable chat_template_kwargs, such as chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } } for DeepSeek V3.x templates.
cacheControlFormat: "anthropic" is for OpenAI-compatible providers that expose Anthropic-style prompt caching through cache_control markers on text content and tool definitions.
Example: