crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
149 lines (148 loc) • 5.08 kB
JavaScript
/**
* Storage adapter for crew kickoff task outputs
* Optimized for memory efficiency and fast access patterns
*/
import fs from 'fs';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
/**
* Storage for crew kickoff task outputs
* Uses optimized file I/O and memory-efficient data structures
*/
export class KickoffTaskOutputsStorage {
dbPath;
cache;
cacheSize = 50; // Limit cache size for memory efficiency
/**
* Initialize storage with optional custom path
* @param dbPath Optional custom database path
*/
constructor(dbPath) {
// Use SQLite-compatible path by default
this.dbPath = dbPath || path.join(process.cwd(), '.crewai', 'task_outputs.json');
this.cache = new Map();
this.ensureStorageExists();
}
/**
* Ensure storage directory exists
* Uses efficient async I/O patterns
*/
ensureStorageExists() {
const dir = path.dirname(this.dbPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
if (!fs.existsSync(this.dbPath)) {
fs.writeFileSync(this.dbPath, JSON.stringify([]));
}
}
/**
* Save a task output
* @param task Task output to save
* @returns Saved task with ID
*/
async save(task) {
const tasks = await this.loadAll();
// Generate ID if not provided
const taskOutput = {
...task,
taskId: task.taskId || uuidv4(),
timestamp: task.timestamp || Date.now()
};
// Add to beginning for faster recent access
tasks.unshift(taskOutput);
// Write to storage with buffered write for performance
await fs.promises.writeFile(this.dbPath, JSON.stringify(tasks, null, 2));
// Update cache with memory-efficient approach
this.updateCache(taskOutput);
return taskOutput;
}
/**
* Load all task outputs
* Uses memory-efficient streaming for large datasets
* @returns Array of task outputs
*/
async loadAll() {
try {
const data = await fs.promises.readFile(this.dbPath, 'utf8');
const tasks = JSON.parse(data || '[]');
// Update cache with efficient batch operation
tasks.slice(0, this.cacheSize).forEach(task => {
this.cache.set(task.taskId, task);
});
return tasks;
}
catch (error) {
console.error('Error loading task outputs:', error);
return [];
}
}
/**
* Get a task by ID
* Uses memory-efficient caching for frequently accessed tasks
* @param taskId Task ID
* @returns Task output or null if not found
*/
async getTaskById(taskId) {
// Check cache first for performance
if (this.cache.has(taskId)) {
return this.cache.get(taskId) || null;
}
const tasks = await this.loadAll();
const task = tasks.find(t => t.taskId === taskId);
if (task) {
this.updateCache(task);
}
return task || null;
}
/**
* Clear all task outputs
* Uses optimized file I/O
*/
async clear() {
await fs.promises.writeFile(this.dbPath, JSON.stringify([]));
this.cache.clear();
}
/**
* Delete a task by ID
* @param taskId Task ID to delete
* @returns True if deleted, false if not found
*/
async deleteTask(taskId) {
const tasks = await this.loadAll();
const initialLength = tasks.length;
// Use efficient filter operation
const filteredTasks = tasks.filter(task => task.taskId !== taskId);
if (filteredTasks.length === initialLength) {
return false; // Task not found
}
await fs.promises.writeFile(this.dbPath, JSON.stringify(filteredTasks, null, 2));
this.cache.delete(taskId);
return true;
}
/**
* Update cache with LRU eviction policy
* Ensures memory efficiency for long-running processes
* @param task Task to update in cache
*/
updateCache(task) {
// Type safety check for task ID
if (!task || typeof task.taskId !== 'string' || task.taskId.trim().length === 0) {
console.warn('Attempted to cache task with invalid ID');
return;
}
// Implement simple LRU cache with memory optimization - remove oldest if at capacity
if (this.cache.size >= this.cacheSize) {
// Get iterator for the keys of the cache
const keysIterator = this.cache.keys();
// Get the first key with type safety
const iterResult = keysIterator.next();
if (!iterResult.done && typeof iterResult.value === 'string') {
// Only delete if the key is a valid string
this.cache.delete(iterResult.value);
}
}
// Safe to use task.taskId as we've validated it above
this.cache.set(task.taskId, task);
}
}