Oi (oi v0.6.4)
Copy MarkdownOi provides a simple API for Orchid and ready-to-use functions for bind OrchidSymbiont, OrchidStratum and OrchidIntervention.
The purpose is building a front-end-friendly interface(exclipit nodes&edges).
Core Concepts
Oi pipeline
There're three phases in Oi's lifecycle:
- Build a
Oi.Topology.Graphof step nodes and edges- By macros in
Oi.Flowgraph - By functions in
Oi.Topology.Graph
- By macros in
Oi.compile/2(topology level) generate staticOi.Compiledstruct (bundles + plan, reusable)Oi.execute/2can resolve data, apply orchid_adapters, run via pluggable executor
Compiled
immutable compilation product. Compile once, dispatch many times with different data / executors.
See Oi.Compiled.
Data format during Oi.execute/2
Oi provides unified data: replaces the separate inputs: / interventions:.
- Ports with no incoming edge → memory (external inputs).
- Ports with an incoming edge → interventions (data originates inside the graph,
user is overriding it). Intervention values use the format
{type, value}wheretypeis an atom naming the intervention strategy (:override,:offset, or a custom module likeMyApp.MergeStrategy). Plain values default to{:override, value}.
When the payload is a tuple, wrap it explicitly in %Orchid.Param{} to
preserve type information:
{:override, %Orchid.Param{name: :key, type: {:tuple, 2}, payload: {1, 2}}}Two formats supported:
# Nested (recommended)
data: %{step1: %{in: "foo"}, step2: %{result: {:override, "bar"}}}
# Tuple keys
data: %{{:step1, :in} => "foo", {:step2, :result} => {:override, "bar"}}Pluggable Executor
Pluggable task fan-out per stage. Built-in: Oi.Executor.Sync (serial),
Oi.Executor.TaskSup (Task.Supervisor within per session).
Custom executors implement the Oi.Executor behaviour.
Session / Multiple Oi instances
Optional multi-tenant isolation via Oi.Runtime.Session,
wrapping per-tenant OrchidSymbiont.Runtime + Task.Supervisor.
Orchid Extension
Oi intentionally does not implement its own workflow plugin system.
Use Orchid hooks through :orchid_opts for step-level concerns such as logging,
metrics, retry, and caching.
Oi also support some ready-to-use functions to load/modify orchid's recipe and
options via Oi.Adapters.
Summary
Functions
Compile graph into static bundles + plan.
Execute compiled plan with inputs and interventions.
All-in-one convenience: compile + execute in a single call.
Types
@type name() :: String.t()
Oi's name, split several scopes.
Functions
@spec compile(Oi.Topology.Graph.t(), Oi.Topology.Cluster.t()) :: {:ok, Oi.Compiled.t()} | {:error, term()}
Compile graph into static bundles + plan.
Pure topology — no interventions. Reusable across different intervention sets and inputs.
@spec execute( Oi.Compiled.t(), keyword() ) :: {:ok, Oi.Result.t()} | {:error, term()}
Execute compiled plan with inputs and interventions.
Unified :data format (recommended)
Oi.execute(compiled, data: %{
step1: %{in: "foo"},
step2: %{result: {:override, "bar"}}
})Supports tuple-key format as well: %{{:step, :port} => value}.
Ports with incoming edges → intervention; ports without → memory.
Intervention values use {type, value} where type is an atom naming the
intervention strategy (:override, :offset, or a custom module).
Plain values default to {:override, value}.
For tuple payloads, wrap in %Orchid.Param{} to preserve type info.
Other options
:executor—Oi.Executor.Sync(default),Oi.Executor.TaskSup, or custom module implementingOi.Executor:executor_opts— passed to the executor (e.g.[sup: MyTaskSup]):orchid_adapters— adapter pipeline; each is a 1-arity or 2-arity function over{recipe, opts}:orchid_baggage— merged into Orchid run baggage:orchid_opts— extra keyword opts forwarded toOrchid.run/3:concurrency— fallback for executor if:executor_optshas none (default:System.schedulers_online()):timeout— fallback for executor (default::infinity):name— optional scope name, merged into baggage as:scope_id
@spec run( Oi.Compiled.t() | Oi.Topology.Graph.t(), keyword() ) :: {:ok, Oi.Result.t()} | {:error, term()}
All-in-one convenience: compile + execute in a single call.
Accepts either a Graph.t() or a Compiled.t(). When given a graph,
extracts :cluster from opts (defaults to empty %Cluster{}),
compiles, then executes with the remaining options. When given a
compiled struct, delegates directly to execute/2.
Options
Same as execute/2, plus:
:cluster—Oi.Topology.Cluster.t()(default:%Cluster{}), only used whengraph_or_compiledis aGraph.t()
Examples
graph = graph do
step(MyStep)
end
{:ok, result} = Oi.run(graph, data: %{my_step: %{in: "hello"}})
# With explicit cluster
{:ok, result} = Oi.run(graph,
data: %{my_step: %{in: "hello"}},
cluster: %Cluster{node_colors: %{my_step: :fast}}
)