From bde6353c57c3f69023566a95d2a9f96a2eea74fe Mon Sep 17 00:00:00 2001 From: Toni Klopfenstein Date: Wed, 20 May 2026 21:16:03 +0000 Subject: [PATCH 1/8] adding Runtime kotlin snippets --- docs/runtime/event-loop.md | 26 +++- docs/runtime/index.md | 2 +- docs/runtime/resume.md | 22 ++-- docs/runtime/runconfig.md | 14 ++- .../snippets/runtime/RunConfigExample.kt | 56 +++++++++ .../kotlin/snippets/runtime/RunnerLoop.kt | 113 ++++++++++++++++++ 6 files changed, 223 insertions(+), 10 deletions(-) create mode 100644 examples/kotlin/snippets/runtime/RunConfigExample.kt create mode 100644 examples/kotlin/snippets/runtime/RunnerLoop.kt diff --git a/docs/runtime/event-loop.md b/docs/runtime/event-loop.md index 3f41480e1d..c2c2efea7d 100644 --- a/docs/runtime/event-loop.md +++ b/docs/runtime/event-loop.md @@ -1,7 +1,7 @@ # Runtime Event Loop
- Supported in ADKPython v0.1.0Typescript v0.2.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0Typescript v0.2.0Go v0.1.0Java v0.1.0Kotlin v0.1.0
The ADK Runtime is the underlying engine that powers your agent application during user interactions. It's the system that takes your defined agents, tools, and callbacks and orchestrates their execution in response to user input, managing the flow of information, state changes, and interactions with external services like LLMs or storage. @@ -179,6 +179,12 @@ The `Runner` acts as the central coordinator for a single user invocation. Its r } ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunnerLoop.kt:conceptual_loop" + ``` + ### Execution Logic's Role (Agent, Tool, Callback) Your code within agents, tools, and callbacks is responsible for the actual computation and decision-making. Its interaction with the loop involves: @@ -372,6 +378,12 @@ Your code within agents, tools, and callbacks is responsible for the actual comp // If this subsequent code needs to yield another event, it would do so here. ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunnerLoop.kt:execution_logic" + ``` + This cooperative yield/pause/resume cycle between the `Runner` and your Execution Logic, mediated by `Event` objects, forms the core of the ADK Runtime. ## Key components of the Runtime @@ -588,6 +600,12 @@ Understanding a few key aspects of how the ADK Runtime handles state, streaming, // or emitting more events based on the now-updated `ctx.session().state()`. ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunnerLoop.kt:state_update_timing" + ``` + ### "Dirty Reads" of Session State * **Definition:** While commitment happens *after* the yield, code running *later within the same invocation*, but *before* the state-changing event is actually yielded and processed, **can often see the local, uncommitted changes**. This is sometimes called a "dirty read". @@ -666,6 +684,12 @@ Understanding a few key aspects of how the ADK Runtime handles state, streaming, // is yielded *after* this tool runs and is processed by the Runner. ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunnerLoop.kt:dirty_read" + ``` + * **Implications:** * **Benefit:** Allows different parts of your logic within a single complex step (e.g., multiple callbacks or tool calls before the next LLM turn) to coordinate using state without waiting for a full yield/commit cycle. * **Caveat:** Relying heavily on dirty reads for critical logic can be risky. If the invocation fails *before* the event carrying the `state_delta` is yielded and processed by the `Runner`, the uncommitted state change will be lost. For critical state transitions, ensure they are associated with an event that gets successfully processed. diff --git a/docs/runtime/index.md b/docs/runtime/index.md index 463e0934bc..c7b57f69fc 100644 --- a/docs/runtime/index.md +++ b/docs/runtime/index.md @@ -1,7 +1,7 @@ # Agent Runtime
- Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0Kotlin v0.1.0
ADK provides several ways to run and test your agents during development. Choose diff --git a/docs/runtime/resume.md b/docs/runtime/resume.md index dda948e771..741bf17fa6 100644 --- a/docs/runtime/resume.md +++ b/docs/runtime/resume.md @@ -1,7 +1,7 @@ # Resume stopped agents
- Supported in ADKPython v1.14.0 + Supported in ADKPython v1.14.0Kotlin v0.1.0
An ADK agent's execution can be interrupted by various factors including @@ -77,13 +77,21 @@ curl -X POST http://localhost:8000/run_sse \ You can also resume a workflow using the Runner object Run Async method, as shown below: -```python -runner.run_async(user_id='u_123', session_id='s_abc', - invocation_id='invocation-123') +=== "Python" -# When new_message is set to a function response, -# we are trying to resume a long running function. -``` + ```python + runner.run_async(user_id='u_123', session_id='s_abc', + invocation_id='invocation-123') + + # When new_message is set to a function response, + # we are trying to resume a long running function. + ``` + +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunConfigExample.kt:resume_usage" + ``` !!! info "Note" Resuming a workflow from the ADK Web user interface or using the ADK diff --git a/docs/runtime/runconfig.md b/docs/runtime/runconfig.md index 422b49cada..5a8ab2edc6 100644 --- a/docs/runtime/runconfig.md +++ b/docs/runtime/runconfig.md @@ -1,7 +1,7 @@ # Runtime Configuration
- Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0Kotlin v0.1.0
`RunConfig` controls how agents behave at runtime, including streaming mode, @@ -58,6 +58,12 @@ to `runner.run_async()` or `runner.run_live()` to override default behavior. .build(); ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunConfigExample.kt:basic_usage" + ``` + ## Manage sessions and context
@@ -151,6 +157,12 @@ execute function calls. CFC uses the Live API under the hood. .build(); ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunConfigExample.kt:streaming_config" + ``` + ## Configure audio and speech
diff --git a/examples/kotlin/snippets/runtime/RunConfigExample.kt b/examples/kotlin/snippets/runtime/RunConfigExample.kt new file mode 100644 index 0000000000..5fbf8e0a89 --- /dev/null +++ b/examples/kotlin/snippets/runtime/RunConfigExample.kt @@ -0,0 +1,56 @@ +package com.google.adk.kt.examples.runtime + +import com.google.adk.kt.agents.RunConfig +import com.google.adk.kt.agents.StreamingMode +import com.google.adk.kt.runners.InMemoryRunner +import com.google.adk.kt.types.Content +import com.google.adk.kt.types.Role +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.runBlocking + +// --8<-- [start:basic_usage] +val config = RunConfig( + streamingMode = StreamingMode.SSE, +) + +// Pass it to runner.runAsync +// runner.runAsync(..., runConfig = config) +// --8<-- [end:basic_usage] + +// --8<-- [start:full_example] +fun runWithConfig(runner: InMemoryRunner) = runBlocking { + val config = RunConfig( + streamingMode = StreamingMode.SSE, + customMetadata = mapOf("priority" to "high") + ) + + runner.runAsync( + userId = "user123", + sessionId = "session456", + newMessage = Content.fromText(Role.USER, "Hello!"), + runConfig = config + ).collect { event -> + // handle events + } +} +// --8<-- [end:full_example] + +// --8<-- [start:custom_metadata] +val metadataConfig = RunConfig( + customMetadata = mapOf("user_tier" to "premium") +) +// --8<-- [end:custom_metadata] + +// --8<-- [start:resume_usage] +fun resumeAgent(runner: InMemoryRunner) = runBlocking { + runner.runAsync( + userId = "user123", + sessionId = "session456", + invocationId = "previous-invocation-id" + ).collect { event -> + // resume execution from previous state + } +} +// --8<-- [end:resume_usage] + + diff --git a/examples/kotlin/snippets/runtime/RunnerLoop.kt b/examples/kotlin/snippets/runtime/RunnerLoop.kt new file mode 100644 index 0000000000..adcccb44fe --- /dev/null +++ b/examples/kotlin/snippets/runtime/RunnerLoop.kt @@ -0,0 +1,113 @@ +package com.google.adk.kt.examples.runtime + +import com.google.adk.kt.agents.LlmAgent +import com.google.adk.kt.events.Event +import com.google.adk.kt.runners.InMemoryRunner +import com.google.adk.kt.sessions.InMemorySessionService +import com.google.adk.kt.types.Content +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onEach + +// --8<-- [start:conceptual_loop] +/** + * Simplified view of Runner's main loop logic in Kotlin + */ +fun runAsync( + userId: String, + sessionId: String, + newMessage: Content, + runner: InMemoryRunner, + sessionService: InMemorySessionService +): Flow { + // 1. Append newMessage to session event history (via SessionService) + // 2. Kick off event loop by calling the agent + // 3. Process generated events, commit changes, and yield upstream + return runner.runAsync( + userId = userId, + sessionId = sessionId, + newMessage = newMessage + ).onEach { event -> + // Process the event and commit changes to services (done internally by Runner) + // sessionService.appendEvent(...) + } +} +// --8<-- [end:conceptual_loop] + +// --8<-- [start:execution_logic] +/** + * Simplified view of logic inside Agent.runAsync, callbacks, or tools in Kotlin + */ +suspend fun executionLogic(ctx: com.google.adk.kt.agents.InvocationContext) { + // ... previous code runs based on current state ... + + // 1. Determine a change or output is needed, construct the event + val updateData = mapOf("field_1" to "value_2") + val eventWithStateChange = com.google.adk.kt.events.Event( + author = "my_agent", + actions = com.google.adk.kt.events.EventActions(stateDelta = updateData.toMutableMap()), + content = com.google.adk.kt.types.Content.fromText(com.google.adk.kt.types.Role.MODEL, "State updated.") + ) + + // 2. Yield the event to the Runner for processing & commit + // In Kotlin, this is done by emitting to the Flow + // emit(eventWithStateChange) + + // <<<<<<<<<<<< EXECUTION PAUSES HERE >>>>>>>>>>>> + // (Implicitly, when the Flow consumer collects the event and processes it) + + // <<<<<<<<<<<< RUNNER PROCESSES & COMMITS THE EVENT >>>>>>>>>>>> + + // 3. Resume execution ONLY after Runner is done processing. + // Now, the state committed by the Runner is reliably reflected. + val val1 = ctx.session.state["field_1"] + println("Resumed execution. Value of field_1 is now: $val1") +} +// --8<-- [end:execution_logic] + +// --8<-- [start:state_update_timing] +/** + * Conceptual view of state update timing in Kotlin + */ +suspend fun stateUpdateTiming(ctx: com.google.adk.kt.agents.InvocationContext) { + // 1. Modify state + ctx.session.state["status"] = "processing" + val event1 = com.google.adk.kt.events.Event( + author = "my_agent", + actions = com.google.adk.kt.events.EventActions(stateDelta = mutableMapOf("status" to "processing")) + ) + + // 2. Yield event with the delta (emit to flow) + // emit(event1) + + // --- PAUSE --- Runner processes event1, SessionService commits 'status' = 'processing' --- + + // 3. Resume execution + // Now it's safe to rely on the committed state + val currentStatus = ctx.session.state["status"] // Guaranteed to be 'processing' + println("Status after resuming: $currentStatus") +} +// --8<-- [end:state_update_timing] + +// --8<-- [start:dirty_read] +/** + * Conceptual view of dirty reads in Kotlin + */ +fun dirtyRead(ctx: com.google.adk.kt.agents.InvocationContext) { + // Code in a callback + ctx.session.state["field_1"] = "value_1" + // State is locally set to 'value_1', but not yet committed by Runner + + // ... agent runs ... + + // Code in a tool called later *within the same invocation* + // Readable (dirty read), but 'value_1' isn't guaranteed persistent yet. + val val1 = ctx.session.state["field_1"] // 'val' will likely be 'value_1' here + println("Dirty read value in tool: $val1") + + // Assume the event carrying the state_delta={'field_1': 'value_1'} + // is yielded *after* this tool runs and is processed by the Runner. +} +// --8<-- [end:dirty_read] + + + From 407612da6cc7c32b4be6e7dd56b3e3166953b2d3 Mon Sep 17 00:00:00 2001 From: Toni Klopfenstein Date: Wed, 20 May 2026 23:26:31 +0000 Subject: [PATCH 2/8] Adding kotlin snippets into files to test --- examples/kotlin/snippets/runtime/RunConfigExample.kt | 6 ++++++ tools/kotlin-snippets/files_to_test.txt | 3 +++ 2 files changed, 9 insertions(+) diff --git a/examples/kotlin/snippets/runtime/RunConfigExample.kt b/examples/kotlin/snippets/runtime/RunConfigExample.kt index 5fbf8e0a89..7df00a2642 100644 --- a/examples/kotlin/snippets/runtime/RunConfigExample.kt +++ b/examples/kotlin/snippets/runtime/RunConfigExample.kt @@ -41,6 +41,12 @@ val metadataConfig = RunConfig( ) // --8<-- [end:custom_metadata] +// --8<-- [start:streaming_config] +val streamingConfig = RunConfig( + streamingMode = StreamingMode.SSE, +) +// --8<-- [end:streaming_config] + // --8<-- [start:resume_usage] fun resumeAgent(runner: InMemoryRunner) = runBlocking { runner.runAsync( diff --git a/tools/kotlin-snippets/files_to_test.txt b/tools/kotlin-snippets/files_to_test.txt index 562f398067..7746771855 100644 --- a/tools/kotlin-snippets/files_to_test.txt +++ b/tools/kotlin-snippets/files_to_test.txt @@ -28,4 +28,7 @@ snippets/tools/function-tools/AgentTool.kt snippets/tools/function-tools/PassData.kt snippets/tools/LimitationsWorkaround.kt snippets/get-started/multi_tool_agent/MultiToolAgent.kt +snippets/runtime/RunConfigExample.kt +snippets/runtime/RunnerLoop.kt + From f64bf1f0a5a181b01935301fa2d2b1f9742169e7 Mon Sep 17 00:00:00 2001 From: Kristopher Overholt Date: Thu, 21 May 2026 12:15:34 -0500 Subject: [PATCH 3/8] Linting fixes via ktlint --- .../snippets/runtime/RunConfigExample.kt | 70 ++++++++++--------- .../kotlin/snippets/runtime/RunnerLoop.kt | 60 ++++++++++------ 2 files changed, 75 insertions(+), 55 deletions(-) diff --git a/examples/kotlin/snippets/runtime/RunConfigExample.kt b/examples/kotlin/snippets/runtime/RunConfigExample.kt index 7df00a2642..7095b63af7 100644 --- a/examples/kotlin/snippets/runtime/RunConfigExample.kt +++ b/examples/kotlin/snippets/runtime/RunConfigExample.kt @@ -9,54 +9,60 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.runBlocking // --8<-- [start:basic_usage] -val config = RunConfig( - streamingMode = StreamingMode.SSE, -) +val config = + RunConfig( + streamingMode = StreamingMode.SSE, + ) // Pass it to runner.runAsync // runner.runAsync(..., runConfig = config) // --8<-- [end:basic_usage] // --8<-- [start:full_example] -fun runWithConfig(runner: InMemoryRunner) = runBlocking { - val config = RunConfig( - streamingMode = StreamingMode.SSE, - customMetadata = mapOf("priority" to "high") - ) +fun runWithConfig(runner: InMemoryRunner) = + runBlocking { + val config = + RunConfig( + streamingMode = StreamingMode.SSE, + customMetadata = mapOf("priority" to "high"), + ) - runner.runAsync( - userId = "user123", - sessionId = "session456", - newMessage = Content.fromText(Role.USER, "Hello!"), - runConfig = config - ).collect { event -> - // handle events + runner + .runAsync( + userId = "user123", + sessionId = "session456", + newMessage = Content.fromText(Role.USER, "Hello!"), + runConfig = config, + ).collect { event -> + // handle events + } } -} // --8<-- [end:full_example] // --8<-- [start:custom_metadata] -val metadataConfig = RunConfig( - customMetadata = mapOf("user_tier" to "premium") -) +val metadataConfig = + RunConfig( + customMetadata = mapOf("user_tier" to "premium"), + ) // --8<-- [end:custom_metadata] // --8<-- [start:streaming_config] -val streamingConfig = RunConfig( - streamingMode = StreamingMode.SSE, -) +val streamingConfig = + RunConfig( + streamingMode = StreamingMode.SSE, + ) // --8<-- [end:streaming_config] // --8<-- [start:resume_usage] -fun resumeAgent(runner: InMemoryRunner) = runBlocking { - runner.runAsync( - userId = "user123", - sessionId = "session456", - invocationId = "previous-invocation-id" - ).collect { event -> - // resume execution from previous state +fun resumeAgent(runner: InMemoryRunner) = + runBlocking { + runner + .runAsync( + userId = "user123", + sessionId = "session456", + invocationId = "previous-invocation-id", + ).collect { event -> + // resume execution from previous state + } } -} // --8<-- [end:resume_usage] - - diff --git a/examples/kotlin/snippets/runtime/RunnerLoop.kt b/examples/kotlin/snippets/runtime/RunnerLoop.kt index adcccb44fe..137f231ad5 100644 --- a/examples/kotlin/snippets/runtime/RunnerLoop.kt +++ b/examples/kotlin/snippets/runtime/RunnerLoop.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onEach // --8<-- [start:conceptual_loop] + /** * Simplified view of Runner's main loop logic in Kotlin */ @@ -17,23 +18,25 @@ fun runAsync( sessionId: String, newMessage: Content, runner: InMemoryRunner, - sessionService: InMemorySessionService + sessionService: InMemorySessionService, ): Flow { // 1. Append newMessage to session event history (via SessionService) // 2. Kick off event loop by calling the agent // 3. Process generated events, commit changes, and yield upstream - return runner.runAsync( - userId = userId, - sessionId = sessionId, - newMessage = newMessage - ).onEach { event -> - // Process the event and commit changes to services (done internally by Runner) - // sessionService.appendEvent(...) - } + return runner + .runAsync( + userId = userId, + sessionId = sessionId, + newMessage = newMessage, + ).onEach { event -> + // Process the event and commit changes to services (done internally by Runner) + // sessionService.appendEvent(...) + } } // --8<-- [end:conceptual_loop] // --8<-- [start:execution_logic] + /** * Simplified view of logic inside Agent.runAsync, callbacks, or tools in Kotlin */ @@ -42,16 +45,23 @@ suspend fun executionLogic(ctx: com.google.adk.kt.agents.InvocationContext) { // 1. Determine a change or output is needed, construct the event val updateData = mapOf("field_1" to "value_2") - val eventWithStateChange = com.google.adk.kt.events.Event( - author = "my_agent", - actions = com.google.adk.kt.events.EventActions(stateDelta = updateData.toMutableMap()), - content = com.google.adk.kt.types.Content.fromText(com.google.adk.kt.types.Role.MODEL, "State updated.") - ) + val eventWithStateChange = + com.google.adk.kt.events.Event( + author = "my_agent", + actions = + com.google.adk.kt.events + .EventActions(stateDelta = updateData.toMutableMap()), + content = + com.google.adk.kt.types.Content.fromText( + com.google.adk.kt.types.Role.MODEL, + "State updated.", + ), + ) // 2. Yield the event to the Runner for processing & commit // In Kotlin, this is done by emitting to the Flow - // emit(eventWithStateChange) - + // emit(eventWithStateChange) + // <<<<<<<<<<<< EXECUTION PAUSES HERE >>>>>>>>>>>> // (Implicitly, when the Flow consumer collects the event and processes it) @@ -65,16 +75,22 @@ suspend fun executionLogic(ctx: com.google.adk.kt.agents.InvocationContext) { // --8<-- [end:execution_logic] // --8<-- [start:state_update_timing] + /** * Conceptual view of state update timing in Kotlin */ suspend fun stateUpdateTiming(ctx: com.google.adk.kt.agents.InvocationContext) { // 1. Modify state ctx.session.state["status"] = "processing" - val event1 = com.google.adk.kt.events.Event( - author = "my_agent", - actions = com.google.adk.kt.events.EventActions(stateDelta = mutableMapOf("status" to "processing")) - ) + val event1 = + com.google.adk.kt.events.Event( + author = "my_agent", + actions = + com.google.adk.kt.events.EventActions( + stateDelta = + mutableMapOf("status" to "processing"), + ), + ) // 2. Yield event with the delta (emit to flow) // emit(event1) @@ -89,6 +105,7 @@ suspend fun stateUpdateTiming(ctx: com.google.adk.kt.agents.InvocationContext) { // --8<-- [end:state_update_timing] // --8<-- [start:dirty_read] + /** * Conceptual view of dirty reads in Kotlin */ @@ -108,6 +125,3 @@ fun dirtyRead(ctx: com.google.adk.kt.agents.InvocationContext) { // is yielded *after* this tool runs and is processed by the Runner. } // --8<-- [end:dirty_read] - - - From 00fa05891011209777f8e01572bd4dc1d19528ef Mon Sep 17 00:00:00 2001 From: Kristopher Overholt Date: Thu, 21 May 2026 12:19:33 -0500 Subject: [PATCH 4/8] Simplify imports --- .../kotlin/snippets/runtime/RunnerLoop.kt | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/examples/kotlin/snippets/runtime/RunnerLoop.kt b/examples/kotlin/snippets/runtime/RunnerLoop.kt index 137f231ad5..fb49bdf4b8 100644 --- a/examples/kotlin/snippets/runtime/RunnerLoop.kt +++ b/examples/kotlin/snippets/runtime/RunnerLoop.kt @@ -1,10 +1,13 @@ package com.google.adk.kt.examples.runtime +import com.google.adk.kt.agents.InvocationContext import com.google.adk.kt.agents.LlmAgent import com.google.adk.kt.events.Event +import com.google.adk.kt.events.EventActions import com.google.adk.kt.runners.InMemoryRunner import com.google.adk.kt.sessions.InMemorySessionService import com.google.adk.kt.types.Content +import com.google.adk.kt.types.Role import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onEach @@ -40,22 +43,16 @@ fun runAsync( /** * Simplified view of logic inside Agent.runAsync, callbacks, or tools in Kotlin */ -suspend fun executionLogic(ctx: com.google.adk.kt.agents.InvocationContext) { +suspend fun executionLogic(ctx: InvocationContext) { // ... previous code runs based on current state ... // 1. Determine a change or output is needed, construct the event val updateData = mapOf("field_1" to "value_2") val eventWithStateChange = - com.google.adk.kt.events.Event( + Event( author = "my_agent", - actions = - com.google.adk.kt.events - .EventActions(stateDelta = updateData.toMutableMap()), - content = - com.google.adk.kt.types.Content.fromText( - com.google.adk.kt.types.Role.MODEL, - "State updated.", - ), + actions = EventActions(stateDelta = updateData.toMutableMap()), + content = Content.fromText(Role.MODEL, "State updated."), ) // 2. Yield the event to the Runner for processing & commit @@ -79,17 +76,13 @@ suspend fun executionLogic(ctx: com.google.adk.kt.agents.InvocationContext) { /** * Conceptual view of state update timing in Kotlin */ -suspend fun stateUpdateTiming(ctx: com.google.adk.kt.agents.InvocationContext) { +suspend fun stateUpdateTiming(ctx: InvocationContext) { // 1. Modify state ctx.session.state["status"] = "processing" val event1 = - com.google.adk.kt.events.Event( + Event( author = "my_agent", - actions = - com.google.adk.kt.events.EventActions( - stateDelta = - mutableMapOf("status" to "processing"), - ), + actions = EventActions(stateDelta = mutableMapOf("status" to "processing")), ) // 2. Yield event with the delta (emit to flow) @@ -109,7 +102,7 @@ suspend fun stateUpdateTiming(ctx: com.google.adk.kt.agents.InvocationContext) { /** * Conceptual view of dirty reads in Kotlin */ -fun dirtyRead(ctx: com.google.adk.kt.agents.InvocationContext) { +fun dirtyRead(ctx: InvocationContext) { // Code in a callback ctx.session.state["field_1"] = "value_1" // State is locally set to 'value_1', but not yet committed by Runner From f8209abeca2b117a205a458074a6ea8c903ec613 Mon Sep 17 00:00:00 2001 From: Kristopher Overholt Date: Thu, 21 May 2026 12:20:20 -0500 Subject: [PATCH 5/8] Add license headers --- .../kotlin/snippets/runtime/RunConfigExample.kt | 16 ++++++++++++++++ examples/kotlin/snippets/runtime/RunnerLoop.kt | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/examples/kotlin/snippets/runtime/RunConfigExample.kt b/examples/kotlin/snippets/runtime/RunConfigExample.kt index 7095b63af7..95a7ff6ae5 100644 --- a/examples/kotlin/snippets/runtime/RunConfigExample.kt +++ b/examples/kotlin/snippets/runtime/RunConfigExample.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.kt.examples.runtime import com.google.adk.kt.agents.RunConfig diff --git a/examples/kotlin/snippets/runtime/RunnerLoop.kt b/examples/kotlin/snippets/runtime/RunnerLoop.kt index fb49bdf4b8..f298ce648d 100644 --- a/examples/kotlin/snippets/runtime/RunnerLoop.kt +++ b/examples/kotlin/snippets/runtime/RunnerLoop.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.kt.examples.runtime import com.google.adk.kt.agents.InvocationContext From 70995db35c897ec977af44881d8b90403d9264be Mon Sep 17 00:00:00 2001 From: Kristopher Overholt Date: Thu, 21 May 2026 12:36:51 -0500 Subject: [PATCH 6/8] Add code snippet for resume config --- docs/runtime/resume.md | 28 ++++++++++++------- .../snippets/runtime/RunConfigExample.kt | 14 ++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/docs/runtime/resume.md b/docs/runtime/resume.md index 741bf17fa6..650b12dcf6 100644 --- a/docs/runtime/resume.md +++ b/docs/runtime/resume.md @@ -23,16 +23,24 @@ Enable the Resume function for an agent workflow by applying a Resumability configuration to the App object of your ADK workflow, as shown in the following code example: -```python -app = App( - name='my_resumable_agent', - root_agent=root_agent, - # Set the resumability config to enable resumability. - resumability_config=ResumabilityConfig( - is_resumable=True, - ), -) -``` +=== "Python" + + ```python + app = App( + name='my_resumable_agent', + root_agent=root_agent, + # Set the resumability config to enable resumability. + resumability_config=ResumabilityConfig( + is_resumable=True, + ), + ) + ``` + +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/runtime/RunConfigExample.kt:resumability_config" + ``` !!! warning "Caution: Long Running Functions, Confirmations, Authentication" For agents that use diff --git a/examples/kotlin/snippets/runtime/RunConfigExample.kt b/examples/kotlin/snippets/runtime/RunConfigExample.kt index 95a7ff6ae5..f434a901ef 100644 --- a/examples/kotlin/snippets/runtime/RunConfigExample.kt +++ b/examples/kotlin/snippets/runtime/RunConfigExample.kt @@ -16,9 +16,12 @@ package com.google.adk.kt.examples.runtime +import com.google.adk.kt.agents.ResumabilityConfig import com.google.adk.kt.agents.RunConfig import com.google.adk.kt.agents.StreamingMode +import com.google.adk.kt.annotations.ExperimentalResumabilityFeature import com.google.adk.kt.runners.InMemoryRunner +import com.google.adk.kt.sessions.InMemorySessionService import com.google.adk.kt.types.Content import com.google.adk.kt.types.Role import kotlinx.coroutines.flow.collect @@ -69,6 +72,17 @@ val streamingConfig = ) // --8<-- [end:streaming_config] +// --8<-- [start:resumability_config] +@OptIn(ExperimentalResumabilityFeature::class) +val runner = + InMemoryRunner( + agent = rootAgent, + appName = "my_resumable_agent", + sessionService = InMemorySessionService(), + resumabilityConfig = ResumabilityConfig(isResumable = true), + ) +// --8<-- [end:resumability_config] + // --8<-- [start:resume_usage] fun resumeAgent(runner: InMemoryRunner) = runBlocking { From dd897d5783c82a399fccbaf00ceebbf6a843738a Mon Sep 17 00:00:00 2001 From: Kristopher Overholt Date: Thu, 21 May 2026 12:55:53 -0500 Subject: [PATCH 7/8] Remove unused import --- examples/kotlin/snippets/runtime/RunnerLoop.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/kotlin/snippets/runtime/RunnerLoop.kt b/examples/kotlin/snippets/runtime/RunnerLoop.kt index f298ce648d..14fd83cc60 100644 --- a/examples/kotlin/snippets/runtime/RunnerLoop.kt +++ b/examples/kotlin/snippets/runtime/RunnerLoop.kt @@ -17,7 +17,6 @@ package com.google.adk.kt.examples.runtime import com.google.adk.kt.agents.InvocationContext -import com.google.adk.kt.agents.LlmAgent import com.google.adk.kt.events.Event import com.google.adk.kt.events.EventActions import com.google.adk.kt.runners.InMemoryRunner From b0ee9bec78dc365b36348228a1546c2ba4cfa004 Mon Sep 17 00:00:00 2001 From: Kristopher Overholt Date: Thu, 21 May 2026 12:58:17 -0500 Subject: [PATCH 8/8] Add placeholder var definition --- examples/kotlin/snippets/runtime/RunConfigExample.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/kotlin/snippets/runtime/RunConfigExample.kt b/examples/kotlin/snippets/runtime/RunConfigExample.kt index f434a901ef..e1f6192079 100644 --- a/examples/kotlin/snippets/runtime/RunConfigExample.kt +++ b/examples/kotlin/snippets/runtime/RunConfigExample.kt @@ -16,10 +16,12 @@ package com.google.adk.kt.examples.runtime +import com.google.adk.kt.agents.LlmAgent import com.google.adk.kt.agents.ResumabilityConfig import com.google.adk.kt.agents.RunConfig import com.google.adk.kt.agents.StreamingMode import com.google.adk.kt.annotations.ExperimentalResumabilityFeature +import com.google.adk.kt.models.Gemini import com.google.adk.kt.runners.InMemoryRunner import com.google.adk.kt.sessions.InMemorySessionService import com.google.adk.kt.types.Content @@ -72,6 +74,9 @@ val streamingConfig = ) // --8<-- [end:streaming_config] +private val rootAgent = + LlmAgent(name = "my_agent", model = Gemini(name = "gemini-flash-latest")) + // --8<-- [start:resumability_config] @OptIn(ExperimentalResumabilityFeature::class) val runner =