Section
This notebook demonstrates operation controls, durable session snapshots, pending review requests, and approval-based resume.
Mix.install(
[
{:kino, "~> 0.14"},
{:jidoka, path: Path.expand("..", __DIR__)}
],
consolidate_protocols: false
)Jidoka.Kino.setup_notebook(model: "test:notebook-model", check_provider?: false)Define A Reviewed Operation
defmodule IssueRefund do
use Jidoka.Action,
name: "issue_refund",
description: "Queues a deterministic refund.",
schema:
Zoi.object(%{
order_id: Zoi.string()
})
@impl true
def run(params, _context) do
order_id = Map.get(params, :order_id) || Map.get(params, "order_id")
{:ok, %{refund_id: "refund-#{order_id}", status: "queued"}}
end
end
defmodule RequireRefundApproval do
use Jidoka.Control, name: "require_refund_approval"
@impl true
def call(_operation), do: {:interrupt, :approval_required}
end
defmodule RefundAgent do
use Jidoka.Agent
agent :refund_agent do
model %{provider: :test, id: "notebook-model"}
instructions "Use issue_refund for refund requests, then summarize the result."
end
tools do
action IssueRefund
end
controls do
max_turns 4
operation RequireRefundApproval, when: [kind: :action, name: :issue_refund]
end
endStart A Durable Session
{:ok, store_pid} = Jidoka.Harness.Store.InMemory.start_link()
store = {Jidoka.Harness.Store.InMemory, pid: store_pid}
{:ok, session} = Jidoka.Harness.start_session(RefundAgent.spec(), session_id: "refund-demo", store: store)
{:ok, _inspection} = Jidoka.Kino.debug_agent(RefundAgent)
session.statusRun Until Review Is Required
fake_llm = fn _intent, journal ->
llm_calls =
journal.results
|> Map.values()
|> Enum.count(&(&1.kind == :llm))
case llm_calls do
0 ->
{:ok, %{type: :operation, name: "issue_refund", arguments: %{"order_id" => "A1001"}}}
1 ->
{:ok, %{type: :final, content: "Refund refund-A1001 is queued."}}
end
end
{:hibernate, waiting, snapshot} =
Jidoka.Harness.run_session("refund-demo", "Refund order A1001.",
store: store,
llm: fake_llm
)
%{
status: waiting.status,
cursor: snapshot.cursor.phase,
reviews: Jidoka.Harness.pending_reviews(waiting)
}Approve And Resume
{:ok, [review]} = Jidoka.Harness.pending_reviews(store)
approval = Jidoka.Review.Response.approve(review.interrupt_id)
{:ok, finished, result} =
Jidoka.Harness.resume_session("refund-demo",
store: store,
approval: approval,
llm: fake_llm,
operations: Jidoka.Runtime.JidoActions.operations([IssueRefund])
)
%{
status: finished.status,
content: result.content,
replay: Jidoka.inspect(finished).replay
}{:ok, _timeline} = Jidoka.Kino.timeline(finished)
{:ok, _graph} = Jidoka.Kino.call_graph(finished)