Canonical, editable graph document used by host applications.
A Docket.Graph is the public build-time representation. Editors and
compilers update this value directly; runtime lowering happens later through
Docket.Graph.Compiler.
Non-bang editing functions return {:ok, graph} or
{:error, %Docket.Graph.Error{}}, which is useful for event-by-event graph
editors. Bang editing functions return the graph or raise, which keeps
pipe-oriented graph construction ergonomic.
Functions that accept record attributes generally accept a keyword list, a
map, or the matching Docket.Graph.* struct. Keyword lists are convenient for
hand-written Elixir construction, maps are convenient for application/UI
payloads, and structs let compilers or importers pass already-normalized graph
records back through the same editing API.
Summary
Functions
Deletes an edge.
Deletes an edge, raising on malformed arguments.
Deletes a field from both input and state field collections.
Deletes a field from both input and state field collections, raising on malformed arguments.
Deletes a node.
Deletes a node, raising on malformed arguments.
Reads diagnostics stored on the graph document.
Decodes a JSON-safe map into an authored graph, returning a tagged result.
Decodes a JSON-safe map into an authored graph, raising on invalid input.
Stores graph-level application metadata.
Stores graph-level application metadata, raising on malformed arguments.
Creates a new editable graph document.
Creates a new editable graph document, raising on invalid options.
Stores a graph-level policy value.
Stores a graph-level policy value, raising on malformed arguments.
Adds or replaces an edge by ID.
Adds or replaces an edge by ID, raising on malformed arguments.
Adds or replaces a state field.
Adds or replaces a state field, raising on malformed arguments.
Adds or replaces an input field.
Adds or replaces an input field, raising on malformed arguments.
Adds or replaces a node by ID.
Adds or replaces a node by ID, raising on malformed arguments.
Adds or replaces an output projection.
Adds or replaces an output projection, raising on malformed arguments.
Encodes the authored graph as a plain, JSON-safe map.
Updates an edge with a map/keyword patch or a function.
Updates an edge with a map/keyword patch or a function, raising on malformed arguments.
Updates a field with a map/keyword patch or a function.
Updates a field with a map/keyword patch or a function, raising on malformed arguments.
Updates a node with a map/keyword patch or a function.
Updates a node with a map/keyword patch or a function, raising on malformed arguments.
Verifies the graph and returns it with compiler diagnostics attached.
Types
@type edit_result() :: {:ok, t()} | {:error, Docket.Graph.Error.t()}
@type id() :: String.t()
@type t() :: %Docket.Graph{ description: String.t() | nil, diagnostics: [Docket.Graph.Diagnostic.t()], edges: %{optional(id()) => Docket.Graph.Edge.t()}, fields: %{optional(id()) => Docket.Graph.Field.t()}, id: id(), inputs: %{optional(id()) => Docket.Graph.Field.t()}, metadata: map(), name: String.t() | nil, nodes: %{optional(id()) => Docket.Graph.Node.t()}, outputs: %{optional(id()) => Docket.Graph.Output.t()}, policies: map(), schema_version: pos_integer() }
Functions
@spec delete_edge(t(), id(), keyword()) :: edit_result()
Deletes an edge.
Deletes an edge, raising on malformed arguments.
@spec delete_field(t(), id(), keyword()) :: edit_result()
Deletes a field from both input and state field collections.
Deletes a field from both input and state field collections, raising on malformed arguments.
@spec delete_node(t(), id(), keyword()) :: edit_result()
Deletes a node.
Deletes a node, raising on malformed arguments.
@spec diagnostics( t(), keyword() ) :: [Docket.Graph.Diagnostic.t()]
Reads diagnostics stored on the graph document.
@spec from_map( map(), keyword() ) :: edit_result()
Decodes a JSON-safe map into an authored graph, returning a tagged result.
Returns {:ok, graph} or {:error, %Docket.Graph.Error{}}. The document is
validated strictly: unknown structural keys, unknown enum values, malformed
tagged expressions, an unsupported schema_version, and non-portable values
are all rejected. Loading never creates atoms from the document - module
implementation identifiers are looked up in the :implementations registry,
and an identifier absent from the registry is a typed error.
See to_map/2 for the registry format and a full round-trip example.
Options
:implementations- registry of identifiers to module implementations, defaulting to%{}.
Decodes a JSON-safe map into an authored graph, raising on invalid input.
Behaves like from_map/2 but returns the graph directly and raises
Docket.Graph.Error on a malformed document or unknown implementation
identifier.
Stores graph-level application metadata.
Stores graph-level application metadata, raising on malformed arguments.
Content is stored as given. Public interchange and publication normalize
open atom keys and values to strings through the same runtime value
boundary; terms without a portable representation are rejected. Keys
starting with "$" are reserved.
@spec new(keyword()) :: edit_result()
Creates a new editable graph document.
Options must be a keyword list. Supported options are:
:id- graph ID; generated when omitted (via:id_generatorwhen given):name- optional display name:description- optional description:schema_version- Docket graph document schema version:metadata- application metadata map:policies- graph policy map
Creates a new editable graph document, raising on invalid options.
Stores a graph-level policy value.
Stores a graph-level policy value, raising on malformed arguments.
Content is stored as given. Public interchange and publication normalize
open atom keys and values to strings through the same runtime value
boundary; terms without a portable representation are rejected. Keys
starting with "$" are reserved.
@spec put_edge(t(), id(), keyword() | map() | Docket.Graph.Edge.t(), keyword()) :: edit_result()
Adds or replaces an edge by ID.
attrs may be a keyword list, a map, or a Docket.Graph.Edge struct. The
explicit id argument is used as the stored edge ID.
Adds or replaces an edge by ID, raising on malformed arguments.
@spec put_field(t(), id(), keyword() | map() | Docket.Graph.Field.t(), keyword()) :: edit_result()
Adds or replaces a state field.
attrs may be a keyword list, a map, or a Docket.Graph.Field struct. The
explicit id argument is used as the stored field ID, and the stored field
kind is forced to :state. :schema accepts a Docket.Schema or schema
shorthand (see Docket.Schema.normalize/1).
Adds or replaces a state field, raising on malformed arguments.
@spec put_input(t(), id(), keyword() | map() | Docket.Graph.Field.t(), keyword()) :: edit_result()
Adds or replaces an input field.
attrs may be a keyword list, a map, or a Docket.Graph.Field struct. The
explicit id argument is used as the stored field ID, and the stored field
kind is forced to :input. :schema accepts a Docket.Schema or schema
shorthand (see Docket.Schema.normalize/1).
Adds or replaces an input field, raising on malformed arguments.
@spec put_node(t(), id(), keyword() | map() | Docket.Graph.Node.t(), keyword()) :: edit_result()
Adds or replaces a node by ID.
attrs may be a keyword list, a map, or a Docket.Graph.Node struct. The
explicit id argument is used as the stored node ID.
:implementation accepts shorthand module forms:
MyNodebecomes%{type: :module, module: MyNode, function: :call}{MyNode, :run}becomes%{type: :module, module: MyNode, function: :run}- maps are preserved for compiler/runtime validation
:inputs and :fields declare graph fields inline: each entry maps a
field ID to either field attrs (everything put_input!/put_field!
accepts) or bare schema shorthand, and materializes as an ordinary graph
field — nothing lands on the node record:
Graph.put_node!(graph, "draft",
implementation: MyApp.Nodes.LLM,
inputs: %{"customer_message" => [schema: :string, required: true]},
fields: %{
"draft_response" => :string,
"messages" => [schema: {:list, :map}, reducer: :append]
}
)Declaring a field that already exists with an identical definition is a
no-op, so multiple nodes can declare a shared field in any order.
A conflicting definition raises ({:error, %Docket.Graph.Error{}} from
put_node/4); redefinition stays explicit via put_input!/put_field!.
Deleting the node later does not delete the fields — they are shared
state, and verify/2 flags fields nothing references.
Adds or replaces a node by ID, raising on malformed arguments.
@spec put_output(t(), id(), keyword() | map() | Docket.Graph.Output.t(), keyword()) :: edit_result()
Adds or replaces an output projection.
attrs may be a keyword list, a map, or a Docket.Graph.Output struct. The
explicit id argument is used as the stored output ID. If :source is omitted,
it defaults to the output ID. :schema accepts a Docket.Schema or schema
shorthand (see Docket.Schema.normalize/1).
Adds or replaces an output projection, raising on malformed arguments.
Encodes the authored graph as a plain, JSON-safe map.
The returned map uses string keys everywhere and only JSON-safe values
(binaries, numbers, booleans, nil, lists, and string-keyed maps), so a host
can persist or transmit it with any JSON codec. Atom keys and values in open
content (metadata, policies, config, schema defaults, reducer opts) are
coerced to strings; terms with no JSON representation (tuples, keyword lists,
PIDs, references, functions, MapSets, structs) raise
Docket.Graph.Error. Keys starting with "$" are reserved for the wire
format and are rejected in open content. Transient diagnostics are omitted,
and "schema_version" is always present.
Executable node implementations are resolved through the :implementations
registry: a map of stable, host-defined string identifiers to module
implementations. Each value may be a module (MyNode), a {module, function}
tuple, or a %{type: :module, module: MyNode, function: :call} map. The
registry must be unambiguous - two identifiers may not resolve to the same
{module, function} pair. A module implementation that is absent from the
registry raises. Passthrough (non-module) implementation maps round-trip as
plain durable values.
Options
:implementations- registry of identifiers to module implementations, defaulting to%{}. Required only when a graph actually uses module implementations.
Example
registry = %{
"watercooler.agent" => WaterCooler.Workflows.Nodes.SpawnAgent,
"watercooler.tap" => {WaterCooler.Workflows.Nodes.Tap, :call}
}
json =
graph
|> Docket.Graph.to_map(implementations: registry)
|> Jason.encode!()
{:ok, graph} =
json
|> Jason.decode!()
|> Docket.Graph.from_map(implementations: registry)This is the editable AUTHORED graph document. It neither contains nor
recomputes a Docket.GraphRef hash: Docket.save_graph/3 materializes node
defaults and privately encodes and hashes the effective graph to produce a
content-addressed reference. Re-saving an authored graph after a node's
defaults change may therefore produce a different effective graph reference
even when this document is unchanged.
@spec update_edge( t(), id(), (Docket.Graph.Edge.t() -> Docket.Graph.Edge.t() | map() | keyword()) | Docket.Graph.Edge.t() | map() | keyword(), keyword() ) :: edit_result()
Updates an edge with a map/keyword patch or a function.
attrs_or_fun may be:
- a keyword list patch
- a map patch
- a
Docket.Graph.Edgestruct - a function that receives the current
Docket.Graph.Edgeand returns a keyword list, map, orDocket.Graph.Edge
The explicit id argument remains the stored edge ID after the update.
@spec update_edge!( t(), id(), (Docket.Graph.Edge.t() -> Docket.Graph.Edge.t() | map() | keyword()) | Docket.Graph.Edge.t() | map() | keyword(), keyword() ) :: t()
Updates an edge with a map/keyword patch or a function, raising on malformed arguments.
@spec update_field( t(), id(), (Docket.Graph.Field.t() -> Docket.Graph.Field.t() | map() | keyword()) | Docket.Graph.Field.t() | map() | keyword(), keyword() ) :: edit_result()
Updates a field with a map/keyword patch or a function.
attrs_or_fun may be:
- a keyword list patch
- a map patch
- a
Docket.Graph.Fieldstruct - a function that receives the current
Docket.Graph.Fieldand returns a keyword list, map, orDocket.Graph.Field
The resulting field kind decides whether the field is stored as an input or a
state field. The explicit id argument remains the stored field ID.
@spec update_field!( t(), id(), (Docket.Graph.Field.t() -> Docket.Graph.Field.t() | map() | keyword()) | Docket.Graph.Field.t() | map() | keyword(), keyword() ) :: t()
Updates a field with a map/keyword patch or a function, raising on malformed arguments.
@spec update_node( t(), id(), (Docket.Graph.Node.t() -> Docket.Graph.Node.t() | map() | keyword()) | Docket.Graph.Node.t() | map() | keyword(), keyword() ) :: edit_result()
Updates a node with a map/keyword patch or a function.
attrs_or_fun may be:
- a keyword list patch
- a map patch
- a
Docket.Graph.Nodestruct - a function that receives the current
Docket.Graph.Nodeand returns a keyword list, map, orDocket.Graph.Node
The explicit id argument remains the stored node ID after the update.
@spec update_node!( t(), id(), (Docket.Graph.Node.t() -> Docket.Graph.Node.t() | map() | keyword()) | Docket.Graph.Node.t() | map() | keyword(), keyword() ) :: t()
Updates a node with a map/keyword patch or a function, raising on malformed arguments.
Verifies the graph and returns it with compiler diagnostics attached.