Jido.Discovery (Jido v1.2.0)

View Source

The Discovery module is Jido's component registry system, providing efficient caching and lookup of system components like Actions, Sensors, Agents, and Skills. Think of it as a "service registry" that helps different parts of your system find and interact with each other.

Core Concepts

Component Discovery

Discovery works by scanning all loaded applications for components that implement Jido's metadata protocols. It automatically finds and indexes:

  • Actions - Discrete units of work
  • Sensors - Event monitoring components
  • Agents - Autonomous workers
  • Skills - Reusable capability packs
  • Demos - Example implementations - used in the Jido Workbench (https://github.com/agentjido/jido_workbench)

The module uses Erlang's :persistent_term for optimal lookup performance

Component Metadata

Each discovered component includes the following metadata:

%{
  module: MyApp.CoolAction,        # The actual module
  name: "cool_action",            # Human-readable name
  description: "Does cool stuff", # What it does
  slug: "abc123de",              # Unique identifier
  category: :utility,            # Broad classification
  tags: [:cool, :stuff]          # Searchable tags
}

Usage Examples

Basic Component Lookup

Find components by their unique slugs:

# Find a specific action
case Discovery.get_action_by_slug("abc123de") do
  %{module: module} ->
    # Use the action module
    {:ok, result} = module.run()
  nil ->
    # Handle missing action
end

# Find a sensor
sensor = Discovery.get_sensor_by_slug("def456gh")

Filtered Component Lists

Get filtered lists of components:

# List all monitoring sensors
sensors = Discovery.list_sensors(
  category: :monitoring,
  tag: :metrics
)

# Get the first 10 utility actions
actions = Discovery.list_actions(
  category: :utility,
  limit: 10
)

# Search agents by name
agents = Discovery.list_agents(
  name: "processor",
  offset: 5,
  limit: 5
)

Cache Management

Control the discovery cache lifecycle:

# Initialize cache (usually done at startup)
:ok = Discovery.init()

# Force cache refresh if needed
:ok = Discovery.refresh()

# Check last update time
{:ok, last_updated} = Discovery.last_updated()

Component Registration

Components are automatically discovered when they:

  1. Are loaded in any application in the system
  2. Implement the appropriate metadata callback
  3. Include required metadata fields

Example component:

defmodule MyApp.CoolAction do
  use Jido.Action,
    name: "cool_action",
    description: "Does cool stuff",
    category: :utility,
    tags: [:cool, :stuff]

  # Metadata is automatically generated from use params
  # def __action_metadata__ do
  #   %{
  #     name: "cool_action",
  #     description: "Does cool stuff",
  #     category: :utility,
  #     tags: [:cool, :stuff]
  #   }
  # end
end

Cache Structure

The discovery cache maintains separate collections for each component type:

%{
  version: "1.0",              # Cache format version
  last_updated: ~U[...],       # Last refresh timestamp
  actions: [...],              # List of actions
  sensors: [...],              # List of sensors
  agents: [...],               # List of agents
  skills: [...],              # List of skills
  demos: [...]                # List of demos
}

Filtering Options

All list functions support these filters:

  • :limit - Maximum results to return
  • :offset - Results to skip (pagination)
  • :name - Filter by name (partial match)
  • :description - Filter by description (partial match)
  • :category - Filter by category (exact match)
  • :tag - Filter by tag (must have exact tag)

Important Notes

  • Cache is shared across all processes
  • Components must be loaded before discovery
  • Metadata changes require cache refresh
  • Slug generation is deterministic
  • Filter options are additive (AND logic)

See Also

Summary

Functions

Retrieves an Action by its slug.

Retrieves an Agent by its slug.

Retrieves a Demo by its slug.

Retrieves a Sensor by its slug.

Retrieves a Skill by its slug.

Initializes the discovery cache. Should be called during application startup.

Gets the last time the cache was updated.

Lists all Actions with optional filtering and pagination.

Lists all Agents with optional filtering and pagination.

Lists all Demos with optional filtering and pagination.

Lists all Sensors with optional filtering and pagination.

Lists all Skills with optional filtering and pagination.

Forces a refresh of the discovery cache.

Types

cache_entry()

@type cache_entry() :: %{
  version: String.t(),
  last_updated: DateTime.t(),
  actions: [component_metadata()],
  sensors: [component_metadata()],
  agents: [component_metadata()],
  skills: [component_metadata()],
  demos: [component_metadata()]
}

component_metadata()

@type component_metadata() :: %{
  module: module(),
  name: String.t(),
  description: String.t(),
  slug: String.t(),
  category: atom() | nil,
  tags: [atom()] | nil
}

component_type()

@type component_type() :: :action | :sensor | :agent | :skill | :demo

Functions

get_action_by_slug(slug)

@spec get_action_by_slug(String.t()) :: component_metadata() | nil

Retrieves an Action by its slug.

Parameters

  • slug: A string representing the unique identifier of the Action.

Returns

The Action metadata if found, otherwise nil.

Examples

iex> Jido.get_action_by_slug("abc123de")
%{module: MyApp.SomeAction, name: "some_action", description: "Does something", slug: "abc123de"}

iex> Jido.get_action_by_slug("nonexistent")
nil

get_agent_by_slug(slug)

@spec get_agent_by_slug(String.t()) :: component_metadata() | nil

Retrieves an Agent by its slug.

Parameters

  • slug: A string representing the unique identifier of the Agent.

Returns

The Agent metadata if found, otherwise nil.

Examples

iex> Jido.get_agent_by_slug("ghi789jk")
%{module: MyApp.SomeAgent, name: "some_agent", description: "Represents an agent", slug: "ghi789jk"}

iex> Jido.get_agent_by_slug("nonexistent")
nil

get_demo_by_slug(slug)

@spec get_demo_by_slug(String.t()) :: component_metadata() | nil

Retrieves a Demo by its slug.

Parameters

  • slug: A string representing the unique identifier of the Demo.

Returns

The Demo metadata if found, otherwise nil.

Examples

iex> Jido.get_demo_by_slug("mno345pq")
%{module: MyApp.SomeDemo, name: "some_demo", description: "Demonstrates something", slug: "mno345pq"}

iex> Jido.get_demo_by_slug("nonexistent")
nil

get_sensor_by_slug(slug)

@spec get_sensor_by_slug(String.t()) :: component_metadata() | nil

Retrieves a Sensor by its slug.

Parameters

  • slug: A string representing the unique identifier of the Sensor.

Returns

The Sensor metadata if found, otherwise nil.

Examples

iex> Jido.get_sensor_by_slug("def456gh")
%{module: MyApp.SomeSensor, name: "some_sensor", description: "Monitors something", slug: "def456gh"}

iex> Jido.get_sensor_by_slug("nonexistent")
nil

get_skill_by_slug(slug)

@spec get_skill_by_slug(String.t()) :: component_metadata() | nil

Retrieves a Skill by its slug.

Parameters

  • slug: A string representing the unique identifier of the Skill.

Returns

The Skill metadata if found, otherwise nil.

Examples

iex> Jido.get_skill_by_slug("jkl012mn")
%{module: MyApp.SomeSkill, name: "some_skill", description: "Provides some capability", slug: "jkl012mn"}

iex> Jido.get_skill_by_slug("nonexistent")
nil

init()

@spec init() :: :ok | {:error, term()}

Initializes the discovery cache. Should be called during application startup.

Returns

  • :ok if cache was initialized successfully
  • {:error, reason} if initialization failed

last_updated()

@spec last_updated() :: {:ok, DateTime.t()} | {:error, :not_initialized}

Gets the last time the cache was updated.

Returns

  • {:ok, datetime} with the last update time
  • {:error, :not_initialized} if cache hasn't been initialized

list_actions(opts \\ [])

@spec list_actions(keyword()) :: [component_metadata()]

Lists all Actions with optional filtering and pagination.

Parameters

  • opts: A keyword list of options for filtering and pagination. Available options:
    • :limit: Maximum number of results to return.
    • :offset: Number of results to skip before starting to return.
    • :name: Filter Actions by name (partial match).
    • :description: Filter Actions by description (partial match).
    • :category: Filter Actions by category (exact match).
    • :tag: Filter Actions by tag (must have the exact tag).

Returns

A list of Action metadata.

Examples

iex> Jido.list_actions(limit: 10, offset: 5, category: :utility)
[%{module: MyApp.SomeAction, name: "some_action", description: "Does something", slug: "abc123de", category: :utility}]

list_agents(opts \\ [])

@spec list_agents(keyword()) :: [component_metadata()]

Lists all Agents with optional filtering and pagination.

Parameters

  • opts: A keyword list of options for filtering and pagination. Available options:
    • :limit: Maximum number of results to return.
    • :offset: Number of results to skip before starting to return.
    • :name: Filter Agents by name (partial match).
    • :description: Filter Agents by description (partial match).
    • :category: Filter Agents by category (exact match).
    • :tag: Filter Agents by tag (must have the exact tag).

Returns

A list of Agent metadata.

Examples

iex> Jido.list_agents(limit: 10, offset: 5, category: :business)
[%{module: MyApp.SomeAgent, name: "some_agent", description: "Represents an agent", slug: "ghi789jk", category: :business}]

list_demos(opts \\ [])

@spec list_demos(keyword()) :: [component_metadata()]

Lists all Demos with optional filtering and pagination.

Parameters

  • opts: A keyword list of options for filtering and pagination. Available options:
    • :limit: Maximum number of results to return.
    • :offset: Number of results to skip before starting to return.
    • :name: Filter Demos by name (partial match).
    • :description: Filter Demos by description (partial match).
    • :category: Filter Demos by category (exact match).
    • :tag: Filter Demos by tag (must have the exact tag).

Returns

A list of Demo metadata.

Examples

iex> Jido.list_demos(limit: 10, offset: 5, category: :example)
[%{module: MyApp.SomeDemo, name: "some_demo", description: "Demonstrates something", slug: "mno345pq", category: :example}]

list_sensors(opts \\ [])

@spec list_sensors(keyword()) :: [component_metadata()]

Lists all Sensors with optional filtering and pagination.

Parameters

  • opts: A keyword list of options for filtering and pagination. Available options:
    • :limit: Maximum number of results to return.
    • :offset: Number of results to skip before starting to return.
    • :name: Filter Sensors by name (partial match).
    • :description: Filter Sensors by description (partial match).
    • :category: Filter Sensors by category (exact match).
    • :tag: Filter Sensors by tag (must have the exact tag).

Returns

A list of Sensor metadata.

Examples

iex> Jido.list_sensors(limit: 10, offset: 5, category: :monitoring)
[%{module: MyApp.SomeSensor, name: "some_sensor", description: "Monitors something", slug: "def456gh", category: :monitoring}]

list_skills(opts \\ [])

@spec list_skills(keyword()) :: [component_metadata()]

Lists all Skills with optional filtering and pagination.

Parameters

  • opts: A keyword list of options for filtering and pagination. Available options:
    • :limit: Maximum number of results to return.
    • :offset: Number of results to skip before starting to return.
    • :name: Filter Skills by name (partial match).
    • :description: Filter Skills by description (partial match).
    • :category: Filter Skills by category (exact match).
    • :tag: Filter Skills by tag (must have the exact tag).

Returns

A list of Skill metadata.

Examples

iex> Jido.list_skills(limit: 10, offset: 5, category: :capability)
[%{module: MyApp.SomeSkill, name: "some_skill", description: "Provides some capability", slug: "jkl012mn", category: :capability}]

refresh()

@spec refresh() :: :ok | {:error, term()}

Forces a refresh of the discovery cache.

Returns

  • :ok if cache was refreshed successfully
  • {:error, reason} if refresh failed