Skip to Content
🎉 Welcome to handit.ai Documentation!
TracingTracing GuideNode Wrapper/Decorator

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()

node_wrappers.js
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 CaseWhy Node Decorators
Individual function optimizationFocus on specific components without affecting the entire workflow
Component-level monitoringTrack performance and behavior of key functions independently
Gradual tracing implementationAdd tracing incrementally to existing codebases without major refactoring
Legacy code integrationIntegrate with existing systems without disrupting current architecture
Function-specific error handlingImplement targeted error recovery and monitoring for critical functions

Implementation Guidelines:

  1. Use descriptive node IDs - Choose clear, meaningful identifiers that match your system architecture
  2. Keep decorators lightweight - Minimize overhead by focusing on essential tracking data
  3. Combine with circuit breakers - Add resilience patterns for external dependencies and unreliable services
  4. Implement proper error boundaries - Handle tracing failures gracefully without affecting business logic
  5. 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

Last updated on