Packages
nous
0.16.2
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/workflow/trace.ex
defmodule Nous.Workflow.Trace do
@moduledoc """
Records execution traces for workflow debugging and observability.
A trace is an ordered list of node executions with timing, status,
and output summaries. Traces are accumulated in-memory during execution
and returned as part of the workflow state metadata.
## Example
{:ok, state} = Nous.Workflow.run(graph, %{}, trace: true)
trace = state.metadata.trace
for entry <- trace.entries do
IO.puts "\#{entry.node_id} (\#{entry.node_type}): \#{entry.status} in \#{entry.duration_ms}ms"
end
"""
@type entry :: %{
node_id: String.t(),
node_type: atom(),
started_at: DateTime.t(),
completed_at: DateTime.t() | nil,
status: :completed | :failed | :skipped | :suspended,
duration_ms: non_neg_integer(),
error: term() | nil
}
@type t :: %__MODULE__{
run_id: String.t(),
entries: [entry()],
started_at: DateTime.t()
}
defstruct run_id: nil,
entries: [],
started_at: nil
@doc """
Create a new empty trace.
"""
@spec new() :: t()
def new do
%__MODULE__{
run_id: generate_id(),
entries: [],
started_at: DateTime.utc_now()
}
end
@doc """
Record a completed node execution.
"""
@spec record(
t(),
String.t(),
atom(),
non_neg_integer(),
:completed | :failed | :skipped | :suspended,
term()
) :: t()
def record(%__MODULE__{} = trace, node_id, node_type, duration_ns, status, error \\ nil) do
now = DateTime.utc_now()
duration_ms = System.convert_time_unit(duration_ns, :native, :millisecond)
entry = %{
node_id: node_id,
node_type: node_type,
started_at: DateTime.add(now, -duration_ms, :millisecond),
completed_at: now,
status: status,
duration_ms: duration_ms,
error: error
}
%{trace | entries: trace.entries ++ [entry]}
end
@doc """
Returns the total execution time in milliseconds.
"""
@spec total_duration_ms(t()) :: non_neg_integer()
def total_duration_ms(%__MODULE__{entries: entries}) do
Enum.reduce(entries, 0, fn entry, acc -> acc + entry.duration_ms end)
end
@doc """
Returns the number of nodes executed.
"""
@spec node_count(t()) :: non_neg_integer()
def node_count(%__MODULE__{entries: entries}), do: length(entries)
defp generate_id do
:crypto.strong_rand_bytes(8) |> Base.encode16(case: :lower)
end
end