@xinghe-labs/pi-context-stats 1.0.0 → 1.1.0
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 +7 -0
- package/README.md +9 -1
- package/extensions/context-stats.ts +39 -0
- package/package.json +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.1.0] - 2026-10-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **预算/阈值提醒**:本会话花费达 $10 或上下文占用 ≥85% 时 `warning` 提醒一次;压缩后占用警报解除可再次触发
|
|
9
|
+
- **压缩前预警**(`session_before_compact`):显示 reason / 压缩前 tokens / 是否 willRetry —— 这是判断「要不要主动 /compact」的最后时机
|
|
10
|
+
- **turn_end 时序修正**:状态栏改为整个回合结束时刷新一次,消除一个回合多条 assistant 消息时的中间值闪烁
|
|
11
|
+
|
|
5
12
|
## [1.0.0] - 2026-10-27
|
|
6
13
|
|
|
7
14
|
### Added
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ pi 扩展:追踪上下文压缩、prompt 缓存命中与 token 花费。**零依
|
|
|
42
42
|
## 安装
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
pi install npm
|
|
45
|
+
pi install npm:@xinghe-labs/pi-context-stats
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
或从本地路径:
|
|
@@ -51,6 +51,14 @@ pi install npm:pi-context-stats
|
|
|
51
51
|
pi install /path/to/context-stats
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
## 提醒与预警
|
|
55
|
+
|
|
56
|
+
| 时机 | 触发条件 | 行为 |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `message_end` / `turn_end` | 会话花费 ≥ $10 | `warning`:💰 花费达阈值(每会话一次) |
|
|
59
|
+
| `message_end` / `turn_end` | 上下文占用 ≥ 85% | `warning`:🧠 占用阈值,建议提前落盘重要结论(每会话一次;压缩后重置可再触发) |
|
|
60
|
+
| `session_before_compact` | pi 即将压缩 | `info`:📦 准备压缩(reason · tokens · willRetry)|
|
|
61
|
+
|
|
54
62
|
## 统计口径
|
|
55
63
|
|
|
56
64
|
### 计入的用量来源
|
|
@@ -170,6 +170,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
170
170
|
// 压缩原因只存在于 live 事件,条目里没有;写入自定义条目以便 resume 后回填
|
|
171
171
|
const REASON_LOG = "context-stats.compaction";
|
|
172
172
|
|
|
173
|
+
// 预算/阈值提醒:默认 $10 / 上下文 85%,各提醒一次后不再重复(新会话重置)
|
|
174
|
+
const DEFAULT_LIMITS = { cost: 10, contextPercent: 85 };
|
|
175
|
+
let alerted = { cost: false, context: false };
|
|
176
|
+
|
|
177
|
+
function checkThresholds(ctx: ExtensionContext): void {
|
|
178
|
+
if (!ctx.hasUI || (alerted.cost && alerted.context)) return;
|
|
179
|
+
if (!alerted.cost && stats.cost >= DEFAULT_LIMITS.cost) {
|
|
180
|
+
alerted.cost = true;
|
|
181
|
+
ctx.ui.notify(`💰 本会话花费已达 $${stats.cost.toFixed(2)}(阈值 $${DEFAULT_LIMITS.cost})`, "warning");
|
|
182
|
+
}
|
|
183
|
+
const cu = ctx.getContextUsage?.();
|
|
184
|
+
if (!alerted.context && cu && cu.percent !== null && cu.percent >= DEFAULT_LIMITS.contextPercent) {
|
|
185
|
+
alerted.context = true;
|
|
186
|
+
ctx.ui.notify(
|
|
187
|
+
`🧠 上下文占用 ${cu.percent.toFixed(0)}%(阈值 ${DEFAULT_LIMITS.contextPercent}%),可能即将压缩,重要结论建议先落盘到文件`,
|
|
188
|
+
"warning",
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
173
193
|
function newStats(): Stats {
|
|
174
194
|
return {
|
|
175
195
|
compactions: [],
|
|
@@ -249,6 +269,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
249
269
|
|
|
250
270
|
pi.on("session_start", async (_event, ctx) => {
|
|
251
271
|
stats = newStats();
|
|
272
|
+
alerted = { cost: false, context: false };
|
|
252
273
|
const entries = ctx.sessionManager.getBranch();
|
|
253
274
|
// 先用自定义条目建 id → reason 映射(压缩条目本身不记录原因)
|
|
254
275
|
const reasons = new Map<string, string>();
|
|
@@ -287,7 +308,23 @@ export default function (pi: ExtensionAPI) {
|
|
|
287
308
|
const m = event.message as { role?: string; usage?: UsageLike };
|
|
288
309
|
if (m.role !== "assistant") return;
|
|
289
310
|
accumulate(m.usage);
|
|
311
|
+
checkThresholds(ctx);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// turn_end:整个回合结束才刷新状态栏,避免一个回合多条 assistant 消息时中间值闪烁
|
|
315
|
+
pi.on("turn_end", async (_event, ctx) => {
|
|
290
316
|
updateStatus(ctx);
|
|
317
|
+
checkThresholds(ctx);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// 压缩前预警:这是判断「要不要主动 /compact」的最后时机
|
|
321
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
322
|
+
if (!ctx.hasUI) return;
|
|
323
|
+
const tokensBefore = event.preparation?.tokensBefore ?? 0;
|
|
324
|
+
ctx.ui.notify(
|
|
325
|
+
`📦 准备压缩(reason=${event.reason} · 压缩前 ${fmt(tokensBefore)} tokens${event.willRetry ? " · 完成后重试本轮" : ""})`,
|
|
326
|
+
"info",
|
|
327
|
+
);
|
|
291
328
|
});
|
|
292
329
|
|
|
293
330
|
pi.on("session_compact", async (event, ctx) => {
|
|
@@ -308,6 +345,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
308
345
|
// 压缩自身的 LLM 调用走 completeSimple 直调,不触发 message_end,必须在这里记账
|
|
309
346
|
accumulate(entry.usage, "compaction");
|
|
310
347
|
if (entry.id) pi.appendEntry(REASON_LOG, { id: entry.id, reason });
|
|
348
|
+
// 上下文被压缩重置,占用警报解除
|
|
349
|
+
alerted.context = false;
|
|
311
350
|
updateStatus(ctx);
|
|
312
351
|
if (ctx.hasUI) {
|
|
313
352
|
const costText = cost > 0 ? ` · $${cost.toFixed(4)}` : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xinghe-labs/pi-context-stats",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "pi 扩展:状态栏实时显示压缩次数+缓存读+花费,/ctxstats 展示压缩历史、缓存命中率、缓存浪费(miss 计费)与各项费用分解",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -33,5 +33,13 @@
|
|
|
33
33
|
],
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/xinghe-labs/pi-context-stats.git"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/xinghe-labs/pi-context-stats#readme",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/xinghe-labs/pi-context-stats/issues"
|
|
36
44
|
}
|
|
37
45
|
}
|