Node Wrapper/Decorator Tracing
The perfect balance between automation and control. Node wrappers and decorators automatically trace individual functions while giving you granular control over what gets tracked and how, making them ideal for component-level monitoring.
Perfect for tracing specific functions in your AI pipeline—like LLM calls, tool executions, or data processing steps—without needing to modify the entire workflow.
Node decorators are ideal for tracing specific functions in your AI pipeline - like LLM calls, tool executions, or data processing steps - without needing to modify the entire workflow.
How It Works
Node wrapper tracing provides function-level precision through:
- Function-level granularity - Trace specific functions without affecting others
- Automatic input/output capture - Records function parameters and return values
- Error handling - Captures exceptions with full context
- Performance tracking - Measures execution time and resource usage
- Composable - Combine multiple traced functions in complex workflows
Implementation
Python: @trace_agent_node()
JavaScript: traceAgentNode()
const { config, traceAgentNode } = require('handit-sdk');
config({ apiKey: 'your-api-key' });
// Example: LLM interaction tracing
const classifyUserIntent = traceAgentNode({
agentNodeId: 'llm-intent-classification',
callback: async (userMessage) => {
// Your LLM call logic here
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{ role: 'system', content: 'Classify user intent into: support, sales, technical' },
{ role: 'user', content: userMessage }
]
})
});
const result = await response.json();
return {
intent: result.choices[0].message.content.trim(),
confidence: 0.95,
modelUsed: 'gpt-4',
tokensUsed: result.usage.total_tokens
};
}
});
// Example: Knowledge base search tracing
const searchKnowledgeBase = traceAgentNode({
agentNodeId: 'knowledge-base-search',
callback: async (query, intentCategory) => {
// Your vector search logic here
const searchResults = await vectorDatabase.search({
query: query,
filters: { category: intentCategory },
topK: 5
});
return {
results: searchResults,
resultCount: searchResults.length,
searchQuality: calculateSearchQuality(searchResults)
};
}
});
// Example: Response generation tracing
const generateResponse = traceAgentNode({
agentNodeId: 'llm-response-generation',
callback: async (userMessage, intent, knowledgeContext) => {
const contextPrompt = `
User Intent: ${intent}
Knowledge Context: ${JSON.stringify(knowledgeContext)}
User Message: ${userMessage}
Generate a helpful response:
`;
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: contextPrompt }],
temperature: 0.7
})
});
const result = await response.json();
return {
response: result.choices[0].message.content,
tokensUsed: result.usage.total_tokens,
model: 'gpt-4'
};
}
});
// Example: Data validation tracing
const validateUserInput = traceAgentNode({
agentNodeId: 'data-validation',
callback: async (userData) => {
const validationResults = {
isValid: true,
errors: [],
warnings: []
};
// Email validation
if (!validateEmail(userData.email)) {
validationResults.errors.push('Invalid email format');
validationResults.isValid = false;
}
// Phone validation
if (!validatePhone(userData.phone)) {
validationResults.warnings.push('Phone number format unusual');
}
return validationResults;
}
});
// Example: API integration tracing
const fetchUserProfile = traceAgentNode({
agentNodeId: 'user-profile-api',
callback: async (userId) => {
const startTime = Date.now();
try {
const response = await fetch(`https://api.userservice.com/users/${userId}`, {
headers: {
'Authorization': `Bearer ${process.env.USER_API_KEY}`
}
});
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
const userProfile = await response.json();
return {
profile: userProfile,
fetchTime: Date.now() - startTime,
source: 'user-api',
cached: false
};
} catch (error) {
// Try cache fallback
const cachedProfile = await getCachedProfile(userId);
if (cachedProfile) {
return {
profile: cachedProfile,
fetchTime: Date.now() - startTime,
source: 'cache',
cached: true,
warning: 'API unavailable, using cached data'
};
}
throw error;
}
}
});
// Using traced functions in a complete workflow
async function completeCustomerServiceFlow(userMessage, userData) {
// Each function is automatically traced individually
const validation = await validateUserInput(userData);
if (!validation.isValid) {
return {
error: 'Invalid user data',
details: validation.errors
};
}
const userProfile = await fetchUserProfile(userData.userId);
const intent = await classifyUserIntent(userMessage);
const knowledge = await searchKnowledgeBase(userMessage, intent.intent);
const response = await generateResponse(userMessage, intent, knowledge);
return {
response: response.response,
intent: intent.intent,
userProfile: userProfile.profile,
validation: validation,
tokensUsed: response.tokensUsed,
processingComplete: true
};
}
// Helper functions
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function validatePhone(phone) {
const phoneRegex = /^\+?[\d\s\-\(\)]{10,}$/;
return phoneRegex.test(phone);
}
async function getCachedProfile(userId) {
// Your cache lookup logic
return { name: 'Cached User', id: userId };
}
function calculateSearchQuality(results) {
// Your quality calculation logic
return results.length > 0 ? 0.8 : 0.0;
}
Advanced Patterns
Conditional Tracing with Decorators
Error Recovery with Node Tracing
Best Practices
When to Use Node Decorators:
Use Case | Why Node Decorators |
---|---|
Individual function optimization | Focus on specific components without affecting the entire workflow |
Component-level monitoring | Track performance and behavior of key functions independently |
Gradual tracing implementation | Add tracing incrementally to existing codebases without major refactoring |
Legacy code integration | Integrate with existing systems without disrupting current architecture |
Function-specific error handling | Implement targeted error recovery and monitoring for critical functions |
Implementation Guidelines:
- Use descriptive node IDs - Choose clear, meaningful identifiers that match your system architecture
- Keep decorators lightweight - Minimize overhead by focusing on essential tracking data
- Combine with circuit breakers - Add resilience patterns for external dependencies and unreliable services
- Implement proper error boundaries - Handle tracing failures gracefully without affecting business logic
- Monitor performance impact - Balance tracing detail with system performance requirements
Node decorators provide the perfect balance between automation and control. They’re ideal for gradually adding tracing to existing codebases and focusing on specific components.
Be mindful of performance when using many decorators. Consider conditional tracing for non-critical functions or high-frequency operations.
Next Steps
- Explore Agent Wrapper/Decorator for complete workflow tracing
- Learn about Manual Agent Tracing for maximum control
- Check Node Function Tracing for programmatic approaches