@monotykamary/pi-tps 1.3.1 → 1.3.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.
|
@@ -175,6 +175,76 @@ describe('pi-tps extension — blended $/M-tokens rate', () => {
|
|
|
175
175
|
expect(banner).toContain('$4.00/M');
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
+
it('corrects the banner + persisted entry when the energy event arrives late', async () => {
|
|
179
|
+
// Provider loads AFTER pi-tps: our turn_end runs first on the list-price
|
|
180
|
+
// fallback, then the provider's turn_end emits the energy event, which
|
|
181
|
+
// must retroactively correct the rate without a second turn_end.
|
|
182
|
+
const message = makeMessageWithCost({
|
|
183
|
+
input: 1000,
|
|
184
|
+
output: 1000,
|
|
185
|
+
costTotal: 0.008, // list price: $4.00/M; billed: $3.00/M
|
|
186
|
+
provider: 'neuralwatt',
|
|
187
|
+
model: 'moonshotai/Kimi-K2.5',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await runBurstTurn(fixture, message, 0);
|
|
191
|
+
|
|
192
|
+
const { notifySpy, appendEntrySpy } = fixture;
|
|
193
|
+
// Initially committed on the list-price fallback.
|
|
194
|
+
expect(appendEntrySpy.mock.calls[0][0]).toBe('tps');
|
|
195
|
+
expect(appendEntrySpy.mock.calls[0][1].rateUsdPerMTokens).toBe(4.0);
|
|
196
|
+
expect(notifySpy.mock.calls[0][0]).toContain('$4.00/M');
|
|
197
|
+
|
|
198
|
+
// Provider's turn_end fires the energy event after ours.
|
|
199
|
+
fixture.emitEvent('neuralwatt:turn-energy', {
|
|
200
|
+
costUsd: 0.006, // $3.00/M for 2000 tokens
|
|
201
|
+
energyJoules: 21.6,
|
|
202
|
+
turnIndex: 0,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// A corrected `tps` entry is appended in place of the list-price one.
|
|
206
|
+
expect(appendEntrySpy.mock.calls).toHaveLength(2);
|
|
207
|
+
expect(appendEntrySpy.mock.calls[1][0]).toBe('tps');
|
|
208
|
+
expect(appendEntrySpy.mock.calls[1][1].rateUsdPerMTokens).toBe(3.0);
|
|
209
|
+
|
|
210
|
+
// Banner is refreshed to the billed rate.
|
|
211
|
+
const lastBanner = notifySpy.mock.calls.at(-1)![0] as string;
|
|
212
|
+
expect(lastBanner).toContain('$3.00/M');
|
|
213
|
+
expect(lastBanner).not.toContain('$4.00/M');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('does not double-correct a turn already billed at commit time', async () => {
|
|
217
|
+
// Provider loads BEFORE pi-tps: the event fires first, our turn_end reads
|
|
218
|
+
// it from the live cache (billedApplied=true), so no late correction.
|
|
219
|
+
const message = makeMessageWithCost({
|
|
220
|
+
input: 1000,
|
|
221
|
+
output: 1000,
|
|
222
|
+
costTotal: 0.008,
|
|
223
|
+
provider: 'neuralwatt',
|
|
224
|
+
model: 'moonshotai/Kimi-K2.5',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
fixture.emitEvent('neuralwatt:turn-energy', {
|
|
228
|
+
costUsd: 0.006,
|
|
229
|
+
energyJoules: 21.6,
|
|
230
|
+
turnIndex: 0,
|
|
231
|
+
});
|
|
232
|
+
await runBurstTurn(fixture, message, 0);
|
|
233
|
+
|
|
234
|
+
// A stray duplicate event must not append a second corrected entry.
|
|
235
|
+
fixture.emitEvent('neuralwatt:turn-energy', {
|
|
236
|
+
costUsd: 0.006,
|
|
237
|
+
energyJoules: 21.6,
|
|
238
|
+
turnIndex: 0,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const { appendEntrySpy, notifySpy } = fixture;
|
|
242
|
+
expect(appendEntrySpy.mock.calls.filter((c) => c[0] === 'tps')).toHaveLength(1);
|
|
243
|
+
expect(notifySpy).toHaveBeenCalledOnce();
|
|
244
|
+
const banner = notifySpy.mock.calls[0][0] as string;
|
|
245
|
+
expect(banner).toContain('$3.00/M');
|
|
246
|
+
});
|
|
247
|
+
|
|
178
248
|
it('ignores neuralwatt:turn-energy payloads lacking a numeric turnIndex', async () => {
|
|
179
249
|
// Defensive: malformed event must not pollute the cache.
|
|
180
250
|
fixture.emitEvent('neuralwatt:turn-energy', { costUsd: 0.006, energyJoules: 21.6 }); // no turnIndex
|
|
@@ -546,6 +546,18 @@ export default function tpsExtension(pi: ExtensionAPI) {
|
|
|
546
546
|
// own session entries. Non-Neuralwatt turns never emit, so this stays empty.
|
|
547
547
|
const neuralwattBilledCostByTurn = new Map<number, number>();
|
|
548
548
|
|
|
549
|
+
// Tracks the most recently committed turn so a late-arriving
|
|
550
|
+
// `neuralwatt:turn-energy` event (provider loaded AFTER us) can retroactively
|
|
551
|
+
// correct a turn that we already persisted with the list-price fallback.
|
|
552
|
+
// Load-order-independent: when the provider loads before us the live cache
|
|
553
|
+
// hits and `billedApplied` is already true, so this never fires.
|
|
554
|
+
let lastCommittedTurn: {
|
|
555
|
+
turnIndex: number;
|
|
556
|
+
telemetry: TurnTelemetry;
|
|
557
|
+
billedApplied: boolean;
|
|
558
|
+
ctx: ExtensionContext;
|
|
559
|
+
} | null = null;
|
|
560
|
+
|
|
549
561
|
pi.events?.on(NEURALWATT_ENERGY_EVENT, (payload: unknown) => {
|
|
550
562
|
if (!payload || typeof payload !== 'object') return;
|
|
551
563
|
const p = payload as Record<string, unknown>;
|
|
@@ -553,6 +565,36 @@ export default function tpsExtension(pi: ExtensionAPI) {
|
|
|
553
565
|
const costUsd = typeof p.costUsd === 'number' ? p.costUsd : null;
|
|
554
566
|
if (turnIndex === null || costUsd === null) return; // require turnIndex correlation
|
|
555
567
|
neuralwattBilledCostByTurn.set(turnIndex, costUsd);
|
|
568
|
+
|
|
569
|
+
// Late-arrival correction: the event reached us after our turn_end already
|
|
570
|
+
// committed this turn on the list-price fallback. Recompute the blended
|
|
571
|
+
// rate from the billed cost, persist a corrected `tps` entry (which later
|
|
572
|
+
// resume/export reads in place of the stale list-price one), and refresh
|
|
573
|
+
// the banner — but only if no newer turn is mid-flight (currentTiming set
|
|
574
|
+
// means a new turn is streaming; updating the banner then would be
|
|
575
|
+
// misleading). Idempotent: once `billedApplied` flips true we never
|
|
576
|
+
// re-correct, so repeated or duplicate events are safe.
|
|
577
|
+
const committed = lastCommittedTurn;
|
|
578
|
+
if (
|
|
579
|
+
committed &&
|
|
580
|
+
!committed.billedApplied &&
|
|
581
|
+
committed.turnIndex === turnIndex &&
|
|
582
|
+
!Number.isNaN(costUsd)
|
|
583
|
+
) {
|
|
584
|
+
const totalTokens = committed.telemetry.tokens.total;
|
|
585
|
+
const correctedRate = computeRateUsdPerM(costUsd, totalTokens);
|
|
586
|
+
if (correctedRate !== null && correctedRate !== committed.telemetry.rateUsdPerMTokens) {
|
|
587
|
+
const corrected = { ...committed.telemetry, rateUsdPerMTokens: correctedRate };
|
|
588
|
+
committed.telemetry = corrected;
|
|
589
|
+
committed.billedApplied = true;
|
|
590
|
+
pi.appendEntry('tps', corrected);
|
|
591
|
+
pi.events?.emit('tps:telemetry', corrected);
|
|
592
|
+
cachedEntries.push({ type: 'custom', customType: 'tps' });
|
|
593
|
+
if (committed.ctx.hasUI && !currentTiming) {
|
|
594
|
+
committed.ctx.ui.notify(composeDisplayString(corrected), 'info');
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
556
598
|
// Bound the cache (oldest = lowest turnIndex)
|
|
557
599
|
if (neuralwattBilledCostByTurn.size > NEURALWATT_ENERGY_CACHE_MAX) {
|
|
558
600
|
let oldest = Infinity;
|
|
@@ -772,6 +814,17 @@ export default function tpsExtension(pi: ExtensionAPI) {
|
|
|
772
814
|
}
|
|
773
815
|
}
|
|
774
816
|
|
|
817
|
+
// Record this turn so a late-arriving neuralwatt:turn-energy event
|
|
818
|
+
// (provider loaded after us) can retroactively correct the rate. Billed
|
|
819
|
+
// cost wins when present, so a turn already billed at commit time is
|
|
820
|
+
// never re-corrected.
|
|
821
|
+
lastCommittedTurn = {
|
|
822
|
+
turnIndex: event.turnIndex,
|
|
823
|
+
telemetry,
|
|
824
|
+
billedApplied: billedCost !== null,
|
|
825
|
+
ctx,
|
|
826
|
+
};
|
|
827
|
+
|
|
775
828
|
// Persist structured telemetry to session for export and rehydration
|
|
776
829
|
pi.appendEntry('tps', telemetry);
|
|
777
830
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monotykamary/pi-tps",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@monotykamary/pi-tps",
|
|
9
|
-
"version": "1.3.
|
|
9
|
+
"version": "1.3.2",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"devDependencies": {
|