Fast tier — detection, storage, and wires#
Where the fast feature's code lives, where its data comes from, and the
detection rules. Sibling doc: thinking-effort.md
(the same "model capability → UI toggle" declarative pattern; the structure
is deliberately aligned).
1. What Fast is#
The gauge button in the composer's thinking-effort panel. When on, the request carries the vendor's high-speed knob:
| Family | Wire shape | Billing reality |
|---|---|---|
| GPT 5.4 / 5.5 / 5.6 | body service_tier: "priority" (OpenAI's priority processing) |
On the Codex subscription the endpoint advertises it as a per-model tier ("1.5x speed, increased usage"); which models expose it comes straight from service_tiers (§2.1), not a guess |
| Claude Opus 4.6 / 4.7 / 4.8 | body speed: "fast" + header anthropic-beta: fast-mode-2026-02-01 |
Also pay-as-you-go; a subscription account without usage credits gets Anthropic's 429 "Usage credits are required for fast mode", surfaced as-is — an account problem, not lack of support |
Absence from this table does not prove that a provider lacks a fast tier.
xAI documents Priority Processing for its official API. Grok 4.6 on the
subscription CLI chat proxy accepts the same service_tier values; other
Grok subscription models stay unverified. The UI and route-aware
behavior are specified in Composer speed control.
Implementation verification is tracked in the linked design status.
2. Route capability#
providers.fast.fast_capability is shared by the agent-settings projection and
turn dispatch. supports_fast is its boolean compatibility projection. Explicit
configuration false disables support. Codex uses the account catalogue; explicit
route true can declare support; Claude retains its existing declaration. Official
xAI API Grok 4.6 has a provider-and-endpoint-scoped declaration. Grok 4.6 on
the Grok subscription route is supported; other subscription models remain
unknown. Other routes retain catalogue lookup.
The thinking panel replaces its help icon with the supplied GaugeIcon. Activation adds no border or background; the needle remains advanced. The preference is stored per session and provider/model. Standard explicitly overrides agent defaults. The user-facing contract and prototype live in Composer speed control.
2.1 Codex's official source#
GET https://chatgpt.com/backend-api/codex/models?client_version=<ver> — the
same account-level endpoint the official codex CLI hits on startup —
authorized with the subscription OAuth bearer + chatgpt-account-id. Each
model carries service_tiers (an id:"priority" entry ⇔ has a fast tier),
supported_reasoning_levels (the thinking picker), and the real subscription
context_window (372k, not the API platform's 1050k). Requests and dispatch
use the originator: codex_cli_rs + version identity — the backend
greylists ids (e.g. gpt-5.6-luna) by client identity, so a different
originator gets the model in the list but 404s at dispatch.
models.dev is not the source here because it tracks the public API platform catalogue rather than the subscription front-door: it lists un-runnable ids, reports the wrong context window, and can only reconstruct a fast flag from an id-prefix guess.
3. Storage: read official → write config → read the file thereafter#
Codex principle: prefer the vendor's live catalogue, then OpenProgram's last-known-good catalogue, then the local official CLI cache.
| Layer | Location | Persistence |
|---|---|---|
| Codex official endpoint and CLI cache | openprogram/providers/openai_codex/list_models.py |
live endpoint first; ~/.codex/models_cache.json is a stale display fallback |
| OpenProgram subscription catalogue | providers/subscription_catalog.py |
atomic last-known-good copy; refreshed after login and periodically |
config spec row (with fast/thinking_levels/context) |
successful official refreshes automatically add, update, and retire account models while honoring disable tombstones | config file |
Model.fast field |
_build_model_from_row reads the config row's fast (row wins; codex rows always carry it); enters ENABLED_MODELS at registry build |
memory only (in-process dict, sourced from config) |
| claude-code hand table | providers/enabled_models.py::default_fast (only the Opus part is on the detection path now) |
source code |
| models.dev catalogue | openprogram/providers/sources/models_dev.py |
remote; 1h in-memory cache, no disk cache |
Flow: official endpoint → normalise → last-known-good cache + config.json → registry → supports_fast / dispatch. CLI cache rows can keep the browser useful offline, but their accompanying error prevents them from deleting or adding configured models as if they were fresh.
4. Event flow: adapts to any switch, no page reload#
connect / session switch / model switch / every turn ack+settle
→ frontend loadAgentSettings() (lib/runtime-bridge/providers.ts)
→ GET /api/agent_settings (apps/server/openprogram_server/_webui/routes/execution/runtime.py)
chat.fast = supports_fast(session's provider, model) ← recomputed
→ zustand agentSettings.chat.fast
→ composer re-renders: enables or explains the gauge control
Send-side double gate: the composer only attaches
service_tier: "priority" when fastEnabled && fastSupported; otherwise it
sends default. Dispatch revalidates the actual model. A stale preference never
enables priority on an unsupported route.
5. Wire side (request builders)#
| Builder | Behavior |
|---|---|
providers/openai_responses / openai_completions |
opts.service_tier → body service_tier (pre-existing) |
providers/openai_codex (ChatGPT subscription) |
opts.service_tier → body passthrough; dispatch uses the originator: codex_cli_rs + version identity (the backend greylists ids by client identity — see §2.1) |
providers/anthropic |
opts.service_tier present and model.fast → body extra_body={"speed":"fast"} + _BETA_FAST header (appended via _build_client(fast=...), never clobbering other betas) |
| all other wires | no passthrough; the knob never leaves the process |
6. File map#
openprogram/providers/types.py Model.fast field
openprogram/providers/enabled_models.py default_fast (claude-code Opus only) + config-row backfill
openprogram/providers/openai_codex/{openai_codex,runtime}.py service_tier passthrough; codex_cli_rs identity + _CODEX_CLIENT_VERSION
openprogram/providers/anthropic/{anthropic,_claude_code_direct_runtime}.py Claude fast wire + registration backfill
openprogram/providers/openai_codex/list_models.py official endpoint fetch + normalise (fast/thinking/context source)
apps/server/openprogram_server/_webui/_model_listing/fetchers/__init__.py orchestration: passes through fetcher fast/thinking, enrich can't overwrite
apps/server/openprogram_server/_webui/_model_listing/listing.py supports_fast entry; list_models_for_provider prefers fetcher thinking
apps/server/openprogram_server/_webui/routes/execution/runtime.py /api/agent_settings emits chat.fast
apps/web/lib/session-store/types.ts AgentBadgeInfo.fast type
apps/web/components/chat/composer/index.tsx toggle visibility + send gate
Change guide: codex fast/thinking is fully automatic — add/remove a model
needs nothing but a Fetch; change detection logic → touch only
listing.py::supports_fast; claude-code add/remove fast → the Opus part of
default_fast; another provider wanting fast → works once models.dev knows it.
Response evidence#
Completions and Responses retain requested and actual tier separately in Usage. Per-call tier evidence is accumulated as a list, saved with the assistant message, and rendered in the existing usage footer, including after reload. Missing tier metadata is unconfirmed. xAI reported cost takes precedence over catalogue cost; no speed ratio is inferred from a requested tier.