Current section
Files
Jump to
Current section
Files
lib/oi.ex
defmodule Oi do
@doc_header """
Oi means Orchid integration.
"""
@external_resource readme = Path.join([__DIR__, "../README.md"])
@doc_main readme
|> File.read!()
|> String.split("<!-- MDOC -->")
|> Enum.fetch!(1)
@moduledoc @doc_header <> @doc_main
@type name :: String.t()
alias Oi.Workspace
alias Oi.{Compiler, Dispatcher, Configurator}
alias Oi.Workspace.{Planning, Drafting}
@doc """
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 compile(Workspace.t()) :: {:ok, Workspace.t()} | {:error, :cycle_detected}
def compile(%Workspace{} = ws) do
case Compiler.compile_graph(ws.graph, ws.cluster) do
{:ok, bundles} ->
{:ok, plan} = Planning.build(bundles)
{:ok, %{ws | static_bundles: bundles, plan: plan}}
{:error, _} = err ->
err
end
end
@doc """
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` — overrides `workspace.interventions` (default)
* `:executor` — `Oi.Executor.Sync` (default), `Oi.Executor.TaskSup`, or `Oi.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")]
)
"""
@spec dispatch(Workspace.t(), keyword()) :: {:ok, Workspace.t()} | {:error, term()}
def dispatch(%Workspace{} = ws, opts \\ []) do
case ws.plan do
nil ->
{:error, :not_compiled}
%Planning.Plan{} ->
interventions = Keyword.get(opts, :interventions, ws.interventions)
bound_bundles = Compiler.bind(ws.static_bundles, interventions)
# Rebuild plan with bound bundles
{:ok, bound_plan} = Planning.build(bound_bundles)
conf = Configurator.new(opts)
drafting = Drafting.new()
case Dispatcher.dispatch(bound_plan, drafting, conf) do
{:ok, final_drafting} ->
{:ok, %{ws | drafting: final_drafting}}
{:error, _} = err ->
err
end
end
end
end