ObanClaude (oban_claude v0.1.0)

Copy Markdown View Source

Run a Claude Code job (via claude_wrapper) on an Oban queue.

oban_claude is the thin seam between Oban and claude:

It owns no state and runs no daemon, and takes no position on what consumes a result. Whether the agent effects its own writes (full-auto) or returns structured data for a downstream effector is encoded in the job's args (prompt + permission mode), never here, so oban_claude supports both.

Example

Build the job's args with ObanClaude.Args.new/1 (atom keys, validated) rather than a hand-written string map:

defmodule MyApp.ClaudeJob do
  use ObanClaude.Worker, queue: :claude, max_attempts: 3
end

ObanClaude.Args.new(prompt: "summarize the repo", working_dir: "/path/to/repo")
|> MyApp.ClaudeJob.new()
|> Oban.insert()

The raw string-keyed map is still accepted as the low-level escape hatch:

%{"prompt" => "summarize the repo", "working_dir" => "/path/to/repo"}
|> MyApp.ClaudeJob.new()
|> Oban.insert()

Telemetry

run/2 emits the following events via :telemetry:

[:oban_claude, :run, :stop]

Emitted after a successful ClaudeWrapper.query/2 call. This includes is_error: true results, which are returned without raising.

  • Measurements:
    • :duration -- wall time of the query in native time units (convert with System.convert_time_unit/3)
    • :cost_usd -- the reported cost in USD from the result (0.0 when the result carries no cost)
  • Metadata:
    • :result -- the %ClaudeWrapper.Result{} struct
    • :args -- the string-keyed args map passed to run/2

[:oban_claude, :run, :exception]

Emitted when ClaudeWrapper.query/2 returns {:error, %ClaudeWrapper.Error{}}.

  • Measurements:
    • :duration -- wall time of the query in native time units
  • Metadata:
    • :error -- the %ClaudeWrapper.Error{} struct
    • :args -- the string-keyed args map passed to run/2

Summary

Functions

Read the "outcome" string from a result's structured_output, when a --json-schema run produced one. Returns nil otherwise.

Run a claude job from a string-keyed args map.

Return the full schema-validated structured_output from a result (a --json-schema run), or nil when the run produced none.

Types

oban_return()

@type oban_return() ::
  :ok
  | {:ok, term()}
  | {:error, term()}
  | {:cancel, term()}
  | {:snooze, pos_integer()}

An Oban.Worker.perform/1 return value.

Functions

outcome(result)

@spec outcome(ClaudeWrapper.Result.t()) :: String.t() | nil

Read the "outcome" string from a result's structured_output, when a --json-schema run produced one. Returns nil otherwise.

Useful inside ObanClaude.Worker.handle_result/2 to branch on a typed outcome (e.g. "done" vs "blocked").

run(args, opts \\ [])

Run a claude job from a string-keyed args map.

Returns {oban_return, %Result{} | %Error{}} so a caller can both act on the Oban verdict and inspect the underlying run (cost, session id, structured output). :prompt is required; every key in @passthrough is forwarded to ClaudeWrapper.query/2.

Options:

  • :classifier -- a 1-arity fun mapping the claude call's result onto {oban_return, term}. Defaults to &ObanClaude.Outcome.classify/1.
  • :query_fun -- the claude entrypoint: a 2-arity fun (prompt, query_opts) -> {:ok, %Result{}} | {:error, %Error{}}. Defaults to &ClaudeWrapper.query/2. Override to stub claude in tests, or to route through a different wrapper entrypoint.

structured(result)

@spec structured(ClaudeWrapper.Result.t()) :: map() | list() | nil

Return the full schema-validated structured_output from a result (a --json-schema run), or nil when the run produced none.

Use inside ObanClaude.Worker.handle_result/2 to branch on a typed result object. outcome/1 is the convenience for the common "outcome" key.