tasq/node_modules/agentic-flow/dist/core/long-running-agent.js

220 lines
8.0 KiB
JavaScript

/**
* Long-Running Agent with Provider Fallback
*
* Demonstrates how to use ProviderManager for resilient, cost-optimized agents
* that can run for hours or days with automatic provider switching.
*/
import { ProviderManager } from './provider-manager.js';
import { logger } from '../utils/logger.js';
export class LongRunningAgent {
providerManager;
config;
startTime;
checkpoints = [];
currentState = {};
isRunning = false;
checkpointInterval;
constructor(config) {
this.config = config;
this.startTime = new Date();
// Initialize provider manager
this.providerManager = new ProviderManager(config.providers, config.fallbackStrategy);
logger.info('Long-running agent initialized', {
agentName: config.agentName,
providers: config.providers.map(p => p.name)
});
}
/**
* Start the agent with automatic checkpointing
*/
async start() {
this.isRunning = true;
this.startTime = new Date();
// Start checkpoint interval
if (this.config.checkpointInterval) {
this.checkpointInterval = setInterval(() => {
this.saveCheckpoint();
}, this.config.checkpointInterval);
}
logger.info('Long-running agent started', {
agentName: this.config.agentName,
startTime: this.startTime
});
}
/**
* Execute a task with automatic provider fallback
*/
async executeTask(task) {
if (!this.isRunning) {
throw new Error('Agent not running. Call start() first.');
}
// Check budget constraint
if (this.config.costBudget) {
const currentCost = this.providerManager.getCostSummary().total;
if (currentCost >= this.config.costBudget) {
throw new Error(`Cost budget exceeded: $${currentCost.toFixed(2)} >= $${this.config.costBudget}`);
}
}
// Check runtime constraint
if (this.config.maxRuntime) {
const runtime = Date.now() - this.startTime.getTime();
if (runtime >= this.config.maxRuntime) {
throw new Error(`Max runtime exceeded: ${runtime}ms >= ${this.config.maxRuntime}ms`);
}
}
logger.info('Executing task', {
agentName: this.config.agentName,
taskName: task.name,
complexity: task.complexity
});
try {
// Execute with automatic fallback
const { result, provider, attempts } = await this.providerManager.executeWithFallback(task.execute, task.complexity, task.estimatedTokens);
// Update state
this.currentState.lastTask = task.name;
this.currentState.lastProvider = provider;
this.currentState.completedTasks = (this.currentState.completedTasks || 0) + 1;
logger.info('Task completed', {
agentName: this.config.agentName,
taskName: task.name,
provider,
attempts
});
return result;
}
catch (error) {
this.currentState.failedTasks = (this.currentState.failedTasks || 0) + 1;
logger.error('Task failed', {
agentName: this.config.agentName,
taskName: task.name,
error: error.message
});
throw error;
}
}
/**
* Save checkpoint of current state
*/
saveCheckpoint() {
const costSummary = this.providerManager.getCostSummary();
const health = this.providerManager.getHealth();
const checkpoint = {
timestamp: new Date(),
taskProgress: this.calculateProgress(),
currentProvider: this.currentState.lastProvider || 'none',
totalCost: costSummary.total,
totalTokens: costSummary.totalTokens,
completedTasks: this.currentState.completedTasks || 0,
failedTasks: this.currentState.failedTasks || 0,
state: { ...this.currentState }
};
this.checkpoints.push(checkpoint);
logger.info('Checkpoint saved', {
agentName: this.config.agentName,
checkpoint: {
...checkpoint,
state: undefined // Don't log full state
}
});
// Alert if cost approaching budget
if (this.config.costBudget) {
const costPercentage = (costSummary.total / this.config.costBudget) * 100;
if (costPercentage >= 80) {
logger.warn('Cost budget warning', {
agentName: this.config.agentName,
currentCost: costSummary.total,
budget: this.config.costBudget,
percentage: costPercentage.toFixed(1) + '%'
});
}
}
// Alert if providers unhealthy
const unhealthyProviders = health.filter(h => !h.isHealthy || h.circuitBreakerOpen);
if (unhealthyProviders.length > 0) {
logger.warn('Unhealthy providers detected', {
agentName: this.config.agentName,
unhealthy: unhealthyProviders.map(h => ({
provider: h.provider,
circuitBreakerOpen: h.circuitBreakerOpen,
consecutiveFailures: h.consecutiveFailures
}))
});
}
}
/**
* Calculate task progress (override in subclass)
*/
calculateProgress() {
// Default: based on completed vs total tasks
const completed = this.currentState.completedTasks || 0;
const failed = this.currentState.failedTasks || 0;
const total = completed + failed;
return total > 0 ? completed / total : 0;
}
/**
* Get current status
*/
getStatus() {
const costSummary = this.providerManager.getCostSummary();
const health = this.providerManager.getHealth();
const runtime = Date.now() - this.startTime.getTime();
return {
isRunning: this.isRunning,
runtime,
completedTasks: this.currentState.completedTasks || 0,
failedTasks: this.currentState.failedTasks || 0,
totalCost: costSummary.total,
totalTokens: costSummary.totalTokens,
providers: health.map(h => ({
name: h.provider,
healthy: h.isHealthy,
circuitBreakerOpen: h.circuitBreakerOpen,
successRate: (h.successRate * 100).toFixed(1) + '%',
avgLatency: h.averageLatency.toFixed(0) + 'ms'
})),
lastCheckpoint: this.checkpoints[this.checkpoints.length - 1]
};
}
/**
* Get detailed metrics
*/
getMetrics() {
return {
providers: this.providerManager.getMetrics(),
health: this.providerManager.getHealth(),
costs: this.providerManager.getCostSummary(),
checkpoints: this.checkpoints
};
}
/**
* Restore from checkpoint
*/
restoreFromCheckpoint(checkpoint) {
this.currentState = { ...checkpoint.state };
logger.info('Restored from checkpoint', {
agentName: this.config.agentName,
checkpoint: checkpoint.timestamp
});
}
/**
* Stop the agent
*/
async stop() {
this.isRunning = false;
// Clear checkpoint interval
if (this.checkpointInterval) {
clearInterval(this.checkpointInterval);
}
// Save final checkpoint
this.saveCheckpoint();
// Cleanup provider manager
this.providerManager.destroy();
logger.info('Long-running agent stopped', {
agentName: this.config.agentName,
runtime: Date.now() - this.startTime.getTime(),
completedTasks: this.currentState.completedTasks,
failedTasks: this.currentState.failedTasks
});
}
}
//# sourceMappingURL=long-running-agent.js.map