@yalieny/pi-better-cost-display-footer 2.1.1 → 2.1.2
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 +16 -2
- package/extensions/pi-better-cost-display-footer.ts +197 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
pi 扩展:脚本化多段计价动态列表 + footer 增强。
|
|
4
4
|
|
|
5
|
-
- **多段动态计价**:每个 provider 一个 `tierFn` 脚本函数(JS 表达式字符串)判定当前档位,**档位是任意字符串 id**(`peak` / `flat` / `valley` / `sharp`…),不绑定峰谷语义;在 `session_start
|
|
5
|
+
- **多段动态计价**:每个 provider 一个 `tierFn` 脚本函数(JS 表达式字符串)判定当前档位,**档位是任意字符串 id**(`peak` / `flat` / `valley` / `sharp`…),不绑定峰谷语义;在 `session_start`、每次发送消息、每次 LLM 调用前重注册 provider 的 `cost`(仅影响 pi 本地用量统计)。
|
|
6
|
+
- **计费自愈**:每轮比对期望 cost(配置 × 当前档位)与实际参与计费的 `ctx.model.cost`,不一致则重注册纠正;连续 3 次未收敛则停止重试并在 footer 显式告警(金额前 `≈`、标签加 `?`),不再静默错价.
|
|
6
7
|
- **档位标签**:价格后紧跟当前档位标签(如 `¥0.047(梁文峰)`),按档位 id 配置文案与颜色;未配置标签的档位不显示。
|
|
7
8
|
- **CH 精度**:footer 缓存命中率小数位可配(默认 1 位)。
|
|
8
9
|
- **货币符号**:按 provider 配置计费金额符号(默认 `$`)。
|
|
@@ -85,10 +86,19 @@ pi install npm:@yalieny/pi-better-cost-display-footer
|
|
|
85
86
|
|
|
86
87
|
该命令把抓取官方价目页、解析价格、更新配置文件的完整任务交给 agent 执行(自然语言指令,无需手动维护价格表)。命令会告知 agent 配置文件位置与 JSON 格式;agent 抓取 [DeepSeek 官方价目页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing) 后更新 `providers.deepseek.models` 下各模型的分档价格,其余字段不动。
|
|
87
88
|
|
|
89
|
+
## 计价诊断
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
/cost-tier
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
只读打印当前模型的档位状态:档位、期望费率 vs 实际计费费率、是否漂移、tierFn 编译状态、注册守卫与重试预算、最近一次注册结果。扩展的 `console.error` 在 TUI 下不落盘,排查"标签与金额不符"时用这条命令而不是猜。
|
|
96
|
+
|
|
88
97
|
## 测试
|
|
89
98
|
|
|
90
99
|
```bash
|
|
91
|
-
npm install && npm test
|
|
100
|
+
npm install && npm test # 纯函数单测(档位判定 / 漂移判定)
|
|
101
|
+
node --experimental-strip-types test/drift-probe.ts # 端到端:守卫重试与自愈(用系统临时目录隔离配置)
|
|
92
102
|
```
|
|
93
103
|
|
|
94
104
|
## 说明
|
|
@@ -97,3 +107,7 @@ npm install && npm test
|
|
|
97
107
|
- provider 必须在 `models.json` 中有定义;未列出的模型保持原价。
|
|
98
108
|
- 模型缺少当前档位价格时保持原价并打日志。
|
|
99
109
|
- cost 仅影响 pi 本地用量统计,不影响 API 实际账单。
|
|
110
|
+
|
|
111
|
+
## 变更记录
|
|
112
|
+
|
|
113
|
+
- **2.1.2** 修复"档位标签正确但计费金额按错档结算":注册守卫改为按实际计费 cost 判定漂移(不再只看档位字符串),注册失败不再写入守卫(下一轮自动重试);漂移 3 次未收敛转为 footer 可见告警(`≈` + 标签 `?`);新增 `/cost-tier` 诊断命令与注册失败一次性提示。方案见 `docs/fix-plan-stale-cost-tier.md`。
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
// 的每次调用)按各 provider 的 tierFn 脚本函数计算当前档位并重注册其 cost,对齐按调用时刻计费。
|
|
8
8
|
// effectiveFrom(含)之前不做任何事。
|
|
9
9
|
//
|
|
10
|
+
// 守卫按“状态”而非“档位字符串”判定:每轮比对期望 cost(配置 × 当前档位)与实际 cost
|
|
11
|
+
// (ctx.model.cost,真正参与计费的对象)。不一致 → 重注册当前 provider 自愈(最多 3 次),
|
|
12
|
+
// 重试用尽仍不符 → footer 显示 ≈ + 标签加 ? 显式告警。注册失败不写守卫,下一轮重试。
|
|
13
|
+
// /cost-tier 命令只读打印上述状态。
|
|
14
|
+
//
|
|
10
15
|
// 档位 = 任意字符串 id(如 "peak" / "flat" / "valley" / "sharp"),不绑定峰值/谷值语义:
|
|
11
16
|
// {
|
|
12
17
|
// "timezone": "Asia/Shanghai", // 计费时区,默认 Asia/Shanghai
|
|
@@ -169,7 +174,16 @@ const DEFAULT_CONFIG: DynamicConfig = {
|
|
|
169
174
|
},
|
|
170
175
|
};
|
|
171
176
|
|
|
172
|
-
|
|
177
|
+
/** 注册守卫:只在全部注册成功时写入,失败保持旧值 → 下一轮 key 不等自动重试 */
|
|
178
|
+
let appliedKey: string | null = null;
|
|
179
|
+
/** 同一漂移状态的连续重注册次数,超 DRIFT_RETRY_LIMIT 后只告警不再重试 */
|
|
180
|
+
let driftRetries = 0;
|
|
181
|
+
/** 最近一次提醒的失败文案,变化时才弹,避免每轮抖动 */
|
|
182
|
+
let lastErrorText: string | null = null;
|
|
183
|
+
/** 最近一次注册结果,供 /cost-tier 诊断 */
|
|
184
|
+
let lastRegisterResult = "尚未注册";
|
|
185
|
+
/** 当前模型计费 cost 与档位不符,footer 降级显示(≈ + 标签加 ?) */
|
|
186
|
+
let footerDrift = false;
|
|
173
187
|
/** 最近一次事件的 ctx,footer 闭包读取;随 input/model_select 事件刷新 */
|
|
174
188
|
let activeCtx: ExtensionContext | null = null;
|
|
175
189
|
/** 当前生效配置,apply() 时刷新,footer 每帧直接读,避免频繁读盘 */
|
|
@@ -177,6 +191,17 @@ let currentCfg: DynamicConfig | null = null;
|
|
|
177
191
|
/** 当前档位标签(null = 不显示),apply() 时刷新 */
|
|
178
192
|
let footerLabel: FooterLabel | null = null;
|
|
179
193
|
|
|
194
|
+
/** ponytail: 漂移重试预算固定 3 次,不做指数退避;真出现抖动再把预算做成配置项 */
|
|
195
|
+
const DRIFT_RETRY_LIMIT = 3;
|
|
196
|
+
|
|
197
|
+
/** 一次性提示:TUI 下扩展的 console.error 不落盘也不可见,错误必须走 ui.notify */
|
|
198
|
+
function notifyOnce(ctx: ExtensionContext, text: string): void {
|
|
199
|
+
if (text === lastErrorText) return;
|
|
200
|
+
lastErrorText = text;
|
|
201
|
+
if (ctx.mode === "tui") ctx.ui.notify(text, "warning");
|
|
202
|
+
else console.error(text);
|
|
203
|
+
}
|
|
204
|
+
|
|
180
205
|
/** 深合并:文件配置覆盖内置默认(全局 + provider + model 三级),部分配置文件也能生效 */
|
|
181
206
|
function mergeConfig(base: DynamicConfig, override: DynamicConfig): DynamicConfig {
|
|
182
207
|
const merged: DynamicConfig = { ...base, ...override };
|
|
@@ -443,23 +468,98 @@ function dimSkip(text: string, marker: string, theme: Theme): string {
|
|
|
443
468
|
);
|
|
444
469
|
}
|
|
445
470
|
|
|
471
|
+
/** 本插件视角下的模型对象子集(pi 的 Model 结构兼容,cost 只看四段扁平价) */
|
|
472
|
+
interface ModelLike {
|
|
473
|
+
provider: string;
|
|
474
|
+
id: string;
|
|
475
|
+
cost?: TierRates;
|
|
476
|
+
}
|
|
477
|
+
|
|
446
478
|
/** 读取当前模型的生效 provider 配置;未配置 / 未生效 → null。 */
|
|
447
479
|
function getActiveModelPricing(
|
|
448
480
|
cfg: DynamicConfig | null,
|
|
449
|
-
|
|
481
|
+
model: ModelLike | undefined,
|
|
482
|
+
date: Date = new Date(),
|
|
450
483
|
): ProviderPricing | null {
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
if (!model || !provider || !provider.models[model.id] || !isEffective(provider)) return null;
|
|
484
|
+
const provider = model ? cfg?.providers?.[model.provider] : undefined;
|
|
485
|
+
if (!model || !provider || !provider.models[model.id] || !isEffective(provider, date)) return null;
|
|
454
486
|
return provider;
|
|
455
487
|
}
|
|
456
488
|
|
|
457
|
-
/**
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
489
|
+
/**
|
|
490
|
+
* 费率比较:浮点相对容差 1e-9,避免 JSON round-trip 造成误判漂移。
|
|
491
|
+
* ponytail: 只比四段扁平价。pi 的 ModelCost 还有请求级 tiers(本插件不产出该字段),
|
|
492
|
+
* 若将来支持分档价,这里要按 tier 逐段比较。
|
|
493
|
+
*/
|
|
494
|
+
export function sameRates(a: TierRates, b: TierRates | undefined): boolean {
|
|
495
|
+
if (!b) return false;
|
|
496
|
+
return (["input", "output", "cacheRead", "cacheWrite"] as const).every((k) => {
|
|
497
|
+
const want = a[k];
|
|
498
|
+
const have = b[k] as number | undefined;
|
|
499
|
+
if (typeof have !== "number") return false;
|
|
500
|
+
return Math.abs(want - have) <= 1e-9 * Math.max(Math.abs(want), Math.abs(have), 1);
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/** 计费与档位不符:期望 = 配置 × 当前档位,实际 = 会话模型对象的 cost(真正参与计费的那个) */
|
|
505
|
+
interface CostDrift {
|
|
506
|
+
providerId: string;
|
|
507
|
+
tier: Tier;
|
|
508
|
+
want: TierRates;
|
|
509
|
+
have: TierRates;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/** 当前模型的档位状态(一次 currentTier 判定,标签 / 漂移 / 诊断共用) */
|
|
513
|
+
interface PricingState {
|
|
514
|
+
/** 生效的 provider 配置;该模型不归本插件管 → null */
|
|
515
|
+
provider: ProviderPricing | null;
|
|
516
|
+
/** 期望档位;同上 → null */
|
|
517
|
+
tier: Tier | null;
|
|
518
|
+
/** 期望费率;该模型该档位未配价 → null(保持原价,不算漂移) */
|
|
519
|
+
rates: TierRates | null;
|
|
520
|
+
/** 实际计费 cost 与期望费率不符 → 详情 */
|
|
521
|
+
drift: CostDrift | null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function pricingState(
|
|
525
|
+
cfg: DynamicConfig | null,
|
|
526
|
+
model: ModelLike | undefined,
|
|
527
|
+
date: Date = new Date(),
|
|
528
|
+
): PricingState {
|
|
529
|
+
const provider = getActiveModelPricing(cfg, model, date);
|
|
530
|
+
if (!provider || !model) return { provider: null, tier: null, rates: null, drift: null };
|
|
531
|
+
const tier = currentTier(provider, date);
|
|
532
|
+
const want = provider.models[model.id]?.[tier];
|
|
533
|
+
const have = model.cost;
|
|
534
|
+
const drift = want && have && !sameRates(want, have)
|
|
535
|
+
? { providerId: model.provider, tier, want, have }
|
|
536
|
+
: null;
|
|
537
|
+
return { provider, tier, rates: want ?? null, drift };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** 当前模型应有的费率(provider 未配置 / 未生效 / 该模型或该档位缺价 → null) */
|
|
541
|
+
export function expectedRatesForModel(
|
|
542
|
+
cfg: DynamicConfig | null,
|
|
543
|
+
model: ModelLike | undefined,
|
|
544
|
+
date: Date = new Date(),
|
|
545
|
+
): { tier: Tier; rates: TierRates } | null {
|
|
546
|
+
const st = pricingState(cfg, model, date);
|
|
547
|
+
return st.tier && st.rates ? { tier: st.tier, rates: st.rates } : null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/** 漂移判定:有期望费率且与实际 cost 不等 → 详情;一致或不归本插件管 → null */
|
|
551
|
+
export function detectCostDrift(
|
|
552
|
+
cfg: DynamicConfig | null,
|
|
553
|
+
model: ModelLike | undefined,
|
|
554
|
+
date: Date = new Date(),
|
|
555
|
+
): CostDrift | null {
|
|
556
|
+
return pricingState(cfg, model, date).drift;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** 档位标签;当前模型无动态计价配置 / 该档位未配置标签 → null */
|
|
560
|
+
function computeFooterLabel(st: PricingState): FooterLabel | null {
|
|
561
|
+
if (!st.provider || !st.tier) return null;
|
|
562
|
+
return st.provider.labels?.[st.tier] ?? DEFAULT_LABELS[st.tier] ?? null;
|
|
463
563
|
}
|
|
464
564
|
|
|
465
565
|
function installCustomFooter(ctx: ExtensionContext): void {
|
|
@@ -525,12 +625,21 @@ function installCustomFooter(ctx: ExtensionContext): void {
|
|
|
525
625
|
// 订阅制 provider 无法从扩展读取 modelRuntime,退化为内置的特例
|
|
526
626
|
const usingSubscription = c?.model?.provider === "kimi-coding";
|
|
527
627
|
// 未配置模型只改 CH;金额符号和档位标签保持内置默认
|
|
528
|
-
const customPricing = getActiveModelPricing(currentCfg, c);
|
|
628
|
+
const customPricing = getActiveModelPricing(currentCfg, c?.model);
|
|
529
629
|
const labelInfo = customPricing ? footerLabel : null;
|
|
530
|
-
|
|
630
|
+
// 漂移时标签转 warning 并加问号,金额前缀 ≈:宁可显示“可疑”,不静默显示错数字
|
|
631
|
+
const drift = customPricing ? footerDrift : false;
|
|
632
|
+
const labelText = labelInfo
|
|
633
|
+
? drift
|
|
634
|
+
? labelInfo.text.endsWith(")")
|
|
635
|
+
? `${labelInfo.text.slice(0, -1)}?)`
|
|
636
|
+
: `${labelInfo.text}?`
|
|
637
|
+
: labelInfo.text
|
|
638
|
+
: "";
|
|
639
|
+
const labelStr = labelInfo ? colorize(theme, drift ? "warning" : labelInfo.color, labelText) : "";
|
|
531
640
|
if (totals.cost || usingSubscription) {
|
|
532
641
|
const symbol = customPricing?.currencySymbol ?? "$";
|
|
533
|
-
const costStr = `${symbol}${totals.cost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`;
|
|
642
|
+
const costStr = `${drift ? "≈" : ""}${symbol}${totals.cost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`;
|
|
534
643
|
statsParts.push(costStr + labelStr);
|
|
535
644
|
}
|
|
536
645
|
statsParts.push(contextPercentStr);
|
|
@@ -596,29 +705,61 @@ function apply(pi: ExtensionAPI, ctx: ExtensionContext): void {
|
|
|
596
705
|
activeCtx = ctx;
|
|
597
706
|
const cfg = loadDynamicConfig();
|
|
598
707
|
currentCfg = cfg;
|
|
599
|
-
|
|
600
|
-
|
|
708
|
+
if (!cfg) {
|
|
709
|
+
footerLabel = null;
|
|
710
|
+
footerDrift = false;
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
// 核心:以“实际计费 cost”为准,而不是以“档位字符串有没有变”为准
|
|
714
|
+
// 注:不能就地把费率写回 ctx.model.cost —— pi 的 model 对象被 Object.freeze(实测
|
|
715
|
+
// "Cannot assign to read only property"),且事件 ctx 无 setModel(只读 model)。
|
|
716
|
+
// 写进“参与计费的那个对象”的唯一通道就是 registerProvider → _refreshCurrentModelFromRegistry()。
|
|
717
|
+
const state = pricingState(cfg, ctx.model);
|
|
718
|
+
footerLabel = computeFooterLabel(state);
|
|
719
|
+
footerDrift = state.drift !== null;
|
|
720
|
+
const drift = state.drift;
|
|
601
721
|
const providerTiers = Object.entries(cfg.providers || {}).map(([providerId, provider]) => {
|
|
602
722
|
const tier = isEffective(provider) ? currentTier(provider) : null;
|
|
603
723
|
return [providerId, tier] as const;
|
|
604
724
|
});
|
|
605
725
|
const key = `${JSON.stringify(cfg)}|${JSON.stringify(providerTiers)}`;
|
|
606
|
-
|
|
607
|
-
//
|
|
726
|
+
const keyChanged = key !== appliedKey;
|
|
727
|
+
// 预算复位:配置/档位变了(新状态)或已一致(收敛)
|
|
728
|
+
if (keyChanged || !drift) driftRetries = 0;
|
|
729
|
+
if (!keyChanged) {
|
|
730
|
+
if (!drift) return; // 正常路径:档位未变且计费一致 → 零开销
|
|
731
|
+
// 同一漂移状态重试用尽 → 不再每轮抖,转由 footer 告警
|
|
732
|
+
if (driftRetries >= DRIFT_RETRY_LIMIT) return;
|
|
733
|
+
}
|
|
734
|
+
// 只统计“漂移驱动”的重试:key 变化那轮本就要全量重注册,不该占预算
|
|
735
|
+
if (!keyChanged && drift) driftRetries++;
|
|
736
|
+
|
|
737
|
+
let allOk = true;
|
|
738
|
+
const touched: string[] = [];
|
|
608
739
|
for (const [providerId, tier] of providerTiers) {
|
|
609
740
|
if (!tier) continue;
|
|
741
|
+
// key 变了:全部重注册(与旧版一致);只有漂移:只重注册当前模型的 provider
|
|
742
|
+
if (!keyChanged && providerId !== drift?.providerId) continue;
|
|
610
743
|
try {
|
|
611
744
|
const config = patchProviderConfig(cfg, providerId, tier);
|
|
745
|
+
// provider 不在 models.json 是永久配置态,不是瞬时失败,不阻塞守卫
|
|
612
746
|
if (!config) continue;
|
|
613
747
|
pi.registerProvider(providerId, config);
|
|
748
|
+
touched.push(`${providerId} → ${tier}`);
|
|
614
749
|
console.log(`[pi-better-cost-display-footer] ${providerId} → ${tier}`);
|
|
615
750
|
} catch (err) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
751
|
+
allOk = false;
|
|
752
|
+
const msg = `${providerId} 注册失败: ${err instanceof Error ? err.message : String(err)}`;
|
|
753
|
+
lastRegisterResult = msg;
|
|
754
|
+
notifyOnce(ctx, `[pi-better-cost-display-footer] ${msg}`);
|
|
619
755
|
}
|
|
620
756
|
}
|
|
621
|
-
|
|
757
|
+
if (touched.length) lastRegisterResult = `${touched.join(", ")} @ 成功`;
|
|
758
|
+
// 失败不写守卫 → 下一轮自动重试(修 A);恢复后允许同类故障再次告警
|
|
759
|
+
if (allOk) {
|
|
760
|
+
appliedKey = key;
|
|
761
|
+
lastErrorText = null; // 恢复后允许同类故障再次告警
|
|
762
|
+
}
|
|
622
763
|
}
|
|
623
764
|
|
|
624
765
|
export default function (pi: ExtensionAPI): void {
|
|
@@ -662,6 +803,38 @@ export default function (pi: ExtensionAPI): void {
|
|
|
662
803
|
);
|
|
663
804
|
},
|
|
664
805
|
});
|
|
806
|
+
// 只读诊断。本次排障最大成本是“扩展 stderr 无落盘 + 状态不可观测”,命令把它们摊开
|
|
807
|
+
pi.registerCommand("cost-tier", {
|
|
808
|
+
description: "打印当前模型的计价档位诊断:期望费率 vs 实际计费费率、是否漂移、最近注册结果",
|
|
809
|
+
handler: async (_args, ctx) => {
|
|
810
|
+
const cfg = currentCfg ?? loadDynamicConfig();
|
|
811
|
+
const m = ctx.model;
|
|
812
|
+
const st = pricingState(cfg, m);
|
|
813
|
+
const fmt = (r: TierRates | null | undefined) =>
|
|
814
|
+
r ? `${r.input}/${r.output}/${r.cacheRead}/${r.cacheWrite}` : "—";
|
|
815
|
+
const fnSrc = m ? cfg?.providers?.[m.provider]?.tierFn : undefined;
|
|
816
|
+
const fnStatus = !fnSrc
|
|
817
|
+
? "未配置(走 peakWindows / defaultTier)"
|
|
818
|
+
: tierFnCache.get(fnSrc) === null
|
|
819
|
+
? "编译失败 → 回退 defaultTier"
|
|
820
|
+
: "OK";
|
|
821
|
+
const lines = [
|
|
822
|
+
`模型: ${m ? `${m.provider}/${m.id}` : "无"}`,
|
|
823
|
+
`配置: ${existsSync(CONFIG_FILE) ? CONFIG_FILE : `${CONFIG_FILE}(不存在,用内置默认)`}`,
|
|
824
|
+
`档位: ${st.tier ?? "不归本插件管(未配置 / 未生效 / 该模型无价)"}`,
|
|
825
|
+
`期望费率 in/out/cR/cW: ${fmt(st.rates)}`,
|
|
826
|
+
`实际 cost in/out/cR/cW: ${fmt(m?.cost)}`,
|
|
827
|
+
`漂移: ${st.drift ? "是(计费与档位不符)" : m?.cost ? "否" : "—"}`,
|
|
828
|
+
`tierFn: ${fnStatus}`,
|
|
829
|
+
`守卫: ${appliedKey ? "已置位(配置与档位未变则不重注册)" : "未置位(下一轮重注册)"}`,
|
|
830
|
+
`漂移重试: ${driftRetries}/${DRIFT_RETRY_LIMIT}`,
|
|
831
|
+
`最近注册: ${lastRegisterResult}`,
|
|
832
|
+
];
|
|
833
|
+
const text = `[pi-better-cost-display-footer]\n${lines.join("\n")}`;
|
|
834
|
+
if (ctx.mode === "tui") ctx.ui.notify(text, "info");
|
|
835
|
+
else console.log(text);
|
|
836
|
+
},
|
|
837
|
+
});
|
|
665
838
|
pi.on("session_start", (_e, ctx) => {
|
|
666
839
|
installCustomFooter(ctx);
|
|
667
840
|
apply(pi, ctx);
|
|
@@ -673,7 +846,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
673
846
|
pi.on("input", (_e, ctx) => apply(pi, ctx));
|
|
674
847
|
// 切换模型时刷新档位标签(深色/深绿 ⇄ 其他模型)
|
|
675
848
|
pi.on("model_select", (_e, ctx) => apply(pi, ctx));
|
|
676
|
-
// 每个 LLM
|
|
677
|
-
//
|
|
849
|
+
// 每个 LLM 调用(含工具循环内的每次调用)开始前重算档位,对齐按调用点计费;
|
|
850
|
+
// 守卫 = 配置/档位 key 未变 且 实际 cost 与期望一致 → O(1) 返回,保证正常路径零开销。
|
|
678
851
|
pi.on("turn_start", (_e, ctx) => apply(pi, ctx));
|
|
679
852
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yalieny/pi-better-cost-display-footer",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "pi extension: scriptable peak/off-peak tier rules per provider with an enhanced footer (tier label, cache-hit-rate precision, custom currency symbol)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|