Packages
nous
0.17.0
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/hook/registry.ex
defmodule Nous.Hook.Registry do
@moduledoc """
Storage and lookup for hooks, indexed by event type.
The registry maintains hooks sorted by priority within each event type.
It provides efficient lookup with optional tool name matching for
`:pre_tool_use` and `:post_tool_use` events.
## Example
registry = Nous.Hook.Registry.new()
|> Nous.Hook.Registry.register(%Nous.Hook{
event: :pre_tool_use,
matcher: "delete_file",
type: :function,
handler: fn _, _ -> :deny end
})
hooks = Nous.Hook.Registry.hooks_for(registry, :pre_tool_use, %{tool_name: "delete_file"})
"""
alias __MODULE__
alias Nous.Hook
@type t :: %Registry{
hooks: %{Hook.event() => [Hook.t()]}
}
defstruct hooks: %{}
@doc """
Create an empty registry.
"""
@spec new() :: t()
def new, do: %Registry{}
@doc """
Create a registry from a list of hooks.
"""
@spec from_hooks([Hook.t()]) :: t()
def from_hooks(hooks) when is_list(hooks) do
Enum.reduce(hooks, new(), ®ister(&2, &1))
end
@doc """
Register a hook in the registry.
Hooks are stored sorted by priority (lower = earlier execution).
"""
@spec register(t(), Hook.t()) :: t()
def register(%Registry{} = registry, %Hook{} = hook) do
hooks_for_event = Map.get(registry.hooks, hook.event, [])
updated =
[hook | hooks_for_event]
|> Enum.sort_by(& &1.priority)
%{registry | hooks: Map.put(registry.hooks, hook.event, updated)}
end
@doc """
Register multiple hooks at once.
"""
@spec register_all(t(), [Hook.t()]) :: t()
def register_all(%Registry{} = registry, hooks) when is_list(hooks) do
Enum.reduce(hooks, registry, ®ister(&2, &1))
end
@doc """
Get all hooks for a given event type.
"""
@spec hooks_for(t(), Hook.event()) :: [Hook.t()]
def hooks_for(%Registry{} = registry, event) do
Map.get(registry.hooks, event, [])
end
@doc """
Get hooks for a given event type that match the payload.
For `:pre_tool_use` and `:post_tool_use`, filters by tool name matching.
For other events, returns all hooks for that event.
"""
@spec hooks_for(t(), Hook.event(), map()) :: [Hook.t()]
def hooks_for(%Registry{} = registry, event, payload) do
registry
|> hooks_for(event)
|> Enum.filter(&Hook.matches?(&1, payload))
end
@doc """
Check if the registry has any hooks for a given event.
"""
@spec has_hooks?(t(), Hook.event()) :: boolean()
def has_hooks?(%Registry{} = registry, event) do
case Map.get(registry.hooks, event) do
nil -> false
[] -> false
_ -> true
end
end
@doc """
Return the total number of registered hooks.
"""
@spec count(t()) :: non_neg_integer()
def count(%Registry{} = registry) do
registry.hooks
|> Map.values()
|> Enum.map(&length/1)
|> Enum.sum()
end
end