Skip to main content
Hooks intercept and modify agent behavior at various lifecycle points - before/after tools, agents, and LLM calls.

Quick Start

1

Create a Hook with HookRegistry

use praisonai::{HookRegistry, HookEvent, HookInput, HookResult};

let mut registry = HookRegistry::new();

// Add a logging hook
registry.add_hook(HookEvent::BeforeTool, |input: &HookInput| {
    println!("[{}] Event: {:?}", input.session_id, input.event);
    HookResult::allow()
});
2

Block Tool Execution

use praisonai::{HookRegistry, HookEvent, HookInput, HookResult};

let mut registry = HookRegistry::new();

// Block dangerous tools
registry.add_hook(HookEvent::BeforeTool, |input: &HookInput| {
    if let Some(tool) = &input.tool_name {
        if tool == "delete_file" {
            return HookResult::deny("Dangerous tool blocked");
        }
    }
    HookResult::allow()
});
3

Hook with Matcher

use praisonai::{HookRegistry, HookEvent, HookInput, HookResult};

let mut registry = HookRegistry::new();

// Only match file-related tools
registry.add_hook_with_matcher(
    HookEvent::BeforeTool,
    "file_*",  // Glob pattern
    |input: &HookInput| {
        println!("File operation: {:?}", input.tool_name);
        HookResult::allow()
    }
);

User Interaction Flow


HookEvent

Events that hooks can subscribe to.
pub enum HookEvent {
    BeforeTool,
    AfterTool,
    BeforeAgent,
    AfterAgent,
    BeforeLLM,
    AfterLLM,
    SessionStart,
    SessionEnd,
    OnError,
    OnRetry,
    OnInit,
    OnShutdown,
}
EventDescription
BeforeToolBefore tool execution
AfterToolAfter tool execution
BeforeAgentBefore agent processes message
AfterAgentAfter agent processes message
BeforeLLMBefore LLM call
AfterLLMAfter LLM call
SessionStartNew session begins
SessionEndSession ends
OnErrorError occurred
OnRetryRetry triggered
OnInitInitialization
OnShutdownShutdown

HookInput

Data provided to hooks.
pub struct HookInput {
    pub event: HookEvent,
    pub session_id: String,
    pub agent_name: Option<String>,
    pub tool_name: Option<String>,
    pub tool_args: Option<Value>,
    pub message: Option<String>,
    pub error: Option<String>,
    pub extra: HashMap<String, Value>,
}

Builder Methods

MethodSignatureDescription
new(event, session_id)fn new(HookEvent, impl Into<String>) -> SelfCreate input
with_agent(name)fn with_agent(self, impl Into<String>) -> SelfSet agent name
with_tool(name, args)fn with_tool(self, impl Into<String>, Value) -> SelfSet tool info
with_message(msg)fn with_message(self, impl Into<String>) -> SelfSet message
with_error(err)fn with_error(self, impl Into<String>) -> SelfSet error
with_extra(key, value)fn with_extra(self, impl Into<String>, Value) -> SelfAdd extra data

HookResult

Result returned from hook execution.
pub struct HookResult {
    pub decision: HookDecision,
    pub reason: Option<String>,
    pub modified_input: Option<HashMap<String, Value>>,
    pub additional_context: Option<String>,
    pub suppress_output: bool,
}

Factory Methods

MethodSignatureDescription
allow()fn allow() -> SelfAllow execution
allow_with_reason(r)fn allow_with_reason(impl Into<String>) -> SelfAllow with reason
deny(reason)fn deny(impl Into<String>) -> SelfDeny execution
block(reason)fn block(impl Into<String>) -> SelfBlock (stronger deny)
ask(reason)fn ask(impl Into<String>) -> SelfAsk for confirmation

Instance Methods

MethodSignatureDescription
is_allowed()fn is_allowed(&self) -> boolCheck if allowed
is_denied()fn is_denied(&self) -> boolCheck if denied
with_modified_input(input)fn with_modified_input(self, HashMap) -> SelfModify inputs
with_context(ctx)fn with_context(self, impl Into<String>) -> SelfAdd context
suppress()fn suppress(self) -> SelfSuppress output

HookDecision

Decision types for hook outputs.
pub enum HookDecision {
    Allow,   // default - Allow operation
    Deny,    // Deny operation
    Block,   // Block (stronger than deny)
    Ask,     // Ask for user confirmation
}

HookDefinition

Define individual hooks with functions and metadata.
pub struct HookDefinition {
    pub id: String,
    pub event: HookEvent,
    pub matcher: Option<String>,
    pub func: HookFn,
    pub enabled: bool,
    pub name: Option<String>,
}

Methods

MethodSignatureDescription
new(event, func)fn new(HookEvent, impl Fn(&HookInput) -> HookResult) -> SelfCreate definition
with_matcher(pattern)fn with_matcher(self, impl Into<String>) -> SelfSet matcher pattern
with_name(name)fn with_name(self, impl Into<String>) -> SelfSet name
matches(target)fn matches(&self, &str) -> boolCheck if matches target
execute(input)fn execute(&self, &HookInput) -> HookResultExecute hook

HookRegistry

Manages a collection of hooks.
pub struct HookRegistry {
    hooks: HashMap<HookEvent, Vec<HookDefinition>>,
}

Methods

MethodSignatureDescription
new()fn new() -> SelfCreate empty registry
add_hook(event, func)fn add_hook(&mut self, HookEvent, impl Fn) -> &mut SelfAdd a hook
add_hook_with_matcher(event, matcher, func)fn add_hook_with_matcher(&mut self, HookEvent, impl Into<String>, impl Fn) -> &mut SelfAdd hook with matcher
add_definition(hook)fn add_definition(&mut self, HookDefinition) -> &mut SelfAdd hook definition
remove_hook(id)fn remove_hook(&mut self, &str) -> boolRemove hook by ID
enable_hook(id)fn enable_hook(&mut self, &str) -> boolEnable hook
disable_hook(id)fn disable_hook(&mut self, &str) -> boolDisable hook

Common Patterns

Rate Limiter Hook

use praisonai::{HookRegistry, HookEvent, HookInput, HookResult};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

let counter = Arc::new(AtomicUsize::new(0));
let max_calls = 100;

let mut registry = HookRegistry::new();

let counter_clone = counter.clone();
registry.add_hook(HookEvent::BeforeTool, move |_input: &HookInput| {
    let count = counter_clone.fetch_add(1, Ordering::SeqCst);
    if count >= max_calls {
        HookResult::deny("Rate limit exceeded")
    } else {
        HookResult::allow()
    }
});

Best Practices

Only subscribe to events you need - reduces overhead and improves performance.
Hooks with higher priority run first. Use for security checks before logging.
Hooks run in the hot path - avoid expensive operations.
When denying, provide actionable error messages.