Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Signal System Overview

guides/signals/overview.livemd

Signal System Overview

Introduction

Signals are the messaging system in Jido, serving as the nervous system of your agent-based applications. Based on the CloudEvents specification (v1.0.2), Signals provide a standardized way to represent events, commands, and state changes across your distributed system.

> ### Learn with Livebook {: .tip} > > This guide is available as a Livebook. The examples below can be run interactively. > Run in Livebook

Mix.install([
  {:jido, "~> 0.1.0"}
])

Core Concepts

Signals in Jido provide:

  • Standardized event structure (CloudEvents v1.0.2 compatible)
  • Rich metadata and context tracking
  • Built-in instruction handling
  • Flexible dispatch configuration
  • Automatic serialization

Basic Signal Structure

A Signal contains both CloudEvents standard fields and Jido-specific extensions:

# Create a basic signal
{:ok, signal} = Jido.Signal.new(%{
  type: "user.created",
  source: "/auth/registration",
  data: %{
    user_id: "123",
    email: "user@example.com"
  }
})

# Inspect the signal structure
IO.inspect(signal, label: "Basic Signal")

CloudEvents Standard Fields

Every Signal includes these CloudEvents fields:

  • specversion: Always “1.0.2”
  • id: Unique identifier (UUID v4)
  • source: Origin of the event (“/service/component”)
  • type: Classification of the event (“domain.entity.action”)
  • subject: Specific subject (optional)
  • time: ISO 8601 timestamp
  • datacontenttype: Media type of data
  • dataschema: Schema definition URL
  • data: Event payload

Signal Types and Naming

Signal types use a hierarchical dot notation:

..[.]

Examples:

# Standard event
{:ok, user_updated} = Jido.Signal.new(%{
  type: "user.profile.updated",
  source: "/users",
  data: %{user_id: "123", changes: %{name: "New Name"}}
})

# Event with qualifier
{:ok, payment_failed} = Jido.Signal.new(%{
  type: "order.payment.processed.failed",
  source: "/payments",
  data: %{order_id: "456", reason: "insufficient_funds"}
})

Best Practices for Signal Types

  1. Use lowercase with dots
  2. Keep segments meaningful
  3. Order from general to specific
  4. Include qualifiers when needed

Jido Extensions

Beyond CloudEvents, Signals include Jido-specific fields:

Instructions

Instructions tell agents what actions to take:

{:ok, task_signal} = Jido.Signal.new(%{
  type: "task.assigned",
  source: "/workflow",
  data: %{task_id: "789"},
  jido_instructions: [
    ValidateTask,
    {AssignTask, %{worker: "agent_1"}},
    NotifyAssignment
  ]
})

Dispatch Configuration

Control how signal responses are delivered:

# Single dispatch target
{:ok, metric_signal} = Jido.Signal.new(%{
  type: "metrics.collected",
  source: "/monitoring",
  data: %{cpu: 80, memory: 70},
  jido_dispatch: {:pubsub, [topic: "metrics"]}
})

# Multiple dispatch targets
{:ok, audit_signal} = Jido.Signal.new(%{
  type: "user.permission.changed",
  source: "/auth",
  data: %{user_id: "123", role: "admin"},
  jido_dispatch: [
    {:pubsub, [topic: "audit.events"]},
    {:logger, [level: :info]},
    {:bus, [target: :audit_bus]}
  ]
})

Built-in Dispatch Adapters

Jido provides several dispatch adapters out of the box:

  • :pid - Direct delivery to a specific process
  • :bus - Delivery to an event bus
  • :named - Delivery to a named process
  • :pubsub - Delivery via Phoenix.PubSub
  • :logger - Log signals using Logger
  • :console - Print signals to console
  • :noop - No-op adapter for testing

Example: Using Different Adapters

# Direct process delivery
pid_config = {:pid, [
  target: self(),
  delivery_mode: :async
]}

# Event bus delivery
bus_config = {:bus, [
  target: :my_bus,
  stream: "default"
]}

# PubSub broadcast
pubsub_config = {:pubsub, [
  target: :my_app_pubsub,
  topic: "events"
]}

# Create and dispatch a signal
{:ok, signal} = Jido.Signal.new(%{
  type: "example.created",
  source: "/demo",
  data: %{id: "123"},
  jido_dispatch: [pid_config, bus_config, pubsub_config]
})

Error Handling

Signals include built-in error handling for various scenarios:

# Handle dispatch failures
case Jido.Signal.Dispatch.dispatch(signal, dispatch_config) do
  :ok ->
    IO.puts("Signal dispatched successfully")

  {:error, :process_not_found} ->
    IO.puts("Target process not found")

  {:error, :bus_not_found} ->
    IO.puts("Target bus not found")

  {:error, reason} ->
    IO.puts("Dispatch failed: #{inspect(reason)}")
end

Custom Dispatch Adapters

You can create custom adapters by implementing the Jido.Signal.Dispatch.Adapter behaviour:

defmodule MyApp.CustomAdapter do
  @behaviour Jido.Signal.Dispatch.Adapter

  @impl true
  def validate_opts(opts) do
    # Validate configuration options
    {:ok, opts}
  end

  @impl true
  def deliver(signal, opts) do
    # Custom delivery logic
    :ok
  end
end

# Use custom adapter
custom_config = {MyApp.CustomAdapter, [
  custom_option: "value"
]}

Next Steps

Now that you understand the basics of Signals in Jido, explore these related guides:

See Also

  • CloudEvents Specification
  • Jido.Signal - Core Signal module
  • Jido.Signal.Dispatch - Signal dispatch system
  • Jido.Signal.Dispatch.Adapter - Adapter behaviour