tests — session-pruning
tests — session-pruning
This document describes the Session Pruning module, located at src/session-pruning/index.js and src/session-pruning/pruning-manager.ts. This module provides a robust and configurable mechanism for automatically managing and reducing the size of session-related data based on defined rules.
The primary goal of this module is to prevent unbounded growth of session data, ensuring efficient resource usage and maintaining system performance by proactively identifying and acting upon items that meet specific criteria (e.g., age, size, type).
Core Concepts
The session pruning module revolves around a few key concepts:
PrunableItem: Any piece of data that can be subject to pruning. It must have anid,sessionId,type,createdAttimestamp, and metrics likesizeBytesandtokens.PruningRule: Defines the criteria for pruning and the action to take. Rules consist ofconditions(what makes an item prunable) and anaction(what to do with it). Rules have apriorityto determine application order.PruningManager: The central class responsible for managing items, rules, configuration, and executing the pruning process. It can be configured globally or on a per-session basis.
PruningManager Overview
The PruningManager is the orchestrator of the session pruning process. It maintains a collection of PrunableItems and PruningRules, and periodically (or on demand) evaluates items against these rules.
Instantiation and Lifecycle
You can create a PruningManager instance directly or use the provided singleton accessors.
import { PruningManager, getPruningManager, resetPruningManager } from 39;../../src/session-pruning/index.js39;;
class="hl-cmt">// Direct instantiation
const manager = new PruningManager({
enabled: true,
rules: [],
checkIntervalMs: 5000, class="hl-cmt">// Check every 5 seconds
minPruneIntervalMs: 1000, class="hl-cmt">// Don39;t prune more often than every 1 second
dryRun: false,
});
class="hl-cmt">// Singleton access
const singletonManager = getPruningManager();
class="hl-cmt">// Resetting the singleton (primarily for testing)
resetPruningManager();
The manager can be stopped to halt any background pruning checks:
manager.stop();
Architecture Diagram
classDiagram
direction LR
class PruningManager {
+PruningManager(config)
+addItem(item: PrunableItem)
+addRule(rule: PruningRule)
+prune(options?): Promise<PruningResult>
+on(event, handler)
+getStats(): PruningStats
+stop()
+registerEvaluator(name, fn)
}
class PrunableItem {
+id: string
+sessionId: string
+type: string
+createdAt: Date
+sizeBytes: number
+tokens: number
+content?: string
+metadata?: object
}
class PruningRule {
+id: string
+name: string
+priority: number
+enabled: boolean
+conditions: PruningCondition[]
+action: PruningAction
}
class PruningResult {
+success: boolean
+prunedItems: PrunedItemInfo[]
+skippedItems: PrunedItemInfo[]
+stats: PruningStats
}
PruningManager "1" -- "*" PrunableItem : manages
PruningManager "1" -- "*" PruningRule : applies
PruningManager "1" -- "1" PruningResult : returns
Configuration
The PruningManager supports both global and session-specific configurations.
Global Configuration
The global configuration dictates the overall behavior of the pruning process.
import { DEFAULT_PRUNING_CONFIG } from 39;../../src/session-pruning/index.js39;;
class="hl-cmt">// Get current config
const config = manager.getConfig();
class="hl-cmt">// Update config
manager.updateConfig({
enabled: false, class="hl-cmt">// Disable pruning globally
dryRun: true, class="hl-cmt">// Perform dry runs only
});
class="hl-cmt">// Default configuration is used if not provided
const defaultManager = new PruningManager();
expect(defaultManager.getConfig().enabled).toBe(DEFAULT_PRUNING_CONFIG.enabled);
Session-Specific Configuration
Individual sessions can have their own pruning settings, such as being exempt from pruning.
class="hl-cmt">// Exempt 39;session-139; from pruning
manager.setSessionConfig(39;session-139;, { exempt: true });
class="hl-cmt">// Remove session-specific config
manager.removeSessionConfig(39;session-139;);
Item Management
The PruningManager acts as a repository for PrunableItems.
import { type PrunableItem } from 39;../../src/session-pruning/index.js39;;
class="hl-cmt">// Helper to create a test item
function createItem(overrides: Partial<PrunableItem> = {}): PrunableItem {
return {
id: `item-${Math.random().toString(36).slice(2)}`,
sessionId: 39;session-139;,
type: 39;message39;,
createdAt: new Date(),
sizeBytes: 100,
tokens: 50,
...overrides,
};
}
const item1 = createItem({ sessionId: 39;session-139; });
const item2 = createItem({ sessionId: 39;session-239; });
class="hl-cmt">// Add items
manager.addItem(item1);
manager.addItem(item2);
class="hl-cmt">// Retrieve items
const retrievedItem = manager.getItem(item1.id);
const allItems = manager.getAllItems();
const session1Items = manager.getSessionItems(39;session-139;);
class="hl-cmt">// Remove items
manager.removeItem(item1.id); class="hl-cmt">// Removes a specific item
manager.clearItems(); class="hl-cmt">// Removes all items
Items that are archived by a rule are moved to a separate collection:
const archivedItems = manager.getArchivedItems();
Pruning Rules
Pruning rules define what to prune and how. Each rule has an id, name, priority, enabled status, conditions, and an action.
import { type PruningRule } from 39;../../src/session-pruning/index.js39;;
const myRule: PruningRule = {
id: 39;my-custom-rule39;,
name: 39;Delete old messages39;,
priority: 100, class="hl-cmt">// Higher priority rules are evaluated first
enabled: true,
conditions: [
{ type: 39;age39;, maxAgeMs: 24 * 60 * 60 * 1000 }, class="hl-cmt">// Older than 24 hours
{ type: 39;type39;, messageTypes: [39;message39;], include: true }, class="hl-cmt">// Only messages
],
action: { type: 39;delete39; },
};
class="hl-cmt">// Add a rule
manager.addRule(myRule);
class="hl-cmt">// Update a rule (by ID)
manager.addRule({ ...myRule, priority: 200 });
class="hl-cmt">// Enable/disable a rule
manager.setRuleEnabled(39;my-custom-rule39;, false);
class="hl-cmt">// Remove a rule
manager.removeRule(39;my-custom-rule39;);
Pruning Conditions
Conditions determine if an item should be pruned. Multiple conditions in a rule are typically combined with an AND logic.
age: Prunes items older thanmaxAgeMs.
{ type: 39;age39;, maxAgeMs: 1000 * 60 * 60 * 24 } class="hl-cmt">// Older than 24 hours
size: Prunes items if theirsizeBytesexceedsmaxBytes.
{ type: 39;size39;, maxBytes: 1024 * 1024 } class="hl-cmt">// Larger than 1MB
tokens: Prunes items if theirtokenscount exceedsmaxTokens.
{ type: 39;tokens39;, maxTokens: 500 } class="hl-cmt">// More than 500 tokens
type: Prunes items based on theirtypeproperty.include: true: Prunes items whosetypeis inmessageTypes.include: false: Prunes items whosetypeis not inmessageTypes.
{ type: 39;type39;, messageTypes: [39;checkpoint39;, 39;system39;], include: true }
{ type: 39;type39;, messageTypes: [39;user_message39;], include: false }
custom: Allows registering and using custom evaluation functions.
class="hl-cmt">// Register a custom evaluator
manager.registerEvaluator(39;isImportant39;, (item: PrunableItem) => {
return item.metadata?.important === true;
});
class="hl-cmt">// Use it in a rule
const customRule: PruningRule = {
id: 39;custom-important-rule39;,
name: 39;Prune important items39;,
priority: 10,
enabled: true,
conditions: [{ type: 39;custom39;, fn: 39;isImportant39; }],
action: { type: 39;delete39; },
};
manager.addRule(customRule);
Pruning Actions
Actions define what happens to an item once it meets a rule's conditions.
delete: Permanently removes the item from the manager.
{ type: 39;delete39; }
archive: Removes the item from the active set and moves it to an internal archive. Adestinationcan be specified for metadata.
{ type: 39;archive39;, destination: 39;long-term-storage39; }
summarize: Modifies the item'scontentto a shorter, summarized version, aiming fortargetTokens. This action implies an external summarization service or logic.
{ type: 39;summarize39;, targetTokens: 50 }
compact: Reduces the item'ssizeBytesandtokensby aratio. This action implies an external compaction service or logic.
{ type: 39;compact39;, ratio: 0.5 } class="hl-cmt">// Reduce size/tokens by 50%
Rule Priority
Rules are applied in order of their priority (higher number first). If an item is pruned by a higher-priority rule, lower-priority rules for that item are skipped.
const lowPriorityRule: PruningRule = { /* ... action: archive */ priority: 10 };
const highPriorityRule: PruningRule = { /* ... action: delete */ priority: 100 };
manager.addRule(lowPriorityRule);
manager.addRule(highPriorityRule);
class="hl-cmt">// If an item matches both, the 39;delete39; action (high priority) will be applied.
Executing Pruning
Pruning can be triggered manually or automatically by the manager's internal timer (if checkIntervalMs is set and enabled is true).
import { type PruningResult } from 39;../../src/session-pruning/index.js39;;
class="hl-cmt">// Trigger pruning manually
const result: PruningResult = await manager.prune();
if (result.success) {
console.log(`Pruned ${result.prunedItems.length} items.`);
console.log(`Freed ${result.stats.freedBytes} bytes and ${result.stats.freedTokens} tokens.`);
} else {
console.error(39;Pruning failed:39;, result.error);
}
Dry Run Mode
When dryRun is enabled in the configuration, the prune() method will identify items that would be pruned but will not modify the actual item store. The PruningResult will still contain prunedItems with a reason indicating it was a dry run.
manager.updateConfig({ dryRun: true });
const result = await manager.prune();
class="hl-cmt">// result.prunedItems will contain items, but manager.getAllItems() will be unchanged.
Session Exemption
Sessions can be marked as exempt from pruning via setSessionConfig. Items belonging to exempt sessions will be skipped unless prune() is called with force: true.
manager.setSessionConfig(39;session-139;, { exempt: true });
class="hl-cmt">// Items in 39;session-139; will be skipped
await manager.prune();
class="hl-cmt">// Items in 39;session-139; will be pruned despite exemption
await manager.prune({ force: true });
Events
The PruningManager extends EventEmitter and emits various events during the pruning process, allowing external components to react to its lifecycle.
manager.on(39;start39;, () => {
console.log(39;Pruning process started.39;);
});
manager.on(39;progress39;, (progress: { scanned: number; total: number }) => {
console.log(`Pruning progress: ${progress.scanned}/${progress.total} items scanned.`);
});
manager.on(39;item-pruned39;, (itemInfo: PruningResult[39;prunedItems39;][0]) => {
console.log(`Item ${itemInfo.item.id} pruned by rule ${itemInfo.ruleId} with action ${itemInfo.action}.`);
});
manager.on(39;complete39;, (result: PruningResult) => {
console.log(39;Pruning process complete.39;, result);
});
Statistics
The manager provides real-time statistics about the items it manages and detailed statistics about each pruning run.
Global Statistics
const stats = manager.getStats();
console.log(39;Global Stats:39;, {
totalItems: stats.globalStats.totalItems,
totalBytes: stats.globalStats.totalBytes,
totalTokens: stats.globalStats.totalTokens,
totalSessions: stats.globalStats.totalSessions,
});
Pruning Run Statistics
The PruningResult object returned by prune() includes statistics specific to that run.
const result = await manager.prune();
console.log(39;Pruning Run Stats:39;, {
scannedCount: result.stats.scannedCount,
prunedCount: result.stats.prunedCount,
skippedCount: result.stats.skippedCount,
freedBytes: result.stats.freedBytes,
freedTokens: result.stats.freedTokens,
durationMs: result.stats.durationMs,
});