Email / tutorial /01-foundation /04-context /solutions /16-runtime-config-solution.js
lenzcom's picture
Upload folder using huggingface_hub
e706de2 verified
/**
* Exercise 16: Runtime Configuration Override
*
* Goal: Learn to override settings at runtime using config.configurable
*
* In this exercise, you'll:
* 1. Build a Runnable that reads from config.configurable
* 2. Override default settings at runtime
* 3. Test different configurations without changing code
* 4. Understand A/B testing with configs
*
* This is how you change LLM temperature, max tokens, etc. at runtime!
*/
import { RunnableConfig } from '../../../../src//core/context.js';
import {Runnable} from '../../../../src/index.js';
import {BaseCallback} from '../../../../src/utils/callbacks.js';
class TextProcessorRunnable extends Runnable {
constructor(options = {}) {
super();
// Set defaults
this.defaultMaxLength = options.maxLength ?? 50;
this.defaultUppercase = options.uppercase ?? false;
this.defaultPrefix = options.prefix ?? '';
}
async _call(input, config) {
const maxLength = config.configurable?.maxLength ?? this.defaultMaxLength;
const uppercase = config.configurable?.uppercase ?? this.defaultUppercase;
const prefix = config.configurable?.prefix ?? this.defaultPrefix;
// Process text
let result = input;
// Apply prefix
if (prefix) {
result = prefix + result;
}
// Apply uppercase
if (uppercase) {
result = result.toUpperCase();
}
// Apply truncation
if (result.length > maxLength) {
result = result.substring(0, maxLength) + '...';
}
return result;
}
}
// Callback to show what config was used
class ConfigLoggerCallback extends BaseCallback {
async onStart(runnable, input, config) {
if (config.configurable && Object.keys(config.configurable).length > 0) {
console.log(`πŸ“‹ Runtime config:`, config.configurable);
}
}
}
async function exercise() {
console.log('=== Exercise 16: Runtime Configuration Override ===\n');
const processor = new TextProcessorRunnable({ maxLength: 50 });
const logger = new ConfigLoggerCallback();
const longText = "The quick brown fox jumps over the lazy dog. This is a longer sentence to test truncation and various configuration options.";
// Test 1: Use defaults
console.log('--- Test 1: Using Defaults ---');
const result1 = await processor.invoke(longText, { callbacks: [logger] });
console.log('Result:', result1);
console.log();
// Test 2: Override maxLength
console.log('--- Test 2: Override maxLength ---');
const result2 = await processor.invoke(longText, {
callbacks: [logger],
configurable: { maxLength: 20 }
});
console.log('Result:', result2);
console.log();
// Test 3: Override multiple settings
console.log('--- Test 3: Override Multiple Settings ---');
const result3 = await processor.invoke(longText, {
callbacks: [logger],
configurable: { uppercase: true, maxLength: 30 }
});
console.log('Result:', result3);
console.log();
// Test 4: Add prefix at runtime
console.log('--- Test 4: Add Prefix at Runtime ---');
const result4 = await processor.invoke(longText, {
callbacks: [logger],
configurable: { prefix: '[PREFIX] ', maxLength: 40 }
});
console.log('Result:', result4);
console.log();
// Test 5: A/B Testing scenario
console.log('--- Test 5: A/B Testing Different Configs ---');
const configA = new RunnableConfig({
callbacks: [logger],
configurable: { maxLength: 25, uppercase: false },
metadata: { variant: 'A', experiment: 'text-processing' }
});
const configB = new RunnableConfig({
callbacks: [logger],
configurable: { maxLength: 40, uppercase: true },
metadata: { variant: 'B', experiment: 'text-processing' }
});
const testText = "Testing A/B configuration variants";
const resultA = await processor.invoke(testText, configA);
const resultB = await processor.invoke(testText, configB);
console.log('Variant A:', resultA);
console.log('Variant B:', resultB);
console.log();
// Test 6: Simulating LLM-style configuration
console.log('--- Test 6: LLM-Style Temperature Override ---');
// Create a mock LLM runnable
class MockLLMRunnable extends Runnable {
constructor(defaultTemp = 0.7) {
super();
this.defaultTemperature = defaultTemp;
}
async _call(input, config) {
const temperature = config.configurable?.temperature ?? this.defaultTemperature;
// Simulate different outputs based on temperature
if (temperature < 0.3) {
return `[temp=${temperature}] Deterministic response: ${input}`;
} else if (temperature > 0.8) {
return `[temp=${temperature}] Creative response about ${input}!!!`;
} else {
return `[temp=${temperature}] Balanced response: ${input}.`;
}
}
}
const llm = new MockLLMRunnable();
console.log('Low temp (0.1):');
const low = await llm.invoke("AI", { configurable: { temperature: 0.1 } });
console.log(low);
console.log('\nMedium temp (0.7):');
const med = await llm.invoke("AI", { configurable: { temperature: 0.7 } });
console.log(med);
console.log('\nHigh temp (1.0):');
const high = await llm.invoke("AI", { configurable: { temperature: 1.0 } });
console.log(high);
console.log('\nβœ“ Exercise 16 complete!');
}
// Run the exercise
exercise().catch(console.error);
/**
* Expected Output:
*
* --- Test 1: Using Defaults ---
* Result: The quick brown fox jumps over the lazy dog. Thi...
*
* --- Test 2: Override maxLength ---
* πŸ“‹ Runtime config: { maxLength: 20 }
* Result: The quick brown fox ...
*
* --- Test 3: Override Multiple Settings ---
* πŸ“‹ Runtime config: { uppercase: true, maxLength: 30 }
* Result: THE QUICK BROWN FOX JUMPS O...
*
* --- Test 4: Add Prefix at Runtime ---
* πŸ“‹ Runtime config: { prefix: '[PREFIX] ', maxLength: 40 }
* Result: [PREFIX] The quick brown fox jumps ove...
*
* --- Test 5: A/B Testing Different Configs ---
* πŸ“‹ Runtime config: { maxLength: 25, uppercase: false }
* Variant A: Testing A/B configurati...
* πŸ“‹ Runtime config: { maxLength: 40, uppercase: true }
* Variant B: TESTING A/B CONFIGURATION VARIANTS
*
* --- Test 6: LLM-Style Temperature Override ---
* Low temp (0.1):
* [temp=0.1] Deterministic response: AI
*
* Medium temp (0.7):
* [temp=0.7] Balanced response: AI.
*
* High temp (1.0):
* [temp=1.0] Creative response about AI!!!
*
* Learning Points:
* 1. config.configurable holds runtime overrides
* 2. Use ?? operator for defaults: config.configurable?.key ?? default
* 3. Don't modify instance defaults - just use config value
* 4. Perfect for A/B testing different settings
* 5. This is how LLMs change temperature/maxTokens at runtime
* 6. Combine with metadata to track which config was used
*/