UNPKG

crewai-ts

Version:

TypeScript port of crewAI for agent-based workflows

282 lines (281 loc) 9.26 kB
/** * Memory retention policies implementation * Provides flexible control over memory retention with various strategies */ /** * Time-based retention policy * Retains items based on their age (creation or access time) */ export class TimeBasedRetentionPolicy { maxAgeMs; timeField; name; constructor(options) { this.maxAgeMs = options.maxAgeMs; this.timeField = options.timeField ?? 'createdAt'; this.name = options.name; } shouldRetain(item) { const now = Date.now(); let timestamp; switch (this.timeField) { case 'lastAccessedAt': timestamp = item.lastAccessedAt ?? item.createdAt; break; case 'updatedAt': // Safe access with type casting timestamp = item.updatedAt ?? item.createdAt; break; case 'createdAt': default: timestamp = item.createdAt; } return now - timestamp < this.maxAgeMs; } async applyToMemory(memory) { // Get all items // This implementation assumes memory has a method to get all items // In a real implementation, we would use an internal method or property // that efficiently allows us to iterate through items // For now, we'll implement a simple version that assumes a search method // This might not be efficient for large memories const allItems = await memory.search({ limit: Number.MAX_SAFE_INTEGER }); let removedCount = 0; // Check each item and remove if it doesn't meet retention criteria for (const item of allItems.items) { if (!this.shouldRetain(item)) { await memory.remove(item.id); removedCount++; } } return removedCount; } } /** * Count-based retention policy * Retains up to a maximum number of items */ export class CountBasedRetentionPolicy { maxItems; strategy; name; constructor(options) { this.maxItems = options.maxItems; this.strategy = options.strategy ?? 'oldest'; this.name = options.name; } shouldRetain(item) { // This method is not useful for CountBasedRetentionPolicy // since retention depends on comparing with other items // Always return true to defer the decision to applyToMemory return true; } async applyToMemory(memory) { // Get all items const allItems = await memory.search({ limit: Number.MAX_SAFE_INTEGER }); // If we're under the limit, do nothing if (allItems.items.length <= this.maxItems) { return 0; } // Sort based on strategy const items = [...allItems.items]; if (this.strategy === 'oldest') { items.sort((a, b) => a.createdAt - b.createdAt); } else { // leastAccessed strategy items.sort((a, b) => { const aTime = a.lastAccessedAt ?? a.createdAt; const bTime = b.lastAccessedAt ?? b.createdAt; return aTime - bTime; }); } // Calculate how many to remove const countToRemove = items.length - this.maxItems; // Remove excess items const itemsToRemove = items.slice(0, countToRemove); let removedCount = 0; for (const item of itemsToRemove) { await memory.remove(item.id); removedCount++; } return removedCount; } } /** * Relevance-based retention policy * Retains items based on their calculated relevance score */ export class RelevanceBasedRetentionPolicy { threshold; relevanceFn; name; constructor(options) { this.threshold = options.threshold; this.relevanceFn = options.relevanceFn; this.name = options.name; } shouldRetain(item) { // Calculate relevance let relevance; if (this.relevanceFn) { // Use custom relevance function relevance = this.relevanceFn(item); } else if (item.relevance !== undefined) { // Use item's relevance property if available with safe type casting relevance = item.relevance || 0; } else { // Default to high relevance if no method to determine return true; } return relevance >= this.threshold; } async applyToMemory(memory) { // Get all items const allItems = await memory.search({ limit: Number.MAX_SAFE_INTEGER }); let removedCount = 0; // Check each item and remove if it doesn't meet relevance threshold for (const item of allItems.items) { if (!this.shouldRetain(item)) { await memory.remove(item.id); removedCount++; } } return removedCount; } } /** * Composite retention policy * Combines multiple retention policies with AND/OR logic */ export class CompositeRetentionPolicy { policies; mode; name; constructor(options) { this.policies = options.policies; this.mode = options.mode ?? 'any'; this.name = options.name; } shouldRetain(item) { if (this.mode === 'all') { // Item must be retained by all policies (AND) return this.policies.every(policy => policy.shouldRetain(item)); } else { // Item must be retained by at least one policy (OR) return this.policies.some(policy => policy.shouldRetain(item)); } } async applyToMemory(memory) { // Get all items const allItems = await memory.search({ limit: Number.MAX_SAFE_INTEGER }); let removedCount = 0; // Check each item against the composite policy for (const item of allItems.items) { if (!this.shouldRetain(item)) { await memory.remove(item.id); removedCount++; } } return removedCount; } } /** * Metadata-based retention policy * Retains items based on their metadata properties */ export class MetadataBasedRetentionPolicy { criteria; invert; name; constructor(options) { this.criteria = options.criteria; this.invert = options.invert ?? false; this.name = options.name; } shouldRetain(item) { // Item must have metadata if (!item.metadata) { return !this.invert; // If inverted, remove items without metadata } // Check if item matches all criteria const matches = Object.entries(this.criteria).every(([key, value]) => { return item.metadata?.[key] === value; }); // Apply inversion if needed return this.invert ? !matches : matches; } async applyToMemory(memory) { // Get all items const allItems = await memory.search({ limit: Number.MAX_SAFE_INTEGER }); let removedCount = 0; // Check each item against metadata criteria for (const item of allItems.items) { if (!this.shouldRetain(item)) { await memory.remove(item.id); removedCount++; } } return removedCount; } } /** * Retention policy factory with common policy creation methods */ export class RetentionPolicyFactory { /** * Create a policy that retains items based on age */ static createTimeBasedPolicy(maxAgeMs, timeField = 'createdAt') { return new TimeBasedRetentionPolicy({ maxAgeMs, timeField, name: `TimeBasedRetention(${timeField}, ${maxAgeMs}ms)` }); } /** * Create a policy that retains a maximum number of items */ static createCountBasedPolicy(maxItems, strategy = 'oldest') { return new CountBasedRetentionPolicy({ maxItems, strategy, name: `CountBasedRetention(${maxItems}, ${strategy})` }); } /** * Create a policy that retains items based on relevance score */ static createRelevanceBasedPolicy(threshold, relevanceFn) { return new RelevanceBasedRetentionPolicy({ threshold, relevanceFn, name: `RelevanceBasedRetention(${threshold})` }); } /** * Create a policy that combines multiple policies */ static createCompositePolicy(policies, mode = 'any') { const policyNames = policies.map(p => p.name || 'unnamed').join(','); return new CompositeRetentionPolicy({ policies, mode, name: `CompositeRetention(${mode}, [${policyNames}])` }); } /** * Create a policy that retains items based on metadata */ static createMetadataBasedPolicy(criteria, invert = false) { const criteriaStr = JSON.stringify(criteria).slice(0, 50) + (JSON.stringify(criteria).length > 50 ? '...' : ''); return new MetadataBasedRetentionPolicy({ criteria, invert, name: `MetadataBasedRetention(${criteriaStr}, invert=${invert})` }); } }