Jido.AI.ToolAdapter (Jido AI v2.2.0)

Copy Markdown View Source

Adapts Jido Actions into ReqLLM.Tool structs for LLM consumption.

This module bridges Jido domain concepts (actions with schemas) to ReqLLM's tool representation.

Design

  • Schema-focused: Tools use a noop callback; Jido owns execution via Directive.ToolExec
  • Adapter pattern: Converts Jido.Action behaviour → ReqLLM.Tool struct
  • Single source of truth: All action→tool conversion goes through this module

Usage

# Convert action modules to ReqLLM tools
tools = Jido.AI.ToolAdapter.from_actions([
  MyApp.Actions.Calculator,
  MyApp.Actions.Search
])

# With options
tools = Jido.AI.ToolAdapter.from_actions(actions,
  prefix: "myapp_",
  filter: fn mod -> mod.category() == :search end
)

# Use in LLM call
ReqLLM.stream_text(model, messages, tools: tools)

Summary

Functions

Converts a single Jido.Action module into a ReqLLM.Tool struct.

Converts a list of Jido.Action modules into ReqLLM.Tool structs.

Looks up an action module by tool name from a list of action modules.

Normalizes tool input into an action lookup map (%{name => module}).

Validates that all modules in the list implement the Jido.Action behaviour.

Functions

from_action(action_module, opts \\ [])

@spec from_action(
  module(),
  keyword()
) :: ReqLLM.Tool.t()

Converts a single Jido.Action module into a ReqLLM.Tool struct.

Arguments

  • action_module - A module implementing the Jido.Action behaviour
  • opts - Optional keyword list of options

Options

  • :prefix - String prefix to add to the tool name (e.g., "myapp_")
  • :strict - Whether to enable strict mode on the tool. When not set, auto-detects based on the action's strict?/0 callback (defaults to false).

Returns

A ReqLLM.Tool struct

Example

tool = Jido.AI.ToolAdapter.from_action(MyApp.Actions.Calculator, prefix: "v2_")
# => %ReqLLM.Tool{name: "v2_calculator", ...}

from_actions(action_modules, opts \\ [])

@spec from_actions(
  [module()],
  keyword()
) :: [ReqLLM.Tool.t()]

Converts a list of Jido.Action modules into ReqLLM.Tool structs.

The returned tools use a noop callback—they're purely for describing available actions to the LLM. Actual execution happens via Jido.AI.Directive.ToolExec.

Arguments

  • action_modules - List of modules implementing the Jido.Action behaviour
  • opts - Optional keyword list of options

Options

  • :prefix - String prefix to add to all tool names (e.g., "myapp_")
  • :filter - Function (module -> boolean) to filter which actions to include
  • :strict - Whether to enable strict mode on the tools. When not set, auto-detects based on each action's strict?/0 callback (defaults to false).

Returns

A list of ReqLLM.Tool structs

Examples

# Basic usage
tools = Jido.AI.ToolAdapter.from_actions([MyApp.Actions.Add, MyApp.Actions.Search])

# With prefix
tools = Jido.AI.ToolAdapter.from_actions(actions, prefix: "calc_")
# Tool names become "calc_add", "calc_search", etc.

# With filter
tools = Jido.AI.ToolAdapter.from_actions(actions,
  filter: fn mod -> mod.category() == :math end
)

lookup_action(tool_name, action_modules, opts \\ [])

@spec lookup_action(String.t(), [module()], keyword()) ::
  {:ok, module()} | {:error, :not_found}

Looks up an action module by tool name from a list of action modules.

Useful for finding which action module corresponds to a tool name returned by an LLM.

Arguments

  • tool_name - The name of the tool to look up
  • action_modules - List of action modules to search

Returns

  • {:ok, module} - If found
  • {:error, :not_found} - If no action module has that tool name

Example

{:ok, module} = ToolAdapter.lookup_action("calculator", [Calculator, Search])
# => {:ok, Calculator}

{:error, :not_found} = ToolAdapter.lookup_action("unknown", [Calculator])
# => {:error, :not_found}

to_action_map(tools)

@spec to_action_map(nil | map() | [module()] | module()) :: %{
  required(String.t()) => module()
}

Normalizes tool input into an action lookup map (%{name => module}).

Accepts any of the common tool container shapes used by actions/skills:

  • nil -> %{}
  • %{"tool_name" => MyAction} -> unchanged
  • %{tool_name: MyAction} -> %{"tool_name" => MyAction} when values are modules
  • [MyAction, OtherAction] -> %{"my_action" => MyAction, "other_action" => OtherAction}
  • MyAction -> %{"my_action" => MyAction}

validate_actions(action_modules)

@spec validate_actions([module()]) ::
  :ok | {:error, {:invalid_action, module(), atom()}}

Validates that all modules in the list implement the Jido.Action behaviour.

Returns :ok if all modules are valid, or {:error, {:invalid_action, module, reason}} for the first invalid module found.

Example

:ok = ToolAdapter.validate_actions([Calculator, Search])
{:error, {:invalid_action, BadModule, :missing_name}} = ToolAdapter.validate_actions([BadModule])