@gondor/pi-pace
v1.0.1
Published
Footer stats for local-model pi sessions: tps, tpm, ttft, tool-time share, and idle — because with local inference, time is the cost.
Downloads
74
Readme
pi-pace
Footer stats for local-model pi sessions — how fast is your model actually working.
With local inference the usual cost metrics are all zero; time is the cost. pi-pace turns that into a compact readout in pi's footer:
idle 13s · tools 23% · ttft 1.2s · ↓2.2k tpm · ↓49.0 tpsStats
| Stat | Meaning |
|---|---|
| ↓N tps | Generation speed — live while streaming, then finalized per reply over its full duration and EWMA-smoothed (α=0.6) so short replies don't spike it |
| ↓N tpm | Session output tokens per minute of active time (generation + tools; your thinking/typing time excluded) |
| ttft | Time to first token, last reply |
| tools | Share of active time spent in tool execution; absolute time when < 1%, hidden when < 100 ms |
| idle | Total idle wall time since the session started |
How they're computed
- Active time = generation time + tool execution time. Tool intervals are merged, so parallel tools are not double-counted. Everything else — your reading, typing, the gaps between replies — counts as idle.
- tpm = total session output tokens ÷ active minutes. Because pauses don't count, it's a stable session-level throughput number, not an average that your thinking time deflates.
- tps = per-reply rate (output tokens ÷ full reply duration), blended with the previous value (EWMA, α=0.6). Replies under 50 ms are ignored (the rate would spike).
- ttft = reply start → first streamed token. With local models this reflects prompt processing, so a long ttft is your signal that the prompt/context (or a cold model) is costing you time.
- idle = wall time − active time, since the session's first activity.
Display rules
- Rates (
tpm,tools) are hidden until there's at least 1 s of active time — sub-second numbers are noise. idleis hidden until it's at least 5 s.toolsshows a rounded percentage at ≥ 1%, an absolute duration below that (e.g.tools 830ms), and hides entirely under 100 ms (a "tools 0%" would read as broken).- Values stay static between events (never cleared at
agent_end) and reset only on in-process session transitions (e.g./new,/reload) — so/reloadwipes the session stats, by design.
Install
pi install npm:@gondor/pi-pace # from npm
pi install git:github.com/gondorsolutions/[email protected] # from git (pinned ref)
pi install /path/to/pi-pace # local pathOr try it without installing:
pi -e npm:@gondor/pi-paceManage it like any pi package:
pi list # show installed packages
pi update --extensions # update pi packages
pi remove npm:@gondor/pi-paceNotes
- Additive — uses the
setStatusAPI, so pi's built-in footer line is preserved; pi-pace adds its stats to the dim status line. - Theme-aware — values bright, labels muted,
·separators dim; follows your active theme. - Purely local — no network calls, no telemetry, no runtime dependencies. All metrics come from pi's session events.
- Requirements — pi (peer dependency) and Node ≥ 22.18. The extension is a single TypeScript file, type-stripped natively — no build step.
Development
The whole extension is extensions/pi-pace.ts. It listens to message_start / message_update / message_end, tool_execution_start / tool_execution_end, and session_start, and renders via ctx.ui.setStatus. Footer status keys render alphabetically, so y-tpm and z-tps are named to keep the two rate stats together on the right.
Smoke-test without pi:
node --input-type=module -e "import('/path/to/extensions/pi-pace.ts').then(m => m.default({on: () => {}})).then(() => console.log('ok'))"For a path-installed copy, /reload in pi picks up edits (and resets the session stats — see Display rules).
Publishing
Releases are published from GitHub CI via npm trusted publishing (OIDC — no long-lived tokens): a vX.Y.Z tag matching package.json triggers the publish, and every release carries a provenance attestation.
License
MIT — Copyright (c) 2026 Gondor Solutions SL
