@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
359 lines (358 loc) • 9.29 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { logger } from "../../../core/monitoring/logger.js";
import * as zlib from "zlib";
import { promisify } from "util";
const gzip = promisify(zlib.gzip);
const gunzip = promisify(zlib.gunzip);
class PerformanceOptimizer {
config;
saveBatch = [];
batchTimer;
cache = /* @__PURE__ */ new Map();
metrics = {
iterationTime: 0,
contextLoadTime: 0,
stateSaveTime: 0,
memoryUsage: 0,
tokenCount: 0,
cacheHitRate: 0
};
cacheHits = 0;
cacheMisses = 0;
strategies = [];
constructor(config) {
this.config = {
asyncSaves: config?.asyncSaves ?? true,
batchSize: config?.batchSize || 10,
compressionLevel: config?.compressionLevel || 2,
cacheEnabled: config?.cacheEnabled ?? true,
parallelOperations: config?.parallelOperations ?? true
};
this.initializeStrategies();
this.startMetricsCollection();
}
/**
* Save frame with optimizations
*/
async saveFrame(frame) {
const startTime = Date.now();
if (this.config.asyncSaves) {
this.addToBatch({
type: "frame",
data: frame,
timestamp: Date.now()
});
} else {
await this.saveFrameInternal(frame);
}
this.metrics.stateSaveTime += Date.now() - startTime;
}
/**
* Save iteration with optimizations
*/
async saveIteration(iteration) {
const startTime = Date.now();
const data = this.config.compressionLevel > 0 ? await this.compressData(iteration) : iteration;
if (this.config.asyncSaves) {
this.addToBatch({
type: "iteration",
data,
timestamp: Date.now()
});
} else {
await this.saveIterationInternal(data);
}
this.metrics.stateSaveTime += Date.now() - startTime;
}
/**
* Load frames with caching
*/
async loadFrames(query) {
const startTime = Date.now();
const cacheKey = this.generateCacheKey("frames", query);
if (this.config.cacheEnabled) {
const cached = this.getFromCache(cacheKey);
if (cached) {
this.cacheHits++;
this.metrics.contextLoadTime += Date.now() - startTime;
return cached;
}
}
this.cacheMisses++;
const frames = await this.loadFramesInternal(query);
if (this.config.cacheEnabled) {
this.setCache(cacheKey, frames, 6e4);
}
this.metrics.contextLoadTime += Date.now() - startTime;
return frames;
}
/**
* Batch save operations
*/
async flushBatch() {
if (this.saveBatch.length === 0) return;
const batch = [...this.saveBatch];
this.saveBatch = [];
logger.debug("Flushing batch", { size: batch.length });
if (this.config.parallelOperations) {
await Promise.all(batch.map((op) => this.executeSaveOperation(op)));
} else {
for (const op of batch) {
await this.executeSaveOperation(op);
}
}
}
/**
* Compress data based on compression level
*/
async compressData(data) {
if (this.config.compressionLevel === 0) return data;
const json = JSON.stringify(data);
const compressionOptions = {
level: this.config.compressionLevel * 3
// Map 1-3 to zlib levels 3-9
};
const compressed = await gzip(json, compressionOptions);
return {
compressed: true,
data: compressed.toString("base64"),
originalSize: json.length,
compressedSize: compressed.length
};
}
/**
* Decompress data
*/
async decompressData(compressed) {
if (!compressed.compressed) return compressed;
const buffer = Buffer.from(compressed.data, "base64");
const decompressed = await gunzip(buffer);
return JSON.parse(decompressed.toString());
}
/**
* Apply optimization strategies
*/
async optimize(operation, data) {
let optimized = data;
for (const strategy of this.strategies) {
if (strategy.enabled) {
try {
optimized = await strategy.apply(optimized);
} catch (error) {
logger.error("Optimization strategy failed", {
strategy: strategy.name,
error: error.message
});
}
}
}
return optimized;
}
/**
* Get performance metrics
*/
getMetrics() {
const totalCacheAttempts = this.cacheHits + this.cacheMisses;
this.metrics.cacheHitRate = totalCacheAttempts > 0 ? this.cacheHits / totalCacheAttempts : 0;
this.metrics.memoryUsage = process.memoryUsage().heapUsed;
return { ...this.metrics };
}
/**
* Clear cache
*/
clearCache() {
const size = this.cache.size;
this.cache.clear();
logger.debug("Cache cleared", { entries: size });
}
/**
* Enable/disable strategy
*/
setStrategyEnabled(strategyName, enabled) {
const strategy = this.strategies.find((s) => s.name === strategyName);
if (strategy) {
strategy.enabled = enabled;
logger.debug("Strategy updated", { name: strategyName, enabled });
}
}
/**
* Cleanup resources
*/
cleanup() {
if (this.batchTimer) {
clearTimeout(this.batchTimer);
}
this.clearCache();
this.saveBatch = [];
}
/**
* Initialize optimization strategies
*/
initializeStrategies() {
this.strategies = [
{
name: "deduplication",
enabled: true,
priority: 1,
apply: async (data) => this.deduplicateData(data),
metrics: () => this.getMetrics()
},
{
name: "chunking",
enabled: true,
priority: 2,
apply: async (data) => this.chunkLargeData(data),
metrics: () => this.getMetrics()
},
{
name: "lazy-loading",
enabled: this.config.cacheEnabled,
priority: 3,
apply: async (data) => this.createLazyProxy(data),
metrics: () => this.getMetrics()
}
];
this.strategies.sort((a, b) => a.priority - b.priority);
}
/**
* Start metrics collection
*/
startMetricsCollection() {
setInterval(() => {
const metrics = this.getMetrics();
logger.debug("Performance metrics", metrics);
}, 3e4);
}
/**
* Add operation to batch
*/
addToBatch(operation) {
this.saveBatch.push(operation);
if (!this.batchTimer) {
this.batchTimer = setTimeout(() => {
this.flushBatch().catch((error) => {
logger.error("Batch flush failed", { error: error.message });
});
this.batchTimer = void 0;
}, 1e3);
}
if (this.saveBatch.length >= this.config.batchSize) {
if (this.batchTimer) {
clearTimeout(this.batchTimer);
this.batchTimer = void 0;
}
this.flushBatch().catch((error) => {
logger.error("Batch flush failed", { error: error.message });
});
}
}
/**
* Execute save operation
*/
async executeSaveOperation(operation) {
switch (operation.type) {
case "frame":
await this.saveFrameInternal(operation.data);
break;
case "iteration":
await this.saveIterationInternal(operation.data);
break;
default:
logger.warn("Unknown operation type", { type: operation.type });
}
}
/**
* Internal frame save
*/
async saveFrameInternal(frame) {
logger.debug("Frame saved", { frameId: frame.frame_id });
}
/**
* Internal iteration save
*/
async saveIterationInternal(iteration) {
logger.debug("Iteration saved", { iteration: iteration.number });
}
/**
* Internal frame load
*/
async loadFramesInternal(query) {
return [];
}
/**
* Generate cache key
*/
generateCacheKey(type, params) {
return `${type}:${JSON.stringify(params)}`;
}
/**
* Get from cache
*/
getFromCache(key) {
const entry = this.cache.get(key);
if (!entry) return void 0;
if (entry.expiry && entry.expiry < Date.now()) {
this.cache.delete(key);
return void 0;
}
return entry.data;
}
/**
* Set cache entry
*/
setCache(key, data, ttl) {
const entry = {
data,
timestamp: Date.now(),
expiry: ttl ? Date.now() + ttl : void 0
};
this.cache.set(key, entry);
if (this.cache.size > 100) {
const entries = Array.from(this.cache.entries());
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
for (let i = 0; i < 20; i++) {
this.cache.delete(entries[i][0]);
}
}
}
/**
* Deduplicate data
*/
deduplicateData(data) {
if (Array.isArray(data)) {
const seen = /* @__PURE__ */ new Set();
return data.filter((item) => {
const key = JSON.stringify(item);
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
return data;
}
/**
* Chunk large data
*/
chunkLargeData(data) {
const json = JSON.stringify(data);
if (json.length > 1e5) {
logger.debug("Large data detected", { size: json.length });
}
return data;
}
/**
* Create lazy-loading proxy
*/
createLazyProxy(data) {
return data;
}
}
export {
PerformanceOptimizer
};
//# sourceMappingURL=performance-optimizer.js.map