Oi (oi v0.1.0)
Copy MarkdownOi means Orchid integration.
Core concepts
Workspace — pure data struct holding graph, cluster, interventions. No process, no GenServer. Compiled and dispatched via pure functions.
Compile + Dispatch — two-phase pipeline:
Oi.compile/1— topology → static bundles (reusable)Oi.dispatch/2— bind interventions → plan → execute via pluggable executor
Executor — pluggable task execution strategy. Built-in:
Sync(serial),TaskSup(Task.Supervisor),Pool(NimblePool).Session — optional process tree per tenant, wrapping
OrchidSymbiont.Runtimefor multi-tenant isolation.
Quick start
Define a step
defmodule MyApp.Steps.Upcase do
use Oi.Step, name: :upcase
manifest(
inputs: [:text],
outputs: [result: :string]
)
routine text, _opts do
text |> String.upcase() |> ok()
end
endBuild and run
alias Oi.Topology.Graph
alias Oi.Topology.Graph.{Node, Edge}
graph =
Graph.new()
|> Graph.add_node(%Node{
id: :up, container: MyApp.Steps.Upcase,
inputs: [:text], outputs: [:result]
})
ws = Oi.Workspace.new("demo", graph)
{:ok, ws} = Oi.compile(ws)
{:ok, ws} = Oi.dispatch(ws,
interventions: %{{:port, :up, :text} => {:input, "hello"}}
)
ws.drafting.memory # => %{"up|result" => "HELLO"}Multi-tenant with Session
Oi.Session.start("tenant-1")
Oi.Session.start("tenant-2")
ws = Oi.Workspace.new("tenant-1", graph)
{:ok, ws} = Oi.compile(ws)
{:ok, ws} = Oi.dispatch(ws,
executor: Oi.Executor.TaskSup,
executor_opts: [sup: Oi.Session.tasks_tuple("tenant-1")]
)Symbiont step
defmodule MyApp.Steps.Predict do
use Oi.Step, name: :predict, symbiont?: true
manifest(
inputs: [:features],
outputs: [prediction: :string],
models: [:model]
)
routine features, models, _opts do
{:ok, result} = OrchidSymbiont.call(models.model, {:predict, features})
ok(result)
end
end
Summary
Functions
Phase 1: Compile graph into static bundles + plan.
Phase 2: Dispatch plan with interventions, filling drafting.
Types
@type name() :: String.t()
Functions
@spec compile(Oi.Workspace.t()) :: {:ok, Oi.Workspace.t()} | {:error, :cycle_detected}
Phase 1: Compile graph into static bundles + plan.
Pure topology — no interventions. Result stored in workspace.static_bundles
and workspace.plan. Reusable across different intervention sets.
@spec dispatch( Oi.Workspace.t(), keyword() ) :: {:ok, Oi.Workspace.t()} | {:error, term()}
Phase 2: Dispatch plan with interventions, filling drafting.
Interventions can be overridden via the :interventions option —
useful for A/B testing with the same compiled plan.
Options
:interventions— overridesworkspace.interventions(default):executor—Oi.Executor.Sync(default),Oi.Executor.TaskSup, orOi.Executor.Pool:executor_opts— passed to the executor (e.g.[sup: MyTaskSup]):plugins— OrchidPlugin pipeline:orchid_baggage— merged into Orchid run baggage
Examples
# Compile once
{:ok, ws} = Oi.compile(ws)
# Dispatch with default interventions
{:ok, ws} = Oi.dispatch(ws)
# Dispatch with swapped interventions (reuses compiled plan)
{:ok, ws_b} = Oi.dispatch(ws, interventions: other_interventions)
# With Task.Supervisor
{:ok, ws} = Oi.dispatch(ws,
executor: Oi.Executor.TaskSup,
executor_opts: [sup: Oi.Session.tasks_tuple("svs-1")]
)