Skip to main content

Model configuration reference

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

The apiKey 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.
In non-command values, $$ produces a literal $ and $! produces a literal !. Legacy uppercase env-var-like values in existing 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

In models.json, headers values must be strings. A null suppression marker is supplied only by provider/catalog auth or a before_provider_headers extension hook; when present there, null suppresses the provider’s default header with the same name.

Model Configuration

Current behavior:
  • /model, --list-models, the interactive footer, status messages, fallback notices, and subagent and workflow labels show a model by its raw provider/id (or id where the provider is already clear), followed by the reasoning level it runs at, e.g. anthropic/claude-sonnet-4-5 high. That is the string --model provider/id:level takes.
  • The configured name is used for model matching and secondary model detail text, such as the dim column in /model. It never replaces the model id.
  • input lists the modalities Atomic can send. Possible values are ["text"], ["text", "image"], and ["text", "image", "pdf"].
PDF support is a platform capability, not a per-model one. Anthropic documents that “All active models support PDF processing”, through the same vision path as images, so upstream metadata includes it on every Claude entry. Atomic advertises PDF only where its runtime can serialize a document block: Anthropic Messages and Amazon Bedrock Converse. Claude mirrors on other providers stay at ["text", "image"]. A document sent to such a model becomes a visible placeholder rather than being silently dropped. Bedrock Converse requires citations for full visual PDF understanding; without them, it falls back to text extraction. "pdf" means PDF specifically. A document block’s media type must be application/pdf; other values are rejected by name.

OpenAI Built-in Models

Prices are in dollars per million tokens, as input / cached input / cache write / output. An empty cell means the fact does not apply or OpenAI has not published it. Notes for every row:
  • All entries accept text and image input and have a 128,000-token output limit.
  • Above 272,000 aggregate input tokens, the long-context price applies to the whole request. OpenAI documents a 1,050,000-token API maximum for Astra and Terra. Increase contextWindow through a model override only when you intend to use the larger window and its prices.
  • With ChatGPT subscription sign-in, GPT-6.1 Sol has no long-context price on Standard, Fast, or Ultrafast. Account aliases inherit the limits for their own login method.
  • A -fast choice sends the base upstream ID with service_tier: priority, and an -ultrafast choice sends it with service_tier: "ultrafast"; Atomic records the suffixed ID. Do not pre-multiply catalog costs. Ultrafast is 6× Standard for input, cached input, cache write and output, following OpenAI’s published Ultrafast pattern; the OpenAI API pricing page lists these GPT-6 Astra and GPT-6.1 Sol rates. Codex does not list a Fast tier for Terra yet. Auto routing requires an Ultrafast choice’s exact ID in modelConstraints.allowedModels. Published evaluations describe base-model results and measured efforts, not separate Fast or Ultrafast measurements. See Fast and Ultrafast tiers and provider tier caveats.
  • Codex’s minimal UI alias is not a separate API effort, and Codex’s client-side ultra orchestration preset is not an API reasoning level; neither appears as its own effort in Atomic’s selector.
  • Mid-conversation effort changes are explained in Changing GPT-6 thinking levels.
  • On Amazon Bedrock, Atomic sends each ID unchanged and sends the selected effort as Bedrock’s OpenAI reasoning_effort field.
  • On GitHub Copilot, requests use Copilot’s Responses endpoint, and account policy controls availability. Copilot fast models are the exact IDs the account’s model list advertises, not derived variants, and Codex’s Ultrafast tier does not apply. The table lists no Copilot rates.
  • Atomic does not synthesize Azure OpenAI Astra entries. Other catalogs are imported as published: OpenRouter publishes openai/gpt-6-astra and openai/gpt-6-astra-pro, and the Vercel AI Gateway publishes openai/gpt-6-astra and openai/gpt-6-astra-fast, with their request-wide long-context prices. Vercel owns its suffixed ID, so it remains route-less and does not gain Atomic’s first-party fast-route behavior.
  • On OpenAI Responses, these models use the newer prompt-cache payload. cacheRetention: "long" sends prompt_cache_options.ttl: "30m" instead of the legacy prompt_cache_retention: "24h"; none sends explicit mode without a cache key, and short sends neither cache option. Earlier Responses models keep the 24-hour field for long retention.

Image Input Limits

