pi-autothink
Pi extension: let a classifier model pick the thinking level for every prompt, so mechanical prompts stay cheap and hard ones get real reasoning — without switching models.
Package details
Install pi-autothink from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-autothink- Package
pi-autothink- Version
0.1.4- Published
- Nov 6, 2026
- Downloads
- 785/mo · 785/wk
- Author
- loofare
- License
- MIT
- Types
- extension
- Size
- 41.8 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions/auto-thinking.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-autothink
English · 中文说明
A Pi extension that decides how hard the model should think before every turn.
Typing
fix this typoandredesign the auth layerno longer cost the same reasoning budget: a small classifier model scores the request, and Pi runs that turn at the matching thinking level.Same model. Same context. Same prompt cache. Only the thinking level moves.
you type a message
│
▼
┌─────────────────────────┐ score 0–5
│ classifier (Jev) │ ─────────────────┐
│ "how deep is this?" │ │
└─────────────────────────┘ ▼
┌──────────────────┐
│ thinking level │
│ minimal … max │
└────────┬─────────┘
┌───────────────────────────────────┘
▼
clamp to what your model supports → run the turn → log the decision to the session
Quick start
pi install npm:pi-autothink # or: pi install git:github.com/loofare/pi-autothink
Then restart Pi (or /reload) and check it is live:
/autothink status
It is on by default. Turn it off with /autothink off or PI_AUTO_THINKING=0.
Set up JEV (5 minutes)
JEV is hosted — there is nothing to install and no local model to download. It is a classifier that answers one question about a piece of text; Pi already knows how to call it. All that is missing is one credential.
This plugin never stores that credential. It asks Pi, and Pi keeps keys in ~/.pi/agent/auth.json. Log in once and every Pi extension can use it — the plugin depends on Pi for auth, on purpose.
1. Get a key
Sign up at console.typesafe.ai and create an API key.
2. Hand it to Pi
Inside a Pi session:
/login typesafe
Paste the key. Pi writes it to ~/.pi/agent/auth.json (mode 600) and handles the rest.
3. Check what the plugin found
/autothink status
A healthy install says:
auto thinking: on
classifier: typesafe/jev-latest
classifier source: built-in candidate list
level bounds: none
current level: medium
config: none (defaults)
last decision: low (jev 1.06/5)
If classifier: says none — keyword fallback, nothing is connected yet: run /autothink setup, which re-checks and shows the exact command for each option.
4. Nothing to configure after that
It runs on every prompt. Tune it later with the optional config file below.
No TypeSafe account? Five other routes
Every one of these ends up in the same place: Pi holds the credential, the plugin finds it.
| Route | Command | Notes |
|---|---|---|
| OpenRouter | /login openrouter |
One login covers Jev and Clef. Many people already have an account |
| Cloudflare Workers AI | export CLOUDFLARE_API_KEY=... + CLOUDFLARE_ACCOUNT_ID |
Has a free allowance; see their pricing |
| Vercel AI Gateway | export AI_GATEWAY_API_KEY=... |
Jev through the gateway |
| OpenCode | export OPENCODE_API_KEY=... |
Jev, text only |
| OpenAI | export OPENAI_API_KEY=... |
GPT-6 Luna via the Decisions API; a ChatGPT login is not enough |
| Nothing | — | Keyword fallback. Coarse, but it never blocks a turn |
Troubleshooting
| Symptom | What to do |
|---|---|
classifier: none — keyword fallback |
No credential found. /autothink setup lists what to connect |
| You just logged in but it still says none | Run /reload, or restart Pi, then /autothink setup again |
classifier source: config (…) but the model is wrong |
Your config.json pins a model; delete that line or fix the id |
| Decisions feel too cheap for your work | "minLevel": "low" (or medium) — the floor always applies |
| Decisions feel expensive on a big repo | "maxLevel": "high" |
| You want to watch it decide | "debug": true in the config file, or PI_AUTO_THINKING_DEBUG=1 |
Optional config file
No config needed. When you want one:
- user:
~/.pi/agent/pi-autothink/config.json - project:
<project>/.pi/pi-autothink/config.json(wins over the user file)
{
"minLevel": "low",
"maxLevel": "high",
"timeoutMs": 3000,
"model": "typesafe/jev-latest",
"enabled": true,
"debug": false,
"maxPromptChars": 6000
}
| Key | What it does |
|---|---|
minLevel / maxLevel |
Your own floor and ceiling. "minLevel": "low" keeps the classifier from ever dropping below low; "maxLevel": "high" caps cost on a big repo |
model |
Force a specific classifier, provider/modelId — same as PI_AUTO_THINKING_MODEL |
timeoutMs |
Classifier timeout, default 4000 |
maxPromptChars |
How much of the prompt is sent, default 6000 |
enabled |
Start enabled or not |
debug |
Print each decision to stderr |
Precedence: environment variable → project config → user config → built-in default. A bad value (typo, wrong type, minLevel above maxLevel) is ignored with a warning, never fatal.
Why this exists
Pi stores the thinking level next to the model selection, so it stays wherever you left it. That leaves two habits, both bad:
| Habit | What it costs |
|---|---|
Leave it at max forever |
Typo fixes and one-line lookups pay architecture-grade reasoning: slower, more expensive, no better |
| Switch it by hand per message | The switching itself eats attention, and you get it wrong at the worst moment |
Thinking tokens are billed as output. This extension puts one cheap classifier call in front of each turn so the level follows the work instead of your mood.
Requirements
| Requirement | Detail |
|---|---|
| Pi with classifier support | ctx.modelRegistry.classify() — Pi 1.4.0 or newer |
| A classifier credential | Any one of: TYPESAFE_API_KEY (Jev, tried first), OPENROUTER_API_KEY / /login, CLOUDFLARE_API_KEY + CLOUDFLARE_ACCOUNT_ID, AI_GATEWAY_API_KEY, OPENCODE_API_KEY, OPENAI_API_KEY (GPT-6 Luna) |
| A model with thinking levels | Without one, Pi clamps everything to off: harmless, pointless |
Missing the credential? It still works. No classifier, an error, or a 4-second timeout falls back to a local keyword heuristic instead of blocking the turn.
How it decides
The classifier gets one scored question — how much internal reasoning does completing this request need? Judge the work itself, not its tone or length — and answers on this scale:
| Score | The work looks like | Level |
|---|---|---|
| 0 | rename a variable, format a file, run a known command | minimal |
| 1 | one small edit, a lookup | low |
| 2 | an ordinary feature, fix, or explanation | medium |
| 3 | multi-file change, non-obvious debugging, a design decision | high |
| 4 | architecture, root cause, security, migration | xhigh |
| 5 | subtle high-risk change across a large system | max |
Two details that matter:
- Score 0 →
minimal, neveroff. Deciding not to think at all stays your call, not the classifier's. - The result is clamped to your model. The requested level is pulled to the nearest level the active model actually supports, so an unsupported level never reaches the provider.
Request text is clipped to 6000 characters. Timeout is 4000 ms.
Controls
| Control | Effect |
|---|---|
/autothink setup |
Guided connect: finds a classifier, or tells you the one command to run |
/autothink on · /autothink off |
Toggle during a session |
/autothink status |
On/off, which classifier, where the setting came from, bounds, last decision |
PI_AUTO_THINKING=0 |
Disable for one process |
pi --auto-thinking |
Force on for one process |
PI_AUTO_THINKING_MODEL=provider/modelId |
Use another classifier, e.g. openai/gpt-6-luna |
PI_AUTO_THINKING_DEBUG=1 |
Print each decision to stderr |
What it does not do
Being explicit about the edges is cheaper than debugging them later:
| It does not… | Why |
|---|---|
| change your model | Only the thinking level moves, so the prompt cache identity survives |
| re-decide mid-turn | One decision per user prompt; tool loops inside that turn keep the level |
| guarantee a correct score | It reads the sentence you wrote, not what the turn will turn out to need |
| think for you when unsure | On failure it falls back to keywords, and always lets the turn run |
| claim a measured saving | It reports a level distribution, not a benchmark |
| load in foreground subagents | Detached (async) subagents do load it and decide for themselves; async: false ones run in-process without it and keep their configured level |
Audit trail
Every decision lands in the session as an auto-thinking custom entry — outside the LLM context, with no prompt text, credentials, or user content:
{"requested":"medium","applied":"low","clamped":true,"source":"jev",
"classifier":"typesafe/jev-latest","model":"some/model","detail":"jev 2.31/5"}
requested is what the classifier said, applied is what actually ran, and clamped says whether the model could not honour it. Retries and recoveries reuse the previous decision instead of paying for another classifier call.
Install variants
pi install npm:pi-autothink # npm
pi install git:github.com/loofare/pi-autothink # git
pi install ./pi-autothink # local checkout
pi -e /path/to/pi-autothink # try once, don't add to settings
License
MIT