Files
Toni Klopfenstein dc3e3020e7 adding Runtime kotlin snippets (#1788)
* adding Runtime kotlin snippets

* Adding kotlin snippets into files to test

* Linting fixes via ktlint

* Simplify imports

* Add license headers

* Add code snippet for resume config

* Remove unused import

* Add placeholder var definition

---------

Co-authored-by: Kristopher Overholt <koverholt@google.com>
2026-05-21 13:06:51 -05:00

136 lines
4.5 KiB
Kotlin

/*
* 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
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
// --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<Event> {
// 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: 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 =
Event(
author = "my_agent",
actions = EventActions(stateDelta = updateData.toMutableMap()),
content = Content.fromText(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: InvocationContext) {
// 1. Modify state
ctx.session.state["status"] = "processing"
val event1 =
Event(
author = "my_agent",
actions = 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: 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]