Use inputLimits.images.resize to configure how new images are encoded before they enter conversation history:
maxBytes is the maximum base64-encoded payload size. Omitted resize fields use Atomic’s conservative defaults: 2000×2000, 4.5 MiB encoded, and JPEG quality 80. Built-in vision models carry that profile explicitly so unknown gateways never receive larger images than before. Atomic applies the selected model’s resize profile to @file attachments, the read tool, and images returned by tools. @file attachments are resized once the run’s model is known, after before_agent_start handlers may have changed it. Images are encoded once before they enter history; changing models does not rewrite historical images or invalidate the cached conversation prefix. The images.autoResize setting can disable resizing globally. The catalog can also record inputLimits.maxRequestBytes, images.maxPerMessage, and images.maxPerRequest. These fields describe hard provider limits; Atomic does not yet rewrite or reject conversation history based on them.

Sampling Parameters

samplingParams is a free-form object merged into every request body for an OpenAI-compatible model after the fields Atomic sets, so its keys win. Use it to send parameters that Atomic does not model, including server-specific values such as llama.cpp’s min_p or vLLM’s top_k:
Only OpenAI-compatible APIs apply these values (openai-completions, openai-responses, and azure-openai-responses); other APIs ignore them. Per-request keys override model defaults and named request fields. In modelOverrides, samplingParams merges per key with the base model’s values. Keys are provider-defined and remain unchanged; malformed samplingParams values are rejected while loading models.json.

Sampling by Thinking Level

samplingParamsByThinkingLevel sets sampling parameters per thinking level on top of the model’s samplingParams. Its keys are Atomic thinking levels (off, minimal, low, medium, high, xhigh, and max), not the provider values from thinkingLevelMap:
Atomic first clamps an unsupported thinking level to the nearest supported one, then merges the model’s samplingParams, the effective level’s override, and request-level samplingParams in that order. Later values win per key, and a level without an entry keeps the model defaults. In modelOverrides, each level’s entry merges per key with the base model’s entry for the same level. These fields apply only to openai-completions, openai-responses, and azure-openai-responses; other APIs ignore them. For openai-completions models with compat.supportsTemperature: false, the restricted keys are still removed from the final request, including keys that come from a level’s override. For vLLM OpenAI-compatible models that share the reasoning and answer budgets, set compat.supportsThinkingTokenBudget to true. Atomic sends the opt-in thinking_token_budget value for an enabled thinking level and always leaves 1024 tokens for the final answer. Pi’s defaults are 1024, 2048, 8192, and 16384 tokens for minimal, low, medium, and high; the thinkingBudgets settings override them. xhigh and max use the high budget, and Atomic omits the field when no positive budget remains after reserving answer space. Model references resolve the complete, unmodified ID before Atomic interprets thinking suffixes or glob syntax. For example, if the catalog contains the literal 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 under cost.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.
Atomic chooses one rate set for the entire request. It calculates aggregate input as 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.
This override changes only the base input rate, retains the model’s other base rates, and clears its inherited long-context tiers. A constant thinking-token cap can go here too, but it will not follow thinkingBudgets or leave room for the answer. Prefer compat.thinkingTokenBudgetField (or the supportsThinkingTokenBudget alias) for that.

Thinking Level Map

Use thinkingLevelMap 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:
Example for a model where thinking cannot be disabled:
Migration: older configs that used compat.reasoningEffortMap should move that mapping to model-level thinkingLevelMap. Use null for levels that should not appear in the UI. /thinking opens the thinking-level selector. Enter applies the level to the current session only. Persist (the selector’s save action) writes settings.modelThinkingLevels for the active model instead of replacing the global defaultThinkingLevel. Settings → Default thinking level per model lists those overrides.

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.
Built-in models take their contextWindow from the bundled pi-ai catalog, and GitHub Copilot models discovered from your account’s model list take it from that list. To change one, use modelOverrides:
To add a new model id under a built-in provider, define it in models:

Overriding Built-in Providers

Route a built-in provider through a proxy without redefining models:
All built-in Anthropic models remain available. Existing OAuth or API key auth continues to work. To merge custom models into a built-in provider, include the models array:
Merge semantics:
  • Built-in models are kept.
  • Custom models are upserted by id within the provider.
  • If a custom model id matches a built-in model id, the custom model replaces that built-in model.
  • If a custom model id is new, it is added alongside built-in models.

Per-model Overrides

Use modelOverrides 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, inputLimits (deep-merged), cost (partial scalar rates plus optional full tier-array replacement), contextWindow, maxTokens, samplingParams (merged per key), samplingParamsByThinkingLevel (merged per level, then per key), 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.json is 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 modelOverrides wins 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 cost override preserves inherited tiers. Supplying cost.tiers replaces 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 models is also defined for a provider in models.json, those custom models are merged after built-in overrides. A custom model with the same id replaces the overridden built-in model entry.

