The configuration surface
The file is~/.pi/agent/models.json, and providers are keyed by a name you
choose:
~/.pi/agent/models.json
/v1. Its examples carry the version prefix on the
baseUrl, so include it.
The file is re-read every time you open /model, so you can edit it during a
session without restarting.
api picks the wire, and picks it per provider
api takes openai-completions, openai-responses, anthropic-messages or
google-generative-ai, and it can be set on the provider or overridden on a
single model. openai-completions is chat completions, which is what this site
is mostly about. openai-responses is the other surface this API serves and
Codex CLI documents. The other two leave the surface
this site publishes, so nothing on these pages tells you what comes back.
Getting this wrong is not a soft failure. A provider speaks one protocol per
entry, so a mistake here sends a differently shaped request to a real endpoint
rather than producing a configuration error.
The key, and the two forms that keep it out of the file
apiKey resolves three ways, and only one of them puts the secret in the file.
"$HOPSCOTCH_API_KEY"reads an environment variable. Interpolation works inside a longer string, and a missing variable leaves the value unresolved rather than empty."!command"runs the whole value as a command and uses its output, which is the form for a password manager or a keychain.- Anything else is a literal, so a plain uppercase word is the key itself and not a variable name.
models.json lives under your home directory rather than in your repository,
which is a smaller exposure than several harnesses here, but the environment
form costs nothing and is the one to use.
Pi resolves a !command at request time and applies no caching or retry of its
own, on the stated grounds that it cannot know the right policy for an arbitrary
command. Wrap a slow or rate-limited helper in your own script.
You can also skip apiKey entirely and store the credential with /login, in
~/.pi/agent/auth.json, or pass --api-key. Models with no auth configured
anywhere still load, but stay unselectable in /model and --list-models,
which is the shape that failure takes.
List the models yourself
A provider entry inmodels.json has no discovery step, so the models Pi offers
are the ones you write down, and each id is a pinned id. Adding one means
editing the file.
Ask GET /v1/models for the strings your key can call.
unirouter/auto is the entry worth having in a file nobody edits twice.
If you would rather not maintain the list by hand, Pi’s extension API can
register a provider from an async factory that fetches a catalog at startup,
which is more machinery than most people want and is the documented way to do
it.
Set maxTokens, because here you can
This is the setting the shared page asks for and most harnesses do not have.
maxTokens on a model is the output ceiling, and Pi applies its own default
when you leave it out. A request that names a realistic ceiling holds less
against your account spend rate cap and is charged exactly what it would have
been anyway, because the charge has always been for what was actually produced.
Set it, and set contextWindow from the same catalog entry so Pi’s own context
accounting is not guessing. Rate limits and spend
controls covers the cap.
cost is Pi’s arithmetic, not ours
A model entry takes a cost block of per-million-token rates, with optional
tiers. Pi uses it to show you what a session cost. It has no effect on what you
are charged, and nothing reconciles it against this platform.
Leave it at zero and read Activity, or fill it in from
the catalog and treat the number Pi prints as an estimate that will drift the
moment a routing profile sends a turn to a different model.
Models covers where a charge’s prices actually come from.
When a request shape needs correcting
Pi has acompat block, on the provider or on a single model, for endpoints
that are OpenAI-compatible in most respects. Two of its switches are the ones
that come up first anywhere: supportsDeveloperRole, which decides whether the
system prompt travels as developer or as system, and maxTokensField, which
picks between max_completion_tokens and max_tokens.
We have not run Pi here, so we cannot tell you that you need either of them.
What we can tell you is where to look if a reasoning model fails while an
ordinary one works, which is the signature of the first switch.
No embeddings feature found
Pi’s documented tool surface reads files, writes them, edits them and runs your shell, and its provider documentation names no embedding model and no index of your repository built by meaning. We did not audit its source to confirm the absence. If some feature of yours fails against this API, no embeddings endpoint is the first thing to check. Subagents are an optional companion package rather than part of the core agent. If you install one, every child it runs is its own conversation against the same key and its own rows in Activity.What we did not verify
- That a Pi session against this API succeeds. Nothing was run.
- Whether a model
idfrommodels.jsonis sent as themodelfield byte for byte. It is what the shape requires, and a mismatch would show as a400withmodel_id_not_pinnedor a404withmodel_not_found, neither of which costs anything. - Which
compatswitches, if any, this API needs. None is set in the block above, on the grounds that guessing at one is worse than leaving it out. - What Pi’s own default output cap is on the wire when
maxTokensis omitted, or whether it sends one at all. - Anything about prompt caching from Pi. Its
compatvocabulary includes cache and session-affinity controls that are provider-specific, and we did not establish which of them apply here.