Hooks System Architecture
This document describes the architecture and design patterns of the Radium hooks system.
Overviewβ
The hooks system provides a framework for intercepting and customizing execution flow at various points. It uses an adapter pattern to integrate different hook types with a unified Hook trait, enabling type-safe and extensible behavior customization.
Core Componentsβ
Hook Registryβ
The HookRegistry is the central component that manages all hooks. It provides:
- Thread-Safe Storage: Uses
Arc<RwLock<Vec<Arc<dyn Hook>>>>for concurrent access - Priority-Based Execution: Hooks execute in priority order (higher priority first)
- Enable/Disable State: Tracks which hooks are enabled via
HashSet<String> - Type Filtering: Retrieves hooks by type for efficient execution
Key Design Decisions:
- Hooks are stored in a single vector and filtered by type during execution
- Enabled state is tracked separately to allow dynamic enable/disable
- Early termination: if a hook returns
should_continue = false, remaining hooks are skipped
Hook Traitβ
The core Hook trait provides a unified interface:
#[async_trait]
pub trait Hook: Send + Sync {
fn name(&self) -> &str;
fn priority(&self) -> HookPriority;
fn hook_type(&self) -> HookType;
async fn execute(&self, context: &HookContext) -> Result<HookExecutionResult>;
}
All hooks must implement this trait, enabling polymorphic execution through the registry.
Adapter Patternβ
The system uses adapters to convert specialized hook traits (ModelHook, ToolHook, ErrorHook) into the unified Hook trait:
- ModelHookAdapter: Converts
ModelHooktoHookfor before/after model calls - ToolHookAdapter: Converts
ToolHooktoHookfor tool execution hooks - ErrorHookAdapter: Converts
ErrorHooktoHookfor error handling hooks
This pattern provides:
- Type Safety: Each hook type has its own context structure
- Flexibility: Hooks can implement multiple hook types
- Unified Execution: All hooks execute through the same registry interface
Hook Typesβ
The system supports 13 hook types organized into four categories:
Model Hooks (2 types)β
BeforeModel: Execute before model callsAfterModel: Execute after model calls
Tool Hooks (3 types)β
BeforeTool: Execute before tool executionAfterTool: Execute after tool executionToolSelection: Execute during tool selection
Error Hooks (4 types)β
ErrorInterception: Intercept errors before propagationErrorTransformation: Transform error messagesErrorRecovery: Attempt error recoveryErrorLogging: Log errors with custom formatting
Telemetry Hooks (4 types)β
TelemetryCollection: Collect and aggregate telemetryCustomLogging: Custom logging hooksMetricsAggregation: Aggregate metricsPerformanceMonitoring: Monitor performance
Execution Flowβ
Hook Execution Processβ
- Registration: Hooks are registered with the registry via
register() - Enablement: Hooks are enabled by default, can be disabled via
set_enabled() - Execution: When a hook point is triggered:
- Registry retrieves all hooks of the specified type
- Hooks are filtered to only enabled ones
- Hooks are sorted by priority (descending)
- Each hook is executed sequentially
- If a hook returns
should_continue = false, execution stops - Results are collected and returned
Priority Systemβ
Hooks execute in priority order:
- Higher priority (200+): Critical hooks that must run first
- Medium priority (100-199): Standard hooks
- Low priority (<100): Optional hooks
Priority is set when creating a hook and cannot be changed after registration.
Error Handlingβ
The registry handles hook execution errors gracefully:
- If a hook execution fails, the error is logged
- An error result is added to the results
- Execution continues with remaining hooks
- This ensures one failing hook doesn't break the entire system
Context Systemβ
HookContextβ
The base context structure passed to all hooks:
pub struct HookContext {
pub hook_type: String,
pub data: serde_json::Value,
pub metadata: serde_json::Value,
}
Specialized Contextsβ
Each hook type has a specialized context:
- ModelHookContext: Contains input, model_id, response
- ToolHookContext: Contains tool_name, arguments, result
- ErrorHookContext: Contains error_message, error_type, error_source
- TelemetryHookContext: Contains event_type, data
These contexts convert to HookContext via to_hook_context() methods.
Result Systemβ
HookExecutionResultβ
Hooks return results that can:
- Continue execution:
HookResult::success() - Modify data:
HookResult::with_data(modified_data) - Stop execution:
HookResult::stop("reason") - Report error:
HookResult::error("message")
Data Modificationβ
Hooks can modify execution data by returning modified data in the result:
- Model hooks can modify input/response
- Tool hooks can modify arguments/results
- Error hooks can transform error messages
The registry collects modifications from all hooks and applies them.
Configuration Systemβ
HookConfigβ
Hooks can be configured via TOML files (.radium/hooks.toml):
[[hooks]]
name = "my-hook"
type = "before_model"
priority = 100
enabled = true
[hooks.config]
log_level = "info"
HookLoaderβ
The HookLoader discovers and loads hook configurations:
- Discovers hooks from extensions
- Loads workspace configurations
- Sets enable/disable state based on configuration
Note: For v1.0, hooks must be registered programmatically. Configuration controls enable/disable state. Dynamic library loading is deferred to v2.0.
Integration Pointsβ
OrchestratorHooksβ
Helper for executing hooks in the orchestrator (requires orchestrator-integration feature):
pub struct OrchestratorHooks {
registry: Arc<HookRegistry>,
}
Provides convenience methods for common hook execution patterns.
HookRegistryAdapterβ
Adapter that implements HookExecutor trait for use with AgentExecutor:
pub struct HookRegistryAdapter {
registry: Arc<HookRegistry>,
}
This adapter bridges the hooks system with the orchestrator without creating circular dependencies.
Thread Safetyβ
The entire hooks system is designed for concurrent access:
- Arc<RwLock<>>: Registry uses
Arc<RwLock>for thread-safe access - Send + Sync: All hooks must be
Send + Sync - Async Execution: All hook execution is async, enabling non-blocking operations
Performance Considerationsβ
- Lazy Filtering: Hooks are filtered by type only when needed
- Priority Sorting: Hooks are sorted once per execution
- Early Termination: Execution stops if a hook requests it
- Error Isolation: Hook failures don't affect other hooks
Extension Pointsβ
The system is designed for extensibility:
- New Hook Types: Add new
HookTypevariants - New Adapters: Create adapters for new hook categories
- Custom Loaders: Implement custom hook loading strategies
- Integration Helpers: Create helpers for specific integration points
Future Enhancementsβ
Planned enhancements (out of scope for v1.0):
- Hook Marketplace: Distribution platform for hooks
- Advanced Composition: Hook composition patterns
- Performance Optimization: Caching and optimization strategies
- Dynamic Loading: Runtime loading of hook libraries