| |
| |
|
|
| export class BaseCallback {
|
| |
| |
|
|
| async onStart(runnable, input, config) {
|
|
|
| }
|
|
|
| |
| |
|
|
| async onEnd(runnable, output, config) {
|
|
|
| }
|
|
|
| |
| |
|
|
| async onError(runnable, error, config) {
|
|
|
| }
|
|
|
| |
| |
|
|
| async onLLMNewToken(token, config) {
|
|
|
| }
|
|
|
| |
| |
|
|
| async onChainStep(stepName, output, config) {
|
|
|
| }
|
| }
|
|
|
| |
| |
|
|
| export class ConsoleCallback extends BaseCallback {
|
| constructor(options = {}) {
|
| super();
|
| this.verbose = options.verbose ?? true;
|
| this.colors = options.colors ?? true;
|
| }
|
|
|
| async onStart(runnable, input, config) {
|
| if (this.verbose) {
|
| console.log(`\n▶ Starting: ${runnable.name}`);
|
| console.log(` Input:`, this._format(input));
|
| }
|
| }
|
|
|
| async onEnd(runnable, output, config) {
|
| if (this.verbose) {
|
| console.log(`✓ Completed: ${runnable.name}`);
|
| console.log(` Output:`, this._format(output));
|
| }
|
| }
|
|
|
| async onError(runnable, error, config) {
|
| console.error(`✗ Error in ${runnable.name}:`, error.message);
|
| }
|
|
|
| async onLLMNewToken(token, config) {
|
| process.stdout.write(token);
|
| }
|
|
|
| _format(value) {
|
| if (typeof value === 'string') {
|
| return value.length > 100 ? value.substring(0, 97) + '...' : value;
|
| }
|
| return JSON.stringify(value, null, 2);
|
| }
|
| }
|
|
|
| |
| |
|
|
| export class MetricsCallback extends BaseCallback {
|
| constructor() {
|
| super();
|
| this.metrics = {
|
| calls: {},
|
| totalTime: {},
|
| errors: {}
|
| };
|
| this.startTimes = new Map();
|
| }
|
|
|
| async onStart(runnable, input, config) {
|
| const name = runnable.name;
|
| this.startTimes.set(name, Date.now());
|
|
|
| this.metrics.calls[name] = (this.metrics.calls[name] || 0) + 1;
|
| }
|
|
|
| async onEnd(runnable, output, config) {
|
| const name = runnable.name;
|
| const startTime = this.startTimes.get(name);
|
|
|
| if (startTime) {
|
| const duration = Date.now() - startTime;
|
| this.metrics.totalTime[name] = (this.metrics.totalTime[name] || 0) + duration;
|
| this.startTimes.delete(name);
|
| }
|
| }
|
|
|
| async onError(runnable, error, config) {
|
| const name = runnable.name;
|
| this.metrics.errors[name] = (this.metrics.errors[name] || 0) + 1;
|
| }
|
|
|
| getReport() {
|
| const report = [];
|
|
|
| for (const [name, calls] of Object.entries(this.metrics.calls)) {
|
| const totalTime = this.metrics.totalTime[name] || 0;
|
| const avgTime = calls > 0 ? (totalTime / calls).toFixed(2) : 0;
|
| const errors = this.metrics.errors[name] || 0;
|
|
|
| report.push({
|
| runnable: name,
|
| calls,
|
| avgTime: `${avgTime}ms`,
|
| totalTime: `${totalTime}ms`,
|
| errors
|
| });
|
| }
|
|
|
| return report;
|
| }
|
|
|
| reset() {
|
| this.metrics = {calls: {}, totalTime: {}, errors: {}};
|
| this.startTimes.clear();
|
| }
|
| }
|
|
|
| |
| |
|
|
| export class FileCallback extends BaseCallback {
|
| constructor(filename) {
|
| super();
|
| this.filename = filename;
|
| this.logs = [];
|
| }
|
|
|
| async onStart(runnable, input, config) {
|
| this.logs.push({
|
| timestamp: new Date().toISOString(),
|
| event: 'start',
|
| runnable: runnable.name,
|
| input: this._serialize(input)
|
| });
|
| }
|
|
|
| async onEnd(runnable, output, config) {
|
| this.logs.push({
|
| timestamp: new Date().toISOString(),
|
| event: 'end',
|
| runnable: runnable.name,
|
| output: this._serialize(output)
|
| });
|
| }
|
|
|
| async onError(runnable, error, config) {
|
| this.logs.push({
|
| timestamp: new Date().toISOString(),
|
| event: 'error',
|
| runnable: runnable.name,
|
| error: error.message
|
| });
|
| }
|
|
|
| async flush() {
|
| const fs = await import('fs/promises');
|
| await fs.writeFile(
|
| this.filename,
|
| JSON.stringify(this.logs, null, 2),
|
| 'utf-8'
|
| );
|
| this.logs = [];
|
| }
|
|
|
| _serialize(value) {
|
| if (typeof value === 'string') return value;
|
| if (value?.content) return value.content;
|
| return JSON.stringify(value);
|
| }
|
| } |