| | |
| | |
| |
|
| | 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);
|
| | }
|
| | } |