The Runner
How the Runner orchestrates invocations, persists events, and exposes run, start, resume, and cancel entry points.
The Runner is the top-level orchestrator of every adk-rs program. It owns the agent tree and the four pluggable services, turns each user message into an invocation, persists the resulting events into the session, and hands you the event stream.
A Runner holds one root BaseAgent plus the service quartet: a required SessionService and optional ArtifactService, MemoryService, and CredentialService. It also carries a PluginManager (see Callbacks & plugins), app-level context-cache, compaction, and resumability configuration, and a registry of in-flight invocations keyed by invocation_id.
Building a Runner
Runner::builder() returns a RunnerBuilder. Three settings are required — app_name, agent, and session_service — and build() returns a config error if any is missing.
fn app_name(self, name: impl Into<String>) -> Self- App name (required). Scopes sessions, artifacts, and memory.
fn agent(self, agent: Arc<dyn BaseAgent>) -> Self- Root agent (required). Workflow agents nest their children under it.
fn session_service(self, s: Arc<dyn SessionService>) -> Self- Session backend (required). See Sessions & state.
fn artifact_service(self, s: Arc<dyn ArtifactService>) -> Self- Optional artifact backend. See Artifacts.
fn memory_service(self, s: Arc<dyn MemoryService>) -> Self- Optional long-term memory backend. See Memory.
fn credential_service(self, s: Arc<dyn CredentialService>) -> Self- Optional credential store for authenticated tools. See Auth.
fn auto_create_session(self, yes: bool) -> Self- When a
session_idis passed but not found, create it instead of returning a not-found error. fn context_cache_config(self, cfg: ContextCacheConfig) -> Self- App-level explicit context caching. Copied into each invocation’s
RunConfigunless the caller set one there. fn compaction(self, cfg: EventsCompactionConfig) -> Self- Enable automatic event compaction after invocations complete. Best-effort: failures are logged, never surfaced.
fn resumable(self, yes: bool) -> Self- Sets
ResumabilityConfig { is_resumable }: workflow agents record checkpoints andRunner::resumecontinues from the last one. See Cancellation & resume. async fn plugin(self, p: Arc<dyn BasePlugin>) -> Result<Self>- Register a plugin. Async (registration may do I/O) and fallible, so it returns
Result<Self>. fn build(self) -> Result<Runner>- Validate the required fields and construct the Runner.
run, run_with, start, resume, cancel
There are four ways into an invocation, each a thin layer over the previous. All of them load (or auto-create) the session, persist the user event first, then launch the root agent and forward its events — persisting every non-partial, non-user event through SessionService::append_event_locked before yielding it to you.
async fn run(&self, user_id, session_id: Option<&str>, user_text: &str) -> Result<EventStream<'static>>- One text turn with
RunConfig::default(). The simplest entry point. async fn run_with(&self, user_id, session_id, user_content: Content, run_config: RunConfig) -> Result<EventStream<'static>>- Typed
Contentplus an explicitRunConfig. Convenience wrapper aroundstartthat returns just the stream. async fn start(&self, user_id, session_id, user_content: Content, run_config: RunConfig) -> Result<RunningInvocation>- Start an invocation and return a handle carrying the generated
invocation_id, the sharedCancellationToken, and the event stream. Prefer this when you need to cancel or resume. async fn resume(&self, user_id, session_id: &str, invocation_id: &str, new_content: Option<Content>, run_config) -> Result<RunningInvocation>- Resume a paused invocation in place — the run keeps its
invocation_id.new_contenttypically carries theFunctionResponsethat unblocks the pause: a long-running tool result, anadk_request_confirmationdecision, or anadk_request_credentialconsent. fn cancel(&self, invocation_id: &str) -> bool- Flip the cancellation token of an in-flight invocation. Returns
falsefor unknown ids. Cooperative: agents check the flag between LLM calls and tool dispatches; in-flight tools run to completion. fn is_active(&self, invocation_id: &str) -> bool- Whether an invocation with this id is currently registered as in-flight.
RunConfig
RunConfig (in adk_rs::core::run_config) carries per-invocation overrides. context_cache_config and resumability are usually inherited from the runner; an explicit value on the RunConfig wins.
| Field | Type | Meaning |
|---|---|---|
streaming_mode | StreamingMode | None (default) or Sse. See Streaming. |
max_llm_calls | Option<u32> | Stop after this many LLM turns. None = unlimited (use with care). Enforced by InvocationContext::check_and_inc_llm_call. |
context_cache_config | Option<ContextCacheConfig> | Explicit context caching; overrides the runner-level setting. |
resumability | Option<ResumabilityConfig> | Pause/resume behaviour; overrides the runner-level setting. |
InvocationContext
For every invocation the runner builds an Arc<InvocationContext> and threads it through the agent tree, tools, callbacks, and plugins. It exposes the identity triple (app_name, user_id, invocation_id), the live session as Arc<Mutex<Session>> (so the runner and tool callbacks can both mutate state safely), all four services, the run_config, the original user_content, an LLM call counter, the shared cancellation token, an attributes bag (Arc<Mutex<HashMap<String, Value>>>) used by plugins and the auth/confirmation preprocessors, and root_agent: Option<Arc<dyn BaseAgent>> — set by the runner to the root of the agent tree and used by agent transfer to reach siblings and ancestors.
fn new_id() -> String- Generate a fresh
inv-<uuid>invocation id. fn check_and_inc_llm_call(&self) -> Result<()>- Increment the LLM call counter; errors once
RunConfig::max_llm_callsis reached. fn is_cancelled(&self) -> bool- True if the cancellation token has been flipped. Agents call this at safe points to halt cleanly.
The origin: InvocationOrigin field records where the invocation came from — Api (default), Cli, or Web. It is mostly informational.
A complete example
use adk_rs::agents::LlmAgent;
use adk_rs::core::{RunConfig, StreamingMode};
use adk_rs::providers::gemini::Gemini;
use adk_rs::runner::Runner;
use adk_rs::services::mem::{
InMemoryArtifactService, InMemoryMemoryService, InMemorySessionService,
};
use adk_rs::genai_types::Content;
use futures::StreamExt;
use std::sync::Arc;
#[tokio::main]
async fn main() -> adk_rs::Result<()> {
let agent = LlmAgent::builder("assistant")
.model(Arc::new(Gemini::from_env("gemini-2.5-flash")?))
.instruction("You are a helpful assistant.")
.build()?;
let runner = Runner::builder()
.app_name("demo")
.agent(Arc::new(agent))
.session_service(Arc::new(InMemorySessionService::new()))
.artifact_service(Arc::new(InMemoryArtifactService::new()))
.memory_service(Arc::new(InMemoryMemoryService::new()))
.auto_create_session(true)
.resumable(true)
.build()?;
// start() hands back the invocation id so you can cancel later.
let run_config = RunConfig {
streaming_mode: StreamingMode::Sse,
max_llm_calls: Some(10),
..RunConfig::default()
};
let handle = runner
.start("alice", Some("s1"), Content::user_text("Hello!"), run_config)
.await?;
println!("invocation: {}", handle.invocation_id);
let mut events = handle.events;
while let Some(event) = events.next().await {
let event = event?;
if let Some(content) = &event.response.content {
println!("[{}] {}", event.author, content.text_concat());
}
}
Ok(())
}Because the example requests StreamingMode::Sse, the stream contains transient partial: Some(true) token events before the final aggregated event of each model turn. See Streaming.
Related pages
- Events — the values the runner persists and yields.
- Streaming —
StreamingModeand partial events. - Cancellation & resume — pause/resume mechanics in depth.
- Callbacks & plugins —
before_run,on_event,after_runhooks.