Custom Models
Add custom providers and models (Ollama, vLLM, LM Studio, proxies) via~/.atomic/agent/models.json (legacy ~/.pi/agent/models.json is also read).
When both files exist, Atomic reads the legacy .pi file first and the primary .atomic file second. For modelOverrides, entries are layered by provider and model ID: disjoint legacy entries remain available, while an exact primary provider/model entry replaces the complete legacy override entry. Atomic does not field-merge one override entry across files; use {} in the primary file to restore the built-in model values for that exact entry.
Built-in subscription providers such as Cursor (experimental) are selected with the same provider/model syntax, for example cursor/composer-2. Cursor image input is scoped to known multimodal Cursor Claude, Composer, Gemini, GPT, and Kimi model families (claude-, composer-, gemini-, gpt-, kimi-), plus grok-4.3; text-only Cursor models still reject images. Cursor image payloads must be non-empty standard base64, with MIME-style line wrapping whitespace accepted and stripped before serialization. Live private-API model metadata may fall back to estimated labels. Because Cursor support targets undocumented private endpoints with Cursor CLI-compatible headers, maintainers and users should explicitly accept the risk that it may conflict with Cursor’s terms, break without notice, or affect the Cursor account used to authenticate.
When Cursor omits token limits, Atomic derives them from its bundled @earendil-works/pi-ai model catalog and treats explicit 1M Cursor labels as a 1,000,000-token context floor; unmatched Cursor-only models keep conservative estimates instead of disappearing from /model.
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:/model. Edit during session; no restart needed.
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.
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 Options
contextWindow remains the scalar default and is always valid. Models that support multiple context sizes can also declare contextWindowOptions as positive token counts. Atomic hides unsupported choices in the /model selection flow and rejects unsupported --context-window values for the selected model. The active selection changes Atomic’s effective model.contextWindow, so local budgeting, compaction, footer/stats, session replay, RPC/SDK state, and extensions all use the selected token budget while the model’s scalar default remains unchanged.
/model and pick a model; when the chosen model exposes more than one window, Atomic immediately prompts for the context window as a follow-up step — a GitHub Copilot CLI-style picker that lists numbered Default and Long context tiers with their token counts (for example 272k tokens / 922k tokens for github-copilot/gpt-5.5, or 200k tokens / 936k tokens for Claude/Gemini long-context models such as github-copilot/claude-sonnet-5) — so you can choose one of the active model’s supported budgets. Persisted interactive selections are stored per model under defaultContextWindows["provider/modelId"] (raw token counts and compact labels such as 400k or 1m are accepted), so a Copilot-specific prompt cap does not leak into Anthropic, Cursor, or other providers. GitHub Copilot long-context requests treat 1m as a branded budget request and resolve it to the model’s largest advertised long-context tier not exceeding the request (for example 936k for Copilot Claude models), while other providers continue to require one of their own exact supported windows or use their natural scalar default. Successful explicit startup selections are recorded as context_window_change entries even when the chosen value equals the scalar default, preserving the user’s explicit budget choice across future settings changes and resume.
Use larger context windows deliberately. Some providers charge more for larger windows, and Atomic preserves each model’s default unless the user explicitly opts in through --context-window, the /model selection flow, per-model defaultContextWindows, or the optional global defaultContextWindow fallback.
GitHub Copilot context windows
GitHub Copilot context windows are measured in input (prompt) tokens, exactly like every other provider’scontextWindow, and are derived dynamically from GitHub’s live CAPI model catalog (GET {baseUrl}/models) rather than a hardcoded model list — so plain Copilot model ids GitHub adds, removes, or retiers are reflected automatically. Atomic fetches the catalog only when you actually have the GitHub Copilot provider authenticated, caches it on disk for 30 minutes, and refreshes the active interactive session’s model metadata as soon as that catalog is applied. Dynamic model creation is intentionally limited to picker-enabled, non-disabled chat entries with plain, non-namespaced ids; enterprise/org-deployed catalog ids containing / (for example octodemo/Octodemo_Foundry/DeepSeek-V3.2) are skipped instead of being exposed as github-copilot/* models. The same catalog also supplies Copilot output-token caps and selectable thinking levels: when CAPI advertises capabilities.limits.max_output_tokens, Atomic uses that live value as the model’s maxTokens instead of the bundled fallback; when CAPI advertises capabilities.supports.reasoning_effort as an array, Atomic hides unsupported thinking levels for both dynamically synthesized Copilot models and bundled pi-ai Copilot models. Models that only advertise thinking budgets or a boolean reasoning-effort flag keep their existing thinking-level behavior.
Each selectable Copilot window is a prompt/input budget. Atomic reads capabilities.limits.max_prompt_tokens for the full prompt cap, capabilities.limits.max_output_tokens for the maximum response/output cap, and treats capabilities.limits.max_context_window_tokens as the model’s total context capacity (prompt plus output reserve) and a compatibility fallback only when the prompt cap is absent. Models with tiered pricing expose their per-tier prompt budgets through billing.token_prices.<tier>.context_max: the default tier becomes the base window and a larger long_context tier is offered as a selectable option. For example github-copilot/gpt-5.5 resolves to a 272k default / 922k long prompt budget, github-copilot/mai-code-1-flash-picker advertises a 128k prompt cap with a 256k total context window, and github-copilot/claude-sonnet-5 plus other Claude/Gemini long-context models resolve to 200k default / 936k long. When the request is a rounded budget such as 1m, Atomic selects the largest advertised Copilot long-context prompt tier at or below that budget instead of falling back to the base 200k/272k window. Offline, unauthenticated, or non-Copilot sessions leave the built-in scalar window and output-token cap untouched and show no picker.
Selecting the long-context window does two client-side things:
- Raises Atomic’s local token budget (e.g.
922_000forgpt-5.5) for context collection, compaction thresholds, footer/stats, session replay, and SDK/RPC metadata. - Sends
X-GitHub-Api-Version: 2026-06-01on Copilot requests so GitHub returns/enforces the absolute long-context limits for eligible accounts.
contextTier, or model-id variant for Copilot long context. GitHub chooses the larger long_context billing tier server-side automatically when the prompt token count exceeds the model’s default budget. That tier consumes more Copilot AI credits and requires the account/actor to have Copilot long-context/usage-based billing entitlement enabled. If the account or selected model is still capped by GitHub’s server-side limit, the request is rejected (for example, prompt token count of N exceeds the limit of M) and Atomic surfaces a friendly entitlement/cost/server-cap hint instead of silently truncating context.
Custom models.json entries remain the escape hatch for providers, proxies, or Copilot accounts where the live catalog is unavailable. To adjust an existing built-in model, use modelOverrides:
models:
parseContextWindowValue(), formatContextWindow(), getSupportedContextWindows(), getModelDefaultContextWindow(), withContextWindowOptions(), and selectContextWindow() are exported from @bastani/atomic alongside their TypeScript helper types. The root export also carries the Model<Api> augmentation for contextWindowOptions and defaultContextWindow.
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, contextWindowOptions, maxTokens, headers, compat.
When both ~/.pi/agent/models.json and ~/.atomic/agent/models.json define modelOverrides, Atomic merges their nested provider/model maps in that order. Different model IDs survive from both files. For the same provider and model ID, the primary .atomic entry replaces the entire legacy .pi override entry rather than deep-merging individual fields. This complete-entry rule includes headers: a primary exact override without headers removes headers that came from the legacy override, but does not erase a surviving custom model definition’s own headers. An empty primary override ({}) therefore restores the model’s built-in values for that entry.
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. - Layered primary/legacy compatibility merges override maps by provider and model ID; disjoint entries survive, while a primary exact entry replaces the complete legacy entry without cross-file field-level merging.
- 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.
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: