@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
242 lines • 9.17 kB
JavaScript
/**
* @file Batch Strategy
* Batch processing for evaluation pipelines
*/
function hasPipelineResult(result) {
return !result.error && result.result !== undefined;
}
/**
* Default batch configuration
*/
const DEFAULT_BATCH_CONFIG = {
concurrency: 5,
batchDelay: 0,
continueOnError: true,
onProgress: () => { },
onResult: () => { },
};
/**
* Batch evaluation strategy
*/
export class BatchStrategy {
_pipeline;
_config;
constructor(pipeline, config) {
this._pipeline = pipeline;
this._config = { ...DEFAULT_BATCH_CONFIG, ...config };
}
/**
* Evaluate a batch of inputs
*/
async evaluate(inputs, options) {
const startTime = Date.now();
const results = [];
const durations = [];
// Process in batches based on concurrency
for (let i = 0; i < inputs.length; i += this._config.concurrency) {
const batch = inputs.slice(i, i + this._config.concurrency);
// Execute batch in parallel
const batchPromises = batch.map((input, batchIndex) => {
const globalIndex = i + batchIndex;
return this._evaluateItem(input, globalIndex, options);
});
const batchResults = await Promise.all(batchPromises);
// Record results
for (const result of batchResults) {
results.push(result);
durations.push(result.duration);
// Notify of individual result
this._config.onResult(result);
// Check for fatal error
if (result.error && !this._config.continueOnError) {
throw new Error(`Batch evaluation failed at index ${result.index}: ${result.error}`);
}
}
// Report progress
this._config.onProgress({
total: inputs.length,
completed: results.length,
failed: results.filter((r) => r.error).length,
pending: inputs.length - results.length,
percentComplete: (results.length / inputs.length) * 100,
estimatedTimeRemaining: this._estimateRemainingTime(durations, inputs.length - results.length),
});
// Apply batch delay if configured
if (this._config.batchDelay > 0 &&
i + this._config.concurrency < inputs.length) {
await this._delay(this._config.batchDelay);
}
}
// Calculate summary
const totalDuration = Date.now() - startTime;
const successfulResults = results.filter(hasPipelineResult);
const scores = successfulResults.map((r) => r.result.overallScore);
const passed = successfulResults.filter((r) => r.result.passed);
return {
results,
summary: {
total: inputs.length,
succeeded: successfulResults.length,
failed: results.length - successfulResults.length,
averageScore: scores.length > 0
? scores.reduce((a, b) => a + b, 0) / scores.length
: 0,
passingRate: successfulResults.length > 0
? passed.length / successfulResults.length
: 0,
totalDuration,
averageDuration: durations.length > 0
? durations.reduce((a, b) => a + b, 0) / durations.length
: 0,
},
};
}
/**
* Evaluate a single item
*/
async _evaluateItem(input, index, options) {
const startTime = Date.now();
try {
const result = await this._pipeline.execute(input, {
...options,
correlationId: options?.correlationId
? `${options.correlationId}-${index}`
: `batch-${index}`,
});
return {
index,
input,
result,
duration: Date.now() - startTime,
};
}
catch (error) {
return {
index,
input,
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - startTime,
};
}
}
/**
* Estimate remaining time based on average duration
*/
_estimateRemainingTime(durations, remaining) {
if (durations.length === 0 || remaining === 0) {
return 0;
}
const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
const batches = Math.ceil(remaining / this._config.concurrency);
return avgDuration * batches + this._config.batchDelay * (batches - 1);
}
/**
* Delay helper
*/
_delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Update configuration
*/
configure(config) {
this._config = { ...this._config, ...config };
}
}
/**
* Create a batch strategy for a pipeline
*/
export function createBatchStrategy(pipeline, config) {
return new BatchStrategy(pipeline, config);
}
/**
* Evaluate a batch of inputs using a pipeline
*/
export async function evaluateBatch(pipeline, inputs, config) {
const strategy = new BatchStrategy(pipeline, config);
return strategy.evaluate(inputs);
}
/**
* Stream batch evaluation results
*/
export async function* streamBatchEvaluation(pipeline, inputs, config) {
const results = [];
const durations = [];
const startTime = Date.now();
const batchConfig = { ...DEFAULT_BATCH_CONFIG, ...config };
for (let i = 0; i < inputs.length; i += batchConfig.concurrency) {
const batch = inputs.slice(i, i + batchConfig.concurrency);
const batchPromises = batch.map(async (input, batchIndex) => {
const globalIndex = i + batchIndex;
const itemStart = Date.now();
try {
const result = await pipeline.execute(input, {
correlationId: `stream-batch-${globalIndex}`,
});
return {
index: globalIndex,
input,
result,
duration: Date.now() - itemStart,
};
}
catch (error) {
return {
index: globalIndex,
input,
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - itemStart,
};
}
});
const batchResults = await Promise.all(batchPromises);
for (const result of batchResults) {
results.push(result);
durations.push(result.duration);
// If continueOnError is false and this result has an error, abort and return summary
if (result.error && batchConfig.continueOnError === false) {
const successfulResults = results.filter(hasPipelineResult);
const earlyScores = successfulResults.map((r) => r.result.overallScore);
const earlyPassed = successfulResults.filter((r) => r.result.passed);
return {
total: inputs.length,
succeeded: successfulResults.length,
failed: results.length - successfulResults.length,
averageScore: earlyScores.length > 0
? earlyScores.reduce((a, b) => a + b, 0) / earlyScores.length
: 0,
passingRate: successfulResults.length > 0
? earlyPassed.length / successfulResults.length
: 0,
totalDuration: Date.now() - startTime,
averageDuration: durations.length > 0
? durations.reduce((a, b) => a + b, 0) / durations.length
: 0,
};
}
yield result;
}
if (batchConfig.batchDelay > 0 &&
i + batchConfig.concurrency < inputs.length) {
await new Promise((resolve) => setTimeout(resolve, batchConfig.batchDelay));
}
}
// Return summary
const successfulResults = results.filter(hasPipelineResult);
const scores = successfulResults.map((r) => r.result.overallScore);
const passed = successfulResults.filter((r) => r.result.passed);
return {
total: inputs.length,
succeeded: successfulResults.length,
failed: results.length - successfulResults.length,
averageScore: scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0,
passingRate: successfulResults.length > 0
? passed.length / successfulResults.length
: 0,
totalDuration: Date.now() - startTime,
averageDuration: durations.length > 0
? durations.reduce((a, b) => a + b, 0) / durations.length
: 0,
};
}
//# sourceMappingURL=batchStrategy.js.map