Derived Fast Model Variants

When a provider enables fast mode through an API parameter (OpenAI and xAI service_tier, Anthropic speed), Atomic derives a <id>-fast variant that sets that parameter (for example anthropic/claude-opus-5-fast). When a provider publishes fast mode as its own model ID (GitHub Copilot), Atomic uses that ID exactly as the provider publishes it, with no derivation; the rest of this section covers derived variants only. Derivation runs after built-in composition, models.json custom models, extension model lists, and modelOverrides on those models, so it sees the final catalog. A modelOverrides entry keyed on a derived -fast ID is then applied to the derived entry itself. See Providers for eligibility and what each provider sends upstream. These rules apply when you write models.json:
  • Custom models do not get automatic fast variants. Entries in a provider’s models array are used exactly as declared, including entries that replace a built-in model. For example, gpt-6-astra-slow does not create gpt-6-astra-slow-fast. Use modelOverrides instead of replacing a built-in through models if you want to customize it while keeping automatic fast derivation.
  • Your exact ID wins. If a provider, a custom model in models, or an extension already defines that exact <base>-fast ID, Atomic keeps yours untouched, does not derive a duplicate, and prints a warning naming the model to rename or remove if you wanted the derived variant instead. A model you define is an ordinary model: the -fast suffix alone never gives it fast routing behavior.
  • modelOverrides applies to derived variants. A derived entry is a real catalog model, so modelOverrides["gpt-5.6-sol-fast"] customizes it exactly like any other model, and its routing metadata survives the override. Overriding the base model still flows through to the derived entry by inheritance; a fast-specific override wins over that inherited value.
A derived variant inherits the base model’s cost. The provider adapter applies the fast multiplier at request time (OpenAI’s priority tier, Anthropic’s fast-mode speed), so do not pre-multiply cost in an override. An Ultrafast variant is the exception: it carries the provider’s published Ultrafast rates as its own cost, which a modelOverrides entry keyed on its ID replaces.

Anthropic Messages Compatibility

For providers or proxies using api: "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.
supportsMidConvoEffort and enforcesPreservedThinkingBinding compose rather than replace one another. The former is restricted to exact provider/model transports that accept effort-only system messages and adds the two per-turn-effort betas. It also enables drop_block, because historical effort changes can invalidate a signed prefix. The latter remains a separate Atomic compatibility flag for transports that enforce preserved-thinking prefixes but do not accept effort-only messages. Do not enable supportsMidConvoEffort for an API that merely imitates the Messages shape. supportsForcedToolChoice and supportsTemperature also exist on the Amazon Bedrock and OpenAI-compatible completions compat objects, with the same meanings and the same true defaults. Unlike the two preserved-thinking flags, which describe Anthropic’s first-party endpoint, these describe the model, so Atomic applies them to every mirror that reaches it rather than only to provider: "anthropic". On the completions adapter, supportsTemperature: false strips temperature, top_p, and top_k from samplingParams. The strip runs after the last-wins merge, including model-level samplingParams defaults, so the merge cannot restore parameters the model rejects. Every other custom key still overrides named request fields as before.

Server-side fallback configuration

Set compat.allowedFallbackModels at provider level, on a custom model, or in modelOverrides. A supplied array replaces inherited targets rather than appending to them. For example, disable built-in fallbacks for one model in models.json:
To enable a target, use an entry such as { "provider": "anthropic", "model": "claude-opus-5", "cost": { "input": 5, "output": 25, "cacheRead": 0.5, "cacheWrite": 6.25 } }. Verify the provider’s current rates and supported fallback targets before configuring them. Atomic sends the model names in array order and uses matching provider/model cost metadata to account for the serving model.

Forced tool use on Claude Fable 5.1

