@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
354 lines • 14 kB
JavaScript
/**
* Optimized Task Executor
* Implements async execution with connection pooling and caching
*/
import { EventEmitter } from "node:events";
import { Logger } from "../../core/logger.js";
import { ClaudeConnectionPool } from "./connection-pool.js";
import { AsyncFileManager } from "./async-file-manager.js";
import { TTLMap } from "./ttl-map.js";
import { CircularBuffer } from "./circular-buffer.js";
import { DEFAULT_MODEL_CONFIG } from "../../config/model-config.js";
export class OptimizedExecutor extends EventEmitter {
config;
logger;
connectionPool;
fileManager;
executionQueue; // Simplified queue
resultCache;
executionHistory;
metrics = {
totalExecuted: 0,
totalSucceeded: 0,
totalFailed: 0,
totalExecutionTime: 0,
cacheHits: 0,
cacheMisses: 0,
};
activeExecutions = new Set();
constructor(config = {}) {
super();
this.config = config;
this.logger = new Logger({ level: "info", format: "json", destination: "console" }, { component: "OptimizedExecutor" });
// Initialize connection pool
this.connectionPool = new ClaudeConnectionPool({
min: config.connectionPool?.min ?? 2,
max: config.connectionPool?.max ?? 10,
apiKey: process.env.ANTHROPIC_API_KEY || "",
});
// Initialize file manager
this.fileManager = new AsyncFileManager({
write: config.fileOperations?.concurrency ?? 10,
read: config.fileOperations?.concurrency || 20,
});
// Initialize execution queue - simplified implementation
this.executionQueue = {
add: async (fn) => fn(),
size: 0,
clear: () => { },
onIdle: async () => { },
};
// Initialize result cache
this.resultCache = new TTLMap({
defaultTTL: config.caching?.ttl || 3600000, // 1 hour
maxSize: config.caching?.maxSize || 1000,
onExpire: (key, value) => {
this.logger.debug("Cache entry expired", { taskId: key });
},
});
// Initialize execution history
this.executionHistory = new CircularBuffer(1000);
// Start monitoring if configured
if (config.monitoring?.metricsInterval) {
setInterval(() => {
this.emitMetrics();
}, config.monitoring.metricsInterval);
}
}
async executeTask(task, agentId) {
const startTime = Date.now();
const taskKey = this.getTaskCacheKey(task);
// Check cache if enabled
if (this.config.caching?.enabled) {
const cached = this.resultCache.get(taskKey);
if (cached) {
this.metrics.cacheHits++;
this.logger.debug("Cache hit for task", { taskId: task.id });
return cached;
}
this.metrics.cacheMisses++;
}
// Add to active executions
this.activeExecutions.add(task.id.id);
// Queue the execution
const result = await this.executionQueue.add(async () => {
try {
// Execute with connection pool
const executionResult = await this.connectionPool.execute(async (api) => {
// Get system prompt if available
const taskWithMetadata = task;
const systemPrompt = taskWithMetadata.metadata?.systemPrompt || `Task: ${task.description}`;
const response = await api.complete({
messages: this.buildMessages(task),
model: task.metadata?.model || DEFAULT_MODEL_CONFIG.primary,
max_tokens: task.constraints.maxTokens || 4096,
temperature: task.metadata?.temperature ?? 0.7,
system: systemPrompt,
});
// Extract text content from response
let outputText = "";
if (response.content && response.content.length > 0) {
const firstContent = response.content[0];
if (firstContent.type === "text") {
outputText = firstContent.text;
}
}
return {
success: true,
output: outputText,
usage: {
inputTokens: response.usage?.input_tokens ?? 0,
outputTokens: response.usage?.output_tokens ?? 0,
},
};
});
// Save result to file asynchronously
if (this.config.fileOperations?.outputDir) {
const outputPath = `${this.config.fileOperations.outputDir}/${task.id.id}.json`;
await this.fileManager.writeJSON(outputPath, {
taskId: task.id,
agentId: agentId.id,
result: executionResult,
timestamp: new Date(),
});
}
// Create task result
// Ensure minimum execution time of 1ms for metrics calculation
const executionTime = Math.max(1, Date.now() - startTime);
const taskResult = {
output: executionResult.output,
artifacts: {},
metadata: {
agentId: agentId.id,
success: executionResult.success,
executionTime,
tokensUsed: executionResult.usage,
},
quality: 1.0,
completeness: 1.0,
accuracy: 1.0,
executionTime,
resourcesUsed: {
cpuTime: executionTime,
maxMemory: 0,
diskIO: 0,
networkIO: 0,
fileHandles: 0,
},
validated: true,
};
// Cache result if enabled
if (this.config.caching?.enabled && executionResult.success) {
this.resultCache.set(taskKey, taskResult);
}
// Update metrics
this.metrics.totalExecuted++;
this.metrics.totalSucceeded++;
this.metrics.totalExecutionTime += taskResult.executionTime;
// Record in history
this.executionHistory.push({
taskId: task.id.id,
duration: executionTime,
status: "success",
timestamp: new Date(),
});
// Check if slow task
if (this.config.monitoring?.slowTaskThreshold &&
taskResult.executionTime > this.config.monitoring.slowTaskThreshold) {
this.logger.warn("Slow task detected", {
taskId: task.id.id,
duration: taskResult.executionTime,
threshold: this.config.monitoring.slowTaskThreshold,
});
}
this.emit("task:completed", taskResult);
return taskResult;
}
catch (error) {
this.metrics.totalExecuted++;
this.metrics.totalFailed++;
// Ensure minimum execution time of 1ms for metrics calculation
const errorExecutionTime = Math.max(1, Date.now() - startTime);
const errorResult = {
output: "",
artifacts: {},
metadata: {
agentId: agentId.id,
success: false,
error: {
type: error instanceof Error ? error.constructor.name : "UnknownError",
message: error instanceof Error ? error.message : "Unknown error",
code: error instanceof Error && "code" in error ? error.code : undefined,
stack: error instanceof Error ? error.stack : undefined,
context: { taskId: task.id.id, agentId: agentId.id },
recoverable: this.isRecoverableError(error),
retryable: this.isRetryableError(error),
},
},
quality: 0.0,
completeness: 0.0,
accuracy: 0.0,
executionTime: errorExecutionTime,
resourcesUsed: {
cpuTime: errorExecutionTime,
maxMemory: 0,
diskIO: 0,
networkIO: 0,
fileHandles: 0,
},
validated: false,
};
// Record in history
this.executionHistory.push({
taskId: task.id.id,
duration: errorExecutionTime,
status: "failed",
timestamp: new Date(),
});
this.emit("task:failed", errorResult);
throw error;
}
finally {
this.activeExecutions.delete(task.id.id);
}
});
return result;
}
async executeBatch(tasks, agentId) {
return Promise.all(tasks.map(task => this.executeTask(task, agentId)));
}
buildMessages(task) {
const messages = [];
// Add main task objective
messages.push({
role: "user",
content: task.description,
});
// Add context if available
if (task.context) {
if (task.context.previousResults && Array.isArray(task.context.previousResults) && task.context.previousResults.length) {
messages.push({
role: "assistant",
content: `Previous results:\n${task.context.previousResults.map((r) => r.output).join("\n\n")}`,
});
}
if (task.context.relatedTasks && Array.isArray(task.context.relatedTasks) && task.context.relatedTasks.length) {
messages.push({
role: "user",
content: `Related context:\n${task.context.relatedTasks.map((t) => t.description).join("\n")}`,
});
}
}
return messages;
}
getTaskCacheKey(task) {
// Create a cache key based on task properties
const taskWithMetadata = task;
return `${task.type}-${task.description}-${JSON.stringify(taskWithMetadata.metadata || {})}`;
}
isRecoverableError(error) {
if (!error)
return false;
const err = error;
// Network errors are often recoverable
if (err.code === "ECONNRESET" ||
err.code === "ETIMEDOUT" ||
err.code === "ENOTFOUND") {
return true;
}
// Rate limit errors are recoverable with backoff
if (err.status === 429) {
return true;
}
return false;
}
isRetryableError(error) {
if (!error)
return false;
// Most recoverable errors are retryable
if (this.isRecoverableError(error)) {
return true;
}
// Server errors might be temporary
if (error.status >= 500 && error.status < 600) {
return true;
}
return false;
}
getMetrics() {
const history = this.executionHistory.getAll();
const avgExecutionTime = this.metrics.totalExecuted > 0
? this.metrics.totalExecutionTime / this.metrics.totalExecuted
: 0;
const cacheTotal = this.metrics.cacheHits + this.metrics.cacheMisses;
const cacheHitRate = cacheTotal > 0
? this.metrics.cacheHits / cacheTotal
: 0;
return {
totalExecuted: this.metrics.totalExecuted,
totalSucceeded: this.metrics.totalSucceeded,
totalFailed: this.metrics.totalFailed,
avgExecutionTime,
cacheHitRate,
queueLength: this.executionQueue.size,
activeExecutions: this.activeExecutions.size,
};
}
emitMetrics() {
const metrics = this.getMetrics();
this.emit("metrics", metrics);
// Also log if configured
this.logger.info("Executor metrics", metrics);
}
async waitForPendingExecutions() {
await this.executionQueue.onIdle?.();
await this.fileManager.waitForPendingOperations();
}
async shutdown() {
this.logger.info("Shutting down optimized executor");
// Clear the queue
this.executionQueue.clear?.();
// Wait for active executions
await this.waitForPendingExecutions();
// Drain connection pool
await this.connectionPool.drain();
// Clear caches
this.resultCache.destroy();
this.logger.info("Optimized executor shut down");
}
/**
* Get execution history for analysis
*/
getExecutionHistory() {
return this.executionHistory.snapshot();
}
/**
* Get connection pool statistics
*/
getConnectionPoolStats() {
return this.connectionPool.getStats();
}
/**
* Get file manager metrics
*/
getFileManagerMetrics() {
return this.fileManager.getMetrics();
}
/**
* Get cache statistics
*/
getCacheStats() {
return this.resultCache.getStats();
}
}
//# sourceMappingURL=optimized-executor.js.map