The console's Models & APIs tab regrouped onto the ledger's eight groups —
speech · fp · tool · kit · persona ·
photo · room · app — in the Usage & cost tab's order
and colours, so a figure on Usage ▸ By function and the knob that moves it are one hop apart.
Every leaf of the vocabulary gets a row, including the eleven that have no knob today.
main, not yet on the box.
The console's Models & APIs tab is this page's §2, for real: lib/admin.html
loadModels() renders the eight groups off U_GROUPS, the fixed seats come
from GET /api/admin/models → fixed, and Usage ▸ By function ⇄ Models hop
both ways on a tab/group hash. §2 stays as the design specimen (its radios are paint);
§3 is the leaf map; §5 is what shipped beyond the mock — the owner's build rules.
v2 (owner's review of v1): one row format for everything — a radio row, the console's own —
the stored kind as every card's key, no explanatory text, an explicit Inherit row where inherit is
the default, and a single checked radio where there is no choice. v3: every row marks
thinking on / off explicitly (a switch where it is a choice, a word where it is not); an
Inherit row shows the model it resolves to; the kind key and the group name wear the group's
colour; sans for all — no mono anywhere in the tab; room.title is Flash only.A reader of the console holds two different groupings of the same money. Usage
stacks spend by the ledger's call kind — the closed group.leaf vocabulary of
usage-dashboard §3, eight coloured groups. Models
lays the knobs out by which part of the product they sit in — six uncoloured surfaces.
So「kit cost $3.10 this week」sends the reader to Models to find… no kit.
The eleven missing leaves — kit.* ×5, persona.* ×4,
photo.* ×3 less the eyes' price row, room.translate,
speech.interrogate, tool.promised — are not missing by accident: their
model is a constant in code, so there was never a knob to draw. But the page is read as an
inventory (「every model & search API, by role」 is its own subtitle), and an inventory
that omits a third of the ledger is a page that cannot answer「what runs persona.dream?」.
U_GROUPS — the usage
tab's own list, in the same file. A group rail under the tab title is the usage chart's
legend made a navigation: one chip per group, the chart's colour, the leaf count; click to jump,
and it follows the scroll.room.translate), then the name (Translate on divergence); the rows are the
console's radio rows. Where a setting exists the rows are the choices you have today, untouched.
Where the model is a constant in code the card has one row, one radio, checked — the fact,
in the same format as a choice. Where inherit is the default, the Inherit row is written
out, followed by the model it resolves to. Every row marks thinking — thinking off
/ thinking on as a word, with the switch beside it where it is a choice — so a call that
cannot think says so instead of saying nothing. No descriptive text anywhere; the server reports
the constants, the page never names a model itself.Markup and classes lifted from lib/admin.html, set in sans; the rail, the group
header, the kind-as-key card header and the thinking mark are the new pieces; every row is the console's .mrow.
Every model & search API, by function — the same eight as Usage ▸ By function.
Everything on this page saves on change — no Save button.
| model | input | cache write 5m | cache write 1h | cache read | output |
|---|---|---|---|---|---|
| Opus 4.8Anthropic | |||||
| Sonnet 5Anthropic | |||||
| Haiku 4.5Anthropic | |||||
| DeepSeek V4 ProDeepSeekoff-peak | |||||
| DeepSeek V4.1 FlashDeepSeekoff-peak | |||||
| Gemini 3.8 FlashGoogle | |||||
| Gemini 3.5 FlashGoogle | |||||
| Gemini EmbeddingGoogle |
The build follows this table: 28 stored kinds, where each sits today, and what its card holds. choice = an existing setting, moved as-is · one radio = a constant in code, one checked row · inherit = an Inherit row, checked.
| group | leaf | today | card | the model (today's constant) |
|---|---|---|---|---|
| speech | speech | Chats ▸ Panel | choice | the user's switcher · default pre-selected |
| speech.interrogate | Chats ▸ the interrogation's mind | choice — V4.1 Flash · V4 Pro (retired) | probe_mind_model · default PROBE_MIND_MODEL = deepseek-v4-pro · thinking ON (the call's own, per kind) | |
| speech.empty | — | inherit | the panel's model, retried | |
| fp | fp | Chats ▸ Floor producer ×2 | choice | Off / Code · V4.1 Flash · V4 Pro (retired, greyed) · V4.1 Flash · ± reasoning |
| tool | tool.prop | The toolbox ▸ prop master | choice — Off · V4.1 Flash · Jev 1.13 (TypeSafe's decision model, 2026-09-23: the same question as one Choice, no prose, ~0.2–0.6 s where Flash took 1.05 s; greyed when the server has neither a TypeSafe nor an OpenRouter key; where Jev fits §3) · Pro (retired) | PROP_GATE_MODEL · env MAD_PROP_GATE_MODEL · the door: TYPESAFE_API_KEY, else OPENROUTER_API_KEY |
| tool.act | The toolbox ▸ act call | choice + inherit | inherit · V4.1 Flash · V4 Pro (retired) | |
| tool.promised | — | one radio | ACT_GATE_MODEL = deepseek-v4.1-flash · env MAD_ACT_GATE_MODEL | |
| kit | kit.interpreter | — | one radio | deepseek-v4.1-flash (inline) |
| kit.ruling | — | one radio | deepseek-v4.1-flash (inline) | |
| kit.voice | — | one radio | deepseek-v4.1-flash (inline) | |
| kit.route | — | one radio | PROBE_ROUTE_MODEL = deepseek-v4.1-flash | |
| kit.authoring | — | choice + inherit | authoring_model: "" (inherit) · deepseek-v4.1-flash · deepseek-v4-pro | |
| persona | persona.memory | — | one radio | MEMORY_MODEL = deepseek-v4.1-flash |
| persona.dream | — | one radio | MEMORY_MODEL = deepseek-v4.1-flash | |
| persona.followup | — | one radio | MEMORY_MODEL = deepseek-v4.1-flash · agenda ⑦'s dry-run judge (send | hold) | |
| persona.growth | — | one radio ×2 | RECENT_MODEL = deepseek-v4.1-flash · the sweep + Serper, the search | |
| persona.embed | — | one radio | wordpick.EMBED_MODEL = gemini-embedding-001 | |
| photo | photo.shadow | — (price row only) | one radio ×2 — every photo · dense pictures | PHOTO_MODEL = deepseek-v4.1-flash · PHOTO_DENSE_EYES = gemini-3.8-flash |
| photo.gate | — | one radio | PHOTO_GATE_MODEL = deepseek-v4.1-flash | |
| photo.look | — | shares the eyes card | the same eyes as the shadow | |
| room | room.translate | — | one radio | TRANSLATE_MODEL = deepseek-v4.1-flash |
| room.title | The toolbox ▸ auto-title | one radio | V4.1 Flash only — the Pro row is dropped (v3) | |
| room.dispatch | Chats ▸ Dispatch | choice | Claude native (Haiku 4.5) · SERP + V4.1 Flash | |
| app | app.convene | Chats ▸ Panel curator | choice | V4 Pro · V4.1 Flash · ± reasoning |
| app | app.search | the persona search's sentence read | fixed | Jev 1.13 — a constant in code; no key on the server → the box keeps its token filter alone |
| app.seen | Discover ▸ Seen | choice ×3 | rank · writer · hero image | |
| app.vibes | Vibes | choice | engine model · news chain | |
| app.ink_editor · _writer · _director · _bank | Ink | choice ×4 | V4 Pro · V4.1 Flash — thinking on for the editor, writer, director; off for the bank | |
| app.ink_judge · _rendition · _desk · _brief · _voice | Ink | choice ×5 | V4.1 Flash · V4 Pro (the rendition thinks; the rest don't) | |
| app.ink_score | Ink | choice | Gemini 3.8 Flash (the rival family) · V4.1 Flash — one seam, judge_text, for every judge | |
| app.ink_copy | Ink | choice | Gemini 3.8 Flash (the rival) · V4.1 Flash | |
| app.ink_art | Ink | choice | Nano Banana 2 · Nano Banana 2 Lite — $/image from the price sheet | |
| app.ink_pegs | Ink | one radio | Serper — the news vertical; the fee stays under prices | |
| app.studio | Persona Studio | choice ×7 | the step matrix | |
| app.stt | API prices ▸ STT | one radio | iFlytek · the price stays under prices | |
| prices | — | API prices | as today | LLM · image · search · STT — one surface |
loadModels() emits the groups in
U_GROUPS order with the new .fgrp header; every card, every POST
endpoint and every change handler is the same code — a card moves, it does not change.GET /api/admin/models grows a
fixed block — {leaf: {model}}, or {inherit: true} — read from
the constants themselves (MEMORY_MODEL, PHOTO_MODEL, PHOTO_DENSE_EYES,
ACT_GATE_MODEL, …). The renderer draws it as the same .mrow it draws a choice
with — one radio, checked, no handler. The page never hard-codes a model name, so a constant that
moves in code moves on the page the same commit.Off · V4 Pro · V4.1 Flash — one radio group; Off posts
prop-gate on=false, a model posts on=true + the model. Same two endpoints
as today, one control instead of two.db.KIND_ALIAS values + the §3 table) appears as a .kind
key in the Models render — a new leaf with no row fails the gate, the way a new kind with no
colour already does.--s1…8 already live in
theme.css for the usage tab; the rail, the group name and the kind key all wear them
(a --fc per group section). No new hue anywhere.var(--sans); the tab title keeps the console's serif.
「reasoning」becomes「thinking」on the switch label, and the state is written out beside it.room.title offers Flash only (the Pro row is removed, the
setting collapses to a single checked radio); the prop master's switch folds into its list.usage-dashboard §3
gains a one-line pointer here; the console-redesign Models section gets a「superseded by」note.fp is split in the ledger — fp.open (the opening staging,
_open_stage) · fp.mid (the per-turn director, _f_engine + the two
drain sites). staging aliases to fp.mid; a row booked as plain fp
before the split stays fp — open or mid was never recorded, so it is never guessed.
The mid-chat lead row reads Code, not Mechanical.cost.PRO_ROUTED_AT): the Pro row prices on the Flash sheet from the hour,
so a Pro-named seat — the floor producer's v1p arm, a fixed constant, a room saved on Pro — bills what DeepSeek bills; the 08-16 Pro
row joined the superseded defaults. The greyed Pro rows in the console keep their place for V4.1 Pro.cost.FLASH_CUT / FLASH_CUT_AT), never an
edit — the ledger prices a call when it is made, so the row flips by the clock; the 08-16 Flash row joined the superseded defaults so a saved
console snapshot cannot shadow the cut. The specimen above shows the cut's figures.MODELS; MODEL_RETIRED reads a room saved on any of them onto V4.1 Flash; every
Flash-tier constant (the clerks, the gates, memory, translate, the title, the dispatch's SERP distiller, the probe's router, the photo eyes and
the photo gate) says deepseek-v4.1-flash; the V4.1 rows carry vision, so a photo's pixels ride a V4.1 room's own line the way
the Vision seat's did; the FP's v1f/v1fr arms are legacy aliases onto V4.1 (a saved room keeps working, the console offers v1x).
Ink's seats and the Studio's steps follow. ⚠ Not baselined: the Flash-tier seats were not in the V4.1 exam (it swapped the Pro seats); the toolbox
gate ran green on the swept build, the memory and Ink seats are owed their own read.deepseek-v4.1-flash ± reasoning is a panel row, a floor-producer arm (v1x/v1xr),
a prop-master and act-call choice, an option on every role radio (curator · Seen · Vibes · the Studio's steps)
and on every Ink seat; the two seats that offered only Pro — the interrogation's mind (a constant until now,
a choice since: probe_mind_model, thinking still the call's own) and the kit's authoring call — offer it too. V4 Pro keeps its rows greyed with a dashed「retired」chip — inert unless it is still
the pick, never offered to a user, never the default — because DeepSeek routes every V4 Pro request to V4.1 Flash
at Flash's price from 09-10; MODEL_RETIRED reads a room saved on Pro onto the V4.1 seat, and the rows
become V4.1 Pro when that ships. The wire id rides one seam, cost.api_id: cost.V41_WIRE_DEFAULT — deepseek-flash, the GA name read off the API's model list at 10:54 CST on 09-10 (the beta id until then; not the guessed deepseek-v4.1-flash, which the API refuses) — while a one-token probe, hourly, says it answers;
deepseek-v4-pro meanwhile — routed to V4.1 Flash by DeepSeek after the launch, so a wrong guess at the name never fails a turn;
MAD_V41_FLASH_API is trusted without a probe (the beta id on dev before launch); settings and the ledger keep the canonical id.claude-sonnet-5; better and cheaper on
the intro price). MODEL_RETIRED + model_now() read a room saved on 4.6 onto the
Sonnet 5 seat at resume and at switch, so a Sonnet chat stays a Sonnet chat. ⚠ On Sonnet 5 an
omitted thinking runs adaptive thinking (4.6 ran none) — the panel call now sends
thinking: disabled explicitly, so the row's「thinking off」is true.builder.MODEL_RETIRED); the 3.7 price row stays so history prices. ⚠ Reviewers report 3.8 writes ~30% more output
tokens on the same task — watch the Ink and Studio bills.builder.MODELS, the default
audit_b, the moderation fallback); a saved step matrix naming 3.5 reads onto 3.7
(builder.MODEL_RETIRED).cost.PRICES now) · Gemini 3.8 Flash · Gemini Embedding. Opus and Haiku
are off the table — cost.py still prices them, so the Claude web-search dispatch (Haiku)
bills from the built-in sheet, uneditable here. Every figure shows as 1.0000; STT keeps
six decimals because $0.000361 at four would read $0.0004.speech.empty ·
tool.act · kit.authoring) re-resolve the moment the Panel card's default radio
moves, before the save round-trips.room.title is Flash only — the endpoint offers one option; one checked radio.kit.authoring became a setting (owner, after the build): Inherit the chat's panel
model (default, unchanged behaviour) or V4 Pro — authoring_model,
POST /api/admin/settings/authoring-model, DeepSeek only like the act call; the call
bills and tallies under the model it actually ran.KIT_CLERK_MODEL was hoisted out
of three inline literals for it, and the wake-door check now follows ACT_GATE_MODEL
(env-movable) like the quick act gate it is.#usage/<group> and
flashes that By-function row (or the table, when the window holds no spend for the group); a
By-function row's function cell opens #models/<group> and flashes the section.#sec-models; the usage tab keeps its mono.