File size: 3,825 Bytes
e706de2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**

 * Solution 13: Build a Simple Logging Callback

 *

 * Goal: Understand the basic callback lifecycle

 *

 * This is the foundation of observability in your framework!

 */

import {Runnable} from '../../../../src/index.js';
import {BaseCallback} from '../../../../src/utils/callbacks.js';

class SimpleLoggerCallback extends BaseCallback {
    constructor(options = {}) {
        super();
        this.showTimestamp = options.showTimestamp ?? false;
    }

    async onStart(runnable, input, config) {
        // Your code here
        console.log(`▶️  Starting: ${runnable.name}`)
        console.log(`   Input: ${input}`)
    }

    async onEnd(runnable, output, config) {
        // Your code here
        console.log(`✔️  Completed: ${runnable.name}`)
        console.log(`   Output: ${output}`)
    }

    async onError(runnable, error, config) {
        // Your code here
        console.log(` ❌ ${runnable.name}: ${error.message}`)
    }
}

// Test Runnables
class GreeterRunnable extends Runnable {
    async _call(input, config) {
        return `Hello, ${input}!`;
    }
}

class UpperCaseRunnable extends Runnable {
    async _call(input, config) {
        if (typeof input !== 'string') {
            throw new Error('Input must be a string');
        }
        return input.toUpperCase();
    }
}

class ErrorRunnable extends Runnable {
    async _call(input, config) {
        throw new Error('Intentional error for testing');
    }
}

async function exercise1() {
    console.log('=== Exercise 1: Simple Logging Callback ===\n');

    const logger = new SimpleLoggerCallback();
    const config = {
        callbacks: [logger]
    };

    // Test 1: Normal execution
    console.log('--- Test 1: Normal Execution ---');
    const greeter = new GreeterRunnable();
    const result1 = await greeter.invoke("World", config);
    console.log('Final result:', result1);
    console.log();

    // Test 2: Pipeline
    console.log('--- Test 2: Pipeline ---');
    const upper = new UpperCaseRunnable();
    const pipeline = greeter.pipe(upper);
    const result2 = await pipeline.invoke("claude", config);
    console.log('Final result:', result2);
    console.log();

    // Test 3: Error handling
    console.log('--- Test 3: Error Handling ---');
    const errorRunnable = new ErrorRunnable();
    try {
        await errorRunnable.invoke("test", config);
    } catch (error) {
        console.log('Caught error (expected):', error.message);
    }

    console.log('\n✓ Exercise 1 complete!');
}

// Run the exercise
exercise1().catch(console.error);

/**

 * Expected Output:

 *

 * --- Test 1: Normal Execution ---

 * ▶️ Starting: GreeterRunnable

 *    Input: World

 * ✔️ Completed: GreeterRunnable

 *    Output: Hello, World!

 * Final result: Hello, World!

 *

 * --- Test 2: Pipeline ---

 * ▶️  Starting: RunnableSequence

 *    Input: claude

 * ▶️  Starting: GreeterRunnable

 *    Input: claude

 * ✔️  Completed: GreeterRunnable

 *    Output: Hello, claude!

 * ▶️  Starting: UpperCaseRunnable

 *    Input: Hello, claude!

 * ✔️  Completed: UpperCaseRunnable

 *    Output: HELLO, CLAUDE!

 * ✔️  Completed: RunnableSequence

 *    Output: HELLO, CLAUDE!

 * Final result: HELLO, CLAUDE!

 *

 * --- Test 3: Error Handling ---

 * ▶️ Starting: ErrorRunnable

 *    Input: test

 * ❌ ErrorRunnable: Intentional error for testing

 * Caught error (expected): Intentional error for testing

 *

 * Learning Points:

 * 1. Callbacks see every step in execution

 * 2. onStart fires before _call()

 * 3. onEnd fires after successful _call()

 * 4. onError fires when _call() throws error

 * 5. Callbacks don't change the output - they just observe

 */