Claude Fable 5.1 rejects forced tool use — tool_choice: {"type": "any"} and {"type": "tool", ...} — on every request with a 400, whichever platform serves it. Anthropic’s guidance is to use tool_choice: {"type": "auto"} with strict tool use or structured outputs instead. Atomic’s own agent loop only ever asks for auto or none, so no interactive session can reach this. It is reachable through the @bastani/pi-ai library’s Anthropic, Bedrock, and OpenAI-completions entry points, which accept the wider tool-choice shape. For a model marked supportsForcedToolChoice: false, all three fail the request with an error naming the model and the remedy, before the round trip. That matters most on a gateway: OpenRouter drops parameters a model does not support, so an unguarded forced choice would vanish silently and return a plausible answer that ignored the instruction. Atomic deliberately does not substitute auto on your behalf. Asking the model to call a specific tool and asking it to decide for itself are different requests, and silently swapping one for the other would discard an instruction you gave explicitly. If the substitution is what you want, make it yourself — branch on compat.supportsForcedToolChoice to decide. Every other model passes forced choices through unchanged, and auto and none are never altered on any model. On the OpenAI-completions path the tool-choice union is wider than Anthropic’s, and four of its members force a call: "required", {"type": "function", ...}, {"type": "custom", ...}, and {"type": "allowed_tools", "allowed_tools": {"mode": "required", ...}}. All four are rejected. allowed_tools with "mode": "auto" is not rejected: OpenAI documents that mode as letting the model pick from the allowed tools and generate a message, so it narrows the candidate set rather than forcing a call, and it reaches the provider unchanged. Two scoping details are worth knowing. Claude Fable 5 is not restricted — Anthropic names Fable 5.1 and Mythos 5.1 as the exceptions to forced tool use working, and OpenRouter’s own metadata agrees, so the guard is version-scoped rather than family-scoped. That is the opposite of supportsTemperature, which Anthropic’s sampling-parameter sentence applies to both Fable generations. And a provider “latest” alias such as OpenRouter’s ~anthropic/claude-fable-latest is not covered: its id names no version, so no rule keyed on the id can stay true if the alias re-points at a model that accepts forced tool use. If you use such an alias and need a forced choice guarded, pin the versioned id instead.

Preserved thinking and model switches

On first-party Anthropic models, switching to Claude Fable 5.1 preserves reasoning from earlier Claude models. Switching from Fable 5.1 to an earlier model drops reasoning that model cannot read, without billing for those dropped blocks. Visible assistant text, tool calls, and tool results remain available in either direction. See Anthropic’s preserved-thinking guide. Changes to the system prompt, tools, or conversation prefix can invalidate signed reasoning. Atomic asks Fable 5.1 to drop affected blocks rather than fail the turn. Reported drops appear in assistant diagnostics as anthropic_input_transformations, with counts, reasons, and block paths. Compaction resets the signed reasoning chain while preserving retained text, tool calls, and tool results. Do not expect pre-compaction reasoning blocks to survive a boundary. Server-side fallback may continue a declined Fable 5.1 response with Claude Opus 4.8 or Claude Opus 5. The assistant turn keeps a fallback marker and records the serving model for usage accounting. Do not remove or reorder this marker when replaying messages. These preserved-thinking behaviors apply only to first-party anthropic models using anthropic-messages. Bedrock, Vertex, Copilot, opencode zen, Vercel AI Gateway, and compatible proxies do not receive them automatically. On those routes, switching models does not replay the earlier model’s thinking, and a changed prefix may cause a provider error. Enable compat.delegatesThinkingModelBinding and compat.enforcesPreservedThinkingBinding only for custom providers known to support the corresponding behavior.

OpenAI Compatibility

For providers with partial OpenAI compatibility, use the compat field.
  • Provider-level compat applies defaults to all models under that provider.
  • Model-level compat overrides provider-level values for that model.

Changing GPT-6 thinking levels

With an API key on openai or openai-api, changing the thinking level during a GPT-6 session preserves the prompt-cache prefix. This includes level changes during tool use. ChatGPT subscription sign-in and thinking off use the normal request-level effort instead. Other changes to the prompt, tools, or conversation can still invalidate the cache. For a custom openai-responses model, enable compat.supportsMidConvoEffort only if its endpoint accepts configuration_update input items. If the endpoint rejects these items, set the flag to false in the model’s compat override.

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

Atomic caches remote catalogs in models-store.json, keeps each provider’s last usable snapshot on failure, and prefers newer bundled data over stale remote overlays. The final catalog combines built-ins, fresh persisted/remote data, the active agent directory’s single models.json, and live provider catalogs/overrides. Project .atomic/.pi model files and legacy agent-directory fallbacks are not merged. 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. Use thinkingFormat: "baseten" with chatTemplateArgs for providers that expose toggle controls through chat_template_args and optionally support top-level reasoning_effort. thinkingTokenBudgetField is independent of thinkingFormat. Do not enable it on the generated Qwen catalog: those models already send reasoning_effort, and DashScope rejects thinking_budget together with reasoning_effort. cacheControlFormat: "anthropic" is for OpenAI-compatible providers that expose Anthropic-style prompt caching through cache_control markers on text content and tool definitions. Example:
Vercel AI Gateway example: