UNPKG

@layr-labs/hourglass-performer

Version:

TypeScript SDK for building Hourglass AVS performers

278 lines 9.47 kB
"use strict"; // Enhanced task processing framework with serialization and validation Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskProcessor = exports.NumberSerializationStrategy = exports.RawSerializationStrategy = exports.JsonSerializationStrategy = void 0; const performer_1 = require("../types/performer"); const taskContext_1 = require("./taskContext"); /** * JSON serialization strategy */ class JsonSerializationStrategy { constructor() { this.name = 'json'; } serialize(data) { const json = JSON.stringify(data); return new TextEncoder().encode(json); } deserialize(bytes) { const json = new TextDecoder().decode(bytes); return JSON.parse(json); } canDeserialize(bytes) { try { const json = new TextDecoder().decode(bytes); JSON.parse(json); return true; } catch { return false; } } } exports.JsonSerializationStrategy = JsonSerializationStrategy; /** * Raw bytes serialization strategy */ class RawSerializationStrategy { constructor() { this.name = 'raw'; } serialize(data) { if (data instanceof Uint8Array) { return data; } if (typeof data === 'string') { return new TextEncoder().encode(data); } throw new Error('Raw serialization only supports Uint8Array or string'); } deserialize(bytes) { return bytes; } canDeserialize(bytes) { return true; } } exports.RawSerializationStrategy = RawSerializationStrategy; /** * Number serialization strategy (64-bit big-endian) */ class NumberSerializationStrategy { constructor() { this.name = 'number'; } serialize(data) { const num = Number(data); if (isNaN(num)) { throw new Error('Invalid number for serialization'); } const bytes = new Uint8Array(8); const view = new DataView(bytes.buffer); view.setBigUint64(0, BigInt(num), false); // big-endian return bytes; } deserialize(bytes) { if (bytes.length !== 8) { throw new Error('Number deserialization requires exactly 8 bytes'); } const view = new DataView(bytes.buffer); return Number(view.getBigUint64(0, false)); // big-endian } canDeserialize(bytes) { return bytes.length === 8; } } exports.NumberSerializationStrategy = NumberSerializationStrategy; /** * Enhanced task processor with pipeline support */ class TaskProcessor { constructor(worker, logger, config = {}) { this.worker = worker; this.logger = logger; this.metrics = []; // Set up default configuration this.config = { timeout: config.timeout ?? 5000, enableMetrics: config.enableMetrics ?? true, validationStages: config.validationStages ?? [], defaultSerialization: config.defaultSerialization ?? new JsonSerializationStrategy(), serializationStrategies: config.serializationStrategies ?? new Map([ ['json', new JsonSerializationStrategy()], ['raw', new RawSerializationStrategy()], ['number', new NumberSerializationStrategy()], ]), }; this.setupPipeline(); } /** * Process a task with full pipeline support */ async processTask(request) { const contextBuilder = new taskContext_1.TaskContextBuilder(request, this.logger, this.config.timeout); const context = contextBuilder.build(); try { // Execute the processing pipeline if (this.pipeline) { await this.pipeline.execute(context); } // Execute the worker task const response = await this.executeWorkerTask(context); // Record success metrics if (this.config.enableMetrics) { this.recordTaskCompletion(context, response); } return response; } catch (error) { // Record failure metrics if (this.config.enableMetrics) { this.recordTaskFailure(context, error); } // Re-throw the error for gRPC handling throw error; } } /** * Execute the worker task with enhanced error handling */ async executeWorkerTask(context) { const { request } = context; try { // Validate task using worker await this.worker.validateTask(request); context.logger.debug('Task validation passed'); // Handle task using worker const response = await this.worker.handleTask(request); context.logger.debug('Task execution completed', { resultSize: response.result.length }); // Validate response this.validateResponse(response, request); return response; } catch (error) { if (error instanceof performer_1.TaskValidationError || error instanceof performer_1.TaskExecutionError) { throw error; } // Wrap unknown errors throw new performer_1.TaskExecutionError(error instanceof Error ? error.message : 'Unknown task execution error', request.taskId); } } /** * Validate task response */ validateResponse(response, request) { if (response.taskId !== request.taskId) { throw new performer_1.TaskExecutionError(`Response task ID (${response.taskId}) does not match request task ID (${request.taskId})`, request.taskId); } if (!response.result) { throw new performer_1.TaskExecutionError('Task response must include a result', request.taskId); } } /** * Set up the processing pipeline */ setupPipeline() { this.pipeline = new taskContext_1.TaskPipeline(); // Add built-in validation stages this.pipeline.addStage(new taskContext_1.ValidationStage(async (context) => { this.validateTaskRequest(context.request); })); // Add custom validation stages for (const stage of this.config.validationStages) { this.pipeline.addStage(stage); } // Add metrics collection stage if enabled if (this.config.enableMetrics) { this.pipeline.addStage({ name: 'metrics-start', execute: async (context) => { const metrics = { taskId: context.request.taskId, executionId: context.executionId, startTime: context.startTime, payloadSize: context.request.payload.length, }; context.metadata.set('metrics', metrics); }, }); } } /** * Validate task request */ validateTaskRequest(request) { if (!request.taskId) { throw new performer_1.TaskValidationError('Task ID is required', ''); } if (!request.payload) { throw new performer_1.TaskValidationError('Task payload is required', request.taskId); } if (request.payload.length === 0) { throw new performer_1.TaskValidationError('Task payload cannot be empty', request.taskId); } } /** * Record successful task completion metrics */ recordTaskCompletion(context, response) { const metrics = context.metadata.get('metrics'); if (metrics) { metrics.endTime = Date.now(); metrics.duration = metrics.endTime - metrics.startTime; metrics.success = true; metrics.resultSize = response.result.length; this.metrics.push(metrics); context.logger.info('Task completed successfully', { duration: `${metrics.duration}ms`, payloadSize: metrics.payloadSize, resultSize: metrics.resultSize, }); } } /** * Record task failure metrics */ recordTaskFailure(context, error) { const metrics = context.metadata.get('metrics'); if (metrics) { metrics.endTime = Date.now(); metrics.duration = metrics.endTime - metrics.startTime; metrics.success = false; metrics.error = error instanceof Error ? error.message : 'Unknown error'; this.metrics.push(metrics); context.logger.error('Task execution failed', { duration: `${metrics.duration}ms`, error: metrics.error, }); } } /** * Get serialization strategy by name */ getSerializationStrategy(name) { return this.config.serializationStrategies.get(name); } /** * Add custom serialization strategy */ addSerializationStrategy(strategy) { this.config.serializationStrategies.set(strategy.name, strategy); } /** * Get task execution metrics */ getMetrics() { return [...this.metrics]; } /** * Clear accumulated metrics */ clearMetrics() { this.metrics = []; } } exports.TaskProcessor = TaskProcessor; //# sourceMappingURL=taskProcessor.js.map