Packages
oban_codex
0.1.0
Run Codex jobs on an Oban queue, with validated args, result helpers, telemetry, and an optional long-lived agent lifecycle.
Current section
Files
Jump to
Current section
Files
examples/propose_dispose.exs
# Structured propose/dispose without a database:
# Codex proposes a typed action under read-only policy; a deterministic handler
# disposes it. The Codex turn is stubbed, but the worker seam is real.
#
# mix run examples/propose_dispose.exs
defmodule ProposeDispose.Sink do
use Agent
def start_link(_), do: Agent.start_link(fn -> [] end, name: __MODULE__)
def record(value), do: Agent.update(__MODULE__, &[value | &1])
def all, do: Agent.get(__MODULE__, &Enum.reverse/1)
end
defmodule ProposeDispose.Worker do
use ObanCodex.Worker,
queue: :triage,
max_attempts: 2,
pinned_args:
ObanCodex.Args.defaults(
sandbox: :read_only,
approval_policy: :never,
output_schema: "/srv/app/priv/triage.schema.json"
),
query_fun: &__MODULE__.fake_query/2
def fake_query(prompt, _opts) do
{:ok,
ObanCodex.Testing.structured_result(%{
"outcome" => prompt,
"summary" => "proposal for #{prompt}"
})}
end
@impl ObanCodex.Worker
def handle_result(result, job) do
case ObanCodex.structured(result) do
%{"outcome" => "fix", "summary" => summary} ->
ProposeDispose.Sink.record({:implement, job.args["issue"], summary})
:ok
%{"outcome" => "needs_review", "summary" => summary} ->
ProposeDispose.Sink.record({:review, job.args["issue"], summary})
:ok
%{"outcome" => "wontfix"} ->
{:cancel, :wontfix}
end
end
end
{:ok, _} = ProposeDispose.Sink.start_link(nil)
for {issue, outcome} <- [{87, "fix"}, {88, "needs_review"}, {89, "wontfix"}] do
args = %{"prompt" => outcome, "issue" => issue}
verdict = ProposeDispose.Worker.perform(%Oban.Job{args: args})
IO.puts("issue #{issue}: #{inspect(verdict)}")
end
IO.inspect(ProposeDispose.Sink.all(), label: "disposed effects")