Current section

Files

Jump to
nous lib nous workflow edge.ex
Raw

lib/nous/workflow/edge.ex

defmodule Nous.Workflow.Edge do
@moduledoc """
A directed edge connecting two nodes in a workflow graph.
Edges define execution flow between nodes. They can be unconditional
(sequential), conditional (evaluated against workflow state), or
default (fallback when no conditional edge matches).
## Edge Types
| Type | Behavior |
|------|----------|
| `:sequential` | Always followed (A -> B) |
| `:conditional` | Followed when `condition.(state)` returns `true` |
| `:default` | Followed when no sibling conditional edges match |
## Examples
Nous.Workflow.Edge.new(%{
from_id: "search",
to_id: "synthesize",
type: :sequential
})
Nous.Workflow.Edge.new(%{
from_id: "evaluate",
to_id: "publish",
type: :conditional,
condition: fn state -> state.data.quality >= 0.8 end
})
"""
@type edge_type :: :sequential | :conditional | :default
@type t :: %__MODULE__{
id: String.t(),
from_id: String.t(),
to_id: String.t(),
type: edge_type(),
condition: (Nous.Workflow.State.t() -> boolean()) | nil,
label: String.t() | nil,
metadata: map()
}
defstruct [
:id,
:from_id,
:to_id,
:condition,
:label,
type: :sequential,
metadata: %{}
]
@doc """
Create a new workflow edge.
Required: `:from_id` and `:to_id`.
## Examples
iex> edge = Nous.Workflow.Edge.new(%{from_id: "a", to_id: "b"})
iex> edge.type
:sequential
iex> is_binary(edge.id)
true
"""
@spec new(map()) :: t()
def new(attrs) when is_map(attrs) do
%__MODULE__{
id: Map.get(attrs, :id) || generate_id(),
from_id: attrs |> Map.fetch!(:from_id) |> to_string(),
to_id: attrs |> Map.fetch!(:to_id) |> to_string(),
type: Map.get(attrs, :type, :sequential),
condition: Map.get(attrs, :condition),
label: Map.get(attrs, :label),
metadata: Map.get(attrs, :metadata, %{})
}
end
defp generate_id do
:crypto.strong_rand_bytes(16) |> Base.encode16(case: :lower)
end
end