@bacnh85/pi-sub 0.1.33 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +8 -0
- package/extensions/index.ts +35 -3
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.36 (2026-09-07)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Removed the incorrect dead `tok-per-sec` module** (added in 0.1.35, never
|
|
8
|
+
wired in): its `withThinking` formula summed `reasoning` on top of `output`,
|
|
9
|
+
but Pi's `usage.output` already includes reasoning tokens (`reasoning` is a
|
|
10
|
+
subset), which would have inflated thinking-mode tok/s up to ~2×. The live
|
|
11
|
+
footer/footer tok/s math (`output / elapsed`) was and remains correct in both
|
|
12
|
+
thinking and normal mode.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`/sub` thinking/answer split** — when the model reasoned, the details line
|
|
17
|
+
now reads e.g. `Last response: 46 tok/s (36 think + 10 answer)` instead of a
|
|
18
|
+
bare total, using the correct subset math (`answer = output − reasoning`).
|
|
19
|
+
Footer keeps the single total number.
|
|
20
|
+
|
|
21
|
+
## 0.1.35 (2026-09-05)
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`zai-anthropic` provider usage tracking** — GLM through the Anthropic-compatible endpoint (`api.z.ai/api/anthropic`, registered by pi-model-tools) now shows the same quota footer as `zai`/`zai-coding-cn`: 5-hour/weekly windows, MCP monthly allowance, and model/tool breakdowns, keyed by the auth.json `zai-anthropic` credential, labeled `Z.ai (Anthropic)`.
|
|
26
|
+
|
|
27
|
+
## 0.1.34 (2026-09-05)
|
|
28
|
+
|
|
29
|
+
- Widen Pi SDK peer range to `>=0.80.8 <0.86.0` for Pi 0.85.0 compatibility (no breaking changes; peer cap widening only).
|
|
30
|
+
|
|
31
|
+
## 0.1.33 (2026-08-29)
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- `/sub` argument completion offers `refresh`.
|
|
36
|
+
|
|
1
37
|
## 0.1.32 (2026-08-22)
|
|
2
38
|
|
|
3
39
|
- **DeepSeek via OmniRoute now shows the real USD balance** (e.g. `M:$18.25`)
|
package/README.md
CHANGED
|
@@ -51,6 +51,14 @@ The built-in `zai-coding-cn` provider targets the domestic BigModel endpoint (`o
|
|
|
51
51
|
(Z.ai (CN) key#1a2b3c4d) R:55%/2H W:80%/3D 42 tok/s
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
### Z.ai via Anthropic endpoint
|
|
55
|
+
|
|
56
|
+
The `zai-anthropic` provider (registered by pi-model-tools, GLM through `api.z.ai/api/anthropic`) is tracked the same way — same api.z.ai quota monitor as the international `zai` provider, keyed by the auth.json `zai-anthropic` credential, labeled `Z.ai (Anthropic)`:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
(Z.ai (Anthropic) key#1a2b3c4d) R:55%/2H W:80%/3D 42 tok/s
|
|
60
|
+
```
|
|
61
|
+
|
|
54
62
|
### Router (pi-router — formerly 9router)
|
|
55
63
|
|
|
56
64
|
For **OmniRoute** instances the footer shows real usage: `GET <origin>/api/usage/om-usage`
|
package/extensions/index.ts
CHANGED
|
@@ -38,6 +38,13 @@ const ZAI_PROVIDER = "zai";
|
|
|
38
38
|
const ZAI_USAGE_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
39
39
|
const ZAI_CODING_CN_PROVIDER = "zai-coding-cn";
|
|
40
40
|
const ZAI_CODING_CN_USAGE_URL = "https://open.bigmodel.cn/api/monitor/usage/quota/limit";
|
|
41
|
+
// GLM via the Anthropic-compatible endpoint (pi-model-tools `zai-anthropic`
|
|
42
|
+
// provider). Same api.z.ai host and quota monitor as the `zai` provider.
|
|
43
|
+
// ponytail: usage URL is fixed to api.z.ai — if ZAI_ANTHROPIC_BASE_URL is
|
|
44
|
+
// overridden to BigModel/zcode-plan, quota still reads from api.z.ai (correct
|
|
45
|
+
// for the z.ai coding-plan key; BigModel-plan keys should use zai-coding-cn).
|
|
46
|
+
const ZAI_ANTHROPIC_PROVIDER = "zai-anthropic";
|
|
47
|
+
const ZAI_ANTHROPIC_USAGE_URL = ZAI_USAGE_URL;
|
|
41
48
|
const ROUTER_PROVIDER = "router";
|
|
42
49
|
const LEGACY_9ROUTER_PROVIDER = "9router";
|
|
43
50
|
// pi-router (formerly pi-9router): URL lives in settings.json `router.baseUrl`
|
|
@@ -120,6 +127,7 @@ interface State {
|
|
|
120
127
|
debounceTimer?: NodeJS.Timeout;
|
|
121
128
|
responseStartTime?: number;
|
|
122
129
|
lastTokPerSec?: number;
|
|
130
|
+
lastTokPerSecLabel?: string;
|
|
123
131
|
cumulativeOutput: number;
|
|
124
132
|
cumulativeDurationMs: number;
|
|
125
133
|
cumulativeCost: number;
|
|
@@ -142,6 +150,10 @@ function isZaiCodingCnModel(model: ModelLike): boolean {
|
|
|
142
150
|
return (model?.provider?.toLowerCase() ?? "") === ZAI_CODING_CN_PROVIDER;
|
|
143
151
|
}
|
|
144
152
|
|
|
153
|
+
function isZaiAnthropicModel(model: ModelLike): boolean {
|
|
154
|
+
return (model?.provider?.toLowerCase() ?? "") === ZAI_ANTHROPIC_PROVIDER;
|
|
155
|
+
}
|
|
156
|
+
|
|
145
157
|
function isRouterModel(model: ModelLike, provider: string = ROUTER_PROVIDER): boolean {
|
|
146
158
|
return (model?.provider?.toLowerCase() ?? "") === provider;
|
|
147
159
|
}
|
|
@@ -451,6 +463,9 @@ if (process.env.PI_SUB_SELF_CHECK === "1") {
|
|
|
451
463
|
assert(p.personalWeekly?.remaining === 90, "personal weekly 90");
|
|
452
464
|
assert(p.session?.remaining === 47, "session 47");
|
|
453
465
|
assert(p.providerWeekly?.remaining === 28, "provider weekly 28");
|
|
466
|
+
// tok/s split label: usage.reasoning ⊂ usage.output, never summed.
|
|
467
|
+
assert(tokPerSecLabel(3200, 2500, 70_000) === "46 tok/s (36 think + 10 answer)", "tok/s split label");
|
|
468
|
+
assert(tokPerSecLabel(200, 0, 10_000) === "20 tok/s", "tok/s plain label");
|
|
454
469
|
assert(p.personalDaily?.resetLabel?.includes("15h") === true, "daily reset label");
|
|
455
470
|
const disabled = parseOmniUsageText("Usage command is disabled for this API key.");
|
|
456
471
|
assert(Object.keys(disabled).length === 0, "disabled text parses empty");
|
|
@@ -991,11 +1006,14 @@ function zaiUsageAdapter(providerId: string, usageUrl: string, displayName: stri
|
|
|
991
1006
|
return { fetchUsage };
|
|
992
1007
|
}
|
|
993
1008
|
|
|
994
|
-
|
|
1009
|
+
// Exported for the adapter-wiring regression test (provider-id string ↔
|
|
1010
|
+
// adapter id ↔ usage URL are exactly what a typo silently breaks).
|
|
1011
|
+
export function supportedAdapter(model: ModelLike): SubscriptionProviderAdapter | undefined {
|
|
995
1012
|
if (isCodexModel(model)) return { id: CODEX_PROVIDER, displayName: "Codex", fetchUsage: fetchCodexUsage };
|
|
996
1013
|
if (isOpenCodeGoModel(model)) return { id: OPC_PROVIDER, displayName: "OpenCode Go", fetchUsage: fetchOpenCodeGoUsage };
|
|
997
1014
|
if (isZaiModel(model)) return { id: ZAI_PROVIDER, displayName: "Z.ai", ...zaiUsageAdapter(ZAI_PROVIDER, ZAI_USAGE_URL, "Z.ai") };
|
|
998
1015
|
if (isZaiCodingCnModel(model)) return { id: ZAI_CODING_CN_PROVIDER, displayName: "Z.ai (CN)", ...zaiUsageAdapter(ZAI_CODING_CN_PROVIDER, ZAI_CODING_CN_USAGE_URL, "Z.ai (CN)") };
|
|
1016
|
+
if (isZaiAnthropicModel(model)) return { id: ZAI_ANTHROPIC_PROVIDER, displayName: "Z.ai (Anthropic)", ...zaiUsageAdapter(ZAI_ANTHROPIC_PROVIDER, ZAI_ANTHROPIC_USAGE_URL, "Z.ai (Anthropic)") };
|
|
999
1017
|
if (isRouterModel(model)) {
|
|
1000
1018
|
const prefix = routerUpstreamPrefix(model);
|
|
1001
1019
|
return {
|
|
@@ -1179,11 +1197,21 @@ function pad(value: string, width: number): string {
|
|
|
1179
1197
|
return value.length >= width ? value : value + " ".repeat(width - value.length);
|
|
1180
1198
|
}
|
|
1181
1199
|
|
|
1200
|
+
/** "46 tok/s (36 think + 10 answer)" — split shown only when the model
|
|
1201
|
+
* reasoned. usage.reasoning is a subset of usage.output (Pi SDK contract),
|
|
1202
|
+
* so answer speed = (output − reasoning)/s, never output + reasoning. */
|
|
1203
|
+
function tokPerSecLabel(output: number, thinking: number, elapsedMs: number): string {
|
|
1204
|
+
const total = Math.round(output / (elapsedMs / 1000));
|
|
1205
|
+
if (thinking <= 0) return `${total} tok/s`;
|
|
1206
|
+
const secs = elapsedMs / 1000;
|
|
1207
|
+
return `${total} tok/s (${Math.round(thinking / secs)} think + ${Math.round((output - thinking) / secs)} answer)`;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1182
1210
|
function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: State): string {
|
|
1183
1211
|
if (!state.adapter) {
|
|
1184
1212
|
const header = `Provider: ${state.model?.provider ?? "unknown"}${state.model?.id ? ` · Model: ${state.model.id}` : ""}`;
|
|
1185
1213
|
if (state.lastTokPerSec === undefined) return `${header}\nSubscription tracking inactive for this provider.`;
|
|
1186
|
-
const tokPerSecLine = `Last response: ${state.
|
|
1214
|
+
const tokPerSecLine = `Last response: ${state.lastTokPerSecLabel}` +
|
|
1187
1215
|
(state.cumulativeDurationMs > 0
|
|
1188
1216
|
? ` · Session avg: ${Math.round(state.cumulativeOutput / (state.cumulativeDurationMs / 1000))} tok/s`
|
|
1189
1217
|
: "");
|
|
@@ -1226,7 +1254,7 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
1226
1254
|
|
|
1227
1255
|
const costLine = state.cumulativeCost > 0 ? `\nSession cost: $${state.cumulativeCost.toFixed(2)}` : "";
|
|
1228
1256
|
const tokPerSecLine = state.lastTokPerSec !== undefined
|
|
1229
|
-
? `\nLast response: ${state.
|
|
1257
|
+
? `\nLast response: ${state.lastTokPerSecLabel}` +
|
|
1230
1258
|
(state.cumulativeDurationMs > 0
|
|
1231
1259
|
? ` · Session avg: ${Math.round(state.cumulativeOutput / (state.cumulativeDurationMs / 1000))} tok/s`
|
|
1232
1260
|
: "")
|
|
@@ -1263,11 +1291,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
1263
1291
|
if (event.message.role === "assistant") {
|
|
1264
1292
|
state.cumulativeCost += (event.message.usage as any)?.cost?.total ?? 0;
|
|
1265
1293
|
if (state.responseStartTime) {
|
|
1294
|
+
// usage.output already includes reasoning tokens (Pi SDK contract) —
|
|
1295
|
+
// this is total tok/s in both thinking and normal mode.
|
|
1266
1296
|
const output = (event.message.usage as any)?.output ?? 0;
|
|
1297
|
+
const reasoning = (event.message.usage as any)?.reasoning ?? 0;
|
|
1267
1298
|
const elapsed = Date.now() - state.responseStartTime;
|
|
1268
1299
|
state.responseStartTime = undefined;
|
|
1269
1300
|
if (elapsed > 0 && output > 0) {
|
|
1270
1301
|
state.lastTokPerSec = Math.round(output / (elapsed / 1000));
|
|
1302
|
+
state.lastTokPerSecLabel = tokPerSecLabel(output, reasoning, elapsed);
|
|
1271
1303
|
state.cumulativeOutput += output;
|
|
1272
1304
|
state.cumulativeDurationMs += elapsed;
|
|
1273
1305
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-sub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Pi extension showing subscription usage for supported model providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
"node": ">=20.3.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@earendil-works/pi-coding-agent": ">=0.80.8 <0.
|
|
42
|
+
"@earendil-works/pi-coding-agent": ">=0.80.8 <0.86.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
43
45
|
}
|
|
44
46
|
}
|