Jidoka: Workflow Patterns
Workflows are deterministic, app-owned execution graphs. Use them when order, data flow, and failure behavior matter more than open-ended model reasoning.
Setup
Mix.install(
[
{:jidoka, git: "https://github.com/mikehostetler/jidoka.git", ref: "924a486f3c1b7e7a943cb3d5ceee0de65f158467"},
{:kino, "~> 0.19.0"}
],
config: [
jidoka: [
model_aliases: %{fast: "anthropic:claude-haiku-4-5"}
]
]
)
Jidoka.Kino.setup()
Define Workflow Steps
defmodule LivebookDemo.WorkflowPatterns.Fns do
def classify(%{topic: topic, priority: priority, suffix: suffix}, _context) do
{:ok, %{label: "#{topic}:#{priority}:#{suffix}", priority: priority}}
end
end
defmodule LivebookDemo.WorkflowPatterns.Tools.RouteTicket do
use Jidoka.Tool,
name: "workflow_route_ticket",
description: "Routes a classified ticket.",
schema: Zoi.object(%{label: Zoi.string(), priority: Zoi.string()})
@impl true
def run(%{label: label, priority: "urgent"}, _context), do: {:ok, %{queue: "escalations", label: label}}
def run(%{label: label}, _context), do: {:ok, %{queue: "standard", label: label}}
end
defmodule LivebookDemo.WorkflowPatterns.Tools.FailStep do
use Jidoka.Tool,
name: "workflow_fail_step",
description: "Fails with a caller-provided reason.",
schema: Zoi.object(%{reason: Zoi.string()})
@impl true
def run(%{reason: reason}, _context), do: {:error, reason}
end
Build A Context-Aware Workflow
defmodule LivebookDemo.WorkflowPatterns.RouteWorkflow do
use Jidoka.Workflow
workflow do
id :livebook_route_workflow
description "Classifies and routes a support topic."
input Zoi.object(%{
topic: Zoi.string(),
priority: Zoi.string() |> Zoi.default("normal")
})
end
steps do
function :classify, {LivebookDemo.WorkflowPatterns.Fns, :classify, 2},
input: %{
topic: input(:topic),
priority: input(:priority),
suffix: context(:suffix)
}
tool :route, LivebookDemo.WorkflowPatterns.Tools.RouteTicket,
input: from(:classify)
end
output from(:route)
end
{:ok, workflow} = Jidoka.inspect_workflow(LivebookDemo.WorkflowPatterns.RouteWorkflow)
Map.take(workflow, [:id, :description, :steps, :dependencies, :output])
Run with debug output to see the execution flow.
{:ok, debug} =
Jidoka.Workflow.run(
LivebookDemo.WorkflowPatterns.RouteWorkflow,
%{topic: "billing", priority: "urgent"},
context: %{suffix: "vip"},
return: :debug
)
rows =
workflow.steps
|> Enum.with_index(1)
|> Enum.map(fn {step, order} ->
%{
order: order,
step: step.name,
waits_for: if(step.dependencies == [], do: "-", else: Enum.join(step.dependencies, ", ")),
output: inspect(Map.fetch!(debug.steps, step.name))
}
end)
Jidoka.Kino.table("Workflow execution flow", rows)
Surface Validation And Failure
{:error, missing_context} =
Jidoka.Workflow.run(
LivebookDemo.WorkflowPatterns.RouteWorkflow,
%{topic: "billing"},
context: %{}
)
Jidoka.format_error(missing_context)
defmodule LivebookDemo.WorkflowPatterns.FailingWorkflow do
use Jidoka.Workflow
workflow do
id :livebook_failing_workflow
input Zoi.object(%{reason: Zoi.string()})
end
steps do
tool :fail, LivebookDemo.WorkflowPatterns.Tools.FailStep,
input: %{reason: input(:reason)}
end
output from(:fail)
end
previous_level = Logger.level()
Logger.configure(level: :emergency)
{:error, failed} =
try do
Jidoka.Workflow.run(LivebookDemo.WorkflowPatterns.FailingWorkflow, %{reason: "boom"})
after
Logger.configure(level: previous_level)
end
Jidoka.format_error(failed)