Jido.Signal (Jido Signal v1.0.0)

View Source

Defines the core Signal structure in Jido, implementing the CloudEvents specification (v1.0.2) with Jido-specific extensions for agent-based systems.

https://cloudevents.io/

Overview

Signals are the universal message format in Jido, serving as the nervous system of your agent-based application. Every event, command, and state change flows through the system as a Signal, providing:

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

CloudEvents Compliance

Each Signal implements the CloudEvents v1.0.2 specification with these required 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")

And optional fields:

  • subject: Specific subject of the event
  • time: Timestamp in ISO 8601 format
  • datacontenttype: Media type of the data (defaults to "application/json")
  • dataschema: Schema defining the data structure
  • data: The actual event payload

Jido Extensions

Beyond the CloudEvents spec, Signals include Jido-specific fields:

  • jido_dispatch: Routing and delivery configuration (optional)

Creating Signals

Signals can be created in several ways:

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

# With dispatch config
{:ok, signal} = Signal.new(%{
  type: "metrics.collected",
  source: "/monitoring",
  data: %{cpu: 80, memory: 70},
  jido_dispatch: {:pubsub, topic: "metrics"}
})

Custom Signal Types

You can define custom Signal types using the use Jido.Signal pattern:

defmodule MySignal do
  use Jido.Signal,
    type: "my.custom.signal",
    default_source: "/my/service",
    datacontenttype: "application/json",
    schema: [
      user_id: [type: :string, required: true],
      message: [type: :string, required: true]
    ]
end

# Create instances
{:ok, signal} = MySignal.new(%{user_id: "123", message: "Hello"})

# Override runtime fields
{:ok, signal} = MySignal.new(
  %{user_id: "123", message: "Hello"},
  source: "/different/source",
  subject: "user-notification",
  jido_dispatch: {:pubsub, topic: "events"}
)

Signal Types

Signal types are strings, but typically use a hierarchical dot notation:

<domain>.<entity>.<action>[.<qualifier>]

Examples:

  • user.profile.updated
  • order.payment.processed.success
  • system.metrics.collected

Guidelines for type naming:

  • Use lowercase with dots
  • Keep segments meaningful
  • Order from general to specific
  • Include qualifiers when needed

Data Content Types

The datacontenttype field indicates the format of the data field:

  • application/json (default) - JSON-structured data
  • text/plain - Unstructured text
  • application/octet-stream - Binary data
  • Custom MIME types for specific formats

Dispatch Configuration

The jido_dispatch field controls how the Signal is delivered:

# Single dispatch config
jido_dispatch: {:pubsub, topic: "events"}

# Multiple dispatch targets
jido_dispatch: [
  {:pubsub, topic: "events"},
  {:logger, level: :info},
  {:webhook, url: "https://api.example.com/webhook"}
]

See Also

Summary

Functions

Defines a new Signal module.

Deserializes binary data back into a Signal struct or list of Signal structs.

Creates a new Signal struct from a map.

Converts a struct or list of structs to Signal data format.

Creates a new Signal struct.

Creates a new Signal struct, raising an error if invalid.

Serializes a Signal or a list of Signals using the specified or default serializer.

Legacy serialize function that returns binary directly (for backward compatibility).

Types

t()

@type t() :: %Jido.Signal{
  data: term() | nil,
  datacontenttype: String.t() | nil,
  dataschema: String.t() | nil,
  id: String.t(),
  jido_dispatch: Jido.Signal.Dispatch.dispatch_configs() | nil,
  source: String.t(),
  specversion: String.t(),
  subject: String.t() | nil,
  time: String.t() | nil,
  type: String.t()
}

Functions

__using__(opts)

(macro)

Defines a new Signal module.

This macro sets up the necessary structure and callbacks for a custom Signal, including configuration validation and default implementations.

Options

  • :type (String.t/0) - Required. The type of the Signal

  • :default_source (String.t/0) - The default source of the Signal

  • :datacontenttype (String.t/0) - The content type of the data field

  • :dataschema (String.t/0) - Schema URI for the data field (optional)

  • :schema (keyword/0) - A NimbleOptions schema for validating the Signal's data parameters The default value is [].

Examples

defmodule MySignal do
  use Jido.Signal,
    type: "my.custom.signal",
    default_source: "/my/service",
    schema: [
      user_id: [type: :string, required: true],
      message: [type: :string, required: true]
    ]
end

deserialize(binary, opts \\ [])

@spec deserialize(
  binary(),
  keyword()
) :: {:ok, t() | [t()]} | {:error, term()}

Deserializes binary data back into a Signal struct or list of Signal structs.

Parameters

  • binary: The serialized binary data to deserialize
  • opts: Optional configuration including:
    • :serializer - The serializer module to use (defaults to configured serializer)
    • :type - Specific type to deserialize to
    • :type_provider - Custom type provider

