Packages
nous
0.15.2
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/tool/registry.ex
defmodule Nous.Tool.Registry do
# Tag/category filtering uses MapSet.disjoint?/2 with sets built via
# capture syntax inside Enum.filter/2; same dialyzer false-positive as
# Nous.Workflow.Engine.
@dialyzer :no_opaque
@moduledoc """
Stateless helpers for building and querying tool collections.
Provides functions to create `Nous.Tool` structs from behaviour modules,
filter by category or tags, and look up tools by name.
## Architecture
The registry is a pure-functional module operating on plain lists of
`Nous.Tool.t()` structs. No GenServer or ETS — just pass a list in,
get a list out.
## Quick Start
tools = Nous.Tool.Registry.from_modules([
MyApp.Tools.FileRead,
MyApp.Tools.FileWrite,
MyApp.Tools.GitStatus
])
# Filter to read-only tools
read_tools = Nous.Tool.Registry.filter(tools, category: :read)
# Filter by tag
git_tools = Nous.Tool.Registry.filter(tools, tags: [:git])
# Look up by name
{:ok, tool} = Nous.Tool.Registry.lookup(tools, "file_read")
"""
alias Nous.Tool
@doc """
Build a list of tools from behaviour modules.
Each module must implement `Nous.Tool.Behaviour`. Options are passed
through to `Nous.Tool.from_module/2` for each module.
## Options
* Any option accepted by `Nous.Tool.from_module/2` (applied to all modules)
## Examples
tools = Registry.from_modules([MyApp.Tools.Search, MyApp.Tools.FileRead])
tools = Registry.from_modules([MyApp.Tools.Search], timeout: 60_000)
"""
@spec from_modules([module()], keyword()) :: [Tool.t()]
def from_modules(modules, opts \\ []) when is_list(modules) do
Enum.map(modules, &Tool.from_module(&1, opts))
end
@doc """
Filter tools by category and/or tags.
When both `:category` and `:tags` are given, a tool must match the
category AND have at least one of the specified tags.
## Options
* `:category` - Filter to tools with this category
* `:tags` - Filter to tools having at least one of these tags
## Examples
# By category
Registry.filter(tools, category: :read)
# By tags (tool must have at least one matching tag)
Registry.filter(tools, tags: [:file, :git])
# Both (must match category AND have at least one tag)
Registry.filter(tools, category: :read, tags: [:file])
"""
@spec filter([Tool.t()], keyword()) :: [Tool.t()]
def filter(tools, opts) when is_list(tools) and is_list(opts) do
category = Keyword.get(opts, :category)
tags = Keyword.get(opts, :tags)
tools
|> filter_by_category(category)
|> filter_by_tags(tags)
end
@doc """
Look up a tool by name.
Returns `{:ok, tool}` if found, `{:error, :not_found}` otherwise.
## Examples
{:ok, tool} = Registry.lookup(tools, "file_read")
{:error, :not_found} = Registry.lookup(tools, "nonexistent")
"""
@spec lookup([Tool.t()], String.t()) :: {:ok, Tool.t()} | {:error, :not_found}
def lookup(tools, name) when is_list(tools) and is_binary(name) do
case Enum.find(tools, &(&1.name == name)) do
nil -> {:error, :not_found}
tool -> {:ok, tool}
end
end
# Private helpers
defp filter_by_category(tools, nil), do: tools
defp filter_by_category(tools, category), do: Enum.filter(tools, &(&1.category == category))
defp filter_by_tags(tools, nil), do: tools
defp filter_by_tags(tools, []), do: tools
defp filter_by_tags(tools, tags) when is_list(tags) do
tag_set = MapSet.new(tags)
Enum.filter(tools, fn tool ->
tool.tags |> MapSet.new() |> MapSet.disjoint?(tag_set) |> Kernel.not()
end)
end
end