@bacnh85/pi-sub 0.1.11 → 0.1.12
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/README.md +19 -8
- package/extensions/index.ts +31 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,25 +34,29 @@ The footer status appears after Pi's built-in status/token usage line and includ
|
|
|
34
34
|
Example subscription line:
|
|
35
35
|
|
|
36
36
|
```text
|
|
37
|
-
(user@example.com) R:15%/2H W:20%/3D
|
|
37
|
+
(user@example.com) R:15%/2H W:20%/3D 42 tok/s
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
### OpenCode Go
|
|
41
41
|
|
|
42
|
-
OpenCode Go does not expose a public usage-window API, so the footer shows the active account/key label
|
|
42
|
+
OpenCode Go does not expose a public usage-window API, so the footer shows the active account/key label, accumulated session cost, and last response speed:
|
|
43
43
|
|
|
44
44
|
```text
|
|
45
|
-
OpenCode Go (OpenCode Go key#1a2b3c4d) $0.23
|
|
45
|
+
OpenCode Go (OpenCode Go key#1a2b3c4d) $0.23 42 tok/s
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
### Z.ai
|
|
49
49
|
|
|
50
|
-
Z.ai (GLM Coding Plan) shows the active account/key label
|
|
50
|
+
Z.ai (GLM Coding Plan) shows the active account/key label, 5-hour rolling and weekly remaining quota with reset countdowns, and last response speed:
|
|
51
51
|
|
|
52
52
|
```text
|
|
53
|
-
(Z.ai key#1a2b3c4d) R:55%/2H W:80%/3D
|
|
53
|
+
(Z.ai key#1a2b3c4d) R:55%/2H W:80%/3D 42 tok/s
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
### Tokens per second
|
|
57
|
+
|
|
58
|
+
`pi-sub` tracks each response's tokens-per-second (tok/s) speed by measuring the time from provider request to message completion against the response's output token count. The last response's speed is shown in the footer next to usage data. The `/sub` detail view shows both the last response speed and the session-wide average.
|
|
59
|
+
|
|
56
60
|
When the current model provider is not supported, `pi-sub` clears its subscription line and does not refresh subscription data.
|
|
57
61
|
|
|
58
62
|
## Commands
|
|
@@ -63,18 +67,23 @@ When the current model provider is not supported, `pi-sub` clears its subscripti
|
|
|
63
67
|
| `/sub status` | Same as `/sub`. |
|
|
64
68
|
| `/sub refresh` | Force a usage refresh, then show details. |
|
|
65
69
|
|
|
66
|
-
When Pi OpenAI Codex auth is available, `/sub` shows the active account usage:
|
|
70
|
+
When Pi OpenAI Codex auth is available, `/sub` shows the active account usage and speed:
|
|
67
71
|
|
|
68
72
|
```text
|
|
73
|
+
Provider: Codex · Model: o4-mini · Fetched: 14:23
|
|
74
|
+
Session cost: $0.12
|
|
75
|
+
Last response: 42 tok/s · Session avg: 39 tok/s
|
|
76
|
+
|
|
69
77
|
ACCOUNT PLAN ROLLING WEEKLY LAST ACTIVITY
|
|
70
78
|
* user@example.com Plus 15%/2H 20%/3D Now
|
|
71
79
|
```
|
|
72
80
|
|
|
73
|
-
For OpenCode Go, `/sub` shows the provider/model, active account/key label,
|
|
81
|
+
For OpenCode Go, `/sub` shows the provider/model, active account/key label, session cost, and speed:
|
|
74
82
|
|
|
75
83
|
```text
|
|
76
84
|
Provider: OpenCode Go · Model: kimi-k2.6 · Fetched: 14:23
|
|
77
85
|
Session cost: $0.23
|
|
86
|
+
Last response: 42 tok/s · Session avg: 39 tok/s
|
|
78
87
|
|
|
79
88
|
ACCOUNT PLAN LAST ACTIVITY
|
|
80
89
|
------------------------------------------------------
|
|
@@ -83,10 +92,12 @@ Session cost: $0.23
|
|
|
83
92
|
OpenCode Go does not expose usage windows.
|
|
84
93
|
```
|
|
85
94
|
|
|
86
|
-
For Z.ai, `/sub` shows the rolling and weekly quota windows:
|
|
95
|
+
For Z.ai, `/sub` shows the rolling and weekly quota windows and speed:
|
|
87
96
|
|
|
88
97
|
```text
|
|
89
98
|
Provider: Z.ai · Model: glm-5.2 · Fetched: 14:23
|
|
99
|
+
Session cost: $0.05
|
|
100
|
+
Last response: 42 tok/s · Session avg: 39 tok/s
|
|
90
101
|
|
|
91
102
|
ACCOUNT PLAN ROLLING WEEKLY LAST ACTIVITY
|
|
92
103
|
------------------------------------------------------------------------
|
package/extensions/index.ts
CHANGED
|
@@ -85,6 +85,10 @@ interface State {
|
|
|
85
85
|
inFlight?: Promise<SubscriptionUsageSnapshot>;
|
|
86
86
|
refreshTimer?: NodeJS.Timeout;
|
|
87
87
|
debounceTimer?: NodeJS.Timeout;
|
|
88
|
+
responseStartTime?: number;
|
|
89
|
+
lastTokPerSec?: number;
|
|
90
|
+
cumulativeOutput: number;
|
|
91
|
+
cumulativeDurationMs: number;
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
function isCodexModel(model: ModelLike): boolean {
|
|
@@ -531,6 +535,7 @@ function renderSubscriptionLine(ctx: ExtensionContext, state: State): void {
|
|
|
531
535
|
const cost = snapshot.cost;
|
|
532
536
|
const hasWindows = windowParts.length > 0;
|
|
533
537
|
if (cost !== undefined && cost > 0) segments.push(`$${cost.toFixed(2)}`);
|
|
538
|
+
if (state.lastTokPerSec !== undefined) segments.push(`${state.lastTokPerSec} tok/s`);
|
|
534
539
|
if (segments.length === 0) {
|
|
535
540
|
line = `Sub ${state.adapter.displayName}`;
|
|
536
541
|
} else if (!hasWindows) {
|
|
@@ -656,7 +661,13 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
656
661
|
});
|
|
657
662
|
|
|
658
663
|
const costLine = snapshot.cost !== undefined ? `\nSession cost: $${snapshot.cost.toFixed(2)}` : "";
|
|
659
|
-
const
|
|
664
|
+
const tokPerSecLine = state.lastTokPerSec !== undefined
|
|
665
|
+
? `\nLast response: ${state.lastTokPerSec} tok/s` +
|
|
666
|
+
(state.cumulativeDurationMs > 0
|
|
667
|
+
? ` · Session avg: ${Math.round(state.cumulativeOutput / (state.cumulativeDurationMs / 1000))} tok/s`
|
|
668
|
+
: "")
|
|
669
|
+
: "";
|
|
670
|
+
const lines = [`Provider: ${snapshot.providerDisplayName} · Model: ${state.model?.id ?? "unknown-model"} · Fetched: ${new Date(snapshot.fetchedAt).toLocaleTimeString()}${costLine}${tokPerSecLine}`, "", header, sep, ...body];
|
|
660
671
|
if (!hasFiveHour && !hasWeekly) {
|
|
661
672
|
lines.push("", `${snapshot.providerDisplayName} does not expose usage windows.`);
|
|
662
673
|
}
|
|
@@ -664,7 +675,7 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
664
675
|
}
|
|
665
676
|
|
|
666
677
|
export default function (pi: ExtensionAPI) {
|
|
667
|
-
const state: State = { lastRefreshAt: 0, refreshGeneration: 0 };
|
|
678
|
+
const state: State = { lastRefreshAt: 0, refreshGeneration: 0, cumulativeOutput: 0, cumulativeDurationMs: 0 };
|
|
668
679
|
|
|
669
680
|
pi.on("session_start", async (_event, ctx) => {
|
|
670
681
|
updateActiveAdapter(ctx, state, ctx.model);
|
|
@@ -676,6 +687,24 @@ export default function (pi: ExtensionAPI) {
|
|
|
676
687
|
if (state.adapter) void refreshUsage(ctx, state, true);
|
|
677
688
|
});
|
|
678
689
|
|
|
690
|
+
pi.on("before_provider_request", async (_event, _ctx) => {
|
|
691
|
+
state.responseStartTime = Date.now();
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
pi.on("message_end", async (event, ctx) => {
|
|
695
|
+
if (event.message.role === "assistant" && state.responseStartTime) {
|
|
696
|
+
const output = (event.message.usage as any)?.output ?? 0;
|
|
697
|
+
const elapsed = Date.now() - state.responseStartTime;
|
|
698
|
+
state.responseStartTime = undefined;
|
|
699
|
+
if (elapsed > 0 && output > 0) {
|
|
700
|
+
state.lastTokPerSec = Math.round(output / (elapsed / 1000));
|
|
701
|
+
state.cumulativeOutput += output;
|
|
702
|
+
state.cumulativeDurationMs += elapsed;
|
|
703
|
+
if (state.adapter) renderSubscriptionLine(ctx, state);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
679
708
|
pi.on("after_provider_response", async (_event, ctx) => {
|
|
680
709
|
if (state.adapter) scheduleRefresh(ctx, state);
|
|
681
710
|
});
|