lenzcom's picture
Upload folder using huggingface_hub
e706de2 verified
/**
* Exercise 15: Config Merging and Child Configs
*
* Goal: Understand how configs inherit and merge
*
* In this exercise, you'll:
* 1. Create parent and child configs
* 2. See how child configs inherit from parents
* 3. Understand callback accumulation
* 4. Learn when to use config.merge() vs config.child()
*
* This is crucial for nested Runnable calls!
*/
import {RunnableConfig} from '../../../../src/core/context.js';
import {Runnable} from '../../../../src/index.js';
import {BaseCallback} from '../../../../src/utils/callbacks.js';
// Simple callback to show when it's called
class TagLoggerCallback extends BaseCallback {
constructor(name) {
super();
this.name = name;
}
async onStart(runnable, input, config) {
console.log(`[${this.name}] Starting ${runnable.name}`);
console.log(` Tags: [${config.tags.join(', ')}]`);
console.log(` Callback count: ${config.callbacks.length}`);
}
async onEnd(runnable, output, config) {
console.log(`[${this.name}] Completed ${runnable.name}`);
}
}
// Test Runnables that create child configs
class Step1Runnable extends Runnable {
async _call(input, config) {
console.log('\n--- Inside Step1 ---');
// TODO: Create a child config with additional tag
const childConfig = null; // Use config.child({ tags: ['step1'] })
// TODO: Log how many callbacks childConfig has
console.log(`Step1 child has ___ callbacks`);
// Simulate nested work
return `Step1(${input})`;
}
}
class Step2Runnable extends Runnable {
async _call(input, config) {
console.log('\n--- Inside Step2 ---');
// TODO: Create a child config with different tag
const childConfig = null; // Use config.child({ tags: ['step2'] })
// TODO: Log how many callbacks childConfig has
console.log(`Step2 child has ___ callbacks`);
return `Step2(${input})`;
}
}
class Step3Runnable extends Runnable {
async _call(input, config) {
console.log('\n--- Inside Step3 ---');
// TODO: Create a child config with:
// - Additional tag: 'step3'
// - Additional metadata: { nested: true }
const childConfig = null; // Use config.child({ ... })
// TODO: Log the tags and metadata
console.log(`Step3 tags: [${childConfig.tags.join(', ')}]`);
console.log(`Step3 metadata:`, childConfig.metadata);
return `Step3(${input})`;
}
}
async function exercise() {
console.log('=== Exercise 15: Config Merging and Child Configs ===\n');
// Part 1: Basic config inheritance
console.log('--- Part 1: Basic Inheritance ---\n');
// TODO: Create parent config with one callback
const parentConfig = null; // new RunnableConfig({ ... })
// TODO: Create child config with additional callback
const childConfig = null; // parentConfig.child({ ... })
// TODO: Check what's in each config
console.log('Parent callbacks:', 0); // parentConfig.callbacks.length
console.log('Child callbacks:', 0); // childConfig.callbacks.length
console.log('Parent tags:', []); // parentConfig.tags
console.log('Child tags:', []); // childConfig.tags
// Part 2: Config in pipelines
console.log('\n--- Part 2: Config in Pipelines ---\n');
// TODO: Create a parent config with callbacks and tags
const pipelineConfig = null; // new RunnableConfig({ ... })
// TODO: Create pipeline
const step1 = new Step1Runnable();
const step2 = new Step2Runnable();
const pipeline = null; // step1.pipe(step2)
// TODO: Invoke pipeline with config
// await pipeline.invoke("test", pipelineConfig);
// Part 3: Multiple levels of nesting
console.log('\n--- Part 3: Multiple Nesting Levels ---\n');
const level1Config = new RunnableConfig({
callbacks: [new TagLoggerCallback('Level1')],
tags: ['level1'],
metadata: {level: 1}
});
// TODO: Create level2 config as child of level1
const level2Config = null; // level1Config.child({ ... })
// TODO: Create level3 config as child of level2
const level3Config = null; // level2Config.child({ ... })
// TODO: Check the accumulation
console.log('Level 1 - Callbacks:', 0, 'Tags:', []); // level1Config
console.log('Level 2 - Callbacks:', 0, 'Tags:', []); // level2Config
console.log('Level 3 - Callbacks:', 0, 'Tags:', []); // level3Config
// Part 4: merge() vs child()
console.log('\n--- Part 4: merge() vs child() ---\n');
const configA = new RunnableConfig({
tags: ['a'],
metadata: {source: 'A'}
});
const configB = new RunnableConfig({
tags: ['b'],
metadata: {source: 'B', extra: 'data'}
});
// TODO: Use merge() - combines two configs as equals
const merged = null; // configA.merge(configB)
// TODO: Use child() - B inherits from A
const child = null; // configA.child({ tags: ['b'], metadata: { extra: 'data' } })
// TODO: Compare results
console.log('Merged metadata:', {}); // merged.metadata
console.log('Child metadata:', {}); // child.metadata
console.log('\n✓ Exercise 3 complete!');
}
// Run the exercise
exercise().catch(console.error);
/**
* Expected Output Snippets:
*
* --- Part 1: Basic Inheritance ---
* Parent callbacks: 1
* Child callbacks: 2
* Parent tags: ['base']
* Child tags: ['base', 'child']
*
* --- Part 2: Config in Pipelines ---
* [Parent] Starting Step1Runnable
* Tags: [base]
* Callback count: 1
*
* --- Inside Step1 ---
* Step1 child has 1 callbacks
* [Parent] Completed Step1Runnable
*
* --- Part 3: Multiple Nesting Levels ---
* Level 1 - Callbacks: 1, Tags: ['level1']
* Level 2 - Callbacks: 2, Tags: ['level1', 'level2']
* Level 3 - Callbacks: 3, Tags: ['level1', 'level2', 'level3']
*
* --- Part 4: merge() vs child() ---
* Merged metadata: { source: 'B', extra: 'data' }
* Child metadata: { source: 'A', extra: 'data' }
*
* Learning Points:
* 1. child() creates a new config inheriting from parent
* 2. Callbacks accumulate (child has parent's + its own)
* 3. Tags accumulate (arrays concatenate)
* 4. Metadata merges (child overrides parent keys)
* 5. merge() treats both configs equally
* 6. child() treats parent as base, child as override
*/