ADK.Runner (ADK v0.0.1-alpha.1)

Copy Markdown View Source

Orchestrates agent execution — creates sessions, runs agents, collects events.

Summary

Functions

Find the agent that should handle the current turn based on transfer history.

Create a new Runner.

Run an agent with a message, returning a list of events.

Run an agent with async streaming — non-blocking, events delivered via messages.

Run an agent with streaming — calls on_event callback for each event as it's produced.

Types

t()

@type t() :: %ADK.Runner{
  agent: ADK.Agent.t(),
  app_name: String.t(),
  artifact_service: {module(), keyword()} | nil,
  memory_store: {module(), keyword()} | nil,
  plugins: [{module(), term()}],
  session_store: {module(), keyword()} | nil
}

Functions

find_active_agent(root_agent, session_pid)

@spec find_active_agent(ADK.Agent.t(), pid() | nil) :: ADK.Agent.t()

Find the agent that should handle the current turn based on transfer history.

Scans session events backward for the last transfer_to_agent action. If found, looks up that agent in the agent tree and returns it. If not found or the target is a non-LLM agent (SequentialAgent, LoopAgent), returns the root agent.

This mirrors Python ADK's Runner._find_agent_to_run().

new(opts)

@spec new(keyword()) :: t()

Create a new Runner.

Options

  • :app_name - application name (required)
  • :agent - the agent to run (required)
  • :session_store - optional {Module, opts} tuple for session persistence
  • :artifact_service - optional {Module, opts} tuple for artifact storage
  • :memory_store - optional {Module, opts} tuple for long-term memory

Examples

iex> agent = ADK.Agent.LlmAgent.new(name: "bot", model: "test", instruction: "Help")
iex> runner = ADK.Runner.new(app_name: "test", agent: agent)
iex> runner.app_name
"test"

run(runner, user_id, session_id, message, opts \\ [])

@spec run(t(), String.t(), String.t(), map() | String.t(), keyword()) :: [
  ADK.Event.t()
]

Run an agent with a message, returning a list of events.

Examples

iex> agent = ADK.Agent.LlmAgent.new(name: "bot", model: "test", instruction: "Help")
iex> runner = %ADK.Runner{app_name: "test", agent: agent}
iex> events = ADK.Runner.run(runner, "user1", "sess1", %{text: "hi"})
iex> is_list(events)
true

run_async(runner, user_id, session_id, message, opts \\ [])

@spec run_async(t(), String.t(), String.t(), map() | String.t(), keyword()) ::
  {:ok, pid()} | {:error, term()}

Run an agent with async streaming — non-blocking, events delivered via messages.

Spawns a supervised Task that runs the agent with the given on_event callback. Messages sent to reply_to (default: self()):

  • {:adk_event, event} for each event (via on_event callback)
  • {:adk_done, events} when the run completes
  • {:adk_error, reason} on failure

Returns {:ok, task_pid}.

Examples

{:ok, _pid} = ADK.Runner.run_async(runner, "user1", "sess1", "hi")
receive do
  {:adk_event, event} -> IO.inspect(event, label: "event")
  {:adk_done, events} -> IO.puts("Done, #{length(events)} events")
end

run_streaming(runner, user_id, session_id, message, opts \\ [])

@spec run_streaming(t(), String.t(), String.t(), map() | String.t(), keyword()) :: [
  ADK.Event.t()
]

Run an agent with streaming — calls on_event callback for each event as it's produced.

Events are delivered in real-time via the agent's execution pipeline. The on_event callback is wired into the execution context so it fires immediately as each event is generated (model response, tool call, tool result), not after the full run completes.

Runs in a supervised Task under ADK.RunnerSupervisor and sends a {:adk_done, events} message to the caller when complete. If the supervisor is not running, falls back to synchronous execution.

Options

Same as run/5 plus:

  • :on_event(ADK.Event.t() -> any()) callback invoked for each event in real-time
  • :reply_to — pid to send {:adk_done, events} when complete (default: self())

Examples

ADK.Runner.run_streaming(runner, "user1", "sess1", "hi",
  on_event: fn event -> IO.inspect(event) end)