Models and Cost
Heph does not have "a model". It has tiers: named groups of one model each,
and each piece of work is sent to the tier for its role. The coding you ask
for goes to the tier whose role includes coding; a plan turn goes to the
review tier; a --quick turn to the quick tier. A profile is a set of
tiers, and each conversation runs on one
(Tiers and Profiles). You can change which model fills
which role in config, or override the choice for one conversation.
The default tiers
Each provider's catalog defaults make a built-in profile of three tiers; with
one provider, it is the default profile. heph profiles prints them all:
| Tier | Roles | Default model (Bedrock) |
|---|---|---|
frontier | coding, review | The catalog's large model, e.g. Claude Opus |
workhorse | research, summarize | The catalog's small model, e.g. Claude Haiku |
fast | quick, commit-message, title | The same small model, as its own tier so it can be pointed elsewhere |
Only coding, review, and quick are used by anything today; the other roles
exist so the layout does not change when the features that use them arrive. The
first-run config file lists these tiers as comments, with the actual model ids,
so you can see what was picked.
Thinking
A tier whose role includes coding or review has extended thinking on by
default, at high effort; the others have it off. Thinking is the model
reasoning before it answers, and it shows in the conversation folded to one
line. It costs output tokens and improves the work on hard problems; for a quick
question it is waste, which is why the fast tier has it off.
[tiers.NAME] thinking = "off" or "adaptive" overrides the default;
effort = "low" | "medium" | "high" | "xhigh" | "max" sets how much. Models
that do not take an effort level (Haiku 4.5, Sonnet 4.5, Opus 4.1) get a
thinking token budget derived from the effort instead; heph knows which is which
from the catalog. A GPT model reasons at its own default effort when the tier
says off, since its API has no switch for it, and on Bedrock returns no
summary of the reasoning to fold into the conversation.
Overriding for one conversation
--profile picks the profile; --tier NAME=MODEL runs one of its tiers on
another model, and --model is short for --tier on the tier doing the
conversation's role:
heph --profile work # every role from the work profile
heph --model fast # the turns on the fast tier's model
heph --model claude-haiku-4-5-20251001 # a model id, if one configured provider has it
heph --model bedrock/us.anthropic.claude-sonnet-4-6 # provider/model, when several could
heph --tier fast=claude-sonnet-4-6 # commit messages and titles on Sonnet
A bare model id is looked up across your configured providers; if two of them
offer it, heph asks you to say which with the provider/ prefix. The tier keeps
its own settings (roles, max_tokens, thinking) on the new model. The
conversation keeps the choice: --continue routes it the same way.
The status bar shows the model's short name: opus-5, sonnet-4-6,
haiku-4-5-20251001.
Native Ollama
heph --model ollama/NAME selects an already installed tool-capable model
without a cloud credential. With no routing, all roles use it and continuation
retains that bootstrap. Existing profiles keep their other role assignments;
selecting a local conversation model is not a local-only promise, and mixed
routing produces a warning. No automatic download or cloud fallback occurs.
heph models --provider ollama discovers the selected endpoint; ordinary cloud
startup does not probe an unselected local server.
Text attachments and vision-capable image attachments use the normal workflow. Thinking defaults to the server's behavior unless the tier explicitly configures supported controls. Runtime allocation, attachment limits, locality cautions, and experimental validation status are in Providers.
What things cost
Every request is recorded with its token counts, split four ways, and its cost at the model's catalog rates:
| Tokens | Rate |
|---|---|
| input | Prompt tokens sent fresh |
| output | What the model wrote, thinking included |
| cache write | Prompt tokens written to the provider's cache |
| cache read | Prompt tokens served from the cache, at a fraction of the input rate |
heph models shows the four base rates for every model. The status bar shows
muted totals like $1.00 / $4.00 / $8.00, in this order:
-
Session: requests made by this active heph process, across ref switches. A new process starts at zero, even when resuming a pending turn.
-
Total: all requests in this ref's ancestor history. Resuming restores it;
/cleardoes not erase it. Forks inherit ancestor spend, but daily totals count each request only once. Moving the ref changes its reachable total. -
Day: all requests across projects, sessions, providers, and models under this heph data root today. Days use the OS timezone through Jiff (or
TZ), including daylight-saving transitions; totals refresh every five seconds.
Dollar amounts have no approximation markers. The status bar uses two decimal
places; other cost displays use two above $1 and four otherwise. A request
interrupted before final provider usage keeps its input estimate; it is not a
confirmed bill. Accounting runs on independent read-only workers, not on prompt
edits or sidebar toggles. The status bar keeps last-good amounts without refresh
messages; - means no compatible sample, not zero spend. In /stats and the
sidebar, loading means the first sample is pending; updating or stale
keeps a compatible last-good value visible. Routine refreshes do not pulse valid
compact labels. Known ledger changes show updating; ten seconds without a
compatible successful sample shows stale. Retries keep failures visible until
recovery. Errors remain visible in /stats, and missing data is unavailable,
not zero. A ref/tip or local-day change clears incompatible values rather than
displaying them under the new label. These totals include title, branch-name,
and commit-message requests as well as normal rounds. They are displays, not
enforced daily budget limits.
Each turn-end line shows tokens and USD separately for uncached input, output
(including thinking), cache reads, and cache writes. It survives reopening. The
cache status figure is the share of live prompt tokens served from cache;
/clear resets live token/cache counters, not persisted spend.
/cost opens a focused dashboard at any width: session, thread, and today split
into uncached input, output (including thinking), cache read, and cache write
tokens and USD. It also shows cross-project daily spend, requests, and
all-category token counts for today plus the previous six local calendar days.
Empty days are included; today is marked partial. The seven-day total and daily
average include partial today. Weekly provider/model totals are ranked by spend.
Request and estimate counts indicate how much usage lacks final provider data.
The input cache hit rate is cache-read tokens divided by uncached input plus
cache-read and cache-write tokens; without input tokens it is n/a.
The dashboard opens from cached data and refreshes asynchronously while open.
Weekly reads open each project once per batch. Failures retain compatible
last-good values with visible errors, never a misleading zero; changed dates or
timezones clear incompatible samples. Up/Down, PgUp/PgDn, and the wheel scroll;
Esc or another non-scroll key closes it. A late completion never reopens it.
There are no arguments or filters. The sidebar's cost hint points to /cost.
In a wide terminal the sidebar shares skills with today's provider/model cost
breakdown. /stats opens immediately at any terminal width using cached totals,
then updates the same window with fresh details. Tool attribution has its own
loading notice until the detailed sample arrives. Up/Down, PgUp/PgDn, and the
wheel scroll; Esc or another non-scroll key closes it. A late completion never
reopens a closed window. Reopened conversation text appears first; its cost
summaries arrive as a batch and show unavailable rather than zero if that read
fails.
The stats view also shows estimated tool request shares for this thread. A request that generates several recorded tool calls is split equally among them, including its input and output tokens. This does not measure the execution cost of a tool, does not include later requests processing its results, and leaves requests without tools unattributed. Shares are not added to spend again.
Cost is computed from the catalog's rates, not from a bill, so it is an estimate
that is exact when the rates are. heph models is the place to check them; the
catalog is a snapshot embedded in the binary.
If an Ollama response omits final usage counts, the status bar shows
tok unknown and cost incomplete instead of claiming actual zero usage.
Native cached-token metrics and nanosecond timings are retained when supplied.
Default local rates are zero API charges, not zero electricity or hardware cost.
Failover
A tier may list several provider/model references for the same model:
[tiers.frontier]
models = ["bedrock/us.anthropic.claude-opus-5", "anthropic/claude-opus-5"]
roles = ["coding", "review"]
The first configured provider that is usable is the one heph talks to. Falling over to the next on a failed request is not implemented yet; today the list picks the first provider you have credentials for.
Choosing
The defaults are a reasonable start: a frontier model for the work you review and a small one for the rest. Reasons to change them:
- Cost. Point
codingat Sonnet for everyday work and keep Opus for a--modeloverride when a problem is hard, or a profile for it. - Provider. You have both an API key and Bedrock; configure both providers and choose per tier.
- GPT. The
openaiandbedrock-openaiproviders carry OpenAI's models, the second under the same AWS credentials asbedrock. A tier is one protocol, so a GPT tier lists GPT providers only; see Providers. - A local or gateway model. A server speaking the Messages or the Responses protocol can be a provider with its own models and rates; see Providers.