Returns

{:ok, Signal.t() | list(Signal.t())} if successful, {:error, reason} otherwise

Examples

# JSON deserialization (default)
iex> json = ~s({"type":"example.event","source":"/example","id":"123"})
iex> {:ok, signal} = Jido.Signal.deserialize(json)
iex> signal.type
"example.event"

# Using a specific serializer
iex> {:ok, signal} = Jido.Signal.deserialize(binary, serializer: Jido.Signal.Serialization.ErlangTermSerializer)
iex> signal.type
"example.event"

# Deserializing multiple Signals
iex> json = ~s([{"type":"event1","source":"/ex1"},{"type":"event2","source":"/ex2"}])
iex> {:ok, signals} = Jido.Signal.deserialize(json)
iex> length(signals)
2

from_map(map)

@spec from_map(map()) :: {:ok, t()} | {:error, String.t()}

Creates a new Signal struct from a map.

Parameters

  • map: A map containing the Signal attributes.

Returns

{:ok, Signal.t()} if the map is valid, {:error, String.t()} otherwise.

Examples

iex> Jido.Signal.from_map(%{"type" => "example.event", "source" => "/example", "id" => "123"})
{:ok, %Jido.Signal{type: "example.event", source: "/example", id: "123", ...}}

map_to_signal_data(signals, fields \\ [])

@spec map_to_signal_data([struct()], Keyword.t()) :: [t()]
@spec map_to_signal_data(
  struct(),
  Keyword.t()
) :: t()

Converts a struct or list of structs to Signal data format.

This function is useful for converting domain objects to Signal format while preserving their type information through the TypeProvider.

Parameters

  • signals: A struct or list of structs to convert
  • fields: Additional fields to include (currently unused)

Returns

Signal struct or list of Signal structs with the original data as payload

Examples

# Converting a single struct
iex> user = %User{id: 1, name: "John"}
iex> signal = Jido.Signal.map_to_signal_data(user)
iex> signal.data
%User{id: 1, name: "John"}

# Converting multiple structs
iex> users = [%User{id: 1}, %User{id: 2}]
iex> signals = Jido.Signal.map_to_signal_data(users)
iex> length(signals)
2

new(attrs)

@spec new(map() | keyword()) :: {:ok, t()} | {:error, String.t()}

Creates a new Signal struct.

Parameters

  • attrs: A map or keyword list containing the Signal attributes.

Returns

{:ok, Signal.t()} if the attributes are valid, {:error, String.t()} otherwise.

Examples

iex> Jido.Signal.new(%{type: "example.event", source: "/example", id: "123"})
{:ok, %Jido.Signal{type: "example.event", source: "/example", id: "123", ...}}

iex> Jido.Signal.new(type: "example.event", source: "/example")
{:ok, %Jido.Signal{type: "example.event", source: "/example", ...}}

new!(attrs)

@spec new!(map() | keyword()) :: t() | no_return()

Creates a new Signal struct, raising an error if invalid.

Parameters

  • attrs: A map or keyword list containing the Signal attributes.

Returns

Signal.t() if the attributes are valid.

Raises

RuntimeError if the attributes are invalid.

Examples

iex> Jido.Signal.new!(%{type: "example.event", source: "/example"})
%Jido.Signal{type: "example.event", source: "/example", ...}

iex> Jido.Signal.new!(type: "example.event", source: "/example")
%Jido.Signal{type: "example.event", source: "/example", ...}

serialize(signal_or_list, opts \\ [])

@spec serialize(
  t() | [t()],
  keyword()
) :: {:ok, binary()} | {:error, term()}

Serializes a Signal or a list of Signals using the specified or default serializer.

Parameters

  • signal_or_list: A Signal struct or list of Signal structs
  • opts: Optional configuration including:
    • :serializer - The serializer module to use (defaults to configured serializer)

Returns

{:ok, binary} on success, {:error, reason} on failure

Examples

iex> signal = %Jido.Signal{type: "example.event", source: "/example"}
iex> {:ok, binary} = Jido.Signal.serialize(signal)
iex> is_binary(binary)
true

# Using a specific serializer
iex> {:ok, binary} = Jido.Signal.serialize(signal, serializer: Jido.Signal.Serialization.ErlangTermSerializer)
iex> is_binary(binary)
true

# Serializing multiple Signals
iex> signals = [
...>   %Jido.Signal{type: "event1", source: "/ex1"},
...>   %Jido.Signal{type: "event2", source: "/ex2"}
...> ]
iex> {:ok, binary} = Jido.Signal.serialize(signals)
iex> is_binary(binary)
true

serialize!(signal_or_list, opts \\ [])

@spec serialize!(
  t() | [t()],
  keyword()
) :: binary()

Legacy serialize function that returns binary directly (for backward compatibility).