@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 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`
@@ -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
- function supportedAdapter(model: ModelLike): SubscriptionProviderAdapter | undefined {
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.lastTokPerSec} tok/s` +
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.lastTokPerSec} tok/s` +
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.33",
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.85.0"
42
+ "@earendil-works/pi-coding-agent": ">=0.80.8 <0.86.0"
43
+ },
44
+ "devDependencies": {
43
45
  }
44
46
  }