pi-cache-optimizer 2.6.21 → 2.6.22
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/index.ts +128 -29
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -14,6 +14,12 @@ type CacheRetentionEnvSnapshot = {
|
|
|
14
14
|
|
|
15
15
|
const PI_CACHE_RETENTION_ENV = "PI_CACHE_RETENTION";
|
|
16
16
|
const LONG_CACHE_RETENTION_VALUE = "long";
|
|
17
|
+
const PI_CACHE_RETENTION_BASELINE_SYMBOL = Symbol.for("pi.cache.optimizer.retention-baseline.v1");
|
|
18
|
+
|
|
19
|
+
type CacheRetentionBaselineV1 = {
|
|
20
|
+
version: 1;
|
|
21
|
+
snapshot: CacheRetentionEnvSnapshot;
|
|
22
|
+
};
|
|
17
23
|
|
|
18
24
|
function captureCacheRetentionEnv(env: MutableEnv = process.env): CacheRetentionEnvSnapshot {
|
|
19
25
|
return {
|
|
@@ -36,7 +42,28 @@ function restoreCacheRetentionEnv(snapshot: CacheRetentionEnvSnapshot, env: Muta
|
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
function isCacheRetentionBaselineV1(value: unknown): value is CacheRetentionBaselineV1 {
|
|
46
|
+
if (typeof value !== "object" || value === null) return false;
|
|
47
|
+
const record = value as { version?: unknown; snapshot?: unknown };
|
|
48
|
+
if (record.version !== 1 || typeof record.snapshot !== "object" || record.snapshot === null) return false;
|
|
49
|
+
const snapshot = record.snapshot as { wasSet?: unknown; value?: unknown };
|
|
50
|
+
return typeof snapshot.wasSet === "boolean" &&
|
|
51
|
+
(snapshot.value === undefined || typeof snapshot.value === "string");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getOrCaptureCacheRetentionBaseline(
|
|
55
|
+
env: MutableEnv = process.env,
|
|
56
|
+
globals: Record<symbol, unknown> = globalThis as Record<symbol, unknown>,
|
|
57
|
+
): CacheRetentionEnvSnapshot {
|
|
58
|
+
const existing = globals[PI_CACHE_RETENTION_BASELINE_SYMBOL];
|
|
59
|
+
if (isCacheRetentionBaselineV1(existing)) return { ...existing.snapshot };
|
|
60
|
+
|
|
61
|
+
const snapshot = captureCacheRetentionEnv(env);
|
|
62
|
+
globals[PI_CACHE_RETENTION_BASELINE_SYMBOL] = { version: 1, snapshot: { ...snapshot } };
|
|
63
|
+
return snapshot;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const STARTUP_CACHE_RETENTION_ENV = getOrCaptureCacheRetentionBaseline();
|
|
40
67
|
|
|
41
68
|
/**
|
|
42
69
|
* Pi Cache Optimizer (formerly pi-deepseek-cache-optimizer)
|
|
@@ -76,6 +103,7 @@ const NO_SKILL_COMPRESSION_ENV = "PI_CACHE_OPTIMIZER_NO_SKILL_COMPRESSION";
|
|
|
76
103
|
const NO_PROMPT_REWRITE_ENV = "PI_CACHE_OPTIMIZER_NO_PROMPT_REWRITE";
|
|
77
104
|
const PI_ROUTING_REGISTRY_SYMBOL = Symbol.for("pi.routing.registry.v1");
|
|
78
105
|
const PI_CACHE_HINTS_SYMBOL = Symbol.for("pi.cache.hints.v1");
|
|
106
|
+
const PI_CACHE_HINTS_OWNER_SYMBOL = Symbol.for("pi.cache.optimizer.hints-owner.v1");
|
|
79
107
|
|
|
80
108
|
let runtimeOptimizerEnabled = true;
|
|
81
109
|
|
|
@@ -921,16 +949,29 @@ function getCacheHintsService(): PiCacheHintsV1 | undefined {
|
|
|
921
949
|
return isCacheHintsServiceV1(candidate) ? candidate : undefined;
|
|
922
950
|
}
|
|
923
951
|
|
|
924
|
-
function
|
|
952
|
+
function markOptimizerOwnedCacheHintsService(service: PiCacheHintsV1): PiCacheHintsV1 {
|
|
953
|
+
(service as PiCacheHintsV1 & Record<symbol, unknown>)[PI_CACHE_HINTS_OWNER_SYMBOL] = true;
|
|
954
|
+
return service;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function isOptimizerOwnedCacheHintsService(value: unknown): boolean {
|
|
958
|
+
return typeof value === "object" && value !== null &&
|
|
959
|
+
(value as Record<symbol, unknown>)[PI_CACHE_HINTS_OWNER_SYMBOL] === true;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function installCacheHintsService(
|
|
963
|
+
service: PiCacheHintsV1,
|
|
964
|
+
options: { discardPrevious?: (value: unknown) => boolean } = {},
|
|
965
|
+
): () => void {
|
|
925
966
|
const globals = getProtocolGlobal();
|
|
926
967
|
const previous = globals[PI_CACHE_HINTS_SYMBOL];
|
|
927
968
|
globals[PI_CACHE_HINTS_SYMBOL] = service;
|
|
928
969
|
return () => {
|
|
929
970
|
if (globals[PI_CACHE_HINTS_SYMBOL] !== service) return;
|
|
930
|
-
if (previous
|
|
931
|
-
delete globals[PI_CACHE_HINTS_SYMBOL];
|
|
932
|
-
} else {
|
|
971
|
+
if (previous !== undefined && !options.discardPrevious?.(previous)) {
|
|
933
972
|
globals[PI_CACHE_HINTS_SYMBOL] = previous;
|
|
973
|
+
} else {
|
|
974
|
+
delete globals[PI_CACHE_HINTS_SYMBOL];
|
|
934
975
|
}
|
|
935
976
|
};
|
|
936
977
|
}
|
|
@@ -3919,6 +3960,15 @@ function mergeCacheSessions(
|
|
|
3919
3960
|
return sessions;
|
|
3920
3961
|
}
|
|
3921
3962
|
|
|
3963
|
+
function createSerializedAsyncRunner(): <T>(operation: () => Promise<T>) => Promise<T> {
|
|
3964
|
+
let tail: Promise<unknown> = Promise.resolve();
|
|
3965
|
+
return <T>(operation: () => Promise<T>): Promise<T> => {
|
|
3966
|
+
const result = tail.then(operation);
|
|
3967
|
+
tail = result.catch(() => undefined);
|
|
3968
|
+
return result;
|
|
3969
|
+
};
|
|
3970
|
+
}
|
|
3971
|
+
|
|
3922
3972
|
function mergeCacheTotals(
|
|
3923
3973
|
existingTotalsByModel: Record<string, CacheStats>,
|
|
3924
3974
|
stateTotalsByModel: Record<string, CacheStats>,
|
|
@@ -6025,10 +6075,15 @@ export const __internals_for_tests = {
|
|
|
6025
6075
|
isVirtualRoutingModel,
|
|
6026
6076
|
installCacheHintsService,
|
|
6027
6077
|
getCacheHintsService,
|
|
6078
|
+
markOptimizerOwnedCacheHintsService,
|
|
6079
|
+
isOptimizerOwnedCacheHintsService,
|
|
6080
|
+
getOrCaptureCacheRetentionBaseline,
|
|
6081
|
+
PI_CACHE_RETENTION_BASELINE_SYMBOL,
|
|
6028
6082
|
// Persistence helpers (for reload/reset tests)
|
|
6029
6083
|
mergeCacheSessions,
|
|
6030
6084
|
mergeCacheTotals,
|
|
6031
6085
|
mergeLastRoutedModels,
|
|
6086
|
+
createSerializedAsyncRunner,
|
|
6032
6087
|
writePersistedCacheStats,
|
|
6033
6088
|
readPersistedCacheStats,
|
|
6034
6089
|
STATE_FILE_PATH,
|
|
@@ -6078,7 +6133,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
6078
6133
|
let lastStatusText: string | undefined;
|
|
6079
6134
|
let persistenceWarningShown = false;
|
|
6080
6135
|
let persistTimer: ReturnType<typeof setTimeout> | null = null;
|
|
6136
|
+
const enqueuePersist = createSerializedAsyncRunner();
|
|
6081
6137
|
let replacePersistedTotalsOnNextWrite = false;
|
|
6138
|
+
let forceReplaceTotalsAfterFailure = false;
|
|
6082
6139
|
const pendingDeletedTotalModelKeys = new Set<string>();
|
|
6083
6140
|
let integrityNotificationShown = false;
|
|
6084
6141
|
let currentSessionId = "";
|
|
@@ -6100,7 +6157,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6100
6157
|
}
|
|
6101
6158
|
}
|
|
6102
6159
|
|
|
6103
|
-
const uninstallCacheHintsService = installCacheHintsService({
|
|
6160
|
+
const uninstallCacheHintsService = installCacheHintsService(markOptimizerOwnedCacheHintsService({
|
|
6104
6161
|
version: 1,
|
|
6105
6162
|
getHints(input: PiCacheHintsInput): PiCacheHintsOutput | undefined {
|
|
6106
6163
|
if (!runtimeOptimizerEnabled || isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV])) return undefined;
|
|
@@ -6119,8 +6176,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6119
6176
|
cacheRetention: hint.cacheRetention,
|
|
6120
6177
|
};
|
|
6121
6178
|
},
|
|
6122
|
-
});
|
|
6123
|
-
void uninstallCacheHintsService;
|
|
6179
|
+
}), { discardPrevious: isOptimizerOwnedCacheHintsService });
|
|
6124
6180
|
|
|
6125
6181
|
/**
|
|
6126
6182
|
* Build a session-scoped stats key from the current session hash + model key.
|
|
@@ -6243,28 +6299,59 @@ export default function (pi: ExtensionAPI) {
|
|
|
6243
6299
|
lastStatusText = undefined;
|
|
6244
6300
|
}
|
|
6245
6301
|
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
} catch (error) {
|
|
6259
|
-
console.warn(`${LOG_PREFIX}: failed to persist cache stats`, error);
|
|
6260
|
-
if (!persistenceWarningShown) {
|
|
6261
|
-
persistenceWarningShown = true;
|
|
6262
|
-
ctx?.ui.notify(
|
|
6263
|
-
`${LOG_PREFIX}: failed to persist footer stats; using in-memory stats for this process.`,
|
|
6264
|
-
"warning",
|
|
6265
|
-
);
|
|
6266
|
-
}
|
|
6302
|
+
function snapshotCacheStatsState(): CacheStatsState {
|
|
6303
|
+
const state = getCacheStatsState();
|
|
6304
|
+
const statsByModel = Object.fromEntries(
|
|
6305
|
+
Object.entries(state.statsByModel).map(([key, stats]) => [key, { ...stats }]),
|
|
6306
|
+
);
|
|
6307
|
+
const totalsByModel = Object.fromEntries(
|
|
6308
|
+
Object.entries(state.totalsByModel).map(([key, stats]) => [key, { ...stats }]),
|
|
6309
|
+
);
|
|
6310
|
+
const legacyFamily: Partial<Record<CacheProviderId, CacheStats>> = {};
|
|
6311
|
+
for (const id of CACHE_PROVIDER_IDS) {
|
|
6312
|
+
const stats = state.legacyFamily[id];
|
|
6313
|
+
if (stats) legacyFamily[id] = { ...stats };
|
|
6267
6314
|
}
|
|
6315
|
+
const lastRoutedModelBySession = state.lastRoutedModelBySession
|
|
6316
|
+
? Object.fromEntries(
|
|
6317
|
+
Object.entries(state.lastRoutedModelBySession).map(([key, model]) => [key, { ...model }]),
|
|
6318
|
+
)
|
|
6319
|
+
: undefined;
|
|
6320
|
+
return { statsByModel, totalsByModel, legacyFamily, lastRoutedModelBySession };
|
|
6321
|
+
}
|
|
6322
|
+
|
|
6323
|
+
function persistCacheStats(ctx?: ExtensionContext): Promise<void> {
|
|
6324
|
+
const state = snapshotCacheStatsState();
|
|
6325
|
+
const sessionHash = currentSessionHashSet ? currentSessionHash : undefined;
|
|
6326
|
+
const deleteModelKeys = Array.from(pendingDeletedTotalModelKeys);
|
|
6327
|
+
const replaceTotals = replacePersistedTotalsOnNextWrite;
|
|
6328
|
+
// This queued write owns the current intents. Later mutations can enqueue
|
|
6329
|
+
// independent intents without an earlier completion clearing them.
|
|
6330
|
+
pendingDeletedTotalModelKeys.clear();
|
|
6331
|
+
replacePersistedTotalsOnNextWrite = false;
|
|
6332
|
+
|
|
6333
|
+
return enqueuePersist(async () => {
|
|
6334
|
+
const recoverFromEarlierFailure = forceReplaceTotalsAfterFailure;
|
|
6335
|
+
try {
|
|
6336
|
+
await writePersistedCacheStats(state, sessionHash, {
|
|
6337
|
+
deleteModelKeys,
|
|
6338
|
+
replaceTotals: replaceTotals || recoverFromEarlierFailure,
|
|
6339
|
+
});
|
|
6340
|
+
if (recoverFromEarlierFailure) forceReplaceTotalsAfterFailure = false;
|
|
6341
|
+
} catch (error) {
|
|
6342
|
+
// The next queued write must replace totals authoritatively so a failed
|
|
6343
|
+
// reset/delete cannot merge stale persisted counters back into state.
|
|
6344
|
+
forceReplaceTotalsAfterFailure = true;
|
|
6345
|
+
console.warn(`${LOG_PREFIX}: failed to persist cache stats`, error);
|
|
6346
|
+
if (!persistenceWarningShown) {
|
|
6347
|
+
persistenceWarningShown = true;
|
|
6348
|
+
ctx?.ui.notify(
|
|
6349
|
+
`${LOG_PREFIX}: failed to persist footer stats; using in-memory stats for this process.`,
|
|
6350
|
+
"warning",
|
|
6351
|
+
);
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
});
|
|
6268
6355
|
}
|
|
6269
6356
|
|
|
6270
6357
|
/** Schedule a debounced persist. Coalesces rapid message_end writes
|
|
@@ -6554,10 +6641,22 @@ export default function (pi: ExtensionAPI) {
|
|
|
6554
6641
|
}
|
|
6555
6642
|
|
|
6556
6643
|
pi.on("session_start", async (event, ctx) => {
|
|
6644
|
+
if (runtimeOptimizerEnabled) requestLongCacheRetention();
|
|
6557
6645
|
await restoreCacheStats(event.reason, ctx);
|
|
6558
6646
|
await publishStatus(ctx);
|
|
6559
6647
|
});
|
|
6560
6648
|
|
|
6649
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
6650
|
+
try {
|
|
6651
|
+
await flushPersistCacheStats(ctx);
|
|
6652
|
+
} finally {
|
|
6653
|
+
latestCacheHint = undefined;
|
|
6654
|
+
delete getProtocolGlobal().__piCacheOptimizerCacheKey__;
|
|
6655
|
+
uninstallCacheHintsService();
|
|
6656
|
+
restoreCacheRetentionEnv(STARTUP_CACHE_RETENTION_ENV);
|
|
6657
|
+
}
|
|
6658
|
+
});
|
|
6659
|
+
|
|
6561
6660
|
pi.on("model_select", async (event, ctx) => {
|
|
6562
6661
|
if (runtimeOptimizerEnabled) notifyCacheCompatIfNeeded(resolveRouteModel(event.model, ctx) ?? event.model, ctx, warnedModels);
|
|
6563
6662
|
await publishStatus(ctx, event.model);
|
package/package.json
CHANGED